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

Side by Side Diff: lib/unittest.dart

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

Powered by Google App Engine
This is Rietveld 408576698