1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// Copyright 2016 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/src/artifacts.dart';
import 'package:flutter_tools/src/base/config.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/platform.dart';
import 'package:flutter_tools/src/base/port_scanner.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/ios/simulators.dart';
import 'package:flutter_tools/src/run_hot.dart';
import 'package:flutter_tools/src/usage.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import 'package:quiver/time.dart';
import 'package:test/test.dart';
import 'common.dart';
/// Return the test logger. This assumes that the current Logger is a BufferLogger.
BufferLogger get testLogger => context[Logger];
MockDeviceManager get testDeviceManager => context[DeviceManager];
MockDoctor get testDoctor => context[Doctor];
typedef dynamic Generator();
typedef void ContextInitializer(AppContext testContext);
void _defaultInitializeContext(AppContext testContext) {
testContext
..putIfAbsent(AnsiTerminal, () => new AnsiTerminal())
..putIfAbsent(DeviceManager, () => new MockDeviceManager())
..putIfAbsent(DevFSConfig, () => new DevFSConfig())
..putIfAbsent(Doctor, () => new MockDoctor())
..putIfAbsent(HotRunnerConfig, () => new HotRunnerConfig())
..putIfAbsent(Cache, () => new Cache())
..putIfAbsent(Artifacts, () => new CachedArtifacts())
..putIfAbsent(OperatingSystemUtils, () => new MockOperatingSystemUtils())
..putIfAbsent(PortScanner, () => new MockPortScanner())
..putIfAbsent(Xcode, () => new Xcode())
..putIfAbsent(IOSSimulatorUtils, () {
final MockIOSSimulatorUtils mock = new MockIOSSimulatorUtils();
when(mock.getAttachedDevices()).thenReturn(<IOSSimulator>[]);
return mock;
})
..putIfAbsent(SimControl, () => new MockSimControl())
..putIfAbsent(Usage, () => new MockUsage())
..putIfAbsent(FlutterVersion, () => new MockFlutterVersion())
..putIfAbsent(Clock, () => const Clock());
}
void testUsingContext(String description, dynamic testMethod(), {
Timeout timeout,
Map<Type, Generator> overrides: const <Type, Generator>{},
ContextInitializer initializeContext: _defaultInitializeContext,
bool skip, // should default to `false`, but https://github.com/dart-lang/test/issues/545 doesn't allow this
}) {
test(description, () async {
final AppContext testContext = new AppContext();
// The context always starts with these value since others depend on them.
testContext
..putIfAbsent(Platform, () => const LocalPlatform())
..putIfAbsent(FileSystem, () => const LocalFileSystem())
..putIfAbsent(ProcessManager, () => const LocalProcessManager())
..putIfAbsent(Logger, () => new BufferLogger())
..putIfAbsent(Config, () => new Config());
// Apply the initializer after seeding the base value above.
initializeContext(testContext);
final String flutterRoot = getFlutterRoot();
try {
return await testContext.runInZone(() async {
// Apply the overrides to the test context in the zone since their
// instantiation may reference items already stored on the context.
overrides.forEach((Type type, dynamic value()) {
context.setVariable(type, value());
});
// 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();
}, onError: (dynamic error, StackTrace stackTrace) {
_printBufferedErrors(testContext);
throw error;
});
} catch (error) {
_printBufferedErrors(testContext);
rethrow;
}
}, 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();
}
}
class MockPortScanner extends PortScanner {
static int _nextAvailablePort = 12345;
@override
Future<bool> isPortAvailable(int port) async => true;
@override
Future<int> findAvailablePort() async => _nextAvailablePort++;
}
class MockDeviceManager implements DeviceManager {
List<Device> devices = <Device>[];
String _specifiedDeviceId;
@override
String get specifiedDeviceId {
if (_specifiedDeviceId == null || _specifiedDeviceId == 'all')
return null;
return _specifiedDeviceId;
}
@override
set specifiedDeviceId(String id) {
_specifiedDeviceId = id;
}
@override
bool get hasSpecifiedDeviceId => specifiedDeviceId != null;
@override
bool get hasSpecifiedAllDevices {
return _specifiedDeviceId != null && _specifiedDeviceId == 'all';
}
@override
Stream<Device> getAllConnectedDevices() => new Stream<Device>.fromIterable(devices);
@override
Stream<Device> getDevicesById(String deviceId) {
return new Stream<Device>.fromIterable(
devices.where((Device device) => device.id == deviceId));
}
@override
Stream<Device> getDevices() {
return hasSpecifiedDeviceId
? getDevicesById(specifiedDeviceId)
: getAllConnectedDevices();
}
void addDevice(Device device) => devices.add(device);
}
class MockDoctor extends Doctor {
// True for testing.
@override
bool get canListAnything => true;
// True for testing.
@override
bool get canLaunchAnything => true;
}
class MockSimControl extends Mock implements SimControl {
MockSimControl() {
when(getConnectedDevices()).thenReturn(<SimDevice>[]);
}
}
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {
@override
List<File> whichAll(String execName) => <File>[];
@override
String get name => 'fake OS name and version';
}
class MockIOSSimulatorUtils extends Mock implements IOSSimulatorUtils {}
class MockUsage implements Usage {
@override
bool get isFirstRun => false;
@override
bool get suppressAnalytics => false;
@override
set suppressAnalytics(bool value) { }
@override
bool get enabled => true;
@override
set enabled(bool value) { }
@override
String get clientId => '00000000-0000-4000-0000-000000000000';
@override
void sendCommand(String command, { Map<String, String> parameters }) { }
@override
void sendEvent(String category, String parameter, { Map<String, String> parameters }) { }
@override
void sendTiming(String category, String variableName, Duration duration, { String label }) { }
@override
void sendException(dynamic exception, StackTrace trace) { }
@override
Stream<Map<String, dynamic>> get onSend => null;
@override
Future<Null> ensureAnalyticsSent() => new Future<Null>.value();
@override
void printWelcome() { }
}
class MockFlutterVersion extends Mock implements FlutterVersion {}
class MockClock extends Mock implements Clock {}