Record/replay test infrastructure. (#8597)

This adds the test harness for record/replay tests.
This commit is contained in:
Todd Volkert 2017-03-07 10:13:40 -08:00 committed by GitHub
parent 5e2d3e956a
commit 3d079414ad
4 changed files with 87 additions and 14 deletions

View file

@ -146,8 +146,8 @@ Future<int> run(List<String> args, List<FlutterCommand> subCommands, {
await _exit(0); await _exit(0);
runCompleter.complete(0); runCompleter.complete(0);
}, onError: (dynamic error, Chain chain) { }, onError: (dynamic error, Chain chain) {
flutterVersion ??= FlutterVersion.getVersionString(); String getVersion() => flutterVersion ?? FlutterVersion.getVersionString();
_handleToolError(error, chain, verbose, args, reportCrashes, flutterVersion) _handleToolError(error, chain, verbose, args, reportCrashes, getVersion)
.then(runCompleter.complete, onError: runCompleter.completeError); .then(runCompleter.complete, onError: runCompleter.completeError);
}); });
return runCompleter.future; return runCompleter.future;
@ -160,7 +160,7 @@ Future<int> _handleToolError(
bool verbose, bool verbose,
List<String> args, List<String> args,
bool reportCrashes, bool reportCrashes,
String flutterVersion, String getFlutterVersion(),
) async { ) async {
if (error is UsageException) { if (error is UsageException) {
stderr.writeln(error.message); stderr.writeln(error.message);
@ -208,7 +208,7 @@ Future<int> _handleToolError(
await CrashReportSender.instance.sendReport( await CrashReportSender.instance.sendReport(
error: error, error: error,
stackTrace: chain, stackTrace: chain,
flutterVersion: flutterVersion, flutterVersion: getFlutterVersion(),
); );
try { try {
final File file = await _createLocalCrashReport(args, error, chain); final File file = await _createLocalCrashReport(args, error, chain);

View file

@ -166,6 +166,13 @@ class BufferLogger extends Logger {
printStatus(message); printStatus(message);
return new Status(); return new Status();
} }
/// Clears all buffers.
void clear() {
_error.clear();
_status.clear();
_trace.clear();
}
} }
class VerboseLogger extends Logger { class VerboseLogger extends Logger {

View file

@ -0,0 +1,61 @@
// Copyright 2015 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/executable.dart' as tools;
import 'package:flutter_tools/src/base/io.dart';
import 'package:test/test.dart';
import '../src/common.dart';
import '../src/context.dart';
/// Runs the specified [testMethod] in a minimal `AppContext` that is set up
/// to redirect log output to a `BufferLogger` to avoid spamming `stdout`.
///
/// Test methods will generally want to use [expectProcessExits] in their method
/// bodies.
void testReplay(
String description,
dynamic testMethod(), {
Timeout timeout,
Map<Type, Generator> overrides: const <Type, Generator>{},
bool skip,
}) {
setUp(() {
setExitFunctionForTests();
});
tearDown(() {
restoreExitFunction();
});
testUsingContext(
description,
testMethod,
timeout: timeout,
overrides: overrides,
skip: skip,
initializeContext: (_) {},
);
}
/// Expects that the specified [command] to Flutter tools exits with the
/// specified [exitCode] (defaults to zero). It is expected that callers will
/// be running in a test via [testReplay].
///
/// [command] should be the list of arguments that are passed to the `flutter`
/// command-line tool. For example:
///
/// ```
/// <String>[
/// 'run',
/// '--no-hot',
/// '--no-resident',
/// ]
/// ```
void expectProcessExits(List<String> command, {dynamic exitCode: 0}) {
final Future<Null> mainFuture = tools.main(command);
expect(mainFuture, throwsProcessExit(exitCode));
}

View file

@ -74,7 +74,7 @@ void testUsingContext(String description, dynamic testMethod(), {
final String flutterRoot = getFlutterRoot(); final String flutterRoot = getFlutterRoot();
try { try {
return await testContext.runInZone(() { return await testContext.runInZone(() async {
// Apply the overrides to the test context in the zone since their // Apply the overrides to the test context in the zone since their
// instantiation may reference items already stored on the context. // instantiation may reference items already stored on the context.
overrides.forEach((Type type, dynamic value()) { overrides.forEach((Type type, dynamic value()) {
@ -83,23 +83,28 @@ void testUsingContext(String description, dynamic testMethod(), {
// Provide a sane default for the flutterRoot directory. Individual // Provide a sane default for the flutterRoot directory. Individual
// tests can override this. // tests can override this.
Cache.flutterRoot = flutterRoot; Cache.flutterRoot = flutterRoot;
return testMethod(); return await testMethod();
}, onError: (dynamic error, StackTrace stackTrace) {
_printBufferedErrors(testContext);
throw error;
}); });
} catch (error) { } catch (error) {
if (testContext[Logger] is BufferLogger) { _printBufferedErrors(testContext);
final BufferLogger bufferLogger = testContext[Logger];
if (bufferLogger.errorText.isNotEmpty)
print(bufferLogger.errorText);
}
// Previously the following line read "throw error;". This is bad because
// it drops the error's actual stacktrace. Use 'rethrow' to preserve
// the stacktrace.
rethrow; rethrow;
} }
}, timeout: timeout, skip: skip); }, timeout: timeout, skip: skip);
} }
void _printBufferedErrors(AppContext testContext) {
if (testContext[Logger] is BufferLogger) {
final BufferLogger bufferLogger = testContext[Logger];
if (bufferLogger.errorText.isNotEmpty)
print(bufferLogger.errorText);
bufferLogger.clear();
}
}
String getFlutterRoot() { String getFlutterRoot() {
Error invalidScript() => new StateError('Invalid script: ${platform.script}'); Error invalidScript() => new StateError('Invalid script: ${platform.script}');