Unverified Commit 23032d77 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] refactor artifact downloading to retry zip exceptions. (#64512)

parent 39d7a019
......@@ -266,6 +266,16 @@ class ErrorHandlingDirectory
Link childLink(String basename) =>
wrapLink(fileSystem.directory(delegate).childLink(basename));
@override
void createSync({bool recursive = false}) {
return _runSync<void>(
() => delegate.createSync(recursive: recursive),
platform: _platform,
failureMessage:
'Flutter failed to create a directory at "${delegate.path}"',
);
}
@override
Future<Directory> createTemp([String prefix]) {
return _run<Directory>(
......
This diff is collapsed.
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/memory.dart';
import 'package:file/src/interface/file.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/net.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:mockito/mockito.dart';
import '../src/common.dart';
void main() {
testWithoutContext('ArtifactUpdater can download a zip archive', () async {
final FakeNet net = FakeNet();
final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final BufferLogger logger = BufferLogger.test();
final ArtifactUpdater artifactUpdater = ArtifactUpdater(
fileSystem: fileSystem,
logger: logger,
net: net,
operatingSystemUtils: operatingSystemUtils,
tempStorage: fileSystem.currentDirectory.childDirectory('temp')
..createSync(),
);
await artifactUpdater.downloadZipArchive(
'test message',
Uri.parse('http:///test.zip'),
fileSystem.currentDirectory.childDirectory('out'),
);
expect(logger.statusText, contains('test message'));
expect(fileSystem.file('out/test'), exists);
});
testWithoutContext('ArtifactUpdater will de-download a file if unzipping fails', () async {
final FakeNet net = FakeNet();
final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final BufferLogger logger = BufferLogger.test();
final ArtifactUpdater artifactUpdater = ArtifactUpdater(
fileSystem: fileSystem,
logger: logger,
net: net,
operatingSystemUtils: operatingSystemUtils,
tempStorage: fileSystem.currentDirectory.childDirectory('temp')
..createSync(),
);
operatingSystemUtils.failures = 1;
await artifactUpdater.downloadZipArchive(
'test message',
Uri.parse('http:///test.zip'),
fileSystem.currentDirectory.childDirectory('out'),
);
expect(logger.statusText, contains('test message'));
expect(fileSystem.file('out/test'), exists);
expect(net.attempts, 2);
});
testWithoutContext('ArtifactUpdater will bail if unzipping fails more than twice', () async {
final FakeNet net = FakeNet();
final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final BufferLogger logger = BufferLogger.test();
final ArtifactUpdater artifactUpdater = ArtifactUpdater(
fileSystem: fileSystem,
logger: logger,
net: net,
operatingSystemUtils: operatingSystemUtils,
tempStorage: fileSystem.currentDirectory.childDirectory('temp')
..createSync(),
);
operatingSystemUtils.failures = 2;
expect(artifactUpdater.downloadZipArchive(
'test message',
Uri.parse('http:///test.zip'),
fileSystem.currentDirectory.childDirectory('out'),
), throwsA(isA<ProcessException>()));
expect(fileSystem.file('te,[/test'), isNot(exists));
expect(fileSystem.file('out/test'), isNot(exists));
});
testWithoutContext('ArtifactUpdater can download a tar archive', () async {
final FakeNet net = FakeNet();
final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final BufferLogger logger = BufferLogger.test();
final ArtifactUpdater artifactUpdater = ArtifactUpdater(
fileSystem: fileSystem,
logger: logger,
net: net,
operatingSystemUtils: operatingSystemUtils,
tempStorage: fileSystem.currentDirectory.childDirectory('temp')
..createSync(),
);
await artifactUpdater.downloadZippedTarball(
'test message',
Uri.parse('http:///test.zip'),
fileSystem.currentDirectory.childDirectory('out'),
);
expect(fileSystem.file('out/test'), exists);
});
testWithoutContext('ArtifactUpdater will delete downloaded files if they exist.', () async {
final FakeNet net = FakeNet();
final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final BufferLogger logger = BufferLogger.test();
final ArtifactUpdater artifactUpdater = ArtifactUpdater(
fileSystem: fileSystem,
logger: logger,
net: net,
operatingSystemUtils: operatingSystemUtils,
tempStorage: fileSystem.currentDirectory.childDirectory('temp')
..createSync(),
);
artifactUpdater.downloadedFiles.addAll(<File>[
fileSystem.file('a/b/c/d')..createSync(recursive: true),
fileSystem.file('d/e/f'),
]);
artifactUpdater.removeDownloadedFiles();
expect(fileSystem.file('a/b/c/d'), isNot(exists));
expect(logger.errorText, isEmpty);
});
}
class FakeNet implements Net {
int attempts = 0;
@override
Future<bool> doesRemoteFileExist(Uri url) async {
return true;
}
@override
Future<List<int>> fetchUrl(Uri url, {int maxAttempts, File destFile}) async {
attempts += 1;
if (destFile != null) {
destFile.createSync();
return null;
}
return <int>[];
}
}
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {
int failures = 0;
@override
void unzip(File file, Directory targetDirectory) {
if (failures > 0) {
failures -= 1;
throw const ProcessException('zip', <String>[], 'Failed to unzip');
}
targetDirectory.childFile(file.fileSystem.path.basenameWithoutExtension(file.path))
.createSync();
}
@override
void unpack(File gzippedTarFile, Directory targetDirectory) {
if (failures > 0) {
failures -= 1;
throw const ProcessException('zip', <String>[], 'Failed to unzip');
}
targetDirectory.childFile(gzippedTarFile.fileSystem.path.basenameWithoutExtension(gzippedTarFile.path))
.createSync();
}
}
......@@ -64,7 +64,7 @@ void setupWriteMocks({
)).thenThrow(FileSystemException('', '', OSError('', errorCode)));
}
void setupCreateTempMocks({
void setupDirectoryMocks({
FileSystem mockFileSystem,
ErrorHandlingFileSystem fs,
int errorCode,
......@@ -76,6 +76,8 @@ void setupCreateTempMocks({
});
when(mockDirectory.createTempSync(any))
.thenThrow(FileSystemException('', '', OSError('', errorCode)));
when(mockDirectory.createSync(recursive: anyNamed('recursive')))
.thenThrow(FileSystemException('', '', OSError('', errorCode)));
}
void main() {
......@@ -158,7 +160,7 @@ void main() {
});
testWithoutContext('when creating a temporary dir on a full device', () async {
setupCreateTempMocks(
setupDirectoryMocks(
mockFileSystem: mockFileSystem,
fs: fs,
errorCode: kDeviceFull,
......@@ -172,6 +174,20 @@ void main() {
expect(() => directory.createTempSync('prefix'),
throwsToolExit(message: expectedMessage));
});
testWithoutContext('when creating a directory with permission issues', () async {
setupDirectoryMocks(
mockFileSystem: mockFileSystem,
fs: fs,
errorCode: kUserPermissionDenied,
);
final Directory directory = fs.directory('directory');
const String expectedMessage = 'Flutter failed to create a directory at';
expect(() => directory.createSync(recursive: true),
throwsToolExit(message: expectedMessage));
});
});
group('throws ToolExit on Linux', () {
......@@ -209,7 +225,7 @@ void main() {
});
testWithoutContext('when creating a temporary dir on a full device', () async {
setupCreateTempMocks(
setupDirectoryMocks(
mockFileSystem: mockFileSystem,
fs: fs,
errorCode: enospc,
......
......@@ -915,11 +915,6 @@ class FakeCache implements Cache {
throw UnsupportedError('Not supported in the fake Cache');
}
@override
Future<String> getThirdPartyFile(String urlStr, String serviceName) {
throw UnsupportedError('Not supported in the fake Cache');
}
@override
String getVersionFor(String artifactName) {
throw UnsupportedError('Not supported in the fake Cache');
......@@ -949,10 +944,6 @@ class FakeCache implements Cache {
Future<void> updateAll(Set<DevelopmentArtifact> requiredArtifacts) async {
}
@override
Future<void> downloadFile(Uri url, File location) async {
}
@override
Future<bool> doesRemoteExist(String message, Uri url) async {
return true;
......
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