diff --git a/packages/flutter_tools/test/general.shard/fake_process_manager_test.dart b/packages/flutter_tools/test/general.shard/fake_process_manager_test.dart index f2d5154848..bfa6478613 100644 --- a/packages/flutter_tools/test/general.shard/fake_process_manager_test.dart +++ b/packages/flutter_tools/test/general.shard/fake_process_manager_test.dart @@ -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: ['faketool'])); + + final Process process = await manager.start(['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: ['faketool'], + stderr: 'hello', + stdout: 'world', + outputFollowsExit: true, + )); + + final List stderrBytes = []; + final List stdoutBytes = []; + + // Start the process. + final Process process = await manager.start(['faketool']); + final StreamSubscription> stderrSubscription = process.stderr.listen((List chunk) { stderrBytes.addAll(chunk); }); + final StreamSubscription> stdoutSubscription = process.stdout.listen((List 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(>[ + 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: ['faketool'])); + + final ProcessResult result = await manager.run(['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: ['faketool'])); + + final ProcessResult result = await manager.run(['faketool']); + expect(result.exitCode, 0); + expect(result.stdout, isA()); + expect(result.stderr, isA()); + }); + + testWithoutContext('stderr, stdout are List if encoding is null', () async { + manager.addCommand(const FakeCommand(command: ['faketool'])); + + final ProcessResult result = await manager.run( + ['faketool'], + stderrEncoding: null, + stdoutEncoding: null, + ); + expect(result.exitCode, 0); + expect(result.stdout, isA>()); + expect(result.stderr, isA>()); + }); + + testWithoutContext('stderr, stdout are String if encoding is specified', () async { + manager.addCommand(const FakeCommand(command: ['faketool'])); + + final ProcessResult result = await manager.run( + ['faketool'], + stderrEncoding: utf8, + stdoutEncoding: utf8, + ); + expect(result.exitCode, 0); + expect(result.stdout, isA()); + expect(result.stderr, isA()); + }); + }); + + group('runSync', () { + testWithoutContext('can run a fake command', () { + manager.addCommand(const FakeCommand(command: ['faketool'])); + + final ProcessResult result = manager.runSync(['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: ['faketool'])); + + final ProcessResult result = manager.runSync(['faketool']); + expect(result.exitCode, 0); + expect(result.stdout, isA()); + expect(result.stderr, isA()); + }); + + testWithoutContext('stderr, stdout are List if encoding is null', () { + manager.addCommand(const FakeCommand(command: ['faketool'])); + + final ProcessResult result = manager.runSync( + ['faketool'], + stderrEncoding: null, + stdoutEncoding: null, + ); + expect(result.exitCode, 0); + expect(result.stdout, isA>()); + expect(result.stderr, isA>()); + }); + + testWithoutContext('stderr, stdout are String if encoding is specified', () { + manager.addCommand(const FakeCommand(command: ['faketool'])); + + final ProcessResult result = manager.runSync( + ['faketool'], + stderrEncoding: utf8, + stdoutEncoding: utf8, + ); + expect(result.exitCode, 0); + expect(result.stdout, isA()); + expect(result.stderr, isA()); + }); + }); + }); } diff --git a/packages/flutter_tools/test/src/fake_process_manager.dart b/packages/flutter_tools/test/src/fake_process_manager.dart index db77d428f6..59194a0343 100644 --- a/packages/flutter_tools/test/src/fake_process_manager.dart +++ b/packages/flutter_tools/test/src/fake_process_manager.dart @@ -158,6 +158,8 @@ class FakeProcess implements io.Process { } else if (outputFollowsExit) { // Wait for the process to exit before emitting stderr. this.stderr = Stream>.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>(() => _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>.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>(() => _stdout); })); } else {