diff --git a/packages/flutter_test/lib/src/goldens.dart b/packages/flutter_test/lib/src/goldens.dart index 537cca03343..8a141a9e2c0 100644 --- a/packages/flutter_test/lib/src/goldens.dart +++ b/packages/flutter_test/lib/src/goldens.dart @@ -125,8 +125,7 @@ class LocalFileComparator implements GoldenFileComparator { /// /// The [testFile] URI must represent a file. LocalFileComparator(Uri testFile, {path.Style pathStyle}) - : assert(testFile.scheme == 'file'), - basedir = _getBasedir(testFile, pathStyle), + : basedir = _getBasedir(testFile, pathStyle), _path = _getPath(pathStyle); static path.Context _getPath(path.Style style) { @@ -135,7 +134,9 @@ class LocalFileComparator implements GoldenFileComparator { static Uri _getBasedir(Uri testFile, path.Style pathStyle) { final path.Context context = _getPath(pathStyle); - return context.toUri(context.dirname(context.fromUri(testFile)) + context.separator); + final String testFilePath = context.fromUri(testFile); + final String testDirectoryPath = context.dirname(testFilePath); + return context.toUri(testDirectoryPath + context.separator); } /// The directory in which the test was loaded. diff --git a/packages/flutter_test/test/goldens_test.dart b/packages/flutter_test/test/goldens_test.dart index b84ed0921a6..e93945b1e60 100644 --- a/packages/flutter_test/test/goldens_test.dart +++ b/packages/flutter_test/test/goldens_test.dart @@ -78,6 +78,11 @@ void main() { expect(comparator.basedir, fs.directory(fix('/foo/bar/')).uri); }); + test('can be instantiated with uri that represents file in same folder', () { + comparator = new LocalFileComparator(Uri.parse('foo_test.dart'), pathStyle: fs.path.style); + expect(comparator.basedir, Uri.parse('./')); + }); + group('compare', () { Future doComparison([String golden = 'golden.png']) { final Uri uri = fs.file(fix(golden)).uri; @@ -101,6 +106,28 @@ void main() { final bool success = await doComparison('sub/foo.png'); expect(success, isTrue); }); + + group('when comparator instantiated with uri that represents file in same folder', () { + test('and golden file is in same folder as test', () async { + fs.file(fix('/foo/bar/golden.png')) + ..createSync(recursive: true) + ..writeAsBytesSync(_kExpectedBytes); + fs.currentDirectory = fix('/foo/bar'); + comparator = new LocalFileComparator(Uri.parse('local_test.dart'), pathStyle: fs.path.style); + final bool success = await doComparison('golden.png'); + expect(success, isTrue); + }); + + test('and golden file is in subfolder of test', () async { + fs.file(fix('/foo/bar/baz/golden.png')) + ..createSync(recursive: true) + ..writeAsBytesSync(_kExpectedBytes); + fs.currentDirectory = fix('/foo/bar'); + comparator = new LocalFileComparator(Uri.parse('local_test.dart'), pathStyle: fs.path.style); + final bool success = await doComparison('baz/golden.png'); + expect(success, isTrue); + }); + }); }); group('fails', () { diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index b2e68a04c2f..bc2c8c127a7 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -95,6 +95,95 @@ void installHook({ ); } +/// Generates the bootstrap entry point script that will be used to launch an +/// individual test file. +/// +/// The [testUrl] argument specifies the path to the test file that is being +/// launched. +/// +/// The [host] argument specifies the address at which the test harness is +/// running. +/// +/// If [testConfigFile] is specified, it must follow the conventions of test +/// configuration files as outlined in the [flutter_test] library. By default, +/// the test file will be launched directly. +/// +/// The [updateGoldens] argument will set the [autoUpdateGoldens] global +/// variable in the [flutter_test] package before invoking the test. +String generateTestBootstrap({ + @required Uri testUrl, + @required InternetAddress host, + File testConfigFile, + bool updateGoldens: false, +}) { + assert(testUrl != null); + assert(host != null); + assert(updateGoldens != null); + + final String websocketUrl = host.type == InternetAddressType.IP_V4 // ignore: deprecated_member_use + ? 'ws://${host.address}' + : 'ws://[${host.address}]'; + final String encodedWebsocketUrl = Uri.encodeComponent(websocketUrl); + + final StringBuffer buffer = new StringBuffer(); + buffer.write(''' +import 'dart:convert'; +import 'dart:io'; // ignore: dart_io_import + +// We import this library first in order to trigger an import error for +// package:test (rather than package:stream_channel) when the developer forgets +// to add a dependency on package:test. +import 'package:test/src/runner/plugin/remote_platform_helpers.dart'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:stream_channel/stream_channel.dart'; +import 'package:test/src/runner/vm/catch_isolate_errors.dart'; + +import '$testUrl' as test; +''' + ); + if (testConfigFile != null) { + buffer.write(''' +import '${new Uri.file(testConfigFile.path)}' as test_config; +''' + ); + } + buffer.write(''' + +void main() { + print('$_kStartTimeoutTimerMessage'); + String serverPort = Platform.environment['SERVER_PORT']; + String server = Uri.decodeComponent('$encodedWebsocketUrl:\$serverPort'); + StreamChannel channel = serializeSuite(() { + catchIsolateErrors(); + goldenFileComparator = new LocalFileComparator(Uri.parse('$testUrl')); + autoUpdateGoldenFiles = $updateGoldens; +''' + ); + if (testConfigFile != null) { + buffer.write(''' + return () => test_config.main(test.main); +'''); + } else { + buffer.write(''' + return test.main; +'''); + } + buffer.write(''' + }); + WebSocket.connect(server).then((WebSocket socket) { + socket.map((dynamic x) { + assert(x is String); + return json.decode(x); + }).pipe(channel.sink); + socket.addStream(channel.stream.map(json.encode)); + }); +} +''' + ); + return buffer.toString(); +} + enum _InitialResult { crashed, timedOut, connected } enum _TestResult { crashed, harnessBailed, testBailed } typedef Future _Finalizer(); @@ -581,7 +670,6 @@ class _FlutterPlatform extends PlatformPlugin { listenerFile.createSync(); listenerFile.writeAsStringSync(_generateTestMain( testUrl: fs.path.toUri(fs.path.absolute(testPath)), - encodedWebsocketUrl: Uri.encodeComponent(_getWebSocketUrl()), )); return listenerFile.path; } @@ -621,15 +709,8 @@ class _FlutterPlatform extends PlatformPlugin { return tempBundleDirectory.path; } - String _getWebSocketUrl() { - return host.type == InternetAddressType.IP_V4 // ignore: deprecated_member_use - ? 'ws://${host.address}' - : 'ws://[${host.address}]'; - } - String _generateTestMain({ Uri testUrl, - String encodedWebsocketUrl, }) { assert(testUrl.scheme == 'file'); File testConfigFile; @@ -648,63 +729,12 @@ class _FlutterPlatform extends PlatformPlugin { } directory = directory.parent; } - final StringBuffer buffer = new StringBuffer(); - buffer.write(''' -import 'dart:convert'; -import 'dart:io'; // ignore: dart_io_import - -// We import this library first in order to trigger an import error for -// package:test (rather than package:stream_channel) when the developer forgets -// to add a dependency on package:test. -import 'package:test/src/runner/plugin/remote_platform_helpers.dart'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:stream_channel/stream_channel.dart'; -import 'package:test/src/runner/vm/catch_isolate_errors.dart'; - -import '$testUrl' as test; -''' + return generateTestBootstrap( + testUrl: testUrl, + testConfigFile: testConfigFile, + host: host, + updateGoldens: updateGoldens, ); - if (testConfigFile != null) { - buffer.write(''' -import '${new Uri.file(testConfigFile.path)}' as test_config; -''' - ); - } - buffer.write(''' - -void main() { - print('$_kStartTimeoutTimerMessage'); - String serverPort = Platform.environment['SERVER_PORT']; - String server = Uri.decodeComponent('$encodedWebsocketUrl:\$serverPort'); - StreamChannel channel = serializeSuite(() { - catchIsolateErrors(); - goldenFileComparator = new LocalFileComparator(Uri.parse('$testUrl')); - autoUpdateGoldenFiles = $updateGoldens; -''' - ); - if (testConfigFile != null) { - buffer.write(''' - return () => test_config.main(test.main); -'''); - } else { - buffer.write(''' - return test.main; -'''); - } - buffer.write(''' - }); - WebSocket.connect(server).then((WebSocket socket) { - socket.map((dynamic x) { - assert(x is String); - return json.decode(x); - }).pipe(channel.sink); - socket.addStream(channel.stream.map(json.encode)); - }); -} -''' - ); - return buffer.toString(); } File _cachedFontConfig;