Unverified Commit 8df58eb6 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] use a single fake implementation of HttpClient (#75471)

parent 645cb643
...@@ -48,6 +48,7 @@ class Net { ...@@ -48,6 +48,7 @@ class Net {
Future<List<int>> fetchUrl(Uri url, { Future<List<int>> fetchUrl(Uri url, {
int maxAttempts, int maxAttempts,
File destFile, File destFile,
@visibleForTesting Duration durationOverride,
}) async { }) async {
int attempts = 0; int attempts = 0;
int durationSeconds = 1; int durationSeconds = 1;
...@@ -78,7 +79,7 @@ class Net { ...@@ -78,7 +79,7 @@ class Net {
'Download failed -- attempting retry $attempts in ' 'Download failed -- attempting retry $attempts in '
'$durationSeconds second${ durationSeconds == 1 ? "" : "s"}...', '$durationSeconds second${ durationSeconds == 1 ? "" : "s"}...',
); );
await Future<void>.delayed(Duration(seconds: durationSeconds)); await Future<void>.delayed(durationOverride ?? Duration(seconds: durationSeconds));
if (durationSeconds < 64) { if (durationSeconds < 64) {
durationSeconds *= 2; durationSeconds *= 2;
} }
...@@ -173,8 +174,6 @@ class Net { ...@@ -173,8 +174,6 @@ class Net {
} }
} }
/// An IOSink that collects whatever is written to it. /// An IOSink that collects whatever is written to it.
class _MemoryIOSink implements IOSink { class _MemoryIOSink implements IOSink {
@override @override
......
...@@ -78,7 +78,6 @@ void main() { ...@@ -78,7 +78,6 @@ void main() {
MockPortForwarder portForwarder; MockPortForwarder portForwarder;
MockDartDevelopmentService mockDds; MockDartDevelopmentService mockDds;
MockAndroidDevice device; MockAndroidDevice device;
MockHttpClient httpClient;
setUp(() { setUp(() {
fakeLogReader = FakeDeviceLogReader(); fakeLogReader = FakeDeviceLogReader();
...@@ -98,13 +97,6 @@ void main() { ...@@ -98,13 +97,6 @@ void main() {
when(mockDds.startDartDevelopmentService(any, any, false, any)).thenReturn(null); when(mockDds.startDartDevelopmentService(any, any, false, any)).thenReturn(null);
when(mockDds.uri).thenReturn(Uri.parse('http://localhost:8181')); when(mockDds.uri).thenReturn(Uri.parse('http://localhost:8181'));
when(mockDds.done).thenAnswer((_) => noopCompleter.future); when(mockDds.done).thenAnswer((_) => noopCompleter.future);
final HttpClientRequest httpClientRequest = MockHttpClientRequest();
httpClient = MockHttpClient();
when(httpClient.putUrl(any))
.thenAnswer((_) => Future<HttpClientRequest>.value(httpClientRequest));
when(httpClientRequest.headers).thenReturn(MockHttpHeaders());
when(httpClientRequest.close())
.thenAnswer((_) => Future<HttpClientResponse>.value(MockHttpClientResponse()));
// We cannot add the device to a device manager because that is // We cannot add the device to a device manager because that is
// only enabled by the context of each testUsingContext call. // only enabled by the context of each testUsingContext call.
...@@ -925,6 +917,3 @@ class TestHotRunnerFactory extends HotRunnerFactory { ...@@ -925,6 +917,3 @@ class TestHotRunnerFactory extends HotRunnerFactory {
} }
class MockDartDevelopmentService extends Mock implements DartDevelopmentService {} class MockDartDevelopmentService extends Mock implements DartDevelopmentService {}
class MockHttpClientRequest extends Mock implements HttpClientRequest {}
class MockHttpClientResponse extends Mock implements HttpClientResponse {}
class MockHttpHeaders extends Mock implements HttpHeaders {}
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:file_testing/file_testing.dart'; import 'package:file_testing/file_testing.dart';
...@@ -31,6 +30,7 @@ import 'package:pubspec_parse/pubspec_parse.dart'; ...@@ -31,6 +30,7 @@ import 'package:pubspec_parse/pubspec_parse.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/fake_http_client.dart';
import '../../src/pubspec_schema.dart'; import '../../src/pubspec_schema.dart';
import '../../src/testbed.dart'; import '../../src/testbed.dart';
...@@ -1627,7 +1627,16 @@ void main() { ...@@ -1627,7 +1627,16 @@ void main() {
expect(projectDir.childDirectory('lib').childFile('main.dart').readAsStringSync(), expect(projectDir.childDirectory('lib').childFile('main.dart').readAsStringSync(),
contains('void main() {}')); contains('void main() {}'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClient(200, result: 'void main() {}'), HttpClientFactory: () {
return () {
return FakeHttpClient.list(<FakeRequest>[
FakeRequest(
Uri.parse('https://master-api.flutter.dev/snippets/foo.bar.Baz.dart'),
response: FakeResponse(body: utf8.encode('void main() {}')),
)
]);
};
},
}); });
testUsingContext('null-safe sample-based project have no analyzer errors', () async { testUsingContext('null-safe sample-based project have no analyzer errors', () async {
...@@ -1641,7 +1650,16 @@ void main() { ...@@ -1641,7 +1650,16 @@ void main() {
contains('String?'), // uses null-safe syntax contains('String?'), // uses null-safe syntax
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => () => MockHttpClient(200, result: 'void main() { String? foo; print(foo); }'), HttpClientFactory: () {
return () {
return FakeHttpClient.list(<FakeRequest>[
FakeRequest(
Uri.parse('https://master-api.flutter.dev/snippets/foo.bar.Baz.dart'),
response: FakeResponse(body: utf8.encode('void main() { String? foo; print(foo); }')),
)
]);
};
},
}); });
testUsingContext('can write samples index to disk', () async { testUsingContext('can write samples index to disk', () async {
...@@ -1659,8 +1677,16 @@ void main() { ...@@ -1659,8 +1677,16 @@ void main() {
expect(expectedFile, exists); expect(expectedFile, exists);
expect(expectedFile.readAsStringSync(), equals(samplesIndexJson)); expect(expectedFile.readAsStringSync(), equals(samplesIndexJson));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => HttpClientFactory: () {
() => MockHttpClient(200, result: samplesIndexJson), return () {
return FakeHttpClient.list(<FakeRequest>[
FakeRequest(
Uri.parse('https://master-api.flutter.dev/snippets/index.json'),
response: FakeResponse(body: utf8.encode(samplesIndexJson)),
)
]);
};
},
}); });
testUsingContext('Throws tool exit on empty samples index', () async { testUsingContext('Throws tool exit on empty samples index', () async {
...@@ -1680,8 +1706,15 @@ void main() { ...@@ -1680,8 +1706,15 @@ void main() {
message: 'Unable to download samples', message: 'Unable to download samples',
)); ));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => HttpClientFactory: () {
() => MockHttpClient(200, result: ''), return () {
return FakeHttpClient.list(<FakeRequest>[
FakeRequest(
Uri.parse('https://master-api.flutter.dev/snippets/index.json'),
)
]);
};
},
}); });
testUsingContext('provides an error to the user if samples json download fails', () async { testUsingContext('provides an error to the user if samples json download fails', () async {
...@@ -1697,8 +1730,16 @@ void main() { ...@@ -1697,8 +1730,16 @@ void main() {
await expectLater(runner.run(args), throwsToolExit(exitCode: 2, message: 'Failed to write samples')); await expectLater(runner.run(args), throwsToolExit(exitCode: 2, message: 'Failed to write samples'));
expect(globals.fs.file(outputFile), isNot(exists)); expect(globals.fs.file(outputFile), isNot(exists));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
HttpClientFactory: () => HttpClientFactory: () {
() => MockHttpClient(404, result: 'not found'), return () {
return FakeHttpClient.list(<FakeRequest>[
FakeRequest(
Uri.parse('https://master-api.flutter.dev/snippets/index.json'),
response: const FakeResponse(statusCode: HttpStatus.notFound),
)
]);
};
},
}); });
testUsingContext('plugin does not support any platform by default', () async { testUsingContext('plugin does not support any platform by default', () async {
...@@ -2655,76 +2696,3 @@ class LoggingProcessManager extends LocalProcessManager { ...@@ -2655,76 +2696,3 @@ class LoggingProcessManager extends LocalProcessManager {
); );
} }
} }
class MockHttpClient implements HttpClient {
MockHttpClient(this.statusCode, {this.result});
final int statusCode;
final String result;
@override
Future<HttpClientRequest> getUrl(Uri url) async {
return MockHttpClientRequest(statusCode, result: result);
}
@override
dynamic noSuchMethod(Invocation invocation) {
throw 'io.HttpClient - $invocation';
}
}
class MockHttpClientRequest implements HttpClientRequest {
MockHttpClientRequest(this.statusCode, {this.result});
final int statusCode;
final String result;
@override
Future<HttpClientResponse> close() async {
return MockHttpClientResponse(statusCode, result: result);
}
@override
dynamic noSuchMethod(Invocation invocation) {
throw 'io.HttpClientRequest - $invocation';
}
}
class MockHttpClientResponse implements HttpClientResponse {
MockHttpClientResponse(this.statusCode, {this.result});
@override
final int statusCode;
final String result;
@override
String get reasonPhrase => '<reason phrase>';
@override
HttpClientResponseCompressionState get compressionState {
return HttpClientResponseCompressionState.decompressed;
}
@override
StreamSubscription<Uint8List> listen(
void onData(Uint8List event), {
Function onError,
void onDone(),
bool cancelOnError,
}) {
return Stream<Uint8List>.fromIterable(<Uint8List>[Uint8List.fromList(result.codeUnits)])
.listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@override
Future<dynamic> forEach(void Function(Uint8List element) action) {
action(Uint8List.fromList(result.codeUnits));
return Future<void>.value();
}
@override
dynamic noSuchMethod(Invocation invocation) {
throw 'io.HttpClientResponse - $invocation';
}
}
...@@ -4,45 +4,32 @@ ...@@ -4,45 +4,32 @@
// @dart = 2.8 // @dart = 2.8
import 'dart:async';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/bot_detector.dart'; import 'package:flutter_tools/src/base/bot_detector.dart';
import 'package:flutter_tools/src/base/io.dart'; 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/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/persistent_tool_state.dart'; import 'package:flutter_tools/src/persistent_tool_state.dart';
import 'package:mockito/mockito.dart';
import 'package:fake_async/fake_async.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/fake_http_client.dart';
import '../../src/fakes.dart'; import '../../src/fakes.dart';
final Uri azureUrl = Uri.parse('http://169.254.169.254/metadata/instance');
void main() { void main() {
group('BotDetector', () { group('BotDetector', () {
FakePlatform fakePlatform; FakePlatform fakePlatform;
FakeStdio fakeStdio; FakeStdio fakeStdio;
MockHttpClient mockHttpClient;
MockHttpClientRequest mockHttpClientRequest;
MockHttpHeaders mockHttpHeaders;
BotDetector botDetector;
PersistentToolState persistentToolState; PersistentToolState persistentToolState;
setUp(() { setUp(() {
fakePlatform = FakePlatform()..environment = <String, String>{}; fakePlatform = FakePlatform()..environment = <String, String>{};
fakeStdio = FakeStdio(); fakeStdio = FakeStdio();
mockHttpClient = MockHttpClient();
mockHttpClientRequest = MockHttpClientRequest();
mockHttpHeaders = MockHttpHeaders();
persistentToolState = PersistentToolState.test( persistentToolState = PersistentToolState.test(
directory: MemoryFileSystem.test().currentDirectory, directory: MemoryFileSystem.test().currentDirectory,
logger: BufferLogger.test(), logger: BufferLogger.test(),
); );
botDetector = BotDetector(
platform: fakePlatform,
httpClientFactory: () => mockHttpClient,
persistentToolState: persistentToolState,
);
}); });
group('isRunningOnBot', () { group('isRunningOnBot', () {
...@@ -50,6 +37,12 @@ void main() { ...@@ -50,6 +37,12 @@ void main() {
fakePlatform.environment['BOT'] = 'false'; fakePlatform.environment['BOT'] = 'false';
fakePlatform.environment['TRAVIS'] = 'true'; fakePlatform.environment['TRAVIS'] = 'true';
final BotDetector botDetector = BotDetector(
platform: fakePlatform,
httpClientFactory: () => FakeHttpClient.any(),
persistentToolState: persistentToolState,
);
expect(await botDetector.isRunningOnBot, isFalse); expect(await botDetector.isRunningOnBot, isFalse);
expect(persistentToolState.isRunningOnBot, isFalse); expect(persistentToolState.isRunningOnBot, isFalse);
}); });
...@@ -58,14 +51,25 @@ void main() { ...@@ -58,14 +51,25 @@ void main() {
fakePlatform.environment['FLUTTER_HOST'] = 'foo'; fakePlatform.environment['FLUTTER_HOST'] = 'foo';
fakePlatform.environment['TRAVIS'] = 'true'; fakePlatform.environment['TRAVIS'] = 'true';
final BotDetector botDetector = BotDetector(
platform: fakePlatform,
httpClientFactory: () => FakeHttpClient.any(),
persistentToolState: persistentToolState,
);
expect(await botDetector.isRunningOnBot, isFalse); expect(await botDetector.isRunningOnBot, isFalse);
expect(persistentToolState.isRunningOnBot, isFalse); expect(persistentToolState.isRunningOnBot, isFalse);
}); });
testWithoutContext('returns false with and without a terminal attached', () async { testWithoutContext('returns false with and without a terminal attached', () async {
when(mockHttpClient.getUrl(any)).thenAnswer((_) { final BotDetector botDetector = BotDetector(
throw const SocketException('HTTP connection timed out'); platform: fakePlatform,
}); httpClientFactory: () => FakeHttpClient.list(<FakeRequest>[
FakeRequest(azureUrl, responseError: const SocketException('HTTP connection timed out')),
]),
persistentToolState: persistentToolState,
);
fakeStdio.stdout.hasTerminal = true; fakeStdio.stdout.hasTerminal = true;
expect(await botDetector.isRunningOnBot, isFalse); expect(await botDetector.isRunningOnBot, isFalse);
fakeStdio.stdout.hasTerminal = false; fakeStdio.stdout.hasTerminal = false;
...@@ -76,38 +80,52 @@ void main() { ...@@ -76,38 +80,52 @@ void main() {
testWithoutContext('can test analytics outputs on bots when outputting to a file', () async { testWithoutContext('can test analytics outputs on bots when outputting to a file', () async {
fakePlatform.environment['TRAVIS'] = 'true'; fakePlatform.environment['TRAVIS'] = 'true';
fakePlatform.environment['FLUTTER_ANALYTICS_LOG_FILE'] = '/some/file'; fakePlatform.environment['FLUTTER_ANALYTICS_LOG_FILE'] = '/some/file';
final BotDetector botDetector = BotDetector(
platform: fakePlatform,
httpClientFactory: () => FakeHttpClient.any(),
persistentToolState: persistentToolState,
);
expect(await botDetector.isRunningOnBot, isFalse); expect(await botDetector.isRunningOnBot, isFalse);
expect(persistentToolState.isRunningOnBot, isFalse); expect(persistentToolState.isRunningOnBot, isFalse);
}); });
testWithoutContext('returns true when azure metadata is reachable', () async { testWithoutContext('returns true when azure metadata is reachable', () async {
when(mockHttpClient.getUrl(any)).thenAnswer((_) { final BotDetector botDetector = BotDetector(
return Future<HttpClientRequest>.value(mockHttpClientRequest); platform: fakePlatform,
}); httpClientFactory: () => FakeHttpClient.any(),
when(mockHttpClientRequest.headers).thenReturn(mockHttpHeaders); persistentToolState: persistentToolState,
);
expect(await botDetector.isRunningOnBot, isTrue); expect(await botDetector.isRunningOnBot, isTrue);
expect(persistentToolState.isRunningOnBot, isTrue); expect(persistentToolState.isRunningOnBot, isTrue);
}); });
testWithoutContext('caches azure bot detection results across instances', () async { testWithoutContext('caches azure bot detection results across instances', () async {
when(mockHttpClient.getUrl(any)).thenAnswer((_) { final BotDetector botDetector = BotDetector(
return Future<HttpClientRequest>.value(mockHttpClientRequest); platform: fakePlatform,
}); httpClientFactory: () => FakeHttpClient.any(),
when(mockHttpClientRequest.headers).thenReturn(mockHttpHeaders); persistentToolState: persistentToolState,
);
expect(await botDetector.isRunningOnBot, isTrue); expect(await botDetector.isRunningOnBot, isTrue);
expect(await BotDetector( expect(await BotDetector(
platform: fakePlatform, platform: fakePlatform,
httpClientFactory: () => mockHttpClient, httpClientFactory: () => FakeHttpClient.list(<FakeRequest>[]),
persistentToolState: persistentToolState, persistentToolState: persistentToolState,
).isRunningOnBot, isTrue); ).isRunningOnBot, isTrue);
verify(mockHttpClient.getUrl(any)).called(1);
}); });
testWithoutContext('returns true when running on borg', () async { testWithoutContext('returns true when running on borg', () async {
fakePlatform.environment['BORG_ALLOC_DIR'] = 'true'; fakePlatform.environment['BORG_ALLOC_DIR'] = 'true';
final BotDetector botDetector = BotDetector(
platform: fakePlatform,
httpClientFactory: () => FakeHttpClient.any(),
persistentToolState: persistentToolState,
);
expect(await botDetector.isRunningOnBot, isTrue); expect(await botDetector.isRunningOnBot, isTrue);
expect(persistentToolState.isRunningOnBot, isTrue); expect(persistentToolState.isRunningOnBot, isTrue);
}); });
...@@ -115,60 +133,34 @@ void main() { ...@@ -115,60 +133,34 @@ void main() {
}); });
group('AzureDetector', () { group('AzureDetector', () {
AzureDetector azureDetector;
MockHttpClient mockHttpClient;
MockHttpClientRequest mockHttpClientRequest;
MockHttpHeaders mockHttpHeaders;
setUp(() {
mockHttpClient = MockHttpClient();
mockHttpClientRequest = MockHttpClientRequest();
mockHttpHeaders = MockHttpHeaders();
azureDetector = AzureDetector(
httpClientFactory: () => mockHttpClient,
);
});
testWithoutContext('isRunningOnAzure returns false when connection times out', () async { testWithoutContext('isRunningOnAzure returns false when connection times out', () async {
when(mockHttpClient.getUrl(any)).thenAnswer((_) { final AzureDetector azureDetector = AzureDetector(
throw const SocketException('HTTP connection timed out'); httpClientFactory: () => FakeHttpClient.list(<FakeRequest>[
}); FakeRequest(azureUrl, responseError: const SocketException('HTTP connection timed out')),
],
));
expect(await azureDetector.isRunningOnAzure, isFalse); expect(await azureDetector.isRunningOnAzure, isFalse);
}); });
testWithoutContext('isRunningOnAzure returns false when the http request times out', () {
FakeAsync().run((FakeAsync time) async {
when(mockHttpClient.getUrl(any)).thenAnswer((_) {
final Completer<HttpClientRequest> completer = Completer<HttpClientRequest>();
return completer.future; // Never completed to test timeout behavior.
});
final Future<bool> onBot = azureDetector.isRunningOnAzure;
time.elapse(const Duration(seconds: 2));
expect(await onBot, isFalse);
});
});
testWithoutContext('isRunningOnAzure returns false when OsError is thrown', () async { testWithoutContext('isRunningOnAzure returns false when OsError is thrown', () async {
when(mockHttpClient.getUrl(any)).thenAnswer((_) { final AzureDetector azureDetector = AzureDetector(
throw const OSError('Connection Refused', 111); httpClientFactory: () => FakeHttpClient.list(<FakeRequest>[
}); FakeRequest(azureUrl, responseError: const OSError('Connection Refused', 111)),
],
));
expect(await azureDetector.isRunningOnAzure, isFalse); expect(await azureDetector.isRunningOnAzure, isFalse);
}); });
testWithoutContext('isRunningOnAzure returns true when azure metadata is reachable', () async { testWithoutContext('isRunningOnAzure returns true when azure metadata is reachable', () async {
when(mockHttpClient.getUrl(any)).thenAnswer((_) { final AzureDetector azureDetector = AzureDetector(
return Future<HttpClientRequest>.value(mockHttpClientRequest); httpClientFactory: () => FakeHttpClient.list(<FakeRequest>[
}); FakeRequest(azureUrl),
when(mockHttpClientRequest.headers).thenReturn(mockHttpHeaders); ],
));
expect(await azureDetector.isRunningOnAzure, isTrue); expect(await azureDetector.isRunningOnAzure, isTrue);
}); });
}); });
} }
class MockHttpClient extends Mock implements HttpClient {}
class MockHttpClientRequest extends Mock implements HttpClientRequest {}
class MockHttpHeaders extends Mock implements HttpHeaders {}
...@@ -20,7 +20,7 @@ import 'package:http/testing.dart'; ...@@ -20,7 +20,7 @@ import 'package:http/testing.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
import '../src/testbed.dart'; import '../src/fake_http_client.dart';
void main() { void main() {
BufferLogger logger; BufferLogger logger;
...@@ -79,7 +79,7 @@ void main() { ...@@ -79,7 +79,7 @@ void main() {
fileSystem: fs, fileSystem: fs,
logger: logger, logger: logger,
flutterProjectFactory: FlutterProjectFactory(fileSystem: fs, logger: logger), flutterProjectFactory: FlutterProjectFactory(fileSystem: fs, logger: logger),
client: FakeHttpClient(), client: FakeHttpClient.any(),
); );
final File file = fs.file('flutter_00.log'); final File file = fs.file('flutter_00.log');
......
...@@ -22,6 +22,7 @@ import 'package:package_config/package_config.dart'; ...@@ -22,6 +22,7 @@ import 'package:package_config/package_config.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
import '../src/fake_http_client.dart';
final FakeVmServiceRequest createDevFSRequest = FakeVmServiceRequest( final FakeVmServiceRequest createDevFSRequest = FakeVmServiceRequest(
method: '_createDevFS', method: '_createDevFS',
...@@ -110,7 +111,6 @@ void main() { ...@@ -110,7 +111,6 @@ void main() {
}); });
testWithoutContext('DevFS create throws a DevFSException when vmservice disconnects unexpectedly', () async { testWithoutContext('DevFS create throws a DevFSException when vmservice disconnects unexpectedly', () async {
final HttpClient httpClient = MockHttpClient();
final FileSystem fileSystem = MemoryFileSystem.test(); final FileSystem fileSystem = MemoryFileSystem.test();
final OperatingSystemUtils osUtils = MockOperatingSystemUtils(); final OperatingSystemUtils osUtils = MockOperatingSystemUtils();
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
...@@ -118,16 +118,6 @@ void main() { ...@@ -118,16 +118,6 @@ void main() {
); );
setHttpAddress(Uri.parse('http://localhost'), fakeVmServiceHost.vmService); setHttpAddress(Uri.parse('http://localhost'), fakeVmServiceHost.vmService);
final MockHttpClientRequest httpRequest = MockHttpClientRequest();
when(httpRequest.headers).thenReturn(MockHttpHeaders());
when(httpClient.putUrl(any)).thenAnswer((Invocation invocation) {
return Future<HttpClientRequest>.value(httpRequest);
});
final MockHttpClientResponse httpClientResponse = MockHttpClientResponse();
when(httpRequest.close()).thenAnswer((Invocation invocation) {
return Future<HttpClientResponse>.value(httpClientResponse);
});
final DevFS devFS = DevFS( final DevFS devFS = DevFS(
fakeVmServiceHost.vmService, fakeVmServiceHost.vmService,
'test', 'test',
...@@ -135,13 +125,12 @@ void main() { ...@@ -135,13 +125,12 @@ void main() {
osUtils: osUtils, osUtils: osUtils,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
httpClient: httpClient, httpClient: FakeHttpClient.any(),
); );
expect(() async => await devFS.create(), throwsA(isA<DevFSException>())); expect(() async => await devFS.create(), throwsA(isA<DevFSException>()));
}); });
testWithoutContext('DevFS destroy is resiliant to vmservice disconnection', () async { testWithoutContext('DevFS destroy is resilient to vmservice disconnection', () async {
final HttpClient httpClient = MockHttpClient();
final FileSystem fileSystem = MemoryFileSystem.test(); final FileSystem fileSystem = MemoryFileSystem.test();
final OperatingSystemUtils osUtils = MockOperatingSystemUtils(); final OperatingSystemUtils osUtils = MockOperatingSystemUtils();
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
...@@ -152,15 +141,6 @@ void main() { ...@@ -152,15 +141,6 @@ void main() {
); );
setHttpAddress(Uri.parse('http://localhost'), fakeVmServiceHost.vmService); setHttpAddress(Uri.parse('http://localhost'), fakeVmServiceHost.vmService);
final MockHttpClientRequest httpRequest = MockHttpClientRequest();
when(httpRequest.headers).thenReturn(MockHttpHeaders());
when(httpClient.putUrl(any)).thenAnswer((Invocation invocation) {
return Future<HttpClientRequest>.value(httpRequest);
});
final MockHttpClientResponse httpClientResponse = MockHttpClientResponse();
when(httpRequest.close()).thenAnswer((Invocation invocation) {
return Future<HttpClientResponse>.value(httpClientResponse);
});
final DevFS devFS = DevFS( final DevFS devFS = DevFS(
fakeVmServiceHost.vmService, fakeVmServiceHost.vmService,
...@@ -169,7 +149,7 @@ void main() { ...@@ -169,7 +149,7 @@ void main() {
osUtils: osUtils, osUtils: osUtils,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
httpClient: httpClient, httpClient: FakeHttpClient.any(),
); );
expect(await devFS.create(), isNotNull); expect(await devFS.create(), isNotNull);
...@@ -177,7 +157,6 @@ void main() { ...@@ -177,7 +157,6 @@ void main() {
}); });
testWithoutContext('DevFS retries uploads when connection reset by peer', () async { testWithoutContext('DevFS retries uploads when connection reset by peer', () async {
final HttpClient httpClient = MockHttpClient();
final FileSystem fileSystem = MemoryFileSystem.test(); final FileSystem fileSystem = MemoryFileSystem.test();
final OperatingSystemUtils osUtils = MockOperatingSystemUtils(); final OperatingSystemUtils osUtils = MockOperatingSystemUtils();
final MockResidentCompiler residentCompiler = MockResidentCompiler(); final MockResidentCompiler residentCompiler = MockResidentCompiler();
...@@ -186,21 +165,6 @@ void main() { ...@@ -186,21 +165,6 @@ void main() {
); );
setHttpAddress(Uri.parse('http://localhost'), fakeVmServiceHost.vmService); setHttpAddress(Uri.parse('http://localhost'), fakeVmServiceHost.vmService);
final MockHttpClientRequest httpRequest = MockHttpClientRequest();
when(httpRequest.headers).thenReturn(MockHttpHeaders());
when(httpClient.putUrl(any)).thenAnswer((Invocation invocation) {
return Future<HttpClientRequest>.value(httpRequest);
});
final MockHttpClientResponse httpClientResponse = MockHttpClientResponse();
int nRequest = 0;
const int kFailedAttempts = 5;
when(httpRequest.close()).thenAnswer((Invocation invocation) {
if (nRequest++ < kFailedAttempts) {
throw const OSError('Connection Reset by peer');
}
return Future<HttpClientResponse>.value(httpClientResponse);
});
when(residentCompiler.recompile( when(residentCompiler.recompile(
any, any,
any, any,
...@@ -220,7 +184,14 @@ void main() { ...@@ -220,7 +184,14 @@ void main() {
osUtils: osUtils, osUtils: osUtils,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
httpClient: httpClient, httpClient: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse('http://localhost'), method: HttpMethod.put, responseError: const OSError('Connection Reset by peer')),
FakeRequest(Uri.parse('http://localhost'), method: HttpMethod.put, responseError: const OSError('Connection Reset by peer')),
FakeRequest(Uri.parse('http://localhost'), method: HttpMethod.put, responseError: const OSError('Connection Reset by peer')),
FakeRequest(Uri.parse('http://localhost'), method: HttpMethod.put, responseError: const OSError('Connection Reset by peer')),
FakeRequest(Uri.parse('http://localhost'), method: HttpMethod.put, responseError: const OSError('Connection Reset by peer')),
FakeRequest(Uri.parse('http://localhost'), method: HttpMethod.put)
]),
uploadRetryThrottle: Duration.zero, uploadRetryThrottle: Duration.zero,
); );
await devFS.create(); await devFS.create();
...@@ -237,9 +208,7 @@ void main() { ...@@ -237,9 +208,7 @@ void main() {
expect(report.syncedBytes, 5); expect(report.syncedBytes, 5);
expect(report.success, isTrue); expect(report.success, isTrue);
verify(httpClient.putUrl(any)).called(kFailedAttempts + 1); verify(osUtils.gzipLevel1Stream(any)).called(6);
verify(httpRequest.close()).called(kFailedAttempts + 1);
verify(osUtils.gzipLevel1Stream(any)).called(kFailedAttempts + 1);
}); });
testWithoutContext('DevFS reports unsuccessful compile when errors are returned', () async { testWithoutContext('DevFS reports unsuccessful compile when errors are returned', () async {
...@@ -255,7 +224,7 @@ void main() { ...@@ -255,7 +224,7 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
httpClient: MockHttpClient(), httpClient: FakeHttpClient.any(),
); );
await devFS.create(); await devFS.create();
...@@ -290,16 +259,6 @@ void main() { ...@@ -290,16 +259,6 @@ void main() {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[createDevFSRequest], requests: <VmServiceExpectation>[createDevFSRequest],
); );
final HttpClient httpClient = MockHttpClient();
final MockHttpClientRequest httpRequest = MockHttpClientRequest();
when(httpRequest.headers).thenReturn(MockHttpHeaders());
when(httpClient.putUrl(any)).thenAnswer((Invocation invocation) {
return Future<HttpClientRequest>.value(httpRequest);
});
final MockHttpClientResponse httpClientResponse = MockHttpClientResponse();
when(httpRequest.close()).thenAnswer((Invocation invocation) async {
return httpClientResponse;
});
final DevFS devFS = DevFS( final DevFS devFS = DevFS(
fakeVmServiceHost.vmService, fakeVmServiceHost.vmService,
...@@ -308,7 +267,7 @@ void main() { ...@@ -308,7 +267,7 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
httpClient: httpClient, httpClient: FakeHttpClient.any(),
); );
await devFS.create(); await devFS.create();
...@@ -407,7 +366,7 @@ void main() { ...@@ -407,7 +366,7 @@ void main() {
fileSystem: fileSystem, fileSystem: fileSystem,
logger: BufferLogger.test(), logger: BufferLogger.test(),
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
httpClient: MockHttpClient(), httpClient: FakeHttpClient.any(),
); );
await devFS.create(); await devFS.create();
...@@ -469,9 +428,6 @@ void main() { ...@@ -469,9 +428,6 @@ void main() {
}); });
} }
class MockHttpClientRequest extends Mock implements HttpClientRequest {}
class MockHttpHeaders extends Mock implements HttpHeaders {}
class MockHttpClientResponse extends Mock implements HttpClientResponse {}
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {} class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
class MockResidentCompiler extends Mock implements ResidentCompiler {} class MockResidentCompiler extends Mock implements ResidentCompiler {}
class MockFile extends Mock implements File {} class MockFile extends Mock implements File {}
......
...@@ -14,6 +14,7 @@ import 'package:flutter_tools/src/reporting/reporting.dart'; ...@@ -14,6 +14,7 @@ import 'package:flutter_tools/src/reporting/reporting.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
import '../src/fake_http_client.dart';
import '../src/testbed.dart'; import '../src/testbed.dart';
const String _kShortURL = 'https://www.example.com/short'; const String _kShortURL = 'https://www.example.com/short';
...@@ -161,7 +162,14 @@ void main() { ...@@ -161,7 +162,14 @@ void main() {
final GitHubTemplateCreator creator = GitHubTemplateCreator( final GitHubTemplateCreator creator = GitHubTemplateCreator(
fileSystem: fs, fileSystem: fs,
logger: logger, logger: logger,
client: SuccessShortenURLFakeHttpClient(), client: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse('https://git.io'), method: HttpMethod.post, response: const FakeResponse(
statusCode: 201,
headers: <String, List<String>>{
HttpHeaders.locationHeader: <String>[_kShortURL],
}
))
]),
flutterProjectFactory: FlutterProjectFactory( flutterProjectFactory: FlutterProjectFactory(
fileSystem: fs, fileSystem: fs,
logger: logger, logger: logger,
...@@ -180,7 +188,11 @@ void main() { ...@@ -180,7 +188,11 @@ void main() {
final GitHubTemplateCreator creator = GitHubTemplateCreator( final GitHubTemplateCreator creator = GitHubTemplateCreator(
fileSystem: fs, fileSystem: fs,
logger: logger, logger: logger,
client: FakeHttpClient(), client: FakeHttpClient.list(<FakeRequest>[
FakeRequest(Uri.parse('https://git.io'), method: HttpMethod.post, response: const FakeResponse(
statusCode: 500,
))
]),
flutterProjectFactory: FlutterProjectFactory( flutterProjectFactory: FlutterProjectFactory(
fileSystem: fs, fileSystem: fs,
logger: logger, logger: logger,
...@@ -206,7 +218,7 @@ void main() { ...@@ -206,7 +218,7 @@ void main() {
final GitHubTemplateCreator creator = GitHubTemplateCreator( final GitHubTemplateCreator creator = GitHubTemplateCreator(
fileSystem: fs, fileSystem: fs,
logger: logger, logger: logger,
client: FakeHttpClient(), client: FakeHttpClient.any(),
flutterProjectFactory: FlutterProjectFactory( flutterProjectFactory: FlutterProjectFactory(
fileSystem: fs, fileSystem: fs,
logger: logger, logger: logger,
...@@ -292,37 +304,7 @@ device_info-0.4.1+4 ...@@ -292,37 +304,7 @@ device_info-0.4.1+4
}); });
} }
class FakeError extends Error {
class SuccessFakeHttpHeaders extends FakeHttpHeaders {
@override
List<String> operator [](String name) => <String>[_kShortURL];
}
class SuccessFakeHttpClientResponse extends FakeHttpClientResponse {
@override
int get statusCode => 201;
@override
HttpHeaders get headers {
return SuccessFakeHttpHeaders();
}
}
class SuccessFakeHttpClientRequest extends FakeHttpClientRequest {
@override
Future<HttpClientResponse> close() async {
return SuccessFakeHttpClientResponse();
}
}
class SuccessShortenURLFakeHttpClient extends FakeHttpClient {
@override
Future<HttpClientRequest> postUrl(Uri url) async {
return SuccessFakeHttpClientRequest();
}
}
class FakeError implements Error {
@override @override
StackTrace get stackTrace => StackTrace.fromString(''' StackTrace get stackTrace => StackTrace.fromString('''
#0 _File.open.<anonymous closure> (dart:io/file_impl.dart:366:9) #0 _File.open.<anonymous closure> (dart:io/file_impl.dart:366:9)
......
...@@ -69,7 +69,7 @@ void main() { ...@@ -69,7 +69,7 @@ void main() {
final HttpClientRequest request = await client.getUrl(null); final HttpClientRequest request = await client.getUrl(null);
final HttpClientResponse response = await request.close(); final HttpClientResponse response = await request.close();
expect(response.statusCode, HttpStatus.badRequest); expect(response.statusCode, HttpStatus.ok);
expect(response.contentLength, 0); expect(response.contentLength, 0);
}); });
}); });
......
...@@ -37,6 +37,7 @@ import 'package:meta/meta.dart'; ...@@ -37,6 +37,7 @@ import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'common.dart'; import 'common.dart';
import 'fake_http_client.dart';
import 'fake_process_manager.dart'; import 'fake_process_manager.dart';
import 'fakes.dart'; import 'fakes.dart';
import 'throwing_pub.dart'; import 'throwing_pub.dart';
...@@ -112,7 +113,7 @@ void testUsingContext( ...@@ -112,7 +113,7 @@ void testUsingContext(
DeviceManager: () => FakeDeviceManager(), DeviceManager: () => FakeDeviceManager(),
Doctor: () => FakeDoctor(globals.logger), Doctor: () => FakeDoctor(globals.logger),
FlutterVersion: () => MockFlutterVersion(), FlutterVersion: () => MockFlutterVersion(),
HttpClient: () => MockHttpClient(), HttpClient: () => FakeHttpClient.any(),
IOSSimulatorUtils: () { IOSSimulatorUtils: () {
final MockIOSSimulatorUtils mock = MockIOSSimulatorUtils(); final MockIOSSimulatorUtils mock = MockIOSSimulatorUtils();
when(mock.getAttachedDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]); when(mock.getAttachedDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]);
...@@ -378,8 +379,6 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter { ...@@ -378,8 +379,6 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter {
class MockFlutterVersion extends Mock implements FlutterVersion {} class MockFlutterVersion extends Mock implements FlutterVersion {}
class MockHttpClient extends Mock implements HttpClient {}
class MockCrashReporter extends Mock implements CrashReporter {} class MockCrashReporter extends Mock implements CrashReporter {}
class LocalFileSystemBlockingSetCurrentDirectory extends LocalFileSystem { class LocalFileSystemBlockingSetCurrentDirectory extends LocalFileSystem {
......
This diff is collapsed.
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