mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

New feature in upcoming Dart 3.2. See https://github.com/dart-lang/language/issues/2020. Feature is enabled by bumping the min SDK version to 3.2. Part of https://github.com/flutter/flutter/issues/134476.
144 lines
5.8 KiB
Dart
144 lines
5.8 KiB
Dart
// Copyright 2014 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 'dart:io';
|
|
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/base/context.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/os.dart';
|
|
import 'package:flutter_tools/src/base/process.dart';
|
|
import 'package:flutter_tools/src/base/signals.dart';
|
|
import 'package:flutter_tools/src/base/terminal.dart';
|
|
import 'package:flutter_tools/src/cache.dart';
|
|
import 'package:flutter_tools/src/context_runner.dart';
|
|
import 'package:flutter_tools/src/dart/pub.dart';
|
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
|
import 'package:flutter_tools/src/reporting/reporting.dart';
|
|
import 'package:flutter_tools/src/version.dart';
|
|
|
|
import 'context.dart';
|
|
import 'fake_http_client.dart';
|
|
import 'fakes.dart';
|
|
import 'throwing_pub.dart';
|
|
|
|
export 'package:flutter_tools/src/base/context.dart' show Generator;
|
|
|
|
// A default value should be provided if the vast majority of tests should use
|
|
// this provider. For example, [BufferLogger], [MemoryFileSystem].
|
|
final Map<Type, Generator> _testbedDefaults = <Type, Generator>{
|
|
// Keeps tests fast by avoiding the actual file system.
|
|
FileSystem: () => MemoryFileSystem(style: globals.platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix),
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
Logger: () => BufferLogger(
|
|
terminal: AnsiTerminal(stdio: globals.stdio, platform: globals.platform), // Danger, using real stdio.
|
|
outputPreferences: OutputPreferences.test(),
|
|
), // Allows reading logs and prevents stdout.
|
|
OperatingSystemUtils: () => FakeOperatingSystemUtils(),
|
|
OutputPreferences: () => OutputPreferences.test(), // configures BufferLogger to avoid color codes.
|
|
Usage: () => TestUsage(), // prevent addition of analytics from burdening test mocks
|
|
FlutterVersion: () => FakeFlutterVersion(), // prevent requirement to mock git for test runner.
|
|
Signals: () => FakeSignals(), // prevent registering actual signal handlers.
|
|
Pub: () => ThrowingPub(), // prevent accidental invocations of pub.
|
|
};
|
|
|
|
/// Manages interaction with the tool injection and runner system.
|
|
///
|
|
/// The Testbed automatically injects reasonable defaults through the context
|
|
/// DI system such as a [BufferLogger] and a [MemoryFileSystem].
|
|
///
|
|
/// Example:
|
|
///
|
|
/// Testing that a filesystem operation works as expected:
|
|
///
|
|
/// void main() {
|
|
/// group('Example', () {
|
|
/// Testbed testbed;
|
|
///
|
|
/// setUp(() {
|
|
/// testbed = Testbed(setUp: () {
|
|
/// globals.fs.file('foo').createSync()
|
|
/// });
|
|
/// })
|
|
///
|
|
/// test('Can delete a file', () => testbed.run(() {
|
|
/// expect(globals.fs.file('foo').existsSync(), true);
|
|
/// globals.fs.file('foo').deleteSync();
|
|
/// expect(globals.fs.file('foo').existsSync(), false);
|
|
/// }));
|
|
/// });
|
|
/// }
|
|
///
|
|
/// For a more detailed example, see the code in test_compiler_test.dart.
|
|
class Testbed {
|
|
/// Creates a new [TestBed]
|
|
///
|
|
/// `overrides` provides more overrides in addition to the test defaults.
|
|
/// `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;
|
|
|
|
final FutureOr<void> Function()? _setup;
|
|
final Map<Type, Generator>? _overrides;
|
|
|
|
/// Runs `test` within a tool zone.
|
|
///
|
|
/// `overrides` may be used to provide new context values for the single test
|
|
/// case or override any context values from the setup.
|
|
Future<T?> run<T>(FutureOr<T> Function() test, {Map<Type, Generator>? overrides}) {
|
|
final Map<Type, Generator> testOverrides = <Type, Generator>{
|
|
..._testbedDefaults,
|
|
// Add the initial setUp overrides
|
|
...?_overrides,
|
|
// Add the test-specific overrides
|
|
...?overrides,
|
|
};
|
|
if (testOverrides.containsKey(ProcessUtils)) {
|
|
throw StateError('Do not inject ProcessUtils for testing, use ProcessManager instead.');
|
|
}
|
|
// Cache the original flutter root to restore after the test case.
|
|
final String? originalFlutterRoot = Cache.flutterRoot;
|
|
// 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.call();
|
|
}
|
|
await test();
|
|
Cache.flutterRoot = originalFlutterRoot;
|
|
for (final 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.any());
|
|
}
|
|
}
|