@@ -40,8 +40,7 @@ Future<void> main(List<String> args) async {
4040 final runner = switch (options.runtime) {
4141 RuntimePlatforms .chrome => ChromeSuiteRunner (options),
4242 RuntimePlatforms .d8 => D8SuiteRunner (options),
43- // TODO(nshahan): Create a suite runner specific to the VM.
44- RuntimePlatforms .vm => HotReloadSuiteRunner (options),
43+ RuntimePlatforms .vm => VMSuiteRunner (options),
4544 };
4645 await runner.runSuite (options);
4746}
@@ -229,11 +228,11 @@ class HotReloadSuiteRunner {
229228 Options options;
230229
231230 /// The root directory containing generated code for all tests.
232- late final generatedCodeDir = Directory .systemTemp.createTempSync ();
231+ late final Directory generatedCodeDir = Directory .systemTemp.createTempSync ();
233232
234233 /// The directory containing files emitted from Frontend Server compiles and
235234 /// recompiles.
236- late final frontendServerEmittedFilesDir =
235+ late final Directory frontendServerEmittedFilesDir =
237236 Directory .fromUri (generatedCodeDir.uri.resolve ('.fes/' ))..createSync ();
238237
239238 /// The output location for .dill file created by the front end server.
@@ -246,16 +245,21 @@ class HotReloadSuiteRunner {
246245 frontendServerEmittedFilesDir.uri.resolve ('output_incremental.dill' );
247246
248247 /// All test results that are reported after running the entire test suite.
249- final testOutcomes = < TestResultOutcome > [];
248+ final List < TestResultOutcome > testOutcomes = [];
250249
251250 /// The directory used as a temporary staging area to construct a compile-able
252251 /// test app across reload/restart generations.
253- late final snapshotDir =
252+ late final Directory snapshotDir =
254253 Directory .fromUri (generatedCodeDir.uri.resolve ('.snapshot/' ))
255254 ..createSync ();
256255
257256 // TODO(markzipan): Support custom entrypoints.
258257 late final Uri snapshotEntrypointUri = snapshotDir.uri.resolve ('main.dart' );
258+ late final String snapshotEntrypointWithScheme = () {
259+ final snapshotEntrypointLibraryName = fe_shared.relativizeUri (
260+ snapshotDir.uri, snapshotEntrypointUri, fe_shared.isWindows);
261+ return '$filesystemScheme :///$snapshotEntrypointLibraryName ' ;
262+ }();
259263
260264 HotReloadMemoryFilesystem ? filesystem;
261265 final stopwatch = Stopwatch ();
@@ -309,7 +313,15 @@ class HotReloadSuiteRunner {
309313 continue ;
310314 }
311315 _print ('Finished emitting assets.' , label: test.name);
312- await runTest (test, tempDirectory);
316+ final testOutputStreamController = StreamController <List <int >>();
317+ final testOutputBuffer = StringBuffer ();
318+ testOutputStreamController.stream
319+ .transform (utf8.decoder)
320+ .listen (testOutputBuffer.write);
321+ final testPassed = await runTest (
322+ test, tempDirectory, IOSink (testOutputStreamController.sink));
323+ await reportTestOutcome (
324+ test.name, testOutputBuffer.toString (), testPassed);
313325 }
314326 await shutdown (controller);
315327 await reportAllResults ();
@@ -638,7 +650,10 @@ class HotReloadSuiteRunner {
638650 }
639651
640652 /// Copy all files in [test] for the given [generation] into the snapshot
641- /// directory.
653+ /// directory and returns uris of all the files copied.
654+ ///
655+ /// The uris describe the copy destination in the form of the hot reload file
656+ /// system scheme.
642657 List <String > copyGenerationSources (HotReloadTest test, int generation) {
643658 _debugPrint ('Entering generation $generation ' , label: test.name);
644659 final updatedFilesInCurrentGeneration = < String > [];
@@ -670,18 +685,14 @@ class HotReloadSuiteRunner {
670685 /// front end server [controller] and copy outputs to [outputDirectory] .
671686 ///
672687 /// Reports test failures on compile time errors.
688+ // TODO(nshahan): Move to a DDC specific suite runner.
673689 Future <bool > compileGeneration (
674690 HotReloadTest test,
675691 int generation,
676692 Directory outputDirectory,
677693 List <String > updatedFiles,
678694 HotReloadFrontendServerController controller) async {
679695 var hasCompileError = false ;
680- final filesystemScheme = 'hot-reload-test' ;
681- final snapshotEntrypointLibraryName = fe_shared.relativizeUri (
682- snapshotDir.uri, snapshotEntrypointUri, fe_shared.isWindows);
683- final snapshotEntrypointWithScheme =
684- '$filesystemScheme :///$snapshotEntrypointLibraryName ' ;
685696 // The first generation calls `compile`, but subsequent ones call
686697 // `recompile`.
687698 // Likewise, use the incremental output directory for `recompile` calls.
@@ -737,44 +748,28 @@ class HotReloadSuiteRunner {
737748 'Frontend Server successfully compiled outputs to: '
738749 '$outputDillPath ' ,
739750 label: test.name);
740- if (options.runtime.emitsJS) {
741- _debugPrint ('Emitting JS code to ${outputDirectory .path }.' ,
742- label: test.name);
743- // Update the memory filesystem with the newly-created JS files.
744- _print ('Loading generation $generation files into the memory filesystem.' ,
745- label: test.name);
746- final codeFile = File ('$outputDillPath .sources' );
747- final manifestFile = File ('$outputDillPath .json' );
748- final sourcemapFile = File ('$outputDillPath .map' );
749- filesystem! .update (codeFile, manifestFile, sourcemapFile,
750- generation: '$generation ' );
751-
752- // Write JS files and sourcemaps to their respective generation.
753- _print ('Writing generation $generation assets.' , label: test.name);
754- _debugPrint ('Writing JS assets to ${outputDirectory .path }' ,
755- label: test.name);
756- filesystem! .writeToDisk (outputDirectory.uri, generation: '$generation ' );
757- } else {
758- final dillOutputDir = Directory .fromUri (
759- outputDirectory.uri.resolve ('generation$generation ' ));
760- dillOutputDir.createSync ();
761- final dillOutputUri = dillOutputDir.uri.resolve ('${test .name }.dill' );
762- File (outputDillPath).copySync (dillOutputUri.toFilePath ());
763- // Write dills their respective generation.
764- _print ('Writing generation $generation assets.' , label: test.name);
765- _debugPrint ('Writing dill to ${dillOutputUri .toFilePath ()}' ,
766- label: test.name);
767- }
751+ _debugPrint ('Emitting JS code to ${outputDirectory .path }.' ,
752+ label: test.name);
753+ // Update the memory filesystem with the newly-created JS files.
754+ _print ('Loading generation $generation files into the memory filesystem.' ,
755+ label: test.name);
756+ final codeFile = File ('$outputDillPath .sources' );
757+ final manifestFile = File ('$outputDillPath .json' );
758+ final sourcemapFile = File ('$outputDillPath .map' );
759+ filesystem! .update (codeFile, manifestFile, sourcemapFile,
760+ generation: '$generation ' );
761+
762+ // Write JS files and sourcemaps to their respective generation.
763+ _print ('Writing generation $generation assets.' , label: test.name);
764+ _debugPrint ('Writing JS assets to ${outputDirectory .path }' ,
765+ label: test.name);
766+ filesystem! .writeToDisk (outputDirectory.uri, generation: '$generation ' );
768767 return true ;
769768 }
770769
771770 // TODO(nshahan): Refactor into runtime specific implementations.
772- Future <void > runTest (HotReloadTest test, Directory tempDirectory) async {
773- final testOutputStreamController = StreamController <List <int >>();
774- final testOutputBuffer = StringBuffer ();
775- testOutputStreamController.stream
776- .transform (utf8.decoder)
777- .listen (testOutputBuffer.write);
771+ Future <bool > runTest (
772+ HotReloadTest test, Directory tempDirectory, IOSink outputSink) async {
778773 var testPassed = false ;
779774 switch (options.runtime) {
780775 case RuntimePlatforms .d8:
@@ -785,7 +780,7 @@ class HotReloadSuiteRunner {
785780 final d8Suite = (this as D8SuiteRunner )
786781 ..bootstrapJsUri =
787782 tempDirectory.uri.resolve ('generation0/bootstrap.js' )
788- ..outputSink = IOSink (testOutputStreamController.sink) ;
783+ ..outputSink = outputSink ;
789784 await d8Suite.setupTest (
790785 testName: test.name,
791786 scriptDescriptors: filesystem! .scriptDescriptorForBootstrap,
@@ -805,7 +800,7 @@ class HotReloadSuiteRunner {
805800 tempDirectory.uri.resolve ('generation0/bootstrap.js' )
806801 ..bootstrapHtmlUri =
807802 tempDirectory.uri.resolve ('generation0/index.html' )
808- ..outputSink = IOSink (testOutputStreamController.sink) ;
803+ ..outputSink = outputSink ;
809804 await suite.setupTest (
810805 testName: test.name,
811806 scriptDescriptors: filesystem! .scriptDescriptorForBootstrap,
@@ -814,48 +809,9 @@ class HotReloadSuiteRunner {
814809 final exitCode = await suite.runTestOld (testName: test.name);
815810 testPassed = exitCode == 0 ;
816811 case RuntimePlatforms .vm:
817- final firstGenerationDillUri =
818- tempDirectory.uri.resolve ('generation0/${test .name }.dill' );
819- // Start the VM at generation 0.
820- final vmArgs = [
821- '--enable-vm-service=0' , // 0 avoids port collisions.
822- '--disable-service-auth-codes' ,
823- '--disable-dart-dev' ,
824- firstGenerationDillUri.toFilePath (),
825- ];
826- final vm = await Process .start (Platform .executable, vmArgs);
827- _debugPrint (
828- 'Starting VM with command: '
829- '${Platform .executable } ${vmArgs .join (" " )}' ,
830- label: test.name);
831- vm.stdout
832- .transform (utf8.decoder)
833- .transform (LineSplitter ())
834- .listen ((String line) {
835- _debugPrint ('VM stdout: $line ' , label: test.name);
836- testOutputBuffer.writeln (line);
837- });
838- vm.stderr
839- .transform (utf8.decoder)
840- .transform (LineSplitter ())
841- .listen ((String err) {
842- _debugPrint ('VM stderr: $err ' , label: test.name);
843- testOutputBuffer.writeln (err);
844- });
845- _print ('Executing VM test.' , label: test.name);
846- final testTimeoutSeconds = 10 ;
847- final vmExitCode = await vm.exitCode
848- .timeout (Duration (seconds: testTimeoutSeconds), onTimeout: () {
849- final timeoutText =
850- 'Test timed out after $testTimeoutSeconds seconds.' ;
851- _print (timeoutText, label: test.name);
852- testOutputBuffer.writeln (timeoutText);
853- vm.kill ();
854- return 1 ;
855- });
856- testPassed = vmExitCode == 0 ;
812+ throw UnsupportedError ('Now implemented in VMSuiteRunner.' );
857813 }
858- await reportTestOutcome (test.name, testOutputBuffer. toString (), testPassed) ;
814+ return testPassed;
859815 }
860816
861817 /// Reports test results to standard out as well as the output .json file if
@@ -1211,3 +1167,127 @@ class ChromeSuiteRunner extends HotReloadSuiteRunner {
12111167 await super .runSuite (options);
12121168 }
12131169}
1170+
1171+ /// Hot reload test suite runner for behavior specific to the VM.
1172+ class VMSuiteRunner extends HotReloadSuiteRunner {
1173+ VMSuiteRunner (super .options);
1174+
1175+ @override
1176+ Future <bool > compileGeneration (
1177+ HotReloadTest test,
1178+ int generation,
1179+ Directory outputDirectory,
1180+ List <String > updatedFiles,
1181+ HotReloadFrontendServerController controller) async {
1182+ // The first generation calls `compile`, but subsequent ones call
1183+ // `recompile`.
1184+ // Likewise, use the incremental output directory for `recompile` calls.
1185+ // TODO(nshahan): Sending compile/recompile instructions is likely
1186+ // the same across backends and should be shared code.
1187+ String outputDillPath;
1188+ _print ('Compiling generation $generation with the Frontend Server.' ,
1189+ label: test.name);
1190+ CompilerOutput compilerOutput;
1191+ if (generation == 0 ) {
1192+ _debugPrint (
1193+ 'Compiling snapshot entrypoint: $snapshotEntrypointWithScheme ' ,
1194+ label: test.name);
1195+ outputDillPath = outputDillUri.toFilePath ();
1196+ compilerOutput =
1197+ await controller.sendCompile (snapshotEntrypointWithScheme);
1198+ } else {
1199+ _debugPrint (
1200+ 'Recompiling snapshot entrypoint: $snapshotEntrypointWithScheme ' ,
1201+ label: test.name);
1202+ outputDillPath = outputIncrementalDillUri.toFilePath ();
1203+ // TODO(markzipan): Add logic to reject bad compiles.
1204+ compilerOutput = await controller.sendRecompile (
1205+ snapshotEntrypointWithScheme,
1206+ invalidatedFiles: updatedFiles);
1207+ }
1208+ var hasCompileError = false ;
1209+ // Frontend Server reported compile errors. Fail if they weren't
1210+ // expected, and do not run tests.
1211+ if (compilerOutput.errorCount > 0 ) {
1212+ hasCompileError = true ;
1213+ await controller.sendReject ();
1214+ // TODO(markzipan): Determine if 'contains' is good enough to determine
1215+ // compilation error correctness.
1216+ if (test.expectedError != null &&
1217+ compilerOutput.outputText.contains (test.expectedError! )) {
1218+ await reportTestOutcome (
1219+ test.name,
1220+ 'Expected error found during compilation: '
1221+ '${test .expectedError }' ,
1222+ true );
1223+ } else {
1224+ await reportTestOutcome (
1225+ test.name,
1226+ 'Test failed with compile error: ${compilerOutput .outputText }' ,
1227+ false );
1228+ }
1229+ } else {
1230+ controller.sendAccept ();
1231+ }
1232+ // Stop processing further generations if compilation failed.
1233+ if (hasCompileError) return false ;
1234+ _debugPrint (
1235+ 'Frontend Server successfully compiled outputs to: '
1236+ '$outputDillPath ' ,
1237+ label: test.name);
1238+ final dillOutputDir =
1239+ Directory .fromUri (outputDirectory.uri.resolve ('generation$generation ' ));
1240+ dillOutputDir.createSync ();
1241+ final dillOutputUri = dillOutputDir.uri.resolve ('${test .name }.dill' );
1242+ // Write dills their respective generation.
1243+ _print ('Writing generation $generation assets.' , label: test.name);
1244+ _debugPrint ('Writing dill to ${dillOutputUri .toFilePath ()}' ,
1245+ label: test.name);
1246+ File (outputDillPath).copySync (dillOutputUri.toFilePath ());
1247+ return true ;
1248+ }
1249+
1250+ @override
1251+ Future <bool > runTest (
1252+ HotReloadTest test, Directory tempDirectory, IOSink outputSink) async {
1253+ final firstGenerationDillUri =
1254+ tempDirectory.uri.resolve ('generation0/${test .name }.dill' );
1255+ // Start the VM at generation 0.
1256+ final vmArgs = [
1257+ '--enable-vm-service=0' , // 0 avoids port collisions.
1258+ '--disable-service-auth-codes' ,
1259+ '--disable-dart-dev' ,
1260+ firstGenerationDillUri.toFilePath (),
1261+ ];
1262+ _debugPrint (
1263+ 'Starting VM with command: '
1264+ '${Platform .executable } ${vmArgs .join (" " )}' ,
1265+ label: test.name);
1266+ final vm = await Process .start (Platform .executable, vmArgs);
1267+ vm.stdout
1268+ .transform (utf8.decoder)
1269+ .transform (LineSplitter ())
1270+ .listen ((String line) {
1271+ _debugPrint ('VM stdout: $line ' , label: test.name);
1272+ outputSink.writeln (line);
1273+ });
1274+ vm.stderr
1275+ .transform (utf8.decoder)
1276+ .transform (LineSplitter ())
1277+ .listen ((String err) {
1278+ _debugPrint ('VM stderr: $err ' , label: test.name);
1279+ outputSink.writeln (err);
1280+ });
1281+ _print ('Executing VM test.' , label: test.name);
1282+ final testTimeoutSeconds = 10 ;
1283+ final vmExitCode = await vm.exitCode
1284+ .timeout (Duration (seconds: testTimeoutSeconds), onTimeout: () {
1285+ final timeoutText = 'Test timed out after $testTimeoutSeconds seconds.' ;
1286+ _print (timeoutText, label: test.name);
1287+ outputSink.writeln (timeoutText);
1288+ vm.kill ();
1289+ return 1 ;
1290+ });
1291+ return vmExitCode == 0 ;
1292+ }
1293+ }
0 commit comments