[ddc] Adding tests for pkg:reload_test and enabling reload tests in test bots.

* Adds tests for the memory filesystem
* Enables hot restart/reload tests
* Adds flags to the hot reload suite
* Makes path resolution logic windows-friendly
* Adds test-reporting logic for trybots to hot reload tests

Change-Id: Ic51a0b8a3c6f8b6de20b58b2ac185dacf444cf47
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/353788
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Reviewed-by: Nate Bosch <nbosch@google.com>
Commit-Queue: Mark Zhou <markzipan@google.com>
This commit is contained in:
MarkZ
2024-03-25 23:58:26 +00:00
committed by Commit Queue
parent 8e1751f4ce
commit d30283f0a1
11 changed files with 458 additions and 73 deletions
@@ -732,6 +732,8 @@ if (!self.dart_library) {
// DDCLoader, which is unique per-app.
this.root = trimmedDirectory;
this.isWindows = false;
// Optional event handlers.
// Called when modules begin loading.
this.onLoadStart = () => { };
@@ -837,21 +839,24 @@ if (!self.dart_library) {
// Joins path segments from the root directory to [script]'s path to get a
// complete URL.
getScriptUrl(script) {
let pathSlash = this.loadConfig.isWindows ? "\\" : "/";
// Get path segments for src
let splitSrc = script.src.toString().toString().split("/");
let splitSrc = script.src.toString().split(pathSlash);
let j = 0;
// Count number of relative path segments
while (splitSrc[j] == "..") {
j++;
}
// Get path segments for root directory
let splitDir = this.loadConfig.root.split("/");
let splitDir = !this.loadConfig.root
|| this.loadConfig.root == pathSlash ? []
: this.loadConfig.root.split(pathSlash);
// Account for relative path from the root directory
let splitPath = splitDir
.slice(0, splitDir.length - j)
.concat(splitSrc.slice(j));
// Join path segments to get a complete path
return splitPath.join("/");
return splitPath.join(pathSlash);
};
// Adds [script] to the dartLoader's internals as if it had been loaded and
+124 -37
View File
@@ -3,40 +3,65 @@
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:_fe_analyzer_shared/src/util/relativize.dart' as fe_shared;
import 'package:args/args.dart';
import 'package:dev_compiler/dev_compiler.dart' as ddc_names
show libraryUriToJsIdentifier;
import 'package:front_end/src/compute_platform_binaries_location.dart' as fe;
import 'package:reload_test/ddc_helpers.dart' as ddc_helpers;
import 'package:reload_test/frontend_server_controller.dart';
import 'package:reload_test/hot_reload_memory_filesystem.dart';
import 'package:reload_test/test_helpers.dart';
final verbose = true;
final debug = true;
final argParser = ArgParser()
..addOption('named-configuration',
abbr: 'n',
defaultsTo: 'no-configuration',
help: 'configuration name to use for emitting test result files.')
..addOption('output-directory', help: 'Directory to emit test results files.')
..addFlag('debug',
abbr: 'd',
defaultsTo: true,
negatable: true,
help: 'enables additional debug behavior and logging.')
..addFlag('verbose',
abbr: 'v',
defaultsTo: true,
negatable: true,
help: 'enables verbose logging.');
late final bool verbose;
late final bool debug;
/// TODO(markzipan): Add arg parsing for additional execution modes
/// (chrome, VM) and diffs across generations.
Future<void> main(List<String> args) async {
final argResults = argParser.parse(args);
verbose = argResults['verbose'] as bool;
debug = argResults['debug'] as bool;
// Used to communicate individual test failures to our test bots.
final emitTestResultsJson = argResults['output-directory'] != null;
final buildRootUri = fe.computePlatformBinariesLocation(forceBuildDir: true);
// We can use the outline instead of the full SDK dill here.
final ddcPlatformDillUri = buildRootUri.resolve('ddc_outline.dill');
final sdkRoot = Platform.script.resolve('../../../');
final packageConfigUri = sdkRoot.resolve('.dart_tool/package_config.json');
final hotReloadTestUri = sdkRoot.resolve('tests/hot_reload/');
final soundStableDartSdkJsPath =
buildRootUri.resolve('gen/utils/ddc/stable/sdk/ddc/dart_sdk.js').path;
final ddcModuleLoaderJsPath =
sdkRoot.resolve('pkg/dev_compiler/lib/js/ddc/ddc_module_loader.js').path;
final allTestsUri = sdkRoot.resolve('tests/hot_reload/');
final soundStableDartSdkJsUri =
buildRootUri.resolve('gen/utils/ddc/stable/sdk/ddc/dart_sdk.js');
final ddcModuleLoaderJsUri =
sdkRoot.resolve('pkg/dev_compiler/lib/js/ddc/ddc_module_loader.js');
final d8PreamblesUri = sdkRoot
.resolve('sdk/lib/_internal/js_dev_runtime/private/preambles/d8.js');
final sealNativeObjectJsUri = sdkRoot.resolve(
'sdk/lib/_internal/js_runtime/lib/preambles/seal_native_object.js');
final d8BinaryUri = sdkRoot.resolveUri(ddc_helpers.d8executableUri);
final allTestsDir = Directory(hotReloadTestUri.path);
// Contains generated code for all tests.
final generatedCodeDir = Directory.systemTemp.createTempSync();
@@ -57,22 +82,24 @@ Future<void> main(List<String> args) async {
// TODO(markzipan): Support custom entrypoints.
final snapshotEntrypointUri = snapshotUri.resolve('main.dart');
final filesystemRootUri = snapshotDir.uri;
final filesystemRootUri = snapshotUri;
final filesystemScheme = 'hot-reload-test';
final snapshotEntrypointLibraryName = fe_shared.relativizeUri(
filesystemRootUri, snapshotEntrypointUri, fe_shared.isWindows);
final snapshotEntrypointWithScheme =
'$filesystemScheme:///$snapshotEntrypointLibraryName';
final ddcPlatformDillFromSdkRoot =
fe_shared.relativizeUri(sdkRoot, ddcPlatformDillUri, fe_shared.isWindows);
final ddcArgs = [
'--dartdevc-module-format=ddc',
'--incremental',
'--filesystem-root=${snapshotDir.path}',
'--filesystem-root=${snapshotUri.toFilePath()}',
'--filesystem-scheme=$filesystemScheme',
'--output-dill=${outputDillUri.path}',
'--output-incremental-dill=${outputIncrementalDillUri.path}',
'--packages=${packageConfigUri.path}',
'--platform=${ddcPlatformDillUri.path}',
'--sdk-root=${sdkRoot.path}',
'--output-dill=${outputDillUri.toFilePath()}',
'--output-incremental-dill=${outputIncrementalDillUri.toFilePath()}',
'--packages=${packageConfigUri.toFilePath()}',
'--platform=$ddcPlatformDillFromSdkRoot',
'--sdk-root=${sdkRoot.toFilePath()}',
'--target=dartdevc',
'--verbosity=${verbose ? 'all' : 'info'}',
];
@@ -90,22 +117,31 @@ Future<void> main(List<String> args) async {
}
}
for (var testDir in allTestsDir.listSync()) {
final testOutcomes = <TestResultOutcome>[];
for (var testDir in Directory.fromUri(allTestsUri).listSync()) {
if (testDir is! Directory) {
if (testDir is File) {
// Ignore Dart source files, which may be imported as helpers
continue;
}
throw Exception(
'Non-directory or file entity found in ${allTestsDir.path}: $testDir');
'Non-directory or file entity found in ${allTestsUri.toFilePath()}: $testDir');
}
final testDirParts = testDir.uri.pathSegments;
final testName = testDirParts[testDirParts.length - 2];
var outcome = TestResultOutcome(
configuration: argResults['named-configuration'] as String,
testName: testName,
);
var stopwatch = Stopwatch()..start();
final tempUri = generatedCodeUri.resolve('$testName/');
Directory.fromUri(tempUri).createSync();
_print('Generating test assets.', label: testName);
_debugPrint('Emitting JS code to ${tempUri.path}.', label: testName);
_debugPrint('Emitting JS code to ${tempUri.toFilePath()}.',
label: testName);
var filesystem = HotReloadMemoryFilesystem(tempUri);
@@ -129,7 +165,7 @@ Future<void> main(List<String> args) async {
}
// TODO(markzipan): replace this with a test-configurable main entrypoint.
final mainDartFilePath = testDir.uri.resolve('main.dart').path;
final mainDartFilePath = testDir.uri.resolve('main.dart').toFilePath();
_debugPrint('Test entrypoint: $mainDartFilePath', label: testName);
_print('Generating code over ${maxGenerations + 1} generations.',
label: testName);
@@ -144,7 +180,7 @@ Future<void> main(List<String> args) async {
// names restored (e.g., path/to/main' from 'path/to/main.0.dart).
// TODO(markzipan): support subdirectories.
_debugPrint(
'Copying Dart files to snapshot directory: ${snapshotDir.path}',
'Copying Dart files to snapshot directory: ${snapshotUri.toFilePath()}',
label: testName);
for (var file in testDir.listSync()) {
// Convert a name like `/path/foo.bar.25.dart` to `/path/foo.bar.dart`.
@@ -164,7 +200,7 @@ Future<void> main(List<String> args) async {
final snapshotPathWithScheme =
'$filesystemScheme:///$relativeSnapshotPath';
updatedFilesInCurrentGeneration.add(snapshotPathWithScheme);
file.copySync(fileSnapshotUri.path);
file.copySync(fileSnapshotUri.toFilePath());
}
}
}
@@ -184,13 +220,13 @@ Future<void> main(List<String> args) async {
_debugPrint(
'Compiling snapshot entrypoint: $snapshotEntrypointWithScheme',
label: testName);
outputDirectoryPath = outputDillUri.path;
outputDirectoryPath = outputDillUri.toFilePath();
await controller.sendCompileAndAccept(snapshotEntrypointWithScheme);
} else {
_debugPrint(
'Recompiling snapshot entrypoint: $snapshotEntrypointWithScheme',
label: testName);
outputDirectoryPath = outputIncrementalDillUri.path;
outputDirectoryPath = outputIncrementalDillUri.toFilePath();
// TODO(markzipan): Add logic to reject bad compiles.
await controller.sendRecompileAndAccept(snapshotEntrypointWithScheme,
invalidatedFiles: updatedFilesInCurrentGeneration);
@@ -217,7 +253,8 @@ Future<void> main(List<String> args) async {
// Write JS files and sourcemaps to their respective generation.
_print('Writing generation $currentGeneration assets.', label: testName);
_debugPrint('Writing JS assets to ${tempUri.path}', label: testName);
_debugPrint('Writing JS assets to ${tempUri.toFilePath()}',
label: testName);
filesystem.writeToDisk(tempUri, generation: '$currentGeneration');
currentGeneration++;
}
@@ -233,10 +270,10 @@ Future<void> main(List<String> args) async {
final d8BootstrapJsUri = tempUri.resolve('generation0/bootstrap.js');
final d8BootstrapJS = ddc_helpers.generateD8Bootstrapper(
ddcModuleLoaderJsPath: ddcModuleLoaderJsPath,
dartSdkJsPath: soundStableDartSdkJsPath,
entrypointModuleName: entrypointModuleName,
entrypointLibraryExportName: entrypointLibraryExportName,
ddcModuleLoaderJsPath: escapedString(ddcModuleLoaderJsUri.toFilePath()),
dartSdkJsPath: escapedString(soundStableDartSdkJsUri.toFilePath()),
entrypointModuleName: escapedString(entrypointModuleName),
entrypointLibraryExportName: escapedString(entrypointLibraryExportName),
scriptDescriptors: filesystem.scriptDescriptorForBootstrap,
modifiedFilesPerGeneration: filesystem.generationsToModifiedFilePaths,
);
@@ -244,22 +281,72 @@ Future<void> main(List<String> args) async {
File.fromUri(d8BootstrapJsUri).writeAsStringSync(d8BootstrapJS);
_debugPrint('Writing D8 bootstrapper: $d8BootstrapJsUri', label: testName);
var process = await startProcess('D8', d8BinaryUri.path, [
sealNativeObjectJsUri.path,
d8PreamblesUri.path,
d8BootstrapJsUri.path
final d8OutputStreamController = StreamController<List<int>>();
final process = await startProcess('D8', d8BinaryUri.toFilePath(), [
sealNativeObjectJsUri.toFilePath(),
d8PreamblesUri.toFilePath(),
d8BootstrapJsUri.toFilePath()
]);
// Send D8's output to a local buffer so we can write or process it later.
final d8OutputBuffer = StringBuffer();
unawaited(process.stdout.pipe(d8OutputStreamController.sink));
d8OutputStreamController.stream
.transform(utf8.decoder)
.listen(d8OutputBuffer.write);
final d8ExitCode = await process.exitCode;
if (d8ExitCode != 0) {
final testPassed = d8ExitCode == 0;
stopwatch.stop();
outcome.elapsedTime = stopwatch.elapsed;
outcome.matchedExpectations = testPassed;
outcome.testOutput = d8OutputBuffer.toString();
testOutcomes.add(outcome);
if (testPassed) {
_print('D8 passed with:\n$d8OutputBuffer', label: testName);
} else {
_print('TEST FAILURE: D8 exited with:\n$d8OutputBuffer', label: testName);
}
_print(outcome.testOutput, label: testName);
if (!testPassed) {
await shutdown();
exit(d8ExitCode);
}
_print('Test passed in D8.', label: testName);
}
await shutdown();
_print('Testing complete.');
if (emitTestResultsJson) {
final testOutcomeResults = testOutcomes.map((o) => o.toRecordJson());
final testOutcomeLogs = testOutcomes.map((o) => o.toLogJson());
final testResultsOutputDir =
Uri.directory(argResults['output-directory'] as String);
_print('Saving test results to ${testResultsOutputDir.toFilePath()}.');
// Test outputs must have one JSON blob per line and be newline-terminated.
final testResultsUri = testResultsOutputDir.resolve('results.json');
final testResultsSink = File.fromUri(testResultsUri).openWrite();
testOutcomeResults.forEach(testResultsSink.writeln);
await testResultsSink.flush();
await testResultsSink.close();
final testLogsUri = testResultsOutputDir.resolve('logs.json');
if (Platform.isWindows) {
// TODO(55297): Logs are disabled on windows until this but is fixed.
_print('Logs are not written on Windows. '
'See: https://github.com/dart-lang/sdk/issues/55297');
} else {
final testLogsSink = File.fromUri(testLogsUri).openWrite();
testOutcomeLogs.forEach(testLogsSink.writeln);
await testLogsSink.flush();
await testLogsSink.close();
}
_print('Emitted logs to ${testResultsUri.toFilePath()} '
'and ${testLogsUri.toFilePath()}.');
}
}
/// Runs the [command] with [args] in [environment].
@@ -267,7 +354,8 @@ Future<void> main(List<String> args) async {
/// Will echo the commands to the console before running them when running in
/// `verbose` mode.
Future<Process> startProcess(String name, String command, List<String> args,
[Map<String, String> environment = const {}]) {
{Map<String, String> environment = const {},
ProcessStartMode mode = ProcessStartMode.normal}) {
if (verbose) {
print('Running $name:\n$command ${args.join(' ')}\n');
if (environment.isNotEmpty) {
@@ -276,8 +364,7 @@ Future<Process> startProcess(String name, String command, List<String> args,
print('With environment:\n$environmentVariables\n');
}
}
return Process.start(command, args,
mode: ProcessStartMode.inheritStdio, environment: environment);
return Process.start(command, args, mode: mode, environment: environment);
}
/// Prints messages if 'verbose' mode is enabled.
+2 -2
View File
@@ -44,14 +44,13 @@ String generateD8Bootstrapper({
required String ddcModuleLoaderJsPath,
required String dartSdkJsPath,
required String entrypointModuleName,
String jsFileRoot = '/',
String jsFileRoot = '',
String uuid = '00000000-0000-0000-0000-000000000000',
required String entrypointLibraryExportName,
required List<Map<String, String?>> scriptDescriptors,
required Map<String, List<String>> modifiedFilesPerGeneration,
}) {
final d8BootstrapJS = '''
load("$ddcModuleLoaderJsPath");
load("$dartSdkJsPath");
@@ -70,6 +69,7 @@ let sdk = dart_library.import('dart_sdk');
let scripts = ${_encoder.convert(scriptDescriptors)};
let loadConfig = new self.\$dartLoader.LoadConfiguration();
loadConfig.isWindows = ${Platform.isWindows};
loadConfig.root = '$jsFileRoot';
// Loading the entrypoint late is only necessary in Chrome.
loadConfig.bootstrapScript = '';
@@ -77,6 +77,7 @@ class HotReloadFrontendServerController {
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((String s) {
if (debug) print('Frontend Server Response: $s');
if (_boundaryKey == null) {
if (s.startsWith(frontEndResponsePrefix)) {
_boundaryKey = s.substring(frontEndResponsePrefix.length);
@@ -99,13 +100,9 @@ class HotReloadFrontendServerController {
}
Future<void> sendCompile(String dartSourcePath) async {
if (!started) {
throw Exception('Frontend Server has not been started yet.');
}
if (!started) throw Exception('Frontend Server has not been started yet.');
final command = 'compile $dartSourcePath\n';
if (debug) {
print('Sending instruction to Frontend Server:\n$command');
}
if (debug) print('Sending instruction to Frontend Server:\n$command');
input.add(command.codeUnits);
await synchronizer.moveNext();
}
@@ -118,14 +115,10 @@ class HotReloadFrontendServerController {
Future<void> sendRecompile(String entrypointPath,
{List<String> invalidatedFiles = const [],
String boundaryKey = fakeBoundaryKey}) async {
if (!started) {
throw Exception('Frontend Server has not been started yet.');
}
if (!started) throw Exception('Frontend Server has not been started yet.');
final command = 'recompile $entrypointPath $boundaryKey\n'
'${invalidatedFiles.join('\n')}\n$boundaryKey\n';
if (debug) {
print('Sending instruction to Frontend Server:\n$command');
}
if (debug) print('Sending instruction to Frontend Server:\n$command');
input.add(command.codeUnits);
await synchronizer.moveNext();
}
@@ -139,26 +132,18 @@ class HotReloadFrontendServerController {
}
void sendAccept() {
if (!started) {
throw Exception('Frontend Server has not been started yet.');
}
if (!started) throw Exception('Frontend Server has not been started yet.');
final command = 'accept\n';
// TODO(markzipan): We should reject certain invalid compiles (e.g., those
// with unimplemented or invalid nodes).
if (debug) {
print('Sending instruction to Frontend Server:\n$command');
}
if (debug) print('Sending instruction to Frontend Server:\n$command');
input.add(command.codeUnits);
}
void _sendQuit() {
if (!started) {
throw Exception('Frontend Server has not been started yet.');
}
if (!started) throw Exception('Frontend Server has not been started yet.');
final command = 'quit\n';
if (debug) {
print('Sending instruction to Frontend Server:\n$command');
}
if (debug) print('Sending instruction to Frontend Server:\n$command');
input.add(command.codeUnits);
}
@@ -136,7 +136,7 @@ class HotReloadMemoryFilesystem {
moduleName: libraryName,
libraryName: libraryName,
dartSourcePath: dartFileName,
jsSourcePath: fullyResolvedFileUri.path);
jsSourcePath: fullyResolvedFileUri.toFilePath());
libraries.add(libraryInfo);
if (generation == '0') {
firstGenerationLibraries.add(libraryInfo);
+50
View File
@@ -0,0 +1,50 @@
// 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:convert';
class TestResultOutcome {
// This encoder must generate each output element on its own line.
final _encoder = JsonEncoder();
final String configuration;
final String suiteName;
final String testName;
late Duration elapsedTime;
final String expectedResult;
late bool matchedExpectations;
String testOutput;
TestResultOutcome({
required this.configuration,
this.suiteName = 'tests/reload',
required this.testName,
this.expectedResult = 'Pass',
this.testOutput = '',
});
String toRecordJson() => _encoder.convert({
'name': '$suiteName/$testName',
'configuration': configuration,
'suite': suiteName,
'test_name': testName,
'time_ms': elapsedTime.inMilliseconds,
'expected': expectedResult,
'result': matchedExpectations ? 'Pass' : 'Fail',
'matches': expectedResult == expectedResult,
});
String toLogJson() => _encoder.convert({
'name': '$suiteName/$testName',
'configuration': configuration,
'result': matchedExpectations ? 'Pass' : 'Fail',
'log': testOutput,
});
}
/// Escapes backslashes in [unescaped].
///
/// Used for wrapping Windows-style paths.
String escapedString(String unescaped) {
return unescaped.replaceAll(r'\', r'\\');
}
+1
View File
@@ -15,3 +15,4 @@ dependencies:
# Use 'any' constraints here; we get our versions from the DEPS file.
dev_dependencies:
lints: any
test: any
+243
View File
@@ -0,0 +1,243 @@
// 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';
import 'package:test/test.dart';
import 'package:reload_test/hot_reload_memory_filesystem.dart';
void main() {
late Directory testDirectory;
late Uri jsOutputUri;
setUp(() {
testDirectory = Directory.systemTemp.createTempSync('test_file_system');
jsOutputUri = testDirectory.uri.resolve('js/');
Directory.fromUri(jsOutputUri).createSync();
});
tearDown(() {
testDirectory.deleteSync(recursive: true);
});
test("Web filesystem behaves correctly across generations.", () {
// Initialize the filesystem and register two test files with their
// corresponding sourcemaps.
final filesystem = HotReloadMemoryFilesystem(jsOutputUri);
final source1 = '''
file1() {
foo();
}
''';
final source2 = '''
file2() {
bar();
}
''';
final sources = '$source1$source2'.codeUnits;
final sourcesFile = File.fromUri(testDirectory.uri.resolve('test.sources'))
..writeAsBytesSync(sources);
final sourcemap1 = '{map1}';
final sourcemap2 = '{map2}';
final sourcemap = '$sourcemap1$sourcemap2'.codeUnits;
final sourcemapFile = File.fromUri(testDirectory.uri.resolve('test.map'))
..writeAsBytesSync(sourcemap);
final manifest = '''
{
"/file1.ext": {
"code": [0, ${source1.codeUnits.length}],
"sourcemap": [0, ${sourcemap1.codeUnits.length}]
},
"/file2.ext": {
"code": [
${source1.codeUnits.length},
${source1.codeUnits.length + source2.codeUnits.length}
],
"sourcemap":[
${sourcemap1.codeUnits.length},
${sourcemap1.codeUnits.length + sourcemap2.codeUnits.length}
]
}
}
'''
.codeUnits;
final manifestFile = File.fromUri(testDirectory.uri.resolve('test.json'))
..writeAsBytesSync(manifest);
var updatedFiles = filesystem
.update(sourcesFile, manifestFile, sourcemapFile, generation: "0");
expect(updatedFiles, equals(['file1.ext', 'file2.ext']),
reason: 'Updated files are correctly reported.');
expect(
filesystem.files,
equals(
{'file1.ext': source1.codeUnits, 'file2.ext': source2.codeUnits}),
reason: 'Filesystem source files are correctly stored.');
expect(
filesystem.sourcemaps,
equals({
'file1.ext.map': sourcemap1.codeUnits,
'file2.ext.map': sourcemap2.codeUnits,
}),
reason: 'Filesystem sourcemaps are correctly stored.');
expect(
filesystem.generationsToModifiedFilePaths,
equals({
'0': [
jsOutputUri.resolve('generation0/file1.ext').toFilePath(),
jsOutputUri.resolve('generation0/file2.ext').toFilePath()
]
}),
reason:
'Filesystem emits correct generation to modfied files mapping.');
// Update the filesystem with two more files in the next generation.
final manifest2 = '''
{
"/file3.ext": {
"code": [0, ${source1.codeUnits.length}],
"sourcemap": [0, ${sourcemap1.codeUnits.length}]
},
"/file4.ext": {
"code": [
${source1.codeUnits.length},
${source1.codeUnits.length + source2.codeUnits.length}
],
"sourcemap":[
${sourcemap1.codeUnits.length},
${sourcemap1.codeUnits.length + sourcemap2.codeUnits.length}
]
}
}
'''
.codeUnits;
manifestFile.writeAsBytesSync(manifest2);
updatedFiles = filesystem.update(sourcesFile, manifestFile, sourcemapFile,
generation: "1");
expect(updatedFiles, equals(['file3.ext', 'file4.ext']),
reason: 'Updated files are correctly reported.');
expect(
filesystem.files,
equals({
'file1.ext': source1.codeUnits,
'file2.ext': source2.codeUnits,
'file3.ext': source1.codeUnits,
'file4.ext': source2.codeUnits,
}),
reason: 'Filesystem source files are correctly stored.');
expect(
filesystem.sourcemaps,
equals({
'file1.ext.map': sourcemap1.codeUnits,
'file2.ext.map': sourcemap2.codeUnits,
'file3.ext.map': sourcemap1.codeUnits,
'file4.ext.map': sourcemap2.codeUnits,
}),
reason: 'Filesystem sourcemaps are correctly stored.');
expect(
filesystem.generationsToModifiedFilePaths,
equals({
'0': [
jsOutputUri.resolve('generation0/file1.ext').toFilePath(),
jsOutputUri.resolve('generation0/file2.ext').toFilePath()
],
'1': [
jsOutputUri.resolve('generation1/file3.ext').toFilePath(),
jsOutputUri.resolve('generation1/file4.ext').toFilePath(),
],
}),
reason:
'Filesystem emits correct generation to modfied files mapping.');
expect(
filesystem.scriptDescriptorForBootstrap,
equals([
{
'id': 'file1.ext',
'src': jsOutputUri.resolve('generation0/file1.ext').toFilePath(),
},
{
'id': 'file2.ext',
'src': jsOutputUri.resolve('generation0/file2.ext').toFilePath(),
},
]),
reason: 'Filesystem emits correct script descriptors.');
// Write files and check that the filesystem's state is properly cleared.
expect(
File(jsOutputUri.resolve('generation3/file1.ext').toFilePath())
.existsSync(),
isFalse);
expect(
File(jsOutputUri.resolve('generation3/file2.ext').toFilePath())
.existsSync(),
isFalse);
expect(
File(jsOutputUri.resolve('generation3/file3.ext').toFilePath())
.existsSync(),
isFalse);
expect(
File(jsOutputUri.resolve('generation3/file4.ext').toFilePath())
.existsSync(),
isFalse);
filesystem.writeToDisk(jsOutputUri, generation: "3");
expect(
File(jsOutputUri.resolve('generation3/file1.ext').toFilePath())
.existsSync(),
isTrue);
expect(
File(jsOutputUri.resolve('generation3/file2.ext').toFilePath())
.existsSync(),
isTrue);
expect(
File(jsOutputUri.resolve('generation3/file3.ext').toFilePath())
.existsSync(),
isTrue);
expect(
File(jsOutputUri.resolve('generation3/file4.ext').toFilePath())
.existsSync(),
isTrue);
expect(filesystem.files, isEmpty,
reason: 'Filesystem clears files after writing to disk.');
expect(filesystem.sourcemaps, isEmpty,
reason: 'Filesystem clears sourcemaps after writing to disk.');
// Check that subsequent writes don't emit already-emitted files.
File(jsOutputUri.resolve('generation3/file1.ext').toFilePath())
.deleteSync();
File(jsOutputUri.resolve('generation3/file2.ext').toFilePath())
.deleteSync();
File(jsOutputUri.resolve('generation3/file3.ext').toFilePath())
.deleteSync();
File(jsOutputUri.resolve('generation3/file4.ext').toFilePath())
.deleteSync();
filesystem.writeToDisk(jsOutputUri, generation: "3");
expect(
File(jsOutputUri.resolve('generation3/file1.ext').toFilePath())
.existsSync(),
isFalse);
expect(
File(jsOutputUri.resolve('generation3/file2.ext').toFilePath())
.existsSync(),
isFalse);
expect(
File(jsOutputUri.resolve('generation3/file3.ext').toFilePath())
.existsSync(),
isFalse);
expect(
File(jsOutputUri.resolve('generation3/file4.ext').toFilePath())
.existsSync(),
isFalse);
});
}
@@ -2,9 +2,6 @@
// 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:core';
import 'dart:html';
import 'package:expect/expect.dart';
import 'package:reload_test/reload_test_utils.dart';
@@ -2,9 +2,6 @@
// 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:core';
import 'dart:html';
import 'package:expect/expect.dart';
import 'package:reload_test/reload_test_utils.dart';
+20
View File
@@ -1778,6 +1778,16 @@
"--use-sdk"
]
},
{
"name": "ddc hot reload tests",
"script": "out/ReleaseX64/dart-sdk/bin/dart",
"testRunner": true,
"arguments": [
"pkg/dev_compiler/test/hot_reload_suite.dart",
"-nddc-${system}-chrome",
"--verbose"
]
},
{
"name": "ddc sourcemap tests",
"script": "out/ReleaseX64/dart",
@@ -1865,6 +1875,16 @@
"--use-sdk"
]
},
{
"name": "ddc hot reload tests",
"script": "xcodebuild/ReleaseARM64/dart-sdk/bin/dart",
"testRunner": true,
"arguments": [
"pkg/dev_compiler/test/hot_reload_suite.dart",
"-nddc-mac-chrome",
"--verbose"
]
},
{
"name": "ddc sourcemap tests",
"script": "xcodebuild/ReleaseARM64/dart",