mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
[flutter_tools] fix failure to create ansi spinner if download needs to be retried (#65797)
Because the Status spinner was only created on setup, the second stop would asset/crash. Fixes #65449
This commit is contained in:
parent
4e20f00053
commit
6acea15f22
|
@ -81,7 +81,10 @@ class OutputPreferences {
|
||||||
|
|
||||||
/// The command line terminal, if available.
|
/// The command line terminal, if available.
|
||||||
abstract class Terminal {
|
abstract class Terminal {
|
||||||
factory Terminal.test() = _TestTerminal;
|
/// Create a new test [Terminal].
|
||||||
|
///
|
||||||
|
/// If not specified, [supportsColor] defaults to `false`.
|
||||||
|
factory Terminal.test({bool supportsColor}) = _TestTerminal;
|
||||||
|
|
||||||
/// Whether the current terminal supports color escape codes.
|
/// Whether the current terminal supports color escape codes.
|
||||||
bool get supportsColor;
|
bool get supportsColor;
|
||||||
|
@ -300,6 +303,8 @@ class AnsiTerminal implements Terminal {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TestTerminal implements Terminal {
|
class _TestTerminal implements Terminal {
|
||||||
|
_TestTerminal({this.supportsColor = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool usesTerminalUi;
|
bool usesTerminalUi;
|
||||||
|
|
||||||
|
@ -329,7 +334,7 @@ class _TestTerminal implements Terminal {
|
||||||
set singleCharMode(bool value) { }
|
set singleCharMode(bool value) { }
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get supportsColor => false;
|
final bool supportsColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get supportsEmoji => false;
|
bool get supportsEmoji => false;
|
||||||
|
|
|
@ -1466,13 +1466,14 @@ class ArtifactUpdater {
|
||||||
) async {
|
) async {
|
||||||
final String downloadPath = flattenNameSubdirs(url, _fileSystem);
|
final String downloadPath = flattenNameSubdirs(url, _fileSystem);
|
||||||
final File tempFile = _createDownloadFile(downloadPath);
|
final File tempFile = _createDownloadFile(downloadPath);
|
||||||
final Status status = _logger.startProgress(
|
Status status;
|
||||||
message,
|
|
||||||
timeout: null, // This will take a variable amount of time based on network connectivity.
|
|
||||||
);
|
|
||||||
int retries = _kRetryCount;
|
int retries = _kRetryCount;
|
||||||
|
|
||||||
while (retries > 0) {
|
while (retries > 0) {
|
||||||
|
status = _logger.startProgress(
|
||||||
|
message,
|
||||||
|
timeout: null, // This will take a variable amount of time based on network connectivity.
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
_ensureExists(tempFile.parent);
|
_ensureExists(tempFile.parent);
|
||||||
final IOSink ioSink = tempFile.openWrite();
|
final IOSink ioSink = tempFile.openWrite();
|
||||||
|
|
|
@ -10,10 +10,12 @@ import 'package:flutter_tools/src/base/io.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/base/os.dart';
|
import 'package:flutter_tools/src/base/os.dart';
|
||||||
import 'package:flutter_tools/src/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
|
import 'package:flutter_tools/src/base/terminal.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
|
|
||||||
import '../src/common.dart';
|
import '../src/common.dart';
|
||||||
|
import '../src/mocks.dart';
|
||||||
|
|
||||||
final Platform testPlatform = FakePlatform(environment: <String, String>{});
|
final Platform testPlatform = FakePlatform(environment: <String, String>{});
|
||||||
|
|
||||||
|
@ -41,6 +43,34 @@ void main() {
|
||||||
expect(fileSystem.file('out/test'), exists);
|
expect(fileSystem.file('out/test'), exists);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWithoutContext('ArtifactUpdater will restart the status ticker if it needs to retry the download', () async {
|
||||||
|
final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();
|
||||||
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
||||||
|
final Logger logger = StdoutLogger(
|
||||||
|
terminal: Terminal.test(supportsColor: true),
|
||||||
|
stdio: MockStdio(),
|
||||||
|
outputPreferences: OutputPreferences.test(),
|
||||||
|
timeoutConfiguration: const TimeoutConfiguration(),
|
||||||
|
);
|
||||||
|
final ArtifactUpdater artifactUpdater = ArtifactUpdater(
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
logger: logger,
|
||||||
|
operatingSystemUtils: operatingSystemUtils,
|
||||||
|
platform: testPlatform,
|
||||||
|
httpClient: MockHttpClient()..exceptionOnFirstRun = true,
|
||||||
|
tempStorage: fileSystem.currentDirectory.childDirectory('temp')
|
||||||
|
..createSync(),
|
||||||
|
);
|
||||||
|
|
||||||
|
await artifactUpdater.downloadZipArchive(
|
||||||
|
'test message',
|
||||||
|
Uri.parse('http:///test.zip'),
|
||||||
|
fileSystem.currentDirectory.childDirectory('out'),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fileSystem.file('out/test'), exists);
|
||||||
|
});
|
||||||
|
|
||||||
testWithoutContext('ArtifactUpdater will re-attempt on a non-200 response', () async {
|
testWithoutContext('ArtifactUpdater will re-attempt on a non-200 response', () async {
|
||||||
final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();
|
final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();
|
||||||
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
|
||||||
|
@ -249,10 +279,15 @@ class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {
|
||||||
class MockHttpClient extends Mock implements HttpClient {
|
class MockHttpClient extends Mock implements HttpClient {
|
||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
bool argumentError = false;
|
bool argumentError = false;
|
||||||
|
bool exceptionOnFirstRun = false;
|
||||||
final MockHttpClientRequest testRequest = MockHttpClientRequest();
|
final MockHttpClientRequest testRequest = MockHttpClientRequest();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<HttpClientRequest> getUrl(Uri url) async {
|
Future<HttpClientRequest> getUrl(Uri url) async {
|
||||||
|
if (exceptionOnFirstRun && attempts == 0) {
|
||||||
|
attempts += 1;
|
||||||
|
throw Exception();
|
||||||
|
}
|
||||||
attempts += 1;
|
attempts += 1;
|
||||||
if (argumentError) {
|
if (argumentError) {
|
||||||
throw ArgumentError();
|
throw ArgumentError();
|
||||||
|
|
Loading…
Reference in a new issue