From 72343ee0e4593ff33ebfc8c09ada9eff654a7ee6 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 30 Mar 2020 12:51:04 -0700 Subject: [PATCH] [flutter_tools] refactor build_system and targets to be context-free (#53268) --- packages/flutter_tools/lib/src/aot.dart | 6 +- .../flutter_tools/lib/src/base/logger.dart | 2 +- .../lib/src/build_system/build_system.dart | 87 +++++- .../lib/src/build_system/source.dart | 47 +-- .../lib/src/build_system/targets/windows.dart | 16 +- packages/flutter_tools/lib/src/bundle.dart | 4 + .../lib/src/commands/assemble.dart | 4 + .../flutter_tools/lib/src/context_runner.dart | 6 +- .../flutter_tools/lib/src/web/compile.dart | 4 + .../build_system/build_system_test.dart | 293 ++++++++++-------- .../build_system/filecache_test.dart | 8 + .../build_system/source_test.dart | 6 + .../build_system/targets/android_test.dart | 55 +++- .../build_system/targets/assets_test.dart | 9 + .../build_system/targets/dart_test.dart | 12 + .../targets/icon_tree_shaker_test.dart | 4 + .../build_system/targets/ios_test.dart | 13 +- .../build_system/targets/linux_test.dart | 16 +- .../build_system/targets/macos_test.dart | 11 +- .../build_system/targets/web_test.dart | 8 +- .../build_system/targets/windows_test.dart | 143 ++++----- 21 files changed, 476 insertions(+), 278 deletions(-) diff --git a/packages/flutter_tools/lib/src/aot.dart b/packages/flutter_tools/lib/src/aot.dart index d3022c5539f..30aa662124f 100644 --- a/packages/flutter_tools/lib/src/aot.dart +++ b/packages/flutter_tools/lib/src/aot.dart @@ -95,7 +95,11 @@ class AotBuilder { kExtraFrontEndOptions: buildInfo.extraFrontEndOptions.join(','), if (platform == TargetPlatform.ios) kIosArchs: iosBuildArchs.map(getNameForDarwinArch).join(' ') - } + }, + artifacts: globals.artifacts, + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, ); final BuildResult result = await globals.buildSystem.build(target, environment); status?.stop(); diff --git a/packages/flutter_tools/lib/src/base/logger.dart b/packages/flutter_tools/lib/src/base/logger.dart index d946e22c8ce..f3277a6b065 100644 --- a/packages/flutter_tools/lib/src/base/logger.dart +++ b/packages/flutter_tools/lib/src/base/logger.dart @@ -382,7 +382,7 @@ class BufferLogger extends Logger { _timeoutConfiguration = timeoutConfiguration, _stopwatchFactory = stopwatchFactory; - @visibleForTesting + /// Create a [BufferLogger] with test preferences. BufferLogger.test({ Terminal terminal, OutputPreferences outputPreferences, diff --git a/packages/flutter_tools/lib/src/build_system/build_system.dart b/packages/flutter_tools/lib/src/build_system/build_system.dart index bcd7c61fe50..621702cb958 100644 --- a/packages/flutter_tools/lib/src/build_system/build_system.dart +++ b/packages/flutter_tools/lib/src/build_system/build_system.dart @@ -8,13 +8,16 @@ import 'package:async/async.dart'; import 'package:convert/convert.dart'; import 'package:crypto/crypto.dart'; import 'package:meta/meta.dart'; +import 'package:platform/platform.dart'; import 'package:pool/pool.dart'; +import 'package:process/process.dart'; +import '../artifacts.dart'; import '../base/file_system.dart'; +import '../base/logger.dart'; import '../base/utils.dart'; import '../cache.dart'; import '../convert.dart'; -import '../globals.dart' as globals; import 'exceptions.dart'; import 'file_hash_store.dart'; import 'source.dart'; @@ -286,6 +289,10 @@ class Environment { @required Directory outputDir, @required Directory cacheDir, @required Directory flutterRootDir, + @required FileSystem fileSystem, + @required Logger logger, + @required Artifacts artifacts, + @required ProcessManager processManager, Directory buildDir, Map defines = const {}, }) { @@ -314,6 +321,10 @@ class Environment { cacheDir: cacheDir, defines: defines, flutterRootDir: flutterRootDir, + fileSystem: fileSystem, + logger: logger, + artifacts: artifacts, + processManager: processManager, ); } @@ -327,6 +338,10 @@ class Environment { Directory flutterRootDir, Directory buildDir, Map defines = const {}, + @required FileSystem fileSystem, + @required Logger logger, + @required Artifacts artifacts, + @required ProcessManager processManager, }) { return Environment( projectDir: projectDir ?? testDirectory, @@ -335,6 +350,10 @@ class Environment { flutterRootDir: flutterRootDir ?? testDirectory, buildDir: buildDir, defines: defines, + fileSystem: fileSystem, + logger: logger, + artifacts: artifacts, + processManager: processManager, ); } @@ -346,6 +365,10 @@ class Environment { @required this.cacheDir, @required this.defines, @required this.flutterRootDir, + @required this.processManager, + @required this.logger, + @required this.fileSystem, + @required this.artifacts, }); /// The [Source] value which is substituted with the path to [projectDir]. @@ -399,6 +422,14 @@ class Environment { /// The root build directory shared by all builds. final Directory rootBuildDir; + + final ProcessManager processManager; + + final Logger logger; + + final Artifacts artifacts; + + final FileSystem fileSystem; } /// The result information from the build system. @@ -422,7 +453,17 @@ class BuildResult { /// The build system is responsible for invoking and ordering [Target]s. class BuildSystem { - const BuildSystem(); + const BuildSystem({ + @required FileSystem fileSystem, + @required Platform platform, + @required Logger logger, + }) : _fileSystem = fileSystem, + _platform = platform, + _logger = logger; + + final FileSystem _fileSystem; + final Platform _platform; + final Logger _logger; /// Build `target` and all of its dependencies. Future build( @@ -436,15 +477,22 @@ class BuildSystem { // Load file hash store from previous builds. final FileHashStore fileCache = FileHashStore( environment: environment, - fileSystem: globals.fs, - logger: globals.logger, + fileSystem: _fileSystem, + logger: _logger, )..initialize(); // Perform sanity checks on build. checkCycles(target); final Node node = target._toNode(environment); - final _BuildInstance buildInstance = _BuildInstance(environment, fileCache, buildSystemConfig); + final _BuildInstance buildInstance = _BuildInstance( + environment: environment, + fileCache: fileCache, + buildSystemConfig: buildSystemConfig, + logger: _logger, + fileSystem: _fileSystem, + platform: _platform, + ); bool passed = true; try { passed = await buildInstance.invokeTarget(node); @@ -486,9 +534,18 @@ class BuildSystem { /// An active instance of a build. class _BuildInstance { - _BuildInstance(this.environment, this.fileCache, this.buildSystemConfig) - : resourcePool = Pool(buildSystemConfig.resourcePoolSize ?? globals.platform?.numberOfProcessors ?? 1); + _BuildInstance({ + this.environment, + this.fileCache, + this.buildSystemConfig, + this.logger, + this.fileSystem, + Platform platform, + }) + : resourcePool = Pool(buildSystemConfig.resourcePoolSize ?? platform?.numberOfProcessors ?? 1); + final Logger logger; + final FileSystem fileSystem; final BuildSystemConfig buildSystemConfig; final Pool resourcePool; final Map> pending = >{}; @@ -545,17 +602,17 @@ class _BuildInstance { // If we're missing a depfile, wait until after evaluating the target to // compute changes. final bool canSkip = !node.missingDepfile && - await node.computeChanges(environment, fileCache); + await node.computeChanges(environment, fileCache, fileSystem, logger); if (canSkip) { skipped = true; - globals.printTrace('Skipping target: ${node.target.name}'); + logger.printTrace('Skipping target: ${node.target.name}'); updateGraph(); return passed; } - globals.printTrace('${node.target.name}: Starting due to ${node.invalidatedReasons}'); + logger.printTrace('${node.target.name}: Starting due to ${node.invalidatedReasons}'); await node.target.build(environment); - globals.printTrace('${node.target.name}: Complete'); + logger.printTrace('${node.target.name}: Complete'); node.inputs ..clear() @@ -581,7 +638,7 @@ class _BuildInstance { if (outputFiles.containsKey(previousOutput)) { continue; } - final File previousFile = globals.fs.file(previousOutput); + final File previousFile = fileSystem.file(previousOutput); if (previousFile.existsSync()) { previousFile.deleteSync(); } @@ -771,6 +828,8 @@ class Node { Future computeChanges( Environment environment, FileHashStore fileHashStore, + FileSystem fileSystem, + Logger logger, ) async { final Set currentOutputPaths = { for (final File file in outputs) file.path, @@ -808,7 +867,7 @@ class Node { // if this isn't a current output file there is no reason to compute the hash. continue; } - final File file = globals.fs.file(previousOutput); + final File file = fileSystem.file(previousOutput); if (!file.existsSync()) { invalidatedReasons.add(InvalidatedReason.outputMissing); _dirty = true; @@ -833,7 +892,7 @@ class Node { if (missingInputs.isNotEmpty) { _dirty = true; final String missingMessage = missingInputs.map((File file) => file.path).join(', '); - globals.printTrace('invalidated build due to missing files: $missingMessage'); + logger.printTrace('invalidated build due to missing files: $missingMessage'); invalidatedReasons.add(InvalidatedReason.inputMissing); } diff --git a/packages/flutter_tools/lib/src/build_system/source.dart b/packages/flutter_tools/lib/src/build_system/source.dart index e0df5cb4ed1..de9260d9b2d 100644 --- a/packages/flutter_tools/lib/src/build_system/source.dart +++ b/packages/flutter_tools/lib/src/build_system/source.dart @@ -5,7 +5,6 @@ import '../artifacts.dart'; import '../base/file_system.dart'; import '../build_info.dart'; -import '../globals.dart' as globals; import 'build_system.dart'; import 'exceptions.dart'; @@ -24,7 +23,7 @@ abstract class ResolvedFiles { /// Collects sources for a [Target] into a single list of [FileSystemEntities]. class SourceVisitor implements ResolvedFiles { /// Create a new [SourceVisitor] from an [Environment]. - SourceVisitor(this.environment, [this.inputs = true]); + SourceVisitor(this.environment, [ this.inputs = true ]); /// The current environment. final Environment environment; @@ -56,7 +55,7 @@ class SourceVisitor implements ResolvedFiles { final String contents = depfile.readAsStringSync(); final List colonSeparated = contents.split(': '); if (colonSeparated.length != 2) { - globals.printError('Invalid depfile: ${depfile.path}'); + environment.logger.printError('Invalid depfile: ${depfile.path}'); return; } if (inputs) { @@ -78,7 +77,7 @@ class SourceVisitor implements ResolvedFiles { .map((String path) => path.replaceAllMapped(_escapeExpr, (Match match) => match.group(1)).trim()) .where((String path) => path.isNotEmpty) .toSet() - .map((String path) => globals.fs.file(path)); + .map(environment.fileSystem.file); } /// Visit a [Source] which contains a file URL. @@ -101,35 +100,36 @@ class SourceVisitor implements ResolvedFiles { switch (rawParts.first) { case Environment.kProjectDirectory: segments.addAll( - globals.fs.path.split(environment.projectDir.resolveSymbolicLinksSync())); + environment.fileSystem.path.split(environment.projectDir.resolveSymbolicLinksSync())); break; case Environment.kBuildDirectory: - segments.addAll(globals.fs.path.split( - environment.buildDir.resolveSymbolicLinksSync())); + segments.addAll(environment.fileSystem.path.split( + environment.buildDir.resolveSymbolicLinksSync())); break; case Environment.kCacheDirectory: segments.addAll( - globals.fs.path.split(environment.cacheDir.resolveSymbolicLinksSync())); + environment.fileSystem.path.split(environment.cacheDir.resolveSymbolicLinksSync())); break; case Environment.kFlutterRootDirectory: // flutter root will not contain a symbolic link. segments.addAll( - globals.fs.path.split(environment.flutterRootDir.absolute.path)); + environment.fileSystem.path.split(environment.flutterRootDir.absolute.path)); break; case Environment.kOutputDirectory: segments.addAll( - globals.fs.path.split(environment.outputDir.resolveSymbolicLinksSync())); + environment.fileSystem.path.split(environment.outputDir.resolveSymbolicLinksSync())); break; default: throw InvalidPatternException(pattern); } rawParts.skip(1).forEach(segments.add); - final String filePath = globals.fs.path.joinAll(segments); + final String filePath = environment.fileSystem.path.joinAll(segments); if (!hasWildcard) { - if (optional && !globals.fs.isFileSync(filePath)) { + if (optional && !environment.fileSystem.isFileSync(filePath)) { return; } - sources.add(globals.fs.file(globals.fs.path.normalize(filePath))); + sources.add(environment.fileSystem.file( + environment.fileSystem.path.normalize(filePath))); return; } // Perform a simple match by splitting the wildcard containing file one @@ -143,21 +143,21 @@ class SourceVisitor implements ResolvedFiles { if (wildcardSegments.length > 2) { throw InvalidPatternException(pattern); } - if (!globals.fs.directory(filePath).existsSync()) { + if (!environment.fileSystem.directory(filePath).existsSync()) { throw Exception('$filePath does not exist!'); } - for (final FileSystemEntity entity in globals.fs.directory(filePath).listSync()) { - final String filename = globals.fs.path.basename(entity.path); + for (final FileSystemEntity entity in environment.fileSystem.directory(filePath).listSync()) { + final String filename = environment.fileSystem.path.basename(entity.path); if (wildcardSegments.isEmpty) { - sources.add(globals.fs.file(entity.absolute)); + sources.add(environment.fileSystem.file(entity.absolute)); } else if (wildcardSegments.length == 1) { if (filename.startsWith(wildcardSegments[0]) || filename.endsWith(wildcardSegments[0])) { - sources.add(globals.fs.file(entity.absolute)); + sources.add(environment.fileSystem.file(entity.absolute)); } } else if (filename.startsWith(wildcardSegments[0])) { if (filename.substring(wildcardSegments[0].length).endsWith(wildcardSegments[1])) { - sources.add(globals.fs.file(entity.absolute)); + sources.add(environment.fileSystem.file(entity.absolute)); } } } @@ -167,15 +167,16 @@ class SourceVisitor implements ResolvedFiles { /// /// If the [Artifact] points to a directory then all child files are included. void visitArtifact(Artifact artifact, TargetPlatform platform, BuildMode mode) { - final String path = globals.artifacts.getArtifactPath(artifact, platform: platform, mode: mode); - if (globals.fs.isDirectorySync(path)) { + final String path = environment.artifacts + .getArtifactPath(artifact, platform: platform, mode: mode); + if (environment.fileSystem.isDirectorySync(path)) { sources.addAll([ - for (FileSystemEntity entity in globals.fs.directory(path).listSync(recursive: true)) + for (FileSystemEntity entity in environment.fileSystem.directory(path).listSync(recursive: true)) if (entity is File) entity, ]); } else { - sources.add(globals.fs.file(path)); + sources.add(environment.fileSystem.file(path)); } } } diff --git a/packages/flutter_tools/lib/src/build_system/targets/windows.dart b/packages/flutter_tools/lib/src/build_system/targets/windows.dart index ce1ee7b6045..16b7acc26a7 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/windows.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/windows.dart @@ -5,7 +5,6 @@ import '../../artifacts.dart'; import '../../base/file_system.dart'; import '../../build_info.dart'; -import '../../globals.dart' as globals; import '../build_system.dart'; /// Copies the Windows desktop embedding files to the copy directory. @@ -40,21 +39,24 @@ class UnpackWindows extends Target { @override Future build(Environment environment) async { // This path needs to match the prefix in the rule below. - final String basePath = globals.artifacts.getArtifactPath(Artifact.windowsDesktopPath); - for (final File input in globals.fs.directory(basePath) + final String basePath = environment.artifacts + .getArtifactPath(Artifact.windowsDesktopPath); + for (final File input in environment.fileSystem.directory(basePath) .listSync(recursive: true) .whereType()) { - final String outputPath = globals.fs.path.join( + final String outputPath = environment.fileSystem.path.join( environment.projectDir.path, 'windows', 'flutter', - globals.fs.path.relative(input.path, from: basePath), + environment.fileSystem.path + .relative(input.path, from: basePath), ); - final File destinationFile = globals.fs.file(outputPath); + final File destinationFile = environment.fileSystem.file(outputPath); if (!destinationFile.parent.existsSync()) { destinationFile.parent.createSync(recursive: true); } - globals.fs.file(input).copySync(destinationFile.path); + environment.fileSystem + .file(input).copySync(destinationFile.path); } } } diff --git a/packages/flutter_tools/lib/src/bundle.dart b/packages/flutter_tools/lib/src/bundle.dart index 80d9041ac84..cfdcc0fb2ab 100644 --- a/packages/flutter_tools/lib/src/bundle.dart +++ b/packages/flutter_tools/lib/src/bundle.dart @@ -132,6 +132,10 @@ Future buildWithAssemble({ if (dartDefines != null && dartDefines.isNotEmpty) kDartDefines: jsonEncode(dartDefines), }, + artifacts: globals.artifacts, + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, ); final Target target = buildMode == BuildMode.debug ? const CopyFlutterBundle() diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart index f5a0dcca4e4..bf1d65a8310 100644 --- a/packages/flutter_tools/lib/src/commands/assemble.dart +++ b/packages/flutter_tools/lib/src/commands/assemble.dart @@ -151,6 +151,10 @@ class AssembleCommand extends FlutterCommand { defines: _parseDefines(stringsArg('define')), cacheDir: globals.cache.getRoot(), flutterRootDir: globals.fs.directory(Cache.flutterRoot), + artifacts: globals.artifacts, + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, ); return result; } diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart index fe0a1aff682..00ab2d53636 100644 --- a/packages/flutter_tools/lib/src/context_runner.dart +++ b/packages/flutter_tools/lib/src/context_runner.dart @@ -89,7 +89,11 @@ Future runInContext( platform: globals.platform, ), AssetBundleFactory: () => AssetBundleFactory.defaultInstance, - BuildSystem: () => const BuildSystem(), + BuildSystem: () => BuildSystem( + fileSystem: globals.fs, + logger: globals.logger, + platform: globals.platform, + ), Cache: () => Cache( fileSystem: globals.fs, logger: globals.logger, diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index 2f9820366df..c80535bea8c 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart @@ -53,6 +53,10 @@ Future buildWeb( kCspMode: csp.toString(), kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(), }, + artifacts: globals.artifacts, + fileSystem: globals.fs, + logger: globals.logger, + processManager: globals.processManager, )); if (!result.success) { for (final ExceptionMeasurement measurement in result.exceptions.values) { diff --git a/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart b/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart index 6bc91d59f31..5948bda228d 100644 --- a/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart @@ -2,28 +2,23 @@ // 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:file_testing/file_testing.dart'; +import 'package:flutter_tools/src/artifacts.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/utils.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/exceptions.dart'; -import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/convert.dart'; -import 'package:flutter_tools/src/globals.dart' as globals; -import 'package:mockito/mockito.dart'; import '../../src/common.dart'; import '../../src/context.dart'; -import '../../src/testbed.dart'; void main() { - setUpAll(() { - Cache.disableLocking(); - }); - - const BuildSystem buildSystem = BuildSystem(); - Testbed testbed; - MockPlatform mockPlatform; + FileSystem fileSystem; Environment environment; Target fooTarget; Target barTarget; @@ -34,14 +29,12 @@ void main() { int shared; setUp(() { + fileSystem = MemoryFileSystem.test(); fooInvocations = 0; barInvocations = 0; shared = 0; - mockPlatform = MockPlatform(); - // Keep file paths the same. - when(mockPlatform.isWindows).thenReturn(false); - /// Create various testing targets. + /// Create various test targets. fooTarget = TestTarget((Environment environment) async { environment .buildDir @@ -91,31 +84,34 @@ void main() { ..inputs = const [ Source.pattern('{PROJECT_DIR}/foo.dart'), ]; - testbed = Testbed( - setup: () { - environment = Environment.test( - globals.fs.currentDirectory, - ); - globals.fs.file('foo.dart') - ..createSync(recursive: true) - ..writeAsStringSync(''); - globals.fs.file('pubspec.yaml').createSync(); - }, - overrides: { - Platform: () => mockPlatform, - }, + environment = Environment.test( + fileSystem.currentDirectory, + artifacts: MockArtifacts(), + processManager: FakeProcessManager.any(), + fileSystem: fileSystem, + logger: BufferLogger.test(), ); + fileSystem.file('foo.dart') + ..createSync(recursive: true) + ..writeAsStringSync(''); + fileSystem.file('pubspec.yaml').createSync(); }); - test('Does not throw exception if asked to build with missing inputs', () => testbed.run(() async { + testWithoutContext('Does not throw exception if asked to build with missing inputs', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); + // Delete required input file. - globals.fs.file('foo.dart').deleteSync(); + fileSystem.file('foo.dart').deleteSync(); final BuildResult buildResult = await buildSystem.build(fooTarget, environment); expect(buildResult.hasException, false); - })); + }); - test('Does not throw exception if it does not produce a specified output', () => testbed.run(() async { + testWithoutContext('Does not throw exception if it does not produce a specified output', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); + + // This target is document as producing foo.dart but does not actually + // output this value. final Target badTarget = TestTarget((Environment environment) async {}) ..inputs = const [ Source.pattern('{PROJECT_DIR}/foo.dart'), @@ -126,81 +122,98 @@ void main() { final BuildResult result = await buildSystem.build(badTarget, environment); expect(result.hasException, false); - })); + }); - test('Saves a stamp file with inputs and outputs', () => testbed.run(() async { + testWithoutContext('Saves a stamp file with inputs and outputs', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); await buildSystem.build(fooTarget, environment); + final File stampFile = fileSystem.file( + '${environment.buildDir.path}/foo.stamp'); - final File stampFile = globals.fs.file(globals.fs.path.join(environment.buildDir.path, 'foo.stamp')); - expect(stampFile.existsSync(), true); + expect(stampFile, exists); - final Map stampContents = castStringKeyedMap(json.decode(stampFile.readAsStringSync())); - expect(stampContents['inputs'], ['/foo.dart']); - })); + final Map stampContents = castStringKeyedMap( + json.decode(stampFile.readAsStringSync())); - test('Creates a BuildResult with inputs and outputs', () => testbed.run(() async { + expect(stampContents, containsPair('inputs', ['/foo.dart'])); + }); + + testWithoutContext('Creates a BuildResult with inputs and outputs', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); final BuildResult result = await buildSystem.build(fooTarget, environment); - expect(result.inputFiles.single.path, globals.fs.path.absolute('foo.dart')); - expect(result.outputFiles.single.path, - globals.fs.path.absolute(globals.fs.path.join(environment.buildDir.path, 'out'))); - })); + expect(result.inputFiles.single.path, '/foo.dart'); + expect(result.outputFiles.single.path, '${environment.buildDir.path}/out'); + }); + + testWithoutContext('Does not re-invoke build if stamp is valid', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); - test('Does not re-invoke build if stamp is valid', () => testbed.run(() async { await buildSystem.build(fooTarget, environment); await buildSystem.build(fooTarget, environment); expect(fooInvocations, 1); - })); + }); - test('Re-invoke build if input is modified', () => testbed.run(() async { + testWithoutContext('Re-invoke build if input is modified', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); await buildSystem.build(fooTarget, environment); - globals.fs.file('foo.dart').writeAsStringSync('new contents'); + fileSystem.file('foo.dart').writeAsStringSync('new contents'); await buildSystem.build(fooTarget, environment); + expect(fooInvocations, 2); - })); + }); - test('does not re-invoke build if input timestamp changes', () => testbed.run(() async { + testWithoutContext('does not re-invoke build if input timestamp changes', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); await buildSystem.build(fooTarget, environment); - globals.fs.file('foo.dart').writeAsStringSync(''); - + // The file was previously empty so this does not modify it. + fileSystem.file('foo.dart').writeAsStringSync(''); await buildSystem.build(fooTarget, environment); + expect(fooInvocations, 1); - })); + }); - test('does not re-invoke build if output timestamp changes', () => testbed.run(() async { + testWithoutContext('does not re-invoke build if output timestamp changes', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); await buildSystem.build(fooTarget, environment); + // This is the same content that the output file previously + // contained. environment.buildDir.childFile('out').writeAsStringSync('hey'); - await buildSystem.build(fooTarget, environment); + expect(fooInvocations, 1); - })); + }); - test('Re-invoke build if output is modified', () => testbed.run(() async { + testWithoutContext('Re-invoke build if output is modified', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); await buildSystem.build(fooTarget, environment); environment.buildDir.childFile('out').writeAsStringSync('Something different'); await buildSystem.build(fooTarget, environment); - expect(fooInvocations, 2); - })); - test('Runs dependencies of targets', () => testbed.run(() async { + expect(fooInvocations, 2); + }); + + testWithoutContext('Runs dependencies of targets', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); barTarget.dependencies.add(fooTarget); await buildSystem.build(barTarget, environment); - expect(globals.fs.file(globals.fs.path.join(environment.buildDir.path, 'bar')).existsSync(), true); + expect(fileSystem.file('${environment.buildDir.path}/bar'), exists); expect(fooInvocations, 1); expect(barInvocations, 1); - })); + }); - test('Only invokes shared dependencies once', () => testbed.run(() async { + testWithoutContext('Only invokes shared dependencies once', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); fooTarget.dependencies.add(sharedTarget); barTarget.dependencies.add(sharedTarget); barTarget.dependencies.add(fooTarget); @@ -208,19 +221,20 @@ void main() { await buildSystem.build(barTarget, environment); expect(shared, 1); - })); + }); - test('Automatically cleans old outputs when dag changes', () => testbed.run(() async { + testWithoutContext('Automatically cleans old outputs when build graph changes', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); final TestTarget testTarget = TestTarget((Environment envionment) async { environment.buildDir.childFile('foo.out').createSync(); }) ..inputs = const [Source.pattern('{PROJECT_DIR}/foo.dart')] ..outputs = const [Source.pattern('{BUILD_DIR}/foo.out')]; - globals.fs.file('foo.dart').createSync(); + fileSystem.file('foo.dart').createSync(); await buildSystem.build(testTarget, environment); - expect(environment.buildDir.childFile('foo.out').existsSync(), true); + expect(environment.buildDir.childFile('foo.out'), exists); final TestTarget testTarget2 = TestTarget((Environment envionment) async { environment.buildDir.childFile('bar.out').createSync(); @@ -230,80 +244,84 @@ void main() { await buildSystem.build(testTarget2, environment); - expect(environment.buildDir.childFile('bar.out').existsSync(), true); - expect(environment.buildDir.childFile('foo.out').existsSync(), false); - })); + expect(environment.buildDir.childFile('bar.out'), exists); + expect(environment.buildDir.childFile('foo.out'), isNot(exists)); + }); - test('Does not crash when filesytem and cache are out of sync', () => testbed.run(() async { - final TestTarget testTarget = TestTarget((Environment environment) async { + testWithoutContext('Does not crash when filesytem and cache are out of sync', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); + final TestTarget testWithoutContextTarget = TestTarget((Environment environment) async { environment.buildDir.childFile('foo.out').createSync(); }) ..inputs = const [Source.pattern('{PROJECT_DIR}/foo.dart')] ..outputs = const [Source.pattern('{BUILD_DIR}/foo.out')]; - globals.fs.file('foo.dart').createSync(); + fileSystem.file('foo.dart').createSync(); - await buildSystem.build(testTarget, environment); + await buildSystem.build(testWithoutContextTarget, environment); - expect(environment.buildDir.childFile('foo.out').existsSync(), true); + expect(environment.buildDir.childFile('foo.out'), exists); environment.buildDir.childFile('foo.out').deleteSync(); - final TestTarget testTarget2 = TestTarget((Environment environment) async { + final TestTarget testWithoutContextTarget2 = TestTarget((Environment environment) async { environment.buildDir.childFile('bar.out').createSync(); }) ..inputs = const [Source.pattern('{PROJECT_DIR}/foo.dart')] ..outputs = const [Source.pattern('{BUILD_DIR}/bar.out')]; - await buildSystem.build(testTarget2, environment); + await buildSystem.build(testWithoutContextTarget2, environment); - expect(environment.buildDir.childFile('bar.out').existsSync(), true); - expect(environment.buildDir.childFile('foo.out').existsSync(), false); - })); + expect(environment.buildDir.childFile('bar.out'), exists); + expect(environment.buildDir.childFile('foo.out'), isNot(exists)); + }); - test('reruns build if stamp is corrupted', () => testbed.run(() async { - final TestTarget testTarget = TestTarget((Environment envionment) async { + testWithoutContext('Reruns build if stamp is corrupted', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); + final TestTarget testWithoutContextTarget = TestTarget((Environment envionment) async { environment.buildDir.childFile('foo.out').createSync(); }) ..inputs = const [Source.pattern('{PROJECT_DIR}/foo.dart')] ..outputs = const [Source.pattern('{BUILD_DIR}/foo.out')]; - globals.fs.file('foo.dart').createSync(); - await buildSystem.build(testTarget, environment); + fileSystem.file('foo.dart').createSync(); + await buildSystem.build(testWithoutContextTarget, environment); // invalid JSON - environment.buildDir.childFile('test.stamp').writeAsStringSync('{X'); - await buildSystem.build(testTarget, environment); + environment.buildDir.childFile('testWithoutContext.stamp').writeAsStringSync('{X'); + await buildSystem.build(testWithoutContextTarget, environment); // empty file - environment.buildDir.childFile('test.stamp').writeAsStringSync(''); - await buildSystem.build(testTarget, environment); + environment.buildDir.childFile('testWithoutContext.stamp').writeAsStringSync(''); + await buildSystem.build(testWithoutContextTarget, environment); // invalid format - environment.buildDir.childFile('test.stamp').writeAsStringSync('{"inputs": 2, "outputs": 3}'); - await buildSystem.build(testTarget, environment); - })); + environment.buildDir.childFile('testWithoutContext.stamp').writeAsStringSync('{"inputs": 2, "outputs": 3}'); + await buildSystem.build(testWithoutContextTarget, environment); + }); - test('handles a throwing build action', () => testbed.run(() async { + testWithoutContext('handles a throwing build action without crashing', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); final BuildResult result = await buildSystem.build(fizzTarget, environment); expect(result.hasException, true); - })); + }); - test('Can describe itself with JSON output', () => testbed.run(() { + testWithoutContext('Can describe itself with JSON output', () { environment.buildDir.createSync(recursive: true); + expect(fooTarget.toJson(environment), { 'inputs': [ '/foo.dart', ], 'outputs': [ - globals.fs.path.join(environment.buildDir.path, 'out'), + fileSystem.path.join(environment.buildDir.path, 'out'), ], 'dependencies': [], 'name': 'foo', - 'stamp': globals.fs.path.join(environment.buildDir.path, 'foo.stamp'), + 'stamp': fileSystem.path.join(environment.buildDir.path, 'foo.stamp'), }); - })); + }); - test('Can find dependency cycles', () { + testWithoutContext('Can find dependency cycles', () { final Target barTarget = TestTarget()..name = 'bar'; final Target fooTarget = TestTarget()..name = 'foo'; barTarget.dependencies.add(fooTarget); @@ -312,73 +330,94 @@ void main() { expect(() => checkCycles(barTarget), throwsA(isA())); }); - test('Target with depfile dependency will not run twice without invalidation', () => testbed.run(() async { + testWithoutContext('Target with depfile dependency will not run twice without invalidation', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); int called = 0; final TestTarget target = TestTarget((Environment environment) async { - environment.buildDir.childFile('example.d') + environment.buildDir + .childFile('example.d') .writeAsStringSync('a.txt: b.txt'); - globals.fs.file('a.txt').writeAsStringSync('a'); + fileSystem.file('a.txt').writeAsStringSync('a'); called += 1; }) ..depfiles = ['example.d']; - globals.fs.file('b.txt').writeAsStringSync('b'); + fileSystem.file('b.txt').writeAsStringSync('b'); await buildSystem.build(target, environment); - expect(globals.fs.file('a.txt').existsSync(), true); + expect(fileSystem.file('a.txt'), exists); expect(called, 1); - // Second build is up to date due to depfil parse. + // Second build is up to date due to depfile parse. await buildSystem.build(target, environment); - expect(called, 1); - })); - test('output directory is an input to the build', () => testbed.run(() async { - final Environment environmentA = Environment.test(globals.fs.currentDirectory, outputDir: globals.fs.directory('a')); - final Environment environmentB = Environment.test(globals.fs.currentDirectory, outputDir: globals.fs.directory('b')); + expect(called, 1); + }); + + testWithoutContext('output directory is an input to the build', () async { + final Environment environmentA = Environment.test( + fileSystem.currentDirectory, + outputDir: fileSystem.directory('a'), + artifacts: MockArtifacts(), + processManager: FakeProcessManager.any(), + fileSystem: fileSystem, + logger: BufferLogger.test(), + ); + final Environment environmentB = Environment.test( + fileSystem.currentDirectory, + outputDir: fileSystem.directory('b'), + artifacts: MockArtifacts(), + processManager: FakeProcessManager.any(), + fileSystem: fileSystem, + logger: BufferLogger.test(), + ); expect(environmentA.buildDir.path, isNot(environmentB.buildDir.path)); - })); + }); - test('A target with depfile dependencies can delete stale outputs on the first run', () => testbed.run(() async { + testWithoutContext('A target with depfile dependencies can delete stale outputs on the first run', () async { + final BuildSystem buildSystem = setUpBuildSystem(fileSystem); int called = 0; final TestTarget target = TestTarget((Environment environment) async { if (called == 0) { environment.buildDir.childFile('example.d') .writeAsStringSync('a.txt c.txt: b.txt'); - globals.fs.file('a.txt').writeAsStringSync('a'); - globals.fs.file('c.txt').writeAsStringSync('a'); + fileSystem.file('a.txt').writeAsStringSync('a'); + fileSystem.file('c.txt').writeAsStringSync('a'); } else { // On second run, we no longer claim c.txt as an output. environment.buildDir.childFile('example.d') .writeAsStringSync('a.txt: b.txt'); - globals.fs.file('a.txt').writeAsStringSync('a'); + fileSystem.file('a.txt').writeAsStringSync('a'); } called += 1; }) ..depfiles = const ['example.d']; - globals.fs.file('b.txt').writeAsStringSync('b'); + fileSystem.file('b.txt').writeAsStringSync('b'); await buildSystem.build(target, environment); - expect(globals.fs.file('a.txt').existsSync(), true); - expect(globals.fs.file('c.txt').existsSync(), true); + expect(fileSystem.file('a.txt'), exists); + expect(fileSystem.file('c.txt'), exists); expect(called, 1); - // rewrite an input to force a rerun, espect that the old c.txt is deleted. - globals.fs.file('b.txt').writeAsStringSync('ba'); + // rewrite an input to force a rerun, expect that the old c.txt is deleted. + fileSystem.file('b.txt').writeAsStringSync('ba'); await buildSystem.build(target, environment); - expect(globals.fs.file('a.txt').existsSync(), true); - expect(globals.fs.file('c.txt').existsSync(), false); + expect(fileSystem.file('a.txt'), exists); + expect(fileSystem.file('c.txt'), isNot(exists)); expect(called, 2); - })); + }); } -class MockPlatform extends Mock implements Platform {} - -// Work-around for silly lint check. -T nonconst(T input) => input; +BuildSystem setUpBuildSystem(FileSystem fileSystem) { + return BuildSystem( + fileSystem: fileSystem, + logger: BufferLogger.test(), + platform: FakePlatform(operatingSystem: 'linux'), + ); +} class TestTarget extends Target { TestTarget([this._build]); @@ -403,3 +442,5 @@ class TestTarget extends Target { @override List outputs = []; } + +class MockArtifacts extends Mock implements Artifacts {} diff --git a/packages/flutter_tools/test/general.shard/build_system/filecache_test.dart b/packages/flutter_tools/test/general.shard/build_system/filecache_test.dart index 8b90884ae4d..a0056a2f6af 100644 --- a/packages/flutter_tools/test/general.shard/build_system/filecache_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/filecache_test.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:file/memory.dart'; +import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/terminal.dart'; @@ -14,6 +15,7 @@ import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import '../../src/common.dart'; +import '../../src/fake_process_manager.dart'; void main() { Environment environment; @@ -29,6 +31,10 @@ void main() { fileSystem.directory('build').createSync(); environment = Environment.test( fileSystem.currentDirectory, + artifacts: MockArtifacts(), + processManager: FakeProcessManager.any(), + logger: logger, + fileSystem: fileSystem, ); environment.buildDir.createSync(recursive: true); }); @@ -168,4 +174,6 @@ class FakeForwardingFileSystem extends ForwardingFileSystem { @override File file(dynamic path) => files[path] ?? super.file(path); } + class MockFile extends Mock implements File {} +class MockArtifacts extends Mock implements Artifacts {} diff --git a/packages/flutter_tools/test/general.shard/build_system/source_test.dart b/packages/flutter_tools/test/general.shard/build_system/source_test.dart index 78e6ad8b8a9..6d9ecdc3249 100644 --- a/packages/flutter_tools/test/general.shard/build_system/source_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/source_test.dart @@ -14,6 +14,7 @@ import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import '../../src/common.dart'; +import '../../src/context.dart'; import '../../src/testbed.dart'; void main() { @@ -32,6 +33,10 @@ void main() { environment = Environment.test( globals.fs.currentDirectory, outputDir: outputs, + artifacts: globals.artifacts, // using real artifacts + processManager: FakeProcessManager.any(), + fileSystem: globals.fs, + logger: globals.logger, ); visitor = SourceVisitor(environment); environment.buildDir.createSync(recursive: true); @@ -212,3 +217,4 @@ void main() { } class MockPlatform extends Mock implements Platform {} +class MockArtifacts extends Mock implements Artifacts {} diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/android_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/android_test.dart index 356c37d489a..384a24fa03e 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/android_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/android_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; @@ -9,6 +10,7 @@ import 'package:flutter_tools/src/build_system/targets/android.dart'; import 'package:flutter_tools/src/build_system/targets/dart.dart'; import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/cache.dart'; +import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import '../../../src/common.dart'; @@ -32,7 +34,11 @@ void main() { outputDir: globals.fs.directory('out')..createSync(), defines: { kBuildMode: 'debug', - } + }, + processManager: fakeProcessManager, + artifacts: MockArtifacts(), + fileSystem: globals.fs, + logger: globals.logger, ); environment.buildDir.createSync(recursive: true); @@ -45,7 +51,6 @@ void main() { hostDirectory.childFile('vm_isolate_snapshot.bin').createSync(); hostDirectory.childFile('isolate_snapshot.bin').createSync(); - await const DebugAndroidApplication().build(environment); expect(globals.fs.file(globals.fs.path.join('out', 'flutter_assets', 'isolate_snapshot_data')).existsSync(), true); @@ -59,7 +64,11 @@ void main() { outputDir: globals.fs.directory('out')..createSync(), defines: { kBuildMode: 'profile', - } + }, + artifacts: MockArtifacts(), + processManager: fakeProcessManager, + fileSystem: globals.fs, + logger: globals.logger, ); environment.buildDir.createSync(recursive: true); @@ -78,7 +87,11 @@ void main() { outputDir: globals.fs.directory('out')..createSync(), defines: { kBuildMode: 'release', - } + }, + artifacts: MockArtifacts(), + processManager: fakeProcessManager, + fileSystem: globals.fs, + logger: globals.logger, ); environment.buildDir.createSync(recursive: true); @@ -92,15 +105,19 @@ void main() { }); testbed.test('AndroidAot can build provided target platform', () async { + fakeProcessManager = FakeProcessManager.list([]); final Environment environment = Environment.test( globals.fs.currentDirectory, outputDir: globals.fs.directory('out')..createSync(), defines: { kBuildMode: 'release', - } + }, + artifacts: MockArtifacts(), + processManager: FakeProcessManager.list([]), + fileSystem: globals.fs, + logger: globals.logger, ); - fakeProcessManager = FakeProcessManager.list([ - FakeCommand(command: [ + fakeProcessManager.addCommand(FakeCommand(command: [ globals.fs.path.absolute(globals.fs.path.join('android-arm64-release', 'linux-x64', 'gen_snapshot')), '--deterministic', '--snapshot_kind=app-aot-elf', @@ -111,18 +128,21 @@ void main() { environment.buildDir.childFile('app.dill').path, ], ) - ]); + ); environment.buildDir.createSync(recursive: true); environment.buildDir.childFile('app.dill').createSync(); environment.projectDir.childFile('.packages').writeAsStringSync('\n'); const AndroidAot androidAot = AndroidAot(TargetPlatform.android_arm64, BuildMode.release); await androidAot.build(environment); + + expect(fakeProcessManager.hasRemainingExpectations, false); }, overrides: { ProcessManager: () => fakeProcessManager, }); testbed.test('kExtraGenSnapshotOptions passes values to gen_snapshot', () async { + fakeProcessManager = FakeProcessManager.list([]); final Environment environment = Environment.test( globals.fs.currentDirectory, outputDir: globals.fs.directory('out')..createSync(), @@ -130,9 +150,13 @@ void main() { kBuildMode: 'release', kExtraGenSnapshotOptions: 'foo,bar,baz=2', kTargetPlatform: 'android-arm', - } + }, + processManager: fakeProcessManager, + artifacts: MockArtifacts(), + fileSystem: globals.fs, + logger: globals.logger, ); - fakeProcessManager = FakeProcessManager.list([ + fakeProcessManager.addCommand( FakeCommand(command: [ globals.fs.path.absolute(globals.fs.path.join('android-arm64-release', 'linux-x64', 'gen_snapshot')), '--deterministic', @@ -145,8 +169,7 @@ void main() { '--no-causal-async-stacks', '--lazy-async-stacks', environment.buildDir.childFile('app.dill').path - ]) - ]); + ])); environment.buildDir.createSync(recursive: true); environment.buildDir.childFile('app.dill').createSync(); environment.projectDir.childFile('.packages').writeAsStringSync('\n'); @@ -163,7 +186,11 @@ void main() { outputDir: globals.fs.directory('out')..createSync(), defines: { kBuildMode: 'release', - } + }, + processManager: fakeProcessManager, + artifacts: MockArtifacts(), + fileSystem: globals.fs, + logger: globals.logger, ); environment.buildDir.createSync(recursive: true); const AndroidAot androidAot = AndroidAot(TargetPlatform.android_arm64, BuildMode.release); @@ -181,3 +208,5 @@ void main() { .childFile('app.so').existsSync(), true); }); } + +class MockArtifacts extends Mock implements Artifacts {} \ No newline at end of file diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/assets_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/assets_test.dart index 253a06d1b52..ebbb535a1f9 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/assets_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/assets_test.dart @@ -4,10 +4,13 @@ import 'package:file/memory.dart'; import 'package:file_testing/file_testing.dart'; +import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/depfile.dart'; import 'package:flutter_tools/src/build_system/targets/assets.dart'; +import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; import '../../../src/common.dart'; @@ -23,6 +26,10 @@ void main() { fileSystem = MemoryFileSystem.test(); environment = Environment.test( fileSystem.currentDirectory, + processManager: FakeProcessManager.any(), + artifacts: MockArtifacts(), + fileSystem: fileSystem, + logger: BufferLogger.test(), ); fileSystem.file(environment.buildDir.childFile('app.dill')).createSync(recursive: true); fileSystem.file('packages/flutter_tools/lib/src/build_system/targets/assets.dart') @@ -91,3 +98,5 @@ flutter: Platform: () => platform, }); } + +class MockArtifacts extends Mock implements Artifacts {} diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart index 5ee0fdb78d4..733491d864b 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart @@ -43,6 +43,10 @@ void main() { kBuildMode: getNameForBuildMode(BuildMode.profile), kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm), }, + artifacts: artifacts, + processManager: processManager, + fileSystem: globals.fs, + logger: globals.logger, ); androidEnvironment.buildDir.createSync(recursive: true); iosEnvironment = Environment.test( @@ -51,6 +55,10 @@ void main() { kBuildMode: getNameForBuildMode(BuildMode.profile), kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios), }, + artifacts: artifacts, + processManager: processManager, + fileSystem: globals.fs, + logger: globals.logger, ); iosEnvironment.buildDir.createSync(recursive: true); artifacts = CachedArtifacts( @@ -261,6 +269,10 @@ void main() { kBuildMode: getNameForBuildMode(BuildMode.debug), kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm), }, + processManager: processManager, + artifacts: artifacts, + fileSystem: globals.fs, + logger: globals.logger, ); final String build = testEnvironment.buildDir.path; processManager = FakeProcessManager.list([ diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/icon_tree_shaker_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/icon_tree_shaker_test.dart index 5c959bec47b..a4933f044e1 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/icon_tree_shaker_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/icon_tree_shaker_test.dart @@ -113,6 +113,10 @@ void main() { return Environment.test( fs.directory('/icon_test')..createSync(recursive: true), defines: defines, + artifacts: mockArtifacts, + processManager: FakeProcessManager.any(), + fileSystem: fs, + logger: BufferLogger.test(), ); } diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart index 09a9bb7e904..3782b8402bf 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart @@ -39,9 +39,16 @@ void main() { setUp(() { testbed = Testbed(setup: () { - environment = Environment.test(globals.fs.currentDirectory, defines: { - kTargetPlatform: 'ios', - }); + environment = Environment.test( + globals.fs.currentDirectory, + defines: { + kTargetPlatform: 'ios', + }, + processManager: processManager, + artifacts: MockArtifacts(), + logger: globals.logger, + fileSystem: globals.fs, + ); }); }); diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart index 629900417f4..f51af65c7e7 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/linux_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter_tools/src/artifacts.dart'; import 'package:platform/platform.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; @@ -12,11 +13,12 @@ import 'package:flutter_tools/src/globals.dart' as globals; import 'package:mockito/mockito.dart'; import '../../../src/common.dart'; +import '../../../src/context.dart'; import '../../../src/testbed.dart'; void main() { Testbed testbed; - const BuildSystem buildSystem = BuildSystem(); + BuildSystem buildSystem; Environment environment; MockPlatform mockPlatform; @@ -33,11 +35,20 @@ void main() { when(mockPlatform.environment).thenReturn(Map.unmodifiable({})); testbed = Testbed(setup: () { Cache.flutterRoot = ''; + buildSystem = BuildSystem( + logger: globals.logger, + platform: globals.platform, + fileSystem: globals.fs, + ); environment = Environment.test( globals.fs.currentDirectory, defines: { kBuildMode: 'debug', - } + }, + artifacts: MockArtifacts(), + processManager: FakeProcessManager.any(), + fileSystem: globals.fs, + logger: globals.logger, ); globals.fs.file('bin/cache/artifacts/engine/linux-x64/unrelated-stuff').createSync(recursive: true); globals.fs.file('bin/cache/artifacts/engine/linux-x64/libflutter_linux_glfw.so').createSync(recursive: true); @@ -105,3 +116,4 @@ void main() { } class MockPlatform extends Mock implements Platform {} +class MockArtifacts extends Mock implements Artifacts {} diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart index f4202b21e4a..a7e023334b5 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/build.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; @@ -16,6 +17,7 @@ import 'package:process/process.dart'; import 'package:platform/platform.dart'; import '../../../src/common.dart'; +import '../../../src/fake_process_manager.dart'; import '../../../src/testbed.dart'; const String _kInputPrefix = 'bin/cache/artifacts/engine/darwin-x64/FlutterMacOS.framework'; @@ -70,8 +72,12 @@ void main() { defines: { kBuildMode: 'debug', kTargetPlatform: 'darwin-x64', - }, - ); + }, + artifacts: MockArtifacts(), + processManager: FakeProcessManager.any(), + logger: globals.logger, + fileSystem: globals.fs, + ); }, overrides: { ProcessManager: () => MockProcessManager(), Platform: () => mockPlatform, @@ -195,6 +201,7 @@ class MockPlatform extends Mock implements Platform {} class MockProcessManager extends Mock implements ProcessManager {} class MockGenSnapshot extends Mock implements GenSnapshot {} class MockXcode extends Mock implements Xcode {} +class MockArtifacts extends Mock implements Artifacts {} class FakeProcessResult implements ProcessResult { @override int exitCode; diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart index c56c39ebfc8..e2459c4dbd7 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:file_testing/file_testing.dart'; +import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart'; @@ -55,7 +56,11 @@ void main() { outputDir: globals.fs.currentDirectory.childDirectory('bar'), defines: { kTargetFile: globals.fs.path.join('foo', 'lib', 'main.dart'), - } + }, + artifacts: MockArtifacts(), + processManager: FakeProcessManager.any(), + logger: globals.logger, + fileSystem: globals.fs, ); depfileService = DepfileService( fileSystem: globals.fs, @@ -460,3 +465,4 @@ void main() { } class MockProcessManager extends Mock implements ProcessManager {} +class MockArtifacts extends Mock implements Artifacts {} diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/windows_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/windows_test.dart index bb14140fa16..04e46b6d748 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/windows_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/windows_test.dart @@ -3,102 +3,77 @@ // 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/build_system.dart'; -import 'package:flutter_tools/src/build_system/targets/windows.dart'; -import 'package:flutter_tools/src/cache.dart'; -import 'package:flutter_tools/src/globals.dart' as globals; +import 'package:file_testing/file_testing.dart'; +import 'package:flutter_tools/src/artifacts.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/build_system/build_system.dart'; +import 'package:flutter_tools/src/build_system/targets/windows.dart'; import '../../../src/common.dart'; import '../../../src/fake_process_manager.dart'; -import '../../../src/testbed.dart'; + +final Platform kWindowsPlatform = FakePlatform( + operatingSystem: 'windows', + environment: {}, +); + +const List kRequiredFiles = [ + r'C:\bin\cache\artifacts\engine\windows-x64\flutter_export.h', + r'C:\bin\cache\artifacts\engine\windows-x64\flutter_messenger.h', + r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll', + r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.exp', + r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.lib', + r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.pdb', + r'C:\bin\cache\artifacts\engine\windows-x64\lutter_export.h', + r'C:\bin\cache\artifacts\engine\windows-x64\flutter_messenger.h', + r'C:\bin\cache\artifacts\engine\windows-x64\flutter_plugin_registrar.h', + r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.h', + r'C:\bin\cache\artifacts\engine\windows-x64\icudtl.dat', + r'C:\bin\cache\artifacts\engine\windows-x64\cpp_client_wrapper\foo', + r'C:\packages\flutter_tools\lib\src\build_system\targets\windows.dart', +]; void main() { - Testbed testbed; - const BuildSystem buildSystem = BuildSystem(); Environment environment; - Platform platform; - - setUpAll(() { - Cache.disableLocking(); - Cache.flutterRoot = ''; - }); + FileSystem fileSystem; setUp(() { - platform = MockPlatform(); - when(platform.isWindows).thenReturn(true); - when(platform.isMacOS).thenReturn(false); - when(platform.isLinux).thenReturn(false); - when(platform.pathSeparator).thenReturn(r'\'); - testbed = Testbed(setup: () { - environment = Environment.test( - globals.fs.currentDirectory, - ); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_export.h').createSync(recursive: true); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_messenger.h').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.exp').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.lib').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.dll.pdb').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\lutter_export.h').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_messenger.h').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_plugin_registrar.h').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_windows.h').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\icudtl.dat').createSync(); - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\cpp_client_wrapper\foo').createSync(recursive: true); - globals.fs.file(r'C:\packages\flutter_tools\lib\src\build_system\targets\windows.dart').createSync(recursive: true); - globals.fs.directory('windows').createSync(); - }, overrides: { - FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows), - ProcessManager: () => FakeProcessManager.any(), - Platform: () => platform, - }); + final MockArtifacts artifacts = MockArtifacts(); + when(artifacts.getArtifactPath(Artifact.windowsDesktopPath)) + .thenReturn(r'C:\bin\cache\artifacts\engine\windows-x64\'); + fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); + environment = Environment.test( + fileSystem.currentDirectory, + artifacts: artifacts, + processManager: FakeProcessManager.any(), + fileSystem: fileSystem, + logger: BufferLogger.test(), + ); + for (final String path in kRequiredFiles) { + fileSystem.file(path).createSync(recursive: true); + } + fileSystem.directory('windows').createSync(); }); - test('Copies files to correct cache directory', () => testbed.run(() async { - await buildSystem.build(const UnpackWindows(), environment); + testWithoutContext('UnpackWindows copies files to the correct cache directory', () async { + await const UnpackWindows().build(environment); - expect(globals.fs.file(r'C:\windows\flutter\flutter_export.h').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\flutter_messenger.h').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\flutter_windows.dll').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\flutter_windows.dll.exp').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\flutter_windows.dll.lib').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\flutter_windows.dll.pdb').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\flutter_export.h').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\flutter_messenger.h').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\flutter_plugin_registrar.h').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\flutter_windows.h').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\icudtl.dat').existsSync(), true); - expect(globals.fs.file(r'C:\windows\flutter\cpp_client_wrapper\foo').existsSync(), true); - })); - - test('Does not re-copy files unecessarily', () => testbed.run(() async { - await buildSystem.build(const UnpackWindows(), environment); - // Set a date in the far distant past to deal with the limited resolution - // of the windows filesystem. - final DateTime theDistantPast = DateTime(1991, 8, 23); - globals.fs.file(r'C:\windows\flutter\flutter_export.h').setLastModifiedSync(theDistantPast); - await buildSystem.build(const UnpackWindows(), environment); - - expect(globals.fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, equals(theDistantPast)); - })); - - test('Detects changes in input cache files', () => testbed.run(() async { - await buildSystem.build(const UnpackWindows(), environment); - // Set a date in the far distant past to deal with the limited resolution - // of the windows filesystem. - final DateTime theDistantPast = DateTime(1991, 8, 23); - globals.fs.file(r'C:\windows\flutter\flutter_export.h').setLastModifiedSync(theDistantPast); - final DateTime modified = globals.fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified; - globals.fs.file(r'C:\bin\cache\artifacts\engine\windows-x64\flutter_export.h').writeAsStringSync('asd'); // modify cache. - - await buildSystem.build(const UnpackWindows(), environment); - - expect(globals.fs.file(r'C:\windows\flutter\flutter_export.h').statSync().modified, isNot(modified)); - })); + expect(fileSystem.file(r'C:\windows\flutter\flutter_export.h'), exists); + expect(fileSystem.file(r'C:\windows\flutter\flutter_messenger.h'), exists); + expect(fileSystem.file(r'C:\windows\flutter\flutter_windows.dll'), exists); + expect(fileSystem.file(r'C:\windows\flutter\flutter_windows.dll.exp'), exists); + expect(fileSystem.file(r'C:\windows\flutter\flutter_windows.dll.lib'), exists); + expect(fileSystem.file(r'C:\windows\flutter\flutter_windows.dll.pdb'), exists); + expect(fileSystem.file(r'C:\windows\flutter\flutter_export.h'), exists); + expect(fileSystem.file(r'C:\windows\flutter\flutter_messenger.h'), exists); + expect(fileSystem.file(r'C:\windows\flutter\flutter_plugin_registrar.h'), exists); + expect(fileSystem.file(r'C:\windows\flutter\flutter_windows.h'), exists); + expect(fileSystem.file(r'C:\windows\flutter\icudtl.dat'), exists); + expect(fileSystem.file(r'C:\windows\flutter\cpp_client_wrapper\foo'), exists); + }); } -class MockPlatform extends Mock implements Platform {} +class MockArtifacts extends Mock implements Artifacts {}