Retry on failed download. (#12293)
diff --git a/packages/flutter_tools/test/base/net_test.dart b/packages/flutter_tools/test/base/net_test.dart
new file mode 100644
index 0000000..3840750
--- /dev/null
+++ b/packages/flutter_tools/test/base/net_test.dart
@@ -0,0 +1,115 @@
+// Copyright 2017 The Chromium 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 'dart:async';
+
+import 'package:flutter_tools/src/base/io.dart' as io;
+import 'package:flutter_tools/src/base/net.dart';
+import 'package:quiver/testing/async.dart';
+import 'package:test/test.dart';
+
+import '../src/context.dart';
+
+void main() {
+ testUsingContext('retry from 500', () async {
+ String error;
+ new FakeAsync().run((FakeAsync time) {
+ fetchUrl(Uri.parse('http://example.invalid/')).then((List<int> value) {
+ error = 'test completed unexpectedly';
+ }, onError: (dynamic error) {
+ error = 'test failed unexpectedly';
+ });
+ expect(testLogger.statusText, '');
+ time.elapse(const Duration(milliseconds: 10000));
+ expect(testLogger.statusText,
+ 'Download failed -- attempting retry 1 in 1 second...\n'
+ 'Download failed -- attempting retry 2 in 2 seconds...\n'
+ 'Download failed -- attempting retry 3 in 4 seconds...\n'
+ 'Download failed -- attempting retry 4 in 8 seconds...\n'
+ );
+ });
+ expect(testLogger.errorText, isEmpty);
+ expect(error, isNull);
+ }, overrides: <Type, Generator>{
+ HttpClientFactory: () => () => new MockHttpClient(500),
+ });
+
+ testUsingContext('retry from network error', () async {
+ String error;
+ new FakeAsync().run((FakeAsync time) {
+ fetchUrl(Uri.parse('http://example.invalid/')).then((List<int> value) {
+ error = 'test completed unexpectedly';
+ }, onError: (dynamic error) {
+ error = 'test failed unexpectedly';
+ });
+ expect(testLogger.statusText, '');
+ time.elapse(const Duration(milliseconds: 10000));
+ expect(testLogger.statusText,
+ 'Download failed -- attempting retry 1 in 1 second...\n'
+ 'Download failed -- attempting retry 2 in 2 seconds...\n'
+ 'Download failed -- attempting retry 3 in 4 seconds...\n'
+ 'Download failed -- attempting retry 4 in 8 seconds...\n'
+ );
+ });
+ expect(testLogger.errorText, isEmpty);
+ expect(error, isNull);
+ }, overrides: <Type, Generator>{
+ HttpClientFactory: () => () => new MockHttpClient(200),
+ });
+}
+
+class MockHttpClient implements io.HttpClient {
+ MockHttpClient(this.statusCode);
+
+ final int statusCode;
+
+ @override
+ Future<io.HttpClientRequest> getUrl(Uri url) async {
+ return new MockHttpClientRequest(statusCode);
+ }
+
+ @override
+ dynamic noSuchMethod(Invocation invocation) {
+ throw 'io.HttpClient - $invocation';
+ }
+}
+
+class MockHttpClientRequest implements io.HttpClientRequest {
+ MockHttpClientRequest(this.statusCode);
+
+ final int statusCode;
+
+ @override
+ Future<io.HttpClientResponse> close() async {
+ return new MockHttpClientResponse(statusCode);
+ }
+
+ @override
+ dynamic noSuchMethod(Invocation invocation) {
+ throw 'io.HttpClientRequest - $invocation';
+ }
+}
+
+class MockHttpClientResponse implements io.HttpClientResponse {
+ MockHttpClientResponse(this.statusCode);
+
+ @override
+ final int statusCode;
+
+ @override
+ String get reasonPhrase => '<reason phrase>';
+
+ @override
+ StreamSubscription<List<int>> listen(void onData(List<int> event), {
+ Function onError, void onDone(), bool cancelOnError
+ }) {
+ return new Stream<List<int>>.fromFuture(new Future<List<int>>.error(const io.SocketException('test')))
+ .listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
+ }
+
+ @override
+ dynamic noSuchMethod(Invocation invocation) {
+ throw 'io.HttpClientResponse - $invocation';
+ }
+}
diff --git a/packages/flutter_tools/test/cache_test.dart b/packages/flutter_tools/test/cache_test.dart
index b921260..a4ef941 100644
--- a/packages/flutter_tools/test/cache_test.dart
+++ b/packages/flutter_tools/test/cache_test.dart
@@ -48,6 +48,7 @@
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();
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index 6d89f62..8913488 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -60,7 +60,8 @@
..putIfAbsent(SimControl, () => new MockSimControl())
..putIfAbsent(Usage, () => new MockUsage())
..putIfAbsent(FlutterVersion, () => new MockFlutterVersion())
- ..putIfAbsent(Clock, () => const Clock());
+ ..putIfAbsent(Clock, () => const Clock())
+ ..putIfAbsent(HttpClient, () => new MockHttpClient());
}
void testUsingContext(String description, dynamic testMethod(), {
@@ -244,3 +245,5 @@
class MockFlutterVersion extends Mock implements FlutterVersion {}
class MockClock extends Mock implements Clock {}
+
+class MockHttpClient extends Mock implements HttpClient {}