diff --git a/packages/flutter_tools/lib/src/test/flutter_web_goldens.dart b/packages/flutter_tools/lib/src/test/flutter_web_goldens.dart index 82d7a0e2207..e7fd741afeb 100644 --- a/packages/flutter_tools/lib/src/test/flutter_web_goldens.dart +++ b/packages/flutter_tools/lib/src/test/flutter_web_goldens.dart @@ -4,44 +4,33 @@ // @dart = 2.8 + import 'dart:async'; import 'dart:typed_data'; -import 'package:meta/meta.dart'; -import 'package:process/process.dart'; - import '../base/common.dart'; import '../base/file_system.dart'; import '../base/io.dart'; -import '../base/logger.dart'; import '../convert.dart'; +import '../globals_null_migrated.dart' as globals; import 'test_compiler.dart'; import 'test_config.dart'; /// Helper class to start golden file comparison in a separate process. /// -/// The golden file comparator is configured using flutter_test_config.dart and that +/// Golden file comparator is configured using flutter_test_config.dart and that /// file can contain arbitrary Dart code that depends on dart:ui. Thus it has to /// be executed in a `flutter_tester` environment. This helper class generates a /// Dart file configured with flutter_test_config.dart to perform the comparison /// of golden files. class TestGoldenComparator { /// Creates a [TestGoldenComparator] instance. - TestGoldenComparator(this.shellPath, this.compilerFactory, { - @required Logger logger, - @required FileSystem fileSystem, - @required ProcessManager processManager, - }) : tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_web_platform.'), - _logger = logger, - _fileSystem = fileSystem, - _processManager = processManager; + TestGoldenComparator(this.shellPath, this.compilerFactory) + : tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_web_platform.'); final String shellPath; final Directory tempDir; final TestCompiler Function() compilerFactory; - final Logger _logger; - final FileSystem _fileSystem; - final ProcessManager _processManager; TestCompiler _compiler; TestGoldenComparatorProcess _previousComparator; @@ -60,10 +49,10 @@ class TestGoldenComparator { return _previousComparator; } - final String bootstrap = TestGoldenComparatorProcess.generateBootstrap(_fileSystem.file(testUri), testUri, logger: _logger); + final String bootstrap = TestGoldenComparatorProcess.generateBootstrap(testUri); final Process process = await _startProcess(bootstrap); unawaited(_previousComparator?.close()); - _previousComparator = TestGoldenComparatorProcess(process, logger: _logger); + _previousComparator = TestGoldenComparatorProcess(process); _previousTestUri = testUri; return _previousComparator; @@ -81,7 +70,7 @@ class TestGoldenComparator { shellPath, '--disable-observatory', '--non-interactive', - '--packages=${_fileSystem.path.join('.dart_tool', 'package_config.json')}', + '--packages=${globals.fs.path.join('.dart_tool', 'package_config.json')}', output, ]; @@ -89,7 +78,7 @@ class TestGoldenComparator { // Chrome is the only supported browser currently. 'FLUTTER_TEST_BROWSER': 'chrome', }; - return _processManager.start(command, environment: environment); + return globals.processManager.start(command, environment: environment); } Future compareGoldens(Uri testUri, Uint8List bytes, Uri goldenKey, bool updateGoldens) async { @@ -111,7 +100,7 @@ class TestGoldenComparator { /// handles communication with the child process. class TestGoldenComparatorProcess { /// Creates a [TestGoldenComparatorProcess] backed by [process]. - TestGoldenComparatorProcess(this.process, {@required Logger logger}) : _logger = logger { + TestGoldenComparatorProcess(this.process) { // Pipe stdout and stderr to printTrace and printError. // Also parse stdout as a stream of JSON objects. streamIterator = StreamIterator>( @@ -119,7 +108,7 @@ class TestGoldenComparatorProcess { .transform(utf8.decoder) .transform(const LineSplitter()) .where((String line) { - logger.printTrace('<<< $line'); + globals.printTrace('<<< $line'); return line.isNotEmpty && line[0] == '{'; }) .map(jsonDecode) @@ -129,17 +118,16 @@ class TestGoldenComparatorProcess { .transform(utf8.decoder) .transform(const LineSplitter()) .forEach((String line) { - logger.printError('<<< $line'); + globals.printError('<<< $line'); }); } - final Logger _logger; final Process process; StreamIterator> streamIterator; Future close() async { + await process.stdin.close(); process.kill(); - await process.exitCode; } void sendCommand(File imageFile, Uri goldenKey, bool updateGoldens) { @@ -148,7 +136,7 @@ class TestGoldenComparatorProcess { 'key': goldenKey.toString(), 'update': updateGoldens, }); - _logger.printTrace('Preparing to send command: $command'); + globals.printTrace('Preparing to send command: $command'); process.stdin.writeln(command); } @@ -158,8 +146,8 @@ class TestGoldenComparatorProcess { return streamIterator.current; } - static String generateBootstrap(File testFile, Uri testUri, {@required Logger logger}) { - final File testConfigFile = findTestConfigFile(testFile, logger); + static String generateBootstrap(Uri testUri) { + final File testConfigFile = findTestConfigFile(globals.fs.file(testUri), globals.logger); // Generate comparator process for the file. return ''' import 'dart:convert'; // flutter_ignore: dart_convert_import diff --git a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart index 03a07625a29..3b9ad88fe5c 100644 --- a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart @@ -12,7 +12,6 @@ import 'package:http_multi_server/http_multi_server.dart'; import 'package:meta/meta.dart'; import 'package:package_config/package_config.dart'; import 'package:pool/pool.dart'; -import 'package:process/process.dart'; import 'package:shelf/shelf.dart' as shelf; import 'package:shelf/shelf_io.dart' as shelf_io; import 'package:shelf_static/shelf_static.dart'; @@ -52,7 +51,6 @@ class FlutterWebPlatform extends PlatformPlugin { @required ChromiumLauncher chromiumLauncher, @required Logger logger, @required Artifacts artifacts, - @required ProcessManager processManager, }) : _fileSystem = fileSystem, _flutterToolPackageConfig = flutterToolPackageConfig, _chromiumLauncher = chromiumLauncher, @@ -77,9 +75,6 @@ class FlutterWebPlatform extends PlatformPlugin { _testGoldenComparator = TestGoldenComparator( shellPath, () => TestCompiler(buildInfo, flutterProject), - fileSystem: _fileSystem, - logger: _logger, - processManager: processManager, ); } @@ -116,7 +111,6 @@ class FlutterWebPlatform extends PlatformPlugin { @required Logger logger, @required ChromiumLauncher chromiumLauncher, @required Artifacts artifacts, - @required ProcessManager processManager, }) async { final shelf_io.IOServer server = shelf_io.IOServer(await HttpMultiServer.loopback(0)); final PackageConfig packageConfig = await loadPackageConfigWithLogging( @@ -144,7 +138,6 @@ class FlutterWebPlatform extends PlatformPlugin { artifacts: artifacts, logger: logger, nullAssertions: nullAssertions, - processManager: processManager, ); } diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart index df23b33ee5c..25c0effc3b1 100644 --- a/packages/flutter_tools/lib/src/test/runner.dart +++ b/packages/flutter_tools/lib/src/test/runner.dart @@ -168,7 +168,6 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner { logger: globals.logger, fileSystem: globals.fs, artifacts: globals.artifacts, - processManager: globals.processManager, chromiumLauncher: ChromiumLauncher( fileSystem: globals.fs, platform: globals.platform, diff --git a/packages/flutter_tools/test/general.shard/flutter_tester_device_test.dart b/packages/flutter_tools/test/general.shard/flutter_tester_device_test.dart index 2bf8d87e924..e5226fb5de9 100644 --- a/packages/flutter_tools/test/general.shard/flutter_tester_device_test.dart +++ b/packages/flutter_tools/test/general.shard/flutter_tester_device_test.dart @@ -17,8 +17,8 @@ import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/test/flutter_tester_device.dart'; import 'package:flutter_tools/src/test/font_config_manager.dart'; import 'package:meta/meta.dart'; +import 'package:mockito/mockito.dart'; import 'package:stream_channel/stream_channel.dart'; -import 'package:test/fake.dart'; import '../src/common.dart'; import '../src/context.dart'; @@ -80,7 +80,7 @@ void main() { ], environment: { 'FLUTTER_TEST': expectedFlutterTestValue, 'FONTCONFIG_FILE': device.fontConfigManager.fontConfigFile.path, - 'SERVER_PORT': '0', + 'SERVER_PORT': 'null', 'APP_NAME': '', }); } @@ -244,28 +244,17 @@ class TestFlutterTesterDevice extends FlutterTesterTestDevice { @override Future startDds(Uri uri) async { _ddsServiceUriCompleter.complete(uri); - return FakeDartDevelopmentService(Uri.parse('http://localhost:${debuggingOptions.hostVmServicePort}'), Uri.parse('http://localhost:8080')); + final MockDartDevelopmentService mock = MockDartDevelopmentService(); + when(mock.uri).thenReturn(Uri.parse('http://localhost:${debuggingOptions.hostVmServicePort}')); + return mock; } @override - Future bind(InternetAddress host, int port) async => FakeHttpServer(); + Future bind(InternetAddress host, int port) async => MockHttpServer(); @override Future> get remoteChannel async => StreamChannelController().foreign; } -class FakeDartDevelopmentService extends Fake implements DartDevelopmentService { - FakeDartDevelopmentService(this.uri, this.original); - - final Uri original; - - @override - final Uri uri; - - @override - Uri get remoteVmServiceUri => original; -} -class FakeHttpServer extends Fake implements HttpServer { - @override - int get port => 0; -} +class MockDartDevelopmentService extends Mock implements DartDevelopmentService {} +class MockHttpServer extends Mock implements HttpServer {} diff --git a/packages/flutter_tools/test/general.shard/web/golden_comparator_process_test.dart b/packages/flutter_tools/test/general.shard/web/golden_comparator_process_test.dart index e11e46eaaf9..efc85cd5ca9 100644 --- a/packages/flutter_tools/test/general.shard/web/golden_comparator_process_test.dart +++ b/packages/flutter_tools/test/general.shard/web/golden_comparator_process_test.dart @@ -7,14 +7,16 @@ import 'dart:convert'; import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import 'package:flutter_tools/src/test/flutter_web_goldens.dart'; import '../../src/common.dart'; import '../../src/fakes.dart'; +import '../../src/testbed.dart'; void main() { + final Testbed testbed = Testbed(); + group('Test that TestGoldenComparatorProcess', () { File imageFile; Uri goldenKey; @@ -33,7 +35,7 @@ void main() { ); }); - testWithoutContext('can pass data', () async { + test('can pass data', () => testbed.run(() async { final Map expectedResponse = { 'success': true, 'message': 'some message', @@ -42,7 +44,7 @@ void main() { final FakeProcess mockProcess = createFakeProcess(jsonEncode(expectedResponse) + '\n'); final MemoryIOSink ioSink = mockProcess.stdin as MemoryIOSink; - final TestGoldenComparatorProcess process = TestGoldenComparatorProcess(mockProcess, logger: BufferLogger.test()); + final TestGoldenComparatorProcess process = TestGoldenComparatorProcess(mockProcess); process.sendCommand(imageFile, goldenKey, false); final Map response = await process.getResponse(); @@ -50,9 +52,9 @@ void main() { expect(response, expectedResponse); expect(stringToStdin, '{"imageFile":"test_image_file","key":"file://golden_key/","update":false}\n'); - }); + })); - testWithoutContext('can handle multiple requests', () async { + test('can handle multiple requests', () => testbed.run(() async { final Map expectedResponse1 = { 'success': true, 'message': 'some message', @@ -65,7 +67,7 @@ void main() { final FakeProcess mockProcess = createFakeProcess(jsonEncode(expectedResponse1) + '\n' + jsonEncode(expectedResponse2) + '\n'); final MemoryIOSink ioSink = mockProcess.stdin as MemoryIOSink; - final TestGoldenComparatorProcess process = TestGoldenComparatorProcess(mockProcess, logger: BufferLogger.test()); + final TestGoldenComparatorProcess process = TestGoldenComparatorProcess(mockProcess); process.sendCommand(imageFile, goldenKey, false); final Map response1 = await process.getResponse(); @@ -78,9 +80,9 @@ void main() { expect(response1, expectedResponse1); expect(response2, expectedResponse2); expect(stringToStdin, '{"imageFile":"test_image_file","key":"file://golden_key/","update":false}\n{"imageFile":"second_test_image_file","key":"file://second_golden_key/","update":true}\n'); - }); + })); - testWithoutContext('ignores anything that does not look like JSON', () async { + test('ignores anything that does not look like JSON', () => testbed.run(() async { final Map expectedResponse = { 'success': true, 'message': 'some message', @@ -95,7 +97,7 @@ Other JSON data after the initial data '''); final MemoryIOSink ioSink = mockProcess.stdin as MemoryIOSink; - final TestGoldenComparatorProcess process = TestGoldenComparatorProcess(mockProcess,logger: BufferLogger.test()); + final TestGoldenComparatorProcess process = TestGoldenComparatorProcess(mockProcess); process.sendCommand(imageFile, goldenKey, false); final Map response = await process.getResponse(); @@ -103,7 +105,7 @@ Other JSON data after the initial data expect(response, expectedResponse); expect(stringToStdin, '{"imageFile":"test_image_file","key":"file://golden_key/","update":false}\n'); - }); + })); }); } diff --git a/packages/flutter_tools/test/general.shard/web/golden_comparator_test.dart b/packages/flutter_tools/test/general.shard/web/golden_comparator_test.dart index 78b8de0c503..f740d49f163 100644 --- a/packages/flutter_tools/test/general.shard/web/golden_comparator_test.dart +++ b/packages/flutter_tools/test/general.shard/web/golden_comparator_test.dart @@ -4,91 +4,95 @@ // @dart = 2.8 -import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; -import 'package:file/memory.dart'; -import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import 'package:flutter_tools/src/test/flutter_web_goldens.dart'; import 'package:flutter_tools/src/test/test_compiler.dart'; -import 'package:test/fake.dart'; +import 'package:mockito/mockito.dart'; +import 'package:process/process.dart'; import '../../src/common.dart'; -import '../../src/context.dart'; - -final Uri goldenKey = Uri.parse('file://golden_key'); -final Uri goldenKey2 = Uri.parse('file://second_golden_key'); -final Uri testUri = Uri.parse('file://test_uri'); -final Uri testUri2 = Uri.parse('file://second_test_uri'); -final Uint8List imageBytes = Uint8List.fromList([1, 2, 3, 4, 5]); +import '../../src/fakes.dart'; +import '../../src/testbed.dart'; void main() { group('Test that TestGoldenComparator', () { - FakeProcessManager processManager; + Testbed testbed; + Uri goldenKey; + Uri goldenKey2; + Uri testUri; + Uri testUri2; + Uint8List imageBytes; + MockProcessManager mockProcessManager; + MockTestCompiler mockCompiler; setUp(() { - processManager = FakeProcessManager.empty(); + goldenKey = Uri.parse('file://golden_key'); + goldenKey2 = Uri.parse('file://second_golden_key'); + testUri = Uri.parse('file://test_uri'); + testUri2 = Uri.parse('file://second_test_uri'); + imageBytes = Uint8List.fromList([1,2,3,4,5]); + mockProcessManager = MockProcessManager(); + mockCompiler = MockTestCompiler(); + when(mockCompiler.compile(any)).thenAnswer((_) => Future.value('compiler_output')); + + testbed = Testbed(overrides: { + ProcessManager: () { + return mockProcessManager; + } + }); }); - testWithoutContext('succeed when golden comparison succeed', () async { + test('succeed when golden comparison succeed', () => testbed.run(() async { final Map expectedResponse = { 'success': true, 'message': 'some message', }; - processManager.addCommand(FakeCommand( - command: const [ - 'shell', - '--disable-observatory', - '--non-interactive', - '--packages=.dart_tool/package_config.json', - 'compiler_output' - ], stdout: jsonEncode(expectedResponse) + '\n', - )); + + when(mockProcessManager.start(any, environment: anyNamed('environment'))) + .thenAnswer((Invocation invocation) async { + return FakeProcess( + exitCode: Future.value(0), + stdout: stdoutFromString(jsonEncode(expectedResponse) + '\n'), + ); + }); final TestGoldenComparator comparator = TestGoldenComparator( 'shell', - () => FakeTestCompiler(), - processManager: processManager, - fileSystem: MemoryFileSystem.test(), - logger: BufferLogger.test(), + () => mockCompiler, ); final String result = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); expect(result, null); - }); + })); - testWithoutContext('fail with error message when golden comparison failed', () async { + test('fail with error message when golden comparison failed', () => testbed.run(() async { final Map expectedResponse = { 'success': false, 'message': 'some message', }; - processManager.addCommand(FakeCommand( - command: const [ - 'shell', - '--disable-observatory', - '--non-interactive', - '--packages=.dart_tool/package_config.json', - 'compiler_output' - ], stdout: jsonEncode(expectedResponse) + '\n', - )); + when(mockProcessManager.start(any, environment: anyNamed('environment'))) + .thenAnswer((Invocation invocation) async { + return FakeProcess( + exitCode: Future.value(0), + stdout: stdoutFromString(jsonEncode(expectedResponse) + '\n'), + ); + }); final TestGoldenComparator comparator = TestGoldenComparator( 'shell', - () => FakeTestCompiler(), - processManager: processManager, - fileSystem: MemoryFileSystem.test(), - logger: BufferLogger.test(), + () => mockCompiler, ); final String result = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); expect(result, 'some message'); - }); + })); - testWithoutContext('reuse the process for the same test file', () async { + test('reuse the process for the same test file', () => testbed.run(() async { final Map expectedResponse1 = { 'success': false, 'message': 'some message', @@ -98,32 +102,27 @@ void main() { 'message': 'some other message', }; - processManager.addCommand(FakeCommand( - command: const [ - 'shell', - '--disable-observatory', - '--non-interactive', - '--packages=.dart_tool/package_config.json', - 'compiler_output' - ], stdout: jsonEncode(expectedResponse1) + '\n' + jsonEncode(expectedResponse2) + '\n', - )); + when(mockProcessManager.start(any, environment: anyNamed('environment'))) + .thenAnswer((Invocation invocation) async { + return FakeProcess( + exitCode: Future.value(0), + stdout: stdoutFromString(jsonEncode(expectedResponse1) + '\n' + jsonEncode(expectedResponse2) + '\n'), + ); + }); final TestGoldenComparator comparator = TestGoldenComparator( 'shell', - () => FakeTestCompiler(), - processManager: processManager, - fileSystem: MemoryFileSystem.test(), - logger: BufferLogger.test(), + () => mockCompiler, ); final String result1 = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); expect(result1, 'some message'); - final String result2 = await comparator.compareGoldens(testUri, imageBytes, goldenKey2, false); expect(result2, 'some other message'); - }); + verify(mockProcessManager.start(any, environment: anyNamed('environment'))).called(1); + })); - testWithoutContext('does not reuse the process for different test file', () async { + test('does not reuse the process for different test file', () => testbed.run(() async { final Map expectedResponse1 = { 'success': false, 'message': 'some message', @@ -133,82 +132,57 @@ void main() { 'message': 'some other message', }; - processManager.addCommand(FakeCommand( - command: const [ - 'shell', - '--disable-observatory', - '--non-interactive', - '--packages=.dart_tool/package_config.json', - 'compiler_output' - ], stdout: jsonEncode(expectedResponse1) + '\n', - )); - processManager.addCommand(FakeCommand( - command: const [ - 'shell', - '--disable-observatory', - '--non-interactive', - '--packages=.dart_tool/package_config.json', - 'compiler_output' - ], stdout: jsonEncode(expectedResponse2) + '\n', - )); + when(mockProcessManager.start(any, environment: anyNamed('environment'))) + .thenAnswer((Invocation invocation) async { + return FakeProcess( + exitCode: Future.value(0), + stdout: stdoutFromString(jsonEncode(expectedResponse1) + '\n' + jsonEncode(expectedResponse2) + '\n'), + ); + }); final TestGoldenComparator comparator = TestGoldenComparator( 'shell', - () => FakeTestCompiler(), - processManager: processManager, - fileSystem: MemoryFileSystem.test(), - logger: BufferLogger.test(), + () => mockCompiler, ); final String result1 = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); expect(result1, 'some message'); - final String result2 = await comparator.compareGoldens(testUri2, imageBytes, goldenKey2, false); - expect(result2, 'some other message'); - }); + expect(result2, 'some message'); + verify(mockProcessManager.start(any, environment: anyNamed('environment'))).called(2); + })); - testWithoutContext('removes all temporary files when closed', () async { - final FileSystem fileSystem = MemoryFileSystem.test(); + test('removes all temporary files when closed', () => testbed.run(() async { final Map expectedResponse = { 'success': true, 'message': 'some message', }; - final StreamController> controller = StreamController>(); - final IOSink stdin = IOSink(controller.sink); - processManager.addCommand(FakeCommand( - command: const [ - 'shell', - '--disable-observatory', - '--non-interactive', - '--packages=.dart_tool/package_config.json', - 'compiler_output' - ], stdout: jsonEncode(expectedResponse) + '\n', - stdin: stdin, - )); + + when(mockProcessManager.start(any, environment: anyNamed('environment'))) + .thenAnswer((Invocation invocation) async { + return FakeProcess( + exitCode: Future.value(0), + stdout: stdoutFromString(jsonEncode(expectedResponse) + '\n'), + ); + }); final TestGoldenComparator comparator = TestGoldenComparator( 'shell', - () => FakeTestCompiler(), - processManager: processManager, - fileSystem: fileSystem, - logger: BufferLogger.test(), + () => mockCompiler, ); final String result = await comparator.compareGoldens(testUri, imageBytes, goldenKey, false); expect(result, null); await comparator.close(); - expect(fileSystem.systemTempDirectory.listSync(recursive: true), isEmpty); - }); + expect(globals.fs.systemTempDirectory.listSync(recursive: true), isEmpty); + })); }); } -class FakeTestCompiler extends Fake implements TestCompiler { - @override - Future compile(Uri mainDart) { - return Future.value('compiler_output'); - } +Stream> stdoutFromString(String string) => Stream>.fromIterable(>[ + utf8.encode(string), +]); - @override - Future dispose() async { } -} +class MockProcessManager extends Mock implements ProcessManager {} +class MockTestCompiler extends Mock implements TestCompiler {}