[tool] Add tests for FakeProcessManager (#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: https://github.com/flutter/flutter/pull/103947 Issue: https://github.com/flutter/flutter/issues/102451
This commit is contained in:
parent
893b4613ca
commit
f9765c136f
@ -3,7 +3,10 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:fake_async/fake_async.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/convert.dart' show utf8;
|
||||
|
||||
import '../src/common.dart';
|
||||
import '../src/fake_process_manager.dart';
|
||||
@ -172,4 +175,149 @@ void main() {
|
||||
expect(stdout, 'stdout'.codeUnits);
|
||||
});
|
||||
});
|
||||
|
||||
group(FakeProcessManager, () {
|
||||
late FakeProcessManager manager;
|
||||
|
||||
setUp(() {
|
||||
manager = FakeProcessManager.empty();
|
||||
});
|
||||
|
||||
group('start', () {
|
||||
testWithoutContext('can run a fake command', () async {
|
||||
manager.addCommand(const FakeCommand(command: <String>['faketool']));
|
||||
|
||||
final Process process = await manager.start(<String>['faketool']);
|
||||
expect(await process.exitCode, 0);
|
||||
expect(await utf8.decodeStream(process.stdout), isEmpty);
|
||||
expect(await utf8.decodeStream(process.stderr), isEmpty);
|
||||
});
|
||||
|
||||
testWithoutContext('outputFollowsExit delays stderr, stdout until after process exit', () async {
|
||||
manager.addCommand(const FakeCommand(
|
||||
command: <String>['faketool'],
|
||||
stderr: 'hello',
|
||||
stdout: 'world',
|
||||
outputFollowsExit: true,
|
||||
));
|
||||
|
||||
final List<int> stderrBytes = <int>[];
|
||||
final List<int> stdoutBytes = <int>[];
|
||||
|
||||
// Start the process.
|
||||
final Process process = await manager.start(<String>['faketool']);
|
||||
final StreamSubscription<List<int>> stderrSubscription = process.stderr.listen((List<int> chunk) { stderrBytes.addAll(chunk); });
|
||||
final StreamSubscription<List<int>> stdoutSubscription = process.stdout.listen((List<int> chunk) { stdoutBytes.addAll(chunk); });
|
||||
|
||||
// Immediately after exit, no output is emitted.
|
||||
await process.exitCode;
|
||||
expect(utf8.decode(stderrBytes), isEmpty);
|
||||
expect(utf8.decode(stdoutBytes), isEmpty);
|
||||
|
||||
// Output is emitted asynchronously after process exit.
|
||||
await Future.wait(<Future<void>>[
|
||||
stderrSubscription.asFuture(),
|
||||
stdoutSubscription.asFuture(),
|
||||
]);
|
||||
expect(utf8.decode(stderrBytes), 'hello');
|
||||
expect(utf8.decode(stdoutBytes), 'world');
|
||||
|
||||
// Clean up stream subscriptions.
|
||||
await stderrSubscription.cancel();
|
||||
await stdoutSubscription.cancel();
|
||||
});
|
||||
});
|
||||
|
||||
group('run', () {
|
||||
testWithoutContext('can run a fake command', () async {
|
||||
manager.addCommand(const FakeCommand(command: <String>['faketool']));
|
||||
|
||||
final ProcessResult result = await manager.run(<String>['faketool']);
|
||||
expect(result.exitCode, 0);
|
||||
expect(result.stdout, isEmpty);
|
||||
expect(result.stderr, isEmpty);
|
||||
});
|
||||
|
||||
testWithoutContext('stderr, stdout are String if encoding is unspecified', () async {
|
||||
manager.addCommand(const FakeCommand(command: <String>['faketool']));
|
||||
|
||||
final ProcessResult result = await manager.run(<String>['faketool']);
|
||||
expect(result.exitCode, 0);
|
||||
expect(result.stdout, isA<String>());
|
||||
expect(result.stderr, isA<String>());
|
||||
});
|
||||
|
||||
testWithoutContext('stderr, stdout are List<int> if encoding is null', () async {
|
||||
manager.addCommand(const FakeCommand(command: <String>['faketool']));
|
||||
|
||||
final ProcessResult result = await manager.run(
|
||||
<String>['faketool'],
|
||||
stderrEncoding: null,
|
||||
stdoutEncoding: null,
|
||||
);
|
||||
expect(result.exitCode, 0);
|
||||
expect(result.stdout, isA<List<int>>());
|
||||
expect(result.stderr, isA<List<int>>());
|
||||
});
|
||||
|
||||
testWithoutContext('stderr, stdout are String if encoding is specified', () async {
|
||||
manager.addCommand(const FakeCommand(command: <String>['faketool']));
|
||||
|
||||
final ProcessResult result = await manager.run(
|
||||
<String>['faketool'],
|
||||
stderrEncoding: utf8,
|
||||
stdoutEncoding: utf8,
|
||||
);
|
||||
expect(result.exitCode, 0);
|
||||
expect(result.stdout, isA<String>());
|
||||
expect(result.stderr, isA<String>());
|
||||
});
|
||||
});
|
||||
|
||||
group('runSync', () {
|
||||
testWithoutContext('can run a fake command', () {
|
||||
manager.addCommand(const FakeCommand(command: <String>['faketool']));
|
||||
|
||||
final ProcessResult result = manager.runSync(<String>['faketool']);
|
||||
expect(result.exitCode, 0);
|
||||
expect(result.stdout, isEmpty);
|
||||
expect(result.stderr, isEmpty);
|
||||
});
|
||||
|
||||
testWithoutContext('stderr, stdout are String if encoding is unspecified', () {
|
||||
manager.addCommand(const FakeCommand(command: <String>['faketool']));
|
||||
|
||||
final ProcessResult result = manager.runSync(<String>['faketool']);
|
||||
expect(result.exitCode, 0);
|
||||
expect(result.stdout, isA<String>());
|
||||
expect(result.stderr, isA<String>());
|
||||
});
|
||||
|
||||
testWithoutContext('stderr, stdout are List<int> if encoding is null', () {
|
||||
manager.addCommand(const FakeCommand(command: <String>['faketool']));
|
||||
|
||||
final ProcessResult result = manager.runSync(
|
||||
<String>['faketool'],
|
||||
stderrEncoding: null,
|
||||
stdoutEncoding: null,
|
||||
);
|
||||
expect(result.exitCode, 0);
|
||||
expect(result.stdout, isA<List<int>>());
|
||||
expect(result.stderr, isA<List<int>>());
|
||||
});
|
||||
|
||||
testWithoutContext('stderr, stdout are String if encoding is specified', () {
|
||||
manager.addCommand(const FakeCommand(command: <String>['faketool']));
|
||||
|
||||
final ProcessResult result = manager.runSync(
|
||||
<String>['faketool'],
|
||||
stderrEncoding: utf8,
|
||||
stdoutEncoding: utf8,
|
||||
);
|
||||
expect(result.exitCode, 0);
|
||||
expect(result.stdout, isA<String>());
|
||||
expect(result.stderr, isA<String>());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -158,6 +158,8 @@ class FakeProcess implements io.Process {
|
||||
} else if (outputFollowsExit) {
|
||||
// Wait for the process to exit before emitting stderr.
|
||||
this.stderr = Stream<List<int>>.fromFuture(this.exitCode.then((_) {
|
||||
// Return a Future so stderr isn't immediately available to those who
|
||||
// await exitCode, but is available asynchronously later.
|
||||
return Future<List<int>>(() => _stderr);
|
||||
}));
|
||||
} else {
|
||||
@ -169,6 +171,8 @@ class FakeProcess implements io.Process {
|
||||
} else if (outputFollowsExit) {
|
||||
// Wait for the process to exit before emitting stdout.
|
||||
this.stdout = Stream<List<int>>.fromFuture(this.exitCode.then((_) {
|
||||
// Return a Future so stdout isn't immediately available to those who
|
||||
// await exitCode, but is available asynchronously later.
|
||||
return Future<List<int>>(() => _stdout);
|
||||
}));
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user