Kate Lovett 9d96df2364
Modernize framework lints (#179089)
WIP

Commits separated as follows:
- Update lints in analysis_options files
- Run `dart fix --apply`
- Clean up leftover analysis issues 
- Run `dart format .` in the right places.

Local analysis and testing passes. Checking CI now.

Part of https://github.com/flutter/flutter/issues/178827
- Adoption of flutter_lints in examples/api coming in a separate change
(cc @loic-sharma)

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-11-26 01:10:39 +00:00

1468 lines
52 KiB
Dart

// 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:collection/collection.dart' show IterableExtension;
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/flutter_cache.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart';
import 'package:test/fake.dart';
import '../src/common.dart';
import '../src/context.dart';
import '../src/fakes.dart';
const unameCommandForX64 = FakeCommand(command: <String>['uname', '-m'], stdout: 'x86_64');
const unameCommandForArm64 = FakeCommand(command: <String>['uname', '-m'], stdout: 'aarch64');
void main() {
late FakeProcessManager fakeProcessManager;
setUp(() {
fakeProcessManager = FakeProcessManager.empty();
});
Cache createCache(Platform platform) {
return Cache.test(platform: platform, processManager: fakeProcessManager);
}
group('Cache.checkLockAcquired', () {
setUp(() {
Cache.enableLocking();
});
tearDown(() {
// Restore locking to prevent potential side-effects in
// tests outside this group (this option is globally shared).
Cache.enableLocking();
});
testWithoutContext('should throw when locking is not acquired', () {
final cache = Cache.test(processManager: FakeProcessManager.any());
expect(cache.checkLockAcquired, throwsStateError);
});
testWithoutContext('should not throw when locking is disabled', () {
final cache = Cache.test(processManager: FakeProcessManager.any());
Cache.disableLocking();
expect(cache.checkLockAcquired, returnsNormally);
});
testWithoutContext('should not throw when lock is acquired', () async {
final String? oldRoot = Cache.flutterRoot;
Cache.flutterRoot = '';
try {
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
fileSystem
.file(fileSystem.path.join('bin', 'cache', 'lockfile'))
.createSync(recursive: true);
await cache.lock();
expect(cache.checkLockAcquired, returnsNormally);
expect(cache.releaseLock, returnsNormally);
} finally {
Cache.flutterRoot = oldRoot;
}
// TODO(zanderso): implement support for lock so this can be tested with the memory file system.
}, skip: true); // https://github.com/flutter/flutter/issues/87923
testWithoutContext('throws tool exit when lockfile open fails', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
fileSystem.file(fileSystem.path.join('bin', 'cache', 'lockfile')).createSync(recursive: true);
expect(() async => cache.lock(), throwsToolExit());
// TODO(zanderso): implement support for lock so this can be tested with the memory file system.
}, skip: true); // https://github.com/flutter/flutter/issues/87923
testWithoutContext('should not throw when FLUTTER_ALREADY_LOCKED is set', () {
final cache = Cache.test(
platform: FakePlatform(environment: <String, String>{'FLUTTER_ALREADY_LOCKED': 'true'}),
processManager: FakeProcessManager.any(),
);
expect(cache.checkLockAcquired, returnsNormally);
});
});
group('Cache', () {
testWithoutContext('Continues on failed stamp file update', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final logger = BufferLogger.test();
final Directory artifactDir = fileSystem.systemTempDirectory.createTempSync(
'flutter_cache_test_artifact.',
);
final Directory downloadDir = fileSystem.systemTempDirectory.createTempSync(
'flutter_cache_test_download.',
);
final Cache cache = FakeSecondaryCache()
..version = 'asdasd'
..artifactDirectory = artifactDir
..downloadDir = downloadDir
..onSetStamp = (String name, String version) {
throw const FileSystemException('stamp write failed');
};
final artifact = FakeSimpleArtifact(cache);
await artifact.update(FakeArtifactUpdater(), logger, fileSystem, FakeOperatingSystemUtils());
expect(logger.warningText, contains('stamp write failed'));
});
testWithoutContext('Continues on missing version file', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final logger = BufferLogger.test();
final Directory artifactDir = fileSystem.systemTempDirectory.createTempSync(
'flutter_cache_test_artifact.',
);
final Directory downloadDir = fileSystem.systemTempDirectory.createTempSync(
'flutter_cache_test_download.',
);
final Cache cache = FakeSecondaryCache()
..version =
null // version is missing.
..artifactDirectory = artifactDir
..downloadDir = downloadDir;
final artifact = FakeSimpleArtifact(cache);
await artifact.update(FakeArtifactUpdater(), logger, fileSystem, FakeOperatingSystemUtils());
expect(logger.warningText, contains('No known version for the artifact name "fake"'));
});
testWithoutContext(
'Gradle wrapper should not be up to date, if some cached artifact is not available',
() {
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
final gradleWrapper = GradleWrapper(cache);
final Directory directory = cache.getCacheDir(
fileSystem.path.join('artifacts', 'gradle_wrapper'),
);
fileSystem
.file(fileSystem.path.join(directory.path, 'gradle', 'wrapper', 'gradle-wrapper.jar'))
.createSync(recursive: true);
expect(gradleWrapper.isUpToDateInner(fileSystem), false);
},
);
testWithoutContext('Gradle wrapper will delete .properties/NOTICES if they exist', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final Directory artifactDir = fileSystem.systemTempDirectory.createTempSync(
'flutter_cache_test_artifact.',
);
final cache = FakeSecondaryCache()
..artifactDirectory = artifactDir
..version = '123456';
final operatingSystemUtils = OperatingSystemUtils(
processManager: FakeProcessManager.any(),
platform: FakePlatform(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
);
final gradleWrapper = GradleWrapper(cache);
final File propertiesFile = fileSystem.file(
fileSystem.path.join(artifactDir.path, 'gradle', 'wrapper', 'gradle-wrapper.properties'),
)..createSync(recursive: true);
final File noticeFile = fileSystem.file(fileSystem.path.join(artifactDir.path, 'NOTICE'))
..createSync(recursive: true);
await gradleWrapper.updateInner(FakeArtifactUpdater(), fileSystem, operatingSystemUtils);
expect(propertiesFile, isNot(exists));
expect(noticeFile, isNot(exists));
});
testWithoutContext(
'Gradle wrapper should be up to date, only if all cached artifact are available',
() {
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
final gradleWrapper = GradleWrapper(cache);
final Directory directory = cache.getCacheDir(
fileSystem.path.join('artifacts', 'gradle_wrapper'),
);
fileSystem
.file(fileSystem.path.join(directory.path, 'gradle', 'wrapper', 'gradle-wrapper.jar'))
.createSync(recursive: true);
fileSystem
.file(fileSystem.path.join(directory.path, 'gradlew'))
.createSync(recursive: true);
fileSystem
.file(fileSystem.path.join(directory.path, 'gradlew.bat'))
.createSync(recursive: true);
expect(gradleWrapper.isUpToDateInner(fileSystem), true);
},
);
testWithoutContext('should not be up to date, if some cached artifact is not', () async {
final CachedArtifact artifact1 = FakeSecondaryCachedArtifact()..upToDate = true;
final CachedArtifact artifact2 = FakeSecondaryCachedArtifact()..upToDate = false;
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(
fileSystem: fileSystem,
artifacts: <CachedArtifact>[artifact1, artifact2],
processManager: FakeProcessManager.any(),
);
expect(await cache.isUpToDate(), isFalse);
});
testWithoutContext('should be up to date, if all cached artifacts are', () async {
final artifact1 = FakeSecondaryCachedArtifact()..upToDate = true;
final artifact2 = FakeSecondaryCachedArtifact()..upToDate = true;
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(
fileSystem: fileSystem,
artifacts: <CachedArtifact>[artifact1, artifact2],
processManager: FakeProcessManager.any(),
);
expect(await cache.isUpToDate(), isTrue);
});
testWithoutContext('should update cached artifacts which are not up to date', () async {
final artifact1 = FakeSecondaryCachedArtifact()..upToDate = true;
final artifact2 = FakeSecondaryCachedArtifact()..upToDate = false;
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(
fileSystem: fileSystem,
artifacts: <CachedArtifact>[artifact1, artifact2],
processManager: FakeProcessManager.any(),
);
await cache.updateAll(<DevelopmentArtifact>{DevelopmentArtifact.universal});
expect(artifact1.didUpdate, false);
expect(artifact2.didUpdate, true);
});
testWithoutContext(
"getter dyLdLibEntry concatenates the output of each artifact's dyLdLibEntry getter",
() async {
final artifact1 = FakeIosUsbArtifacts();
final artifact2 = FakeIosUsbArtifacts();
final artifact3 = FakeIosUsbArtifacts();
artifact1.environment = <String, String>{
'DYLD_LIBRARY_PATH': '/path/to/alpha:/path/to/beta',
};
artifact2.environment = <String, String>{
'DYLD_LIBRARY_PATH': '/path/to/gamma:/path/to/delta:/path/to/epsilon',
};
artifact3.environment = <String, String>{'DYLD_LIBRARY_PATH': ''};
final cache = Cache.test(
artifacts: <CachedArtifact>[artifact1, artifact2, artifact3],
processManager: FakeProcessManager.any(),
);
expect(cache.dyLdLibEntry.key, 'DYLD_LIBRARY_PATH');
expect(
cache.dyLdLibEntry.value,
'/path/to/alpha:/path/to/beta:/path/to/gamma:/path/to/delta:/path/to/epsilon',
);
},
);
testWithoutContext('failed storage.googleapis.com download shows China warning', () async {
final InternetAddress address = (await InternetAddress.lookup(
'storage.googleapis.com',
)).first;
final artifact1 = FakeSecondaryCachedArtifact()..upToDate = false;
final artifact2 = FakeSecondaryCachedArtifact()
..upToDate = false
..updateException = SocketException('Connection reset by peer', address: address);
final logger = BufferLogger.test();
final cache = Cache.test(
artifacts: <CachedArtifact>[artifact1, artifact2],
processManager: FakeProcessManager.any(),
logger: logger,
);
await expectLater(
() => cache.updateAll(<DevelopmentArtifact>{DevelopmentArtifact.universal}),
throwsException,
);
expect(artifact1.didUpdate, true);
// Don't continue when retrieval fails.
expect(artifact2.didUpdate, false);
expect(logger.errorText, contains('https://flutter.dev/to/china-setup'));
});
testWithoutContext('Invalid URI for FLUTTER_STORAGE_BASE_URL throws ToolExit', () async {
final cache = Cache.test(
platform: FakePlatform(
environment: <String, String>{'FLUTTER_STORAGE_BASE_URL': ' http://foo'},
),
processManager: FakeProcessManager.any(),
);
expect(() => cache.storageBaseUrl, throwsToolExit());
});
testWithoutContext('overridden storage base url prints warning', () async {
final logger = BufferLogger.test();
const baseUrl = 'https://storage.com';
final cache = Cache.test(
platform: FakePlatform(environment: <String, String>{'FLUTTER_STORAGE_BASE_URL': baseUrl}),
processManager: FakeProcessManager.any(),
logger: logger,
);
expect(cache.storageBaseUrl, baseUrl);
expect(logger.warningText, contains('Flutter assets will be downloaded from $baseUrl'));
expect(logger.statusText, isEmpty);
});
testWithoutContext('a non-empty realm is included in the storage url', () async {
final fileSystem = MemoryFileSystem.test();
final Directory cacheDir = fileSystem.currentDirectory
.childDirectory('bin')
.childDirectory('cache');
final File engineVersionFile = cacheDir.childFile('engine.stamp');
engineVersionFile.createSync(recursive: true);
engineVersionFile.writeAsStringSync('abcdef');
final File engineRealmFile = cacheDir.childFile('engine.realm');
engineRealmFile.createSync(recursive: true);
engineRealmFile.writeAsStringSync('flutter_archives_v2');
final cache = Cache.test(processManager: FakeProcessManager.any(), fileSystem: fileSystem);
expect(cache.storageBaseUrl, contains('flutter_archives_v2'));
});
});
testWithoutContext('flattenNameSubdirs', () {
expect(
flattenNameSubdirs(Uri.parse('http://flutter.dev/foo/bar'), MemoryFileSystem.test()),
'flutter.dev/foo/bar',
);
expect(
flattenNameSubdirs(Uri.parse('http://api.flutter.dev/foo/bar'), MemoryFileSystem.test()),
'api.flutter.dev/foo/bar',
);
expect(
flattenNameSubdirs(Uri.parse('https://www.flutter.dev'), MemoryFileSystem.test()),
'www.flutter.dev',
);
});
testWithoutContext(
'EngineCachedArtifact makes binary dirs readable and executable by all',
() async {
final operatingSystemUtils = FakeOperatingSystemUtils();
final FileSystem fileSystem = MemoryFileSystem.test();
final Directory artifactDir = fileSystem.systemTempDirectory.createTempSync(
'flutter_cache_test_artifact.',
);
final Directory downloadDir = fileSystem.systemTempDirectory.createTempSync(
'flutter_cache_test_download.',
);
final cache = FakeSecondaryCache()
..artifactDirectory = artifactDir
..downloadDir = downloadDir;
artifactDir.childDirectory('bin_dir').createSync();
artifactDir.childFile('unused_url_path').createSync();
final artifact = FakeCachedArtifact(
cache: cache,
binaryDirs: <List<String>>[
<String>['bin_dir', 'unused_url_path'],
],
requiredArtifacts: DevelopmentArtifact.universal,
);
await artifact.updateInner(FakeArtifactUpdater(), fileSystem, operatingSystemUtils);
final Directory dir = fileSystem.systemTempDirectory
.listSync(recursive: true)
.whereType<Directory>()
.singleWhereOrNull((Directory directory) => directory.basename == 'bin_dir')!;
expect(dir, isNotNull);
expect(dir.path, artifactDir.childDirectory('bin_dir').path);
expect(operatingSystemUtils.chmods, <List<String>>[
<String>['/.tmp_rand0/flutter_cache_test_artifact.rand0/bin_dir', 'a+r,a+x'],
]);
},
);
testWithoutContext('EngineCachedArtifact downloads package zip from expected URL', () async {
final operatingSystemUtils = FakeOperatingSystemUtils();
final FileSystem fileSystem = MemoryFileSystem.test();
final Directory artifactDir = fileSystem.systemTempDirectory.createTempSync(
'flutter_cache_test_artifact.',
);
final Directory downloadDir = fileSystem.systemTempDirectory.createTempSync(
'flutter_cache_test_download.',
);
final cache = FakeSecondaryCache()
..artifactDirectory = artifactDir
..downloadDir = downloadDir;
artifactDir.childDirectory('pkg').createSync();
final artifact = FakeCachedArtifact(
cache: cache,
binaryDirs: <List<String>>[],
packageDirs: <String>['package_dir'],
requiredArtifacts: DevelopmentArtifact.universal,
);
Uri? packageUrl;
final ArtifactUpdater artifactUpdater = FakeArtifactUpdater()
..onDownloadZipArchive = (String message, Uri url, Directory location) {
location.childDirectory('package_dir').createSync();
packageUrl = url;
};
await artifact.updateInner(artifactUpdater, fileSystem, operatingSystemUtils);
expect(packageUrl, isNotNull);
expect(
packageUrl.toString(),
'https://storage.googleapis.com/flutter_infra_release/flutter/abc123/package_dir.zip',
);
final Directory dir = fileSystem.systemTempDirectory
.listSync(recursive: true)
.whereType<Directory>()
.singleWhereOrNull((Directory directory) => directory.basename == 'pkg')!;
expect(dir.path, artifactDir.childDirectory('pkg').path);
expect(dir.childDirectory('package_dir').existsSync(), isTrue);
});
testWithoutContext('Try to remove without a parent', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final Directory parent = fileSystem.directory('dir');
parent.createSync();
final Directory child = parent.childDirectory('child');
child.createSync();
final Directory tempStorage = parent.childDirectory('temp');
tempStorage.createSync();
final fakeArtifact = FakeArtifactUpdaterDownload(
operatingSystemUtils: FakeOperatingSystemUtils(),
logger: BufferLogger.test(),
fileSystem: fileSystem,
tempStorage: tempStorage,
httpClient: HttpClient(),
platform: FakePlatform(),
allowedBaseUrls: <String>[],
);
final File file = child.childFile('file');
file.createSync();
fakeArtifact.addFiles(<File>[file]);
child.deleteSync(recursive: true);
fakeArtifact.removeDownloadedFiles();
});
testWithoutContext(
'IosUsbArtifacts verifies executables for libimobiledevice in isUpToDateInner',
() async {
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
final iosUsbArtifacts = IosUsbArtifacts(
'libimobiledevice',
cache,
platform: FakePlatform(operatingSystem: 'macos'),
);
iosUsbArtifacts.location.createSync();
final File ideviceScreenshotFile = iosUsbArtifacts.location.childFile('idevicescreenshot')
..createSync();
iosUsbArtifacts.location.childFile('idevicesyslog').createSync();
expect(iosUsbArtifacts.isUpToDateInner(fileSystem), true);
ideviceScreenshotFile.deleteSync();
expect(iosUsbArtifacts.isUpToDateInner(fileSystem), false);
},
);
testWithoutContext('IosUsbArtifacts verifies iproxy for libusbmuxd in isUpToDateInner', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
final iosUsbArtifacts = IosUsbArtifacts(
'libusbmuxd',
cache,
platform: FakePlatform(operatingSystem: 'macos'),
);
iosUsbArtifacts.location.createSync();
final File iproxy = iosUsbArtifacts.location.childFile('iproxy')..createSync();
expect(iosUsbArtifacts.isUpToDateInner(fileSystem), true);
iproxy.deleteSync();
expect(iosUsbArtifacts.isUpToDateInner(fileSystem), false);
});
testWithoutContext(
'IosUsbArtifacts does not verify executables for openssl in isUpToDateInner',
() async {
final FileSystem fileSystem = MemoryFileSystem.test();
final cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
final iosUsbArtifacts = IosUsbArtifacts(
'openssl',
cache,
platform: FakePlatform(operatingSystem: 'macos'),
);
iosUsbArtifacts.location.createSync();
expect(iosUsbArtifacts.isUpToDateInner(fileSystem), true);
},
);
testWithoutContext('IosUsbArtifacts uses unsigned when specified', () async {
final cache = Cache.test(processManager: FakeProcessManager.any());
cache.useUnsignedMacBinaries = true;
final iosUsbArtifacts = IosUsbArtifacts(
'name',
cache,
platform: FakePlatform(operatingSystem: 'macos'),
);
expect(iosUsbArtifacts.archiveUri.toString(), contains('/unsigned/'));
});
testWithoutContext('IosUsbArtifacts does not use unsigned when not specified', () async {
final cache = Cache.test(processManager: FakeProcessManager.any());
final iosUsbArtifacts = IosUsbArtifacts(
'name',
cache,
platform: FakePlatform(operatingSystem: 'macos'),
);
expect(iosUsbArtifacts.archiveUri.toString(), isNot(contains('/unsigned/')));
});
testWithoutContext('FlutterRunnerDebugSymbols downloads Flutter runner debug symbols', () async {
final FileSystem fileSystem = MemoryFileSystem.test();
final Cache cache = FakeSecondaryCache()
..artifactDirectory = fileSystem.currentDirectory
..version = '123456';
final packageResolver = FakeVersionedPackageResolver();
final flutterRunnerDebugSymbols = FlutterRunnerDebugSymbols(
cache,
packageResolver: packageResolver,
platform: FakePlatform(),
);
await flutterRunnerDebugSymbols.updateInner(
FakeArtifactUpdater(),
fileSystem,
FakeOperatingSystemUtils(),
);
expect(packageResolver.resolved, <List<String>>[
<String>['fuchsia-debug-symbols-x64', '123456'],
<String>['fuchsia-debug-symbols-arm64', '123456'],
]);
});
testWithoutContext('FontSubset in universal artifacts', () {
final cache = Cache.test(processManager: FakeProcessManager.any());
final artifacts = FontSubsetArtifacts(cache, platform: FakePlatform());
expect(artifacts.developmentArtifact, DevelopmentArtifact.universal);
});
testWithoutContext('FontSubset artifacts on x64 linux', () {
fakeProcessManager.addCommand(unameCommandForX64);
final Cache cache = createCache(FakePlatform());
final artifacts = FontSubsetArtifacts(cache, platform: FakePlatform());
cache.includeAllPlatforms = false;
expect(artifacts.getBinaryDirs(), <List<String>>[
<String>['linux-x64', 'linux-x64/font-subset.zip'],
]);
});
testWithoutContext('FontSubset artifacts on arm64 linux', () {
fakeProcessManager.addCommand(unameCommandForArm64);
final Cache cache = createCache(FakePlatform());
final artifacts = FontSubsetArtifacts(cache, platform: FakePlatform());
cache.includeAllPlatforms = false;
expect(artifacts.getBinaryDirs(), <List<String>>[
<String>['linux-arm64', 'linux-arm64/font-subset.zip'],
]);
});
testWithoutContext('FontSubset artifacts on windows', () {
final Cache cache = createCache(FakePlatform(operatingSystem: 'windows'));
final artifacts = FontSubsetArtifacts(
cache,
platform: FakePlatform(operatingSystem: 'windows'),
);
cache.includeAllPlatforms = false;
expect(artifacts.getBinaryDirs(), <List<String>>[
<String>['windows-x64', 'windows-x64/font-subset.zip'],
]);
});
testWithoutContext('FontSubset artifacts on macos', () {
fakeProcessManager.addCommands(<FakeCommand>[
const FakeCommand(command: <String>['which', 'sysctl'], stdout: '/sbin/sysctl'),
const FakeCommand(
command: <String>['sysctl', 'hw.optional.arm64'],
stdout: 'hw.optional.arm64: 0',
),
]);
final Cache cache = createCache(FakePlatform(operatingSystem: 'macos'));
final artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'macos'));
cache.includeAllPlatforms = false;
expect(artifacts.getBinaryDirs(), <List<String>>[
<String>['darwin-x64', 'darwin-x64/font-subset.zip'],
]);
});
testWithoutContext('FontSubset artifacts on fuchsia', () {
fakeProcessManager.addCommand(unameCommandForX64);
final Cache cache = createCache(FakePlatform(operatingSystem: 'fuchsia'));
final artifacts = FontSubsetArtifacts(
cache,
platform: FakePlatform(operatingSystem: 'fuchsia'),
);
cache.includeAllPlatforms = false;
expect(
artifacts.getBinaryDirs,
throwsToolExit(message: 'Unsupported operating system: fuchsia'),
);
});
testWithoutContext('FontSubset artifacts for all platforms on x64 hosts', () {
fakeProcessManager.addCommand(unameCommandForX64);
final Cache cache = createCache(FakePlatform(operatingSystem: 'fuchsia'));
final artifacts = FontSubsetArtifacts(
cache,
platform: FakePlatform(operatingSystem: 'fuchsia'),
);
cache.includeAllPlatforms = true;
expect(artifacts.getBinaryDirs(), <List<String>>[
<String>['darwin-x64', 'darwin-x64/font-subset.zip'],
<String>['linux-x64', 'linux-x64/font-subset.zip'],
<String>['windows-x64', 'windows-x64/font-subset.zip'],
]);
});
testWithoutContext('FontSubset artifacts for all platforms on arm64 hosts', () {
fakeProcessManager.addCommand(unameCommandForArm64);
final Cache cache = createCache(FakePlatform(operatingSystem: 'fuchsia'));
final artifacts = FontSubsetArtifacts(
cache,
platform: FakePlatform(operatingSystem: 'fuchsia'),
);
cache.includeAllPlatforms = true;
expect(artifacts.getBinaryDirs(), <List<String>>[
<String>['darwin-x64', 'darwin-arm64/font-subset.zip'],
<String>['linux-arm64', 'linux-arm64/font-subset.zip'],
<String>['windows-arm64', 'windows-arm64/font-subset.zip'],
]);
});
testWithoutContext('macOS desktop artifacts include all gen_snapshot binaries', () {
final cache = Cache.test(processManager: FakeProcessManager.any());
final artifacts = MacOSEngineArtifacts(cache, platform: FakePlatform());
cache.includeAllPlatforms = false;
cache.platformOverrideArtifacts = <String>{'macos'};
expect(
artifacts.getBinaryDirs(),
containsAll(<List<String>>[
<String>['darwin-x64', 'darwin-x64/gen_snapshot.zip'],
<String>['darwin-x64-profile', 'darwin-x64-profile/gen_snapshot.zip'],
<String>['darwin-x64-release', 'darwin-x64-release/gen_snapshot.zip'],
]),
);
});
testWithoutContext('macOS desktop artifacts ignore filtering when requested', () {
final cache = Cache.test(processManager: FakeProcessManager.any());
final artifacts = MacOSEngineArtifacts(cache, platform: FakePlatform());
cache.includeAllPlatforms = false;
cache.platformOverrideArtifacts = <String>{'macos'};
expect(artifacts.getBinaryDirs(), isNotEmpty);
});
testWithoutContext('Windows desktop artifacts ignore filtering when requested', () {
final cache = Cache.test(processManager: FakeProcessManager.any());
final artifacts = WindowsEngineArtifacts(cache, platform: FakePlatform());
cache.includeAllPlatforms = false;
cache.platformOverrideArtifacts = <String>{'windows'};
expect(artifacts.getBinaryDirs(), isNotEmpty);
});
testWithoutContext('Windows desktop artifacts include profile and release artifacts', () {
final cache = Cache.test(processManager: FakeProcessManager.any());
final artifacts = WindowsEngineArtifacts(
cache,
platform: FakePlatform(operatingSystem: 'windows'),
);
expect(
artifacts.getBinaryDirs(),
containsAll(<Matcher>[contains(contains('profile')), contains(contains('release'))]),
);
});
testWithoutContext('Linux desktop artifacts ignore filtering when requested', () {
fakeProcessManager.addCommand(unameCommandForX64);
final Cache cache = createCache(FakePlatform());
final artifacts = LinuxEngineArtifacts(cache, platform: FakePlatform(operatingSystem: 'macos'));
cache.includeAllPlatforms = false;
cache.platformOverrideArtifacts = <String>{'linux'};
expect(artifacts.getBinaryDirs(), isNotEmpty);
});
testWithoutContext('Linux desktop artifacts for x64 include profile and release artifacts', () {
fakeProcessManager.addCommand(unameCommandForX64);
final Cache cache = createCache(FakePlatform());
final artifacts = LinuxEngineArtifacts(cache, platform: FakePlatform());
expect(artifacts.getBinaryDirs(), <List<String>>[
<String>['linux-x64', 'linux-x64-debug/linux-x64-flutter-gtk.zip'],
<String>['linux-x64-profile', 'linux-x64-profile/linux-x64-flutter-gtk.zip'],
<String>['linux-x64-release', 'linux-x64-release/linux-x64-flutter-gtk.zip'],
]);
});
testWithoutContext('Linux desktop artifacts for arm64 include profile and release artifacts', () {
fakeProcessManager.addCommand(unameCommandForArm64);
final Cache cache = createCache(FakePlatform());
final artifacts = LinuxEngineArtifacts(cache, platform: FakePlatform());
expect(artifacts.getBinaryDirs(), <List<String>>[
<String>['linux-arm64', 'linux-arm64-debug/linux-arm64-flutter-gtk.zip'],
<String>['linux-arm64-profile', 'linux-arm64-profile/linux-arm64-flutter-gtk.zip'],
<String>['linux-arm64-release', 'linux-arm64-release/linux-arm64-flutter-gtk.zip'],
]);
});
testWithoutContext('Cache can delete stampfiles of artifacts', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final artifactSet = FakeIosUsbArtifacts();
final logger = BufferLogger.test();
artifactSet.stampName = 'STAMP';
final cache = Cache(
artifacts: <ArtifactSet>[artifactSet],
logger: logger,
fileSystem: fileSystem,
platform: FakePlatform(),
osUtils: FakeOperatingSystemUtils(),
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 = FakeIosUsbArtifacts();
final logger = BufferLogger.test();
artifactSet.stampName = 'STAMP';
final cache = Cache(
artifacts: <ArtifactSet>[artifactSet],
logger: logger,
fileSystem: fileSystem,
platform: FakePlatform(),
osUtils: FakeOperatingSystemUtils(),
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 = FakeIosUsbArtifacts();
final logger = BufferLogger.test();
artifactSet.stampName = 'STAMP';
final cache = Cache(
artifacts: <ArtifactSet>[artifactSet],
logger: logger,
fileSystem: fileSystem,
platform: FakePlatform(),
osUtils: FakeOperatingSystemUtils(),
rootOverride: fileSystem.currentDirectory,
);
cache.clearStampFiles();
expect(logger.warningText, contains('Failed to delete some stamp files'));
});
testWithoutContext(
'FlutterWebSdk fetches web artifacts and deletes previous directory contents',
() async {
final fileSystem = MemoryFileSystem.test();
final Directory internalDir = fileSystem.currentDirectory
.childDirectory('bin')
.childDirectory('internal');
final File canvasKitVersionFile = internalDir.childFile('canvaskit.version');
canvasKitVersionFile.createSync(recursive: true);
canvasKitVersionFile.writeAsStringSync('abcdefg');
final Directory cacheDir = fileSystem.currentDirectory
.childDirectory('bin')
.childDirectory('cache');
final File engineVersionFile = cacheDir.childFile('engine.stamp');
engineVersionFile.createSync(recursive: true);
engineVersionFile.writeAsStringSync('hijklmnop');
final cache = Cache.test(processManager: FakeProcessManager.any(), fileSystem: fileSystem);
final Directory webCacheDirectory = cache.getWebSdkDirectory();
final artifactUpdater = FakeArtifactUpdater();
final webSdk = FlutterWebSdk(cache);
final messages = <String>[];
final downloads = <String>[];
final locations = <String>[];
artifactUpdater.onDownloadZipArchive = (String message, Uri uri, Directory location) {
messages.add(message);
downloads.add(uri.toString());
locations.add(location.path);
location.createSync(recursive: true);
location.childFile('foo').createSync();
};
webCacheDirectory.childFile('bar').createSync(recursive: true);
await webSdk.updateInner(artifactUpdater, fileSystem, FakeOperatingSystemUtils());
expect(messages, <String>['Downloading Web SDK...']);
expect(downloads, <String>[
'https://storage.googleapis.com/flutter_infra_release/flutter/hijklmnop/flutter-web-sdk.zip',
]);
expect(locations, <String>['/bin/cache/flutter_web_sdk']);
expect(webCacheDirectory.childFile('foo'), exists);
expect(webCacheDirectory.childFile('bar'), isNot(exists));
},
);
testWithoutContext(
'FlutterWebSdk CanvasKit URL can be overridden via FLUTTER_STORAGE_BASE_URL',
() async {
final fileSystem = MemoryFileSystem.test();
final Directory internalDir = fileSystem.currentDirectory
.childDirectory('bin')
.childDirectory('internal');
final File canvasKitVersionFile = internalDir.childFile('canvaskit.version');
canvasKitVersionFile.createSync(recursive: true);
canvasKitVersionFile.writeAsStringSync('abcdefg');
final Directory cacheDir = fileSystem.currentDirectory
.childDirectory('bin')
.childDirectory('cache');
final File engineVersionFile = cacheDir.childFile('engine.stamp');
engineVersionFile.createSync(recursive: true);
engineVersionFile.writeAsStringSync('hijklmnop');
final cache = Cache.test(
processManager: FakeProcessManager.any(),
fileSystem: fileSystem,
platform: FakePlatform(
environment: <String, String>{
'FLUTTER_STORAGE_BASE_URL': 'https://flutter.storage.com/override',
},
),
);
final Directory webCacheDirectory = cache.getWebSdkDirectory();
final artifactUpdater = FakeArtifactUpdater();
final webSdk = FlutterWebSdk(cache);
final downloads = <String>[];
final locations = <String>[];
artifactUpdater.onDownloadZipArchive = (String message, Uri uri, Directory location) {
downloads.add(uri.toString());
locations.add(location.path);
location.createSync(recursive: true);
location.childFile('foo').createSync();
};
webCacheDirectory.childFile('bar').createSync(recursive: true);
await webSdk.updateInner(artifactUpdater, fileSystem, FakeOperatingSystemUtils());
expect(downloads, <String>[
'https://flutter.storage.com/override/flutter_infra_release/flutter/hijklmnop/flutter-web-sdk.zip',
]);
},
);
testWithoutContext('FlutterWebSdk uses tryToDelete to handle directory edge cases', () async {
final handler = FileExceptionHandler();
final fileSystem = MemoryFileSystem.test(opHandle: handler.opHandle);
final cache = Cache.test(processManager: FakeProcessManager.any(), fileSystem: fileSystem);
final Directory webCacheDirectory = cache.getWebSdkDirectory();
final artifactUpdater = FakeArtifactUpdater();
final webSdk = FlutterWebSdk(cache);
final Directory cacheDir = fileSystem.currentDirectory
.childDirectory('bin')
.childDirectory('cache');
final File engineVersionFile = cacheDir.childFile('engine.stamp');
engineVersionFile.createSync(recursive: true);
engineVersionFile.writeAsStringSync('hijklmnop');
artifactUpdater.onDownloadZipArchive = (String message, Uri uri, Directory location) {
location.createSync(recursive: true);
location.childFile('foo').createSync();
};
webCacheDirectory.childFile('bar').createSync(recursive: true);
handler.addError(
webCacheDirectory,
FileSystemOp.delete,
const FileSystemException('', '', OSError('', 2)),
);
await expectLater(
() => webSdk.updateInner(artifactUpdater, fileSystem, FakeOperatingSystemUtils()),
throwsToolExit(
message: RegExp('Unable to delete file or directory at "/bin/cache/flutter_web_sdk"'),
),
);
});
testWithoutContext('FlutterEngineStamp fetches engine_stamp.json', () async {
final fileSystem = MemoryFileSystem.test();
final Directory cacheDir = fileSystem.currentDirectory
.childDirectory('bin')
.childDirectory('cache');
final File engineVersionFile = cacheDir.childFile('engine.stamp');
engineVersionFile.createSync(recursive: true);
engineVersionFile.writeAsStringSync('hijklmnop');
final cache = Cache.test(processManager: FakeProcessManager.any(), fileSystem: fileSystem);
final artifactUpdater = FakeArtifactUpdater();
final engineStamp = FlutterEngineStamp(cache, BufferLogger.test());
final messages = <String>[];
final downloads = <String>[];
final locations = <String>[];
artifactUpdater.onDownloadFile = (String message, Uri uri, Directory location) {
messages.add(message);
downloads.add(uri.toString());
locations.add(location.path);
location.createSync(recursive: true);
};
await engineStamp.updateInner(artifactUpdater, fileSystem, FakeOperatingSystemUtils());
expect(messages, <String>['Downloading engine information...']);
expect(downloads, <String>[
'https://storage.googleapis.com/flutter_infra_release/flutter/hijklmnop/engine_stamp.json',
]);
expect(locations, <String>['/bin/cache']);
// file copy is done by the real uploader; not the fake.
});
testWithoutContext(
'LegacyCanvasKitRemover removes old canvaskit artifacts if they exist',
() async {
final handler = FileExceptionHandler();
final fileSystem = MemoryFileSystem.test(opHandle: handler.opHandle);
final cache = Cache.test(processManager: FakeProcessManager.any(), fileSystem: fileSystem);
final File canvasKitWasm = fileSystem.file(
fileSystem.path.join(cache.getRoot().path, 'canvaskit', 'canvaskit.wasm'),
);
canvasKitWasm.createSync(recursive: true);
canvasKitWasm.writeAsStringSync('hello world');
final remover = LegacyCanvasKitRemover(cache);
expect(await remover.isUpToDate(fileSystem), false);
await remover.update(
FakeArtifactUpdater(),
BufferLogger.test(),
fileSystem,
FakeOperatingSystemUtils(),
);
expect(await remover.isUpToDate(fileSystem), true);
expect(canvasKitWasm.existsSync(), isFalse);
},
);
testWithoutContext('Cache handles exception thrown if stamp file cannot be parsed', () {
final exceptionHandler = FileExceptionHandler();
final FileSystem fileSystem = MemoryFileSystem.test(opHandle: exceptionHandler.opHandle);
final Logger logger = BufferLogger.test();
final cache = FakeCache(
fileSystem: fileSystem,
logger: logger,
platform: FakePlatform(),
osUtils: FakeOperatingSystemUtils(),
);
final File file = fileSystem.file('stamp');
cache.stampFile = file;
expect(cache.getStampFor('foo'), null);
file.createSync();
exceptionHandler.addError(file, FileSystemOp.read, const FileSystemException());
expect(cache.getStampFor('foo'), null);
});
testWithoutContext('Cache parses stamp file', () {
final FileSystem fileSystem = MemoryFileSystem.test();
final Logger logger = BufferLogger.test();
final cache = FakeCache(
fileSystem: fileSystem,
logger: logger,
platform: FakePlatform(),
osUtils: FakeOperatingSystemUtils(),
);
final File file = fileSystem.file('stamp')..writeAsStringSync('ABC ');
cache.stampFile = file;
expect(cache.getStampFor('foo'), 'ABC');
});
testWithoutContext('PubDependencies needs to be updated if the package config'
' file or the source directories are missing', () async {
final logger = BufferLogger.test();
final fileSystem = MemoryFileSystem.test();
final pubDependencies = PubDependencies(
flutterRoot: () => '',
logger: logger,
pub: () => FakePub(),
projectFactory: FakeFlutterProjectFactory(),
);
expect(await pubDependencies.isUpToDate(fileSystem), false); // no package config
fileSystem.file('packages/flutter_tools/.dart_tool/package_config.json')
..createSync(recursive: true)
..writeAsStringSync('''
{
"configVersion": 2,
"packages": [
{
"name": "example",
"rootUri": "file:///.pub-cache/hosted/pub.dartlang.org/example-7.0.0",
"packageUri": "lib/",
"languageVersion": "2.7"
}
],
"generated": "2020-09-15T20:29:20.691147Z",
"generator": "pub",
"generatorVersion": "2.10.0-121.0.dev"
}
''');
expect(await pubDependencies.isUpToDate(fileSystem), false); // dependencies are missing.
fileSystem
.file('.pub-cache/hosted/pub.dartlang.org/example-7.0.0/pubspec.yaml')
.createSync(recursive: true);
expect(await pubDependencies.isUpToDate(fileSystem), true);
});
testWithoutContext('PubDependencies updates via pub get', () async {
final logger = BufferLogger.test();
final fileSystem = MemoryFileSystem.test();
final pub = FakePub();
final pubDependencies = PubDependencies(
flutterRoot: () => '',
logger: logger,
pub: () => pub,
projectFactory: FakeFlutterProjectFactory(),
);
await pubDependencies.update(
FakeArtifactUpdater(),
logger,
fileSystem,
FakeOperatingSystemUtils(),
);
expect(pub.calledGet, 1);
expect(
pub.invocations.first,
predicate<FakePubInvocation>(
(FakePubInvocation invocation) => invocation.outputMode == PubOutputMode.failuresOnly,
'Pub invoked with PubOutputMode.none',
),
);
});
testUsingContext('Check current DevTools version', () async {
final String currentDevToolsVersion = globals.cache.devToolsVersion;
final devToolsVersionFormat = RegExp(r'\d+\.\d+\.\d+(?:-\S+)?');
expect(devToolsVersionFormat.allMatches(currentDevToolsVersion).length, 1);
});
// Check that the build number matches the format documented here:
// https://dart.dev/get-dart#release-channels
testUsingContext('Check current Dart SDK build number', () async {
final String currentDartSdkVersion = globals.cache.dartSdkBuild;
final dartSdkVersionFormat = RegExp(r'\d+\.\d+\.\d+(?:-\S+)?');
expect(dartSdkVersionFormat.allMatches(currentDartSdkVersion).length, 1);
});
group('AndroidMavenArtifacts', () {
MemoryFileSystem? memoryFileSystem;
Cache? cache;
FakeAndroidSdk? fakeAndroidSdk;
setUp(() {
memoryFileSystem = MemoryFileSystem.test();
cache = Cache.test(fileSystem: memoryFileSystem, processManager: FakeProcessManager.any());
fakeAndroidSdk = FakeAndroidSdk();
});
testWithoutContext('AndroidMavenArtifacts has a specified development artifact', () async {
final mavenArtifacts = AndroidMavenArtifacts(
cache!,
java: FakeJava(),
platform: FakePlatform(),
);
expect(mavenArtifacts.developmentArtifact, DevelopmentArtifact.androidMaven);
});
testUsingContext(
'AndroidMavenArtifacts can invoke Gradle resolve dependencies if Android SDK is present',
() async {
final String? oldRoot = Cache.flutterRoot;
Cache.flutterRoot = '';
try {
final mavenArtifacts = AndroidMavenArtifacts(
cache!,
java: FakeJava(),
platform: FakePlatform(),
);
expect(await mavenArtifacts.isUpToDate(memoryFileSystem!), isFalse);
final Directory gradleWrapperDir = cache!.getArtifactDirectory('gradle_wrapper')
..createSync(recursive: true);
gradleWrapperDir.childFile('gradlew').writeAsStringSync('irrelevant');
gradleWrapperDir.childFile('gradlew.bat').writeAsStringSync('irrelevant');
await mavenArtifacts.update(
FakeArtifactUpdater(),
BufferLogger.test(),
memoryFileSystem!,
FakeOperatingSystemUtils(),
);
expect(await mavenArtifacts.isUpToDate(memoryFileSystem!), isFalse);
expect(fakeAndroidSdk!.reinitialized, true);
} finally {
Cache.flutterRoot = oldRoot;
}
},
overrides: <Type, Generator>{
Cache: () => cache,
FileSystem: () => memoryFileSystem,
Platform: () => FakePlatform(),
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>[
'/bin/cache/flutter_gradle_wrapper.rand0/gradlew',
'-b',
'packages/flutter_tools/gradle/resolve_dependencies.gradle.kts',
'--project-cache-dir',
'/bin/cache/flutter_gradle_wrapper.rand0',
'resolveDependencies',
],
),
]),
AndroidSdk: () => fakeAndroidSdk,
},
);
testUsingContext(
'AndroidMavenArtifacts is a no-op if the Android SDK is absent',
() async {
final mavenArtifacts = AndroidMavenArtifacts(
cache!,
java: FakeJava(),
platform: FakePlatform(),
);
expect(await mavenArtifacts.isUpToDate(memoryFileSystem!), isFalse);
await mavenArtifacts.update(
FakeArtifactUpdater(),
BufferLogger.test(),
memoryFileSystem!,
FakeOperatingSystemUtils(),
);
expect(await mavenArtifacts.isUpToDate(memoryFileSystem!), isFalse);
},
overrides: <Type, Generator>{
Cache: () => cache,
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.empty(),
AndroidSdk: () => null, // Android SDK was not located.
},
);
});
}
class FakeCachedArtifact extends EngineCachedArtifact {
FakeCachedArtifact({
String stampName = 'STAMP',
required Cache cache,
required DevelopmentArtifact requiredArtifacts,
this.binaryDirs = const <List<String>>[],
this.licenseDirs = const <String>[],
this.packageDirs = const <String>[],
}) : super(stampName, cache, requiredArtifacts);
final List<List<String>> binaryDirs;
final List<String> licenseDirs;
final List<String> packageDirs;
@override
List<List<String>> getBinaryDirs() => binaryDirs;
@override
List<String> getLicenseDirs() => licenseDirs;
@override
List<String> getPackageDirs() => packageDirs;
}
class FakeSimpleArtifact extends CachedArtifact {
FakeSimpleArtifact(Cache cache) : super('fake', cache, DevelopmentArtifact.universal);
@override
Future<void> updateInner(
ArtifactUpdater artifactUpdater,
FileSystem fileSystem,
OperatingSystemUtils operatingSystemUtils,
) async {}
}
class FakeSecondaryCachedArtifact extends Fake implements CachedArtifact {
bool upToDate = false;
bool didUpdate = false;
Exception? updateException;
@override
Future<bool> isUpToDate(FileSystem fileSystem) async => upToDate;
@override
Future<void> update(
ArtifactUpdater artifactUpdater,
Logger logger,
FileSystem fileSystem,
OperatingSystemUtils operatingSystemUtils, {
bool offline = false,
}) async {
if (updateException != null) {
throw updateException!;
}
didUpdate = true;
}
@override
DevelopmentArtifact get developmentArtifact => DevelopmentArtifact.universal;
}
class FakeIosUsbArtifacts extends Fake implements IosUsbArtifacts {
@override
Map<String, String> environment = <String, String>{};
@override
String stampName = 'ios-usb';
}
class FakeSecondaryCache extends Fake implements Cache {
Directory? downloadDir;
late Directory artifactDirectory;
String? version;
late void Function(String artifactName, String version) onSetStamp;
@override
String get storageBaseUrl => 'https://storage.googleapis.com';
@override
String get engineRevision => version ?? 'abc123';
@override
Directory getDownloadDir() => artifactDirectory;
@override
Directory getArtifactDirectory(String name) => artifactDirectory;
@override
Directory getCacheDir(String name, {bool shouldCreate = true}) {
return artifactDirectory.childDirectory(name);
}
@override
File getLicenseFile() {
return artifactDirectory.childFile('LICENSE');
}
@override
String? getVersionFor(String artifactName) => version;
@override
void setStampFor(String artifactName, String version) {
onSetStamp(artifactName, version);
}
}
class FakeVersionedPackageResolver extends Fake implements VersionedPackageResolver {
final resolved = <List<String>>[];
@override
String resolveUrl(String packageName, String version) {
resolved.add(<String>[packageName, version]);
return '';
}
}
class FakePubInvocation {
FakePubInvocation({required this.outputMode});
final PubOutputMode outputMode;
}
class FakePub extends Fake implements Pub {
final invocations = <FakePubInvocation>[];
int get calledGet => invocations.length;
@override
Future<void> get({
PubContext? context,
required FlutterProject project,
bool upgrade = false,
bool offline = false,
String? flutterRootOverride,
bool checkUpToDate = false,
bool shouldSkipThirdPartyGenerator = true,
bool printProgress = true,
bool enforceLockfile = false,
PubOutputMode outputMode = PubOutputMode.all,
}) async {
invocations.add(FakePubInvocation(outputMode: outputMode));
}
}
class FakeCache extends Cache {
FakeCache({
required super.logger,
required super.fileSystem,
required super.platform,
required super.osUtils,
}) : super(artifacts: <ArtifactSet>[]);
late File stampFile;
@override
File getStampFileFor(String artifactName) {
return stampFile;
}
}
class FakeAndroidSdk extends Fake implements AndroidSdk {
bool reinitialized = false;
@override
void reinitialize({FileSystem? fileSystem}) {
reinitialized = true;
}
}
class FakeArtifactUpdater extends Fake implements ArtifactUpdater {
void Function(String, Uri, Directory)? onDownloadZipArchive;
void Function(String, Uri, Directory)? onDownloadZipTarball;
void Function(String, Uri, Directory)? onDownloadFile;
@override
Future<void> downloadZippedTarball(String message, Uri url, Directory location) async {
onDownloadZipTarball?.call(message, url, location);
}
@override
Future<void> downloadZipArchive(String message, Uri url, Directory location) async {
onDownloadZipArchive?.call(message, url, location);
}
@override
Future<void> downloadFile(String message, Uri url, Directory location) async {
onDownloadFile?.call(message, url, location);
}
@override
void removeDownloadedFiles() {}
}
class FakeArtifactUpdaterDownload extends ArtifactUpdater {
FakeArtifactUpdaterDownload({
required super.operatingSystemUtils,
required super.logger,
required super.fileSystem,
required super.tempStorage,
required super.httpClient,
required super.platform,
required super.allowedBaseUrls,
});
void addFiles(List<File> files) {
downloadedFiles.addAll(files);
}
}