Add gradle wrapper to project template (#10928)
diff --git a/bin/internal/gradle_wrapper.version b/bin/internal/gradle_wrapper.version
new file mode 100644
index 0000000..97e3b0d
--- /dev/null
+++ b/bin/internal/gradle_wrapper.version
@@ -0,0 +1 @@
+0b5c1398d1d04ac245a310de98825cb7b3278e2a
diff --git a/packages/flutter_tools/lib/src/base/file_system.dart b/packages/flutter_tools/lib/src/base/file_system.dart
index 6d5f3bc..6baeecf 100644
--- a/packages/flutter_tools/lib/src/base/file_system.dart
+++ b/packages/flutter_tools/lib/src/base/file_system.dart
@@ -71,10 +71,11 @@
}
}
-/// Recursively copies `srcDir` to `destDir`.
+/// Recursively copies `srcDir` to `destDir`, invoking [onFileCopied] if
+/// specified for each source/destination file pair.
///
/// Creates `destDir` if needed.
-void copyDirectorySync(Directory srcDir, Directory destDir) {
+void copyDirectorySync(Directory srcDir, Directory destDir, [void onFileCopied(File srcFile, File destFile)]) {
if (!srcDir.existsSync())
throw new Exception('Source directory "${srcDir.path}" does not exist, nothing to copy');
@@ -86,6 +87,7 @@
if (entity is File) {
final File newFile = destDir.fileSystem.file(newPath);
newFile.writeAsBytesSync(entity.readAsBytesSync());
+ onFileCopied?.call(entity, newFile);
} else if (entity is Directory) {
copyDirectorySync(
entity, destDir.fileSystem.directory(newPath));
diff --git a/packages/flutter_tools/lib/src/base/os.dart b/packages/flutter_tools/lib/src/base/os.dart
index 1cb5696..8dfcbb4 100644
--- a/packages/flutter_tools/lib/src/base/os.dart
+++ b/packages/flutter_tools/lib/src/base/os.dart
@@ -47,6 +47,8 @@
void unzip(File file, Directory targetDirectory);
+ void unpack(File gzippedTarFile, Directory targetDirectory);
+
/// Returns a pretty name string for the current operating system.
///
/// If available, the detailed version of the OS is included.
@@ -97,6 +99,12 @@
runSync(<String>['unzip', '-o', '-q', file.path, '-d', targetDirectory.path]);
}
+ // tar -xzf tarball -C dest
+ @override
+ void unpack(File gzippedTarFile, Directory targetDirectory) {
+ runSync(<String>['tar', '-xzf', gzippedTarFile.path, '-C', targetDirectory.path]);
+ }
+
@override
File makePipe(String path) {
runSync(<String>['mkfifo', path]);
@@ -167,7 +175,18 @@
@override
void unzip(File file, Directory targetDirectory) {
final Archive archive = new ZipDecoder().decodeBytes(file.readAsBytesSync());
+ _unpackArchive(archive, targetDirectory);
+ }
+ @override
+ void unpack(File gzippedTarFile, Directory targetDirectory) {
+ final Archive archive = new TarDecoder().decodeBytes(
+ new GZipDecoder().decodeBytes(gzippedTarFile.readAsBytesSync()),
+ );
+ _unpackArchive(archive, targetDirectory);
+ }
+
+ void _unpackArchive(Archive archive, Directory targetDirectory) {
for (ArchiveFile archiveFile in archive.files) {
// The archive package doesn't correctly set isFile.
if (!archiveFile.isFile || archiveFile.name.endsWith('/'))
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index 5d10d2a..007e7a5 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -17,9 +17,19 @@
/// A wrapper around the `bin/cache/` directory.
class Cache {
/// [rootOverride] is configurable for testing.
- Cache({ Directory rootOverride }) : _rootOverride = rootOverride;
+ /// [artifacts] is configurable for testing.
+ Cache({ Directory rootOverride, List<CachedArtifact> artifacts }) : _rootOverride = rootOverride {
+ if (artifacts == null) {
+ _artifacts.add(new MaterialFonts(this));
+ _artifacts.add(new FlutterEngine(this));
+ _artifacts.add(new GradleWrapper(this));
+ } else {
+ _artifacts.addAll(artifacts);
+ }
+ }
final Directory _rootOverride;
+ final List<CachedArtifact> _artifacts = <CachedArtifact>[];
// Initialized by FlutterCommandRunner on startup.
static String flutterRoot;
@@ -155,16 +165,9 @@
return fs.file(fs.path.join(getRoot().path, '$artifactName.stamp'));
}
- bool isUpToDate() {
- final MaterialFonts materialFonts = new MaterialFonts(cache);
- final FlutterEngine engine = new FlutterEngine(cache);
+ bool isUpToDate() => _artifacts.every((CachedArtifact artifact) => artifact.isUpToDate());
- return materialFonts.isUpToDate() && engine.isUpToDate();
- }
-
- Future<String> getThirdPartyFile(String urlStr, String serviceName, {
- bool unzip: false
- }) async {
+ Future<String> getThirdPartyFile(String urlStr, String serviceName) async {
final Uri url = Uri.parse(urlStr);
final Directory thirdPartyDir = getArtifactDirectory('third_party');
@@ -175,7 +178,7 @@
final File cachedFile = fs.file(fs.path.join(serviceDir.path, url.pathSegments.last));
if (!cachedFile.existsSync()) {
try {
- await _downloadFileToCache(url, cachedFile, unzip);
+ await _downloadFile(url, cachedFile);
} catch (e) {
printError('Failed to fetch third-party artifact $url: $e');
rethrow;
@@ -188,77 +191,65 @@
Future<Null> updateAll() async {
if (!_lockEnabled)
return null;
- final MaterialFonts materialFonts = new MaterialFonts(cache);
- if (!materialFonts.isUpToDate())
- await materialFonts.download();
-
- final FlutterEngine engine = new FlutterEngine(cache);
- if (!engine.isUpToDate())
- await engine.download();
- }
-
- /// Download a file from the given url and write it to the cache.
- /// If [unzip] is true, treat the url as a zip file, and unzip it to the
- /// directory given.
- static Future<Null> _downloadFileToCache(Uri url, FileSystemEntity location, bool unzip) async {
- if (!location.parent.existsSync())
- location.parent.createSync(recursive: true);
-
- final List<int> fileBytes = await fetchUrl(url);
- if (unzip) {
- if (location is Directory && !location.existsSync())
- location.createSync(recursive: true);
-
- final File tempFile = fs.file(fs.path.join(fs.systemTempDirectory.path, '${url.toString().hashCode}.zip'));
- tempFile.writeAsBytesSync(fileBytes, flush: true);
- os.unzip(tempFile, location);
- tempFile.deleteSync();
- } else {
- final File file = location;
- file.writeAsBytesSync(fileBytes, flush: true);
+ for (CachedArtifact artifact in _artifacts) {
+ if (!artifact.isUpToDate())
+ await artifact.update();
}
}
}
-class MaterialFonts {
- MaterialFonts(this.cache);
+/// An artifact managed by the cache.
+abstract class CachedArtifact {
+ CachedArtifact(this.name, this.cache);
- static const String kName = 'material_fonts';
-
+ final String name;
final Cache cache;
+ Directory get location => cache.getArtifactDirectory(name);
+ String get version => cache.getVersionFor(name);
+
bool isUpToDate() {
- if (!cache.getArtifactDirectory(kName).existsSync())
+ if (!location.existsSync())
return false;
- return cache.getVersionFor(kName) == cache.getStampFor(kName);
+ if (version != cache.getStampFor(name))
+ return false;
+ return isUpToDateInner();
}
- Future<Null> download() {
+ Future<Null> update() async {
+ if (location.existsSync())
+ location.deleteSync(recursive: true);
+ location.createSync(recursive: true);
+ return updateInner().then<Null>((_) {
+ cache.setStampFor(name, version);
+ });
+ }
+
+ /// Hook method for extra checks for being up-to-date.
+ bool isUpToDateInner() => true;
+
+ /// Template method to perform artifact update.
+ Future<Null> updateInner();
+}
+
+/// A cached artifact containing fonts used for Material Design.
+class MaterialFonts extends CachedArtifact {
+ MaterialFonts(Cache cache): super('material_fonts', cache);
+
+ @override
+ Future<Null> updateInner() {
final Status status = logger.startProgress('Downloading Material fonts...', expectSlowOperation: true);
-
- final Directory fontsDir = cache.getArtifactDirectory(kName);
- if (fontsDir.existsSync())
- fontsDir.deleteSync(recursive: true);
-
- return Cache._downloadFileToCache(
- Uri.parse(cache.getVersionFor(kName)), fontsDir, true
- ).then<Null>((Null value) {
- cache.setStampFor(kName, cache.getVersionFor(kName));
+ return _downloadZipArchive(Uri.parse(version), location).then<Null>((_) {
status.stop();
}).whenComplete(status.cancel);
}
}
-class FlutterEngine {
+/// A cached artifact containing the Flutter engine binaries.
+class FlutterEngine extends CachedArtifact {
+ FlutterEngine(Cache cache): super('engine', cache);
- FlutterEngine(this.cache);
-
- static const String kName = 'engine';
- static const String kSkyEngine = 'sky_engine';
-
- final Cache cache;
-
- List<String> _getPackageDirs() => const <String>[kSkyEngine];
+ List<String> _getPackageDirs() => const <String>['sky_engine'];
// Return a list of (cache directory path, download URL path) tuples.
List<List<String>> _getBinaryDirs() {
@@ -320,7 +311,8 @@
<String>['ios-release', 'ios-release/artifacts.zip'],
];
- bool isUpToDate() {
+ @override
+ bool isUpToDateInner() {
final Directory pkgDir = cache.getCacheDir('pkg');
for (String pkgName in _getPackageDirs()) {
final String pkgPath = fs.path.join(pkgDir.path, pkgName);
@@ -328,19 +320,17 @@
return false;
}
- final Directory engineDir = cache.getArtifactDirectory(kName);
for (List<String> toolsDir in _getBinaryDirs()) {
- final Directory dir = fs.directory(fs.path.join(engineDir.path, toolsDir[0]));
+ final Directory dir = fs.directory(fs.path.join(location.path, toolsDir[0]));
if (!dir.existsSync())
return false;
}
-
- return cache.getVersionFor(kName) == cache.getStampFor(kName);
+ return true;
}
- Future<Null> download() async {
- final String engineVersion = cache.getVersionFor(kName);
- final String url = 'https://storage.googleapis.com/flutter_infra/flutter/$engineVersion/';
+ @override
+ Future<Null> updateInner() async {
+ final String url = 'https://storage.googleapis.com/flutter_infra/flutter/$version/';
final Directory pkgDir = cache.getCacheDir('pkg');
for (String pkgName in _getPackageDirs()) {
@@ -351,14 +341,10 @@
await _downloadItem('Downloading package $pkgName...', url + pkgName + '.zip', pkgDir);
}
- final Directory engineDir = cache.getArtifactDirectory(kName);
- if (engineDir.existsSync())
- engineDir.deleteSync(recursive: true);
-
for (List<String> toolsDir in _getBinaryDirs()) {
final String cacheDir = toolsDir[0];
final String urlPath = toolsDir[1];
- final Directory dir = fs.directory(fs.path.join(engineDir.path, cacheDir));
+ final Directory dir = fs.directory(fs.path.join(location.path, cacheDir));
await _downloadItem('Downloading $cacheDir tools...', url + urlPath, dir);
_makeFilesExecutable(dir);
@@ -370,8 +356,6 @@
os.unzip(frameworkZip, framework);
}
}
-
- cache.setStampFor(kName, cache.getVersionFor(kName));
}
void _makeFilesExecutable(Directory dir) {
@@ -386,8 +370,68 @@
Future<Null> _downloadItem(String message, String url, Directory dest) {
final Status status = logger.startProgress(message, expectSlowOperation: true);
- return Cache._downloadFileToCache(Uri.parse(url), dest, true).then<Null>((Null value) {
+ return _downloadZipArchive(Uri.parse(url), dest).then<Null>((_) {
status.stop();
}).whenComplete(status.cancel);
}
}
+
+/// A cached artifact containing Gradle Wrapper scripts and binaries.
+class GradleWrapper extends CachedArtifact {
+ GradleWrapper(Cache cache): super('gradle_wrapper', cache);
+
+ @override
+ Future<Null> updateInner() async {
+ final Status status = logger.startProgress('Downloading Gradle Wrapper...', expectSlowOperation: true);
+
+ final String url = 'https://android.googlesource.com'
+ '/platform/tools/base/+archive/$version/templates/gradle/wrapper.tgz';
+ await _downloadZippedTarball(Uri.parse(url), location).then<Null>((_) {
+ // Delete property file, allowing templates to provide it.
+ fs.file(fs.path.join(location.path, 'gradle', 'wrapper', 'gradle-wrapper.properties')).deleteSync();
+ status.stop();
+ }).whenComplete(status.cancel);
+ }
+}
+
+/// Download a file from the given [url] and write it to [location].
+Future<Null> _downloadFile(Uri url, File location) async {
+ _ensureExists(location.parent);
+ final List<int> fileBytes = await fetchUrl(url);
+ location.writeAsBytesSync(fileBytes, flush: true);
+}
+
+/// Download a zip archive from the given [url] and unzip it to [location].
+Future<Null> _downloadZipArchive(Uri url, Directory location) {
+ return _withTemporaryFile('download.zip', (File tempFile) async {
+ await _downloadFile(url, tempFile);
+ _ensureExists(location);
+ os.unzip(tempFile, location);
+ });
+}
+
+/// Download a gzipped tarball from the given [url] and unpack it to [location].
+Future<Null> _downloadZippedTarball(Uri url, Directory location) {
+ return _withTemporaryFile('download.tgz', (File tempFile) async {
+ await _downloadFile(url, tempFile);
+ _ensureExists(location);
+ os.unpack(tempFile, location);
+ });
+}
+
+/// Create a file with the given name in a new temporary directory, invoke
+/// [onTemporaryFile] with the file as argument, then delete the temporary
+/// directory.
+Future<Null> _withTemporaryFile(String name, Future<Null> onTemporaryFile(File file)) async {
+ final Directory tempDir = fs.systemTempDirectory.createTempSync();
+ final File tempFile = fs.file(fs.path.join(tempDir.path, name));
+ await onTemporaryFile(tempFile).whenComplete(() {
+ tempDir.delete(recursive: true);
+ });
+}
+
+/// Create the given [directory] and parents, as necessary.
+void _ensureExists(Directory directory) {
+ if (!directory.existsSync())
+ directory.createSync(recursive: true);
+}
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index cb60d49..0b6f67c 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -11,6 +11,7 @@
import '../android/gradle.dart' as gradle;
import '../base/common.dart';
import '../base/file_system.dart';
+import '../base/os.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../cache.dart';
@@ -167,6 +168,10 @@
}
generatedCount += _renderTemplate('create', appPath, templateContext);
+ generatedCount += _injectGradleWrapper(appPath);
+ if (appPath != dirPath) {
+ generatedCount += _injectGradleWrapper(dirPath);
+ }
if (argResults['with-driver-test']) {
final String testPath = fs.path.join(appPath, 'test_driver');
generatedCount += _renderTemplate('driver', testPath, templateContext);
@@ -272,6 +277,22 @@
final Template template = new Template.fromName(templateName);
return template.render(fs.directory(dirPath), context, overwriteExisting: false);
}
+
+ int _injectGradleWrapper(String projectDir) {
+ int filesCreated = 0;
+ copyDirectorySync(
+ cache.getArtifactDirectory('gradle_wrapper'),
+ fs.directory(fs.path.join(projectDir, 'android')),
+ (File sourceFile, File destinationFile) {
+ filesCreated++;
+ final String modes = sourceFile.statSync().modeString();
+ if (modes != null && modes.contains('x')) {
+ os.makeExecutable(destinationFile);
+ }
+ },
+ );
+ return filesCreated;
+ }
}
String _createAndroidIdentifier(String organization, String name) {
diff --git a/packages/flutter_tools/lib/src/services.dart b/packages/flutter_tools/lib/src/services.dart
index 00bd107..1aaf0d2 100644
--- a/packages/flutter_tools/lib/src/services.dart
+++ b/packages/flutter_tools/lib/src/services.dart
@@ -69,20 +69,18 @@
if (jars != null && serviceConfig['jars'] is Iterable) {
for (String jar in serviceConfig['jars'])
- jars.add(fs.file(await getServiceFromUrl(jar, serviceRoot, service, unzip: false)));
+ jars.add(fs.file(await getServiceFromUrl(jar, serviceRoot, service)));
}
}
}
-Future<String> getServiceFromUrl(
- String url, String rootDir, String serviceName, { bool unzip: false }
-) async {
+Future<String> getServiceFromUrl(String url, String rootDir, String serviceName) async {
if (url.startsWith("android-sdk:") && androidSdk != null) {
// It's something shipped in the standard android SDK.
return url.replaceAll('android-sdk:', '${androidSdk.directory}/');
} else if (url.startsWith("http")) {
// It's a regular file to download.
- return await cache.getThirdPartyFile(url, serviceName, unzip: unzip);
+ return await cache.getThirdPartyFile(url, serviceName);
} else {
// Assume url is a path relative to the service's root dir.
return fs.path.join(rootDir, url);
diff --git a/packages/flutter_tools/templates/create/android-java.tmpl/build.gradle b/packages/flutter_tools/templates/create/android-java.tmpl/build.gradle
index f5004b9..879b8ca 100644
--- a/packages/flutter_tools/templates/create/android-java.tmpl/build.gradle
+++ b/packages/flutter_tools/templates/create/android-java.tmpl/build.gradle
@@ -4,7 +4,7 @@
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.3'
+ classpath 'com.android.tools.build:gradle:2.3.3'
}
}
@@ -26,7 +26,3 @@
task clean(type: Delete) {
delete rootProject.buildDir
}
-
-task wrapper(type: Wrapper) {
- gradleVersion = '2.14.1'
-}
diff --git a/packages/flutter_tools/templates/create/android-kotlin.tmpl/build.gradle b/packages/flutter_tools/templates/create/android-kotlin.tmpl/build.gradle
index b22b7b7..27a170c 100644
--- a/packages/flutter_tools/templates/create/android-kotlin.tmpl/build.gradle
+++ b/packages/flutter_tools/templates/create/android-kotlin.tmpl/build.gradle
@@ -4,7 +4,7 @@
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.3'
+ classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-4'
}
}
@@ -27,7 +27,3 @@
task clean(type: Delete) {
delete rootProject.buildDir
}
-
-task wrapper(type: Wrapper) {
- gradleVersion = '2.14.1'
-}
diff --git a/packages/flutter_tools/templates/create/android.tmpl/.gitignore b/packages/flutter_tools/templates/create/android.tmpl/.gitignore
index 1fd9325..1658458 100644
--- a/packages/flutter_tools/templates/create/android.tmpl/.gitignore
+++ b/packages/flutter_tools/templates/create/android.tmpl/.gitignore
@@ -7,7 +7,3 @@
/build
/captures
GeneratedPluginRegistrant.java
-
-/gradle
-/gradlew
-/gradlew.bat
diff --git a/packages/flutter_tools/templates/create/android.tmpl/gradle/wrapper/gradle-wrapper.properties b/packages/flutter_tools/templates/create/android.tmpl/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..45e7f14
--- /dev/null
+++ b/packages/flutter_tools/templates/create/android.tmpl/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/packages/flutter_tools/templates/plugin/android-java.tmpl/build.gradle.tmpl b/packages/flutter_tools/templates/plugin/android-java.tmpl/build.gradle.tmpl
index a67e4e9..5ad2e82 100644
--- a/packages/flutter_tools/templates/plugin/android-java.tmpl/build.gradle.tmpl
+++ b/packages/flutter_tools/templates/plugin/android-java.tmpl/build.gradle.tmpl
@@ -7,7 +7,7 @@
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.0'
+ classpath 'com.android.tools.build:gradle:2.3.3'
}
}
diff --git a/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/build.gradle.tmpl b/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/build.gradle.tmpl
index 9d3eecd..02514fd 100644
--- a/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/build.gradle.tmpl
+++ b/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/build.gradle.tmpl
@@ -7,7 +7,7 @@
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.0'
+ classpath 'com.android.tools.build:gradle:2.3.3'
}
}
diff --git a/packages/flutter_tools/templates/plugin/android.tmpl/.gitignore b/packages/flutter_tools/templates/plugin/android.tmpl/.gitignore
index 5c4ef82..c6cbe56 100644
--- a/packages/flutter_tools/templates/plugin/android.tmpl/.gitignore
+++ b/packages/flutter_tools/templates/plugin/android.tmpl/.gitignore
@@ -6,7 +6,3 @@
.DS_Store
/build
/captures
-
-/gradle
-/gradlew
-/gradlew.bat
diff --git a/packages/flutter_tools/templates/plugin/android.tmpl/gradle/wrapper/gradle-wrapper.properties b/packages/flutter_tools/templates/plugin/android.tmpl/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..45e7f14
--- /dev/null
+++ b/packages/flutter_tools/templates/plugin/android.tmpl/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/packages/flutter_tools/test/cache_test.dart b/packages/flutter_tools/test/cache_test.dart
index f4b4509..b921260 100644
--- a/packages/flutter_tools/test/cache_test.dart
+++ b/packages/flutter_tools/test/cache_test.dart
@@ -48,6 +48,34 @@
Platform: () => new FakePlatform()..environment = <String, String>{'FLUTTER_ALREADY_LOCKED': 'true'},
});
});
+ group('Cache', () {
+ test('should not be up to date, if some cached artifact is not', () {
+ final CachedArtifact artifact1 = new MockCachedArtifact();
+ final CachedArtifact artifact2 = new MockCachedArtifact();
+ when(artifact1.isUpToDate()).thenReturn(true);
+ when(artifact2.isUpToDate()).thenReturn(false);
+ final Cache cache = new Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
+ expect(cache.isUpToDate(), isFalse);
+ });
+ test('should be up to date, if all cached artifacts are', () {
+ final CachedArtifact artifact1 = new MockCachedArtifact();
+ final CachedArtifact artifact2 = new MockCachedArtifact();
+ when(artifact1.isUpToDate()).thenReturn(true);
+ when(artifact2.isUpToDate()).thenReturn(true);
+ final Cache cache = new Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
+ expect(cache.isUpToDate(), isTrue);
+ });
+ test('should update cached artifacts which are not up to date', () async {
+ final CachedArtifact artifact1 = new MockCachedArtifact();
+ final CachedArtifact artifact2 = new MockCachedArtifact();
+ when(artifact1.isUpToDate()).thenReturn(true);
+ when(artifact2.isUpToDate()).thenReturn(false);
+ final Cache cache = new Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
+ await cache.updateAll();
+ verifyNever(artifact1.update());
+ verify(artifact2.update());
+ });
+ });
}
class MockFileSystem extends MemoryFileSystem {
@@ -65,3 +93,4 @@
}
class MockRandomAccessFile extends Mock implements RandomAccessFile {}
+class MockCachedArtifact extends Mock implements CachedArtifact {}