From 07e2d6f63bdddbf317a72a497c969040b202df65 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 7 Jul 2020 19:33:25 -0700 Subject: [PATCH] [flutter_tools] make precache force blow away stamp files (#61003) update flutter precache --force to delete all stamp files. In the event that a user is hitting a cache issue, this should be easier than re-downloading all artifacts or manually blowing away the cache. This is probably how it should have worked in the first place --- packages/flutter_tools/lib/executable.dart | 9 +- packages/flutter_tools/lib/src/cache.dart | 35 ++- .../lib/src/commands/precache.dart | 50 ++- .../hermetic/precache_test.dart | 293 +++++++++++------- .../test/general.shard/cache_test.dart | 87 +++++- packages/flutter_tools/test/src/testbed.dart | 3 + 6 files changed, 335 insertions(+), 142 deletions(-) diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index a7dcfbceed..fcfb47d060 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -48,6 +48,7 @@ import 'src/commands/train.dart'; import 'src/commands/update_packages.dart'; import 'src/commands/upgrade.dart'; import 'src/commands/version.dart'; +import 'src/features.dart'; import 'src/globals.dart' as globals; import 'src/runner/flutter_command.dart'; import 'src/web/compile.dart'; @@ -98,7 +99,13 @@ Future main(List args) async { LogsCommand(), MakeHostAppEditableCommand(), PackagesCommand(), - PrecacheCommand(verboseHelp: verboseHelp), + PrecacheCommand( + verboseHelp: verboseHelp, + cache: globals.cache, + logger: globals.logger, + platform: globals.platform, + featureFlags: featureFlags, + ), RunCommand(verboseHelp: verboseHelp), ScreenshotCommand(), ShellCompletionCommand(), diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index b1e895b6f6..93a2460655 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart @@ -37,6 +37,7 @@ class DevelopmentArtifact { /// Artifacts required for Android development. static const DevelopmentArtifact androidGenSnapshot = DevelopmentArtifact._('android_gen_snapshot'); static const DevelopmentArtifact androidMaven = DevelopmentArtifact._('android_maven'); + // Artifacts used for internal builds. static const DevelopmentArtifact androidInternalBuild = DevelopmentArtifact._('android_internal_build'); @@ -243,8 +244,8 @@ class Cache { /// Checks if the current process owns the lock for the cache directory at /// this very moment; throws a [StateError] if it doesn't. - static void checkLockAcquired() { - if (_lockEnabled && _lock == null && globals.platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { + static void checkLockAcquired([Platform platform]) { + if (_lockEnabled && _lock == null && (platform ?? globals.platform).environment['FLUTTER_ALREADY_LOCKED'] != 'true') { throw StateError( 'The current process does not own the lock for the cache directory. This is a bug in Flutter CLI tools.', ); @@ -372,6 +373,21 @@ class Cache { return versionFile.existsSync() ? versionFile.readAsStringSync().trim() : null; } + /// Delete all stamp files maintained by the cache. + void clearStampFiles() { + try { + getStampFileFor('flutter_tools').deleteSync(); + for (final ArtifactSet artifact in _artifacts) { + final File file = getStampFileFor(artifact.stampName); + if (file.existsSync()) { + file.deleteSync(); + } + } + } on FileSystemException catch (err) { + _logger.printError('Failed to delete some stamp files: $err'); + } + } + String getStampFor(String artifactName) { final File stampFile = getStampFileFor(artifactName); return stampFile.existsSync() ? stampFile.readAsStringSync().trim() : null; @@ -508,6 +524,13 @@ abstract class ArtifactSet { /// Updates the artifact. Future update(); + + /// The canonical name of the artifact. + String get name; + + // The name of the stamp file. Defaults to the same as the + // artifact name. + String get stampName => name; } /// An artifact set managed by the cache. @@ -520,11 +543,10 @@ abstract class CachedArtifact extends ArtifactSet { final Cache cache; - /// The canonical name of the artifact. + @override final String name; - // The name of the stamp file. Defaults to the same as the - // artifact name. + @override String get stampName => name; Directory get location => cache.getArtifactDirectory(name); @@ -1043,6 +1065,9 @@ class AndroidMavenArtifacts extends ArtifactSet { // Therefore, call Gradle to figure this out. return false; } + + @override + String get name => 'android-maven-artifacts'; } class IOSEngineArtifacts extends EngineCachedArtifact { diff --git a/packages/flutter_tools/lib/src/commands/precache.dart b/packages/flutter_tools/lib/src/commands/precache.dart index c265d295fd..41b5d2977e 100644 --- a/packages/flutter_tools/lib/src/commands/precache.dart +++ b/packages/flutter_tools/lib/src/commands/precache.dart @@ -4,18 +4,36 @@ import 'dart:async'; +import 'package:meta/meta.dart'; + import '../base/common.dart'; +import '../base/logger.dart'; +import '../base/platform.dart'; import '../cache.dart'; import '../features.dart'; import '../globals.dart' as globals; import '../runner/flutter_command.dart'; +import '../version.dart'; +/// The flutter precache command allows downloading of cache artifacts without +/// the use of device/artifact autodetection. class PrecacheCommand extends FlutterCommand { - PrecacheCommand({bool verboseHelp = false}) { + PrecacheCommand({ + bool verboseHelp = false, + @required Cache cache, + @required Platform platform, + @required Logger logger, + @required FeatureFlags featureFlags, + FlutterVersion flutterVersion, // flutter version cannot be injected. + }) : _cache = cache, + _platform = platform, + _logger = logger, + _featureFlags = featureFlags, + _flutterVersion = flutterVersion { argParser.addFlag('all-platforms', abbr: 'a', negatable: false, help: 'Precache artifacts for all host platforms.'); argParser.addFlag('force', abbr: 'f', negatable: false, - help: 'Force downloading of artifacts.'); + help: 'Force re-downloading of artifacts.'); argParser.addFlag('android', negatable: true, defaultsTo: true, help: 'Precache artifacts for Android development.', hide: verboseHelp); @@ -48,6 +66,12 @@ class PrecacheCommand extends FlutterCommand { help: 'Precache the unsigned mac binaries when available.', hide: true); } + final Cache _cache; + final Logger _logger; + final Platform _platform; + final FeatureFlags _featureFlags; + final FlutterVersion _flutterVersion; + @override final String name = 'precache'; @@ -112,26 +136,29 @@ class PrecacheCommand extends FlutterCommand { @override Future runCommand() async { // Re-lock the cache. - if (globals.platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { + if (_platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { await Cache.lock(); } + if (boolArg('force')) { + _cache.clearStampFiles(); + } final bool includeAllPlatforms = boolArg('all-platforms'); if (includeAllPlatforms) { - globals.cache.includeAllPlatforms = true; + _cache.includeAllPlatforms = true; } if (boolArg('use-unsigned-mac-binaries')) { - globals.cache.useUnsignedMacBinaries = true; + _cache.useUnsignedMacBinaries = true; } - globals.cache.platformOverrideArtifacts = _explicitArtifactSelections(); + _cache.platformOverrideArtifacts = _explicitArtifactSelections(); final Map umbrellaForArtifact = _umbrellaForArtifactMap(); final Set requiredArtifacts = {}; for (final DevelopmentArtifact artifact in DevelopmentArtifact.values) { // Don't include unstable artifacts on stable branches. - if (!globals.flutterVersion.isMaster && artifact.unstable) { + if (!(_flutterVersion ?? globals.flutterVersion).isMaster && artifact.unstable) { continue; } - if (artifact.feature != null && !featureFlags.isEnabled(artifact.feature)) { + if (artifact.feature != null && !_featureFlags.isEnabled(artifact.feature)) { continue; } @@ -140,11 +167,10 @@ class PrecacheCommand extends FlutterCommand { requiredArtifacts.add(artifact); } } - final bool forceUpdate = boolArg('force'); - if (forceUpdate || !globals.cache.isUpToDate()) { - await globals.cache.updateAll(requiredArtifacts); + if (!_cache.isUpToDate()) { + await _cache.updateAll(requiredArtifacts); } else { - globals.printStatus('Already up-to-date.'); + _logger.printStatus('Already up-to-date.'); } return FlutterCommandResult.success(); } diff --git a/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart index 72e7e5025c..5e92e4a282 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart @@ -2,10 +2,10 @@ // 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/base/logger.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/precache.dart'; -import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart'; import 'package:flutter_tools/src/version.dart'; import 'package:mockito/mockito.dart'; @@ -38,38 +38,53 @@ void main() { }); testUsingContext('precache should acquire lock', () async { - final PrecacheCommand command = PrecacheCommand(); + final Platform platform = FakePlatform(environment: {}); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + platform: platform, + featureFlags: TestFeatureFlags(), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache']); expect(Cache.isLocked(), isTrue); // Do not throw StateError, lock is acquired. - Cache.checkLockAcquired(); - }, overrides: { - Cache: () => cache, + expect(() => Cache.checkLockAcquired(platform), returnsNormally); }); testUsingContext('precache should not re-entrantly acquire lock', () async { - final PrecacheCommand command = PrecacheCommand(); - applyMocksToCommand(command); - await createTestCommandRunner(command).run(const ['precache']); - - expect(Cache.isLocked(), isFalse); - // Do not throw StateError, acquired reentrantly with FLUTTER_ALREADY_LOCKED. - Cache.checkLockAcquired(); - }, overrides: { - Cache: () => cache, - Platform: () => FakePlatform( + final Platform platform = FakePlatform( operatingSystem: 'windows', environment: { 'FLUTTER_ROOT': 'flutter', 'FLUTTER_ALREADY_LOCKED': 'true', }, - ), + ); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(), + platform: platform, + ); + applyMocksToCommand(command); + await createTestCommandRunner(command).run(const ['precache']); + + expect(Cache.isLocked(), isFalse); + // Do not throw StateError, acquired reentrantly with FLUTTER_ALREADY_LOCKED. + expect(() => Cache.checkLockAcquired(platform), returnsNormally); }); testUsingContext('precache downloads web artifacts on dev branch when feature is enabled.', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(isWebEnabled: true), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache', '--web', '--no-android', '--no-ios']); @@ -77,26 +92,32 @@ void main() { DevelopmentArtifact.universal, DevelopmentArtifact.web, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), }); testUsingContext('precache does not download web artifacts on dev branch when feature is enabled.', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(isWebEnabled: false), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache', '--web', '--no-android', '--no-ios']); expect(artifacts, unorderedEquals({ DevelopmentArtifact.universal, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags(isWebEnabled: false), }); testUsingContext('precache downloads macOS artifacts on dev branch when macOS is enabled.', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(isMacOSEnabled: true), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache', '--macos', '--no-android', '--no-ios']); @@ -104,26 +125,32 @@ void main() { DevelopmentArtifact.universal, DevelopmentArtifact.macOS, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), }); testUsingContext('precache does not download macOS artifacts on dev branch when feature is enabled.', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(isMacOSEnabled: false), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache', '--macos', '--no-android', '--no-ios']); expect(artifacts, unorderedEquals({ DevelopmentArtifact.universal, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false), }); testUsingContext('precache downloads Windows artifacts on dev branch when feature is enabled.', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(isWindowsEnabled: true), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache', '--windows', '--no-android', '--no-ios']); @@ -131,26 +158,32 @@ void main() { DevelopmentArtifact.universal, DevelopmentArtifact.windows, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), }); testUsingContext('precache does not download Windows artifacts on dev branch when feature is enabled.', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(isWindowsEnabled: false), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache', '--windows', '--no-android', '--no-ios']); expect(artifacts, unorderedEquals({ DevelopmentArtifact.universal, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false), }); testUsingContext('precache downloads Linux artifacts on dev branch when feature is enabled.', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(isLinuxEnabled: true), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache', '--linux', '--no-android', '--no-ios']); @@ -158,39 +191,53 @@ void main() { DevelopmentArtifact.universal, DevelopmentArtifact.linux, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true), }); testUsingContext('precache does not download Linux artifacts on dev branch when feature is enabled.', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(isLinuxEnabled: false), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache', '--linux', '--no-android', '--no-ios']); expect(artifacts, unorderedEquals({ DevelopmentArtifact.universal, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false), }); testUsingContext('precache exits if requesting mismatched artifacts.', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(isWebEnabled: false), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); expect(createTestCommandRunner(command).run(const ['precache', '--no-android', '--android_gen_snapshot', ]), throwsToolExit(message: '--android_gen_snapshot requires --android')); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags(isWebEnabled: false), }); testUsingContext('precache adds artifact flags to requested artifacts', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: masterFlutterVersion, + featureFlags: TestFeatureFlags( + isWebEnabled: true, + isLinuxEnabled: true, + isMacOSEnabled: true, + isWindowsEnabled: true, + ), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run( const [ @@ -218,19 +265,16 @@ void main() { DevelopmentArtifact.fuchsia, DevelopmentArtifact.flutterRunner, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags( - isWebEnabled: true, - isLinuxEnabled: true, - isMacOSEnabled: true, - isWindowsEnabled: true, - ), - FlutterVersion: () => masterFlutterVersion, }); testUsingContext('precache expands android artifacts when the android flag is used', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run( const [ @@ -245,12 +289,16 @@ void main() { DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidInternalBuild, })); - }, overrides: { - Cache: () => cache, }); testUsingContext('precache adds artifact flags to requested android artifacts', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run( const [ @@ -267,12 +315,16 @@ void main() { DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidInternalBuild, })); - }, overrides: { - Cache: () => cache, }); testUsingContext('precache adds artifact flags to requested artifacts on stable', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run( const [ @@ -296,14 +348,16 @@ void main() { DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidInternalBuild, })); - }, overrides: { - Cache: () => cache, - FlutterVersion: () => flutterVersion, - FeatureFlags: () => TestFeatureFlags(isWebEnabled: false), }); testUsingContext('precache downloads iOS and Android artifacts by default', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags(), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run( @@ -319,12 +373,21 @@ void main() { DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidInternalBuild, })); - }, overrides: { - Cache: () => cache, }); testUsingContext('precache --all-platforms gets all artifacts', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: masterFlutterVersion, + featureFlags: TestFeatureFlags( + isWebEnabled: true, + isLinuxEnabled: true, + isMacOSEnabled: true, + isWindowsEnabled: true, + ), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run( @@ -347,19 +410,16 @@ void main() { DevelopmentArtifact.fuchsia, DevelopmentArtifact.flutterRunner, })); - }, overrides: { - Cache: () => cache, - FeatureFlags: () => TestFeatureFlags( - isWebEnabled: true, - isLinuxEnabled: true, - isMacOSEnabled: true, - isWindowsEnabled: true, - ), - FlutterVersion: () => masterFlutterVersion, }); testUsingContext('precache with default artifacts does not override platform filtering', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: masterFlutterVersion, + featureFlags: TestFeatureFlags(), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run( @@ -369,13 +429,24 @@ void main() { ); verify(cache.platformOverrideArtifacts = {}); - }, overrides: { - Cache: () => cache, - FlutterVersion: () => masterFlutterVersion, }); testUsingContext('precache with explicit artifact options overrides platform filtering', () async { - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: masterFlutterVersion, + featureFlags: TestFeatureFlags( + isMacOSEnabled: true, + ), + platform: FakePlatform( + operatingSystem: 'windows', + environment: { + 'FLUTTER_ROOT': 'flutter', + 'FLUTTER_ALREADY_LOCKED': 'true', + }, + ), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run( @@ -392,39 +463,23 @@ void main() { DevelopmentArtifact.macOS, })); verify(cache.platformOverrideArtifacts = {'macos'}); - }, overrides: { - Cache: () => cache, - FlutterVersion: () => masterFlutterVersion, - FeatureFlags: () => TestFeatureFlags( - isMacOSEnabled: true, - ), - Platform: () => FakePlatform( - operatingSystem: 'windows', - environment: { - 'FLUTTER_ROOT': 'flutter', - 'FLUTTER_ALREADY_LOCKED': 'true', - }, - ), }); - testUsingContext('precache downloads artifacts when --force is provided', () async { + testUsingContext('precache deletes artifact stampfiles when --force is provided', () async { when(cache.isUpToDate()).thenReturn(true); - final PrecacheCommand command = PrecacheCommand(); + final PrecacheCommand command = PrecacheCommand( + cache: cache, + logger: BufferLogger.test(), + flutterVersion: flutterVersion, + featureFlags: TestFeatureFlags( + isMacOSEnabled: true, + ), + platform: FakePlatform(environment: {}), + ); applyMocksToCommand(command); await createTestCommandRunner(command).run(const ['precache', '--force']); - expect(artifacts, unorderedEquals({ - DevelopmentArtifact.universal, - DevelopmentArtifact.iOS, - DevelopmentArtifact.androidGenSnapshot, - DevelopmentArtifact.androidMaven, - DevelopmentArtifact.androidInternalBuild, - })); - }, overrides: { - Cache: () => cache, - FlutterVersion: () => flutterVersion, - FeatureFlags: () => TestFeatureFlags( - isMacOSEnabled: true, - ), + + verify(cache.clearStampFiles()).called(1); }); } diff --git a/packages/flutter_tools/test/general.shard/cache_test.dart b/packages/flutter_tools/test/general.shard/cache_test.dart index 4201bb06b2..3682bc7e69 100644 --- a/packages/flutter_tools/test/general.shard/cache_test.dart +++ b/packages/flutter_tools/test/general.shard/cache_test.dart @@ -9,6 +9,7 @@ import 'package:flutter_tools/src/android/gradle_utils.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart' show InternetAddress, SocketException; import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/net.dart'; import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/platform.dart'; @@ -241,13 +242,13 @@ void main() { }); testUsingContext('Invalid URI for FLUTTER_STORAGE_BASE_URL throws ToolExit', () async { - when(globals.platform.environment).thenReturn(const { - 'FLUTTER_STORAGE_BASE_URL': ' http://foo', - }); final Cache cache = Cache(); + expect(() => cache.storageBaseUrl, throwsToolExit()); }, overrides: { - Platform: () => MockPlatform(), + Platform: () => FakePlatform(environment: { + 'FLUTTER_STORAGE_BASE_URL': ' http://foo', + }), }); }); @@ -624,6 +625,83 @@ void main() { contains(contains('release')), ])); }); + + testWithoutContext('Cache can delete stampfiles of artifacts', () { + final FileSystem fileSystem = MemoryFileSystem.test(); + final ArtifactSet artifactSet = MockIosUsbArtifacts(); + final BufferLogger logger = BufferLogger.test(); + + when(artifactSet.stampName).thenReturn('STAMP'); + final Cache cache = Cache( + artifacts: [ + artifactSet, + ], + logger: logger, + fileSystem: fileSystem, + platform: FakePlatform(), + osUtils: MockOperatingSystemUtils(), + rootOverride: fileSystem.currentDirectory, + ); + final File toolStampFile = fileSystem.file('bin/cache/flutter_tools.stamp'); + final File stampFile = cache.getStampFileFor(artifactSet.stampName); + stampFile.createSync(recursive: true); + toolStampFile.createSync(recursive: true); + + cache.clearStampFiles(); + + expect(logger.errorText, isEmpty); + expect(stampFile, isNot(exists)); + expect(toolStampFile, isNot(exists)); + }); + + testWithoutContext('Cache does not attempt to delete already missing stamp files', () { + final FileSystem fileSystem = MemoryFileSystem.test(); + final ArtifactSet artifactSet = MockIosUsbArtifacts(); + final BufferLogger logger = BufferLogger.test(); + + when(artifactSet.stampName).thenReturn('STAMP'); + final Cache cache = Cache( + artifacts: [ + artifactSet, + ], + logger: logger, + fileSystem: fileSystem, + platform: FakePlatform(), + osUtils: MockOperatingSystemUtils(), + rootOverride: fileSystem.currentDirectory, + ); + final File toolStampFile = fileSystem.file('bin/cache/flutter_tools.stamp'); + final File stampFile = cache.getStampFileFor(artifactSet.stampName); + toolStampFile.createSync(recursive: true); + + cache.clearStampFiles(); + + expect(logger.errorText, isEmpty); + expect(stampFile, isNot(exists)); + expect(toolStampFile, isNot(exists)); + }); + + testWithoutContext('Cache catches file system exception from missing tool stamp file', () { + final FileSystem fileSystem = MemoryFileSystem.test(); + final ArtifactSet artifactSet = MockIosUsbArtifacts(); + final BufferLogger logger = BufferLogger.test(); + + when(artifactSet.stampName).thenReturn('STAMP'); + final Cache cache = Cache( + artifacts: [ + artifactSet, + ], + logger: logger, + fileSystem: fileSystem, + platform: FakePlatform(), + osUtils: MockOperatingSystemUtils(), + rootOverride: fileSystem.currentDirectory, + ); + + cache.clearStampFiles(); + + expect(logger.errorText, contains('Failed to delete some stamp files')); + }); } class FakeCachedArtifact extends EngineCachedArtifact { @@ -689,7 +767,6 @@ class MockIosUsbArtifacts extends Mock implements IosUsbArtifacts {} class MockInternetAddress extends Mock implements InternetAddress {} class MockCache extends Mock implements Cache {} class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {} -class MockPlatform extends Mock implements Platform {} class MockVersionedPackageResolver extends Mock implements VersionedPackageResolver {} class MockHttpClientRequest extends Mock implements HttpClientRequest {} diff --git a/packages/flutter_tools/test/src/testbed.dart b/packages/flutter_tools/test/src/testbed.dart index 408df4e7f2..3d43fe841c 100644 --- a/packages/flutter_tools/test/src/testbed.dart +++ b/packages/flutter_tools/test/src/testbed.dart @@ -934,4 +934,7 @@ class FakeCache implements Cache { Future doesRemoteExist(String message, Uri url) async { return true; } + + @override + void clearStampFiles() {} }