Revert "Rollback patch that broke microbenchmarks" (#11641)
* Revert "Fix a typo in the saved certificate error message (#11640)"
This reverts commit bfda885a9de024b61c3279e7848cc91e8c81adc1.
* Revert "Rollback patch that broke microbenchmarks (#11616)"
This reverts commit 70fe6f4c232111d61187ea5c2b511bc4ebb339ca.
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart
index 6b48ea6..f5f7f8a 100644
--- a/packages/flutter_tools/lib/src/base/build.dart
+++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -2,11 +2,48 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'dart:async';
import 'dart:convert' show JSON;
import 'package:crypto/crypto.dart' show md5;
+import 'package:meta/meta.dart';
+import '../artifacts.dart';
+import '../build_info.dart';
+import '../globals.dart';
+import 'context.dart';
import 'file_system.dart';
+import 'process.dart';
+
+GenSnapshot get genSnapshot => context.putIfAbsent(GenSnapshot, () => const GenSnapshot());
+
+class GenSnapshot {
+ const GenSnapshot();
+
+ Future<int> run({
+ @required TargetPlatform targetPlatform,
+ @required BuildMode buildMode,
+ @required String packagesPath,
+ @required String depfilePath,
+ Iterable<String> additionalArgs: const <String>[],
+ }) {
+ final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData);
+ final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData);
+ final List<String> args = <String>[
+ '--assert_initializer',
+ '--await_is_keyword',
+ '--causal_async_stacks',
+ '--vm_snapshot_data=$vmSnapshotData',
+ '--isolate_snapshot_data=$isolateSnapshotData',
+ '--packages=$packagesPath',
+ '--dependencies=$depfilePath',
+ '--print_snapshot_sizes',
+ ]..addAll(additionalArgs);
+
+ final String snapshotterPath = artifacts.getArtifactPath(Artifact.genSnapshot, targetPlatform, buildMode);
+ return runCommandAndStreamOutput(<String>[snapshotterPath]..addAll(args));
+ }
+}
/// A collection of checksums for a set of input files.
///
@@ -42,3 +79,134 @@
@override
int get hashCode => _checksums.hashCode;
}
+
+final RegExp _separatorExpr = new RegExp(r'([^\\]) ');
+final RegExp _escapeExpr = new RegExp(r'\\(.)');
+
+/// Parses a VM snapshot dependency file.
+///
+/// Snapshot dependency files are a single line mapping the output snapshot to a
+/// space-separated list of input files used to generate that output. Spaces and
+/// backslashes are escaped with a backslash. e.g,
+///
+/// outfile : file1.dart fil\\e2.dart fil\ e3.dart
+///
+/// will return a set containing: 'file1.dart', 'fil\e2.dart', 'fil e3.dart'.
+Future<Set<String>> readDepfile(String depfilePath) async {
+ // Depfile format:
+ // outfile1 outfile2 : file1.dart file2.dart file3.dart
+ final String contents = await fs.file(depfilePath).readAsString();
+ final String dependencies = contents.split(': ')[1];
+ return dependencies
+ .replaceAllMapped(_separatorExpr, (Match match) => '${match.group(1)}\n')
+ .split('\n')
+ .map((String path) => path.replaceAllMapped(_escapeExpr, (Match match) => match.group(1)).trim())
+ .where((String path) => path.isNotEmpty)
+ .toSet();
+}
+
+/// Dart snapshot builder.
+///
+/// Builds Dart snapshots in one of three modes:
+/// * Script snapshot: architecture-independent snapshot of a Dart script core
+/// libraries.
+/// * AOT snapshot: architecture-specific ahead-of-time compiled snapshot
+/// suitable for loading with `mmap`.
+/// * Assembly AOT snapshot: architecture-specific ahead-of-time compile to
+/// assembly suitable for compilation as a static or dynamic library.
+class Snapshotter {
+ /// Builds an architecture-independent snapshot of the specified script.
+ Future<int> buildScriptSnapshot({
+ @required String mainPath,
+ @required String snapshotPath,
+ @required String depfilePath,
+ @required String packagesPath
+ }) async {
+ final List<String> args = <String>[
+ '--snapshot_kind=script',
+ '--script_snapshot=$snapshotPath',
+ mainPath,
+ ];
+
+ final String checksumsPath = '$depfilePath.checksums';
+ final int exitCode = await _build(
+ outputSnapshotPath: snapshotPath,
+ packagesPath: packagesPath,
+ snapshotArgs: args,
+ depfilePath: depfilePath,
+ mainPath: mainPath,
+ checksumsPath: checksumsPath,
+ );
+ if (exitCode != 0)
+ return exitCode;
+ await _writeChecksum(snapshotPath, depfilePath, mainPath, checksumsPath);
+ return exitCode;
+ }
+
+ /// Builds an architecture-specific ahead-of-time compiled snapshot of the specified script.
+ Future<Null> buildAotSnapshot() async {
+ throw new UnimplementedError('AOT snapshotting not yet implemented');
+ }
+
+ Future<int> _build({
+ @required List<String> snapshotArgs,
+ @required String outputSnapshotPath,
+ @required String packagesPath,
+ @required String depfilePath,
+ @required String mainPath,
+ @required String checksumsPath,
+ }) async {
+ if (!await _isBuildRequired(outputSnapshotPath, depfilePath, mainPath, checksumsPath)) {
+ printTrace('Skipping snapshot build. Checksums match.');
+ return 0;
+ }
+
+ // Build the snapshot.
+ final int exitCode = await genSnapshot.run(
+ targetPlatform: null,
+ buildMode: BuildMode.debug,
+ packagesPath: packagesPath,
+ depfilePath: depfilePath,
+ additionalArgs: snapshotArgs,
+ );
+ if (exitCode != 0)
+ return exitCode;
+
+ _writeChecksum(outputSnapshotPath, depfilePath, mainPath, checksumsPath);
+ return 0;
+ }
+
+ Future<bool> _isBuildRequired(String outputSnapshotPath, String depfilePath, String mainPath, String checksumsPath) async {
+ final File checksumFile = fs.file(checksumsPath);
+ final File outputSnapshotFile = fs.file(outputSnapshotPath);
+ final File depfile = fs.file(depfilePath);
+ if (!outputSnapshotFile.existsSync() || !depfile.existsSync() || !checksumFile.existsSync())
+ return true;
+
+ try {
+ if (checksumFile.existsSync()) {
+ final Checksum oldChecksum = new Checksum.fromJson(await checksumFile.readAsString());
+ final Set<String> checksumPaths = await readDepfile(depfilePath)
+ ..addAll(<String>[outputSnapshotPath, mainPath]);
+ final Checksum newChecksum = new Checksum.fromFiles(checksumPaths);
+ return oldChecksum != newChecksum;
+ }
+ } catch (e, s) {
+ // Log exception and continue, this step is a performance improvement only.
+ printTrace('Error during snapshot checksum output: $e\n$s');
+ }
+ return true;
+ }
+
+ Future<Null> _writeChecksum(String outputSnapshotPath, String depfilePath, String mainPath, String checksumsPath) async {
+ try {
+ final Set<String> checksumPaths = await readDepfile(depfilePath)
+ ..addAll(<String>[outputSnapshotPath, mainPath]);
+ final Checksum checksum = new Checksum.fromFiles(checksumPaths);
+ await fs.file(checksumsPath).writeAsString(checksum.toJson());
+ } catch (e, s) {
+ // Log exception and continue, this step is a performance improvement only.
+ print('Error during snapshot checksum output: $e\n$s');
+ }
+ }
+}
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart
index f042441..9414cf0 100644
--- a/packages/flutter_tools/lib/src/commands/build_aot.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -5,6 +5,7 @@
import 'dart:async';
import '../artifacts.dart';
+import '../base/build.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
@@ -149,13 +150,16 @@
final String uiPath = fs.path.join(skyEnginePkg, 'lib', 'ui', 'ui.dart');
final String vmServicePath = fs.path.join(skyEnginePkg, 'sdk_ext', 'vmservice_io.dart');
- final List<String> filePaths = <String>[
+ final List<String> inputPaths = <String>[
vmEntryPoints,
ioEntryPoints,
uiPath,
vmServicePath,
+ mainPath,
];
+ final Set<String> outputPaths = new Set<String>();
+
// These paths are used only on iOS.
String snapshotDartIOS;
String assembly;
@@ -164,13 +168,15 @@
case TargetPlatform.android_arm:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
+ outputPaths.addAll(<String>[
+ vmSnapshotData,
+ isolateSnapshotData,
+ ]);
break;
case TargetPlatform.ios:
snapshotDartIOS = artifacts.getArtifactPath(Artifact.snapshotDart, platform, buildMode);
assembly = fs.path.join(outputDir.path, 'snapshot_assembly.S');
- filePaths.addAll(<String>[
- snapshotDartIOS,
- ]);
+ inputPaths.add(snapshotDartIOS);
break;
case TargetPlatform.darwin_x64:
case TargetPlatform.linux_x64:
@@ -179,9 +185,9 @@
assert(false);
}
- final List<String> missingFiles = filePaths.where((String p) => !fs.isFileSync(p)).toList();
- if (missingFiles.isNotEmpty) {
- printError('Missing files: $missingFiles');
+ final Iterable<String> missingInputs = inputPaths.where((String p) => !fs.isFileSync(p));
+ if (missingInputs.isNotEmpty) {
+ printError('Missing input files: $missingInputs');
return null;
}
if (!processManager.canRun(genSnapshot)) {
@@ -208,6 +214,17 @@
genSnapshotCmd.add('--embedder_entry_points_manifest=$ioEntryPoints');
}
+ // iOS symbols used to load snapshot data in the engine.
+ const String kVmSnapshotData = 'kDartVmSnapshotData';
+ const String kIsolateSnapshotData = 'kDartIsolateSnapshotData';
+
+ // iOS snapshot generated files, compiled object files.
+ final String kVmSnapshotDataC = fs.path.join(outputDir.path, '$kVmSnapshotData.c');
+ final String kIsolateSnapshotDataC = fs.path.join(outputDir.path, '$kIsolateSnapshotData.c');
+ final String kVmSnapshotDataO = fs.path.join(outputDir.path, '$kVmSnapshotData.o');
+ final String kIsolateSnapshotDataO = fs.path.join(outputDir.path, '$kIsolateSnapshotData.o');
+ final String assemblyO = fs.path.join(outputDir.path, 'snapshot_assembly.o');
+
switch (platform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_x64:
@@ -224,9 +241,14 @@
if (interpreter) {
genSnapshotCmd.add('--snapshot_kind=core');
genSnapshotCmd.add(snapshotDartIOS);
+ outputPaths.addAll(<String>[
+ kVmSnapshotDataO,
+ kIsolateSnapshotDataO,
+ ]);
} else {
genSnapshotCmd.add('--snapshot_kind=app-aot-assembly');
genSnapshotCmd.add('--assembly=$assembly');
+ outputPaths.add(assemblyO);
}
break;
case TargetPlatform.darwin_x64:
@@ -245,6 +267,27 @@
genSnapshotCmd.add(mainPath);
+ final File checksumFile = fs.file('$dependencies.checksum');
+ final List<File> checksumFiles = <File>[checksumFile, fs.file(dependencies)]
+ ..addAll(inputPaths.map(fs.file))
+ ..addAll(outputPaths.map(fs.file));
+ if (checksumFiles.every((File file) => file.existsSync())) {
+ try {
+ final String json = await checksumFile.readAsString();
+ final Checksum oldChecksum = new Checksum.fromJson(json);
+ final Set<String> snapshotInputPaths = await readDepfile(dependencies);
+ snapshotInputPaths.addAll(outputPaths);
+ final Checksum newChecksum = new Checksum.fromFiles(snapshotInputPaths);
+ if (oldChecksum == newChecksum) {
+ printTrace('Skipping AOT snapshot build. Checksums match.');
+ return outputPath;
+ }
+ } catch (e, s) {
+ // Log exception and continue, this step is a performance improvement only.
+ printTrace('Error during AOT snapshot checksum check: $e\n$s');
+ }
+ }
+
final RunResult results = await runAsync(genSnapshotCmd);
if (results.exitCode != 0) {
printError('Dart snapshot generator failed with exit code ${results.exitCode}');
@@ -261,16 +304,6 @@
if (platform == TargetPlatform.ios) {
printStatus('Building App.framework...');
- // These names are known to from the engine.
- const String kVmSnapshotData = 'kDartVmSnapshotData';
- const String kIsolateSnapshotData = 'kDartIsolateSnapshotData';
-
- final String kVmSnapshotDataC = fs.path.join(outputDir.path, '$kVmSnapshotData.c');
- final String kIsolateSnapshotDataC = fs.path.join(outputDir.path, '$kIsolateSnapshotData.c');
- final String kVmSnapshotDataO = fs.path.join(outputDir.path, '$kVmSnapshotData.o');
- final String kIsolateSnapshotDataO = fs.path.join(outputDir.path, '$kIsolateSnapshotData.o');
- final String assemblyO = fs.path.join(outputDir.path, 'snapshot_assembly.o');
-
final List<String> commonBuildOptions = <String>['-arch', 'arm64', '-miphoneos-version-min=8.0'];
if (interpreter) {
@@ -317,5 +350,16 @@
await runCheckedAsync(linkCommand);
}
+ // Compute and record checksums.
+ try {
+ final Set<String> snapshotInputPaths = await readDepfile(dependencies);
+ snapshotInputPaths..addAll(outputPaths);
+ final Checksum checksum = new Checksum.fromFiles(snapshotInputPaths);
+ await checksumFile.writeAsString(checksum.toJson());
+ } catch (e, s) {
+ // Log exception and continue, this step is a performance improvement only.
+ printTrace('Error during AOT snapshot checksum output: $e\n$s');
+ }
+
return outputPath;
}
diff --git a/packages/flutter_tools/lib/src/flx.dart b/packages/flutter_tools/lib/src/flx.dart
index 996b302..2d9abf2 100644
--- a/packages/flutter_tools/lib/src/flx.dart
+++ b/packages/flutter_tools/lib/src/flx.dart
@@ -4,14 +4,10 @@
import 'dart:async';
-import 'package:meta/meta.dart' show required;
-
-import 'artifacts.dart';
import 'asset.dart';
import 'base/build.dart';
import 'base/common.dart';
import 'base/file_system.dart';
-import 'base/process.dart';
import 'build_info.dart';
import 'dart/package_map.dart';
import 'devfs.dart';
@@ -30,90 +26,6 @@
const String _kSnapshotKey = 'snapshot_blob.bin';
const String _kDylibKey = 'libapp.so';
-Future<int> _createSnapshot({
- @required String mainPath,
- @required String snapshotPath,
- @required String depfilePath,
- @required String packages
-}) async {
- assert(mainPath != null);
- assert(snapshotPath != null);
- assert(depfilePath != null);
- assert(packages != null);
- final String snapshotterPath = artifacts.getArtifactPath(Artifact.genSnapshot, null, BuildMode.debug);
- final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData);
- final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData);
-
- final List<String> args = <String>[
- snapshotterPath,
- '--snapshot_kind=script',
- '--vm_snapshot_data=$vmSnapshotData',
- '--isolate_snapshot_data=$isolateSnapshotData',
- '--packages=$packages',
- '--script_snapshot=$snapshotPath',
- '--dependencies=$depfilePath',
- mainPath,
- ];
-
- // Write the depfile path to disk.
- fs.file(depfilePath).parent.childFile('gen_snapshot.d').writeAsString('$depfilePath: $snapshotterPath\n');
-
- final File checksumFile = fs.file('$depfilePath.checksums');
- final File snapshotFile = fs.file(snapshotPath);
- final File depfile = fs.file(depfilePath);
- if (snapshotFile.existsSync() && depfile.existsSync() && checksumFile.existsSync()) {
- try {
- final String json = await checksumFile.readAsString();
- final Checksum oldChecksum = new Checksum.fromJson(json);
- final Set<String> inputPaths = await _readDepfile(depfilePath);
- inputPaths.add(snapshotPath);
- final Checksum newChecksum = new Checksum.fromFiles(inputPaths);
- if (oldChecksum == newChecksum) {
- printTrace('Skipping snapshot build. Checksums match.');
- return 0;
- }
- } catch (e, s) {
- // Log exception and continue, this step is a performance improvement only.
- printTrace('Error during snapshot checksum check: $e\n$s');
- }
- }
-
- // Build the snapshot.
- final int exitCode = await runCommandAndStreamOutput(args);
- if (exitCode != 0)
- return exitCode;
-
- // Compute and record input file checksums.
- try {
- final Set<String> inputPaths = await _readDepfile(depfilePath);
- inputPaths.add(snapshotPath);
- final Checksum checksum = new Checksum.fromFiles(inputPaths);
- await checksumFile.writeAsString(checksum.toJson());
- } catch (e, s) {
- // Log exception and continue, this step is a performance improvement only.
- printTrace('Error during snapshot checksum output: $e\n$s');
- }
- return 0;
-}
-
-/// Parses a VM snapshot dependency file.
-///
-/// Snapshot dependency files are a single line mapping the output snapshot to a
-/// space-separated list of input files used to generate that output. e.g,
-///
-/// outfile : file1.dart file2.dart file3.dart
-Future<Set<String>> _readDepfile(String depfilePath) async {
- // Depfile format:
- // outfile : file1.dart file2.dart file3.dart
- final String contents = await fs.file(depfilePath).readAsString();
- final String dependencies = contents.split(': ')[1];
- return dependencies
- .split(' ')
- .map((String path) => path.trim())
- .where((String path) => path.isNotEmpty)
- .toSet();
-}
-
Future<Null> build({
String mainPath: defaultMainPath,
String manifestPath: defaultManifestPath,
@@ -139,11 +51,12 @@
// In a precompiled snapshot, the instruction buffer contains script
// content equivalents
- final int result = await _createSnapshot(
+ final Snapshotter snapshotter = new Snapshotter();
+ final int result = await snapshotter.buildScriptSnapshot(
mainPath: mainPath,
snapshotPath: snapshotPath,
depfilePath: depfilePath,
- packages: packagesPath
+ packagesPath: packagesPath,
);
if (result != 0)
throwToolExit('Failed to run the Flutter compiler. Exit code: $result', exitCode: result);
diff --git a/packages/flutter_tools/test/base/build_test.dart b/packages/flutter_tools/test/base/build_test.dart
index ae1682a..c80a869 100644
--- a/packages/flutter_tools/test/base/build_test.dart
+++ b/packages/flutter_tools/test/base/build_test.dart
@@ -2,13 +2,54 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'dart:async';
+import 'dart:convert' show JSON;
+
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/build.dart';
+import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/build_info.dart';
import 'package:test/test.dart';
import '../src/context.dart';
+class _FakeGenSnapshot implements GenSnapshot {
+ _FakeGenSnapshot({
+ this.succeed: true,
+ this.snapshotPath: 'output.snapshot',
+ this.snapshotContent: '',
+ this.depfilePath: 'output.snapshot.d',
+ this.depfileContent: 'output.snapshot.d : main.dart',
+ });
+
+ final bool succeed;
+ final String snapshotPath;
+ final String snapshotContent;
+ final String depfilePath;
+ final String depfileContent;
+ int _callCount = 0;
+
+ int get callCount => _callCount;
+
+ @override
+ Future<int> run({
+ TargetPlatform targetPlatform,
+ BuildMode buildMode,
+ String packagesPath,
+ String depfilePath,
+ Iterable<String> additionalArgs,
+ }) async {
+ _callCount += 1;
+
+ if (!succeed)
+ return 1;
+ await fs.file(snapshotPath).writeAsString(snapshotContent);
+ await fs.file(depfilePath).writeAsString(depfileContent);
+ return 0;
+ }
+}
+
void main() {
group('Checksum', () {
group('fromFiles', () {
@@ -64,4 +105,185 @@
});
});
});
+
+ group('readDepfile', () {
+ MemoryFileSystem fs;
+
+ setUp(() {
+ fs = new MemoryFileSystem();
+ });
+
+ testUsingContext('returns one file if only one is listed', () async {
+ await fs.file('a.d').writeAsString('snapshot.d: /foo/a.dart');
+ expect(await readDepfile('a.d'), unorderedEquals(<String>['/foo/a.dart']));
+ }, overrides: <Type, Generator>{ FileSystem: () => fs });
+
+ testUsingContext('returns multiple files', () async {
+ await fs.file('a.d').writeAsString('snapshot.d: /foo/a.dart /foo/b.dart');
+ expect(await readDepfile('a.d'), unorderedEquals(<String>[
+ '/foo/a.dart',
+ '/foo/b.dart',
+ ]));
+ }, overrides: <Type, Generator>{ FileSystem: () => fs});
+
+ testUsingContext('trims extra spaces between files', () async {
+ await fs.file('a.d').writeAsString('snapshot.d: /foo/a.dart /foo/b.dart /foo/c.dart');
+ expect(await readDepfile('a.d'), unorderedEquals(<String>[
+ '/foo/a.dart',
+ '/foo/b.dart',
+ '/foo/c.dart',
+ ]));
+ }, overrides: <Type, Generator>{ FileSystem: () => fs });
+
+ testUsingContext('returns files with spaces and backslashes', () async {
+ await fs.file('a.d').writeAsString(r'snapshot.d: /foo/a\ a.dart /foo/b\\b.dart /foo/c\\ c.dart');
+ expect(await readDepfile('a.d'), unorderedEquals(<String>[
+ r'/foo/a a.dart',
+ r'/foo/b\b.dart',
+ r'/foo/c\ c.dart',
+ ]));
+ }, overrides: <Type, Generator>{ FileSystem: () => fs });
+ });
+
+ group('Snapshotter', () {
+ _FakeGenSnapshot genSnapshot;
+ MemoryFileSystem fs;
+ Snapshotter snapshotter;
+
+ setUp(() {
+ fs = new MemoryFileSystem();
+ genSnapshot = new _FakeGenSnapshot();
+ snapshotter = new Snapshotter();
+ });
+
+ testUsingContext('builds snapshot and checksums when no checksums are present', () async {
+ await fs.file('main.dart').writeAsString('void main() {}');
+ await fs.file('output.snapshot').create();
+ await fs.file('output.snapshot.d').writeAsString('snapshot : main.dart');
+ await snapshotter.buildScriptSnapshot(
+ mainPath: 'main.dart',
+ snapshotPath: 'output.snapshot',
+ depfilePath: 'output.snapshot.d',
+ packagesPath: '.packages',
+ );
+
+ expect(genSnapshot.callCount, 1);
+
+ final Map<String, dynamic> json = JSON.decode(await fs.file('output.snapshot.d.checksums').readAsString());
+ expect(json, hasLength(2));
+ expect(json['main.dart'], '27f5ebf0f8c559b2af9419d190299a5e');
+ expect(json['output.snapshot'], 'd41d8cd98f00b204e9800998ecf8427e');
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fs,
+ GenSnapshot: () => genSnapshot,
+ });
+
+ testUsingContext('builds snapshot and checksums when checksums differ', () async {
+ await fs.file('main.dart').writeAsString('void main() {}');
+ await fs.file('output.snapshot').create();
+ await fs.file('output.snapshot.d').writeAsString('output.snapshot : main.dart');
+ await fs.file('output.snapshot.d.checksums').writeAsString(JSON.encode(<String, dynamic>{
+ 'main.dart': '27f5ebf0f8c559b2af9419d190299a5e',
+ 'output.snapshot': 'deadbeef01234567890abcdef0123456',
+ }));
+ await snapshotter.buildScriptSnapshot(
+ mainPath: 'main.dart',
+ snapshotPath: 'output.snapshot',
+ depfilePath: 'output.snapshot.d',
+ packagesPath: '.packages',
+ );
+
+ expect(genSnapshot.callCount, 1);
+
+ final Map<String, dynamic> json = JSON.decode(await fs.file('output.snapshot.d.checksums').readAsString());
+ expect(json, hasLength(2));
+ expect(json['main.dart'], '27f5ebf0f8c559b2af9419d190299a5e');
+ expect(json['output.snapshot'], 'd41d8cd98f00b204e9800998ecf8427e');
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fs,
+ GenSnapshot: () => genSnapshot,
+ });
+
+ testUsingContext('builds snapshot and checksums when checksums match but previous snapshot not present', () async {
+ await fs.file('main.dart').writeAsString('void main() {}');
+ await fs.file('output.snapshot.d').writeAsString('output.snapshot : main.dart');
+ await fs.file('output.snapshot.d.checksums').writeAsString(JSON.encode(<String, dynamic>{
+ 'main.dart': '27f5ebf0f8c559b2af9419d190299a5e',
+ 'output.snapshot': 'd41d8cd98f00b204e9800998ecf8427e',
+ }));
+ await snapshotter.buildScriptSnapshot(
+ mainPath: 'main.dart',
+ snapshotPath: 'output.snapshot',
+ depfilePath: 'output.snapshot.d',
+ packagesPath: '.packages',
+ );
+
+ expect(genSnapshot.callCount, 1);
+
+ final Map<String, dynamic> json = JSON.decode(await fs.file('output.snapshot.d.checksums').readAsString());
+ expect(json, hasLength(2));
+ expect(json['main.dart'], '27f5ebf0f8c559b2af9419d190299a5e');
+ expect(json['output.snapshot'], 'd41d8cd98f00b204e9800998ecf8427e');
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fs,
+ GenSnapshot: () => genSnapshot,
+ });
+
+ testUsingContext('builds snapshot and checksums when main entry point changes', () async {
+ final _FakeGenSnapshot genSnapshot = new _FakeGenSnapshot(
+ snapshotPath: 'output.snapshot',
+ depfilePath: 'output.snapshot.d',
+ depfileContent: 'output.snapshot : other.dart',
+ );
+ context.setVariable(GenSnapshot, genSnapshot);
+
+ await fs.file('main.dart').writeAsString('void main() {}');
+ await fs.file('other.dart').writeAsString('void main() { print("Kanpai ima kimi wa jinsei no ookina ookina butai ni tachi"); }');
+ await fs.file('output.snapshot.d').writeAsString('output.snapshot : main.dart');
+ await fs.file('output.snapshot.d.checksums').writeAsString(JSON.encode(<String, dynamic>{
+ 'main.dart': '27f5ebf0f8c559b2af9419d190299a5e',
+ 'output.snapshot': 'd41d8cd98f00b204e9800998ecf8427e',
+ }));
+ await snapshotter.buildScriptSnapshot(
+ mainPath: 'other.dart',
+ snapshotPath: 'output.snapshot',
+ depfilePath: 'output.snapshot.d',
+ packagesPath: '.packages',
+ );
+
+ expect(genSnapshot.callCount, 1);
+ final Map<String, dynamic> json = JSON.decode(await fs.file('output.snapshot.d.checksums').readAsString());
+ expect(json, hasLength(2));
+ expect(json['other.dart'], '3238d0ae341339b1731d3c2e195ad177');
+ expect(json['output.snapshot'], 'd41d8cd98f00b204e9800998ecf8427e');
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fs,
+ });
+
+ testUsingContext('skips snapshot when checksums match and previous snapshot is present', () async {
+ await fs.file('main.dart').writeAsString('void main() {}');
+ await fs.file('output.snapshot').create();
+ await fs.file('output.snapshot.d').writeAsString('output.snapshot : main.dart');
+ await fs.file('output.snapshot.d.checksums').writeAsString(JSON.encode(<String, dynamic>{
+ 'main.dart': '27f5ebf0f8c559b2af9419d190299a5e',
+ 'output.snapshot': 'd41d8cd98f00b204e9800998ecf8427e',
+ }));
+ await snapshotter.buildScriptSnapshot(
+ mainPath: 'main.dart',
+ snapshotPath: 'output.snapshot',
+ depfilePath: 'output.snapshot.d',
+ packagesPath: '.packages',
+ );
+
+ expect(genSnapshot.callCount, 0);
+
+ final Map<String, dynamic> json = JSON.decode(await fs.file('output.snapshot.d.checksums').readAsString());
+ expect(json, hasLength(2));
+ expect(json['main.dart'], '27f5ebf0f8c559b2af9419d190299a5e');
+ expect(json['output.snapshot'], 'd41d8cd98f00b204e9800998ecf8427e');
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fs,
+ GenSnapshot: () => genSnapshot,
+ });
+ });
}