// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package: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:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fakes.dart'; import '../../src/test_flutter_command_runner.dart'; void main() { late FakeCache cache; setUp(() { cache = FakeCache(); cache.isUpToDateValue = false; }); testUsingContext('precache should acquire lock', () async { final Platform platform = FakePlatform(environment: <String, String>{}); final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), platform: platform, featureFlags: TestFeatureFlags(), ); await createTestCommandRunner(command).run(const <String>['precache']); expect(cache.locked, true); }); testUsingContext('precache should not re-entrantly acquire lock', () async { final Platform platform = FakePlatform( operatingSystem: 'windows', environment: <String, String>{ 'FLUTTER_ROOT': 'flutter', 'FLUTTER_ALREADY_LOCKED': 'true', }, ); final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: platform, ); await createTestCommandRunner(command).run(const <String>['precache']); expect(cache.locked, false); }); testUsingContext('precache downloads web artifacts on dev branch when feature is enabled.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(isWebEnabled: true), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache', '--web', '--no-android', '--no-ios']); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.web, })); }); testUsingContext('precache does not download web artifacts on dev branch when feature is enabled.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache', '--web', '--no-android', '--no-ios']); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, })); }); testUsingContext('precache downloads macOS artifacts on dev branch when macOS is enabled.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(isMacOSEnabled: true), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache', '--macos', '--no-android', '--no-ios']); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.macOS, })); }); testUsingContext('precache does not download macOS artifacts on dev branch when feature is enabled.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache', '--macos', '--no-android', '--no-ios']); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, })); }); testUsingContext('precache downloads Windows artifacts on dev branch when feature is enabled.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(isWindowsEnabled: true), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache', '--windows', '--no-android', '--no-ios']); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.windows, })); }); testUsingContext('precache does not download Windows artifacts on dev branch when feature is enabled.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache', '--windows', '--no-android', '--no-ios']); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, })); }); testUsingContext('precache downloads Linux artifacts on dev branch when feature is enabled.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(isLinuxEnabled: true), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache', '--linux', '--no-android', '--no-ios']); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.linux, })); }); testUsingContext('precache does not download Linux artifacts on dev branch when feature is enabled.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache', '--linux', '--no-android', '--no-ios']); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, })); }); testUsingContext('precache exits if requesting mismatched artifacts.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: FakePlatform(environment: <String, String>{}), ); expect(createTestCommandRunner(command).run(const <String>['precache', '--no-android', '--android_gen_snapshot', ]), throwsToolExit(message: '--android_gen_snapshot requires --android')); }); testUsingContext('precache adds artifact flags to requested artifacts', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags( isWebEnabled: true, isLinuxEnabled: true, isMacOSEnabled: true, isWindowsEnabled: true, isFuchsiaEnabled: true, ), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run( const <String>[ 'precache', '--ios', '--android', '--web', '--macos', '--linux', '--windows', '--fuchsia', '--flutter_runner', ], ); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.iOS, DevelopmentArtifact.androidGenSnapshot, DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidInternalBuild, DevelopmentArtifact.web, DevelopmentArtifact.macOS, DevelopmentArtifact.linux, DevelopmentArtifact.windows, DevelopmentArtifact.fuchsia, DevelopmentArtifact.flutterRunner, })); }); testUsingContext('precache expands android artifacts when the android flag is used', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run( const <String>[ 'precache', '--no-ios', '--android', ], ); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.androidGenSnapshot, DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidInternalBuild, })); }); testUsingContext('precache adds artifact flags to requested android artifacts', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run( const <String>[ 'precache', '--no-ios', '--android', '--android_gen_snapshot', '--android_maven', '--android_internal_build', ], ); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.androidGenSnapshot, DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidInternalBuild, })); }); testUsingContext('precache downloads iOS and Android artifacts by default', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run( const <String>[ 'precache', ], ); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.iOS, DevelopmentArtifact.androidGenSnapshot, DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidInternalBuild, })); }); testUsingContext('precache --all-platforms gets all artifacts', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags( isWebEnabled: true, isLinuxEnabled: true, isMacOSEnabled: true, isWindowsEnabled: true, isFuchsiaEnabled: true, ), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run( const <String>[ 'precache', '--all-platforms', ], ); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.iOS, DevelopmentArtifact.androidGenSnapshot, DevelopmentArtifact.androidMaven, DevelopmentArtifact.androidInternalBuild, DevelopmentArtifact.web, DevelopmentArtifact.macOS, DevelopmentArtifact.linux, DevelopmentArtifact.windows, DevelopmentArtifact.fuchsia, DevelopmentArtifact.flutterRunner, })); }); testUsingContext('precache with default artifacts does not override platform filtering', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags(), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run( const <String>[ 'precache', ], ); expect(cache.platformOverrideArtifacts, <String>{}); }); testUsingContext('precache with explicit artifact options overrides platform filtering', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags( isMacOSEnabled: true, ), platform: FakePlatform( operatingSystem: 'windows', environment: <String, String>{ 'FLUTTER_ROOT': 'flutter', 'FLUTTER_ALREADY_LOCKED': 'true', }, ), ); await createTestCommandRunner(command).run( const <String>[ 'precache', '--no-ios', '--no-android', '--macos', ], ); expect(cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.universal, DevelopmentArtifact.macOS, })); expect(cache.platformOverrideArtifacts, <String>{'macos'}); }); testUsingContext('precache deletes artifact stampfiles when --force is provided', () async { cache.isUpToDateValue = true; final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags( isMacOSEnabled: true, ), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache', '--force']); expect(cache.clearedStampFiles, true); }); testUsingContext('precache downloads all enabled platforms if no flags are provided.', () async { final PrecacheCommand command = PrecacheCommand( cache: cache, logger: BufferLogger.test(), featureFlags: TestFeatureFlags( isWebEnabled: true, isLinuxEnabled: true, isWindowsEnabled: true, isMacOSEnabled: true, isIOSEnabled: false, isAndroidEnabled: false, ), platform: FakePlatform(environment: <String, String>{}), ); await createTestCommandRunner(command).run(const <String>['precache']); expect( cache.artifacts, unorderedEquals(<DevelopmentArtifact>{ DevelopmentArtifact.web, DevelopmentArtifact.macOS, DevelopmentArtifact.windows, DevelopmentArtifact.linux, DevelopmentArtifact.universal, // iOS and android specifically excluded })); }); } class FakeCache extends Fake implements Cache { bool isUpToDateValue = false; bool clearedStampFiles = false; bool locked = false; Set<DevelopmentArtifact>? artifacts; @override Future<void> lock() async { locked = true; } @override void releaseLock() { locked = false; } @override Future<bool> isUpToDate() async => isUpToDateValue; @override Future<void> updateAll(Set<DevelopmentArtifact> requiredArtifacts, {bool offline = false}) async { artifacts = requiredArtifacts; } @override void clearStampFiles() { clearedStampFiles = true; } @override Set<String>? platformOverrideArtifacts; @override bool includeAllPlatforms = false; }