Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 144 | 144 |
| 145 import 'src/utils.dart'; | 145 import 'src/utils.dart'; |
| 146 | 146 |
| 147 import 'src/configuration.dart'; | 147 import 'src/configuration.dart'; |
| 148 export 'src/configuration.dart'; | 148 export 'src/configuration.dart'; |
| 149 | 149 |
| 150 part 'src/simple_configuration.dart'; | 150 part 'src/simple_configuration.dart'; |
| 151 part 'src/group_context.dart'; | 151 part 'src/group_context.dart'; |
| 152 part 'src/spread_args_helper.dart'; | 152 part 'src/spread_args_helper.dart'; |
| 153 part 'src/test_case.dart'; | 153 part 'src/test_case.dart'; |
| 154 part 'src/test_environment.dart'; | |
| 154 | 155 |
| 155 Configuration _config; | 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) | |
| 166 return _defaultEnvironment; | |
| 167 return environment; | |
| 168 } | |
| 169 | |
| 170 // Convenience getter for the current environment's config. | |
| 171 Configuration get _config => _environment.config; | |
| 172 | |
| 173 // Convenience setter for the current environment's config. | |
| 174 void set _config(Configuration config) { | |
| 175 _environment.config = config; | |
| 176 } | |
| 177 | |
| 178 // Convenience getter for the current environment's test cases. | |
| 179 List<TestCase> get _testCases => _environment.testCases; | |
| 156 | 180 |
| 157 /// [Configuration] used by the unittest library. | 181 /// [Configuration] used by the unittest library. |
| 158 /// | 182 /// |
| 159 /// Note that if a configuration has not been set, calling this getter will | 183 /// Note that if a configuration has not been set, calling this getter will |
| 160 /// create a default configuration. | 184 /// create a default configuration. |
| 161 Configuration get unittestConfiguration { | 185 Configuration get unittestConfiguration { |
| 162 if (_config == null) { | 186 if (_config == null) { |
| 163 _config = new Configuration(); | 187 _config = new Configuration(); |
| 164 } | 188 } |
| 165 return _config; | 189 return _config; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 178 } | 202 } |
| 179 | 203 |
| 180 /// Can be called by tests to log status. Tests should use this | 204 /// Can be called by tests to log status. Tests should use this |
| 181 /// instead of [print]. | 205 /// instead of [print]. |
| 182 void logMessage(String message) => | 206 void logMessage(String message) => |
| 183 _config.onLogMessage(currentTestCase, message); | 207 _config.onLogMessage(currentTestCase, message); |
| 184 | 208 |
| 185 /// Separator used between group names and test names. | 209 /// Separator used between group names and test names. |
| 186 String groupSep = ' '; | 210 String groupSep = ' '; |
| 187 | 211 |
| 188 final List<TestCase> _testCases = new List<TestCase>(); | |
| 189 | |
| 190 /// Tests executed in this suite. | 212 /// Tests executed in this suite. |
| 191 final List<TestCase> testCases = new UnmodifiableListView<TestCase>(_testCases); | 213 final List<TestCase> testCases = |
|
nweiz
2014/11/17 23:02:55
This should be a getter, not a field. As a field i
wibling
2014/11/18 10:16:49
Thanks for catching this. I missed that when chang
| |
| 214 new UnmodifiableListView<TestCase>(_environment.testCases); | |
| 192 | 215 |
| 193 /// Interval (in msecs) after which synchronous tests will insert an async | 216 /// Interval (in msecs) after which synchronous tests will insert an async |
| 194 /// delay to allow DOM or other updates. | 217 /// delay to allow DOM or other updates. |
| 195 const int BREATH_INTERVAL = 200; | 218 const int BREATH_INTERVAL = 200; |
| 196 | 219 |
| 197 /// The set of tests to run can be restricted by using [solo_test] and | |
| 198 /// [solo_group]. | |
| 199 /// As groups can be nested we use a counter to keep track of the nest level | |
| 200 /// of soloing, and a flag to tell if we have seen any solo tests. | |
| 201 int _soloNestingLevel = 0; | |
| 202 bool _soloTestSeen = false; | |
| 203 | |
| 204 // We use a 'dummy' context for the top level to eliminate null | |
| 205 // checks when querying the context. This allows us to easily | |
| 206 // support top-level setUp/tearDown functions as well. | |
| 207 final _rootContext = new _GroupContext(); | |
| 208 _GroupContext _currentContext = _rootContext; | |
| 209 | |
| 210 /// Represents the index of the currently running test case | |
| 211 /// == -1 implies the test system is not running | |
| 212 /// == [number of test cases] is a short-lived state flagging that the last test | |
| 213 /// has completed | |
| 214 int _currentTestCaseIndex = -1; | |
| 215 | |
| 216 /// [TestCase] currently being executed. | 220 /// [TestCase] currently being executed. |
| 217 TestCase get currentTestCase => | 221 TestCase get currentTestCase => |
| 218 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length) | 222 (_environment.currentTestCaseIndex >= 0 && |
| 219 ? testCases[_currentTestCaseIndex] | 223 _environment.currentTestCaseIndex < testCases.length) |
| 224 ? testCases[_environment.currentTestCaseIndex] | |
| 220 : null; | 225 : null; |
| 221 | 226 |
| 222 /// Whether the framework is in an initialized state. | |
| 223 bool _initialized = false; | |
| 224 | |
| 225 String _uncaughtErrorMessage = null; | |
| 226 | |
| 227 /// Time since we last gave non-sync code a chance to be scheduled. | |
| 228 int _lastBreath = new DateTime.now().millisecondsSinceEpoch; | |
| 229 | |
| 230 /* Test case result strings. */ | 227 /* Test case result strings. */ |
| 231 // TODO(gram) we should change these constants to use a different string | 228 // TODO(gram) we should change these constants to use a different string |
| 232 // (so that writing 'FAIL' in the middle of a test doesn't | 229 // (so that writing 'FAIL' in the middle of a test doesn't |
| 233 // imply that the test fails). We can't do it without also changing | 230 // imply that the test fails). We can't do it without also changing |
| 234 // the testrunner and test.dart though. | 231 // the testrunner and test.dart though. |
| 235 /// Result string for a passing test case. | 232 /// Result string for a passing test case. |
| 236 const PASS = 'pass'; | 233 const PASS = 'pass'; |
| 237 /// Result string for a failing test case. | 234 /// Result string for a failing test case. |
| 238 const FAIL = 'fail'; | 235 const FAIL = 'fail'; |
| 239 /// Result string for an test case with an error. | 236 /// Result string for an test case with an error. |
| 240 const ERROR = 'error'; | 237 const ERROR = 'error'; |
| 241 | 238 |
| 242 /// Creates a new test case with the given description and body. The | 239 /// Creates a new test case with the given description and body. The |
| 243 /// description will include the descriptions of any surrounding group() | 240 /// description will include the descriptions of any surrounding group() |
| 244 /// calls. | 241 /// calls. |
| 245 void test(String spec, TestFunction body) { | 242 void test(String spec, TestFunction body) { |
| 246 _requireNotRunning(); | 243 _requireNotRunning(); |
| 247 ensureInitialized(); | 244 ensureInitialized(); |
| 248 if (!_soloTestSeen || _soloNestingLevel > 0) { | 245 if (!_environment.soloTestSeen || _environment.soloNestingLevel > 0) { |
| 249 var testcase = new TestCase._internal(testCases.length + 1, _fullSpec(spec), | 246 var testcase = new TestCase._internal(testCases.length + 1, _fullSpec(spec), |
| 250 body); | 247 body); |
| 251 _testCases.add(testcase); | 248 _testCases.add(testcase); |
| 252 } | 249 } |
| 253 } | 250 } |
| 254 | 251 |
| 255 /// Convenience function for skipping a test. | 252 /// Convenience function for skipping a test. |
| 256 void skip_test(String spec, TestFunction body) {} | 253 void skip_test(String spec, TestFunction body) {} |
| 257 | 254 |
| 258 /// Creates a new test case with the given description and body. The | 255 /// Creates a new test case with the given description and body. The |
| 259 /// description will include the descriptions of any surrounding group() | 256 /// description will include the descriptions of any surrounding group() |
| 260 /// calls. | 257 /// calls. |
| 261 /// | 258 /// |
| 262 /// If we use [solo_test] (or [solo_group]) instead of test, then all non-solo | 259 /// If we use [solo_test] (or [solo_group]) instead of test, then all non-solo |
| 263 /// tests will be disabled. Note that if we use [solo_group], all tests in | 260 /// tests will be disabled. Note that if we use [solo_group], all tests in |
| 264 /// the group will be enabled, regardless of whether they use [test] or | 261 /// the group will be enabled, regardless of whether they use [test] or |
| 265 /// [solo_test], or whether they are in a nested [group] vs [solo_group]. Put | 262 /// [solo_test], or whether they are in a nested [group] vs [solo_group]. Put |
| 266 /// another way, if there are any calls to [solo_test] or [solo_group] in a test | 263 /// another way, if there are any calls to [solo_test] or [solo_group] in a test |
| 267 /// file, all tests that are not inside a [solo_group] will be disabled unless | 264 /// file, all tests that are not inside a [solo_group] will be disabled unless |
| 268 /// they are [solo_test]s. | 265 /// they are [solo_test]s. |
| 269 /// | 266 /// |
| 270 /// [skip_test] and [skip_group] take precedence over soloing, by virtue of the | 267 /// [skip_test] and [skip_group] take precedence over soloing, by virtue of the |
| 271 /// fact that they are effectively no-ops. | 268 /// fact that they are effectively no-ops. |
| 272 void solo_test(String spec, TestFunction body) { | 269 void solo_test(String spec, TestFunction body) { |
| 273 _requireNotRunning(); | 270 _requireNotRunning(); |
| 274 ensureInitialized(); | 271 ensureInitialized(); |
| 275 if (!_soloTestSeen) { | 272 if (!_environment.soloTestSeen) { |
| 276 _soloTestSeen = true; | 273 _environment.soloTestSeen = true; |
| 277 // This is the first solo-ed test. Discard all tests up to now. | 274 // This is the first solo-ed test. Discard all tests up to now. |
| 278 _testCases.clear(); | 275 _testCases.clear(); |
| 279 } | 276 } |
| 280 ++_soloNestingLevel; | 277 ++_environment.soloNestingLevel; |
| 281 try { | 278 try { |
| 282 test(spec, body); | 279 test(spec, body); |
| 283 } finally { | 280 } finally { |
| 284 --_soloNestingLevel; | 281 --_environment.soloNestingLevel; |
| 285 } | 282 } |
| 286 } | 283 } |
| 287 | 284 |
| 288 /// Indicate that [callback] is expected to be called a [count] number of times | 285 /// Indicate that [callback] is expected to be called a [count] number of times |
| 289 /// (by default 1). | 286 /// (by default 1). |
| 290 /// | 287 /// |
| 291 /// The unittest framework will wait for the callback to run the | 288 /// The unittest framework will wait for the callback to run the |
| 292 /// specified [count] times before it continues with the following test. Using | 289 /// specified [count] times before it continues with the following test. Using |
| 293 /// [expectAsync] will also ensure that errors that occur within [callback] are | 290 /// [expectAsync] will also ensure that errors that occur within [callback] are |
| 294 /// tracked and reported. [callback] should take 0 positional arguments (named | 291 /// tracked and reported. [callback] should take 0 positional arguments (named |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 328 {String id, String reason}) => | 325 {String id, String reason}) => |
| 329 new _SpreadArgsHelper(callback, 0, -1, id, reason, isDone: isDone).func; | 326 new _SpreadArgsHelper(callback, 0, -1, id, reason, isDone: isDone).func; |
| 330 | 327 |
| 331 /// Creates a new named group of tests. | 328 /// Creates a new named group of tests. |
| 332 /// | 329 /// |
| 333 /// Calls to group() or test() within the body of the function passed to this | 330 /// Calls to group() or test() within the body of the function passed to this |
| 334 /// named group will inherit this group's description. | 331 /// named group will inherit this group's description. |
| 335 void group(String description, void body()) { | 332 void group(String description, void body()) { |
| 336 ensureInitialized(); | 333 ensureInitialized(); |
| 337 _requireNotRunning(); | 334 _requireNotRunning(); |
| 338 _currentContext = new _GroupContext(_currentContext, description); | 335 _environment.currentContext = |
| 336 new _GroupContext(_environment.currentContext, description); | |
| 339 try { | 337 try { |
| 340 body(); | 338 body(); |
| 341 } catch (e, trace) { | 339 } catch (e, trace) { |
| 342 var stack = (trace == null) ? '' : ': ${trace.toString()}'; | 340 var stack = (trace == null) ? '' : ': ${trace.toString()}'; |
| 343 _uncaughtErrorMessage = "${e.toString()}$stack"; | 341 _environment.uncaughtErrorMessage = "${e.toString()}$stack"; |
| 344 } finally { | 342 } finally { |
| 345 // Now that the group is over, restore the previous one. | 343 // Now that the group is over, restore the previous one. |
| 346 _currentContext = _currentContext.parent; | 344 _environment.currentContext = _environment.currentContext.parent; |
| 347 } | 345 } |
| 348 } | 346 } |
| 349 | 347 |
| 350 /// Like [skip_test], but for groups. | 348 /// Like [skip_test], but for groups. |
| 351 void skip_group(String description, void body()) {} | 349 void skip_group(String description, void body()) {} |
| 352 | 350 |
| 353 /// Like [solo_test], but for groups. | 351 /// Like [solo_test], but for groups. |
| 354 void solo_group(String description, void body()) { | 352 void solo_group(String description, void body()) { |
| 355 _requireNotRunning(); | 353 _requireNotRunning(); |
| 356 ensureInitialized(); | 354 ensureInitialized(); |
| 357 if (!_soloTestSeen) { | 355 if (!_environment.soloTestSeen) { |
| 358 _soloTestSeen = true; | 356 _environment.soloTestSeen = true; |
| 359 // This is the first solo-ed group. Discard all tests up to now. | 357 // This is the first solo-ed group. Discard all tests up to now. |
| 360 _testCases.clear(); | 358 _testCases.clear(); |
| 361 } | 359 } |
| 362 ++_soloNestingLevel; | 360 ++_environment.soloNestingLevel; |
| 363 try { | 361 try { |
| 364 group(description, body); | 362 group(description, body); |
| 365 } finally { | 363 } finally { |
| 366 --_soloNestingLevel; | 364 --_environment.soloNestingLevel; |
| 367 } | 365 } |
| 368 } | 366 } |
| 369 | 367 |
| 370 /// Register a [setUp] function for a test [group]. | 368 /// Register a [setUp] function for a test [group]. |
| 371 /// | 369 /// |
| 372 /// This function will be called before each test in the group is run. | 370 /// This function will be called before each test in the group is run. |
| 373 /// [setUp] and [tearDown] should be called within the [group] before any | 371 /// [setUp] and [tearDown] should be called within the [group] before any |
| 374 /// calls to [test]. The [setupTest] function can be asynchronous; in this | 372 /// calls to [test]. The [setupTest] function can be asynchronous; in this |
| 375 /// case it must return a [Future]. | 373 /// case it must return a [Future]. |
| 376 void setUp(Function setupTest) { | 374 void setUp(Function setupTest) { |
| 377 _requireNotRunning(); | 375 _requireNotRunning(); |
| 378 _currentContext.testSetup = setupTest; | 376 _environment.currentContext.testSetup = setupTest; |
| 379 } | 377 } |
| 380 | 378 |
| 381 /// Register a [tearDown] function for a test [group]. | 379 /// Register a [tearDown] function for a test [group]. |
| 382 /// | 380 /// |
| 383 /// This function will be called after each test in the group is run. | 381 /// This function will be called after each test in the group is run. |
| 384 /// | 382 /// |
| 385 /// Note that if groups are nested only the most locally scoped [teardownTest] | 383 /// Note that if groups are nested only the most locally scoped [teardownTest] |
| 386 /// function will be run. [setUp] and [tearDown] should be called within the | 384 /// function will be run. [setUp] and [tearDown] should be called within the |
| 387 /// [group] before any calls to [test]. The [teardownTest] function can be | 385 /// [group] before any calls to [test]. The [teardownTest] function can be |
| 388 /// asynchronous; in this case it must return a [Future]. | 386 /// asynchronous; in this case it must return a [Future]. |
| 389 void tearDown(Function teardownTest) { | 387 void tearDown(Function teardownTest) { |
| 390 _requireNotRunning(); | 388 _requireNotRunning(); |
| 391 _currentContext.testTeardown = teardownTest; | 389 _environment.currentContext.testTeardown = teardownTest; |
| 392 } | 390 } |
| 393 | 391 |
| 394 /// Advance to the next test case. | 392 /// Advance to the next test case. |
| 395 void _nextTestCase() { | 393 void _nextTestCase() { |
| 396 _currentTestCaseIndex++; | 394 _environment.currentTestCaseIndex++; |
| 397 _runTest(); | 395 _runTest(); |
| 398 } | 396 } |
| 399 | 397 |
| 400 /// Handle errors that happen outside the tests. | 398 /// Handle errors that happen outside the tests. |
| 401 // TODO(vsm): figure out how to expose the stack trace here | 399 // TODO(vsm): figure out how to expose the stack trace here |
| 402 // Currently e.message works in dartium, but not in dartc. | 400 // Currently e.message works in dartium, but not in dartc. |
| 403 void handleExternalError(e, String message, [stack]) { | 401 void handleExternalError(e, String message, [stack]) { |
| 404 var msg = '$message\nCaught $e'; | 402 var msg = '$message\nCaught $e'; |
| 405 | 403 |
| 406 if (currentTestCase != null) { | 404 if (currentTestCase != null) { |
| 407 currentTestCase._error(msg, stack); | 405 currentTestCase._error(msg, stack); |
| 408 } else { | 406 } else { |
| 409 _uncaughtErrorMessage = "$msg: $stack"; | 407 _environment.uncaughtErrorMessage = "$msg: $stack"; |
| 410 } | 408 } |
| 411 } | 409 } |
| 412 | 410 |
| 413 /// Filter the tests by [testFilter]. | 411 /// Filter the tests by [testFilter]. |
| 414 /// | 412 /// |
| 415 /// [testFilter] can be a [RegExp], a [String] or a | 413 /// [testFilter] can be a [RegExp], a [String] or a |
| 416 /// predicate function. This is different from enabling or disabling tests | 414 /// predicate function. This is different from enabling or disabling tests |
| 417 /// in that it removes the tests completely. | 415 /// in that it removes the tests completely. |
| 418 void filterTests(testFilter) { | 416 void filterTests(testFilter) { |
| 419 var filterFunction; | 417 var filterFunction; |
| 420 if (testFilter is String) { | 418 if (testFilter is String) { |
| 421 RegExp re = new RegExp(testFilter); | 419 RegExp re = new RegExp(testFilter); |
| 422 filterFunction = (t) => re.hasMatch(t.description); | 420 filterFunction = (t) => re.hasMatch(t.description); |
| 423 } else if (testFilter is RegExp) { | 421 } else if (testFilter is RegExp) { |
| 424 filterFunction = (t) => testFilter.hasMatch(t.description); | 422 filterFunction = (t) => testFilter.hasMatch(t.description); |
| 425 } else if (testFilter is Function) { | 423 } else if (testFilter is Function) { |
| 426 filterFunction = testFilter; | 424 filterFunction = testFilter; |
| 427 } | 425 } |
| 428 _testCases.retainWhere(filterFunction); | 426 _testCases.retainWhere(filterFunction); |
| 429 } | 427 } |
| 430 | 428 |
| 431 /// Runs all queued tests, one at a time. | 429 /// Runs all queued tests, one at a time. |
| 432 void runTests() { | 430 void runTests() { |
| 433 _requireNotRunning(); | 431 _requireNotRunning(); |
| 434 _ensureInitialized(false); | 432 _ensureInitialized(false); |
| 435 _currentTestCaseIndex = 0; | 433 _environment.currentTestCaseIndex = 0; |
| 436 _config.onStart(); | 434 _config.onStart(); |
| 437 _runTest(); | 435 _runTest(); |
| 438 } | 436 } |
| 439 | 437 |
| 440 /// Registers that an exception was caught for the current test. | 438 /// Registers that an exception was caught for the current test. |
| 441 void registerException(e, [trace]) { | 439 void registerException(e, [trace]) { |
| 442 _registerException(currentTestCase, e, trace); | 440 _registerException(currentTestCase, e, trace); |
| 443 } | 441 } |
| 444 | 442 |
| 445 /// Registers that an exception was caught for the current test. | 443 /// Registers that an exception was caught for the current test. |
| 446 void _registerException(TestCase testCase, e, [trace]) { | 444 void _registerException(TestCase testCase, e, [trace]) { |
| 447 String message = (e is TestFailure) ? e.message : 'Caught $e'; | 445 String message = (e is TestFailure) ? e.message : 'Caught $e'; |
| 448 if (testCase.result == null) { | 446 if (testCase.result == null) { |
| 449 testCase._fail(message, trace); | 447 testCase._fail(message, trace); |
| 450 } else { | 448 } else { |
| 451 testCase._error(message, trace); | 449 testCase._error(message, trace); |
| 452 } | 450 } |
| 453 } | 451 } |
| 454 | 452 |
| 455 /// Runs the next test. | 453 /// Runs the next test. |
| 456 void _runTest() { | 454 void _runTest() { |
| 457 if (_currentTestCaseIndex >= testCases.length) { | 455 if (_environment.currentTestCaseIndex >= testCases.length) { |
| 458 assert(_currentTestCaseIndex == testCases.length); | 456 assert(_environment.currentTestCaseIndex == testCases.length); |
| 459 _completeTests(); | 457 _completeTests(); |
| 460 } else { | 458 } else { |
| 461 var testCase = testCases[_currentTestCaseIndex]; | 459 var testCase = testCases[_environment.currentTestCaseIndex]; |
| 462 Future f = runZoned(testCase._run, onError: (error, stack) { | 460 Future f = runZoned(testCase._run, onError: (error, stack) { |
| 463 // TODO(kevmoo) Do a better job of flagging these are async errors. | 461 // TODO(kevmoo) Do a better job of flagging these are async errors. |
| 464 // https://code.google.com/p/dart/issues/detail?id=16530 | 462 // https://code.google.com/p/dart/issues/detail?id=16530 |
| 465 _registerException(testCase, error, stack); | 463 _registerException(testCase, error, stack); |
| 466 }); | 464 }); |
| 467 | 465 |
| 468 var timeout = unittestConfiguration.timeout; | 466 var timeout = unittestConfiguration.timeout; |
| 469 | 467 |
| 470 Timer timer; | 468 Timer timer; |
| 471 if (timeout != null) { | 469 if (timeout != null) { |
| 472 try { | 470 try { |
| 473 timer = new Timer(timeout, () { | 471 timer = new Timer(timeout, () { |
| 474 testCase._error("Test timed out after ${timeout.inSeconds} seconds."); | 472 testCase._error("Test timed out after ${timeout.inSeconds} seconds."); |
| 475 _nextTestCase(); | 473 _nextTestCase(); |
| 476 }); | 474 }); |
| 477 } on UnsupportedError catch (e) { | 475 } on UnsupportedError catch (e) { |
| 478 if (e.message != "Timer greater than 0.") rethrow; | 476 if (e.message != "Timer greater than 0.") rethrow; |
| 479 // Support running on d8 and jsshell which don't support timers. | 477 // Support running on d8 and jsshell which don't support timers. |
| 480 } | 478 } |
| 481 } | 479 } |
| 482 f.whenComplete(() { | 480 f.whenComplete(() { |
| 483 if (timer != null) timer.cancel(); | 481 if (timer != null) timer.cancel(); |
| 484 var now = new DateTime.now().millisecondsSinceEpoch; | 482 var now = new DateTime.now().millisecondsSinceEpoch; |
| 485 if ((now - _lastBreath) >= BREATH_INTERVAL) { | 483 if ((now - _environment.lastBreath) >= BREATH_INTERVAL) { |
| 486 _lastBreath = now; | 484 _environment.lastBreath = now; |
| 487 Timer.run(_nextTestCase); | 485 Timer.run(_nextTestCase); |
| 488 } else { | 486 } else { |
| 489 scheduleMicrotask(_nextTestCase); // Schedule the next test. | 487 scheduleMicrotask(_nextTestCase); // Schedule the next test. |
| 490 } | 488 } |
| 491 }); | 489 }); |
| 492 } | 490 } |
| 493 } | 491 } |
| 494 | 492 |
| 495 /// Publish results on the page and notify controller. | 493 /// Publish results on the page and notify controller. |
| 496 void _completeTests() { | 494 void _completeTests() { |
| 497 if (!_initialized) return; | 495 if (!_environment.initialized) return; |
| 498 int passed = 0; | 496 int passed = 0; |
| 499 int failed = 0; | 497 int failed = 0; |
| 500 int errors = 0; | 498 int errors = 0; |
| 501 | 499 |
| 502 for (TestCase t in testCases) { | 500 for (TestCase t in testCases) { |
| 503 switch (t.result) { | 501 switch (t.result) { |
| 504 case PASS: passed++; break; | 502 case PASS: passed++; break; |
| 505 case FAIL: failed++; break; | 503 case FAIL: failed++; break; |
| 506 case ERROR: errors++; break; | 504 case ERROR: errors++; break; |
| 507 } | 505 } |
| 508 } | 506 } |
| 509 _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage); | 507 _config.onSummary(passed, failed, errors, testCases, |
| 508 _environment.uncaughtErrorMessage); | |
| 510 _config.onDone(passed > 0 && failed == 0 && errors == 0 && | 509 _config.onDone(passed > 0 && failed == 0 && errors == 0 && |
| 511 _uncaughtErrorMessage == null); | 510 _environment.uncaughtErrorMessage == null); |
| 512 _initialized = false; | 511 _environment.initialized = false; |
| 513 _currentTestCaseIndex = -1; | 512 _environment.currentTestCaseIndex = -1; |
| 514 } | 513 } |
| 515 | 514 |
| 516 String _fullSpec(String spec) { | 515 String _fullSpec(String spec) { |
| 517 var group = '${_currentContext.fullName}'; | 516 var group = '${_environment.currentContext.fullName}'; |
| 518 if (spec == null) return group; | 517 if (spec == null) return group; |
| 519 return group != '' ? '$group$groupSep$spec' : spec; | 518 return group != '' ? '$group$groupSep$spec' : spec; |
| 520 } | 519 } |
| 521 | 520 |
| 522 /// Lazily initializes the test library if not already initialized. | 521 /// Lazily initializes the test library if not already initialized. |
| 523 void ensureInitialized() { | 522 void ensureInitialized() { |
| 524 _ensureInitialized(true); | 523 _ensureInitialized(true); |
| 525 } | 524 } |
| 526 | 525 |
| 527 void _ensureInitialized(bool configAutoStart) { | 526 void _ensureInitialized(bool configAutoStart) { |
| 528 if (_initialized) { | 527 if (_environment.initialized) { |
| 529 return; | 528 return; |
| 530 } | 529 } |
| 531 _initialized = true; | 530 _environment.initialized = true; |
| 532 // Hook our async guard into the matcher library. | 531 // Hook our async guard into the matcher library. |
| 533 wrapAsync = (f, [id]) => expectAsync(f, id: id); | 532 wrapAsync = (f, [id]) => expectAsync(f, id: id); |
| 534 | 533 |
| 535 _uncaughtErrorMessage = null; | 534 _environment.uncaughtErrorMessage = null; |
| 536 | 535 |
| 537 unittestConfiguration.onInit(); | 536 unittestConfiguration.onInit(); |
| 538 | 537 |
| 539 if (configAutoStart && _config.autoStart) { | 538 if (configAutoStart && _config.autoStart) { |
| 540 // Immediately queue the suite up. It will run after a timeout (i.e. after | 539 // Immediately queue the suite up. It will run after a timeout (i.e. after |
| 541 // main() has returned). | 540 // main() has returned). |
| 542 scheduleMicrotask(runTests); | 541 scheduleMicrotask(runTests); |
| 543 } | 542 } |
| 544 } | 543 } |
| 545 | 544 |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 576 /// Useful to disable when debugging unittest or matcher customizations. | 575 /// Useful to disable when debugging unittest or matcher customizations. |
| 577 bool formatStacks = true; | 576 bool formatStacks = true; |
| 578 | 577 |
| 579 /// A flag that controls whether we try to filter out irrelevant frames from | 578 /// A flag that controls whether we try to filter out irrelevant frames from |
| 580 /// the stack trace. | 579 /// the stack trace. |
| 581 /// | 580 /// |
| 582 /// Requires [formatStacks] to be set. | 581 /// Requires [formatStacks] to be set. |
| 583 bool filterStacks = true; | 582 bool filterStacks = true; |
| 584 | 583 |
| 585 void _requireNotRunning() { | 584 void _requireNotRunning() { |
| 586 if (_currentTestCaseIndex != -1) { | 585 if (_environment.currentTestCaseIndex != -1) { |
| 587 throw new StateError('Not allowed when tests are running.'); | 586 throw new StateError('Not allowed when tests are running.'); |
| 588 } | 587 } |
| 589 } | 588 } |
| 589 | |
| 590 /// Method to create a test environment running in its own zone scope. | |
| 591 /// | |
| 592 /// This allows for multiple invocations of the unittest library in the same | |
| 593 /// application instance. | |
| 594 /// This is useful when, for example, creating a test runner application which | |
| 595 /// needs to create a new pristine test environment on each invocation to run | |
| 596 /// a given set of test. | |
| 597 dynamic withTestEnvironment(callback()) { | |
| 598 return runZoned(callback, | |
| 599 zoneValues: {_UNITTEST_ENVIRONMENT: new _TestEnvironment()}); | |
| 600 } | |
| OLD | NEW |