Unverified Commit f53b32eb authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

refactor depfile usage and update linux rule (#42487)

parent 315471bf
......@@ -502,7 +502,7 @@ class _BuildInstance {
outputFiles[output.path] = output;
}
for (File input in node.inputs) {
final String resolvedPath = input.resolveSymbolicLinksSync();
final String resolvedPath = input.absolute.path;
if (outputFiles.containsKey(resolvedPath)) {
continue;
}
......@@ -704,7 +704,7 @@ class Node {
/// One or more reasons why a task was invalidated.
///
/// May be empty if the task was skipped.
final Set<InvalidedReason> invalidatedReasons = <InvalidedReason>{};
final Set<InvalidatedReason> invalidatedReasons = <InvalidatedReason>{};
/// Whether this node needs an action performed.
bool get dirty => _dirty;
......@@ -735,7 +735,7 @@ class Node {
if (fileHashStore.currentHashes.containsKey(absolutePath)) {
final String currentHash = fileHashStore.currentHashes[absolutePath];
if (currentHash != previousHash) {
invalidatedReasons.add(InvalidedReason.inputChanged);
invalidatedReasons.add(InvalidatedReason.inputChanged);
_dirty = true;
}
} else {
......@@ -749,13 +749,13 @@ class Node {
// output paths changed.
if (!currentOutputPaths.contains(previousOutput)) {
_dirty = true;
invalidatedReasons.add(InvalidedReason.outputSetChanged);
invalidatedReasons.add(InvalidatedReason.outputSetChanged);
// if this isn't a current output file there is no reason to compute the hash.
continue;
}
final File file = fs.file(previousOutput);
if (!file.existsSync()) {
invalidatedReasons.add(InvalidedReason.outputMissing);
invalidatedReasons.add(InvalidatedReason.outputMissing);
_dirty = true;
continue;
}
......@@ -764,7 +764,7 @@ class Node {
if (fileHashStore.currentHashes.containsKey(absolutePath)) {
final String currentHash = fileHashStore.currentHashes[absolutePath];
if (currentHash != previousHash) {
invalidatedReasons.add(InvalidedReason.outputChanged);
invalidatedReasons.add(InvalidatedReason.outputChanged);
_dirty = true;
}
} else {
......@@ -772,9 +772,14 @@ class Node {
}
}
// If we depend on a file that doesnt exist on disk, kill the build.
// If we depend on a file that doesnt exist on disk, mark the build as
// dirty. if the rule is not correctly specified, this will result in it
// always being rerun.
if (missingInputs.isNotEmpty) {
throw MissingInputException(missingInputs, target.name);
_dirty = true;
final String missingMessage = missingInputs.map((File file) => file.path).join(', ');
printTrace('invalidated build due to missing files: $missingMessage');
invalidatedReasons.add(InvalidatedReason.inputMissing);
}
// If we have files to hash, compute them asynchronously and then
......@@ -782,7 +787,7 @@ class Node {
if (sourcesToHash.isNotEmpty) {
final List<File> dirty = await fileHashStore.hashFiles(sourcesToHash);
if (dirty.isNotEmpty) {
invalidatedReasons.add(InvalidedReason.inputChanged);
invalidatedReasons.add(InvalidatedReason.inputChanged);
_dirty = true;
}
}
......@@ -790,8 +795,12 @@ class Node {
}
}
/// A description of why a task was rerun.
enum InvalidedReason {
/// A description of why a target was rerun.
enum InvalidatedReason {
/// An input file that was expected is missing. This can occur when using
/// depfile dependencies, or if a target is incorrectly specified.
inputMissing,
/// An input file has an updated hash.
inputChanged,
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../base/file_system.dart';
import '../base/platform.dart';
import '../globals.dart';
/// A class for representing depfile formats.
class Depfile {
/// Create a [Depfile] from a list of [input] files and [output] files.
const Depfile(this.inputs, this.outputs);
/// Parse the depfile contents from [file].
///
/// If the syntax is invalid, returns an empty [Depfile].
factory Depfile.parse(File file) {
final String contents = file.readAsStringSync();
final List<String> colonSeparated = contents.split(': ');
if (colonSeparated.length != 2) {
printError('Invalid depfile: ${file.path}');
return const Depfile(<File>[], <File>[]);
}
final List<File> inputs = _processList(colonSeparated[1].trim());
final List<File> outputs = _processList(colonSeparated[0].trim());
return Depfile(inputs, outputs);
}
/// The input files for this depfile.
final List<File> inputs;
/// The output files for this depfile.
final List<File> outputs;
/// Given an [depfile] File, write the depfile contents.
///
/// If either [inputs] or [outputs] is empty, does not write to the file.
void writeToFile(File depfile) {
if (inputs.isEmpty || outputs.isEmpty) {
return;
}
final StringBuffer buffer = StringBuffer();
_writeFilesToBuffer(outputs, buffer);
buffer.write(': ');
_writeFilesToBuffer(inputs, buffer);
depfile.writeAsStringSync(buffer.toString());
}
void _writeFilesToBuffer(List<File> files, StringBuffer buffer) {
for (File outputFile in files) {
if (platform.isWindows) {
// Paths in a depfile have to be escaped on windows.
final String escapedPath = outputFile.path.replaceAll(r'\', r'\\');
buffer.write(' $escapedPath');
} else {
buffer.write(' ${outputFile.path}');
}
}
}
static final RegExp _separatorExpr = RegExp(r'([^\\]) ');
static final RegExp _escapeExpr = RegExp(r'\\(.)');
static List<File> _processList(String rawText) {
return rawText
// Put every file on right-hand side on the separate line
.replaceAllMapped(_separatorExpr, (Match match) => '${match.group(1)}\n')
.split('\n')
// Expand escape sequences, so that '\ ', for example,ß becomes ' '
.map<String>((String path) => path.replaceAllMapped(_escapeExpr, (Match match) => match.group(1)).trim())
.where((String path) => path.isNotEmpty)
// The tool doesn't write duplicates to these lists. This call is an attempt to
// be resillient to the outputs of other tools which write or user edits to depfiles.
.toSet()
.map((String path) => fs.file(path))
.toList();
}
}
......@@ -11,10 +11,22 @@ import '../../build_info.dart';
import '../../devfs.dart';
import '../../globals.dart';
import '../build_system.dart';
import '../depfile.dart';
import '../exceptions.dart';
import 'assets.dart';
import 'dart.dart';
/// The only files/subdirectories we care out.
const List<String> _kLinuxArtifacts = <String>[
'libflutter_linux_glfw.so',
'flutter_export.h',
'flutter_messenger.h',
'flutter_plugin_registrar.h',
'flutter_glfw.h',
'icudtl.dat',
'cpp_client_wrapper_glfw/',
];
/// Copies the Linux desktop embedding files to the copy directory.
class UnpackLinuxDebug extends Target {
const UnpackLinuxDebug();
......@@ -25,17 +37,12 @@ class UnpackLinuxDebug extends Target {
@override
List<Source> get inputs => const <Source>[
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/linux.dart'),
Source.artifact(Artifact.linuxDesktopPath, mode: BuildMode.debug),
Source.depfile('linux_engine_sources.d'),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/libflutter_linux_glfw.so'),
Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/flutter_export.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/flutter_messenger.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/flutter_plugin_registrar.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/flutter_glfw.h'),
Source.pattern('{PROJECT_DIR}/linux/flutter/ephemeral/icudtl.dat'),
Source.depfile('linux_engine_sources.d'),
];
@override
......@@ -44,22 +51,55 @@ class UnpackLinuxDebug extends Target {
@override
Future<void> build(Environment environment) async {
final String basePath = artifacts.getArtifactPath(Artifact.linuxDesktopPath);
for (File input in fs.directory(basePath)
.listSync(recursive: true)
.whereType<File>()) {
final String outputPath = fs.path.join(
environment.projectDir.path,
'linux',
'flutter',
'ephemeral',
fs.path.relative(input.path, from: basePath),
);
final File destinationFile = fs.file(outputPath);
if (!destinationFile.parent.existsSync()) {
destinationFile.parent.createSync(recursive: true);
final List<File> inputs = <File>[];
final List<File> outputs = <File>[];
final String outputPrefix = fs.path.join(
environment.projectDir.path,
'linux',
'flutter',
'ephemeral',
);
// The native linux artifacts are composed of 6 files and a directory (listed above)
// which need to be copied to the target directory.
for (String artifact in _kLinuxArtifacts) {
final String entityPath = fs.path.join(basePath, artifact);
// If this artifact is a file, just copy the source over.
if (fs.isFileSync(entityPath)) {
final String outputPath = fs.path.join(
outputPrefix,
fs.path.relative(entityPath, from: basePath),
);
final File destinationFile = fs.file(outputPath);
if (!destinationFile.parent.existsSync()) {
destinationFile.parent.createSync(recursive: true);
}
final File inputFile = fs.file(entityPath);
inputFile.copySync(destinationFile.path);
inputs.add(inputFile);
outputs.add(destinationFile);
continue;
}
// If the artifact is the directory cpp_client_wrapper, recursively
// copy every file from it.
for (File input in fs.directory(entityPath)
.listSync(recursive: true)
.whereType<File>()) {
final String outputPath = fs.path.join(
outputPrefix,
fs.path.relative(input.path, from: basePath),
);
final File destinationFile = fs.file(outputPath);
if (!destinationFile.parent.existsSync()) {
destinationFile.parent.createSync(recursive: true);
}
final File inputFile = fs.file(input);
inputFile.copySync(destinationFile.path);
inputs.add(inputFile);
outputs.add(destinationFile);
}
fs.file(input).copySync(destinationFile.path);
}
final Depfile depfile = Depfile(inputs, outputs);
depfile.writeToFile(environment.buildDir.childFile('linux_engine_sources.d'));
}
}
......
......@@ -7,15 +7,13 @@ import 'dart:async';
import 'package:meta/meta.dart';
import 'package:pool/pool.dart';
import 'artifacts.dart';
import 'asset.dart';
import 'base/common.dart';
import 'base/file_system.dart';
import 'base/platform.dart';
import 'build_info.dart';
import 'build_system/build_system.dart';
import 'build_system/depfile.dart';
import 'build_system/targets/dart.dart';
import 'compile.dart';
import 'dart/package_map.dart';
import 'devfs.dart';
import 'globals.dart';
......@@ -45,10 +43,6 @@ String getKernelPathForTransformerOptions(
const String defaultPrivateKeyPath = 'privatekey.der';
const String _kKernelKey = 'kernel_blob.bin';
const String _kVMSnapshotData = 'vm_snapshot_data';
const String _kIsolateSnapshotData = 'isolate_snapshot_data';
/// Provides a `build` method that builds the bundle.
class BundleBuilder {
/// Builds the bundle for the given target platform.
......@@ -72,73 +66,29 @@ class BundleBuilder {
List<String> extraGenSnapshotOptions = const <String>[],
List<String> fileSystemRoots,
String fileSystemScheme,
bool shouldBuildWithAssemble = false,
}) async {
mainPath ??= defaultMainPath;
depfilePath ??= defaultDepfilePath;
assetDirPath ??= getAssetBuildDirectory();
packagesPath ??= fs.path.absolute(PackageMap.globalPackagesPath);
applicationKernelFilePath ??= getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation);
final FlutterProject flutterProject = FlutterProject.current();
if (shouldBuildWithAssemble) {
await buildWithAssemble(
buildMode: buildMode ?? BuildMode.debug,
targetPlatform: platform,
mainPath: mainPath,
flutterProject: flutterProject,
outputDir: assetDirPath,
depfilePath: depfilePath,
precompiled: precompiledSnapshot,
);
return;
}
DevFSContent kernelContent;
if (!precompiledSnapshot) {
if ((extraFrontEndOptions != null) && extraFrontEndOptions.isNotEmpty) {
printTrace('Extra front-end options: $extraFrontEndOptions');
}
ensureDirectoryExists(applicationKernelFilePath);
final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
final CompilerOutput compilerOutput = await kernelCompiler.compile(
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode),
mainPath: fs.file(mainPath).absolute.path,
outputFilePath: applicationKernelFilePath,
depFilePath: depfilePath,
buildMode: buildMode,
trackWidgetCreation: trackWidgetCreation,
extraFrontEndOptions: extraFrontEndOptions,
fileSystemRoots: fileSystemRoots,
fileSystemScheme: fileSystemScheme,
packagesPath: packagesPath,
);
if (compilerOutput?.outputFilename == null) {
throwToolExit('Compiler failed on $mainPath');
}
kernelContent = DevFSFileContent(fs.file(compilerOutput.outputFilename));
fs.directory(getBuildDirectory()).childFile('frontend_server.d')
.writeAsStringSync('frontend_server.d: ${artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk)}\n');
}
final AssetBundle assets = await buildAssets(
manifestPath: manifestPath,
assetDirPath: assetDirPath,
packagesPath: packagesPath,
reportLicensedPackages: reportLicensedPackages,
await buildWithAssemble(
buildMode: buildMode ?? BuildMode.debug,
targetPlatform: platform,
mainPath: mainPath,
flutterProject: flutterProject,
outputDir: assetDirPath,
depfilePath: depfilePath,
precompiled: precompiledSnapshot,
);
if (assets == null) {
throwToolExit('Error building assets', exitCode: 1);
// Work around for flutter_tester placing kernel artifacts in odd places.
if (applicationKernelFilePath != null) {
final File outputDill = fs.directory(assetDirPath).childFile('kernel_blob.bin');
if (outputDill.existsSync()) {
outputDill.copySync(applicationKernelFilePath);
}
}
await assemble(
buildMode: buildMode,
assetBundle: assets,
kernelContent: kernelContent,
privateKeyPath: privateKeyPath,
assetDirPath: assetDirPath,
);
return;
}
}
......@@ -178,30 +128,13 @@ Future<void> buildWithAssemble({
}
throwToolExit('Failed to build bundle.');
}
// Output depfile format:
final StringBuffer buffer = StringBuffer();
buffer.write('flutter_bundle');
_writeFilesToBuffer(result.outputFiles, buffer);
buffer.write(': ');
_writeFilesToBuffer(result.inputFiles, buffer);
final File depfile = fs.file(depfilePath);
if (!depfile.parent.existsSync()) {
depfile.parent.createSync(recursive: true);
}
depfile.writeAsStringSync(buffer.toString());
}
void _writeFilesToBuffer(List<File> files, StringBuffer buffer) {
for (File outputFile in files) {
if (platform.isWindows) {
// Paths in a depfile have to be escaped on windows.
final String escapedPath = outputFile.path.replaceAll(r'\', r'\\');
buffer.write(' $escapedPath');
} else {
buffer.write(' ${outputFile.path}');
if (depfilePath != null) {
final Depfile depfile = Depfile(result.inputFiles, result.outputFiles);
final File outputDepfile = fs.file(depfilePath);
if (!outputDepfile.parent.existsSync()) {
outputDepfile.parent.createSync(recursive: true);
}
depfile.writeToFile(outputDepfile);
}
}
......@@ -231,32 +164,6 @@ Future<AssetBundle> buildAssets({
return assetBundle;
}
Future<void> assemble({
BuildMode buildMode,
AssetBundle assetBundle,
DevFSContent kernelContent,
String privateKeyPath = defaultPrivateKeyPath,
String assetDirPath,
}) async {
assetDirPath ??= getAssetBuildDirectory();
printTrace('Building bundle');
final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.from(assetBundle.entries);
if (kernelContent != null) {
final String vmSnapshotData = artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: buildMode);
final String isolateSnapshotData = artifacts.getArtifactPath(Artifact.isolateSnapshotData, mode: buildMode);
assetEntries[_kKernelKey] = kernelContent;
assetEntries[_kVMSnapshotData] = DevFSFileContent(fs.file(vmSnapshotData));
assetEntries[_kIsolateSnapshotData] = DevFSFileContent(fs.file(isolateSnapshotData));
}
printTrace('Writing asset files to $assetDirPath');
ensureDirectoryExists(assetDirPath);
await writeBundle(fs.directory(assetDirPath), assetEntries);
printTrace('Wrote $assetDirPath');
}
Future<void> writeBundle(
Directory bundleDir,
Map<String, DevFSContent> assetEntries,
......
......@@ -138,7 +138,6 @@ class BuildBundleCommand extends BuildSubCommand {
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
fileSystemScheme: argResults['filesystem-scheme'],
fileSystemRoots: argResults['filesystem-root'],
shouldBuildWithAssemble: true,
);
return null;
}
......
......@@ -145,12 +145,13 @@ class FlutterTesterDevice extends Device {
trackWidgetCreation: buildInfo.trackWidgetCreation,
);
await BundleBuilder().build(
buildMode: buildInfo.mode,
mainPath: mainPath,
assetDirPath: assetDirPath,
applicationKernelFilePath: applicationKernelFilePath,
precompiledSnapshot: false,
buildMode: buildInfo.mode,
trackWidgetCreation: buildInfo.trackWidgetCreation,
platform: getTargetPlatformForName(getNameForHostPlatform(getCurrentHostPlatform())),
);
command.add('--flutter-assets-dir=$assetDirPath');
......
......@@ -106,13 +106,12 @@ void main() {
);
});
test('Throws exception if asked to build with missing inputs', () => testbed.run(() async {
test('Does not throw exception if asked to build with missing inputs', () => testbed.run(() async {
// Delete required input file.
fs.file('foo.dart').deleteSync();
final BuildResult buildResult = await buildSystem.build(fooTarget, environment);
expect(buildResult.hasException, true);
expect(buildResult.exceptions.values.single.exception, isInstanceOf<MissingInputException>());
expect(buildResult.hasException, false);
}));
test('Throws exception if it does not produce a specified output', () => testbed.run(() async {
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_system/depfile.dart';
import '../../src/common.dart';
import '../../src/testbed.dart';
void main() {
Testbed testbed;
setUp(() {
testbed = Testbed();
});
test('Can parse depfile from file', () => testbed.run(() {
final File depfileSource = fs.file('example.d')..writeAsStringSync('''
a.txt: b.txt
''');
final Depfile depfile = Depfile.parse(depfileSource);
expect(depfile.inputs.single.path, 'b.txt');
expect(depfile.outputs.single.path, 'a.txt');
}));
test('Can parse depfile with multiple inputs', () => testbed.run(() {
final FileSystem fs = MemoryFileSystem();
final File depfileSource = fs.file('example.d')..writeAsStringSync('''
a.txt: b.txt c.txt d.txt
''');
final Depfile depfile = Depfile.parse(depfileSource);
expect(depfile.inputs.map((File file) => file.path), <String>[
'b.txt',
'c.txt',
'd.txt',
]);
expect(depfile.outputs.single.path, 'a.txt');
}));
test('Can parse depfile with multiple outputs', () => testbed.run(() {
final FileSystem fs = MemoryFileSystem();
final File depfileSource = fs.file('example.d')..writeAsStringSync('''
a.txt c.txt d.txt: b.txt
''');
final Depfile depfile = Depfile.parse(depfileSource);
expect(depfile.inputs.single.path, 'b.txt');
expect(depfile.outputs.map((File file) => file.path), <String>[
'a.txt',
'c.txt',
'd.txt',
]);
}));
test('Can parse depfile with windows file paths', () => testbed.run(() {
final FileSystem fs = MemoryFileSystem();
final File depfileSource = fs.file('example.d')..writeAsStringSync(r'''
C:\\a.txt: C:\\b.txt
''');
final Depfile depfile = Depfile.parse(depfileSource);
expect(depfile.inputs.single.path, r'C:\b.txt');
expect(depfile.outputs.single.path, r'C:\a.txt');
}, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
}));
test('Resillient to weird whitespace', () => testbed.run(() {
final FileSystem fs = MemoryFileSystem();
final File depfileSource = fs.file('example.d')..writeAsStringSync(r'''
a.txt
: b.txt c.txt
''');
final Depfile depfile = Depfile.parse(depfileSource);
expect(depfile.inputs, hasLength(2));
expect(depfile.outputs.single.path, 'a.txt');
}));
test('Resillient to duplicate files', () => testbed.run(() {
final FileSystem fs = MemoryFileSystem();
final File depfileSource = fs.file('example.d')..writeAsStringSync(r'''
a.txt: b.txt b.txt
''');
final Depfile depfile = Depfile.parse(depfileSource);
expect(depfile.inputs.single.path, 'b.txt');
expect(depfile.outputs.single.path, 'a.txt');
}));
test('Resillient to malformed file, missing :', () => testbed.run(() {
final FileSystem fs = MemoryFileSystem();
final File depfileSource = fs.file('example.d')..writeAsStringSync(r'''
a.text b.txt
''');
final Depfile depfile = Depfile.parse(depfileSource);
expect(depfile.inputs, isEmpty);
expect(depfile.outputs, isEmpty);
}));
}
......@@ -39,6 +39,7 @@ void main() {
kBuildMode: 'debug',
}
);
fs.file('bin/cache/artifacts/engine/linux-x64/unrelated-stuff').createSync(recursive: true);
fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux_glfw.so').createSync(recursive: true);
fs.file('bin/cache/artifacts/engine/linux-x64/flutter_export.h').createSync();
fs.file('bin/cache/artifacts/engine/linux-x64/flutter_messenger.h').createSync();
......@@ -53,7 +54,7 @@ void main() {
});
});
test('Copies files to correct cache directory', () => testbed.run(() async {
test('Copies files to correct cache directory, excluding unrelated code', () => testbed.run(() async {
final BuildResult result = await buildSystem.build(const UnpackLinuxDebug(), environment);
expect(result.hasException, false);
......@@ -64,6 +65,7 @@ void main() {
expect(fs.file('linux/flutter/ephemeral/flutter_glfw.h').existsSync(), true);
expect(fs.file('linux/flutter/ephemeral/icudtl.dat').existsSync(), true);
expect(fs.file('linux/flutter/ephemeral/cpp_client_wrapper_glfw/foo').existsSync(), true);
expect(fs.file('linux/flutter/ephemeral/unrelated-stuff').existsSync(), false);
}));
test('Does not re-copy files unecessarily', () => testbed.run(() async {
......
......@@ -43,7 +43,7 @@ void main() {
);
expect(fs.file(fs.path.join('example', 'kernel_blob.bin')).existsSync(), true);
expect(fs.file(fs.path.join('example', 'LICENSE')).existsSync(), true);
expect(fs.file(fs.path.join('example.d')).existsSync(), true);
expect(fs.file(fs.path.join('example.d')).existsSync(), false);
}));
test('Handles build system failure', () => testbed.run(() {
......
......@@ -9,10 +9,8 @@ import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/tester/flutter_tester.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
......@@ -102,24 +100,23 @@ void main() {
String mainPath;
MockArtifacts mockArtifacts;
MockKernelCompiler mockKernelCompiler;
MockProcessManager mockProcessManager;
MockProcess mockProcess;
MockBuildSystem mockBuildSystem;
final Map<Type, Generator> startOverrides = <Type, Generator>{
Platform: () => FakePlatform(operatingSystem: 'linux'),
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
Cache: () => Cache(rootOverride: fs.directory(flutterRoot)),
KernelCompilerFactory: () => FakeKernelCompilerFactory(mockKernelCompiler),
Artifacts: () => mockArtifacts,
BuildSystem: () => mockBuildSystem,
};
setUp(() {
mockBuildSystem = MockBuildSystem();
flutterRoot = fs.path.join('home', 'me', 'flutter');
flutterTesterPath = fs.path.join(flutterRoot, 'bin', 'cache',
'artifacts', 'engine', 'linux-x64', 'flutter_tester');
final File flutterTesterFile = fs.file(flutterTesterPath);
flutterTesterFile.parent.createSync(recursive: true);
flutterTesterFile.writeAsBytesSync(const <int>[]);
......@@ -139,24 +136,23 @@ void main() {
mode: anyNamed('mode')
)).thenReturn(artifactPath);
mockKernelCompiler = MockKernelCompiler();
when(mockBuildSystem.build(
any,
any,
)).thenAnswer((_) async {
fs.file('$mainPath.dill').createSync(recursive: true);
return BuildResult(success: true);
});
});
testUsingContext('not debug', () async {
final LaunchResult result = await device.startApp(null,
mainPath: mainPath,
debuggingOptions: DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null)));
expect(result.started, isFalse);
}, overrides: startOverrides);
testUsingContext('no flutter_tester', () async {
fs.file(flutterTesterPath).deleteSync();
expect(() async {
await device.startApp(null,
mainPath: mainPath,
debuggingOptions: DebuggingOptions.disabled(const BuildInfo(BuildMode.debug, null)));
}, throwsToolExit());
}, overrides: startOverrides);
testUsingContext('start', () async {
final Uri observatoryUri = Uri.parse('http://127.0.0.1:6666/');
......@@ -168,22 +164,6 @@ Hello!
.codeUnits,
]));
when(mockKernelCompiler.compile(
sdkRoot: anyNamed('sdkRoot'),
mainPath: anyNamed('mainPath'),
outputFilePath: anyNamed('outputFilePath'),
depFilePath: anyNamed('depFilePath'),
buildMode: BuildMode.debug,
trackWidgetCreation: anyNamed('trackWidgetCreation'),
extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
fileSystemRoots: anyNamed('fileSystemRoots'),
fileSystemScheme: anyNamed('fileSystemScheme'),
packagesPath: anyNamed('packagesPath'),
)).thenAnswer((_) async {
fs.file('$mainPath.dill').createSync(recursive: true);
return CompilerOutput('$mainPath.dill', 0, <Uri>[]);
});
final LaunchResult result = await device.startApp(null,
mainPath: mainPath,
debuggingOptions: DebuggingOptions.enabled(const BuildInfo(BuildMode.debug, null)));
......@@ -195,15 +175,5 @@ Hello!
});
}
class MockBuildSystem extends Mock implements BuildSystem {}
class MockArtifacts extends Mock implements Artifacts {}
class MockKernelCompiler extends Mock implements KernelCompiler {}
class FakeKernelCompilerFactory implements KernelCompilerFactory {
FakeKernelCompilerFactory(this.kernelCompiler);
final KernelCompiler kernelCompiler;
@override
Future<KernelCompiler> create(FlutterProject flutterProject) async {
return kernelCompiler;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment