mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

Our current top crasher is an unclear error when ProcessManager fails to resolve an executable path. To fix this, we'd like to being adjusting the process resolution logic and adding more instrumentation to track failures. In order to begin the process, the ProcessManager has been folded back into the flutter tool
795 lines
32 KiB
Dart
795 lines
32 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.
|
|
|
|
// @dart = 2.8
|
|
|
|
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' show InternetAddress, SocketException;
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/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:meta/meta.dart';
|
|
import 'package:mockito/mockito.dart';
|
|
|
|
import '../src/common.dart';
|
|
import '../src/context.dart';
|
|
|
|
void main() {
|
|
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 = Cache.test();
|
|
|
|
expect(cache.checkLockAcquired, throwsStateError);
|
|
});
|
|
|
|
testWithoutContext('should not throw when locking is disabled', () {
|
|
final Cache cache = Cache.test();
|
|
Cache.disableLocking();
|
|
|
|
expect(cache.checkLockAcquired, returnsNormally);
|
|
});
|
|
|
|
testWithoutContext('should not throw when lock is acquired', () async {
|
|
Cache.flutterRoot = '';
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final Cache cache = Cache.test(fileSystem: fileSystem);
|
|
fileSystem.file(fileSystem.path.join('bin', 'cache', 'lockfile'))
|
|
.createSync(recursive: true);
|
|
|
|
await cache.lock();
|
|
|
|
expect(cache.checkLockAcquired, returnsNormally);
|
|
expect(cache.releaseLock, returnsNormally);
|
|
}, skip: true); // TODO(jonahwilliams): implement support for lock so this can be tested with the memory file system.
|
|
|
|
testWithoutContext('throws tool exit when lockfile open fails', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final Cache cache = Cache.test(fileSystem: fileSystem);
|
|
fileSystem.file(fileSystem.path.join('bin', 'cache', 'lockfile'))
|
|
.createSync(recursive: true);
|
|
|
|
expect(() async => await cache.lock(), throwsToolExit());
|
|
}, skip: true); // TODO(jonahwilliams): implement support for lock so this can be tested with the memory file system.
|
|
|
|
testWithoutContext('should not throw when FLUTTER_ALREADY_LOCKED is set', () {
|
|
final Cache cache = Cache.test(platform: FakePlatform(environment: <String, String>{
|
|
'FLUTTER_ALREADY_LOCKED': 'true',
|
|
}));
|
|
|
|
expect(cache.checkLockAcquired, returnsNormally);
|
|
});
|
|
});
|
|
|
|
group('Cache', () {
|
|
testWithoutContext('Continues on failed stamp file update', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final Cache mockCache = MockCache();
|
|
final Directory artifactDir = fileSystem.systemTempDirectory.createTempSync('flutter_cache_test_artifact.');
|
|
final Directory downloadDir = fileSystem.systemTempDirectory.createTempSync('flutter_cache_test_download.');
|
|
|
|
when(mockCache.getArtifactDirectory(any)).thenReturn(artifactDir);
|
|
when(mockCache.getDownloadDir()).thenReturn(downloadDir);
|
|
when(mockCache.setStampFor(any, any)).thenAnswer((_) {
|
|
throw const FileSystemException('stamp write failed');
|
|
});
|
|
final FakeSimpleArtifact artifact = FakeSimpleArtifact(mockCache);
|
|
await artifact.update(MockArtifactUpdater(), logger, fileSystem, MockOperatingSystemUtils());
|
|
|
|
expect(logger.errorText, contains('stamp write failed'));
|
|
});
|
|
|
|
testWithoutContext('Gradle wrapper should not be up to date, if some cached artifact is not available', () {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final Cache cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
|
|
final GradleWrapper 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 should be up to date, only if all cached artifact are available', () {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final Cache cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
|
|
final GradleWrapper 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 = MockCachedArtifact();
|
|
final CachedArtifact artifact2 = MockCachedArtifact();
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
|
|
when(artifact1.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future<bool>.value(true));
|
|
when(artifact2.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future<bool>.value(false));
|
|
final Cache 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 CachedArtifact artifact1 = MockCachedArtifact();
|
|
final CachedArtifact artifact2 = MockCachedArtifact();
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
|
|
when(artifact1.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future<bool>.value(true));
|
|
when(artifact2.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future<bool>.value(true));
|
|
final Cache 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 CachedArtifact artifact1 = MockCachedArtifact();
|
|
final CachedArtifact artifact2 = MockCachedArtifact();
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
|
|
when(artifact1.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future<bool>.value(true));
|
|
when(artifact2.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future<bool>.value(false));
|
|
final Cache cache = Cache.test(
|
|
fileSystem: fileSystem,
|
|
artifacts: <CachedArtifact>[artifact1, artifact2],
|
|
processManager: FakeProcessManager.any(),
|
|
);
|
|
|
|
await cache.updateAll(<DevelopmentArtifact>{
|
|
null,
|
|
});
|
|
verifyNever(artifact1.update(any, any, any, any));
|
|
verify(artifact2.update(any, any, any, any));
|
|
});
|
|
|
|
testWithoutContext("getter dyLdLibEntry concatenates the output of each artifact's dyLdLibEntry getter", () async {
|
|
final IosUsbArtifacts artifact1 = MockIosUsbArtifacts();
|
|
final IosUsbArtifacts artifact2 = MockIosUsbArtifacts();
|
|
final IosUsbArtifacts artifact3 = MockIosUsbArtifacts();
|
|
when(artifact1.environment)
|
|
.thenReturn(<String, String>{
|
|
'DYLD_LIBRARY_PATH': '/path/to/alpha:/path/to/beta',
|
|
});
|
|
when(artifact2.environment)
|
|
.thenReturn(<String, String>{
|
|
'DYLD_LIBRARY_PATH': '/path/to/gamma:/path/to/delta:/path/to/epsilon',
|
|
});
|
|
when(artifact3.environment)
|
|
.thenReturn(<String, String>{
|
|
'DYLD_LIBRARY_PATH': '',
|
|
});
|
|
final Cache 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 CachedArtifact artifact1 = MockCachedArtifact();
|
|
final CachedArtifact artifact2 = MockCachedArtifact();
|
|
when(artifact1.isUpToDate(any)).thenAnswer((Invocation _) => Future<bool>.value(false));
|
|
when(artifact2.isUpToDate(any)).thenAnswer((Invocation _) => Future<bool>.value(false));
|
|
final MockInternetAddress address = MockInternetAddress();
|
|
when(address.host).thenReturn('storage.googleapis.com');
|
|
when(artifact1.update(any, any, any, any)).thenThrow(SocketException(
|
|
'Connection reset by peer',
|
|
address: address,
|
|
));
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final Cache cache = Cache.test(
|
|
artifacts: <CachedArtifact>[artifact1, artifact2],
|
|
processManager: FakeProcessManager.any(),
|
|
logger: logger,
|
|
);
|
|
try {
|
|
await cache.updateAll(<DevelopmentArtifact>{
|
|
null,
|
|
});
|
|
fail('Mock thrown exception expected');
|
|
} on Exception {
|
|
verify(artifact1.update(any, any, any, any));
|
|
// Don't continue when retrieval fails.
|
|
verifyNever(artifact2.update(any, any, any, any));
|
|
expect(
|
|
logger.errorText,
|
|
contains('https://flutter.dev/community/china'),
|
|
);
|
|
}
|
|
});
|
|
|
|
testWithoutContext('Invalid URI for FLUTTER_STORAGE_BASE_URL throws ToolExit', () async {
|
|
final Cache cache = Cache.test(
|
|
platform: FakePlatform(environment: <String, String>{
|
|
'FLUTTER_STORAGE_BASE_URL': ' http://foo',
|
|
},
|
|
));
|
|
|
|
expect(() => cache.storageBaseUrl, throwsToolExit());
|
|
});
|
|
});
|
|
|
|
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 operatingSystemUtils = MockOperatingSystemUtils();
|
|
final MockCache cache = MockCache();
|
|
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.');
|
|
|
|
when(cache.getArtifactDirectory(any)).thenReturn(artifactDir);
|
|
when(cache.getDownloadDir()).thenReturn(downloadDir);
|
|
artifactDir.childDirectory('bin_dir').createSync();
|
|
artifactDir.childFile('unused_url_path').createSync();
|
|
|
|
final FakeCachedArtifact artifact = FakeCachedArtifact(
|
|
cache: cache,
|
|
binaryDirs: <List<String>>[
|
|
<String>['bin_dir', 'unused_url_path'],
|
|
],
|
|
requiredArtifacts: DevelopmentArtifact.universal,
|
|
);
|
|
await artifact.updateInner(MockArtifactUpdater(), fileSystem, operatingSystemUtils);
|
|
final Directory dir = fileSystem.systemTempDirectory
|
|
.listSync(recursive: true)
|
|
.whereType<Directory>()
|
|
.singleWhere((Directory directory) => directory.basename == 'bin_dir', orElse: () => null);
|
|
|
|
expect(dir, isNotNull);
|
|
expect(dir.path, artifactDir.childDirectory('bin_dir').path);
|
|
verify(operatingSystemUtils.chmod(argThat(hasPath(dir.path)), 'a+r,a+x'));
|
|
});
|
|
|
|
testWithoutContext('IosUsbArtifacts verifies executables for libimobiledevice in isUpToDateInner', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final Cache cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
|
|
final IosUsbArtifacts 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 usbmuxd in isUpToDateInner', () async {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final Cache cache = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
|
|
final IosUsbArtifacts iosUsbArtifacts = IosUsbArtifacts('usbmuxd', 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 = Cache.test(fileSystem: fileSystem, processManager: FakeProcessManager.any());
|
|
final IosUsbArtifacts 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 = Cache.test();
|
|
cache.useUnsignedMacBinaries = true;
|
|
|
|
final IosUsbArtifacts 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 = Cache.test();
|
|
final IosUsbArtifacts 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 = Cache.test(
|
|
fileSystem: fileSystem,
|
|
processManager: FakeProcessManager.any(),
|
|
);
|
|
final MockVersionedPackageResolver mockPackageResolver = MockVersionedPackageResolver();
|
|
final FlutterRunnerDebugSymbols flutterRunnerDebugSymbols = FlutterRunnerDebugSymbols(
|
|
cache,
|
|
packageResolver: mockPackageResolver,
|
|
platform: FakePlatform(operatingSystem: 'linux'),
|
|
);
|
|
when(mockPackageResolver.resolveUrl(any, any)).thenReturn('');
|
|
|
|
await flutterRunnerDebugSymbols.updateInner(MockArtifactUpdater(), fileSystem, MockOperatingSystemUtils());
|
|
|
|
verifyInOrder(<void>[
|
|
mockPackageResolver.resolveUrl('fuchsia-debug-symbols-x64', any),
|
|
mockPackageResolver.resolveUrl('fuchsia-debug-symbols-arm64', any),
|
|
]);
|
|
});
|
|
|
|
testWithoutContext('FontSubset in universal artifacts', () {
|
|
final Cache cache = Cache.test();
|
|
final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux'));
|
|
|
|
expect(artifacts.developmentArtifact, DevelopmentArtifact.universal);
|
|
});
|
|
|
|
testWithoutContext('FontSubset artifacts on linux', () {
|
|
final Cache cache = Cache.test();
|
|
final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux'));
|
|
cache.includeAllPlatforms = false;
|
|
|
|
expect(artifacts.getBinaryDirs(), <List<String>>[<String>['linux-x64', 'linux-x64/font-subset.zip']]);
|
|
});
|
|
|
|
testWithoutContext('FontSubset artifacts on windows', () {
|
|
final Cache cache = Cache.test();
|
|
final FontSubsetArtifacts 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', () {
|
|
final Cache cache = Cache.test();
|
|
final FontSubsetArtifacts 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', () {
|
|
final Cache cache = Cache.test();
|
|
final FontSubsetArtifacts 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', () {
|
|
final Cache cache = Cache.test();
|
|
final FontSubsetArtifacts 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('macOS desktop artifacts ignore filtering when requested', () {
|
|
final Cache cache = Cache.test();
|
|
final MacOSEngineArtifacts artifacts = MacOSEngineArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux'));
|
|
cache.includeAllPlatforms = false;
|
|
cache.platformOverrideArtifacts = <String>{'macos'};
|
|
|
|
expect(artifacts.getBinaryDirs(), isNotEmpty);
|
|
});
|
|
|
|
testWithoutContext('Windows desktop artifacts ignore filtering when requested', () {
|
|
final Cache cache = Cache.test();
|
|
final WindowsEngineArtifacts artifacts = WindowsEngineArtifacts(
|
|
cache,
|
|
platform: FakePlatform(operatingSystem: 'linux'),
|
|
);
|
|
cache.includeAllPlatforms = false;
|
|
cache.platformOverrideArtifacts = <String>{'windows'};
|
|
|
|
expect(artifacts.getBinaryDirs(), isNotEmpty);
|
|
});
|
|
|
|
testWithoutContext('Windows desktop artifacts include profile and release artifacts', () {
|
|
final Cache cache = Cache.test();
|
|
final WindowsEngineArtifacts 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', () {
|
|
final Cache cache = Cache.test();
|
|
final LinuxEngineArtifacts artifacts = LinuxEngineArtifacts(
|
|
cache,
|
|
platform: FakePlatform(operatingSystem: 'macos'),
|
|
);
|
|
cache.includeAllPlatforms = false;
|
|
cache.platformOverrideArtifacts = <String>{'linux'};
|
|
|
|
expect(artifacts.getBinaryDirs(), isNotEmpty);
|
|
});
|
|
|
|
testWithoutContext('Linux desktop artifacts include profile and release artifacts', () {
|
|
final Cache cache = Cache.test();
|
|
final LinuxEngineArtifacts artifacts = LinuxEngineArtifacts(
|
|
cache,
|
|
platform: FakePlatform(operatingSystem: 'linux'),
|
|
);
|
|
|
|
expect(artifacts.getBinaryDirs(), containsAll(<Matcher>[
|
|
contains(contains('profile')),
|
|
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'));
|
|
});
|
|
|
|
testWithoutContext('FlutterWebSdk deletes previous directory contents', () {
|
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
|
final Directory webStuff = fileSystem.directory('web-stuff');
|
|
final MockCache cache = MockCache();
|
|
final MockArtifactUpdater artifactUpdater = MockArtifactUpdater();
|
|
final FlutterWebSdk webSdk = FlutterWebSdk(cache, platform: FakePlatform(operatingSystem: 'linux'));
|
|
|
|
when(cache.getWebSdkDirectory()).thenReturn(webStuff);
|
|
when(artifactUpdater.downloadZipArchive('Downloading Web SDK...', any, any))
|
|
.thenAnswer((Invocation invocation) async {
|
|
final Directory location = invocation.positionalArguments[2] as Directory;
|
|
location.createSync(recursive: true);
|
|
location.childFile('foo').createSync();
|
|
});
|
|
webStuff.childFile('bar').createSync(recursive: true);
|
|
|
|
webSdk.updateInner(artifactUpdater, fileSystem, MockOperatingSystemUtils());
|
|
|
|
expect(webStuff.childFile('foo'), exists);
|
|
expect(webStuff.childFile('bar'), isNot(exists));
|
|
});
|
|
|
|
testWithoutContext('Cache handles exception thrown if stamp file cannot be parsed', () {
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final Logger logger = BufferLogger.test();
|
|
final FakeCache cache = FakeCache(
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
platform: FakePlatform(),
|
|
osUtils: MockOperatingSystemUtils()
|
|
);
|
|
final MockFile file = MockFile();
|
|
cache.stampFile = file;
|
|
when(file.existsSync()).thenReturn(false);
|
|
|
|
expect(cache.getStampFor('foo'), null);
|
|
|
|
when(file.existsSync()).thenReturn(true);
|
|
when(file.readAsStringSync()).thenThrow(const FileSystemException());
|
|
|
|
expect(cache.getStampFor('foo'), null);
|
|
|
|
when(file.existsSync()).thenReturn(true);
|
|
when(file.readAsStringSync()).thenReturn('ABC ');
|
|
|
|
expect(cache.getStampFor('foo'), 'ABC');
|
|
});
|
|
|
|
testWithoutContext('PubDependencies needs to be updated if the package config'
|
|
' file or the source directories are missing', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
|
final PubDependencies pubDependencies = PubDependencies(
|
|
flutterRoot: () => '',
|
|
logger: logger,
|
|
pub: () => MockPub(),
|
|
);
|
|
|
|
expect(await pubDependencies.isUpToDate(fileSystem), false); // no package config
|
|
|
|
fileSystem.file('packages/flutter_tools/.packages')
|
|
..createSync(recursive: true)
|
|
..writeAsStringSync('\n');
|
|
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/lib/foo.dart')
|
|
.createSync(recursive: true);
|
|
|
|
expect(await pubDependencies.isUpToDate(fileSystem), true);
|
|
});
|
|
|
|
testWithoutContext('PubDependencies updates via pub get', () async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
|
final MockPub pub = MockPub();
|
|
final PubDependencies pubDependencies = PubDependencies(
|
|
flutterRoot: () => '',
|
|
logger: logger,
|
|
pub: () => pub,
|
|
);
|
|
|
|
await pubDependencies.update(MockArtifactUpdater(), logger, fileSystem, MockOperatingSystemUtils());
|
|
|
|
verify(pub.get(
|
|
context: PubContext.pubGet,
|
|
directory: 'packages/flutter_tools',
|
|
)).called(1);
|
|
});
|
|
|
|
group('AndroidMavenArtifacts', () {
|
|
MemoryFileSystem memoryFileSystem;
|
|
Cache cache;
|
|
|
|
setUp(() {
|
|
memoryFileSystem = MemoryFileSystem.test();
|
|
cache = Cache.test(
|
|
fileSystem: memoryFileSystem,
|
|
processManager: FakeProcessManager.any(),
|
|
);
|
|
});
|
|
|
|
testWithoutContext('AndroidMavenArtifacts has a specified development artifact', () async {
|
|
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux'));
|
|
expect(mavenArtifacts.developmentArtifact, DevelopmentArtifact.androidMaven);
|
|
});
|
|
|
|
testUsingContext('AndroidMavenArtifacts can invoke Gradle resolve dependencies if Android SDK is present', () async {
|
|
Cache.flutterRoot = '';
|
|
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux'));
|
|
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(MockArtifactUpdater(), BufferLogger.test(), memoryFileSystem, MockOperatingSystemUtils());
|
|
|
|
expect(await mavenArtifacts.isUpToDate(memoryFileSystem), isFalse);
|
|
}, overrides: <Type, Generator>{
|
|
Cache: () => cache,
|
|
FileSystem: () => memoryFileSystem,
|
|
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
|
const FakeCommand(command: <String>[
|
|
'/cache/bin/cache/flutter_gradle_wrapper.rand0/gradlew',
|
|
'-b',
|
|
'packages/flutter_tools/gradle/resolve_dependencies.gradle',
|
|
'--project-cache-dir',
|
|
'cache/bin/cache/flutter_gradle_wrapper.rand0',
|
|
'resolveDependencies',
|
|
])
|
|
]),
|
|
AndroidSdk: () => FakeAndroidSdk()
|
|
});
|
|
|
|
testUsingContext('AndroidMavenArtifacts is a no-op if the Android SDK is absent', () async {
|
|
final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux'));
|
|
expect(await mavenArtifacts.isUpToDate(memoryFileSystem), isFalse);
|
|
|
|
await mavenArtifacts.update(MockArtifactUpdater(), BufferLogger.test(), memoryFileSystem, MockOperatingSystemUtils());
|
|
|
|
expect(await mavenArtifacts.isUpToDate(memoryFileSystem), isFalse);
|
|
}, overrides: <Type, Generator>{
|
|
Cache: () => cache,
|
|
FileSystem: () => memoryFileSystem,
|
|
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[]),
|
|
AndroidSdk: () => null // Android SDK was not located.
|
|
});
|
|
});
|
|
}
|
|
|
|
class FakeCachedArtifact extends EngineCachedArtifact {
|
|
FakeCachedArtifact({
|
|
String stampName = 'STAMP',
|
|
@required Cache cache,
|
|
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 FakeDownloadedArtifact extends CachedArtifact {
|
|
FakeDownloadedArtifact(this.downloadedFile, Cache cache) : super(
|
|
'fake',
|
|
cache,
|
|
DevelopmentArtifact.universal,
|
|
);
|
|
|
|
final File downloadedFile;
|
|
|
|
@override
|
|
Future<void> updateInner(ArtifactUpdater artifactUpdater, FileSystem fileSystem, OperatingSystemUtils operatingSystemUtils) async { }
|
|
}
|
|
|
|
class MockArtifactUpdater extends Mock implements ArtifactUpdater {}
|
|
class MockFile extends Mock implements File {}
|
|
class MockCachedArtifact extends Mock implements CachedArtifact {}
|
|
class MockIosUsbArtifacts extends Mock implements IosUsbArtifacts {}
|
|
class MockInternetAddress extends Mock implements InternetAddress {}
|
|
class MockCache extends Mock implements Cache {}
|
|
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
|
|
class MockVersionedPackageResolver extends Mock implements VersionedPackageResolver {}
|
|
class MockPub extends Mock implements Pub {}
|
|
class FakeCache extends Cache {
|
|
FakeCache({
|
|
@required Logger logger,
|
|
@required FileSystem fileSystem,
|
|
@required Platform platform,
|
|
@required OperatingSystemUtils osUtils,
|
|
}) : super(
|
|
logger: logger,
|
|
fileSystem: fileSystem,
|
|
platform: platform,
|
|
osUtils: osUtils,
|
|
artifacts: <ArtifactSet>[],
|
|
);
|
|
|
|
File stampFile;
|
|
|
|
@override
|
|
File getStampFileFor(String artifactName) {
|
|
return stampFile;
|
|
}
|
|
}
|
|
class FakeAndroidSdk extends Fake implements AndroidSdk {}
|