Unverified Commit 07e2d6f6 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[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
parent 77310c15
...@@ -48,6 +48,7 @@ import 'src/commands/train.dart'; ...@@ -48,6 +48,7 @@ import 'src/commands/train.dart';
import 'src/commands/update_packages.dart'; import 'src/commands/update_packages.dart';
import 'src/commands/upgrade.dart'; import 'src/commands/upgrade.dart';
import 'src/commands/version.dart'; import 'src/commands/version.dart';
import 'src/features.dart';
import 'src/globals.dart' as globals; import 'src/globals.dart' as globals;
import 'src/runner/flutter_command.dart'; import 'src/runner/flutter_command.dart';
import 'src/web/compile.dart'; import 'src/web/compile.dart';
...@@ -98,7 +99,13 @@ Future<void> main(List<String> args) async { ...@@ -98,7 +99,13 @@ Future<void> main(List<String> args) async {
LogsCommand(), LogsCommand(),
MakeHostAppEditableCommand(), MakeHostAppEditableCommand(),
PackagesCommand(), PackagesCommand(),
PrecacheCommand(verboseHelp: verboseHelp), PrecacheCommand(
verboseHelp: verboseHelp,
cache: globals.cache,
logger: globals.logger,
platform: globals.platform,
featureFlags: featureFlags,
),
RunCommand(verboseHelp: verboseHelp), RunCommand(verboseHelp: verboseHelp),
ScreenshotCommand(), ScreenshotCommand(),
ShellCompletionCommand(), ShellCompletionCommand(),
......
...@@ -37,6 +37,7 @@ class DevelopmentArtifact { ...@@ -37,6 +37,7 @@ class DevelopmentArtifact {
/// Artifacts required for Android development. /// Artifacts required for Android development.
static const DevelopmentArtifact androidGenSnapshot = DevelopmentArtifact._('android_gen_snapshot'); static const DevelopmentArtifact androidGenSnapshot = DevelopmentArtifact._('android_gen_snapshot');
static const DevelopmentArtifact androidMaven = DevelopmentArtifact._('android_maven'); static const DevelopmentArtifact androidMaven = DevelopmentArtifact._('android_maven');
// Artifacts used for internal builds. // Artifacts used for internal builds.
static const DevelopmentArtifact androidInternalBuild = DevelopmentArtifact._('android_internal_build'); static const DevelopmentArtifact androidInternalBuild = DevelopmentArtifact._('android_internal_build');
...@@ -243,8 +244,8 @@ class Cache { ...@@ -243,8 +244,8 @@ class Cache {
/// Checks if the current process owns the lock for the cache directory at /// Checks if the current process owns the lock for the cache directory at
/// this very moment; throws a [StateError] if it doesn't. /// this very moment; throws a [StateError] if it doesn't.
static void checkLockAcquired() { static void checkLockAcquired([Platform platform]) {
if (_lockEnabled && _lock == null && globals.platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { if (_lockEnabled && _lock == null && (platform ?? globals.platform).environment['FLUTTER_ALREADY_LOCKED'] != 'true') {
throw StateError( throw StateError(
'The current process does not own the lock for the cache directory. This is a bug in Flutter CLI tools.', '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 { ...@@ -372,6 +373,21 @@ class Cache {
return versionFile.existsSync() ? versionFile.readAsStringSync().trim() : null; 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) { String getStampFor(String artifactName) {
final File stampFile = getStampFileFor(artifactName); final File stampFile = getStampFileFor(artifactName);
return stampFile.existsSync() ? stampFile.readAsStringSync().trim() : null; return stampFile.existsSync() ? stampFile.readAsStringSync().trim() : null;
...@@ -508,6 +524,13 @@ abstract class ArtifactSet { ...@@ -508,6 +524,13 @@ abstract class ArtifactSet {
/// Updates the artifact. /// Updates the artifact.
Future<void> update(); Future<void> 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. /// An artifact set managed by the cache.
...@@ -520,11 +543,10 @@ abstract class CachedArtifact extends ArtifactSet { ...@@ -520,11 +543,10 @@ abstract class CachedArtifact extends ArtifactSet {
final Cache cache; final Cache cache;
/// The canonical name of the artifact. @override
final String name; final String name;
// The name of the stamp file. Defaults to the same as the @override
// artifact name.
String get stampName => name; String get stampName => name;
Directory get location => cache.getArtifactDirectory(name); Directory get location => cache.getArtifactDirectory(name);
...@@ -1043,6 +1065,9 @@ class AndroidMavenArtifacts extends ArtifactSet { ...@@ -1043,6 +1065,9 @@ class AndroidMavenArtifacts extends ArtifactSet {
// Therefore, call Gradle to figure this out. // Therefore, call Gradle to figure this out.
return false; return false;
} }
@override
String get name => 'android-maven-artifacts';
} }
class IOSEngineArtifacts extends EngineCachedArtifact { class IOSEngineArtifacts extends EngineCachedArtifact {
......
...@@ -4,18 +4,36 @@ ...@@ -4,18 +4,36 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/logger.dart';
import '../base/platform.dart';
import '../cache.dart'; import '../cache.dart';
import '../features.dart'; import '../features.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../runner/flutter_command.dart'; 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 { 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, argParser.addFlag('all-platforms', abbr: 'a', negatable: false,
help: 'Precache artifacts for all host platforms.'); help: 'Precache artifacts for all host platforms.');
argParser.addFlag('force', abbr: 'f', negatable: false, 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, argParser.addFlag('android', negatable: true, defaultsTo: true,
help: 'Precache artifacts for Android development.', help: 'Precache artifacts for Android development.',
hide: verboseHelp); hide: verboseHelp);
...@@ -48,6 +66,12 @@ class PrecacheCommand extends FlutterCommand { ...@@ -48,6 +66,12 @@ class PrecacheCommand extends FlutterCommand {
help: 'Precache the unsigned mac binaries when available.', hide: true); 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 @override
final String name = 'precache'; final String name = 'precache';
...@@ -112,26 +136,29 @@ class PrecacheCommand extends FlutterCommand { ...@@ -112,26 +136,29 @@ class PrecacheCommand extends FlutterCommand {
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
// Re-lock the cache. // Re-lock the cache.
if (globals.platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { if (_platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') {
await Cache.lock(); await Cache.lock();
} }
if (boolArg('force')) {
_cache.clearStampFiles();
}
final bool includeAllPlatforms = boolArg('all-platforms'); final bool includeAllPlatforms = boolArg('all-platforms');
if (includeAllPlatforms) { if (includeAllPlatforms) {
globals.cache.includeAllPlatforms = true; _cache.includeAllPlatforms = true;
} }
if (boolArg('use-unsigned-mac-binaries')) { if (boolArg('use-unsigned-mac-binaries')) {
globals.cache.useUnsignedMacBinaries = true; _cache.useUnsignedMacBinaries = true;
} }
globals.cache.platformOverrideArtifacts = _explicitArtifactSelections(); _cache.platformOverrideArtifacts = _explicitArtifactSelections();
final Map<String, String> umbrellaForArtifact = _umbrellaForArtifactMap(); final Map<String, String> umbrellaForArtifact = _umbrellaForArtifactMap();
final Set<DevelopmentArtifact> requiredArtifacts = <DevelopmentArtifact>{}; final Set<DevelopmentArtifact> requiredArtifacts = <DevelopmentArtifact>{};
for (final DevelopmentArtifact artifact in DevelopmentArtifact.values) { for (final DevelopmentArtifact artifact in DevelopmentArtifact.values) {
// Don't include unstable artifacts on stable branches. // Don't include unstable artifacts on stable branches.
if (!globals.flutterVersion.isMaster && artifact.unstable) { if (!(_flutterVersion ?? globals.flutterVersion).isMaster && artifact.unstable) {
continue; continue;
} }
if (artifact.feature != null && !featureFlags.isEnabled(artifact.feature)) { if (artifact.feature != null && !_featureFlags.isEnabled(artifact.feature)) {
continue; continue;
} }
...@@ -140,11 +167,10 @@ class PrecacheCommand extends FlutterCommand { ...@@ -140,11 +167,10 @@ class PrecacheCommand extends FlutterCommand {
requiredArtifacts.add(artifact); requiredArtifacts.add(artifact);
} }
} }
final bool forceUpdate = boolArg('force'); if (!_cache.isUpToDate()) {
if (forceUpdate || !globals.cache.isUpToDate()) { await _cache.updateAll(requiredArtifacts);
await globals.cache.updateAll(requiredArtifacts);
} else { } else {
globals.printStatus('Already up-to-date.'); _logger.printStatus('Already up-to-date.');
} }
return FlutterCommandResult.success(); return FlutterCommandResult.success();
} }
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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/base/platform.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/precache.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/runner/flutter_command.dart';
import 'package:flutter_tools/src/version.dart'; import 'package:flutter_tools/src/version.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
...@@ -38,38 +38,53 @@ void main() { ...@@ -38,38 +38,53 @@ void main() {
}); });
testUsingContext('precache should acquire lock', () async { testUsingContext('precache should acquire lock', () async {
final PrecacheCommand command = PrecacheCommand(); final Platform platform = FakePlatform(environment: <String, String>{});
final PrecacheCommand command = PrecacheCommand(
cache: cache,
logger: BufferLogger.test(),
flutterVersion: flutterVersion,
platform: platform,
featureFlags: TestFeatureFlags(),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache']); await createTestCommandRunner(command).run(const <String>['precache']);
expect(Cache.isLocked(), isTrue); expect(Cache.isLocked(), isTrue);
// Do not throw StateError, lock is acquired. // Do not throw StateError, lock is acquired.
Cache.checkLockAcquired(); expect(() => Cache.checkLockAcquired(platform), returnsNormally);
}, overrides: <Type, Generator>{
Cache: () => cache,
}); });
testUsingContext('precache should not re-entrantly acquire lock', () async { testUsingContext('precache should not re-entrantly acquire lock', () async {
final PrecacheCommand command = PrecacheCommand(); final Platform platform = FakePlatform(
applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache']);
expect(Cache.isLocked(), isFalse);
// Do not throw StateError, acquired reentrantly with FLUTTER_ALREADY_LOCKED.
Cache.checkLockAcquired();
}, overrides: <Type, Generator>{
Cache: () => cache,
Platform: () => FakePlatform(
operatingSystem: 'windows', operatingSystem: 'windows',
environment: <String, String>{ environment: <String, String>{
'FLUTTER_ROOT': 'flutter', 'FLUTTER_ROOT': 'flutter',
'FLUTTER_ALREADY_LOCKED': 'true', '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 <String>['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 { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache', '--web', '--no-android', '--no-ios']); await createTestCommandRunner(command).run(const <String>['precache', '--web', '--no-android', '--no-ios']);
...@@ -77,26 +92,32 @@ void main() { ...@@ -77,26 +92,32 @@ void main() {
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
DevelopmentArtifact.web, DevelopmentArtifact.web,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
}); });
testUsingContext('precache does not download web artifacts on dev branch when feature is enabled.', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache', '--web', '--no-android', '--no-ios']); await createTestCommandRunner(command).run(const <String>['precache', '--web', '--no-android', '--no-ios']);
expect(artifacts, unorderedEquals(<DevelopmentArtifact>{ expect(artifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: false),
}); });
testUsingContext('precache downloads macOS artifacts on dev branch when macOS is enabled.', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache', '--macos', '--no-android', '--no-ios']); await createTestCommandRunner(command).run(const <String>['precache', '--macos', '--no-android', '--no-ios']);
...@@ -104,26 +125,32 @@ void main() { ...@@ -104,26 +125,32 @@ void main() {
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
DevelopmentArtifact.macOS, DevelopmentArtifact.macOS,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
}); });
testUsingContext('precache does not download macOS artifacts on dev branch when feature is enabled.', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache', '--macos', '--no-android', '--no-ios']); await createTestCommandRunner(command).run(const <String>['precache', '--macos', '--no-android', '--no-ios']);
expect(artifacts, unorderedEquals(<DevelopmentArtifact>{ expect(artifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false),
}); });
testUsingContext('precache downloads Windows artifacts on dev branch when feature is enabled.', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache', '--windows', '--no-android', '--no-ios']); await createTestCommandRunner(command).run(const <String>['precache', '--windows', '--no-android', '--no-ios']);
...@@ -131,26 +158,32 @@ void main() { ...@@ -131,26 +158,32 @@ void main() {
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
DevelopmentArtifact.windows, DevelopmentArtifact.windows,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('precache does not download Windows artifacts on dev branch when feature is enabled.', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache', '--windows', '--no-android', '--no-ios']); await createTestCommandRunner(command).run(const <String>['precache', '--windows', '--no-android', '--no-ios']);
expect(artifacts, unorderedEquals(<DevelopmentArtifact>{ expect(artifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false),
}); });
testUsingContext('precache downloads Linux artifacts on dev branch when feature is enabled.', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache', '--linux', '--no-android', '--no-ios']); await createTestCommandRunner(command).run(const <String>['precache', '--linux', '--no-android', '--no-ios']);
...@@ -158,39 +191,53 @@ void main() { ...@@ -158,39 +191,53 @@ void main() {
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
DevelopmentArtifact.linux, DevelopmentArtifact.linux,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
}); });
testUsingContext('precache does not download Linux artifacts on dev branch when feature is enabled.', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache', '--linux', '--no-android', '--no-ios']); await createTestCommandRunner(command).run(const <String>['precache', '--linux', '--no-android', '--no-ios']);
expect(artifacts, unorderedEquals(<DevelopmentArtifact>{ expect(artifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
}); });
testUsingContext('precache exits if requesting mismatched artifacts.', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
expect(createTestCommandRunner(command).run(const <String>['precache', expect(createTestCommandRunner(command).run(const <String>['precache',
'--no-android', '--no-android',
'--android_gen_snapshot', '--android_gen_snapshot',
]), throwsToolExit(message: '--android_gen_snapshot requires --android')); ]), throwsToolExit(message: '--android_gen_snapshot requires --android'));
}, overrides: <Type, Generator>{
Cache: () => cache,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: false),
}); });
testUsingContext('precache adds artifact flags to requested artifacts', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>[ const <String>[
...@@ -218,19 +265,16 @@ void main() { ...@@ -218,19 +265,16 @@ void main() {
DevelopmentArtifact.fuchsia, DevelopmentArtifact.fuchsia,
DevelopmentArtifact.flutterRunner, DevelopmentArtifact.flutterRunner,
})); }));
}, overrides: <Type, Generator>{
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 { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>[ const <String>[
...@@ -245,12 +289,16 @@ void main() { ...@@ -245,12 +289,16 @@ void main() {
DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidMaven,
DevelopmentArtifact.androidInternalBuild, DevelopmentArtifact.androidInternalBuild,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
}); });
testUsingContext('precache adds artifact flags to requested android artifacts', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>[ const <String>[
...@@ -267,12 +315,16 @@ void main() { ...@@ -267,12 +315,16 @@ void main() {
DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidMaven,
DevelopmentArtifact.androidInternalBuild, DevelopmentArtifact.androidInternalBuild,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
}); });
testUsingContext('precache adds artifact flags to requested artifacts on stable', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>[ const <String>[
...@@ -296,14 +348,16 @@ void main() { ...@@ -296,14 +348,16 @@ void main() {
DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidMaven,
DevelopmentArtifact.androidInternalBuild, DevelopmentArtifact.androidInternalBuild,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
FlutterVersion: () => flutterVersion,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: false),
}); });
testUsingContext('precache downloads iOS and Android artifacts by default', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
...@@ -319,12 +373,21 @@ void main() { ...@@ -319,12 +373,21 @@ void main() {
DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidMaven,
DevelopmentArtifact.androidInternalBuild, DevelopmentArtifact.androidInternalBuild,
})); }));
}, overrides: <Type, Generator>{
Cache: () => cache,
}); });
testUsingContext('precache --all-platforms gets all artifacts', () async { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
...@@ -347,19 +410,16 @@ void main() { ...@@ -347,19 +410,16 @@ void main() {
DevelopmentArtifact.fuchsia, DevelopmentArtifact.fuchsia,
DevelopmentArtifact.flutterRunner, DevelopmentArtifact.flutterRunner,
})); }));
}, overrides: <Type, Generator>{
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 { 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: <String, String>{}),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
...@@ -369,13 +429,24 @@ void main() { ...@@ -369,13 +429,24 @@ void main() {
); );
verify(cache.platformOverrideArtifacts = <String>{}); verify(cache.platformOverrideArtifacts = <String>{});
}, overrides: <Type, Generator>{
Cache: () => cache,
FlutterVersion: () => masterFlutterVersion,
}); });
testUsingContext('precache with explicit artifact options overrides platform filtering', () async { 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: <String, String>{
'FLUTTER_ROOT': 'flutter',
'FLUTTER_ALREADY_LOCKED': 'true',
},
),
);
applyMocksToCommand(command); applyMocksToCommand(command);
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
...@@ -392,39 +463,23 @@ void main() { ...@@ -392,39 +463,23 @@ void main() {
DevelopmentArtifact.macOS, DevelopmentArtifact.macOS,
})); }));
verify(cache.platformOverrideArtifacts = <String>{'macos'}); verify(cache.platformOverrideArtifacts = <String>{'macos'});
}, overrides: <Type, Generator>{
Cache: () => cache,
FlutterVersion: () => masterFlutterVersion,
FeatureFlags: () => TestFeatureFlags(
isMacOSEnabled: true,
),
Platform: () => FakePlatform(
operatingSystem: 'windows',
environment: <String, String>{
'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); when(cache.isUpToDate()).thenReturn(true);
final PrecacheCommand command = PrecacheCommand(); final PrecacheCommand command = PrecacheCommand(
applyMocksToCommand(command); cache: cache,
await createTestCommandRunner(command).run(const <String>['precache', '--force']); logger: BufferLogger.test(),
expect(artifacts, unorderedEquals(<DevelopmentArtifact>{ flutterVersion: flutterVersion,
DevelopmentArtifact.universal, featureFlags: TestFeatureFlags(
DevelopmentArtifact.iOS,
DevelopmentArtifact.androidGenSnapshot,
DevelopmentArtifact.androidMaven,
DevelopmentArtifact.androidInternalBuild,
}));
}, overrides: <Type, Generator>{
Cache: () => cache,
FlutterVersion: () => flutterVersion,
FeatureFlags: () => TestFeatureFlags(
isMacOSEnabled: true, isMacOSEnabled: true,
), ),
platform: FakePlatform(environment: <String, String>{}),
);
applyMocksToCommand(command);
await createTestCommandRunner(command).run(const <String>['precache', '--force']);
verify(cache.clearStampFiles()).called(1);
}); });
} }
......
...@@ -9,6 +9,7 @@ import 'package:flutter_tools/src/android/gradle_utils.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/file_system.dart';
import 'package:flutter_tools/src/base/io.dart' show InternetAddress, SocketException; 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/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/net.dart'; import 'package:flutter_tools/src/base/net.dart';
import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
...@@ -241,13 +242,13 @@ void main() { ...@@ -241,13 +242,13 @@ void main() {
}); });
testUsingContext('Invalid URI for FLUTTER_STORAGE_BASE_URL throws ToolExit', () async { testUsingContext('Invalid URI for FLUTTER_STORAGE_BASE_URL throws ToolExit', () async {
when(globals.platform.environment).thenReturn(const <String, String>{
'FLUTTER_STORAGE_BASE_URL': ' http://foo',
});
final Cache cache = Cache(); final Cache cache = Cache();
expect(() => cache.storageBaseUrl, throwsToolExit()); expect(() => cache.storageBaseUrl, throwsToolExit());
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => MockPlatform(), Platform: () => FakePlatform(environment: <String, String>{
'FLUTTER_STORAGE_BASE_URL': ' http://foo',
}),
}); });
}); });
...@@ -624,6 +625,83 @@ void main() { ...@@ -624,6 +625,83 @@ void main() {
contains(contains('release')), 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>[
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>[
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>[
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 { class FakeCachedArtifact extends EngineCachedArtifact {
...@@ -689,7 +767,6 @@ class MockIosUsbArtifacts extends Mock implements IosUsbArtifacts {} ...@@ -689,7 +767,6 @@ class MockIosUsbArtifacts extends Mock implements IosUsbArtifacts {}
class MockInternetAddress extends Mock implements InternetAddress {} class MockInternetAddress extends Mock implements InternetAddress {}
class MockCache extends Mock implements Cache {} class MockCache extends Mock implements Cache {}
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {} class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
class MockPlatform extends Mock implements Platform {}
class MockVersionedPackageResolver extends Mock implements VersionedPackageResolver {} class MockVersionedPackageResolver extends Mock implements VersionedPackageResolver {}
class MockHttpClientRequest extends Mock implements HttpClientRequest {} class MockHttpClientRequest extends Mock implements HttpClientRequest {}
......
...@@ -934,4 +934,7 @@ class FakeCache implements Cache { ...@@ -934,4 +934,7 @@ class FakeCache implements Cache {
Future<bool> doesRemoteExist(String message, Uri url) async { Future<bool> doesRemoteExist(String message, Uri url) async {
return true; return true;
} }
@override
void clearStampFiles() {}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment