blob: 4f43b48127ce3aa065415330dd5bcd51acbe20a8 [file] [log] [blame]
// Copyright 2013 The Flutter 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_migrate/src/base/context.dart';
import 'package:flutter_migrate/src/base/file_system.dart';
import 'package:flutter_migrate/src/base/logger.dart';
import 'package:flutter_migrate/src/base/terminal.dart';
import 'package:meta/meta.dart';
import 'package:process/process.dart';
import 'common.dart';
import 'fakes.dart';
/// Return the test logger. This assumes that the current Logger is a BufferLogger.
BufferLogger get testLogger => context.get<Logger>()! as BufferLogger;
@isTest
void testUsingContext(
String description,
dynamic Function() testMethod, {
Map<Type, Generator> overrides = const <Type, Generator>{},
bool initializeFlutterRoot = true,
String? testOn,
bool?
skip, // should default to `false`, but https://github.com/dart-lang/test/issues/545 doesn't allow this
}) {
if (overrides[FileSystem] != null && overrides[ProcessManager] == null) {
throw StateError(
'If you override the FileSystem context you must also provide a ProcessManager, '
'otherwise the processes you launch will not be dealing with the same file system '
'that you are dealing with in your test.');
}
// Ensure we don't rely on the default [Config] constructor which will
// leak a sticky $HOME/.flutter_settings behind!
Directory? configDir;
tearDown(() {
if (configDir != null) {
tryToDelete(configDir!);
configDir = null;
}
});
test(description, () async {
await runInContext<dynamic>(() {
return context.run<dynamic>(
name: 'mocks',
overrides: <Type, Generator>{
AnsiTerminal: () => AnsiTerminal(stdio: FakeStdio()),
OutputPreferences: () => OutputPreferences.test(),
Logger: () => BufferLogger.test(),
ProcessManager: () => const LocalProcessManager(),
},
body: () {
return runZonedGuarded<Future<dynamic>>(() {
try {
return context.run<dynamic>(
// Apply the overrides to the test context in the zone since their
// instantiation may reference items already stored on the context.
overrides: overrides,
name: 'test-specific overrides',
body: () async {
if (initializeFlutterRoot) {
// Provide a sane default for the flutterRoot directory. Individual
// tests can override this either in the test or during setup.
// Cache.flutterRoot ??= flutterRoot;
}
return await testMethod();
},
);
// This catch rethrows, so doesn't need to catch only Exception.
} catch (error) {
// ignore: avoid_catches_without_on_clauses
_printBufferedErrors(context);
rethrow;
}
}, (Object error, StackTrace stackTrace) {
// When things fail, it's ok to print to the console!
print(error); // ignore: avoid_print
print(stackTrace); // ignore: avoid_print
_printBufferedErrors(context);
throw error; //ignore: only_throw_errors
});
},
);
}, overrides: <Type, Generator>{});
}, testOn: testOn, skip: skip);
// We don't support "timeout"; see ../../dart_test.yaml which
// configures all tests to have a 15 minute timeout which should
// definitely be enough.
}
void _printBufferedErrors(AppContext testContext) {
if (testContext.get<Logger>() is BufferLogger) {
final BufferLogger bufferLogger =
testContext.get<Logger>()! as BufferLogger;
if (bufferLogger.errorText.isNotEmpty) {
// This is where the logger outputting errors is implemented, so it has
// to use `print`.
print(bufferLogger.errorText); // ignore: avoid_print
}
bufferLogger.clear();
}
}
Future<T> runInContext<T>(
FutureOr<T> Function() runner, {
Map<Type, Generator>? overrides,
}) async {
// Wrap runner with any asynchronous initialization that should run with the
// overrides and callbacks.
// late bool runningOnBot;
FutureOr<T> runnerWrapper() async {
return runner();
}
return context.run<T>(
name: 'global fallbacks',
body: runnerWrapper,
overrides: overrides,
fallbacks: <Type, Generator>{});
}