Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(76)

Side by Side Diff: sky/tests/resources/third_party/unittest/unittest.dart

Issue 921863002: Add testing resources from SkyDart branch (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: move to third_party Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library unittest;
6
7 import 'dart:async';
8 import 'dart:collection';
9
10 import '../matcher/matcher.dart' show TestFailure, wrapAsync;
11
12 import 'src/configuration.dart';
13 import 'src/expected_function.dart';
14 import 'src/group_context.dart';
15 import 'src/internal_test_case.dart';
16 import 'src/test_case.dart';
17 import 'src/test_environment.dart';
18
19 export '../matcher/matcher.dart';
20
21 export 'src/configuration.dart';
22 export 'src/simple_configuration.dart';
23 export 'src/test_case.dart';
24
25 /// The signature for a function passed to [test].
26 typedef dynamic TestFunction();
27
28 /// [Configuration] used by the unittest library.
29 ///
30 /// Note that if a configuration has not been set, calling this getter will
31 /// create a default configuration.
32 Configuration get unittestConfiguration {
33 if (config == null) environment.config = new Configuration();
34 return config;
35 }
36
37 /// If `true`, stack traces are reformatted to be more readable.
38 bool formatStacks = true;
39
40 /// If `true`, irrelevant frames are filtered from the stack trace.
41 ///
42 /// This does nothing if [formatStacks] is false.
43 bool filterStacks = true;
44
45 /// Separator used between group names and test names.
46 String groupSep = ' ';
47
48 /// Sets the [Configuration] used by the unittest library.
49 ///
50 /// Throws a [StateError] if there is an existing, incompatible value.
51 void set unittestConfiguration(Configuration value) {
52 if (identical(config, value)) return;
53 if (config != null) {
54 logMessage('Warning: The unittestConfiguration has already been set. New '
55 'unittestConfiguration ignored.');
56 } else {
57 environment.config = value;
58 }
59 }
60
61 /// Logs [message] associated with the current test case.
62 ///
63 /// Tests should use this instead of [print].
64 void logMessage(String message) =>
65 config.onLogMessage(currentTestCase, message);
66
67 /// The test cases that have been defined so far.
68 List<TestCase> get testCases =>
69 new UnmodifiableListView<TestCase>(environment.testCases);
70
71 /// The interval (in milliseconds) after which a non-microtask asynchronous
72 /// delay will be scheduled between tests.
73 ///
74 /// This is used to avoid starving the DOM or other non-microtask events.
75 const int BREATH_INTERVAL = 200;
76
77 /// The [TestCase] currently being executed.
78 TestCase get currentTestCase => (environment.currentTestCaseIndex >= 0 &&
79 environment.currentTestCaseIndex < testCases.length)
80 ? testCases[environment.currentTestCaseIndex]
81 : null;
82
83 /// The same as [currentTestCase], but typed as an [InternalTestCase].
84 InternalTestCase get _currentTestCase => currentTestCase as InternalTestCase;
85
86 /// The result string for a passing test case.
87 const PASS = 'pass';
88
89 /// The result string for a failing test case.
90 const FAIL = 'fail';
91
92 /// The result string for an test case with an error.
93 const ERROR = 'error';
94
95 /// Creates a new test case with the given description and body.
96 ///
97 /// The description will be added to the descriptions of any surrounding
98 /// [group]s.
99 void test(String description, TestFunction body) {
100 _requireNotRunning();
101 ensureInitialized();
102
103 if (environment.soloTestSeen && environment.soloNestingLevel == 0) return;
104 var testCase = new InternalTestCase(
105 testCases.length + 1, _fullDescription(description), body);
106 environment.testCases.add(testCase);
107 }
108
109 /// Returns [description] with all of its group prefixes prepended.
110 String _fullDescription(String description) {
111 var group = environment.currentContext.fullName;
112 if (description == null) return group;
113 return group != '' ? '$group$groupSep$description' : description;
114 }
115
116 /// A convenience function for skipping a test.
117 void skip_test(String spec, TestFunction body) {}
118
119 /// Creates a new test case with the given description and body.
120 ///
121 /// If [solo_test] is used instead of [test], then all non-solo tests will be
122 /// disabled. Note that if [solo_group] is used as well, all tests in the group
123 /// will be enabled, regardless of whether they use [test] or [solo_test], or
124 /// whether they are in a nested [group] versus [solo_group]. Put another way,
125 /// if there are any calls to [solo_test] or [solo_group] in a test file, all
126 /// tests that are not inside a [solo_group] will be disabled unless they are
127 /// [solo_test]s.
128 void solo_test(String spec, TestFunction body) {
129 _requireNotRunning();
130 ensureInitialized();
131 if (!environment.soloTestSeen) {
132 environment.soloTestSeen = true;
133 // This is the first solo-ed test. Discard all tests up to now.
134 environment.testCases.clear();
135 }
136 environment.soloNestingLevel++;
137 try {
138 test(spec, body);
139 } finally {
140 environment.soloNestingLevel--;
141 }
142 }
143
144 /// Indicate that [callback] is expected to be called [count] number of times
145 /// (by default 1).
146 ///
147 /// The unittest framework will wait for the callback to run the [count] times
148 /// before it considers the current test to be complete. Using [expectAsync]
149 /// will also ensure that errors that occur within [callback] are tracked and
150 /// reported. [callback] may take up to six optional or required positional
151 /// arguments; named arguments are not supported.
152 ///
153 /// [max] can be used to specify an upper bound on the number of calls; if this
154 /// is exceeded the test will fail. If [max] is `0` (the default), the callback
155 /// is expected to be called exactly [count] times. If [max] is `-1`, the
156 /// callback is allowed to be called any number of times greater than [count].
157 ///
158 /// Both [id] and [reason] are optional and provide extra information about the
159 /// callback when debugging. [id] should be the name of the callback, while
160 /// [reason] should be the reason the callback is expected to be called.
161 Function expectAsync(Function callback,
162 {int count: 1, int max: 0, String id, String reason}) =>
163 new ExpectedFunction(callback, count, max, id: id, reason: reason).func;
164
165 /// Indicate that [callback] is expected to be called until [isDone] returns
166 /// true.
167 ///
168 /// [isDone] is called after each time the function is run. Only when it returns
169 /// true will the callback be considered complete. Using [expectAsyncUntil] will
170 /// also ensure that errors that occur within [callback] are tracked and
171 /// reported. [callback] may take up to six optional or required positional
172 /// arguments; named arguments are not supported.
173 ///
174 /// Both [id] and [reason] are optional and provide extra information about the
175 /// callback when debugging. [id] should be the name of the callback, while
176 /// [reason] should be the reason the callback is expected to be called.
177 Function expectAsyncUntil(Function callback, bool isDone(),
178 {String id, String reason}) => new ExpectedFunction(callback, 0, -1,
179 id: id, reason: reason, isDone: isDone).func;
180
181 /// Creates a group of tests.
182 ///
183 /// A group's description is included in the descriptions of any tests or
184 /// sub-groups it contains. [setUp] and [tearDown] are also scoped to the
185 /// containing group.
186 void group(String description, void body()) {
187 ensureInitialized();
188 _requireNotRunning();
189 environment.currentContext =
190 new GroupContext(environment.currentContext, description);
191 try {
192 body();
193 } catch (e, trace) {
194 var stack = (trace == null) ? '' : ': ${trace.toString()}';
195 environment.uncaughtErrorMessage = "${e.toString()}$stack";
196 } finally {
197 // Now that the group is over, restore the previous one.
198 environment.currentContext = environment.currentContext.parent;
199 }
200 }
201
202 /// A convenience function for skipping a group of tests.
203 void skip_group(String description, void body()) {}
204
205 /// Creates a group of tests.
206 ///
207 /// If [solo_group] is used instead of [group], then all tests not declared with
208 /// [solo_test] or in a [solo_group] will be disabled. Note that all tests in a
209 /// [solo_group] will be run, regardless of whether they're declared with [test]
210 /// or [solo_test].
211 ///
212 /// [skip_test] and [skip_group] take precedence over [solo_group].
213 void solo_group(String description, void body()) {
214 _requireNotRunning();
215 ensureInitialized();
216 if (!environment.soloTestSeen) {
217 environment.soloTestSeen = true;
218 // This is the first solo-ed group. Discard all tests up to now.
219 environment.testCases.clear();
220 }
221 ++environment.soloNestingLevel;
222 try {
223 group(description, body);
224 } finally {
225 --environment.soloNestingLevel;
226 }
227 }
228
229 /// Registers a function to be run before tests.
230 ///
231 /// This function will be called before each test is run. [callback] may be
232 /// asynchronous; if so, it must return a [Future].
233 ///
234 /// If this is called within a test group, it applies only to tests in that
235 /// group. [callback] will be run after any set-up callbacks in parent groups or
236 /// at the top level.
237 void setUp(Function callback) {
238 _requireNotRunning();
239 environment.currentContext.testSetUp = callback;
240 }
241
242 /// Registers a function to be run after tests.
243 ///
244 /// This function will be called after each test is run. [callback] may be
245 /// asynchronous; if so, it must return a [Future].
246 ///
247 /// If this is called within a test group, it applies only to tests in that
248 /// group. [callback] will be run before any tear-down callbacks in parent group s or
249 /// at the top level.
250 void tearDown(Function callback) {
251 _requireNotRunning();
252 environment.currentContext.testTearDown = callback;
253 }
254
255 /// Advance to the next test case.
256 void _nextTestCase() {
257 environment.currentTestCaseIndex++;
258 _runTest();
259 }
260
261 /// Handle an error that occurs outside of any test.
262 void handleExternalError(e, String message, [stackTrace]) {
263 var msg = '$message\nCaught $e';
264
265 if (currentTestCase != null) {
266 _currentTestCase.error(msg, stackTrace);
267 } else {
268 environment.uncaughtErrorMessage = "$msg: $stackTrace";
269 }
270 }
271
272 /// Remove any tests that match [testFilter].
273 ///
274 /// [testFilter] can be a predicate function, a [RegExp], or a [String]. If it's
275 /// a function, it's called with each [TestCase]. If it's a [String], it's
276 /// parsed as a [RegExp] and matched against each [TestCase.description].
277 ///
278 /// This is different from enabling or disabling tests in that it removes the
279 /// tests completely.
280 void filterTests(testFilter) {
281 var filterFunction;
282 if (testFilter is String) {
283 var re = new RegExp(testFilter);
284 filterFunction = (t) => re.hasMatch(t.description);
285 } else if (testFilter is RegExp) {
286 filterFunction = (t) => testFilter.hasMatch(t.description);
287 } else if (testFilter is Function) {
288 filterFunction = testFilter;
289 }
290 environment.testCases.retainWhere(filterFunction);
291 }
292
293 /// Runs all queued tests, one at a time.
294 void runTests() {
295 _requireNotRunning();
296 _ensureInitialized(false);
297 environment.currentTestCaseIndex = 0;
298 config.onStart();
299 _runTest();
300 }
301
302 /// Registers an exception that was caught for the current test.
303 void registerException(error, [StackTrace stackTrace]) =>
304 _currentTestCase.registerException(error, stackTrace);
305
306 /// Runs the next test.
307 void _runTest() {
308 if (environment.currentTestCaseIndex >= testCases.length) {
309 assert(environment.currentTestCaseIndex == testCases.length);
310 _completeTests();
311 return;
312 }
313
314 var testCase = _currentTestCase;
315 var f = runZoned(testCase.run, onError: (error, stack) {
316 // TODO(kevmoo) Do a better job of flagging these are async errors.
317 // https://code.google.com/p/dart/issues/detail?id=16530
318 testCase.registerException(error, stack);
319 });
320
321 var timer;
322 var timeout = unittestConfiguration.timeout;
323 if (timeout != null) {
324 try {
325 timer = new Timer(timeout, () {
326 testCase.error("Test timed out after ${timeout.inSeconds} seconds.");
327 _nextTestCase();
328 });
329 } on UnsupportedError catch (e) {
330 if (e.message != "Timer greater than 0.") rethrow;
331 // Support running on d8 and jsshell which don't support timers.
332 }
333 }
334
335 f.whenComplete(() {
336 if (timer != null) timer.cancel();
337 var now = new DateTime.now().millisecondsSinceEpoch;
338 if (now - environment.lastBreath >= BREATH_INTERVAL) {
339 environment.lastBreath = now;
340 Timer.run(_nextTestCase);
341 } else {
342 scheduleMicrotask(_nextTestCase); // Schedule the next test.
343 }
344 });
345 }
346
347 /// Notify the configuration that the testing has finished.
348 void _completeTests() {
349 if (!environment.initialized) return;
350
351 var passed = 0;
352 var failed = 0;
353 var errors = 0;
354 for (var testCase in testCases) {
355 switch (testCase.result) {
356 case PASS:
357 passed++;
358 break;
359 case FAIL:
360 failed++;
361 break;
362 case ERROR:
363 errors++;
364 break;
365 }
366 }
367
368 config.onSummary(
369 passed, failed, errors, testCases, environment.uncaughtErrorMessage);
370 config.onDone(passed > 0 &&
371 failed == 0 &&
372 errors == 0 &&
373 environment.uncaughtErrorMessage == null);
374 environment.initialized = false;
375 environment.currentTestCaseIndex = -1;
376 }
377
378 /// Initializes the test environment if it hasn't already been initialized.
379 void ensureInitialized() {
380 _ensureInitialized(true);
381 }
382
383 /// Initializes the test environment.
384 ///
385 /// If [configAutoStart] is `true`, schedule a microtask to run the tests. This
386 /// microtask is expected to run after all the tests are defined.
387 void _ensureInitialized(bool configAutoStart) {
388 if (environment.initialized) return;
389
390 environment.initialized = true;
391 // Hook our async guard into the matcher library.
392 wrapAsync = (f, [id]) => expectAsync(f, id: id);
393
394 environment.uncaughtErrorMessage = null;
395
396 unittestConfiguration.onInit();
397
398 // Immediately queue the suite up. It will run after a timeout (i.e. after
399 // main() has returned).
400 if (configAutoStart && config.autoStart) scheduleMicrotask(runTests);
401 }
402
403 /// Remove all tests other than the one identified by [id].
404 void setSoloTest(int id) =>
405 environment.testCases.retainWhere((t) => t.id == id);
406
407 /// Enable the test identified by [id].
408 void enableTest(int id) => _setTestEnabledState(id, enable: true);
409
410 /// Disable the test by [id].
411 void disableTest(int id) => _setTestEnabledState(id, enable: false);
412
413 /// Enable or disable the test identified by [id].
414 void _setTestEnabledState(int id, {bool enable: true}) {
415 // Try fast path first.
416 if (testCases.length > id && testCases[id].id == id) {
417 environment.testCases[id].enabled = enable;
418 } else {
419 for (var i = 0; i < testCases.length; i++) {
420 if (testCases[i].id != id) continue;
421 environment.testCases[i].enabled = enable;
422 break;
423 }
424 }
425 }
426
427 /// Throws a [StateError] if tests are running.
428 void _requireNotRunning() {
429 if (environment.currentTestCaseIndex == -1) return;
430 throw new StateError('Not allowed when tests are running.');
431 }
432
433 /// Creates a test environment running in its own zone scope.
434 ///
435 /// This allows for multiple invocations of the unittest library in the same
436 /// application instance. This is useful when, for example, creating a test
437 /// runner application which needs to create a new pristine test environment on
438 /// each invocation to run a given set of tests.
439 withTestEnvironment(callback()) {
440 return runZoned(callback,
441 zoneValues: {#unittest.environment: new TestEnvironment()});
442 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698