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

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: Code review changes 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
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,
(...skipping 11 matching lines...) Expand all
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 /// The global declarer.
55 typedef dynamic TestFunction(); 57 ///
58 /// This is used if a test file is run directly, rather than through the runner.
59 Declarer _globalDeclarer;
56 60
57 /// [Configuration] used by the unittest library. 61 /// Gets the declarer for the current scope.
58 /// 62 ///
59 /// Note that if a configuration has not been set, calling this getter will 63 /// When using the runner, this returns the [Zone]-scoped declarer that's set by
60 /// create a default configuration. 64 /// [VmListener]. If the test file is run directly, this returns
61 Configuration get unittestConfiguration { 65 /// [_globalDeclarer] (and sets it up on the first call).
62 if (config == null) environment.config = new Configuration(); 66 Declarer get _declarer {
63 return config; 67 var declarer = Zone.current[#unittest.declarer];
68 if (declarer != null) return declarer;
69 if (_globalDeclarer != null) return _globalDeclarer;
70
71 // Since there's no Zone-scoped declarer, the test file is being run directly.
72 // In order to run the tests, we set up our own Declarer via
73 // [_globalDeclarer], and schedule a microtask to run the tests once they're
74 // finished being defined.
75 _globalDeclarer = new Declarer();
76 scheduleMicrotask(() {
77 var suite = new Suite(p.prettyUri(Uri.base), _globalDeclarer.tests);
78 // TODO(nweiz): Use a reporter that doesn't import dart:io here.
79 // TODO(nweiz): Set the exit code on the VM when issue 6943 is fixed.
80 new ConsoleReporter([suite]).run();
81 });
82 return _globalDeclarer;
64 } 83 }
65 84
66 /// If `true`, stack traces are reformatted to be more readable. 85 // TODO(nweiz): This and other top-level functions should throw exceptions if
67 bool formatStacks = true; 86 // they're called after the declarer has finished declaring.
87 void test(String description, body()) => _declarer.test(description, body);
68 88
69 /// If `true`, irrelevant frames are filtered from the stack trace. 89 void group(String description, void body()) =>
70 /// 90 _declarer.group(description, body);
71 /// This does nothing if [formatStacks] is false.
72 bool filterStacks = true;
73 91
74 /// Separator used between group names and test names. 92 void setUp(callback()) => _declarer.setUp(callback);
75 String groupSep = ' ';
76 93
77 /// Sets the [Configuration] used by the unittest library. 94 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 95
290 /// Handle an error that occurs outside of any test. 96 /// Handle an error that occurs outside of any test.
291 void handleExternalError(e, String message, [stackTrace]) { 97 void handleExternalError(error, String message, [stackTrace]) {
292 var msg = '$message\nCaught $e'; 98 // TODO(nweiz): handle this better.
293 99 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 } 100 }
330 101
331 /// Registers an exception that was caught for the current test. 102 /// Registers an exception that was caught for the current test.
332 void registerException(error, [StackTrace stackTrace]) => 103 void registerException(error, [StackTrace stackTrace]) =>
333 _currentTestCase.registerException(error, stackTrace); 104 Invoker.current.handleError(error, stackTrace);
334 105
335 /// Runs the next test. 106 // What follows are stubs for various top-level names supported by unittest
336 void _runTest() { 107 // 0.11.*. These are preserved for the time being for ease of migration, but
337 if (environment.currentTestCaseIndex >= testCases.length) { 108 // should be removed before this is released as stable.
338 assert(environment.currentTestCaseIndex == testCases.length);
339 _completeTests();
340 return;
341 }
342 109
343 var testCase = _currentTestCase; 110 @deprecated
344 var f = runZoned(testCase.run, onError: (error, stack) { 111 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 112
350 var timer; 113 @deprecated
351 var timeout = unittestConfiguration.timeout; 114 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 115
364 f.whenComplete(() { 116 @deprecated
365 if (timer != null) timer.cancel(); 117 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 118
376 /// Notify the configuration that the testing has finished. 119 @deprecated
377 void _completeTests() { 120 bool filterStacks = true;
378 if (!environment.initialized) return;
379 121
380 var passed = 0; 122 @deprecated
381 var failed = 0; 123 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 124
397 config.onSummary( 125 @deprecated
398 passed, failed, errors, testCases, environment.uncaughtErrorMessage); 126 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 127
407 /// Initializes the test environment if it hasn't already been initialized. 128 @deprecated
408 void ensureInitialized() { 129 final testCases = [];
409 _ensureInitialized(true);
410 }
411 130
412 /// Initializes the test environment. 131 @deprecated
413 /// 132 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 133
419 environment.initialized = true; 134 @deprecated
135 TestCase get currentTestCase => null;
420 136
421 environment.uncaughtErrorMessage = null; 137 @deprecated
138 const PASS = 'pass';
422 139
423 unittestConfiguration.onInit(); 140 @deprecated
141 const FAIL = 'fail';
424 142
425 // Immediately queue the suite up. It will run after a timeout (i.e. after 143 @deprecated
426 // main() has returned). 144 const ERROR = 'error';
427 if (configAutoStart && config.autoStart) scheduleMicrotask(runTests);
428 }
429 145
430 /// Remove all tests other than the one identified by [id]. 146 @deprecated
431 void setSoloTest(int id) => 147 void skip_test(String spec, TestFunction body) {}
432 environment.testCases.retainWhere((t) => t.id == id);
433 148
434 /// Enable the test identified by [id]. 149 @deprecated
435 void enableTest(int id) => _setTestEnabledState(id, enable: true); 150 void solo_test(String spec, TestFunction body) => test(spec, body);
436 151
437 /// Disable the test by [id]. 152 @deprecated
438 void disableTest(int id) => _setTestEnabledState(id, enable: false); 153 void skip_group(String description, void body()) {}
439 154
440 /// Enable or disable the test identified by [id]. 155 @deprecated
441 void _setTestEnabledState(int id, {bool enable: true}) { 156 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 157
454 /// Throws a [StateError] if tests are running. 158 @deprecated
455 void _requireNotRunning() { 159 void filterTests(testFilter) {}
456 if (environment.currentTestCaseIndex == -1) return;
457 throw new StateError('Not allowed when tests are running.');
458 }
459 160
460 /// Creates a test environment running in its own zone scope. 161 @deprecated
461 /// 162 void runTests() {}
462 /// This allows for multiple invocations of the unittest library in the same 163
463 /// application instance. This is useful when, for example, creating a test 164 @deprecated
464 /// runner application which needs to create a new pristine test environment on 165 void ensureInitialized() {}
465 /// each invocation to run a given set of tests. 166
466 withTestEnvironment(callback()) { 167 @deprecated
467 return runZoned(callback, 168 void setSoloTest(int id) {}
468 zoneValues: {#unittest.environment: new TestEnvironment()}); 169
469 } 170 @deprecated
171 void enableTest(int id) {}
172
173 @deprecated
174 void disableTest(int id) {}
175
176 @deprecated
177 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