// 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 '../src/common.dart'; import '../src/context.dart'; void main() { testUsingContext('retry from 500', () async { String error; FakeAsync().run((FakeAsync time) { fetchUrl(Uri.parse('http://example.invalid/')).then((List value) { error = 'test completed unexpectedly'; }, onError: (dynamic exception) { error = 'test failed unexpectedly: $exception'; }); 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: { HttpClientFactory: () => () => MockHttpClient(500), }); testUsingContext('retry from network error', () async { String error; FakeAsync().run((FakeAsync time) { fetchUrl(Uri.parse('http://example.invalid/')).then((List value) { error = 'test completed unexpectedly'; }, onError: (dynamic exception) { error = 'test failed unexpectedly: $exception'; }); 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: { HttpClientFactory: () => () => MockHttpClient(200), }); testUsingContext('retry from SocketException', () async { String error; FakeAsync().run((FakeAsync time) { fetchUrl(Uri.parse('http://example.invalid/')).then((List value) { error = 'test completed unexpectedly'; }, onError: (dynamic exception) { error = 'test failed unexpectedly: $exception'; }); 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); expect(testLogger.traceText, contains('Download error: SocketException')); }, overrides: { HttpClientFactory: () => () => MockHttpClientThrowing( const io.SocketException('test exception handling'), ), }); testUsingContext('no retry from HandshakeException', () async { String error; FakeAsync().run((FakeAsync time) { fetchUrl(Uri.parse('http://example.invalid/')).then((List value) { error = 'test completed unexpectedly'; }, onError: (dynamic exception) { error = 'test failed: $exception'; }); expect(testLogger.statusText, ''); time.elapse(const Duration(milliseconds: 10000)); expect(testLogger.statusText, ''); }); expect(error, startsWith('test failed')); expect(testLogger.traceText, contains('HandshakeException')); }, overrides: { HttpClientFactory: () => () => MockHttpClientThrowing( const io.HandshakeException('test exception handling'), ), }); testUsingContext('remote file non-existant', () async { final Uri invalid = Uri.parse('http://example.invalid/'); final bool result = await doesRemoteFileExist(invalid); expect(result, false); }, overrides: { HttpClientFactory: () => () => MockHttpClient(404), }); testUsingContext('remote file server error', () async { final Uri valid = Uri.parse('http://example.valid/'); final bool result = await doesRemoteFileExist(valid); expect(result, false); }, overrides: { HttpClientFactory: () => () => MockHttpClient(500), }); testUsingContext('remote file exists', () async { final Uri valid = Uri.parse('http://example.valid/'); final bool result = await doesRemoteFileExist(valid); expect(result, true); }, overrides: { HttpClientFactory: () => () => MockHttpClient(200), }); } class MockHttpClientThrowing implements io.HttpClient { MockHttpClientThrowing(this.exception); final Exception exception; @override Future getUrl(Uri url) async { throw exception; } @override dynamic noSuchMethod(Invocation invocation) { throw 'io.HttpClient - $invocation'; } } class MockHttpClient implements io.HttpClient { MockHttpClient(this.statusCode); final int statusCode; @override Future getUrl(Uri url) async { return MockHttpClientRequest(statusCode); } @override Future headUrl(Uri url) async { return MockHttpClientRequest(statusCode); } @override dynamic noSuchMethod(Invocation invocation) { throw 'io.HttpClient - $invocation'; } } class MockHttpClientRequest implements io.HttpClientRequest { MockHttpClientRequest(this.statusCode); final int statusCode; @override Future close() async { return MockHttpClientResponse(statusCode); } @override dynamic noSuchMethod(Invocation invocation) { throw 'io.HttpClientRequest - $invocation'; } } class MockHttpClientResponse extends Stream> implements io.HttpClientResponse { MockHttpClientResponse(this.statusCode); @override final int statusCode; @override String get reasonPhrase => ''; @override StreamSubscription> listen(void onData(List event), { Function onError, void onDone(), bool cancelOnError }) { return Stream>.fromFuture(Future>.error(const io.SocketException('test'))) .listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } @override dynamic noSuchMethod(Invocation invocation) { throw 'io.HttpClientResponse - $invocation'; } }