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

Side by Side Diff: lib/unittest.dart

Issue 869043002: Clean up unittest a bunch. (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/src/test_environment.dart ('k') | pubspec.yaml » ('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 /// Support for writing Dart unit tests. 5 /// Support for writing Dart unit tests.
6 /// 6 ///
7 /// For information on installing and importing this library, see the 7 /// For information on installing and importing this library, see the
8 /// [unittest package on pub.dartlang.org] 8 /// [unittest package on pub.dartlang.org]
9 /// (http://pub.dartlang.org/packages/unittest). 9 /// (http://pub.dartlang.org/packages/unittest).
10 /// 10 ///
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
129 /// var delta = new DateTime.now().difference(time); 129 /// var delta = new DateTime.now().difference(time);
130 /// 130 ///
131 /// expect(delta, greaterThanOrEqualTo(duration)); 131 /// expect(delta, greaterThanOrEqualTo(duration));
132 /// }); 132 /// });
133 /// }); 133 /// });
134 /// } 134 /// }
135 library unittest; 135 library unittest;
136 136
137 import 'dart:async'; 137 import 'dart:async';
138 import 'dart:collection'; 138 import 'dart:collection';
139 import 'dart:isolate'; 139
140 140 import 'package:matcher/matcher.dart' show TestFailure, wrapAsync;
141 import 'package:matcher/matcher.dart' show DefaultFailureHandler, 141
142 configureExpectFailureHandler, TestFailure, wrapAsync; 142 import 'src/configuration.dart';
143 import 'src/expected_function.dart';
144 import 'src/group_context.dart';
145 import 'src/internal_test_case.dart';
146 import 'src/test_case.dart';
147 import 'src/test_environment.dart';
148
143 export 'package:matcher/matcher.dart'; 149 export 'package:matcher/matcher.dart';
144 150
145 import 'src/utils.dart';
146
147 import 'src/configuration.dart';
148 export 'src/configuration.dart'; 151 export 'src/configuration.dart';
149 152 export 'src/simple_configuration.dart';
150 part 'src/simple_configuration.dart'; 153 export 'src/test_case.dart';
151 part 'src/group_context.dart'; 154
152 part 'src/spread_args_helper.dart'; 155 /// The signature for a function passed to [test].
153 part 'src/test_case.dart'; 156 typedef dynamic TestFunction();
154 part 'src/test_environment.dart';
155
156 const Symbol _UNITTEST_ENVIRONMENT = #unittest.environment;
157
158 final _TestEnvironment _defaultEnvironment = new _TestEnvironment();
159
160 /**
161 * Internal getter for the current unittest config.
162 */
163 _TestEnvironment get _environment {
164 var environment = Zone.current[_UNITTEST_ENVIRONMENT];
165 if (environment == null) return _defaultEnvironment;
166 return environment;
167 }
168
169 // Convenience getter for the current environment's config.
170 Configuration get _config => _environment.config;
171
172 // Convenience setter for the current environment's config.
173 void set _config(Configuration config) {
174 _environment.config = config;
175 }
176
177 // Convenience getter for the current environment's test cases.
178 List<TestCase> get _testCases => _environment.testCases;
179 157
180 /// [Configuration] used by the unittest library. 158 /// [Configuration] used by the unittest library.
181 /// 159 ///
182 /// Note that if a configuration has not been set, calling this getter will 160 /// Note that if a configuration has not been set, calling this getter will
183 /// create a default configuration. 161 /// create a default configuration.
184 Configuration get unittestConfiguration { 162 Configuration get unittestConfiguration {
185 if (_config == null) { 163 if (config == null) environment.config = new Configuration();
186 _config = new Configuration(); 164 return config;
187 } 165 }
188 return _config; 166
189 } 167 /// If `true`, stack traces are reformatted to be more readable.
168 bool formatStacks = true;
169
170 /// If `true`, irrelevant frames are filtered from the stack trace.
171 ///
172 /// This does nothing if [formatStacks] is false.
173 bool filterStacks = true;
174
175 /// Separator used between group names and test names.
176 String groupSep = ' ';
190 177
191 /// Sets the [Configuration] used by the unittest library. 178 /// Sets the [Configuration] used by the unittest library.
192 /// 179 ///
193 /// Throws a [StateError] if there is an existing, incompatible value. 180 /// Throws a [StateError] if there is an existing, incompatible value.
194 void set unittestConfiguration(Configuration value) { 181 void set unittestConfiguration(Configuration value) {
195 if (!identical(_config, value)) { 182 if (identical(config, value)) return;
196 if (_config != null) { 183 if (config != null) {
197 logMessage('Warning: The unittestConfiguration has already been set. New ' 184 logMessage('Warning: The unittestConfiguration has already been set. New '
198 'unittestConfiguration ignored.'); 185 'unittestConfiguration ignored.');
199 } else { 186 } else {
200 _config = value; 187 environment.config = value;
201 } 188 }
202 } 189 }
203 } 190
204 191 /// Logs [message] associated with the current test case.
205 /// Can be called by tests to log status. Tests should use this 192 ///
206 /// instead of [print]. 193 /// Tests should use this instead of [print].
207 void logMessage(String message) => 194 void logMessage(String message) =>
208 _config.onLogMessage(currentTestCase, message); 195 config.onLogMessage(currentTestCase, message);
209 196
210 /// Separator used between group names and test names. 197 /// The test cases that have been defined so far.
211 String groupSep = ' ';
212
213 /// Tests executed in this suite.
214 List<TestCase> get testCases => 198 List<TestCase> get testCases =>
215 new UnmodifiableListView<TestCase>(_environment.testCases); 199 new UnmodifiableListView<TestCase>(environment.testCases);
216 200
217 /// Interval (in msecs) after which synchronous tests will insert an async 201 /// The interval (in milliseconds) after which a non-microtask asynchronous
218 /// delay to allow DOM or other updates. 202 /// delay will be scheduled between tests.
203 ///
204 /// This is used to avoid starving the DOM or other non-microtask events.
219 const int BREATH_INTERVAL = 200; 205 const int BREATH_INTERVAL = 200;
220 206
221 /// [TestCase] currently being executed. 207 /// The [TestCase] currently being executed.
222 TestCase get currentTestCase => 208 TestCase get currentTestCase =>
223 (_environment.currentTestCaseIndex >= 0 && 209 (environment.currentTestCaseIndex >= 0 &&
224 _environment.currentTestCaseIndex < testCases.length) 210 environment.currentTestCaseIndex < testCases.length)
225 ? testCases[_environment.currentTestCaseIndex] 211 ? testCases[environment.currentTestCaseIndex]
226 : null; 212 : null;
227 213
228 /* Test case result strings. */ 214 /// The same as [currentTestCase], but typed as an [InternalTestCase].
229 // TODO(gram) we should change these constants to use a different string 215 InternalTestCase get _currentTestCase => currentTestCase as InternalTestCase;
230 // (so that writing 'FAIL' in the middle of a test doesn't 216
231 // imply that the test fails). We can't do it without also changing 217 /// The result string for a passing test case.
232 // the testrunner and test.dart though.
233 /// Result string for a passing test case.
234 const PASS = 'pass'; 218 const PASS = 'pass';
235 /// Result string for a failing test case. 219
220 /// The result string for a failing test case.
236 const FAIL = 'fail'; 221 const FAIL = 'fail';
237 /// Result string for an test case with an error. 222
223 /// The result string for an test case with an error.
238 const ERROR = 'error'; 224 const ERROR = 'error';
239 225
240 /// Creates a new test case with the given description and body. The 226 /// Creates a new test case with the given description and body.
241 /// description will include the descriptions of any surrounding group() 227 ///
242 /// calls. 228 /// The description will be added to the descriptions of any surrounding
243 void test(String spec, TestFunction body) { 229 /// [group]s.
230 void test(String description, TestFunction body) {
244 _requireNotRunning(); 231 _requireNotRunning();
245 ensureInitialized(); 232 ensureInitialized();
246 if (!_environment.soloTestSeen || _environment.soloNestingLevel > 0) { 233
247 var testcase = new TestCase._internal(testCases.length + 1, _fullSpec(spec), 234 if (environment.soloTestSeen && environment.soloNestingLevel == 0) return;
248 body); 235 var testCase = new InternalTestCase(
249 _testCases.add(testcase); 236 testCases.length + 1, _fullDescription(description), body);
250 } 237 environment.testCases.add(testCase);
251 } 238 }
252 239
253 /// Convenience function for skipping a test. 240 /// Returns [description] with all of its group prefixes prepended.
241 String _fullDescription(String description) {
242 var group = environment.currentContext.fullName;
243 if (description == null) return group;
244 return group != '' ? '$group$groupSep$description' : description;
245 }
246
247 /// A convenience function for skipping a test.
254 void skip_test(String spec, TestFunction body) {} 248 void skip_test(String spec, TestFunction body) {}
255 249
256 /// Creates a new test case with the given description and body. The 250 /// Creates a new test case with the given description and body.
257 /// description will include the descriptions of any surrounding group() 251 ///
258 /// calls. 252 /// If [solo_test] is used instead of [test], then all non-solo tests will be
259 /// 253 /// disabled. Note that if [solo_group] is used as well, all tests in the group
260 /// If we use [solo_test] (or [solo_group]) instead of test, then all non-solo 254 /// will be enabled, regardless of whether they use [test] or [solo_test], or
261 /// tests will be disabled. Note that if we use [solo_group], all tests in 255 /// whether they are in a nested [group] versus [solo_group]. Put another way,
262 /// the group will be enabled, regardless of whether they use [test] or 256 /// if there are any calls to [solo_test] or [solo_group] in a test file, all
263 /// [solo_test], or whether they are in a nested [group] vs [solo_group]. Put 257 /// tests that are not inside a [solo_group] will be disabled unless they are
264 /// another way, if there are any calls to [solo_test] or [solo_group] in a test 258 /// [solo_test]s.
265 /// file, all tests that are not inside a [solo_group] will be disabled unless
266 /// they are [solo_test]s.
267 ///
268 /// [skip_test] and [skip_group] take precedence over soloing, by virtue of the
269 /// fact that they are effectively no-ops.
270 void solo_test(String spec, TestFunction body) { 259 void solo_test(String spec, TestFunction body) {
271 _requireNotRunning(); 260 _requireNotRunning();
272 ensureInitialized(); 261 ensureInitialized();
273 if (!_environment.soloTestSeen) { 262 if (!environment.soloTestSeen) {
274 _environment.soloTestSeen = true; 263 environment.soloTestSeen = true;
275 // This is the first solo-ed test. Discard all tests up to now. 264 // This is the first solo-ed test. Discard all tests up to now.
276 _testCases.clear(); 265 environment.testCases.clear();
277 } 266 }
278 ++_environment.soloNestingLevel; 267 environment.soloNestingLevel++;
279 try { 268 try {
280 test(spec, body); 269 test(spec, body);
281 } finally { 270 } finally {
282 --_environment.soloNestingLevel; 271 environment.soloNestingLevel--;
283 } 272 }
284 } 273 }
285 274
286 /// Indicate that [callback] is expected to be called a [count] number of times 275 /// Indicate that [callback] is expected to be called [count] number of times
287 /// (by default 1). 276 /// (by default 1).
288 /// 277 ///
289 /// The unittest framework will wait for the callback to run the 278 /// The unittest framework will wait for the callback to run the [count] times
290 /// specified [count] times before it continues with the following test. Using 279 /// before it considers the current test to be complete. Using [expectAsync]
291 /// [expectAsync] will also ensure that errors that occur within [callback] are 280 /// will also ensure that errors that occur within [callback] are tracked and
292 /// tracked and reported. [callback] should take 0 positional arguments (named 281 /// reported. [callback] may take up to six optional or required positional
293 /// arguments are not supported). [id] can be used to provide more 282 /// arguments; named arguments are not supported.
294 /// descriptive error messages if the callback is called more often than 283 ///
295 /// expected. 284 /// [max] can be used to specify an upper bound on the number of calls; if this
296 /// 285 /// is exceeded the test will fail. If [max] is `0` (the default), the callback
297 /// [max] can be used to specify an upper bound on the number of 286 /// is expected to be called exactly [count] times. If [max] is `-1`, the
298 /// calls; if this is exceeded the test will fail (or be marked as in error if 287 /// callback is allowed to be called any number of times greater than [count].
299 /// it was already complete). A value of 0 for [max] (the default) will set 288 ///
300 /// the upper bound to the same value as [count]; i.e. the callback should be 289 /// Both [id] and [reason] are optional and provide extra information about the
301 /// called exactly [count] times. A value of -1 for [max] will mean no upper 290 /// callback when debugging. [id] should be the name of the callback, while
302 /// bound. 291 /// [reason] should be the reason the callback is expected to be called.
303 ///
304 /// [reason] is optional and is typically not supplied, as a reason is generated
305 /// by the unittest package; if reason is included it is appended to the
306 /// generated reason.
307 Function expectAsync(Function callback, 292 Function expectAsync(Function callback,
308 {int count: 1, int max: 0, String id, String reason}) => 293 {int count: 1, int max: 0, String id, String reason}) =>
309 new _SpreadArgsHelper(callback, count, max, id, reason).func; 294 new ExpectedFunction(callback, count, max, id: id, reason: reason).func;
310 295
311 /// Indicate that [callback] is expected to be called until [isDone] returns 296 /// Indicate that [callback] is expected to be called until [isDone] returns
312 /// true. 297 /// true.
313 /// 298 ///
314 /// The unittest framework checks [isDone] after each callback and only 299 /// [isDone] is called after each time the function is run. Only when it returns
315 /// when it returns true will it continue with the following test. Using 300 /// true will the callback be considered complete. Using [expectAsyncUntil] will
316 /// [expectAsyncUntil] will also ensure that errors that occur within 301 /// also ensure that errors that occur within [callback] are tracked and
317 /// [callback] are tracked and reported. [callback] should take 0 positional 302 /// reported. [callback] may take up to six optional or required positional
318 /// arguments (named arguments are not supported). [id] can be used to 303 /// arguments; named arguments are not supported.
319 /// identify the callback in error messages (for example if it is called 304 ///
320 /// after the test case is complete). 305 /// Both [id] and [reason] are optional and provide extra information about the
321 /// 306 /// callback when debugging. [id] should be the name of the callback, while
322 /// [reason] is optional and is typically not supplied, as a reason is generated 307 /// [reason] should be the reason the callback is expected to be called.
323 /// by the unittest package; if reason is included it is appended to the 308 Function expectAsyncUntil(Function callback, bool isDone(), {String id,
324 /// generated reason. 309 String reason}) =>
325 Function expectAsyncUntil(Function callback, bool isDone(), 310 new ExpectedFunction(callback, 0, -1, id: id, reason: reason, isDone: isDone)
326 {String id, String reason}) => 311 .func;
327 new _SpreadArgsHelper(callback, 0, -1, id, reason, isDone: isDone).func; 312
328 313 /// Creates a group of tests.
329 /// Creates a new named group of tests. 314 ///
330 /// 315 /// A group's description is included in the descriptions of any tests or
331 /// Calls to group() or test() within the body of the function passed to this 316 /// sub-groups it contains. [setUp] and [tearDown] are also scoped to the
332 /// named group will inherit this group's description. 317 /// containing group.
333 void group(String description, void body()) { 318 void group(String description, void body()) {
334 ensureInitialized(); 319 ensureInitialized();
335 _requireNotRunning(); 320 _requireNotRunning();
336 _environment.currentContext = 321 environment.currentContext =
337 new _GroupContext(_environment.currentContext, description); 322 new GroupContext(environment.currentContext, description);
338 try { 323 try {
339 body(); 324 body();
340 } catch (e, trace) { 325 } catch (e, trace) {
341 var stack = (trace == null) ? '' : ': ${trace.toString()}'; 326 var stack = (trace == null) ? '' : ': ${trace.toString()}';
342 _environment.uncaughtErrorMessage = "${e.toString()}$stack"; 327 environment.uncaughtErrorMessage = "${e.toString()}$stack";
343 } finally { 328 } finally {
344 // Now that the group is over, restore the previous one. 329 // Now that the group is over, restore the previous one.
345 _environment.currentContext = _environment.currentContext.parent; 330 environment.currentContext = environment.currentContext.parent;
346 } 331 }
347 } 332 }
348 333
349 /// Like [skip_test], but for groups. 334 /// A convenience function for skipping a group of tests.
350 void skip_group(String description, void body()) {} 335 void skip_group(String description, void body()) {}
351 336
352 /// Like [solo_test], but for groups. 337 /// Creates a group of tests.
338 ///
339 /// If [solo_group] is used instead of [group], then all tests not declared with
340 /// [solo_test] or in a [solo_group] will be disabled. Note that all tests in a
341 /// [solo_group] will be run, regardless of whether they're declared with [test]
342 /// or [solo_test].
343 ///
344 /// [skip_test] and [skip_group] take precedence over [solo_group].
353 void solo_group(String description, void body()) { 345 void solo_group(String description, void body()) {
354 _requireNotRunning(); 346 _requireNotRunning();
355 ensureInitialized(); 347 ensureInitialized();
356 if (!_environment.soloTestSeen) { 348 if (!environment.soloTestSeen) {
357 _environment.soloTestSeen = true; 349 environment.soloTestSeen = true;
358 // This is the first solo-ed group. Discard all tests up to now. 350 // This is the first solo-ed group. Discard all tests up to now.
359 _testCases.clear(); 351 environment.testCases.clear();
360 } 352 }
361 ++_environment.soloNestingLevel; 353 ++environment.soloNestingLevel;
362 try { 354 try {
363 group(description, body); 355 group(description, body);
364 } finally { 356 } finally {
365 --_environment.soloNestingLevel; 357 --environment.soloNestingLevel;
366 } 358 }
367 } 359 }
368 360
369 /// Register a [setUp] function for a test [group]. 361 /// Registers a function to be run before tests.
370 /// 362 ///
371 /// This function will be called before each test in the group is run. 363 /// This function will be called before each test is run. [callback] may be
372 /// [setUp] and [tearDown] should be called within the [group] before any 364 /// asynchronous; if so, it must return a [Future].
373 /// calls to [test]. The [setupTest] function can be asynchronous; in this 365 ///
374 /// case it must return a [Future]. 366 /// If this is called within a test group, it applies only to tests in that
375 void setUp(Function setupTest) { 367 /// group. [callback] will be run after any set-up callbacks in parent groups or
376 _requireNotRunning(); 368 /// at the top level.
377 _environment.currentContext.testSetup = setupTest; 369 void setUp(Function callback) {
378 } 370 _requireNotRunning();
379 371 environment.currentContext.testSetUp = callback;
380 /// Register a [tearDown] function for a test [group]. 372 }
381 /// 373
382 /// This function will be called after each test in the group is run. 374 /// Registers a function to be run after tests.
383 /// 375 ///
384 /// Note that if groups are nested only the most locally scoped [teardownTest] 376 /// This function will be called after each test is run. [callback] may be
385 /// function will be run. [setUp] and [tearDown] should be called within the 377 /// asynchronous; if so, it must return a [Future].
386 /// [group] before any calls to [test]. The [teardownTest] function can be 378 ///
387 /// asynchronous; in this case it must return a [Future]. 379 /// If this is called within a test group, it applies only to tests in that
388 void tearDown(Function teardownTest) { 380 /// group. [callback] will be run before any tear-down callbacks in parent group s or
389 _requireNotRunning(); 381 /// at the top level.
390 _environment.currentContext.testTeardown = teardownTest; 382 void tearDown(Function callback) {
383 _requireNotRunning();
384 environment.currentContext.testTearDown = callback;
391 } 385 }
392 386
393 /// Advance to the next test case. 387 /// Advance to the next test case.
394 void _nextTestCase() { 388 void _nextTestCase() {
395 _environment.currentTestCaseIndex++; 389 environment.currentTestCaseIndex++;
396 _runTest(); 390 _runTest();
397 } 391 }
398 392
399 /// Handle errors that happen outside the tests. 393 /// Handle an error that occurs outside of any test.
400 // TODO(vsm): figure out how to expose the stack trace here 394 void handleExternalError(e, String message, [stackTrace]) {
401 // Currently e.message works in dartium, but not in dartc.
402 void handleExternalError(e, String message, [stack]) {
403 var msg = '$message\nCaught $e'; 395 var msg = '$message\nCaught $e';
404 396
405 if (currentTestCase != null) { 397 if (currentTestCase != null) {
406 currentTestCase._error(msg, stack); 398 _currentTestCase.error(msg, stackTrace);
407 } else { 399 } else {
408 _environment.uncaughtErrorMessage = "$msg: $stack"; 400 environment.uncaughtErrorMessage = "$msg: $stackTrace";
409 } 401 }
410 } 402 }
411 403
412 /// Filter the tests by [testFilter]. 404 /// Remove any tests that match [testFilter].
413 /// 405 ///
414 /// [testFilter] can be a [RegExp], a [String] or a 406 /// [testFilter] can be a predicate function, a [RegExp], or a [String]. If it's
415 /// predicate function. This is different from enabling or disabling tests 407 /// a function, it's called with each [TestCase]. If it's a [String], it's
416 /// in that it removes the tests completely. 408 /// parsed as a [RegExp] and matched against each [TestCase.description].
409 ///
410 /// This is different from enabling or disabling tests in that it removes the
411 /// tests completely.
417 void filterTests(testFilter) { 412 void filterTests(testFilter) {
418 var filterFunction; 413 var filterFunction;
419 if (testFilter is String) { 414 if (testFilter is String) {
420 RegExp re = new RegExp(testFilter); 415 var re = new RegExp(testFilter);
421 filterFunction = (t) => re.hasMatch(t.description); 416 filterFunction = (t) => re.hasMatch(t.description);
422 } else if (testFilter is RegExp) { 417 } else if (testFilter is RegExp) {
423 filterFunction = (t) => testFilter.hasMatch(t.description); 418 filterFunction = (t) => testFilter.hasMatch(t.description);
424 } else if (testFilter is Function) { 419 } else if (testFilter is Function) {
425 filterFunction = testFilter; 420 filterFunction = testFilter;
426 } 421 }
427 _testCases.retainWhere(filterFunction); 422 environment.testCases.retainWhere(filterFunction);
428 } 423 }
429 424
430 /// Runs all queued tests, one at a time. 425 /// Runs all queued tests, one at a time.
431 void runTests() { 426 void runTests() {
432 _requireNotRunning(); 427 _requireNotRunning();
433 _ensureInitialized(false); 428 _ensureInitialized(false);
434 _environment.currentTestCaseIndex = 0; 429 environment.currentTestCaseIndex = 0;
435 _config.onStart(); 430 config.onStart();
436 _runTest(); 431 _runTest();
437 } 432 }
438 433
439 /// Registers that an exception was caught for the current test. 434 /// Registers an exception that was caught for the current test.
440 void registerException(e, [trace]) { 435 void registerException(error, [StackTrace stackTrace]) =>
441 _registerException(currentTestCase, e, trace); 436 _currentTestCase.registerException(error, stackTrace);
442 }
443
444 /// Registers that an exception was caught for the current test.
445 void _registerException(TestCase testCase, e, [trace]) {
446 String message = (e is TestFailure) ? e.message : 'Caught $e';
447 if (testCase.result == null) {
448 testCase._fail(message, trace);
449 } else {
450 testCase._error(message, trace);
451 }
452 }
453 437
454 /// Runs the next test. 438 /// Runs the next test.
455 void _runTest() { 439 void _runTest() {
456 if (_environment.currentTestCaseIndex >= testCases.length) { 440 if (environment.currentTestCaseIndex >= testCases.length) {
457 assert(_environment.currentTestCaseIndex == testCases.length); 441 assert(environment.currentTestCaseIndex == testCases.length);
458 _completeTests(); 442 _completeTests();
459 } else { 443 return;
460 var testCase = testCases[_environment.currentTestCaseIndex]; 444 }
461 Future f = runZoned(testCase._run, onError: (error, stack) { 445
462 // TODO(kevmoo) Do a better job of flagging these are async errors. 446 var testCase = _currentTestCase;
463 // https://code.google.com/p/dart/issues/detail?id=16530 447 var f = runZoned(testCase.run, onError: (error, stack) {
464 _registerException(testCase, error, stack); 448 // TODO(kevmoo) Do a better job of flagging these are async errors.
465 }); 449 // https://code.google.com/p/dart/issues/detail?id=16530
466 450 testCase.registerException(error, stack);
467 var timeout = unittestConfiguration.timeout; 451 });
468 452
469 Timer timer; 453 var timer;
470 if (timeout != null) { 454 var timeout = unittestConfiguration.timeout;
471 try { 455 if (timeout != null) {
472 timer = new Timer(timeout, () { 456 try {
473 testCase._error("Test timed out after ${timeout.inSeconds} seconds."); 457 timer = new Timer(timeout, () {
474 _nextTestCase(); 458 testCase.error("Test timed out after ${timeout.inSeconds} seconds.");
475 }); 459 _nextTestCase();
476 } on UnsupportedError catch (e) { 460 });
477 if (e.message != "Timer greater than 0.") rethrow; 461 } on UnsupportedError catch (e) {
478 // Support running on d8 and jsshell which don't support timers. 462 if (e.message != "Timer greater than 0.") rethrow;
479 } 463 // Support running on d8 and jsshell which don't support timers.
480 } 464 }
481 f.whenComplete(() { 465 }
482 if (timer != null) timer.cancel(); 466
483 var now = new DateTime.now().millisecondsSinceEpoch; 467 f.whenComplete(() {
484 if ((now - _environment.lastBreath) >= BREATH_INTERVAL) { 468 if (timer != null) timer.cancel();
485 _environment.lastBreath = now; 469 var now = new DateTime.now().millisecondsSinceEpoch;
486 Timer.run(_nextTestCase); 470 if (now - environment.lastBreath >= BREATH_INTERVAL) {
487 } else { 471 environment.lastBreath = now;
488 scheduleMicrotask(_nextTestCase); // Schedule the next test. 472 Timer.run(_nextTestCase);
489 } 473 } else {
490 }); 474 scheduleMicrotask(_nextTestCase); // Schedule the next test.
491 } 475 }
492 } 476 });
493 477 }
494 /// Publish results on the page and notify controller. 478
479 /// Notify the configuration that the testing has finished.
495 void _completeTests() { 480 void _completeTests() {
496 if (!_environment.initialized) return; 481 if (!environment.initialized) return;
497 int passed = 0; 482
498 int failed = 0; 483 var passed = 0;
499 int errors = 0; 484 var failed = 0;
500 485 var errors = 0;
501 for (TestCase t in testCases) { 486 for (var testCase in testCases) {
502 switch (t.result) { 487 switch (testCase.result) {
503 case PASS: passed++; break; 488 case PASS: passed++; break;
504 case FAIL: failed++; break; 489 case FAIL: failed++; break;
505 case ERROR: errors++; break; 490 case ERROR: errors++; break;
506 } 491 }
507 } 492 }
508 _config.onSummary(passed, failed, errors, testCases, 493
509 _environment.uncaughtErrorMessage); 494 config.onSummary(passed, failed, errors, testCases,
510 _config.onDone(passed > 0 && failed == 0 && errors == 0 && 495 environment.uncaughtErrorMessage);
511 _environment.uncaughtErrorMessage == null); 496 config.onDone(passed > 0 && failed == 0 && errors == 0 &&
512 _environment.initialized = false; 497 environment.uncaughtErrorMessage == null);
513 _environment.currentTestCaseIndex = -1; 498 environment.initialized = false;
514 } 499 environment.currentTestCaseIndex = -1;
515 500 }
516 String _fullSpec(String spec) { 501
517 var group = '${_environment.currentContext.fullName}'; 502 /// Initializes the test environment if it hasn't already been initialized.
518 if (spec == null) return group;
519 return group != '' ? '$group$groupSep$spec' : spec;
520 }
521
522 /// Lazily initializes the test library if not already initialized.
523 void ensureInitialized() { 503 void ensureInitialized() {
524 _ensureInitialized(true); 504 _ensureInitialized(true);
525 } 505 }
526 506
507 /// Initializes the test environment.
508 ///
509 /// If [configAutoStart] is `true`, schedule a microtask to run the tests. This
510 /// microtask is expected to run after all the tests are defined.
527 void _ensureInitialized(bool configAutoStart) { 511 void _ensureInitialized(bool configAutoStart) {
528 if (_environment.initialized) { 512 if (environment.initialized) return;
529 return; 513
530 } 514 environment.initialized = true;
531 _environment.initialized = true;
532 // Hook our async guard into the matcher library. 515 // Hook our async guard into the matcher library.
533 wrapAsync = (f, [id]) => expectAsync(f, id: id); 516 wrapAsync = (f, [id]) => expectAsync(f, id: id);
534 517
535 _environment.uncaughtErrorMessage = null; 518 environment.uncaughtErrorMessage = null;
536 519
537 unittestConfiguration.onInit(); 520 unittestConfiguration.onInit();
538 521
539 if (configAutoStart && _config.autoStart) { 522 // Immediately queue the suite up. It will run after a timeout (i.e. after
540 // Immediately queue the suite up. It will run after a timeout (i.e. after 523 // main() has returned).
541 // main() has returned). 524 if (configAutoStart && config.autoStart) scheduleMicrotask(runTests);
542 scheduleMicrotask(runTests); 525 }
543 } 526
544 } 527 /// Remove all tests other than the one identified by [id].
545 528 void setSoloTest(int id) =>
546 /// Select a solo test by ID. 529 environment.testCases.retainWhere((t) => t.id == id);
547 void setSoloTest(int id) => _testCases.retainWhere((t) => t.id == id); 530
548 531 /// Enable the test identified by [id].
549 /// Enable/disable a test by ID. 532 void enableTest(int id) => _setTestEnabledState(id, enable: true);
550 void _setTestEnabledState(int testId, bool state) { 533
534 /// Disable the test by [id].
535 void disableTest(int id) => _setTestEnabledState(id, enable: false);
536
537 /// Enable or disable the test identified by [id].
538 void _setTestEnabledState(int id, {bool enable: true}) {
551 // Try fast path first. 539 // Try fast path first.
552 if (testCases.length > testId && testCases[testId].id == testId) { 540 if (testCases.length > id && testCases[id].id == id) {
553 testCases[testId]._enabled = state; 541 environment.testCases[id].enabled = enable;
554 } else { 542 } else {
555 for (var i = 0; i < testCases.length; i++) { 543 for (var i = 0; i < testCases.length; i++) {
556 if (testCases[i].id == testId) { 544 if (testCases[i].id != id) continue;
557 testCases[i]._enabled = state; 545 environment.testCases[i].enabled = enable;
558 break; 546 break;
559 }
560 } 547 }
561 } 548 }
562 } 549 }
563 550
564 /// Enable a test by ID. 551 /// Throws a [StateError] if tests are running.
565 void enableTest(int testId) => _setTestEnabledState(testId, true);
566
567 /// Disable a test by ID.
568 void disableTest(int testId) => _setTestEnabledState(testId, false);
569
570 /// Signature for a test function.
571 typedef dynamic TestFunction();
572
573 /// A flag that controls whether we hide unittest and core library details in
574 /// exception stacks.
575 ///
576 /// Useful to disable when debugging unittest or matcher customizations.
577 bool formatStacks = true;
578
579 /// A flag that controls whether we try to filter out irrelevant frames from
580 /// the stack trace.
581 ///
582 /// Requires [formatStacks] to be set.
583 bool filterStacks = true;
584
585 void _requireNotRunning() { 552 void _requireNotRunning() {
586 if (_environment.currentTestCaseIndex != -1) { 553 if (environment.currentTestCaseIndex == -1) return;
587 throw new StateError('Not allowed when tests are running.'); 554 throw new StateError('Not allowed when tests are running.');
588 } 555 }
589 } 556
590 557 /// Creates a test environment running in its own zone scope.
591 /// Method to create a test environment running in its own zone scope.
592 /// 558 ///
593 /// This allows for multiple invocations of the unittest library in the same 559 /// This allows for multiple invocations of the unittest library in the same
594 /// application instance. 560 /// application instance. This is useful when, for example, creating a test
595 /// This is useful when, for example, creating a test runner application which 561 /// runner application which needs to create a new pristine test environment on
596 /// needs to create a new pristine test environment on each invocation to run 562 /// each invocation to run a given set of tests.
597 /// a given set of test. 563 withTestEnvironment(callback()) {
598 dynamic withTestEnvironment(callback()) {
599 return runZoned(callback, 564 return runZoned(callback,
600 zoneValues: {_UNITTEST_ENVIRONMENT: new _TestEnvironment()}); 565 zoneValues: {#unittest.environment: new TestEnvironment()});
601 } 566 }
OLDNEW
« no previous file with comments | « lib/src/test_environment.dart ('k') | pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698