Unverified Commit 29c026fa authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[flutter_tool] Don't crash when failing to delete downloaded artifacts (#44933)

* [flutter_tool] Don't crash when failing to delete downloaded artifacts

* Add space

* Add more spaces.
parent 4372ed80
...@@ -432,7 +432,8 @@ abstract class CachedArtifact extends ArtifactSet { ...@@ -432,7 +432,8 @@ abstract class CachedArtifact extends ArtifactSet {
/// can delete them after completion. We don't delete them right after /// can delete them after completion. We don't delete them right after
/// extraction in case [update] is interrupted, so we can restart without /// extraction in case [update] is interrupted, so we can restart without
/// starting from scratch. /// starting from scratch.
final List<File> _downloadedFiles = <File>[]; @visibleForTesting
final List<File> downloadedFiles = <File>[];
@override @override
bool isUpToDate() { bool isUpToDate() {
...@@ -465,8 +466,13 @@ abstract class CachedArtifact extends ArtifactSet { ...@@ -465,8 +466,13 @@ abstract class CachedArtifact extends ArtifactSet {
/// Clear any zip/gzip files downloaded. /// Clear any zip/gzip files downloaded.
void _removeDownloadedFiles() { void _removeDownloadedFiles() {
for (File f in _downloadedFiles) { for (File f in downloadedFiles) {
f.deleteSync(); try {
f.deleteSync();
} on FileSystemException catch (e) {
printError('Failed to delete "${f.path}". Please delete manually. $e');
continue;
}
for (Directory d = f.parent; d.absolute.path != cache.getDownloadDir().absolute.path; d = d.parent) { for (Directory d = f.parent; d.absolute.path != cache.getDownloadDir().absolute.path; d = d.parent) {
if (d.listSync().isEmpty) { if (d.listSync().isEmpty) {
d.deleteSync(); d.deleteSync();
...@@ -532,10 +538,10 @@ abstract class CachedArtifact extends ArtifactSet { ...@@ -532,10 +538,10 @@ abstract class CachedArtifact extends ArtifactSet {
} }
/// Create a temporary file and invoke [onTemporaryFile] with the file as /// Create a temporary file and invoke [onTemporaryFile] with the file as
/// argument, then add the temporary file to the [_downloadedFiles]. /// argument, then add the temporary file to the [downloadedFiles].
Future<void> _withDownloadFile(String name, Future<void> onTemporaryFile(File file)) async { Future<void> _withDownloadFile(String name, Future<void> onTemporaryFile(File file)) async {
final File tempFile = fs.file(fs.path.join(cache.getDownloadDir().path, name)); final File tempFile = fs.file(fs.path.join(cache.getDownloadDir().path, name));
_downloadedFiles.add(tempFile); downloadedFiles.add(tempFile);
await onTemporaryFile(tempFile); await onTemporaryFile(tempFile);
} }
} }
......
...@@ -93,6 +93,27 @@ void main() { ...@@ -93,6 +93,27 @@ void main() {
memoryFileSystem = MemoryFileSystem(); memoryFileSystem = MemoryFileSystem();
}); });
testUsingContext('Continues on failed delete', () async {
final Directory artifactDir = fs.systemTempDirectory.createTempSync('flutter_cache_test_artifact.');
final Directory downloadDir = fs.systemTempDirectory.createTempSync('flutter_cache_test_download.');
when(mockCache.getArtifactDirectory(any)).thenReturn(artifactDir);
when(mockCache.getDownloadDir()).thenReturn(downloadDir);
final File mockFile = MockFile();
when(mockFile.deleteSync()).thenAnswer((_) {
throw const FileSystemException('delete failed');
});
final FakeDownloadedArtifact artifact = FakeDownloadedArtifact(
mockFile,
mockCache,
);
await artifact.update();
expect(testLogger.errorText, contains('delete failed'));
}, overrides: <Type, Generator>{
Cache: () => mockCache,
FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('Gradle wrapper should not be up to date, if some cached artifact is not available', () { testUsingContext('Gradle wrapper should not be up to date, if some cached artifact is not available', () {
final GradleWrapper gradleWrapper = GradleWrapper(mockCache); final GradleWrapper gradleWrapper = GradleWrapper(mockCache);
final Directory directory = fs.directory('/Applications/flutter/bin/cache'); final Directory directory = fs.directory('/Applications/flutter/bin/cache');
...@@ -101,7 +122,7 @@ void main() { ...@@ -101,7 +122,7 @@ void main() {
when(mockCache.getCacheDir(fs.path.join('artifacts', 'gradle_wrapper'))).thenReturn(fs.directory(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper'))); when(mockCache.getCacheDir(fs.path.join('artifacts', 'gradle_wrapper'))).thenReturn(fs.directory(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper')));
expect(gradleWrapper.isUpToDateInner(), false); expect(gradleWrapper.isUpToDateInner(), false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Cache: ()=> mockCache, Cache: () => mockCache,
FileSystem: () => memoryFileSystem, FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
...@@ -117,7 +138,7 @@ void main() { ...@@ -117,7 +138,7 @@ void main() {
when(mockCache.getCacheDir(fs.path.join('artifacts', 'gradle_wrapper'))).thenReturn(fs.directory(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper'))); when(mockCache.getCacheDir(fs.path.join('artifacts', 'gradle_wrapper'))).thenReturn(fs.directory(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper')));
expect(gradleWrapper.isUpToDateInner(), true); expect(gradleWrapper.isUpToDateInner(), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Cache: ()=> mockCache, Cache: () => mockCache,
FileSystem: () => memoryFileSystem, FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
...@@ -174,7 +195,7 @@ void main() { ...@@ -174,7 +195,7 @@ void main() {
'/path/to/alpha:/path/to/beta:/path/to/gamma:/path/to/delta:/path/to/epsilon', '/path/to/alpha:/path/to/beta:/path/to/gamma:/path/to/delta:/path/to/epsilon',
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Cache: ()=> mockCache, Cache: () => mockCache,
}); });
testUsingContext('failed storage.googleapis.com download shows China warning', () async { testUsingContext('failed storage.googleapis.com download shows China warning', () async {
final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact1 = MockCachedArtifact();
...@@ -272,7 +293,7 @@ void main() { ...@@ -272,7 +293,7 @@ void main() {
expect(dir.path, artifactDir.childDirectory('bin_dir').path); expect(dir.path, artifactDir.childDirectory('bin_dir').path);
verify(mockOperatingSystemUtils.chmod(argThat(hasPath(dir.path)), 'a+r,a+x')); verify(mockOperatingSystemUtils.chmod(argThat(hasPath(dir.path)), 'a+r,a+x'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Cache: ()=> mockCache, Cache: () => mockCache,
FileSystem: () => memoryFileSystem, FileSystem: () => memoryFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
HttpClientFactory: () => () => fakeHttpClient, HttpClientFactory: () => () => fakeHttpClient,
...@@ -324,7 +345,7 @@ void main() { ...@@ -324,7 +345,7 @@ void main() {
expect(mavenArtifacts.isUpToDate(), isFalse); expect(mavenArtifacts.isUpToDate(), isFalse);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Cache: ()=> mockCache, Cache: () => mockCache,
FileSystem: () => memoryFileSystem, FileSystem: () => memoryFileSystem,
ProcessManager: () => processManager, ProcessManager: () => processManager,
}); });
...@@ -435,6 +456,21 @@ class FakeCachedArtifact extends EngineCachedArtifact { ...@@ -435,6 +456,21 @@ class FakeCachedArtifact extends EngineCachedArtifact {
List<String> getPackageDirs() => packageDirs; List<String> getPackageDirs() => packageDirs;
} }
class FakeDownloadedArtifact extends CachedArtifact {
FakeDownloadedArtifact(this.downloadedFile, Cache cache) : super(
'fake',
cache,
DevelopmentArtifact.universal,
);
final File downloadedFile;
@override
Future<void> updateInner() async {
downloadedFiles.add(downloadedFile);
}
}
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
class MockFileSystem extends Mock implements FileSystem {} class MockFileSystem extends Mock implements FileSystem {}
class MockFile extends Mock implements File {} class MockFile extends Mock implements File {}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment