diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index c1499ccbd7..482557aa46 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -618,6 +618,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { integrationTestUserIdentifier: stringArg(FlutterOptions.kDeviceUser), testTimeRecorder: testTimeRecorder, nativeAssetsBuilder: nativeAssetsBuilder, + buildInfo: buildInfo, ); } testTimeRecorder?.stop(TestTimePhases.TestRunner, testRunnerTimeRecorderStopwatch!); diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index 26b4883275..68648b3525 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -15,6 +15,7 @@ import 'package:test_core/src/platform.dart'; // ignore: implementation_imports import '../base/common.dart'; import '../base/file_system.dart'; import '../base/io.dart'; +import '../build_info.dart'; import '../cache.dart'; import '../compile.dart'; import '../convert.dart'; @@ -71,6 +72,7 @@ FlutterPlatform installHook({ TestTimeRecorder? testTimeRecorder, UriConverter? uriConverter, TestCompilerNativeAssetsBuilder? nativeAssetsBuilder, + BuildInfo? buildInfo, }) { assert(enableVmService || enableObservatory || (!debuggingOptions.startPaused && debuggingOptions.hostVmServicePort == null)); @@ -102,6 +104,7 @@ FlutterPlatform installHook({ testTimeRecorder: testTimeRecorder, uriConverter: uriConverter, nativeAssetsBuilder: nativeAssetsBuilder, + buildInfo: buildInfo, ); platformPluginRegistration(platform); return platform; @@ -120,6 +123,9 @@ FlutterPlatform installHook({ /// configuration files as outlined in the [flutter_test] library. By default, /// the test file will be launched directly. /// +/// The [packageConfigUri] argument specifies the package config location for +/// the test file being launched. This is expected to be a file URI. +/// /// The [updateGoldens] argument will set the [autoUpdateGoldens] global /// variable in the [flutter_test] package before invoking the test. /// @@ -132,6 +138,7 @@ String generateTestBootstrap({ required Uri testUrl, required InternetAddress host, File? testConfigFile, + Uri? packageConfigUri, bool updateGoldens = false, String languageVersionHeader = '', bool nullSafety = false, @@ -174,6 +181,15 @@ import '$testUrl' as test; import '${Uri.file(testConfigFile.path)}' as test_config; '''); } + + // IMPORTANT: DO NOT RENAME, REMOVE, OR MODIFY THE + // 'const packageConfigLocation' VARIABLE. + // Dash tooling like Dart DevTools performs an evaluation on this variable at + // runtime to get the package config location for Flutter test targets. + buffer.write(''' + +const packageConfigLocation = '$packageConfigUri'; +'''); buffer.write(''' /// Returns a serialized test suite. @@ -295,6 +311,7 @@ class FlutterPlatform extends PlatformPlugin { this.testTimeRecorder, this.uriConverter, this.nativeAssetsBuilder, + this.buildInfo, }); final String shellPath; @@ -312,6 +329,7 @@ class FlutterPlatform extends PlatformPlugin { final String? icudtlPath; final TestTimeRecorder? testTimeRecorder; final TestCompilerNativeAssetsBuilder? nativeAssetsBuilder; + final BuildInfo? buildInfo; // This can be used by internal projects that require custom logic for converting package: URIs to local paths. final UriConverter? uriConverter; @@ -641,6 +659,8 @@ class FlutterPlatform extends PlatformPlugin { return generateTestBootstrap( testUrl: testUrl, testConfigFile: findTestConfigFile(globals.fs.file(testUrl), globals.logger), + // This MUST be a file URI. + packageConfigUri: buildInfo != null ? globals.fs.path.toUri(buildInfo!.packageConfigPath) : null, host: host!, updateGoldens: updateGoldens!, flutterTestDep: packageConfig['flutter_test'] != null, diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart index 38b07e7c2f..fa0aff8969 100644 --- a/packages/flutter_tools/lib/src/test/runner.dart +++ b/packages/flutter_tools/lib/src/test/runner.dart @@ -64,6 +64,7 @@ abstract class FlutterTestRunner { String? integrationTestUserIdentifier, TestTimeRecorder? testTimeRecorder, TestCompilerNativeAssetsBuilder? nativeAssetsBuilder, + BuildInfo? buildInfo, }); /// Runs tests using the experimental strategy of spawning each test in a @@ -131,6 +132,7 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner { String? integrationTestUserIdentifier, TestTimeRecorder? testTimeRecorder, TestCompilerNativeAssetsBuilder? nativeAssetsBuilder, + BuildInfo? buildInfo, }) async { // Configure package:test to use the Flutter engine for child processes. final String shellPath = globals.artifacts!.getArtifactPath(Artifact.flutterTester); @@ -258,6 +260,7 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner { integrationTestUserIdentifier: integrationTestUserIdentifier, testTimeRecorder: testTimeRecorder, nativeAssetsBuilder: nativeAssetsBuilder, + buildInfo: buildInfo, ); try { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart index 7e2f271869..ba47030ccf 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart @@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/terminal.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/test.dart'; import 'package:flutter_tools/src/device.dart'; @@ -1479,6 +1480,7 @@ class FakeFlutterTestRunner implements FlutterTestRunner { String? integrationTestUserIdentifier, TestTimeRecorder? testTimeRecorder, TestCompilerNativeAssetsBuilder? nativeAssetsBuilder, + BuildInfo? buildInfo, }) async { lastEnableVmServiceValue = enableVmService; lastDebuggingOptionsValue = debuggingOptions; diff --git a/packages/flutter_tools/test/general.shard/flutter_platform_test.dart b/packages/flutter_tools/test/general.shard/flutter_platform_test.dart index b204e69481..60a6b1610f 100644 --- a/packages/flutter_tools/test/general.shard/flutter_platform_test.dart +++ b/packages/flutter_tools/test/general.shard/flutter_platform_test.dart @@ -122,4 +122,41 @@ void main() { expect(flutterPlatform.uriConverter?.call('hello'), 'hello/test'); }); }); + + group('generateTestBootstrap', () { + group('writes a "const packageConfigLocation" string', () { + test('with null packageConfigUri', () { + final String contents = generateTestBootstrap( + testUrl: + Uri.parse('file:///Users/me/some_package/test/some_test.dart'), + host: InternetAddress('127.0.0.1', type: InternetAddressType.IPv4), + ); + // IMPORTANT: DO NOT RENAME, REMOVE, OR MODIFY THE + // 'const packageConfigLocation' VARIABLE. + // Dash tooling like Dart DevTools performs an evaluation on this variable + // at runtime to get the package config location for Flutter test targets. + expect(contents, contains("const packageConfigLocation = 'null';")); + }); + + test('with non-null packageConfigUri', () { + final String contents = generateTestBootstrap( + testUrl: + Uri.parse('file:///Users/me/some_package/test/some_test.dart'), + host: InternetAddress('127.0.0.1', type: InternetAddressType.IPv4), + packageConfigUri: Uri.parse( + 'file:///Users/me/some_package/.dart_tool/package_config.json'), + ); + // IMPORTANT: DO NOT RENAME, REMOVE, OR MODIFY THE + // 'const packageConfigLocation' VARIABLE. + // Dash tooling like Dart DevTools performs an evaluation on this variable + // at runtime to get the package config location for Flutter test targets. + expect( + contents, + contains( + "const packageConfigLocation = 'file:///Users/me/some_package/.dart_tool/package_config.json';", + ), + ); + }); + }); + }); }