Unverified Commit 34467289 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Add timer checking and Fake http client to testbed (#35392)

parent 49cce984
......@@ -142,6 +142,7 @@ class AppContext {
String name,
Map<Type, Generator> overrides,
Map<Type, Generator> fallbacks,
ZoneSpecification zoneSpecification,
}) async {
final AppContext child = AppContext._(
this,
......@@ -152,6 +153,7 @@ class AppContext {
return await runZoned<Future<V>>(
() async => await body(),
zoneValues: <_Key, AppContext>{_Key.key: child},
zoneSpecification: zoneSpecification,
);
}
......
......@@ -3,8 +3,12 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
......@@ -25,7 +29,9 @@ export 'package:flutter_tools/src/base/context.dart' show Generator;
// - More TBD.
final Map<Type, Generator> _testbedDefaults = <Type, Generator>{
// Keeps tests fast by avoid actual file system.
FileSystem: () => MemoryFileSystem(style: platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix),
FileSystem: () => MemoryFileSystem(style: platform.isWindows
? FileSystemStyle.windows
: FileSystemStyle.posix),
Logger: () => BufferLogger(), // Allows reading logs and prevents stdout.
OutputPreferences: () => OutputPreferences(showColor: false), // configures BufferLogger to avoid color codes.
Usage: () => NoOpUsage(), // prevent addition of analytics from burdening test mocks
......@@ -67,9 +73,8 @@ class Testbed {
/// `setup` may be provided to apply mocks within the tool managed zone,
/// including any specified overrides.
Testbed({FutureOr<void> Function() setup, Map<Type, Generator> overrides})
: _setup = setup,
_overrides = overrides;
: _setup = setup,
_overrides = overrides;
final Future<void> Function() _setup;
final Map<Type, Generator> _overrides;
......@@ -88,21 +93,42 @@ class Testbed {
};
// Cache the original flutter root to restore after the test case.
final String originalFlutterRoot = Cache.flutterRoot;
return runInContext<T>(() {
return context.run<T>(
name: 'testbed',
overrides: testOverrides,
body: () async {
Cache.flutterRoot = '';
if (_setup != null) {
await _setup();
}
await test();
Cache.flutterRoot = originalFlutterRoot;
return null;
}
);
});
// Track pending timers to verify that they were correctly cleaned up.
final Map<Timer, StackTrace> timers = <Timer, StackTrace>{};
return HttpOverrides.runZoned(() {
return runInContext<T>(() {
return context.run<T>(
name: 'testbed',
overrides: testOverrides,
zoneSpecification: ZoneSpecification(
createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void Function() timer) {
final Timer result = parent.createTimer(zone, duration, timer);
timers[result] = StackTrace.current;
return result;
},
createPeriodicTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration period, void Function(Timer) timer) {
final Timer result = parent.createPeriodicTimer(zone, period, timer);
timers[result] = StackTrace.current;
return result;
}
),
body: () async {
Cache.flutterRoot = '';
if (_setup != null) {
await _setup();
}
await test();
Cache.flutterRoot = originalFlutterRoot;
for (MapEntry<Timer, StackTrace> entry in timers.entries) {
if (entry.key.isActive) {
throw StateError('A Timer was active at the end of a test: ${entry.value}');
}
}
return null;
});
});
}, createHttpClient: (SecurityContext c) => FakeHttpClient());
}
}
......@@ -135,12 +161,259 @@ class NoOpUsage implements Usage {
void sendCommand(String command, {Map<String, String> parameters}) {}
@override
void sendEvent(String category, String parameter, {Map<String, String> parameters}) {}
void sendEvent(String category, String parameter,{ Map<String, String> parameters }) {}
@override
void sendException(dynamic exception, StackTrace trace) {}
@override
void sendTiming(String category, String variableName, Duration duration, { String label }) {}
}
class FakeHttpClient implements HttpClient {
@override
bool autoUncompress;
@override
Duration connectionTimeout;
@override
Duration idleTimeout;
@override
int maxConnectionsPerHost;
@override
String userAgent;
@override
void addCredentials(
Uri url, String realm, HttpClientCredentials credentials) {}
@override
void addProxyCredentials(
String host, int port, String realm, HttpClientCredentials credentials) {}
@override
set authenticate(
Future<bool> Function(Uri url, String scheme, String realm) f) {}
@override
set authenticateProxy(
Future<bool> Function(String host, int port, String scheme, String realm)
f) {}
@override
set badCertificateCallback(
bool Function(X509Certificate cert, String host, int port) callback) {}
@override
void close({bool force = false}) {}
@override
Future<HttpClientRequest> delete(String host, int port, String path) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> deleteUrl(Uri url) async {
return FakeHttpClientRequest();
}
@override
set findProxy(String Function(Uri url) f) {}
@override
Future<HttpClientRequest> get(String host, int port, String path) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> getUrl(Uri url) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> head(String host, int port, String path) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> headUrl(Uri url) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> open(String method, String host, int port, String path) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> openUrl(String method, Uri url) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> patch(String host, int port, String path) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> patchUrl(Uri url) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> post(String host, int port, String path) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> postUrl(Uri url) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> put(String host, int port, String path) async {
return FakeHttpClientRequest();
}
@override
Future<HttpClientRequest> putUrl(Uri url) async {
return FakeHttpClientRequest();
}
}
class FakeHttpClientRequest implements HttpClientRequest {
FakeHttpClientRequest();
@override
bool bufferOutput;
@override
int contentLength;
@override
Encoding encoding;
@override
bool followRedirects;
@override
int maxRedirects;
@override
bool persistentConnection;
@override
void add(List<int> data) {}
@override
void addError(Object error, [StackTrace stackTrace]) {}
@override
Future<void> addStream(Stream<List<int>> stream) async {}
@override
Future<HttpClientResponse> close() async {
return FakeHttpClientResponse();
}
@override
HttpConnectionInfo get connectionInfo => null;
@override
List<Cookie> get cookies => <Cookie>[];
@override
Future<HttpClientResponse> get done => null;
@override
Future<void> flush() {
return Future<void>.value();
}
@override
HttpHeaders get headers => null;
@override
String get method => null;
@override
Uri get uri => null;
@override
void write(Object obj) {}
@override
void writeAll(Iterable<Object> objects, [String separator = '']) {}
@override
void writeCharCode(int charCode) {}
@override
void writeln([Object obj = '']) {}
}
class FakeHttpClientResponse extends Stream<Uint8List>
implements HttpClientResponse {
final Stream<List<int>> _content = const Stream<List<int>>.empty();
@override
X509Certificate get certificate => null;
@override
HttpClientResponseCompressionState get compressionState => null;
@override
HttpConnectionInfo get connectionInfo => null;
@override
int get contentLength => 0;
@override
List<Cookie> get cookies => <Cookie>[];
@override
Future<Socket> detachSocket() async {
return null;
}
@override
HttpHeaders get headers => null;
@override
bool get isRedirect => null;
@override
StreamSubscription<Uint8List> listen(void Function(Uint8List event) onData,
{Function onError, void Function() onDone, bool cancelOnError}) {
return _content.listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError
);
}
@override
bool get persistentConnection => false;
@override
String get reasonPhrase => null;
@override
Future<HttpClientResponse> redirect(
[String method, Uri url, bool followLoops]) {
return null;
}
@override
List<RedirectInfo> get redirects => const <RedirectInfo>[];
@override
int get statusCode => HttpStatus.badRequest;
void sendTiming(String category, String variableName, Duration duration, {String label}) {}
}
......
......@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:io';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
......@@ -50,6 +53,35 @@ void main() {
expect(instance, isA<B>());
});
test('provides a mocked http client', () async {
final Testbed testbed = Testbed();
await testbed.run(() async {
final HttpClient client = HttpClient();
final HttpClientRequest request = await client.getUrl(null);
final HttpClientResponse response = await request.close();
expect(response.statusCode, HttpStatus.badRequest);
expect(response.contentLength, 0);
});
});
test('Throws StateError if Timer is left pending', () async {
final Testbed testbed = Testbed();
expect(testbed.run(() async {
Timer.periodic(const Duration(seconds: 1), (Timer timer) { });
}), throwsA(isInstanceOf<StateError>()));
});
test('Doesnt throw a StateError if Timer is left cleaned up', () async {
final Testbed testbed = Testbed();
testbed.run(() async {
final Timer timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) { });
timer.cancel();
});
});
});
}
......
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