diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart index 070f7eda9fc..96ee6d8632e 100644 --- a/packages/flutter_tools/lib/src/commands/test.dart +++ b/packages/flutter_tools/lib/src/commands/test.dart @@ -727,8 +727,15 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { FlutterProject flutterProject, PackageConfig packageConfig, ) { - final String projectName = flutterProject.manifest.appName; - final Set packagesToInclude = {if (packagesRegExps.isEmpty) projectName}; + final Set packagesToInclude = {}; + if (packagesRegExps.isEmpty) { + void addProject(FlutterProject project) { + packagesToInclude.add(project.manifest.appName); + project.workspaceProjects.forEach(addProject); + } + + addProject(flutterProject); + } try { for (final String regExpStr in packagesRegExps) { final RegExp regExp = RegExp(regExpStr); diff --git a/packages/flutter_tools/lib/src/flutter_manifest.dart b/packages/flutter_tools/lib/src/flutter_manifest.dart index 335ec185583..0fe600cc3a0 100644 --- a/packages/flutter_tools/lib/src/flutter_manifest.dart +++ b/packages/flutter_tools/lib/src/flutter_manifest.dart @@ -140,6 +140,10 @@ class FlutterManifest { return dependencies != null ? {...dependencies.keys.cast()} : {}; } + /// List of all the entries in the workspace field of the `pubspec.yaml` file. + List get workspace => + (_descriptor['workspace'] as YamlList?)?.cast() ?? []; + // Flag to avoid printing multiple invalid version messages. bool _hasShowInvalidVersionMsg = false; diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index 23dec319bc1..4ef0d270790 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -131,6 +131,16 @@ class FlutterProject { /// The manifest of the example sub-project of this project. final FlutterManifest _exampleManifest; + /// List of [FlutterProject]s corresponding to the workspace entries. + List get workspaceProjects => + manifest.workspace + .map( + (String entry) => FlutterProject.fromDirectory( + directory.childDirectory(directory.fileSystem.path.normalize(entry)), + ), + ) + .toList(); + /// The set of organization names found in this project as /// part of iOS product bundle identifier, Android application ID, or /// Gradle group ID. 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 7b87fc3222d..1d034c309cd 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart @@ -358,6 +358,98 @@ dev_dependencies: }, ); + testUsingContext( + 'Coverage provides current library name and workspace names to Coverage Collector by default', + () async { + final Directory package = fs.currentDirectory; + package.childFile('pubspec.yaml').writeAsStringSync(''' +name: my_app +dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter +workspace: +- child1 +- child2 +'''); + package.childDirectory('child1').childFile('pubspec.yaml') + ..createSync(recursive: true) + ..writeAsStringSync(''' +name: child1 +resolution: workspace +'''); + package.childDirectory('child2').childFile('pubspec.yaml') + ..createSync(recursive: true) + ..writeAsStringSync(''' +name: child2 +resolution: workspace +workspace: +- example +'''); + package.childDirectory('child2').childDirectory('example').childFile('pubspec.yaml') + ..createSync(recursive: true) + ..writeAsStringSync(''' +name: child2_example +resolution: workspace +'''); + + final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( + requests: [ + FakeVmServiceRequest( + method: 'getVM', + jsonResponse: + (VM.parse({})! + ..isolates = [ + IsolateRef.parse({'id': '1'})!, + ]) + .toJson(), + ), + FakeVmServiceRequest( + method: 'getSourceReport', + args: { + 'isolateId': '1', + 'reports': ['Coverage'], + 'forceCompile': true, + 'reportLines': true, + 'libraryFilters': [ + 'package:my_app/', + 'package:child1/', + 'package:child2/', + 'package:child2_example/', + ], + 'librariesAlreadyCompiled': [], + }, + jsonResponse: SourceReport(ranges: []).toJson(), + ), + ], + ); + final FakeFlutterTestRunner testRunner = FakeFlutterTestRunner(0, null, fakeVmServiceHost); + + final TestCommand testCommand = TestCommand(testRunner: testRunner); + final CommandRunner commandRunner = createTestCommandRunner(testCommand); + await commandRunner.run(const [ + 'test', + '--no-pub', + '--coverage', + '--', + 'test/some_test.dart', + ]); + expect(fakeVmServiceHost.hasRemainingExpectations, false); + expect((testRunner.lastTestWatcher! as CoverageCollector).libraryNames, { + 'my_app', + 'child1', + 'child2', + 'child2_example', + }); + }, + overrides: { + FileSystem: () => fs, + ProcessManager: () => FakeProcessManager.any(), + Cache: () => Cache.test(processManager: FakeProcessManager.any()), + }, + ); + testUsingContext( 'Coverage provides library names matching regexps to Coverage Collector', () async { diff --git a/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart b/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart index 9b597479ff2..d9ae0e77d44 100644 --- a/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart +++ b/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart @@ -1547,6 +1547,35 @@ flutter: args: - deferredComponentArg'''); }); + + testWithoutContext('FlutterManifest can parse workspace', () async { + const String manifest = ''' +name: test +workspace: +- pkgs/bar +- pkgs/foo +'''; + final FlutterManifest? flutterManifest = FlutterManifest.createFromString( + manifest, + logger: BufferLogger.test(), + ); + + expect(flutterManifest, isNotNull); + expect(flutterManifest!.workspace, ['pkgs/bar', 'pkgs/foo']); + }); + + testWithoutContext('FlutterManifest can parse empty workspace', () async { + const String manifest = ''' +name: test +'''; + final FlutterManifest? flutterManifest = FlutterManifest.createFromString( + manifest, + logger: BufferLogger.test(), + ); + + expect(flutterManifest, isNotNull); + expect(flutterManifest!.workspace, isEmpty); + }); } Matcher matchesManifest({String? appVersion, String? buildName, String? buildNumber}) { diff --git a/packages/flutter_tools/test/general.shard/project_test.dart b/packages/flutter_tools/test/general.shard/project_test.dart index 570f30f995c..8df65c1a143 100644 --- a/packages/flutter_tools/test/general.shard/project_test.dart +++ b/packages/flutter_tools/test/general.shard/project_test.dart @@ -1522,6 +1522,50 @@ plugins { expect(updatedPubspecContents, validPubspecWithDependenciesAndNullValues); }); }); + + group('workspaces', () { + _testInMemory('fails on invalid pubspec.yaml', () async { + final Directory directory = globals.fs.directory('myproject'); + directory.childFile('pubspec.yaml') + ..createSync(recursive: true) + ..writeAsStringSync(''' +name: parent +flutter: +workspace: +- child1 +- child2 +- child2/example +'''); + directory.childDirectory('child1').childFile('pubspec.yaml') + ..createSync(recursive: true) + ..writeAsStringSync(''' +name: child1 +flutter: +resolution: workspace +'''); + directory.childDirectory('child2').childFile('pubspec.yaml') + ..createSync(recursive: true) + ..writeAsStringSync(''' +name: child2 +flutter: +resolution: workspace +'''); + directory.childDirectory('child2').childDirectory('example').childFile('pubspec.yaml') + ..createSync(recursive: true) + ..writeAsStringSync(''' +name: child2_example +flutter: +resolution: workspace +'''); + + expect( + FlutterProject.fromDirectory(directory).workspaceProjects + .map((FlutterProject subproject) => subproject.manifest.appName) + .toList(), + ['child1', 'child2', 'child2_example'], + ); + }); + }); }); group('watch companion', () {