diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index cd0e93194b9..5ff744794c6 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart @@ -103,44 +103,37 @@ class Cache { Cache({ @protected Directory rootOverride, @protected List artifacts, - // TODO(jonahwilliams): make required once migrated to context-free. - Logger logger, - FileSystem fileSystem, - Platform platform, - OperatingSystemUtils osUtils, + @required Logger logger, + @required FileSystem fileSystem, + @required Platform platform, + @required OperatingSystemUtils osUtils, }) : _rootOverride = rootOverride, - _logger = logger ?? globals.logger, - _fileSystem = fileSystem ?? globals.fs, - _platform = platform ?? globals.platform, - _osUtils = osUtils ?? globals.os { - // TODO(zra): Move to initializer list once logger and platform parameters - // are required. - _net = Net(logger: _logger, platform: _platform); - _fsUtils = FileSystemUtils(fileSystem: _fileSystem, platform: _platform); + _logger = logger, + _fileSystem = fileSystem, + _platform = platform, + _osUtils = osUtils, + _net = Net(logger: logger, platform: platform), + _fsUtils = FileSystemUtils(fileSystem: fileSystem, platform: platform) { if (artifacts == null) { _artifacts.add(MaterialFonts(this)); - _artifacts.add(GradleWrapper(this)); - _artifacts.add(AndroidMavenArtifacts(this)); - _artifacts.add(AndroidGenSnapshotArtifacts(this)); + _artifacts.add(AndroidGenSnapshotArtifacts(this, platform: _platform)); _artifacts.add(AndroidInternalBuildArtifacts(this)); - - _artifacts.add(IOSEngineArtifacts(this)); + _artifacts.add(IOSEngineArtifacts(this, platform: _platform)); _artifacts.add(FlutterWebSdk(this, platform: _platform)); - _artifacts.add(FlutterSdk(this)); + _artifacts.add(FlutterSdk(this, platform: _platform)); _artifacts.add(WindowsEngineArtifacts(this, platform: _platform)); - _artifacts.add(MacOSEngineArtifacts(this)); + _artifacts.add(MacOSEngineArtifacts(this, platform: _platform)); _artifacts.add(LinuxEngineArtifacts(this, platform: _platform)); - _artifacts.add(LinuxFuchsiaSDKArtifacts(this)); - _artifacts.add(MacOSFuchsiaSDKArtifacts(this)); - _artifacts.add(FlutterRunnerSDKArtifacts(this)); + _artifacts.add(LinuxFuchsiaSDKArtifacts(this, platform: _platform)); + _artifacts.add(MacOSFuchsiaSDKArtifacts(this, platform: _platform)); + _artifacts.add(FlutterRunnerSDKArtifacts(this, platform: _platform)); _artifacts.add(FlutterRunnerDebugSymbols(this, platform: _platform)); for (final String artifactName in IosUsbArtifacts.artifactNames) { - _artifacts.add(IosUsbArtifacts(artifactName, this)); + _artifacts.add(IosUsbArtifacts(artifactName, this, platform: _platform)); } - _artifacts.add(FontSubsetArtifacts(this)); + _artifacts.add(FontSubsetArtifacts(this, platform: _platform)); _artifacts.add(PubDependencies( - fileSystem: _fileSystem, logger: _logger, // flutter root and pub must be lazily initialized to avoid accessing // before the version is determined. @@ -324,19 +317,18 @@ class Cache { /// (see [FlutterCommandRunner.runCommand]). /// /// This uses normal POSIX flock semantics. - static Future lock() async { + Future lock() async { if (!_lockEnabled) { return; } assert(_lock == null); final File lockFile = - globals.fs.file(globals.fs.path.join(flutterRoot, 'bin', 'cache', 'lockfile')); + _fileSystem.file(_fileSystem.path.join(flutterRoot, 'bin', 'cache', 'lockfile')); try { _lock = lockFile.openSync(mode: FileMode.write); } on FileSystemException catch (e) { - globals.printError('Failed to open or create the artifact cache lockfile: "$e"'); - globals.printError('Please ensure you have permissions to create or open ' - '${lockFile.path}'); + _logger.printError('Failed to open or create the artifact cache lockfile: "$e"'); + _logger.printError('Please ensure you have permissions to create or open ${lockFile.path}'); throwToolExit('Failed to open or create the lockfile'); } bool locked = false; @@ -347,8 +339,8 @@ class Cache { locked = true; } on FileSystemException { if (!printed) { - globals.printTrace('Waiting to be able to obtain lock of Flutter binary artifacts directory: ${_lock.path}'); - globals.printStatus('Waiting for another flutter command to release the startup lock...'); + _logger.printTrace('Waiting to be able to obtain lock of Flutter binary artifacts directory: ${_lock.path}'); + _logger.printStatus('Waiting for another flutter command to release the startup lock...'); printed = true; } await Future.delayed(const Duration(milliseconds: 50)); @@ -360,7 +352,7 @@ class Cache { /// /// This happens automatically on startup (see [FlutterCommand.verifyThenRunCommand]) /// after the command's required artifacts are updated. - static void releaseLock() { + void releaseLock() { if (!_lockEnabled || _lock == null) { return; } @@ -370,16 +362,15 @@ class Cache { /// Checks if the current process owns the lock for the cache directory at /// this very moment; throws a [StateError] if it doesn't. - static void checkLockAcquired([Platform platform]) { - if (_lockEnabled && _lock == null && (platform ?? globals.platform).environment['FLUTTER_ALREADY_LOCKED'] != 'true') { + void checkLockAcquired() { + if (_lockEnabled && _lock == null && _platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { throw StateError( 'The current process does not own the lock for the cache directory. This is a bug in Flutter CLI tools.', ); } } - String _dartSdkVersion; - + /// The current version of Dart used to build Flutter and run the tool. String get dartSdkVersion { if (_dartSdkVersion == null) { // Make the version string more customer-friendly. @@ -392,6 +383,7 @@ class Cache { } return _dartSdkVersion; } + String _dartSdkVersion; /// The current version of the Flutter engine the flutter tool will download. String get engineRevision { @@ -547,7 +539,7 @@ class Cache { Future isUpToDate() async { for (final ArtifactSet artifact in _artifacts) { - if (!await artifact.isUpToDate()) { + if (!await artifact.isUpToDate(_fileSystem)) { return false; } } @@ -564,11 +556,11 @@ class Cache { _logger.printTrace('Artifact $artifact is not required, skipping update.'); continue; } - if (await artifact.isUpToDate()) { + if (await artifact.isUpToDate(_fileSystem)) { continue; } try { - await artifact.update(_artifactUpdater); + await artifact.update(_artifactUpdater, _logger, _fileSystem, _osUtils); } on SocketException catch (e) { if (_hostsBlockedInChina.contains(e.address?.host)) { _logger.printError( @@ -588,15 +580,15 @@ class Cache { bool includeAllPlatforms = true, }) async { final bool includeAllPlatformsState = this.includeAllPlatforms; - bool allAvailible = true; + bool allAvailable = true; this.includeAllPlatforms = includeAllPlatforms; for (final ArtifactSet cachedArtifact in _artifacts) { if (cachedArtifact is EngineCachedArtifact) { - allAvailible &= await cachedArtifact.checkForArtifacts(engineVersion); + allAvailable &= await cachedArtifact.checkForArtifacts(engineVersion); } } this.includeAllPlatforms = includeAllPlatformsState; - return allAvailible; + return allAvailable; } Future doesRemoteExist(String message, Uri url) async { @@ -621,7 +613,7 @@ abstract class ArtifactSet { final DevelopmentArtifact developmentArtifact; /// [true] if the artifact is up to date. - Future isUpToDate(); + Future isUpToDate(FileSystem fileSystem); /// The environment variables (if any) required to consume the artifacts. Map get environment { @@ -629,7 +621,12 @@ abstract class ArtifactSet { } /// Updates the artifact. - Future update(ArtifactUpdater artifactUpdater); + Future update( + ArtifactUpdater artifactUpdater, + Logger logger, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ); /// The canonical name of the artifact. String get name; @@ -665,34 +662,39 @@ abstract class CachedArtifact extends ArtifactSet { } @override - Future isUpToDate() async { + Future isUpToDate(FileSystem fileSystem) async { if (!location.existsSync()) { return false; } if (version != cache.getStampFor(stampName)) { return false; } - return isUpToDateInner(); + return isUpToDateInner(fileSystem); } @override - Future update(ArtifactUpdater artifactUpdater) async { + Future update( + ArtifactUpdater artifactUpdater, + Logger logger, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { if (!location.existsSync()) { try { location.createSync(recursive: true); } on FileSystemException catch (err) { - globals.printError(err.toString()); + logger.printError(err.toString()); throwToolExit( 'Failed to create directory for flutter cache at ${location.path}. ' 'Flutter may be missing permissions in its cache directory.' ); } } - await updateInner(artifactUpdater); + await updateInner(artifactUpdater, fileSystem, operatingSystemUtils); try { cache.setStampFor(stampName, version); } on FileSystemException catch (err) { - globals.printError( + logger.printError( 'The new artifact "$name" was downloaded, but Flutter failed to update ' 'its stamp file, receiving the error "$err". ' 'Flutter can continue, but the artifact may be re-downloaded on ' @@ -703,10 +705,13 @@ abstract class CachedArtifact extends ArtifactSet { } /// Hook method for extra checks for being up-to-date. - bool isUpToDateInner() => true; + bool isUpToDateInner(FileSystem fileSystem) => true; - /// Template method to perform artifact update. - Future updateInner(ArtifactUpdater artifactUpdater); + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ); Uri _toStorageUri(String path) => Uri.parse('${cache.storageBaseUrl}/$path'); } @@ -720,24 +725,23 @@ class PubDependencies extends ArtifactSet { PubDependencies({ // Needs to be lazy to avoid reading from the cache before the root is initialized. @required String Function() flutterRoot, - @required FileSystem fileSystem, @required Logger logger, @required Pub Function() pub, }) : _logger = logger, - _fileSystem = fileSystem, _flutterRoot = flutterRoot, _pub = pub, super(DevelopmentArtifact.universal); final String Function() _flutterRoot; - final FileSystem _fileSystem; final Logger _logger; final Pub Function() _pub; @override - Future isUpToDate() async { - final File toolPackageConfig = _fileSystem.file( - _fileSystem.path.join(_flutterRoot(), 'packages', 'flutter_tools', kPackagesFileName), + Future isUpToDate( + FileSystem fileSystem, + ) async { + final File toolPackageConfig = fileSystem.file( + fileSystem.path.join(_flutterRoot(), 'packages', 'flutter_tools', kPackagesFileName), ); if (!toolPackageConfig.existsSync()) { return false; @@ -751,7 +755,7 @@ class PubDependencies extends ArtifactSet { return false; } for (final Package package in packageConfig.packages) { - if (!_fileSystem.directory(package.packageUriRoot).existsSync()) { + if (!fileSystem.directory(package.packageUriRoot).existsSync()) { return false; } } @@ -762,10 +766,15 @@ class PubDependencies extends ArtifactSet { String get name => 'pub_dependencies'; @override - Future update(ArtifactUpdater artifactUpdater) async { + Future update( + ArtifactUpdater artifactUpdater, + Logger logger, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { await _pub().get( context: PubContext.pubGet, - directory: _fileSystem.path.join(_flutterRoot(), 'packages', 'flutter_tools'), + directory: fileSystem.path.join(_flutterRoot(), 'packages', 'flutter_tools'), generateSyntheticPackage: false, ); } @@ -780,7 +789,11 @@ class MaterialFonts extends CachedArtifact { ); @override - Future updateInner(ArtifactUpdater artifactUpdater) { + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) { final Uri archiveUri = _toStorageUri(version); return artifactUpdater.downloadZipArchive('Downloading Material fonts...', archiveUri, location); } @@ -791,8 +804,8 @@ class MaterialFonts extends CachedArtifact { /// /// This SDK references code within the regular Dart sdk to reduce download size. class FlutterWebSdk extends CachedArtifact { - FlutterWebSdk(Cache cache, {Platform platform}) - : _platform = platform ?? globals.platform, + FlutterWebSdk(Cache cache, {@required Platform platform}) + : _platform = platform, super( 'flutter_web_sdk', cache, @@ -808,7 +821,11 @@ class FlutterWebSdk extends CachedArtifact { String get version => cache.getVersionFor('engine'); @override - Future updateInner(ArtifactUpdater artifactUpdater) async { + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { String platformName = 'flutter-web-sdk-'; if (_platform.isMacOS) { platformName += 'darwin-x64'; @@ -859,24 +876,24 @@ abstract class EngineCachedArtifact extends CachedArtifact { List getPackageDirs(); @override - bool isUpToDateInner() { + bool isUpToDateInner(FileSystem fileSystem) { final Directory pkgDir = cache.getCacheDir('pkg'); for (final String pkgName in getPackageDirs()) { - final String pkgPath = globals.fs.path.join(pkgDir.path, pkgName); - if (!globals.fs.directory(pkgPath).existsSync()) { + final String pkgPath = fileSystem.path.join(pkgDir.path, pkgName); + if (!fileSystem.directory(pkgPath).existsSync()) { return false; } } for (final List toolsDir in getBinaryDirs()) { - final Directory dir = globals.fs.directory(globals.fs.path.join(location.path, toolsDir[0])); + final Directory dir = fileSystem.directory(fileSystem.path.join(location.path, toolsDir[0])); if (!dir.existsSync()) { return false; } } for (final String licenseDir in getLicenseDirs()) { - final File file = globals.fs.file(globals.fs.path.join(location.path, licenseDir, 'LICENSE')); + final File file = fileSystem.file(fileSystem.path.join(location.path, licenseDir, 'LICENSE')); if (!file.existsSync()) { return false; } @@ -885,7 +902,11 @@ abstract class EngineCachedArtifact extends CachedArtifact { } @override - Future updateInner(ArtifactUpdater artifactUpdater) async { + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { final String url = '${cache.storageBaseUrl}/flutter_infra/flutter/$version/'; final Directory pkgDir = cache.getCacheDir('pkg'); @@ -896,28 +917,28 @@ abstract class EngineCachedArtifact extends CachedArtifact { for (final List toolsDir in getBinaryDirs()) { final String cacheDir = toolsDir[0]; final String urlPath = toolsDir[1]; - final Directory dir = globals.fs.directory(globals.fs.path.join(location.path, cacheDir)); + final Directory dir = fileSystem.directory(fileSystem.path.join(location.path, cacheDir)); // Avoid printing things like 'Downloading linux-x64 tools...' multiple times. final String friendlyName = urlPath.replaceAll('/artifacts.zip', '').replaceAll('.zip', ''); await artifactUpdater.downloadZipArchive('Downloading $friendlyName tools...', Uri.parse(url + urlPath), dir); - _makeFilesExecutable(dir); + _makeFilesExecutable(dir, operatingSystemUtils); const List frameworkNames = ['Flutter', 'FlutterMacOS']; for (final String frameworkName in frameworkNames) { - final File frameworkZip = globals.fs.file(globals.fs.path.join(dir.path, '$frameworkName.framework.zip')); + final File frameworkZip = fileSystem.file(fileSystem.path.join(dir.path, '$frameworkName.framework.zip')); if (frameworkZip.existsSync()) { - final Directory framework = globals.fs.directory(globals.fs.path.join(dir.path, '$frameworkName.framework')); + final Directory framework = fileSystem.directory(fileSystem.path.join(dir.path, '$frameworkName.framework')); framework.createSync(); - globals.os.unzip(frameworkZip, framework); + operatingSystemUtils.unzip(frameworkZip, framework); } } } final File licenseSource = cache.getLicenseFile(); for (final String licenseDir in getLicenseDirs()) { - final String licenseDestinationPath = globals.fs.path.join(location.path, licenseDir, 'LICENSE'); + final String licenseDestinationPath = fileSystem.path.join(location.path, licenseDir, 'LICENSE'); await licenseSource.copy(licenseDestinationPath); } } @@ -928,8 +949,7 @@ abstract class EngineCachedArtifact extends CachedArtifact { bool exists = false; for (final String pkgName in getPackageDirs()) { - exists = await cache.doesRemoteExist('Checking package $pkgName is available...', - Uri.parse(url + pkgName + '.zip')); + exists = await cache.doesRemoteExist('Checking package $pkgName is available...', Uri.parse(url + pkgName + '.zip')); if (!exists) { return false; } @@ -947,16 +967,14 @@ abstract class EngineCachedArtifact extends CachedArtifact { return true; } - void _makeFilesExecutable(Directory dir) { - globals.os.chmod(dir, 'a+r,a+x'); - for (final FileSystemEntity entity in dir.listSync(recursive: true)) { - if (entity is File) { - final FileStat stat = entity.statSync(); - final bool isUserExecutable = ((stat.mode >> 6) & 0x1) == 1; - if (entity.basename == 'flutter_tester' || isUserExecutable) { - // Make the file readable and executable by all users. - globals.os.chmod(entity, 'a+r,a+x'); - } + void _makeFilesExecutable(Directory dir, OperatingSystemUtils operatingSystemUtils) { + operatingSystemUtils.chmod(dir, 'a+r,a+x'); + for (final File file in dir.listSync(recursive: true).whereType()) { + final FileStat stat = file.statSync(); + final bool isUserExecutable = ((stat.mode >> 6) & 0x1) == 1; + if (file.basename == 'flutter_tester' || isUserExecutable) { + // Make the file readable and executable by all users. + operatingSystemUtils.chmod(file, 'a+r,a+x'); } } } @@ -964,11 +982,16 @@ abstract class EngineCachedArtifact extends CachedArtifact { /// A cached artifact containing the dart:ui source code. class FlutterSdk extends EngineCachedArtifact { - FlutterSdk(Cache cache) : super( - 'flutter_sdk', - cache, - DevelopmentArtifact.universal, - ); + FlutterSdk(Cache cache, { + @required Platform platform, + }) : _platform = platform, + super( + 'flutter_sdk', + cache, + DevelopmentArtifact.universal, + ); + + final Platform _platform; @override List getPackageDirs() => const ['sky_engine']; @@ -983,11 +1006,11 @@ class FlutterSdk extends EngineCachedArtifact { ['linux-x64', 'linux-x64/artifacts.zip'], ['darwin-x64', 'darwin-x64/artifacts.zip'], ] - else if (globals.platform.isWindows) + else if (_platform.isWindows) ['windows-x64', 'windows-x64/artifacts.zip'] - else if (globals.platform.isMacOS) + else if (_platform.isMacOS) ['darwin-x64', 'darwin-x64/artifacts.zip'] - else if (globals.platform.isLinux) + else if (_platform.isLinux) ['linux-x64', 'linux-x64/artifacts.zip'], ]; } @@ -997,18 +1020,23 @@ class FlutterSdk extends EngineCachedArtifact { } class MacOSEngineArtifacts extends EngineCachedArtifact { - MacOSEngineArtifacts(Cache cache) : super( - 'macos-sdk', - cache, - DevelopmentArtifact.macOS, - ); + MacOSEngineArtifacts(Cache cache, { + @required Platform platform, + }) : _platform = platform, + super( + 'macos-sdk', + cache, + DevelopmentArtifact.macOS, + ); + + final Platform _platform; @override List getPackageDirs() => const []; @override List> getBinaryDirs() { - if (globals.platform.isMacOS || ignorePlatformFiltering) { + if (_platform.isMacOS || ignorePlatformFiltering) { return _macOSDesktopBinaryDirs; } return const >[]; @@ -1076,11 +1104,16 @@ class LinuxEngineArtifacts extends EngineCachedArtifact { /// The artifact used to generate snapshots for Android builds. class AndroidGenSnapshotArtifacts extends EngineCachedArtifact { - AndroidGenSnapshotArtifacts(Cache cache) : super( - 'android-sdk', - cache, - DevelopmentArtifact.androidGenSnapshot, - ); + AndroidGenSnapshotArtifacts(Cache cache, { + @required Platform platform, + }) : _platform = platform, + super( + 'android-sdk', + cache, + DevelopmentArtifact.androidGenSnapshot, + ); + + final Platform _platform; @override List getPackageDirs() => const []; @@ -1093,11 +1126,11 @@ class AndroidGenSnapshotArtifacts extends EngineCachedArtifact { ..._linuxBinaryDirs, ..._windowsBinaryDirs, ..._dartSdks, - ] else if (globals.platform.isWindows) + ] else if (_platform.isWindows) ..._windowsBinaryDirs - else if (globals.platform.isMacOS) + else if (_platform.isMacOS) ..._osxBinaryDirs - else if (globals.platform.isLinux) + else if (_platform.isLinux) ..._linuxBinaryDirs, ]; } @@ -1106,6 +1139,64 @@ class AndroidGenSnapshotArtifacts extends EngineCachedArtifact { List getLicenseDirs() { return []; } } +/// A cached artifact containing the Maven dependencies used to build Android projects. +class AndroidMavenArtifacts extends ArtifactSet { + AndroidMavenArtifacts(this.cache, { + @required Platform platform, + }) : _platform = platform, + super(DevelopmentArtifact.androidMaven); + + final Platform _platform; + final Cache cache; + + @override + Future update( + ArtifactUpdater artifactUpdater, + Logger logger, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { + final Directory tempDir = cache.getRoot().createTempSync( + 'flutter_gradle_wrapper.', + ); + gradleUtils.injectGradleWrapperIfNeeded(tempDir); + + final Status status = logger.startProgress('Downloading Android Maven dependencies...'); + final File gradle = tempDir.childFile( + _platform.isWindows ? 'gradlew.bat' : 'gradlew', + ); + try { + final String gradleExecutable = gradle.absolute.path; + final String flutterSdk = globals.fsUtils.escapePath(Cache.flutterRoot); + final RunResult processResult = await globals.processUtils.run( + [ + gradleExecutable, + '-b', globals.fs.path.join(flutterSdk, 'packages', 'flutter_tools', 'gradle', 'resolve_dependencies.gradle'), + '--project-cache-dir', tempDir.path, + 'resolveDependencies', + ], + environment: gradleEnvironment); + if (processResult.exitCode != 0) { + logger.printError('Failed to download the Android dependencies'); + } + } finally { + status.stop(); + tempDir.deleteSync(recursive: true); + } + } + + @override + Future isUpToDate(FileSystem fileSystem) async { + // The dependencies are downloaded and cached by Gradle. + // The tool doesn't know if the dependencies are already cached at this point. + // Therefore, call Gradle to figure this out. + return false; + } + + @override + String get name => 'android-maven-artifacts'; +} + /// Artifacts used for internal builds. The flutter tool builds Android projects /// using the artifacts cached by [AndroidMavenArtifacts]. class AndroidInternalBuildArtifacts extends EngineCachedArtifact { @@ -1127,73 +1218,29 @@ class AndroidInternalBuildArtifacts extends EngineCachedArtifact { List getLicenseDirs() { return []; } } -/// A cached artifact containing the Maven dependencies used to build Android projects. -class AndroidMavenArtifacts extends ArtifactSet { - AndroidMavenArtifacts(this.cache) : super(DevelopmentArtifact.androidMaven); - - final Cache cache; - - @override - Future update(ArtifactUpdater artifactUpdater) async { - final Directory tempDir = cache.getRoot().createTempSync( - 'flutter_gradle_wrapper.', - ); - gradleUtils.injectGradleWrapperIfNeeded(tempDir); - - final Status status = globals.logger.startProgress('Downloading Android Maven dependencies...'); - final File gradle = tempDir.childFile( - globals.platform.isWindows ? 'gradlew.bat' : 'gradlew', - ); - try { - final String gradleExecutable = gradle.absolute.path; - final String flutterSdk = globals.fsUtils.escapePath(Cache.flutterRoot); - final RunResult processResult = await globals.processUtils.run( - [ - gradleExecutable, - '-b', globals.fs.path.join(flutterSdk, 'packages', 'flutter_tools', 'gradle', 'resolve_dependencies.gradle'), - '--project-cache-dir', tempDir.path, - 'resolveDependencies', - ], - environment: gradleEnvironment); - if (processResult.exitCode != 0) { - globals.printError('Failed to download the Android dependencies'); - } - } finally { - status.stop(); - tempDir.deleteSync(recursive: true); - } - } - - @override - Future isUpToDate() async { - // The dependencies are downloaded and cached by Gradle. - // The tool doesn't know if the dependencies are already cached at this point. - // Therefore, call Gradle to figure this out. - return false; - } - - @override - String get name => 'android-maven-artifacts'; -} - class IOSEngineArtifacts extends EngineCachedArtifact { - IOSEngineArtifacts(Cache cache) : super( - 'ios-sdk', - cache, - DevelopmentArtifact.iOS, - ); + IOSEngineArtifacts(Cache cache, { + @required Platform platform, + }) : _platform = platform, + super( + 'ios-sdk', + cache, + DevelopmentArtifact.iOS, + ); + + final Platform _platform; @override List> getBinaryDirs() { return >[ - if (globals.platform.isMacOS || ignorePlatformFiltering) + if (_platform.isMacOS || ignorePlatformFiltering) ..._iosBinaryDirs, ]; } @override List getLicenseDirs() { - if (globals.platform.isMacOS || ignorePlatformFiltering) { + if (_platform.isMacOS || ignorePlatformFiltering) { return const ['ios', 'ios-profile', 'ios-release']; } return const []; @@ -1218,32 +1265,36 @@ class GradleWrapper extends CachedArtifact { List get _gradleScripts => ['gradlew', 'gradlew.bat']; - String get _gradleWrapper => globals.fs.path.join('gradle', 'wrapper', 'gradle-wrapper.jar'); - @override - Future updateInner(ArtifactUpdater artifactUpdater) { + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { final Uri archiveUri = _toStorageUri(version); - return artifactUpdater.downloadZippedTarball('Downloading Gradle Wrapper...', archiveUri, location).then((_) { - // Delete property file, allowing templates to provide it. - globals.fs.file(globals.fs.path.join(location.path, 'gradle', 'wrapper', 'gradle-wrapper.properties')).deleteSync(); - // Remove NOTICE file. Should not be part of the template. - globals.fs.file(globals.fs.path.join(location.path, 'NOTICE')).deleteSync(); - }); + await artifactUpdater.downloadZippedTarball('Downloading Gradle Wrapper...', archiveUri, location); + // Delete property file, allowing templates to provide it. + fileSystem.file(fileSystem.path.join(location.path, 'gradle', 'wrapper', 'gradle-wrapper.properties')).deleteSync(); + // Remove NOTICE file. Should not be part of the template. + fileSystem.file(fileSystem.path.join(location.path, 'NOTICE')).deleteSync(); } @override - bool isUpToDateInner() { - final Directory wrapperDir = cache.getCacheDir(globals.fs.path.join('artifacts', 'gradle_wrapper')); - if (!globals.fs.directory(wrapperDir).existsSync()) { + bool isUpToDateInner( + FileSystem fileSystem, + ) { + final String gradleWrapper = fileSystem.path.join('gradle', 'wrapper', 'gradle-wrapper.jar'); + final Directory wrapperDir = cache.getCacheDir(fileSystem.path.join('artifacts', 'gradle_wrapper')); + if (!fileSystem.directory(wrapperDir).existsSync()) { return false; } for (final String scriptName in _gradleScripts) { - final File scriptFile = globals.fs.file(globals.fs.path.join(wrapperDir.path, scriptName)); + final File scriptFile = fileSystem.file(fileSystem.path.join(wrapperDir.path, scriptName)); if (!scriptFile.existsSync()) { return false; } } - final File gradleWrapperJar = globals.fs.file(globals.fs.path.join(wrapperDir.path, _gradleWrapper)); + final File gradleWrapperJar = fileSystem.file(fileSystem.path.join(wrapperDir.path, gradleWrapper)); if (!gradleWrapperJar.existsSync()) { return false; } @@ -1251,8 +1302,7 @@ class GradleWrapper extends CachedArtifact { } } - const String _cipdBaseUrl = - 'https://chrome-infra-packages.appspot.com/dl'; +const String _cipdBaseUrl = 'https://chrome-infra-packages.appspot.com/dl'; /// Common functionality for pulling Fuchsia SDKs. abstract class _FuchsiaSDKArtifacts extends CachedArtifact { @@ -1278,11 +1328,16 @@ abstract class _FuchsiaSDKArtifacts extends CachedArtifact { /// The pre-built flutter runner for Fuchsia development. class FlutterRunnerSDKArtifacts extends CachedArtifact { - FlutterRunnerSDKArtifacts(Cache cache) : super( - 'flutter_runner', - cache, - DevelopmentArtifact.flutterRunner, - ); + FlutterRunnerSDKArtifacts(Cache cache, { + @required Platform platform, + }) : _platform = platform, + super( + 'flutter_runner', + cache, + DevelopmentArtifact.flutterRunner, + ); + + final Platform _platform; @override Directory get location => cache.getArtifactDirectory('flutter_runner'); @@ -1291,13 +1346,16 @@ class FlutterRunnerSDKArtifacts extends CachedArtifact { String get version => cache.getVersionFor('engine'); @override - Future updateInner(ArtifactUpdater artifactUpdater) async { - if (!globals.platform.isLinux && !globals.platform.isMacOS) { - return Future.value(); + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { + if (!_platform.isLinux && !_platform.isMacOS) { + return; } final String url = '$_cipdBaseUrl/flutter/fuchsia/+/git_revision:$version'; - await artifactUpdater.downloadZipArchive('Downloading package flutter runner...', - Uri.parse(url), location); + await artifactUpdater.downloadZipArchive('Downloading package flutter runner...', Uri.parse(url), location); } } @@ -1349,7 +1407,11 @@ class FlutterRunnerDebugSymbols extends CachedArtifact { } @override - Future updateInner(ArtifactUpdater artifactUpdater) async { + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { if (!_platform.isLinux && !_platform.isMacOS) { return; } @@ -1360,12 +1422,21 @@ class FlutterRunnerDebugSymbols extends CachedArtifact { /// The Fuchsia core SDK for Linux. class LinuxFuchsiaSDKArtifacts extends _FuchsiaSDKArtifacts { - LinuxFuchsiaSDKArtifacts(Cache cache) : super(cache, 'linux'); + LinuxFuchsiaSDKArtifacts(Cache cache, { + @required Platform platform, + }) : _platform = platform, + super(cache, 'linux'); + + final Platform _platform; @override - Future updateInner(ArtifactUpdater artifactUpdater) { - if (!globals.platform.isLinux) { - return Future.value(); + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { + if (!_platform.isLinux) { + return; } return _doUpdate(artifactUpdater); } @@ -1373,12 +1444,21 @@ class LinuxFuchsiaSDKArtifacts extends _FuchsiaSDKArtifacts { /// The Fuchsia core SDK for MacOS. class MacOSFuchsiaSDKArtifacts extends _FuchsiaSDKArtifacts { - MacOSFuchsiaSDKArtifacts(Cache cache) : super(cache, 'mac'); + MacOSFuchsiaSDKArtifacts(Cache cache, { + @required Platform platform, + }) : _platform = platform, + super(cache, 'mac'); + + final Platform _platform; @override - Future updateInner(ArtifactUpdater artifactUpdater) async { - if (!globals.platform.isMacOS) { - return Future.value(); + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { + if (!_platform.isMacOS) { + return; } return _doUpdate(artifactUpdater); } @@ -1386,7 +1466,12 @@ class MacOSFuchsiaSDKArtifacts extends _FuchsiaSDKArtifacts { /// Cached artifacts for font subsetting. class FontSubsetArtifacts extends EngineCachedArtifact { - FontSubsetArtifacts(Cache cache) : super(artifactName, cache, DevelopmentArtifact.universal); + FontSubsetArtifacts(Cache cache, { + @required Platform platform, + }) : _platform = platform, + super(artifactName, cache, DevelopmentArtifact.universal); + + final Platform _platform; static const String artifactName = 'font-subset'; @@ -1400,9 +1485,9 @@ class FontSubsetArtifacts extends EngineCachedArtifact { if (cache.includeAllPlatforms) { return artifacts.values.toList(); } else { - final List binaryDirs = artifacts[globals.platform.operatingSystem]; + final List binaryDirs = artifacts[_platform.operatingSystem]; if (binaryDirs == null) { - throwToolExit('Unsupported operating system: ${globals.platform.operatingSystem}'); + throwToolExit('Unsupported operating system: ${_platform.operatingSystem}'); } return >[binaryDirs]; } @@ -1417,12 +1502,16 @@ class FontSubsetArtifacts extends EngineCachedArtifact { /// Cached iOS/USB binary artifacts. class IosUsbArtifacts extends CachedArtifact { - IosUsbArtifacts(String name, Cache cache) : super( - name, - cache, - // This is universal to ensure every command checks for them first - DevelopmentArtifact.universal, - ); + IosUsbArtifacts(String name, Cache cache, { + @required Platform platform, + }) : _platform = platform, + super( + name, + cache, + DevelopmentArtifact.universal, + ); + + final Platform _platform; static const List artifactNames = [ 'libimobiledevice', @@ -1454,7 +1543,7 @@ class IosUsbArtifacts extends CachedArtifact { } @override - bool isUpToDateInner() { + bool isUpToDateInner(FileSystem fileSystem) { final List executables =_kExecutables[name]; if (executables == null) { return true; @@ -1468,14 +1557,18 @@ class IosUsbArtifacts extends CachedArtifact { } @override - Future updateInner(ArtifactUpdater artifactUpdater) { - if (!globals.platform.isMacOS && !ignorePlatformFiltering) { - return Future.value(); + Future updateInner( + ArtifactUpdater artifactUpdater, + FileSystem fileSystem, + OperatingSystemUtils operatingSystemUtils, + ) async { + if (!_platform.isMacOS && !ignorePlatformFiltering) { + return; } if (location.existsSync()) { location.deleteSync(recursive: true); } - return artifactUpdater.downloadZipArchive('Downloading $name...', archiveUri, location); + await artifactUpdater.downloadZipArchive('Downloading $name...', archiveUri, location); } @visibleForTesting @@ -1808,7 +1901,7 @@ class ArtifactUpdater { try { file.deleteSync(); } on FileSystemException catch (e) { - globals.printError('Failed to delete "${file.path}". Please delete manually. $e'); + _logger.printError('Failed to delete "${file.path}". Please delete manually. $e'); continue; } for (Directory directory = file.parent; directory.absolute.path != _tempStorage.absolute.path; directory = directory.parent) { diff --git a/packages/flutter_tools/lib/src/commands/precache.dart b/packages/flutter_tools/lib/src/commands/precache.dart index de4761486c0..6854fb82e0a 100644 --- a/packages/flutter_tools/lib/src/commands/precache.dart +++ b/packages/flutter_tools/lib/src/commands/precache.dart @@ -130,7 +130,7 @@ class PrecacheCommand extends FlutterCommand { Future runCommand() async { // Re-lock the cache. if (_platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { - await Cache.lock(); + await _cache.lock(); } if (boolArg('force')) { _cache.clearStampFiles(); diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart index fb9f7e9e209..0727a72217b 100644 --- a/packages/flutter_tools/lib/src/context_runner.dart +++ b/packages/flutter_tools/lib/src/context_runner.dart @@ -120,6 +120,7 @@ Future runInContext( fileSystem: globals.fs, logger: globals.logger, platform: globals.platform, + osUtils: globals.os, ), CocoaPods: () => CocoaPods( fileSystem: globals.fs, diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 7d7d76fad8c..243c03e4bef 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -962,7 +962,7 @@ abstract class FlutterCommand extends Command { void _registerSignalHandlers(String commandPath, DateTime startTime) { final SignalHandler handler = (io.ProcessSignal s) { - Cache.releaseLock(); + globals.cache.releaseLock(); _sendPostUsage( commandPath, const FlutterCommandResult(ExitStatus.killed), @@ -1035,7 +1035,7 @@ abstract class FlutterCommand extends Command { await globals.cache.updateAll({DevelopmentArtifact.universal}); await globals.cache.updateAll(await requiredArtifacts); } - Cache.releaseLock(); + globals.cache.releaseLock(); await validateCommand(); diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart index a9414c7c857..66d31bb0b77 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart @@ -235,7 +235,7 @@ class FlutterCommandRunner extends CommandRunner { globals.logger.quiet = topLevelResults['quiet'] as bool; if (globals.platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { - await Cache.lock(); + await globals.cache.lock(); } if (topLevelResults['suppress-analytics'] as bool) { diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart index e019555719c..3ec6bcd8995 100644 --- a/packages/flutter_tools/lib/src/version.dart +++ b/packages/flutter_tools/lib/src/version.dart @@ -484,7 +484,7 @@ class FlutterVersion { /// Returns null if the cached version is out-of-date or missing, and we are /// unable to reach the server to get the latest version. Future _getLatestAvailableFlutterDate() async { - Cache.checkLockAcquired(); + globals.cache.checkLockAcquired(); final VersionCheckStamp versionCheckStamp = await VersionCheckStamp.load(); if (versionCheckStamp.lastTimeVersionWasChecked != null) { diff --git a/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart index 51a0b0566a7..2a23db2b0d7 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/precache_test.dart @@ -20,7 +20,7 @@ void main() { setUp(() { cache = MockCache(); // Release lock between test cases. - Cache.releaseLock(); + cache.releaseLock(); when(cache.isUpToDate()).thenAnswer((Invocation _) => Future.value(false)); when(cache.updateAll(any)).thenAnswer((Invocation invocation) { @@ -39,9 +39,8 @@ void main() { ); await createTestCommandRunner(command).run(const ['precache']); - expect(Cache.isLocked(), isTrue); // Do not throw StateError, lock is acquired. - expect(() => Cache.checkLockAcquired(platform), returnsNormally); + expect(() => cache.checkLockAcquired(), returnsNormally); }); testUsingContext('precache should not re-entrantly acquire lock', () async { @@ -62,7 +61,7 @@ void main() { expect(Cache.isLocked(), isFalse); // Do not throw StateError, acquired reentrantly with FLUTTER_ALREADY_LOCKED. - expect(() => Cache.checkLockAcquired(platform), returnsNormally); + expect(() => cache.checkLockAcquired(), returnsNormally); }); testUsingContext('precache downloads web artifacts on dev branch when feature is enabled.', () async { diff --git a/packages/flutter_tools/test/general.shard/android/gradle_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_test.dart index be389dfd40b..634c2c814c2 100644 --- a/packages/flutter_tools/test/general.shard/android/gradle_test.dart +++ b/packages/flutter_tools/test/general.shard/android/gradle_test.dart @@ -1024,7 +1024,7 @@ plugin1=${plugin1.path} when(mockAndroidSdk.directory).thenReturn('irrelevant'); final Directory rootDirectory = fileSystem.currentDirectory; - cache = Cache( + cache = Cache.test( rootOverride: rootDirectory, fileSystem: fileSystem, ); diff --git a/packages/flutter_tools/test/general.shard/android/gradle_utils_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_utils_test.dart index 1d25b6635bb..cd850b10784 100644 --- a/packages/flutter_tools/test/general.shard/android/gradle_utils_test.dart +++ b/packages/flutter_tools/test/general.shard/android/gradle_utils_test.dart @@ -72,7 +72,7 @@ void main() { 'zipStorePath=wrapper/dists\n' 'distributionUrl=https\\://services.gradle.org/distributions/gradle-5.6.2-all.zip\n'); }, overrides: { - Cache: () => Cache(rootOverride: tempDir), + Cache: () => Cache.test(rootOverride: tempDir, fileSystem: memoryFileSystem), FileSystem: () => memoryFileSystem, ProcessManager: () => FakeProcessManager.any(), }); @@ -113,7 +113,7 @@ void main() { 'zipStorePath=wrapper/dists\n' 'distributionUrl=https\\://services.gradle.org/distributions/gradle-5.6.2-all.zip\n'); }, overrides: { - Cache: () => Cache(rootOverride: tempDir), + Cache: () => Cache.test(rootOverride: tempDir, fileSystem: memoryFileSystem), FileSystem: () => memoryFileSystem, ProcessManager: () => FakeProcessManager.any(), }); diff --git a/packages/flutter_tools/test/general.shard/cache_test.dart b/packages/flutter_tools/test/general.shard/cache_test.dart index e12efc10773..26bb1c8b14b 100644 --- a/packages/flutter_tools/test/general.shard/cache_test.dart +++ b/packages/flutter_tools/test/general.shard/cache_test.dart @@ -14,29 +14,16 @@ 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/globals.dart' as globals; import 'package:meta/meta.dart'; import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; import '../src/common.dart'; import '../src/context.dart'; -import '../src/testbed.dart'; void main() { - group('$Cache.checkLockAcquired', () { - MockFileSystem mockFileSystem; - MemoryFileSystem memoryFileSystem; - MockFile mockFile; - MockRandomAccessFile mockRandomAccessFile; - + group('Cache.checkLockAcquired', () { setUp(() { - mockFileSystem = MockFileSystem(); - memoryFileSystem = MemoryFileSystem.test(); - mockFile = MockFile(); - mockRandomAccessFile = MockRandomAccessFile(); - when(mockFileSystem.path).thenReturn(memoryFileSystem.path); - Cache.enableLocking(); }); @@ -44,145 +31,146 @@ void main() { // Restore locking to prevent potential side-effects in // tests outside this group (this option is globally shared). Cache.enableLocking(); - Cache.releaseLock(); }); - test('should throw when locking is not acquired', () { - expect(Cache.checkLockAcquired, throwsStateError); + testWithoutContext('should throw when locking is not acquired', () { + final Cache cache = Cache.test(); + + expect(cache.checkLockAcquired, throwsStateError); }); - test('should not throw when locking is disabled', () { + testWithoutContext('should not throw when locking is disabled', () { + final Cache cache = Cache.test(); Cache.disableLocking(); - Cache.checkLockAcquired(); + + expect(cache.checkLockAcquired, returnsNormally); }); - testUsingContext('should not throw when lock is acquired', () async { - when(mockFileSystem.file(argThat(endsWith('lockfile')))).thenReturn(mockFile); - when(mockFile.openSync(mode: anyNamed('mode'))).thenReturn(mockRandomAccessFile); - await Cache.lock(); - Cache.checkLockAcquired(); - Cache.releaseLock(); - }, overrides: { - FileSystem: () => mockFileSystem, - ProcessManager: () => FakeProcessManager.any(), - }); + 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); - testUsingContext('throws tool exit when lockfile open fails', () async { - when(mockFileSystem.file(argThat(endsWith('lockfile')))).thenReturn(mockFile); - when(mockFile.openSync(mode: anyNamed('mode'))).thenThrow(const FileSystemException()); - expect(() async => await Cache.lock(), throwsToolExit()); - }, overrides: { - FileSystem: () => mockFileSystem, - ProcessManager: () => FakeProcessManager.any(), - }); + await cache.lock(); - testUsingContext('should not throw when FLUTTER_ALREADY_LOCKED is set', () async { - Cache.checkLockAcquired(); - }, overrides: { - Platform: () => FakePlatform()..environment = {'FLUTTER_ALREADY_LOCKED': 'true'}, + 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: { + 'FLUTTER_ALREADY_LOCKED': 'true', + })); + + expect(cache.checkLockAcquired, returnsNormally); }); }); group('Cache', () { - MockCache mockCache; - Cache cache; - MemoryFileSystem memoryFileSystem; - ProcessManager fakeProcessManager; + 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.'); - setUp(() { - fakeProcessManager = FakeProcessManager.any(); - mockCache = MockCache(); - cache = Cache.test( - fileSystem: memoryFileSystem, - processManager: fakeProcessManager, - ); - memoryFileSystem = MemoryFileSystem.test(); - }); - - testUsingContext('Continues on failed stamp file update', () async { - final Directory artifactDir = globals.fs.systemTempDirectory.createTempSync('flutter_cache_test_artifact.'); - final Directory downloadDir = globals.fs.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()); - expect(testLogger.errorText, contains('stamp write failed')); - }, overrides: { - Cache: () => mockCache, - FileSystem: () => memoryFileSystem, - ProcessManager: () => FakeProcessManager.any(), + await artifact.update(MockArtifactUpdater(), logger, fileSystem, MockOperatingSystemUtils()); + + expect(logger.errorText, contains('stamp write failed')); }); - testUsingContext('Gradle wrapper should not be up to date, if some cached artifact is not available', () { + 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(globals.fs.path.join('artifacts', 'gradle_wrapper')); - globals.fs.file(globals.fs.path.join(directory.path, 'gradle', 'wrapper', 'gradle-wrapper.jar')).createSync(recursive: true); + 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(), false); - }, overrides: { - Cache: () => cache, - FileSystem: () => memoryFileSystem, - ProcessManager: () => FakeProcessManager.any(), + expect(gradleWrapper.isUpToDateInner(fileSystem), false); }); - testUsingContext('Gradle wrapper should be up to date, only if all cached artifact are available', () { + 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(globals.fs.path.join('artifacts', 'gradle_wrapper')); - globals.fs.file(globals.fs.path.join(directory.path, 'gradle', 'wrapper', 'gradle-wrapper.jar')).createSync(recursive: true); - globals.fs.file(globals.fs.path.join(directory.path, 'gradlew')).createSync(recursive: true); - globals.fs.file(globals.fs.path.join(directory.path, 'gradlew.bat')).createSync(recursive: true); + 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(), true); - }, overrides: { - Cache: () => cache, - FileSystem: () => memoryFileSystem, - ProcessManager: () => FakeProcessManager.any(), + expect(gradleWrapper.isUpToDateInner(fileSystem), true); }); - testUsingContext('should not be up to date, if some cached artifact is not', () async { + testWithoutContext('should not be up to date, if some cached artifact is not', () async { final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact(); - when(artifact1.isUpToDate()).thenAnswer((Invocation _) => Future.value(true)); - when(artifact2.isUpToDate()).thenAnswer((Invocation _) => Future.value(false)); - final Cache cache = Cache(artifacts: [artifact1, artifact2]); + final FileSystem fileSystem = MemoryFileSystem.test(); + + when(artifact1.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future.value(true)); + when(artifact2.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future.value(false)); + final Cache cache = Cache.test( + fileSystem: fileSystem, + artifacts: [artifact1, artifact2], + processManager: FakeProcessManager.any(), + ); + expect(await cache.isUpToDate(), isFalse); - }, overrides: { - ProcessManager: () => FakeProcessManager.any(), - FileSystem: () => MemoryFileSystem.test(), }); - testUsingContext('should be up to date, if all cached artifacts are', () async { + testWithoutContext('should be up to date, if all cached artifacts are', () async { final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact(); - when(artifact1.isUpToDate()).thenAnswer((Invocation _) => Future.value(true)); - when(artifact2.isUpToDate()).thenAnswer((Invocation _) => Future.value(true)); - final Cache cache = Cache(artifacts: [artifact1, artifact2]); + final FileSystem fileSystem = MemoryFileSystem.test(); + + when(artifact1.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future.value(true)); + when(artifact2.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future.value(true)); + final Cache cache = Cache.test( + fileSystem: fileSystem, + artifacts: [artifact1, artifact2], + processManager: FakeProcessManager.any(), + ); + expect(await cache.isUpToDate(), isTrue); - }, overrides: { - ProcessManager: () => FakeProcessManager.any(), - FileSystem: () => MemoryFileSystem.test(), }); - testUsingContext('should update cached artifacts which are not up to date', () async { + testWithoutContext('should update cached artifacts which are not up to date', () async { final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact(); - when(artifact1.isUpToDate()).thenAnswer((Invocation _) => Future.value(true)); - when(artifact2.isUpToDate()).thenAnswer((Invocation _) => Future.value(false)); - final Cache cache = Cache(artifacts: [artifact1, artifact2]); + final FileSystem fileSystem = MemoryFileSystem.test(); + + when(artifact1.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future.value(true)); + when(artifact2.isUpToDate(fileSystem)).thenAnswer((Invocation _) => Future.value(false)); + final Cache cache = Cache.test( + fileSystem: fileSystem, + artifacts: [artifact1, artifact2], + processManager: FakeProcessManager.any(), + ); + await cache.updateAll({ null, }); - verifyNever(artifact1.update(any)); - verify(artifact2.update(any)); - }, overrides: { - ProcessManager: () => FakeProcessManager.any(), - FileSystem: () => MemoryFileSystem.test(), + verifyNever(artifact1.update(any, any, any, any)); + verify(artifact2.update(any, any, any, any)); }); - testUsingContext("getter dyLdLibEntry concatenates the output of each artifact's dyLdLibEntry getter", () async { + 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(); @@ -198,7 +186,10 @@ void main() { .thenReturn({ 'DYLD_LIBRARY_PATH': '', }); - final Cache cache = Cache(artifacts: [artifact1, artifact2, artifact3]); + final Cache cache = Cache.test( + artifacts: [artifact1, artifact2, artifact3], + processManager: FakeProcessManager.any(), + ); expect(cache.dyLdLibEntry.key, 'DYLD_LIBRARY_PATH'); expect( @@ -207,45 +198,47 @@ void main() { ); }); - testUsingContext('failed storage.googleapis.com download shows China warning', () async { + testWithoutContext('failed storage.googleapis.com download shows China warning', () async { final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact(); - when(artifact1.isUpToDate()).thenAnswer((Invocation _) => Future.value(false)); - when(artifact2.isUpToDate()).thenAnswer((Invocation _) => Future.value(false)); + when(artifact1.isUpToDate(any)).thenAnswer((Invocation _) => Future.value(false)); + when(artifact2.isUpToDate(any)).thenAnswer((Invocation _) => Future.value(false)); final MockInternetAddress address = MockInternetAddress(); when(address.host).thenReturn('storage.googleapis.com'); - when(artifact1.update(any)).thenThrow(SocketException( + when(artifact1.update(any, any, any, any)).thenThrow(SocketException( 'Connection reset by peer', address: address, )); - final Cache cache = Cache(artifacts: [artifact1, artifact2]); + final BufferLogger logger = BufferLogger.test(); + final Cache cache = Cache.test( + artifacts: [artifact1, artifact2], + processManager: FakeProcessManager.any(), + logger: logger, + ); try { await cache.updateAll({ null, }); fail('Mock thrown exception expected'); } on Exception { - verify(artifact1.update(any)); + verify(artifact1.update(any, any, any, any)); // Don't continue when retrieval fails. - verifyNever(artifact2.update(any)); + verifyNever(artifact2.update(any, any, any, any)); expect( - testLogger.errorText, + logger.errorText, contains('https://flutter.dev/community/china'), ); } - }, overrides: { - ProcessManager: () => FakeProcessManager.any(), - FileSystem: () => MemoryFileSystem.test(), }); - testUsingContext('Invalid URI for FLUTTER_STORAGE_BASE_URL throws ToolExit', () async { - final Cache cache = Cache(); + testWithoutContext('Invalid URI for FLUTTER_STORAGE_BASE_URL throws ToolExit', () async { + final Cache cache = Cache.test( + platform: FakePlatform(environment: { + 'FLUTTER_STORAGE_BASE_URL': ' http://foo', + }, + )); expect(() => cache.storageBaseUrl, throwsToolExit()); - }, overrides: { - Platform: () => FakePlatform(environment: { - 'FLUTTER_STORAGE_BASE_URL': ' http://foo', - }), }); }); @@ -255,176 +248,96 @@ void main() { expect(flattenNameSubdirs(Uri.parse('https://www.flutter.dev'), MemoryFileSystem.test()), 'www.flutter.dev'); }); - group('EngineCachedArtifact', () { - FakePlatform fakePlatform; - MemoryFileSystem fileSystem; - MockCache mockCache; - MockOperatingSystemUtils mockOperatingSystemUtils; + 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.'); - setUp(() { - fakePlatform = FakePlatform(environment: const {}, operatingSystem: 'linux'); - mockCache = MockCache(); - mockOperatingSystemUtils = MockOperatingSystemUtils(); - fileSystem = MemoryFileSystem.test(); - }); + when(cache.getArtifactDirectory(any)).thenReturn(artifactDir); + when(cache.getDownloadDir()).thenReturn(downloadDir); + artifactDir.childDirectory('bin_dir').createSync(); + artifactDir.childFile('unused_url_path').createSync(); - testUsingContext('makes binary dirs readable and executable by all', () async { - 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); - artifactDir.childDirectory('bin_dir').createSync(); - artifactDir.childFile('unused_url_path').createSync(); + final FakeCachedArtifact artifact = FakeCachedArtifact( + cache: cache, + binaryDirs: >[ + ['bin_dir', 'unused_url_path'], + ], + requiredArtifacts: DevelopmentArtifact.universal, + ); + await artifact.updateInner(MockArtifactUpdater(), fileSystem, operatingSystemUtils); + final Directory dir = fileSystem.systemTempDirectory + .listSync(recursive: true) + .whereType() + .singleWhere((Directory directory) => directory.basename == 'bin_dir', orElse: () => null); - final FakeCachedArtifact artifact = FakeCachedArtifact( - cache: mockCache, - binaryDirs: >[ - ['bin_dir', 'unused_url_path'], - ], - requiredArtifacts: DevelopmentArtifact.universal, - ); - await artifact.updateInner(MockArtifactUpdater()); - final Directory dir = fileSystem.systemTempDirectory - .listSync(recursive: true) - .whereType() - .singleWhere((Directory directory) => directory.basename == 'bin_dir', orElse: () => null); - expect(dir, isNotNull); - expect(dir.path, artifactDir.childDirectory('bin_dir').path); - verify(mockOperatingSystemUtils.chmod(argThat(hasPath(dir.path)), 'a+r,a+x')); - }, overrides: { - Cache: () => mockCache, - FileSystem: () => fileSystem, - ProcessManager: () => FakeProcessManager.any(), - OperatingSystemUtils: () => mockOperatingSystemUtils, - Platform: () => fakePlatform, - }); + expect(dir, isNotNull); + expect(dir.path, artifactDir.childDirectory('bin_dir').path); + verify(operatingSystemUtils.chmod(argThat(hasPath(dir.path)), 'a+r,a+x')); }); - group('AndroidMavenArtifacts', () { - MemoryFileSystem memoryFileSystem; - MockProcessManager processManager; - Cache cache; + 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(); - setUp(() { - memoryFileSystem = MemoryFileSystem.test(); - processManager = MockProcessManager(); - cache = Cache.test( - fileSystem: memoryFileSystem, - processManager: FakeProcessManager.any(), - ); - }); + expect(iosUsbArtifacts.isUpToDateInner(fileSystem), true); - test('development artifact', () async { - final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(cache); - expect(mavenArtifacts.developmentArtifact, DevelopmentArtifact.androidMaven); - }); + ideviceScreenshotFile.deleteSync(); - testUsingContext('update', () async { - final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(cache); - expect(await mavenArtifacts.isUpToDate(), isFalse); - - final Directory gradleWrapperDir = cache.getArtifactDirectory('gradle_wrapper')..createSync(recursive: true); - gradleWrapperDir.childFile('gradlew').writeAsStringSync('irrelevant'); - gradleWrapperDir.childFile('gradlew.bat').writeAsStringSync('irrelevant'); - - when(globals.processManager.run(any, environment: captureAnyNamed('environment'))) - .thenAnswer((Invocation invocation) { - final List args = invocation.positionalArguments[0] as List; - expect(args.length, 6); - expect(args[1], '-b'); - expect(args[2].endsWith('resolve_dependencies.gradle'), isTrue); - expect(args[5], 'resolveDependencies'); - expect(invocation.namedArguments[#environment], gradleEnvironment); - return Future.value(ProcessResult(0, 0, '', '')); - }); - - await mavenArtifacts.update(MockArtifactUpdater()); - - expect(await mavenArtifacts.isUpToDate(), isFalse); - }, overrides: { - Cache: () => cache, - FileSystem: () => memoryFileSystem, - ProcessManager: () => processManager, - }); + expect(iosUsbArtifacts.isUpToDateInner(fileSystem), false); }); - group('macOS artifacts', () { - Cache cache; + 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(); - setUp(() { - cache = Cache.test( - processManager: FakeProcessManager.any(), - ); - }); + expect(iosUsbArtifacts.isUpToDateInner(fileSystem), true); - testUsingContext('verifies executables for libimobiledevice in isUpToDateInner', () async { - final IosUsbArtifacts iosUsbArtifacts = IosUsbArtifacts('libimobiledevice', cache); - iosUsbArtifacts.location.createSync(); - final File ideviceScreenshotFile = iosUsbArtifacts.location.childFile('idevicescreenshot') - ..createSync(); - iosUsbArtifacts.location.childFile('idevicesyslog') - .createSync(); + iproxy.deleteSync(); - expect(iosUsbArtifacts.isUpToDateInner(), true); - - ideviceScreenshotFile.deleteSync(); - - expect(iosUsbArtifacts.isUpToDateInner(), false); - }, overrides: { - Cache: () => cache, - FileSystem: () => MemoryFileSystem.test(), - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('verifies iproxy for usbmuxd in isUpToDateInner', () async { - final IosUsbArtifacts iosUsbArtifacts = IosUsbArtifacts('usbmuxd', cache); - iosUsbArtifacts.location.createSync(); - final File iproxy = iosUsbArtifacts.location.childFile('iproxy') - ..createSync(); - - expect(iosUsbArtifacts.isUpToDateInner(), true); - - iproxy.deleteSync(); - - expect(iosUsbArtifacts.isUpToDateInner(), false); - }, overrides: { - Cache: () => cache, - FileSystem: () => MemoryFileSystem.test(), - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('Does not verify executables for openssl in isUpToDateInner', () async { - final IosUsbArtifacts iosUsbArtifacts = IosUsbArtifacts('openssl', cache); - iosUsbArtifacts.location.createSync(); - - expect(iosUsbArtifacts.isUpToDateInner(), true); - }, overrides: { - Cache: () => cache, - FileSystem: () => MemoryFileSystem.test(), - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('use unsigned when specified', () async { - cache.useUnsignedMacBinaries = true; - - final IosUsbArtifacts iosUsbArtifacts = IosUsbArtifacts('name', cache); - expect(iosUsbArtifacts.archiveUri.toString(), contains('/unsigned/')); - }, overrides: { - Cache: () => cache, - }); - - testUsingContext('not use unsigned when not specified', () async { - cache.useUnsignedMacBinaries = false; - - final IosUsbArtifacts iosUsbArtifacts = IosUsbArtifacts('name', cache); - expect(iosUsbArtifacts.archiveUri.toString(), isNot(contains('/unsigned/'))); - }, overrides: { - Cache: () => cache, - }); + expect(iosUsbArtifacts.isUpToDateInner(fileSystem), false); }); - testWithoutContext('Downloads Flutter runner debug symbols', () async { + 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(); @@ -435,7 +348,7 @@ void main() { ); when(mockPackageResolver.resolveUrl(any, any)).thenReturn(''); - await flutterRunnerDebugSymbols.updateInner(MockArtifactUpdater()); + await flutterRunnerDebugSymbols.updateInner(MockArtifactUpdater(), fileSystem, MockOperatingSystemUtils()); verifyInOrder([ mockPackageResolver.resolveUrl('fuchsia-debug-symbols-x64', any), @@ -443,70 +356,64 @@ void main() { ]); }); - testUsingContext('FontSubset in univeral artifacts', () { + testWithoutContext('FontSubset in universal artifacts', () { final Cache cache = Cache.test(); - final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache); + final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux')); + expect(artifacts.developmentArtifact, DevelopmentArtifact.universal); }); - testUsingContext('FontSubset artifacts on linux', () { + testWithoutContext('FontSubset artifacts on linux', () { final Cache cache = Cache.test(); - final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache); + final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux')); cache.includeAllPlatforms = false; + expect(artifacts.getBinaryDirs(), >[['linux-x64', 'linux-x64/font-subset.zip']]); - }, overrides: { - Platform: () => FakePlatform(operatingSystem: 'linux'), }); - testUsingContext('FontSubset artifacts on windows', () { + testWithoutContext('FontSubset artifacts on windows', () { final Cache cache = Cache.test(); - final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache); + final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'windows')); cache.includeAllPlatforms = false; + expect(artifacts.getBinaryDirs(), >[['windows-x64', 'windows-x64/font-subset.zip']]); - }, overrides: { - Platform: () => FakePlatform(operatingSystem: 'windows'), }); - testUsingContext('FontSubset artifacts on macos', () { + testWithoutContext('FontSubset artifacts on macos', () { final Cache cache = Cache.test(); - final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache); + final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'macos')); cache.includeAllPlatforms = false; + expect(artifacts.getBinaryDirs(), >[['darwin-x64', 'darwin-x64/font-subset.zip']]); - }, overrides: { - Platform: () => FakePlatform(operatingSystem: 'macos'), }); - testUsingContext('FontSubset artifacts on fuchsia', () { + testWithoutContext('FontSubset artifacts on fuchsia', () { final Cache cache = Cache.test(); - final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache); + final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'fuchsia')); cache.includeAllPlatforms = false; - expect(artifacts.getBinaryDirs, throwsToolExit(message: 'Unsupported operating system: ${globals.platform.operatingSystem}')); - }, overrides: { - Platform: () => FakePlatform(operatingSystem: 'fuchsia'), + + expect(artifacts.getBinaryDirs, throwsToolExit(message: 'Unsupported operating system: fuchsia')); }); - testUsingContext('FontSubset artifacts for all platforms', () { + testWithoutContext('FontSubset artifacts for all platforms', () { final Cache cache = Cache.test(); - final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache); + final FontSubsetArtifacts artifacts = FontSubsetArtifacts(cache, platform: FakePlatform(operatingSystem: 'fuchsia')); cache.includeAllPlatforms = true; + expect(artifacts.getBinaryDirs(), >[ - ['darwin-x64', 'darwin-x64/font-subset.zip'], - ['linux-x64', 'linux-x64/font-subset.zip'], - ['windows-x64', 'windows-x64/font-subset.zip'], + ['darwin-x64', 'darwin-x64/font-subset.zip'], + ['linux-x64', 'linux-x64/font-subset.zip'], + ['windows-x64', 'windows-x64/font-subset.zip'], ]); - }, overrides: { - Platform: () => FakePlatform(operatingSystem: 'fuchsia'), }); - testUsingContext('macOS desktop artifacts ignore filtering when requested', () { + testWithoutContext('macOS desktop artifacts ignore filtering when requested', () { final Cache cache = Cache.test(); - final MacOSEngineArtifacts artifacts = MacOSEngineArtifacts(cache); + final MacOSEngineArtifacts artifacts = MacOSEngineArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux')); cache.includeAllPlatforms = false; cache.platformOverrideArtifacts = {'macos'}; expect(artifacts.getBinaryDirs(), isNotEmpty); - }, overrides: { - Platform: () => FakePlatform(operatingSystem: 'linux'), }); testWithoutContext('Windows desktop artifacts ignore filtering when requested', () { @@ -652,7 +559,7 @@ void main() { }); webStuff.childFile('bar').createSync(recursive: true); - webSdk.updateInner(artifactUpdater); + webSdk.updateInner(artifactUpdater, fileSystem, MockOperatingSystemUtils()); expect(webStuff.childFile('foo'), exists); expect(webStuff.childFile('bar'), isNot(exists)); @@ -690,12 +597,11 @@ void main() { final MemoryFileSystem fileSystem = MemoryFileSystem.test(); final PubDependencies pubDependencies = PubDependencies( flutterRoot: () => '', - fileSystem: fileSystem, logger: logger, pub: () => MockPub(), ); - expect(await pubDependencies.isUpToDate(), false); // no package config + expect(await pubDependencies.isUpToDate(fileSystem), false); // no package config fileSystem.file('packages/flutter_tools/.packages') ..createSync(recursive: true) @@ -719,12 +625,12 @@ void main() { } '''); - expect(await pubDependencies.isUpToDate(), false); // dependencies are missing. + 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(), true); + expect(await pubDependencies.isUpToDate(fileSystem), true); }); testWithoutContext('PubDependencies updates via pub get', () async { @@ -733,18 +639,65 @@ void main() { final MockPub pub = MockPub(); final PubDependencies pubDependencies = PubDependencies( flutterRoot: () => '', - fileSystem: fileSystem, logger: logger, pub: () => pub, ); - await pubDependencies.update(MockArtifactUpdater()); + await pubDependencies.update(MockArtifactUpdater(), logger, fileSystem, MockOperatingSystemUtils()); verify(pub.get( context: PubContext.pubGet, directory: 'packages/flutter_tools', )).called(1); }); + + group('AndroidMavenArtifacts', () { + MemoryFileSystem memoryFileSystem; + MockProcessManager processManager; + Cache cache; + + setUp(() { + memoryFileSystem = MemoryFileSystem.test(); + processManager = MockProcessManager(); + cache = Cache.test( + fileSystem: memoryFileSystem, + processManager: FakeProcessManager.any(), + ); + }); + + testWithoutContext('development artifact', () async { + final AndroidMavenArtifacts mavenArtifacts = AndroidMavenArtifacts(cache, platform: FakePlatform(operatingSystem: 'linux')); + expect(mavenArtifacts.developmentArtifact, DevelopmentArtifact.androidMaven); + }); + + testUsingContext('update', () async { + 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'); + + when(processManager.run(any, environment: captureAnyNamed('environment'))) + .thenAnswer((Invocation invocation) { + final List args = invocation.positionalArguments[0] as List; + expect(args.length, 6); + expect(args[1], '-b'); + expect(args[2].endsWith('resolve_dependencies.gradle'), isTrue); + expect(args[5], 'resolveDependencies'); + expect(invocation.namedArguments[#environment], gradleEnvironment); + return Future.value(ProcessResult(0, 0, '', '')); + }); + + await mavenArtifacts.update(MockArtifactUpdater(), BufferLogger.test(), memoryFileSystem, MockOperatingSystemUtils()); + + expect(await mavenArtifacts.isUpToDate(memoryFileSystem), isFalse); + }, overrides: { + Cache: () => cache, + FileSystem: () => memoryFileSystem, + ProcessManager: () => processManager, + }); + }); } class FakeCachedArtifact extends EngineCachedArtifact { @@ -779,9 +732,7 @@ class FakeSimpleArtifact extends CachedArtifact { ); @override - Future updateInner(ArtifactUpdater artifactUpdater) async { - // nop. - } + Future updateInner(ArtifactUpdater artifactUpdater, FileSystem fileSystem, OperatingSystemUtils operatingSystemUtils) async { } } class FakeDownloadedArtifact extends CachedArtifact { @@ -794,7 +745,7 @@ class FakeDownloadedArtifact extends CachedArtifact { final File downloadedFile; @override - Future updateInner(ArtifactUpdater artifactUpdater) async {} + Future updateInner(ArtifactUpdater artifactUpdater, FileSystem fileSystem, OperatingSystemUtils operatingSystemUtils) async { } } class MockArtifactUpdater extends Mock implements ArtifactUpdater {} diff --git a/packages/flutter_tools/test/general.shard/project_test.dart b/packages/flutter_tools/test/general.shard/project_test.dart index 08971875f57..e74e8ab6190 100644 --- a/packages/flutter_tools/test/general.shard/project_test.dart +++ b/packages/flutter_tools/test/general.shard/project_test.dart @@ -771,7 +771,12 @@ void _testInMemory(String description, Future testMethod()) { testFileSystem.file('.packages').writeAsStringSync('\n'); // Transfer needed parts of the Flutter installation folder // to the in-memory file system used during testing. - transfer(Cache().getArtifactDirectory('gradle_wrapper'), testFileSystem); + transfer(Cache( + fileSystem: globals.fs, + logger: globals.logger, + osUtils: globals.os, + platform: globals.platform, + ).getArtifactDirectory('gradle_wrapper'), testFileSystem); transfer(globals.fs.directory(Cache.flutterRoot) .childDirectory('packages') .childDirectory('flutter_tools') @@ -801,7 +806,12 @@ void _testInMemory(String description, Future testMethod()) { overrides: { FileSystem: () => testFileSystem, ProcessManager: () => FakeProcessManager.any(), - Cache: () => Cache(), + Cache: () => Cache( + logger: globals.logger, + fileSystem: globals.fs, + osUtils: globals.os, + platform: globals.platform, + ), FlutterProjectFactory: () => flutterProjectFactory, }, ); diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart index 8bd89227b5a..0e9dc16584f 100644 --- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart @@ -57,7 +57,7 @@ void main() { testUsingContext('honors shouldUpdateCache false', () async { final DummyFlutterCommand flutterCommand = DummyFlutterCommand(shouldUpdateCache: false); await flutterCommand.run(); - verifyZeroInteractions(cache); + verifyNever(cache.updateAll(any)); expect(flutterCommand.deprecated, isFalse); expect(flutterCommand.hidden, isFalse); }, @@ -352,7 +352,7 @@ void main() { final Completer checkLockCompleter = Completer(); final DummyFlutterCommand flutterCommand = DummyFlutterCommand(commandFunction: () async { - await Cache.lock(); + await globals.cache.lock(); checkLockCompleter.complete(); final Completer c = Completer(); await c.future; @@ -362,13 +362,13 @@ void main() { unawaited(flutterCommand.run()); await checkLockCompleter.future; - Cache.checkLockAcquired(); + globals.cache.checkLockAcquired(); signalController.add(mockSignal); await completer.future; - await Cache.lock(); - Cache.releaseLock(); + await globals.cache.lock(); + globals.cache.releaseLock(); }, overrides: { ProcessInfo: () => mockProcessInfo, Signals: () => FakeSignals( diff --git a/packages/flutter_tools/test/src/fakes.dart b/packages/flutter_tools/test/src/fakes.dart index e580ecf5f11..f6dcacacf63 100644 --- a/packages/flutter_tools/test/src/fakes.dart +++ b/packages/flutter_tools/test/src/fakes.dart @@ -4,6 +4,9 @@ import 'dart:async'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/device.dart'; @@ -51,12 +54,12 @@ class FakeDyldEnvironmentArtifact extends ArtifactSet { }; @override - Future isUpToDate() => Future.value(true); + Future isUpToDate(FileSystem fileSystem) => Future.value(true); @override String get name => 'fake'; @override - Future update(ArtifactUpdater artifactUpdater) async { + Future update(ArtifactUpdater artifactUpdater, Logger logger, FileSystem fileSystem, OperatingSystemUtils operatingSystemUtils) async { } } diff --git a/packages/flutter_tools/test/src/testbed.dart b/packages/flutter_tools/test/src/testbed.dart index 4f3361bac05..f0f8a4cf55c 100644 --- a/packages/flutter_tools/test/src/testbed.dart +++ b/packages/flutter_tools/test/src/testbed.dart @@ -897,5 +897,14 @@ class FakeCache implements Cache { } @override - void clearStampFiles() {} + void clearStampFiles() { } + + @override + void checkLockAcquired() { } + + @override + Future lock() async { } + + @override + void releaseLock() { } }