Skip to content

Commit 232c2b4

Browse files
cbrackencamsim99
authored andcommitted
[tool] Add tests for FakeProcessManager (flutter#104456)
Adds a bit more clarifying documentation to the implementation of the outputFollowsExit case, and adds tests that verify the behaviour of stderr, stdout of processes launched via FakeProcessManager. Specifically: * Verifies that stderr, stdout are not emitted immediately after process exit if outputFollowsExit is true. They must be emitted at least one turn through the event loop later. * Verifies that ProcessResult.stderr, stdout have the type documented according to the encoding passted to Process.run/runSync: * List<int> if null is passed as the encoding. * String (in the default system encoding) if no encoding is specified. * String (in the specified encoding) if an encoding is specified. This is additional testing relating to refactoring landed in: flutter#103947 Issue: flutter#102451
1 parent d59227c commit 232c2b4

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

packages/flutter_tools/test/general.shard/fake_process_manager_test.dart

+148
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
67
import 'package:fake_async/fake_async.dart';
8+
import 'package:flutter_tools/src/base/io.dart';
9+
import 'package:flutter_tools/src/convert.dart' show utf8;
710

811
import '../src/common.dart';
912
import '../src/fake_process_manager.dart';
@@ -172,4 +175,149 @@ void main() {
172175
expect(stdout, 'stdout'.codeUnits);
173176
});
174177
});
178+
179+
group(FakeProcessManager, () {
180+
late FakeProcessManager manager;
181+
182+
setUp(() {
183+
manager = FakeProcessManager.empty();
184+
});
185+
186+
group('start', () {
187+
testWithoutContext('can run a fake command', () async {
188+
manager.addCommand(const FakeCommand(command: <String>['faketool']));
189+
190+
final Process process = await manager.start(<String>['faketool']);
191+
expect(await process.exitCode, 0);
192+
expect(await utf8.decodeStream(process.stdout), isEmpty);
193+
expect(await utf8.decodeStream(process.stderr), isEmpty);
194+
});
195+
196+
testWithoutContext('outputFollowsExit delays stderr, stdout until after process exit', () async {
197+
manager.addCommand(const FakeCommand(
198+
command: <String>['faketool'],
199+
stderr: 'hello',
200+
stdout: 'world',
201+
outputFollowsExit: true,
202+
));
203+
204+
final List<int> stderrBytes = <int>[];
205+
final List<int> stdoutBytes = <int>[];
206+
207+
// Start the process.
208+
final Process process = await manager.start(<String>['faketool']);
209+
final StreamSubscription<List<int>> stderrSubscription = process.stderr.listen((List<int> chunk) { stderrBytes.addAll(chunk); });
210+
final StreamSubscription<List<int>> stdoutSubscription = process.stdout.listen((List<int> chunk) { stdoutBytes.addAll(chunk); });
211+
212+
// Immediately after exit, no output is emitted.
213+
await process.exitCode;
214+
expect(utf8.decode(stderrBytes), isEmpty);
215+
expect(utf8.decode(stdoutBytes), isEmpty);
216+
217+
// Output is emitted asynchronously after process exit.
218+
await Future.wait(<Future<void>>[
219+
stderrSubscription.asFuture(),
220+
stdoutSubscription.asFuture(),
221+
]);
222+
expect(utf8.decode(stderrBytes), 'hello');
223+
expect(utf8.decode(stdoutBytes), 'world');
224+
225+
// Clean up stream subscriptions.
226+
await stderrSubscription.cancel();
227+
await stdoutSubscription.cancel();
228+
});
229+
});
230+
231+
group('run', () {
232+
testWithoutContext('can run a fake command', () async {
233+
manager.addCommand(const FakeCommand(command: <String>['faketool']));
234+
235+
final ProcessResult result = await manager.run(<String>['faketool']);
236+
expect(result.exitCode, 0);
237+
expect(result.stdout, isEmpty);
238+
expect(result.stderr, isEmpty);
239+
});
240+
241+
testWithoutContext('stderr, stdout are String if encoding is unspecified', () async {
242+
manager.addCommand(const FakeCommand(command: <String>['faketool']));
243+
244+
final ProcessResult result = await manager.run(<String>['faketool']);
245+
expect(result.exitCode, 0);
246+
expect(result.stdout, isA<String>());
247+
expect(result.stderr, isA<String>());
248+
});
249+
250+
testWithoutContext('stderr, stdout are List<int> if encoding is null', () async {
251+
manager.addCommand(const FakeCommand(command: <String>['faketool']));
252+
253+
final ProcessResult result = await manager.run(
254+
<String>['faketool'],
255+
stderrEncoding: null,
256+
stdoutEncoding: null,
257+
);
258+
expect(result.exitCode, 0);
259+
expect(result.stdout, isA<List<int>>());
260+
expect(result.stderr, isA<List<int>>());
261+
});
262+
263+
testWithoutContext('stderr, stdout are String if encoding is specified', () async {
264+
manager.addCommand(const FakeCommand(command: <String>['faketool']));
265+
266+
final ProcessResult result = await manager.run(
267+
<String>['faketool'],
268+
stderrEncoding: utf8,
269+
stdoutEncoding: utf8,
270+
);
271+
expect(result.exitCode, 0);
272+
expect(result.stdout, isA<String>());
273+
expect(result.stderr, isA<String>());
274+
});
275+
});
276+
277+
group('runSync', () {
278+
testWithoutContext('can run a fake command', () {
279+
manager.addCommand(const FakeCommand(command: <String>['faketool']));
280+
281+
final ProcessResult result = manager.runSync(<String>['faketool']);
282+
expect(result.exitCode, 0);
283+
expect(result.stdout, isEmpty);
284+
expect(result.stderr, isEmpty);
285+
});
286+
287+
testWithoutContext('stderr, stdout are String if encoding is unspecified', () {
288+
manager.addCommand(const FakeCommand(command: <String>['faketool']));
289+
290+
final ProcessResult result = manager.runSync(<String>['faketool']);
291+
expect(result.exitCode, 0);
292+
expect(result.stdout, isA<String>());
293+
expect(result.stderr, isA<String>());
294+
});
295+
296+
testWithoutContext('stderr, stdout are List<int> if encoding is null', () {
297+
manager.addCommand(const FakeCommand(command: <String>['faketool']));
298+
299+
final ProcessResult result = manager.runSync(
300+
<String>['faketool'],
301+
stderrEncoding: null,
302+
stdoutEncoding: null,
303+
);
304+
expect(result.exitCode, 0);
305+
expect(result.stdout, isA<List<int>>());
306+
expect(result.stderr, isA<List<int>>());
307+
});
308+
309+
testWithoutContext('stderr, stdout are String if encoding is specified', () {
310+
manager.addCommand(const FakeCommand(command: <String>['faketool']));
311+
312+
final ProcessResult result = manager.runSync(
313+
<String>['faketool'],
314+
stderrEncoding: utf8,
315+
stdoutEncoding: utf8,
316+
);
317+
expect(result.exitCode, 0);
318+
expect(result.stdout, isA<String>());
319+
expect(result.stderr, isA<String>());
320+
});
321+
});
322+
});
175323
}

packages/flutter_tools/test/src/fake_process_manager.dart

+4
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ class FakeProcess implements io.Process {
158158
} else if (outputFollowsExit) {
159159
// Wait for the process to exit before emitting stderr.
160160
this.stderr = Stream<List<int>>.fromFuture(this.exitCode.then((_) {
161+
// Return a Future so stderr isn't immediately available to those who
162+
// await exitCode, but is available asynchronously later.
161163
return Future<List<int>>(() => _stderr);
162164
}));
163165
} else {
@@ -169,6 +171,8 @@ class FakeProcess implements io.Process {
169171
} else if (outputFollowsExit) {
170172
// Wait for the process to exit before emitting stdout.
171173
this.stdout = Stream<List<int>>.fromFuture(this.exitCode.then((_) {
174+
// Return a Future so stdout isn't immediately available to those who
175+
// await exitCode, but is available asynchronously later.
172176
return Future<List<int>>(() => _stdout);
173177
}));
174178
} else {

0 commit comments

Comments
 (0)