Unverified Commit 96320ae7 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] remove mocks from devFS test (#82471)

parent 344f3ab7
...@@ -14,13 +14,15 @@ import 'package:flutter_tools/src/base/file_system.dart'; ...@@ -14,13 +14,15 @@ import 'package:flutter_tools/src/base/file_system.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/os.dart'; import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/vmservice.dart'; import 'package:flutter_tools/src/vmservice.dart';
import 'package:mockito/mockito.dart';
import 'package:package_config/package_config.dart'; import 'package:package_config/package_config.dart';
import 'package:test/fake.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart';
import '../src/fake_http_client.dart'; import '../src/fake_http_client.dart';
import '../src/fake_vm_services.dart'; import '../src/fake_vm_services.dart';
import '../src/fakes.dart'; import '../src/fakes.dart';
...@@ -123,7 +125,7 @@ void main() { ...@@ -123,7 +125,7 @@ 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 FileSystem fileSystem = MemoryFileSystem.test(); final FileSystem fileSystem = MemoryFileSystem.test();
final OperatingSystemUtils osUtils = MockOperatingSystemUtils(); final OperatingSystemUtils osUtils = FakeOperatingSystemUtils();
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[failingCreateDevFSRequest], requests: <VmServiceExpectation>[failingCreateDevFSRequest],
httpAddress: Uri.parse('http://localhost'), httpAddress: Uri.parse('http://localhost'),
...@@ -143,7 +145,7 @@ void main() { ...@@ -143,7 +145,7 @@ void main() {
testWithoutContext('DevFS destroy is resilient to vmservice disconnection', () async { testWithoutContext('DevFS destroy is resilient to vmservice disconnection', () async {
final FileSystem fileSystem = MemoryFileSystem.test(); final FileSystem fileSystem = MemoryFileSystem.test();
final OperatingSystemUtils osUtils = MockOperatingSystemUtils(); final OperatingSystemUtils osUtils = FakeOperatingSystemUtils();
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[ requests: <VmServiceExpectation>[
createDevFSRequest, createDevFSRequest,
...@@ -168,26 +170,28 @@ void main() { ...@@ -168,26 +170,28 @@ void main() {
testWithoutContext('DevFS retries uploads when connection reset by peer', () async { testWithoutContext('DevFS retries uploads when connection reset by peer', () async {
final FileSystem fileSystem = MemoryFileSystem.test(); final FileSystem fileSystem = MemoryFileSystem.test();
final OperatingSystemUtils osUtils = MockOperatingSystemUtils(); final OperatingSystemUtils osUtils = OperatingSystemUtils(
final MockResidentCompiler residentCompiler = MockResidentCompiler(); fileSystem: fileSystem,
platform: FakePlatform(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
);
final FakeResidentCompiler residentCompiler = FakeResidentCompiler();
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[createDevFSRequest], requests: <VmServiceExpectation>[createDevFSRequest],
httpAddress: Uri.parse('http://localhost'), httpAddress: Uri.parse('http://localhost'),
); );
residentCompiler.onRecompile = (Uri mainUri, List<Uri> invalidatedFiles) async {
when(residentCompiler.recompile(
any,
any,
outputPath: anyNamed('outputPath'),
packageConfig: anyNamed('packageConfig'),
projectRootPath: anyNamed('projectRootPath'),
fs: anyNamed('fs'),
)).thenAnswer((Invocation invocation) async {
fileSystem.file('lib/foo.dill') fileSystem.file('lib/foo.dill')
..createSync(recursive: true) ..createSync(recursive: true)
..writeAsBytesSync(<int>[1, 2, 3, 4, 5]); ..writeAsBytesSync(<int>[1, 2, 3, 4, 5]);
return const CompilerOutput('lib/foo.dill', 0, <Uri>[]); return const CompilerOutput('lib/foo.dill', 0, <Uri>[]);
}); };
/// This output can change based on the host platform.
final List<List<int>> expectedEncoded = await osUtils.gzipLevel1Stream(
Stream<List<int>>.value(<int>[1, 2, 3, 4, 5]),
).toList();
final DevFS devFS = DevFS( final DevFS devFS = DevFS(
fakeVmServiceHost.vmService, fakeVmServiceHost.vmService,
...@@ -202,7 +206,8 @@ void main() { ...@@ -202,7 +206,8 @@ void main() {
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, responseError: const OSError('Connection Reset by peer')),
FakeRequest(Uri.parse('http://localhost'), method: HttpMethod.put) // This is the value of `<int>[1, 2, 3, 4, 5]` run through `osUtils.gzipLevel1Stream`.
FakeRequest(Uri.parse('http://localhost'), method: HttpMethod.put, body: <int>[for (List<int> chunk in expectedEncoded) ...chunk])
]), ]),
uploadRetryThrottle: Duration.zero, uploadRetryThrottle: Duration.zero,
); );
...@@ -220,7 +225,6 @@ void main() { ...@@ -220,7 +225,6 @@ void main() {
expect(report.syncedBytes, 5); expect(report.syncedBytes, 5);
expect(report.success, isTrue); expect(report.success, isTrue);
verify(osUtils.gzipLevel1Stream(any)).called(6);
}); });
testWithoutContext('DevFS reports unsuccessful compile when errors are returned', () async { testWithoutContext('DevFS reports unsuccessful compile when errors are returned', () async {
...@@ -243,17 +247,10 @@ void main() { ...@@ -243,17 +247,10 @@ void main() {
await devFS.create(); await devFS.create();
final DateTime previousCompile = devFS.lastCompiled; final DateTime previousCompile = devFS.lastCompiled;
final MockResidentCompiler residentCompiler = MockResidentCompiler(); final FakeResidentCompiler residentCompiler = FakeResidentCompiler();
when(residentCompiler.recompile( residentCompiler.onRecompile = (Uri mainUri, List<Uri> invalidatedFiles) async {
any,
any,
outputPath: anyNamed('outputPath'),
packageConfig: anyNamed('packageConfig'),
projectRootPath: anyNamed('projectRootPath'),
fs: anyNamed('fs'),
)).thenAnswer((Invocation invocation) async {
return const CompilerOutput('lib/foo.dill', 2, <Uri>[]); return const CompilerOutput('lib/foo.dill', 2, <Uri>[]);
}); };
final UpdateFSReport report = await devFS.update( final UpdateFSReport report = await devFS.update(
mainUri: Uri.parse('lib/foo.txt'), mainUri: Uri.parse('lib/foo.txt'),
...@@ -289,18 +286,11 @@ void main() { ...@@ -289,18 +286,11 @@ void main() {
await devFS.create(); await devFS.create();
final DateTime previousCompile = devFS.lastCompiled; final DateTime previousCompile = devFS.lastCompiled;
final MockResidentCompiler residentCompiler = MockResidentCompiler(); final FakeResidentCompiler residentCompiler = FakeResidentCompiler();
when(residentCompiler.recompile( residentCompiler.onRecompile = (Uri mainUri, List<Uri> invalidatedFiles) async {
any, fileSystem.file('lib/foo.txt.dill').createSync(recursive: true);
any,
outputPath: anyNamed('outputPath'),
packageConfig: anyNamed('packageConfig'),
projectRootPath: anyNamed('projectRootPath'),
fs: anyNamed('fs'),
)).thenAnswer((Invocation invocation) async {
fileSystem.file('example').createSync();
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]); return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
}); };
final UpdateFSReport report = await devFS.update( final UpdateFSReport report = await devFS.update(
mainUri: Uri.parse('lib/main.dart'), mainUri: Uri.parse('lib/main.dart'),
...@@ -337,18 +327,11 @@ void main() { ...@@ -337,18 +327,11 @@ void main() {
await devFS.create(); await devFS.create();
final DateTime previousCompile = devFS.lastCompiled; final DateTime previousCompile = devFS.lastCompiled;
final MockResidentCompiler residentCompiler = MockResidentCompiler(); final FakeResidentCompiler residentCompiler = FakeResidentCompiler();
when(residentCompiler.recompile( residentCompiler.onRecompile = (Uri mainUri, List<Uri> invalidatedFiles) async {
any,
any,
outputPath: anyNamed('outputPath'),
packageConfig: anyNamed('packageConfig'),
projectRootPath: anyNamed('projectRootPath'),
fs: anyNamed('fs'),
)).thenAnswer((Invocation invocation) async {
fileSystem.file('lib/foo.txt.dill').createSync(recursive: true); fileSystem.file('lib/foo.txt.dill').createSync(recursive: true);
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]); return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
}); };
final UpdateFSReport report = await devFS.update( final UpdateFSReport report = await devFS.update(
mainUri: Uri.parse('lib/main.dart'), mainUri: Uri.parse('lib/main.dart'),
...@@ -391,18 +374,11 @@ void main() { ...@@ -391,18 +374,11 @@ void main() {
await devFS.create(); await devFS.create();
final MockResidentCompiler residentCompiler = MockResidentCompiler(); final FakeResidentCompiler residentCompiler = FakeResidentCompiler();
when(residentCompiler.recompile( residentCompiler.onRecompile = (Uri mainUri, List<Uri> invalidatedFiles) async {
any,
any,
outputPath: anyNamed('outputPath'),
packageConfig: anyNamed('packageConfig'),
projectRootPath: anyNamed('projectRootPath'),
fs: anyNamed('fs'),
)).thenAnswer((Invocation invocation) async {
fileSystem.file('example').createSync(); fileSystem.file('example').createSync();
return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]); return const CompilerOutput('lib/foo.txt.dill', 0, <Uri>[]);
}); };
expect(writer.written, false); expect(writer.written, false);
...@@ -439,10 +415,11 @@ void main() { ...@@ -439,10 +415,11 @@ void main() {
}); });
testWithoutContext('Local DevFSWriter turns FileSystemException into DevFSException', () async { testWithoutContext('Local DevFSWriter turns FileSystemException into DevFSException', () async {
final FileSystem fileSystem = MemoryFileSystem.test(); final FileExceptionHandler handler = FileExceptionHandler();
final FileSystem fileSystem = MemoryFileSystem.test(opHandle: handler.opHandle);
final LocalDevFSWriter writer = LocalDevFSWriter(fileSystem: fileSystem); final LocalDevFSWriter writer = LocalDevFSWriter(fileSystem: fileSystem);
final File file = MockFile(); final File file = fileSystem.file('foo');
when(file.copySync(any)).thenThrow(const FileSystemException('foo')); handler.addError(file, FileSystemOp.read, const FileSystemException('foo'));
await expectLater(() async => writer.write(<Uri, DevFSContent>{ await expectLater(() async => writer.write(<Uri, DevFSContent>{
Uri.parse('goodbye'): DevFSFileContent(file), Uri.parse('goodbye'): DevFSFileContent(file),
...@@ -450,9 +427,16 @@ void main() { ...@@ -450,9 +427,16 @@ void main() {
}); });
} }
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {} class FakeResidentCompiler extends Fake implements ResidentCompiler {
class MockResidentCompiler extends Mock implements ResidentCompiler {} Future<CompilerOutput> Function(Uri mainUri, List<Uri> invalidatedFiles) onRecompile;
class MockFile extends Mock implements File {}
@override
Future<CompilerOutput> recompile(Uri mainUri, List<Uri> invalidatedFiles, {String outputPath, PackageConfig packageConfig, String projectRootPath, FileSystem fs, bool suppressErrors = false}) {
return onRecompile?.call(mainUri, invalidatedFiles)
?? Future<CompilerOutput>.value(const CompilerOutput('', 1, <Uri>[]));
}
}
class FakeDevFSWriter implements DevFSWriter { class FakeDevFSWriter implements DevFSWriter {
bool written = false; bool written = false;
......
...@@ -7,6 +7,8 @@ import 'dart:convert'; ...@@ -7,6 +7,8 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:collection/collection.dart';
/// The HTTP verb for a [FakeRequest]. /// The HTTP verb for a [FakeRequest].
enum HttpMethod { enum HttpMethod {
get, get,
...@@ -85,12 +87,14 @@ class FakeRequest { ...@@ -85,12 +87,14 @@ class FakeRequest {
this.method = HttpMethod.get, this.method = HttpMethod.get,
this.response = FakeResponse.empty, this.response = FakeResponse.empty,
this.responseError, this.responseError,
this.body,
}); });
final Uri uri; final Uri uri;
final HttpMethod method; final HttpMethod method;
final FakeResponse response; final FakeResponse response;
final Object? responseError; final Object? responseError;
final List<int>? body;
@override @override
String toString() => 'Request{${_toMethodString(method)}, $uri}'; String toString() => 'Request{${_toMethodString(method)}, $uri}';
...@@ -182,7 +186,7 @@ class FakeHttpClient implements HttpClient { ...@@ -182,7 +186,7 @@ class FakeHttpClient implements HttpClient {
@override @override
Future<HttpClientRequest> deleteUrl(Uri url) async { Future<HttpClientRequest> deleteUrl(Uri url) async {
return _findRequest(HttpMethod.delete, url); return _findRequest(HttpMethod.delete, url, StackTrace.current);
} }
@override @override
...@@ -196,7 +200,7 @@ class FakeHttpClient implements HttpClient { ...@@ -196,7 +200,7 @@ class FakeHttpClient implements HttpClient {
@override @override
Future<HttpClientRequest> getUrl(Uri url) async { Future<HttpClientRequest> getUrl(Uri url) async {
return _findRequest(HttpMethod.get, url); return _findRequest(HttpMethod.get, url, StackTrace.current);
} }
@override @override
...@@ -207,7 +211,7 @@ class FakeHttpClient implements HttpClient { ...@@ -207,7 +211,7 @@ class FakeHttpClient implements HttpClient {
@override @override
Future<HttpClientRequest> headUrl(Uri url) async { Future<HttpClientRequest> headUrl(Uri url) async {
return _findRequest(HttpMethod.head, url); return _findRequest(HttpMethod.head, url, StackTrace.current);
} }
@override @override
...@@ -218,7 +222,7 @@ class FakeHttpClient implements HttpClient { ...@@ -218,7 +222,7 @@ class FakeHttpClient implements HttpClient {
@override @override
Future<HttpClientRequest> openUrl(String method, Uri url) async { Future<HttpClientRequest> openUrl(String method, Uri url) async {
return _findRequest(_fromMethodString(method), url); return _findRequest(_fromMethodString(method), url, StackTrace.current);
} }
@override @override
...@@ -229,7 +233,7 @@ class FakeHttpClient implements HttpClient { ...@@ -229,7 +233,7 @@ class FakeHttpClient implements HttpClient {
@override @override
Future<HttpClientRequest> patchUrl(Uri url) async { Future<HttpClientRequest> patchUrl(Uri url) async {
return _findRequest(HttpMethod.patch, url); return _findRequest(HttpMethod.patch, url, StackTrace.current);
} }
@override @override
...@@ -240,7 +244,7 @@ class FakeHttpClient implements HttpClient { ...@@ -240,7 +244,7 @@ class FakeHttpClient implements HttpClient {
@override @override
Future<HttpClientRequest> postUrl(Uri url) async { Future<HttpClientRequest> postUrl(Uri url) async {
return _findRequest(HttpMethod.post, url); return _findRequest(HttpMethod.post, url, StackTrace.current);
} }
@override @override
...@@ -251,12 +255,12 @@ class FakeHttpClient implements HttpClient { ...@@ -251,12 +255,12 @@ class FakeHttpClient implements HttpClient {
@override @override
Future<HttpClientRequest> putUrl(Uri url) async { Future<HttpClientRequest> putUrl(Uri url) async {
return _findRequest(HttpMethod.put, url); return _findRequest(HttpMethod.put, url, StackTrace.current);
} }
int _requestCount = 0; int _requestCount = 0;
_FakeHttpClientRequest _findRequest(HttpMethod method, Uri uri) { _FakeHttpClientRequest _findRequest(HttpMethod method, Uri uri, StackTrace stackTrace) {
// Ensure the fake client throws similar errors to the real client. // Ensure the fake client throws similar errors to the real client.
if (uri.host.isEmpty) { if (uri.host.isEmpty) {
throw ArgumentError('No host specified in URI $uri'); throw ArgumentError('No host specified in URI $uri');
...@@ -270,6 +274,8 @@ class FakeHttpClient implements HttpClient { ...@@ -270,6 +274,8 @@ class FakeHttpClient implements HttpClient {
uri, uri,
methodString, methodString,
null, null,
null,
stackTrace,
); );
} }
FakeRequest? matchedRequest; FakeRequest? matchedRequest;
...@@ -292,17 +298,22 @@ class FakeHttpClient implements HttpClient { ...@@ -292,17 +298,22 @@ class FakeHttpClient implements HttpClient {
uri, uri,
methodString, methodString,
matchedRequest.responseError, matchedRequest.responseError,
matchedRequest.body,
stackTrace,
); );
} }
} }
class _FakeHttpClientRequest implements HttpClientRequest { class _FakeHttpClientRequest implements HttpClientRequest {
_FakeHttpClientRequest(this._response, this._uri, this._method, this._responseError); _FakeHttpClientRequest(this._response, this._uri, this._method, this._responseError, this._expectedBody, this._stackTrace);
final FakeResponse _response; final FakeResponse _response;
final String _method; final String _method;
final Uri _uri; final Uri _uri;
final Object? _responseError; final Object? _responseError;
final List<int> _body = <int>[];
final List<int>? _expectedBody;
final StackTrace _stackTrace;
@override @override
bool bufferOutput = true; bool bufferOutput = true;
...@@ -328,16 +339,33 @@ class _FakeHttpClientRequest implements HttpClientRequest { ...@@ -328,16 +339,33 @@ class _FakeHttpClientRequest implements HttpClientRequest {
} }
@override @override
void add(List<int> data) { } void add(List<int> data) {
_body.addAll(data);
}
@override @override
void addError(Object error, [StackTrace? stackTrace]) { } void addError(Object error, [StackTrace? stackTrace]) { }
@override @override
Future<void> addStream(Stream<List<int>> stream) async { } Future<void> addStream(Stream<List<int>> stream) async {
final Completer<void> completer = Completer<void>();
stream.listen(_body.addAll, onDone: completer.complete);
await completer.future;
}
@override @override
Future<HttpClientResponse> close() async { Future<HttpClientResponse> close() async {
final Completer<void> completer = Completer<void>();
Timer.run(() {
if (_expectedBody != null && !const ListEquality<int>().equals(_expectedBody, _body)) {
completer.completeError(StateError(
'Expected a request with the following body:\n$_expectedBody\n but found:\n$_body'
), _stackTrace);
} else {
completer.complete();
}
});
await completer.future;
if (_responseError != null) { if (_responseError != null) {
return Future<HttpClientResponse>.error(_responseError!); return Future<HttpClientResponse>.error(_responseError!);
} }
...@@ -366,16 +394,24 @@ class _FakeHttpClientRequest implements HttpClientRequest { ...@@ -366,16 +394,24 @@ class _FakeHttpClientRequest implements HttpClientRequest {
Uri get uri => _uri; Uri get uri => _uri;
@override @override
void write(Object? object) { } void write(Object? object) {
_body.addAll(utf8.encode(object.toString()));
}
@override @override
void writeAll(Iterable<dynamic> objects, [String separator = '']) { } void writeAll(Iterable<dynamic> objects, [String separator = '']) {
_body.addAll(utf8.encode(objects.join(separator)));
}
@override @override
void writeCharCode(int charCode) { } void writeCharCode(int charCode) {
_body.add(charCode);
}
@override @override
void writeln([Object? object = '']) { } void writeln([Object? object = '']) {
_body.addAll(utf8.encode(object.toString() + '\n'));
}
} }
class _FakeHttpClientResponse extends Stream<List<int>> implements HttpClientResponse { class _FakeHttpClientResponse extends Stream<List<int>> implements HttpClientResponse {
......
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