[modular_test] Delete support for macro execution

Mostly a revert of https://dart-review.googlesource.com/c/sdk/+/361261.
A straight revert contained merge conflicts that I resolved manually but
I left some of the cleanup or refactoring in place.

Change-Id: I2add9f4951d7c6c2399d8c95f36b1483279eea44
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/409401
Commit-Queue: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Jake Macdonald <jakemac@google.com>
This commit is contained in:
Nicholas Shahan
2025-02-13 08:30:33 -08:00
committed by Commit Queue
parent ffe97e5fdc
commit 1ee0894631
16 changed files with 32 additions and 288 deletions
@@ -11,7 +11,6 @@ import 'dart:async';
import 'package:modular_test/src/io_pipeline.dart';
import 'package:modular_test/src/runner.dart';
import 'package:modular_test/src/steps/macro_precompile_aot.dart';
import 'modular_test_suite_helper.dart';
Future<void> main(List<String> args) async {
@@ -23,7 +22,6 @@ Future<void> main(List<String> args) async {
'tests/modular',
options,
IOPipeline([
PrecompileMacroAotStep(verbose: options.verbose),
OutlineDillCompilationStep(),
FullDillCompilationStep(),
ConcatenateDillsStep(),
@@ -20,7 +20,6 @@ import 'package:modular_test/src/io_pipeline.dart';
import 'package:modular_test/src/pipeline.dart';
import 'package:modular_test/src/runner.dart';
import 'package:modular_test/src/suite.dart';
import 'package:modular_test/src/steps/macro_precompile_aot.dart';
import 'package:modular_test/src/steps/util.dart';
import 'package:path/path.dart' as p;
@@ -157,11 +156,6 @@ abstract class CFEStep extends IOModularStep {
...(transitiveDependencies.expand(
(m) => ['--input-summary', '${toUri(m, inputData)}'],
)),
...transitiveDependencies
.where((m) => m.macroConstructors.isNotEmpty)
.expand(
(m) => ['--precompiled-macro', '${precompiledMacroArg(m, toUri)};'],
),
...(sources.expand((String uri) => ['--source', uri])),
...(flags.expand((String flag) => ['--enable-experiment', flag])),
];
@@ -196,10 +190,7 @@ class OutlineDillCompilationStep extends CFEStep {
bool get needsSources => true;
@override
List<DataId> get dependencyDataNeeded => const [
dillSummaryId,
precompiledMacroId,
];
List<DataId> get dependencyDataNeeded => const [dillSummaryId];
@override
List<DataId> get moduleDataNeeded => const [];
@@ -229,10 +220,7 @@ class FullDillCompilationStep extends CFEStep {
bool get needsSources => true;
@override
List<DataId> get dependencyDataNeeded => const [
dillSummaryId,
precompiledMacroId,
];
List<DataId> get dependencyDataNeeded => const [dillSummaryId];
@override
List<DataId> get moduleDataNeeded => const [];
@@ -335,8 +335,7 @@ Future<CompilerResult> _compile(List<String> args,
environmentDefines: declaredVariables,
trackNeededDillLibraries: recordUsedInputs,
nnbdMode:
options.soundNullSafety ? fe.NnbdMode.Strong : fe.NnbdMode.Weak,
precompiledMacros: options.precompiledMacros);
options.soundNullSafety ? fe.NnbdMode.Strong : fe.NnbdMode.Weak);
var incrementalCompiler = compilerState.incrementalCompiler!;
var cachedSdkInput = compileSdk
? null
+2 -20
View File
@@ -9,7 +9,6 @@ import 'package:modular_test/src/create_package_config.dart';
import 'package:modular_test/src/io_pipeline.dart';
import 'package:modular_test/src/pipeline.dart';
import 'package:modular_test/src/runner.dart';
import 'package:modular_test/src/steps/macro_precompile_aot.dart';
import 'package:modular_test/src/steps/util.dart';
import 'package:modular_test/src/suite.dart';
import 'package:path/path.dart' as p;
@@ -34,7 +33,7 @@ class SourceToSummaryDillStep implements IOModularStep {
bool get needsSources => true;
@override
List<DataId> get dependencyDataNeeded => const [dillId, precompiledMacroId];
List<DataId> get dependencyDataNeeded => const [dillId];
@override
List<DataId> get moduleDataNeeded => const [];
@@ -108,10 +107,6 @@ class SourceToSummaryDillStep implements IOModularStep {
...transitiveDependencies
.where((m) => !m.isSdk)
.expand((m) => ['--input-summary', '${toUri(m, dillId)}']),
...transitiveDependencies
.where((m) => m.macroConstructors.isNotEmpty)
.expand((m) =>
['--precompiled-macro', '${precompiledMacroArg(m, toUri)};']),
...sources.expand((String uri) => ['--source', uri]),
...flags.expand((String flag) => ['--enable-experiment', flag]),
];
@@ -125,9 +120,6 @@ class SourceToSummaryDillStep implements IOModularStep {
void notifyCached(Module module) {
if (_options.verbose) print('\ncached step: source-to-dill on $module');
}
@override
bool shouldExecute(Module module) => true;
}
class DDCStep implements IOModularStep {
@@ -142,7 +134,7 @@ class DDCStep implements IOModularStep {
bool get needsSources => true;
@override
List<DataId> get dependencyDataNeeded => const [dillId, precompiledMacroId];
List<DataId> get dependencyDataNeeded => const [dillId];
@override
List<DataId> get moduleDataNeeded => const [dillId];
@@ -206,10 +198,6 @@ class DDCStep implements IOModularStep {
...transitiveDependencies
.where((m) => !m.isSdk)
.expand((m) => ['-s', '${toUri(m, dillId)}=${m.name}']),
...transitiveDependencies
.where((m) => m.macroConstructors.isNotEmpty)
.expand((m) =>
['--precompiled-macro', '${precompiledMacroArg(m, toUri)};']),
'-o',
'$output',
];
@@ -222,9 +210,6 @@ class DDCStep implements IOModularStep {
void notifyCached(Module module) {
if (_options.verbose) print('\ncached step: ddc on $module');
}
@override
bool shouldExecute(Module module) => true;
}
class RunD8 implements IOModularStep {
@@ -287,9 +272,6 @@ class RunD8 implements IOModularStep {
void notifyCached(Module module) {
if (_options.verbose) print('\ncached step: d8 on $module');
}
@override
bool shouldExecute(Module module) => true;
}
String get _d8executable {
-2
View File
@@ -9,7 +9,6 @@ library;
import 'package:modular_test/src/io_pipeline.dart';
import 'package:modular_test/src/runner.dart';
import 'package:modular_test/src/steps/macro_precompile_aot.dart';
import 'modular_helpers.dart';
@@ -21,7 +20,6 @@ void main(List<String> args) async {
'tests/modular',
options,
IOPipeline([
PrecompileMacroAotStep(verbose: options.verbose),
SourceToSummaryDillStep(),
DDCStep(canaryFeatures: false),
RunD8(),
@@ -9,7 +9,6 @@ library;
import 'package:modular_test/src/io_pipeline.dart';
import 'package:modular_test/src/runner.dart';
import 'package:modular_test/src/steps/macro_precompile_aot.dart';
import 'modular_helpers.dart';
@@ -21,7 +20,6 @@ void main(List<String> args) async {
'tests/modular',
options,
IOPipeline([
PrecompileMacroAotStep(verbose: options.verbose),
SourceToSummaryDillStep(),
DDCStep(canaryFeatures: true),
RunD8(),
@@ -161,8 +161,6 @@ Future<InitializedCompilerState> initializeIncrementalCompiler(
required Map<ExperimentalFlag, bool> explicitExperimentalFlags,
required Map<String, String> environmentDefines,
bool trackNeededDillLibraries = false,
bool requirePrebuiltMacros = false,
List<String> precompiledMacros = const [],
required NnbdMode nnbdMode}) {
return modular.initializeIncrementalCompiler(
oldState,
+4 -17
View File
@@ -26,10 +26,6 @@ abstract class IOModularStep extends ModularStep {
/// should be stored under `root.resolveUri(toUri(module, resultKind))`.
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags);
/// Whether this step should apply to [module]. Most steps apply to all
/// modules, but not all.
bool shouldExecute(Module module) => true;
}
class IOPipeline extends Pipeline<IOModularStep> {
@@ -104,9 +100,6 @@ class IOPipeline extends Pipeline<IOModularStep> {
@override
Future<void> runStep(IOModularStep step, Module module,
Map<Module, Set<DataId>> visibleData, List<String> flags) async {
// Skip it if we aren't expected to run.
if (!step.shouldExecute(module)) return;
final resultsFolderUri = _resultsFolderUri!;
if (cacheSharedModules && module.isShared) {
// If all expected outputs are already available, skip the step.
@@ -134,13 +127,8 @@ class IOPipeline extends Pipeline<IOModularStep> {
for (var dataId in visibleData[module]!) {
var assetUri = resultsFolderUri
.resolve(_toFileName(module, dataId, configSpecific: true));
var originalFile = File.fromUri(assetUri);
// Some steps don't actually have an output, if they implement
// shouldExecute.
if (!(await originalFile.exists())) continue;
var newPath =
stepFolder.uri.resolve(_toFileName(module, dataId)).toFilePath();
await originalFile.copy(newPath);
await File.fromUri(assetUri).copy(
stepFolder.uri.resolve(_toFileName(module, dataId)).toFilePath());
}
}
if (step.needsSources) {
@@ -163,10 +151,9 @@ class IOPipeline extends Pipeline<IOModularStep> {
"Step '${step.runtimeType}' on module '${module.name}' didn't "
"produce an output file");
}
var newPath = resultsFolderUri
await outputFile.copy(resultsFolderUri
.resolve(_toFileName(module, dataId, configSpecific: true))
.toFilePath();
await outputFile.copy(newPath);
.toFilePath());
}
await stepFolder.delete(recursive: true);
}
+4 -20
View File
@@ -60,7 +60,7 @@ Future<ModularTest> loadTest(Uri uri) async {
}
var relativeUri = Uri.parse(fileName);
var isMain = moduleName == 'main';
var module = Module(moduleName, [], testUri, [relativeUri], {},
var module = Module(moduleName, [], testUri, [relativeUri],
mainSource: isMain ? relativeUri : null,
isMain: isMain,
packageBase: Uri.parse('.'));
@@ -87,7 +87,7 @@ Future<ModularTest> loadTest(Uri uri) async {
return _moduleConflict(moduleName, modules[moduleName]!, testUri);
}
var sources = await _listModuleSources(entryUri);
modules[moduleName] = Module(moduleName, [], testUri, sources, {},
modules[moduleName] = Module(moduleName, [], testUri, sources,
packageBase: Uri.parse('$moduleName/'));
}
}
@@ -109,7 +109,6 @@ Future<ModularTest> loadTest(Uri uri) async {
await _addModulePerPackage(spec.packages, testUri, modules);
_attachDependencies(spec.dependencies, modules);
_attachDependencies(defaultTestSpecification.dependencies, modules);
_attachMacros(spec.macros, modules);
_addSdkDependencies(modules, sdkModule);
_detectCyclesAndRemoveUnreachable(modules, mainModule);
var sortedModules = modules.values.toList()
@@ -155,20 +154,6 @@ void _attachDependencies(
});
}
void _attachMacros(Map<String, Map<String, Map<String, List<String>>>> macros,
Map<String, Module> modules) {
macros.forEach((name, macroConstructors) {
final module = modules[name];
if (module == null) {
_invalidTest("declared macros for a nonexistent module named '$name'");
}
if (module.macroConstructors.isNotEmpty) {
_invalidTest("Module macros have already been declared on $name.");
}
module.macroConstructors.addAll(macroConstructors);
});
}
/// Make every module depend on the sdk module.
void _addSdkDependencies(Map<String, Module> modules, Module sdkModule) {
for (var module in modules.values) {
@@ -192,8 +177,7 @@ Future<void> _addModulePerPackage(Map<String, String> packages, Uri configRoot,
// TODO(sigmund): validate that we don't use a different alias for a
// module that is part of the test (package name and module name should
// match).
modules[packageName] = Module(
packageName, [], packageRootUri, sources, {},
modules[packageName] = Module(packageName, [], packageRootUri, sources,
isPackage: true, packageBase: Uri.parse('lib/'), isShared: true);
}
}
@@ -221,7 +205,7 @@ Future<Module> _createSdkModule(Uri root) async {
}
}
sources.sort((a, b) => a.path.compareTo(b.path));
return Module('sdk', [], root, sources, {}, isSdk: true, isShared: true);
return Module('sdk', [], root, sources, isSdk: true, isShared: true);
}
/// Trim the set of modules, and detect cycles while we are at it.
@@ -1,100 +0,0 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
// ignore: implementation_imports
import 'package:macros/src/bootstrap.dart';
// ignore: implementation_imports
import 'package:macros/src/executor/serialization.dart';
import 'util.dart';
import '../create_package_config.dart';
import '../io_pipeline.dart';
import '../pipeline.dart';
import '../suite.dart';
const precompiledMacroId = DataId('macro.exe');
/// Bootstraps a macro program and compiles it to an AOT executable.
class PrecompileMacroAotStep implements IOModularStep {
final bool verbose;
PrecompileMacroAotStep({required this.verbose});
@override
List<DataId> get resultData => const [precompiledMacroId];
@override
bool get needsSources => true;
@override
List<DataId> get dependencyDataNeeded => const [];
@override
List<DataId> get moduleDataNeeded => const [];
@override
bool get onlyOnMain => false;
@override
bool get onlyOnSdk => false;
@override
bool get notOnSdk => true;
@override
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags) async {
if (verbose) {
print('\nstep: precompile-macro-aot on $module');
}
var transitiveDependencies = computeTransitiveDependencies(module);
var packageConfigUri = await writePackageConfig(
module, transitiveDependencies, root,
useRealPaths: true);
var bootstrapContent = bootstrapMacroIsolate(
module.macroConstructors, SerializationMode.byteData)
// TODO: Don't do this https://github.com/dart-lang/sdk/issues/55388
.replaceFirst('dev-dart-app:/', '');
var bootstrapFile = File.fromUri(
root.replace(path: '${root.path}/${module.name}.macro.bootstrap.dart'));
await bootstrapFile.create(recursive: true);
await bootstrapFile.writeAsString(bootstrapContent);
var args = [
'compile',
'exe',
'--packages',
packageConfigUri.toFilePath(),
'--output',
'${toUri(module, precompiledMacroId)}',
...flags.expand((String flag) => ['--enable-experiment', flag]),
bootstrapFile.path,
];
var result = await runProcess(
Platform.resolvedExecutable, args, root.toFilePath(), verbose);
checkExitCode(result, this, module, verbose);
}
@override
void notifyCached(Module module) {
if (verbose) {
print('\ncached step: precompile-macro-aot on $module');
}
}
@override
bool shouldExecute(Module module) => module.macroConstructors.isNotEmpty;
}
// The value of the --precompiled-macro argument for macros coming from
// `module`.
String precompiledMacroArg(Module module, ModuleDataToRelativeUri toUri) {
var executableUri = toUri(module, precompiledMacroId);
return '$executableUri;${module.macroConstructors.keys.join(';')}';
}
-10
View File
@@ -65,17 +65,7 @@ class Module {
/// will be true only for the SDK and shared packages like `package:expect`.
bool isShared;
/// A map containing information about the macros defined in the module.
///
/// The outer map is keyed on the source URI, and the values are the macros
/// defined in that source.
///
/// The inner map is keyed on class names, and the values are lists of the
/// names of the public constructors for those macros.
Map<String, Map<String, List<String>>> macroConstructors;
Module(this.name, this.dependencies, this.rootUri, this.sources,
this.macroConstructors,
{this.mainSource,
this.isPackage = false,
this.isMain = false,
@@ -110,60 +110,7 @@ TestSpecification parseTestSpecification(String contents) {
_invalidSpecification("packages is not a map");
}
}
final Map<String, Map<String, Map<String, List<String>>>> normalizedMacros =
{};
var macros = spec['macros'];
if (macros != null) {
if (macros is Map) {
macros.forEach((module, macroConfig) {
if (module is! String) {
_invalidSpecification("macros key $module was not a String");
}
if (macroConfig is! Map) {
_invalidSpecification(
"macros[$module] value $macroConfig was not a Map");
}
var normalizedConfig = normalizedMacros[module] = {};
macroConfig.forEach((library, macroConstructors) {
if (library is! String) {
_invalidSpecification(
"macros[$module] key `$library` was not a String");
}
if (macroConstructors is! Map) {
_invalidSpecification(
"macros[$module][$library] value `$macroConstructors` was not "
"a Map");
}
var normalizedConstructors = normalizedConfig[library] = {};
macroConstructors.forEach((clazz, constructors) {
if (clazz is! String) {
_invalidSpecification(
"macros[$module][$library] key `$clazz` was not a String");
}
if (constructors is! List) {
_invalidSpecification(
"macro[$module][$library][$clazz] value `$constructors` was "
"not a List");
}
var constructorNames = normalizedConstructors[clazz] = [];
for (var constructor in constructors) {
if (constructor is! String) {
_invalidSpecification(
"macros[$module][$library][$clazz] element `$constructor` "
"was not a String");
}
constructorNames.add(constructor);
}
});
});
});
} else {
_invalidSpecification("macros is not a Map");
}
}
return TestSpecification(
normalizedFlags, normalizedMap, normalizedPackages, normalizedMacros);
return TestSpecification(normalizedFlags, normalizedMap, normalizedPackages);
}
/// Data specifying details about a modular test including dependencies and
@@ -189,15 +136,7 @@ class TestSpecification {
/// where this test specification was defined.
final Map<String, String> packages;
/// A map containing information about the macros defined in each module.
///
/// The keys are the macro names, and the values describe the macros in each
/// module.
///
/// See the `Module` class for more information about the values.
final Map<String, Map<String, Map<String, List<String>>>> macros;
TestSpecification(this.flags, this.dependencies, this.packages, this.macros);
TestSpecification(this.flags, this.dependencies, this.packages);
}
Never _invalidSpecification(String message) {
-1
View File
@@ -12,7 +12,6 @@ environment:
# Use 'any' constraints here; we get our versions from the DEPS file.
dependencies:
args: any
macros: any
package_config: any
yaml: any
@@ -136,9 +136,6 @@ class SourceOnlyStep implements IOModularStep {
@override
void notifyCached(Module module) {}
@override
bool shouldExecute(Module module) => true;
}
class ModuleDataStep implements IOModularStep {
@@ -175,9 +172,6 @@ class ModuleDataStep implements IOModularStep {
@override
void notifyCached(Module module) {}
@override
bool shouldExecute(Module module) => true;
}
class TwoOutputStep implements IOModularStep {
@@ -220,9 +214,6 @@ class TwoOutputStep implements IOModularStep {
@override
void notifyCached(Module module) {}
@override
bool shouldExecute(Module module) => true;
}
class LinkStep implements IOModularStep {
@@ -264,9 +255,6 @@ class LinkStep implements IOModularStep {
@override
void notifyCached(Module module) {}
@override
bool shouldExecute(Module module) => true;
}
class MainOnlyStep implements IOModularStep {
@@ -308,9 +296,6 @@ class MainOnlyStep implements IOModularStep {
@override
void notifyCached(Module module) {}
@override
bool shouldExecute(Module module) => true;
}
Future<String?> _readHelper(Module module, Uri root, DataId dataId,
+3 -4
View File
@@ -101,12 +101,11 @@ runPipelineTest<S extends ModularStep>(PipelineTestStrategy<S> testStrategy) {
};
var m1 = Module("a", const [], testStrategy.testRootUri,
[Uri.parse("a1.dart"), Uri.parse("a2.dart")], {},
[Uri.parse("a1.dart"), Uri.parse("a2.dart")],
isShared: true);
var m2 = Module("b", [m1], testStrategy.testRootUri,
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")], {});
var m3 = Module(
"c", [m2], testStrategy.testRootUri, [Uri.parse("c.dart")], {},
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")]);
var m3 = Module("c", [m2], testStrategy.testRootUri, [Uri.parse("c.dart")],
isMain: true);
var singleModuleInput = ModularTest([m1], m1, []);
+14 -14
View File
@@ -10,23 +10,23 @@ import 'package:modular_test/src/suite.dart';
main() {
test('module test is not empty', () {
var m = Module("a", [], Uri.parse("app:/"), [], {});
var m = Module("a", [], Uri.parse("app:/"), []);
expect(() => ModularTest([], m, []), throwsA(TypeMatcher<ArgumentError>()));
});
test('package must depend on package', () {
var m1a = Module("a", const [], Uri.parse("app:/"),
[Uri.parse("a1.dart"), Uri.parse("a2.dart")], {},
[Uri.parse("a1.dart"), Uri.parse("a2.dart")],
isPackage: false);
var m1b = Module("a", const [], Uri.parse("app:/"),
[Uri.parse("a1.dart"), Uri.parse("a2.dart")], {},
[Uri.parse("a1.dart"), Uri.parse("a2.dart")],
isPackage: true);
var m2a = Module("b", [m1a], Uri.parse("app:/"),
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")], {},
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")],
isPackage: true);
var m2b = Module("b", [m1b], Uri.parse("app:/"),
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")], {},
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")],
isPackage: true);
expect(() => ModularTest([m1a, m2a], m2a, []),
throwsA(TypeMatcher<InvalidModularTestError>()));
@@ -35,17 +35,17 @@ main() {
test('shared module must depend on shared modules', () {
var m1a = Module("a", const [], Uri.parse("app:/"),
[Uri.parse("a1.dart"), Uri.parse("a2.dart")], {},
[Uri.parse("a1.dart"), Uri.parse("a2.dart")],
isShared: false);
var m1b = Module("a", const [], Uri.parse("app:/"),
[Uri.parse("a1.dart"), Uri.parse("a2.dart")], {},
[Uri.parse("a1.dart"), Uri.parse("a2.dart")],
isShared: true);
var m2a = Module("b", [m1a], Uri.parse("app:/"),
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")], {},
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")],
isShared: true);
var m2b = Module("b", [m1b], Uri.parse("app:/"),
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")], {},
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")],
isShared: true);
expect(() => ModularTest([m1a, m2a], m2a, []),
throwsA(TypeMatcher<InvalidModularTestError>()));
@@ -54,17 +54,17 @@ main() {
test('sdk module must not have dependencies', () {
var m1a = Module("a", const [], Uri.parse("app:/"),
[Uri.parse("a1.dart"), Uri.parse("a2.dart")], {},
[Uri.parse("a1.dart"), Uri.parse("a2.dart")],
isSdk: false);
var m1b = Module("a", const [], Uri.parse("app:/"),
[Uri.parse("a1.dart"), Uri.parse("a2.dart")], {},
[Uri.parse("a1.dart"), Uri.parse("a2.dart")],
isSdk: true);
var m2a = Module("b", [m1a], Uri.parse("app:/"),
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")], {},
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")],
isSdk: true);
var m2b = Module("b", [m1b], Uri.parse("app:/"),
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")], {},
[Uri.parse("b/b1.dart"), Uri.parse("b/b2.dart")],
isSdk: true);
expect(() => ModularTest([m1a, m2a], m2a, []),
throwsA(TypeMatcher<InvalidModularTestError>()));
@@ -74,7 +74,7 @@ main() {
test('sdk module cannot be package module', () {
var m = Module("a", const [], Uri.parse("app:/"),
[Uri.parse("a1.dart"), Uri.parse("a2.dart")], {},
[Uri.parse("a1.dart"), Uri.parse("a2.dart")],
isSdk: true);
expect(ModularTest([m], m, []), isNotNull);