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