From 0a098e7f895842db421b7fad29eb1de0d1047ee8 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum <nweiz@google.com> Date: Thu, 19 Feb 2015 15:44:20 -0800 Subject: [PATCH] Replace the existing unittest APIs with the new runner infrastructure. This preserves the shape of the previous API, but stubs out the functionality. This is a temporary measure designed to make it easier for users to try the runner out on their existing tests. R=kevmoo@google.com Closes #2 Review URL: https://codereview.chromium.org//934413002 --- .status | 52 +- CHANGELOG.md | 22 +- lib/compact_vm_config.dart | 212 +------- lib/coverage_controller.js | 28 +- lib/html_config.dart | 174 +------ lib/html_enhanced_config.dart | 404 +-------------- lib/html_individual_config.dart | 51 +- lib/src/configuration.dart | 54 +- ...pected_function.dart => expect_async.dart} | 89 ++-- lib/src/future_matchers.dart | 20 +- lib/src/group_context.dart | 75 --- lib/src/internal_test_case.dart | 227 -------- lib/src/prints_matcher.dart | 14 +- lib/src/simple_configuration.dart | 113 +--- lib/src/test_case.dart | 42 +- lib/src/test_environment.dart | 74 --- lib/src/throws_matcher.dart | 34 +- lib/src/utils.dart | 40 -- lib/test_controller.js | 230 +------- lib/unittest.dart | 490 ++++-------------- lib/vm_config.dart | 59 +-- pubspec.yaml | 4 - test/async_exception_test.dart | 32 -- test/async_exception_with_future_test.dart | 42 -- test/async_setup_teardown_test.dart | 71 --- test/breath_test.dart | 51 -- test/completion_test.dart | 33 -- test/console_reporter_test.dart | 47 +- test/correct_callback_test.dart | 27 - test/exception_test.dart | 21 - test/excess_callback_test.dart | 42 -- test/expect_async_args_test.dart | 48 -- test/expect_async_test.dart | 348 ++++++++++--- test/future_matchers_test.dart | 98 ---- test/group_name_test.dart | 24 - test/invalid_ops_test.dart | 27 - test/io.dart | 16 +- test/late_exception_test.dart | 37 -- test/loader_test.dart | 9 +- test/matcher/completion_test.dart | 100 ++++ test/matcher/prints_test.dart | 149 ++++++ test/matcher/throws_test.dart | 149 ++++++ test/matcher/throws_type_test.dart | 178 +++++++ test/matchers_minified_test.dart | 139 ----- test/matchers_unminified_test.dart | 136 ----- test/middle_exception_test.dart | 32 -- test/missing_tick_test.dart | 27 - test/nested_groups_setup_teardown_test.dart | 64 --- test/prints_matcher_test.dart | 144 ----- test/protect_async_test.dart | 58 --- test/returning_future_test.dart | 70 --- .../returning_future_using_runasync_test.dart | 87 ---- test/runner_test.dart | 29 +- test/runtests_without_tests_test.dart | 19 - test/setup_and_teardown_test.dart | 35 -- test/setup_test.dart | 27 - test/single_correct_test.dart | 19 - test/single_failing_test.dart | 19 - test/skipped_soloed_nested_test.dart | 87 ---- test/teardown_test.dart | 30 -- test/test_utils.dart | 81 --- test/testcases_immutable_test.dart | 22 - test/throws_matchers_test.dart | 69 --- test/utils.dart | 169 +++++- test/vm_listener_test.dart | 22 +- test/with_test_environment_test.dart | 84 --- 66 files changed, 1381 insertions(+), 4145 deletions(-) rename lib/src/{expected_function.dart => expect_async.dart} (66%) delete mode 100644 lib/src/group_context.dart delete mode 100644 lib/src/internal_test_case.dart delete mode 100644 lib/src/test_environment.dart delete mode 100644 test/async_exception_test.dart delete mode 100644 test/async_exception_with_future_test.dart delete mode 100644 test/async_setup_teardown_test.dart delete mode 100644 test/breath_test.dart delete mode 100644 test/completion_test.dart delete mode 100644 test/correct_callback_test.dart delete mode 100644 test/exception_test.dart delete mode 100644 test/excess_callback_test.dart delete mode 100644 test/expect_async_args_test.dart delete mode 100644 test/future_matchers_test.dart delete mode 100644 test/group_name_test.dart delete mode 100644 test/invalid_ops_test.dart delete mode 100644 test/late_exception_test.dart create mode 100644 test/matcher/completion_test.dart create mode 100644 test/matcher/prints_test.dart create mode 100644 test/matcher/throws_test.dart create mode 100644 test/matcher/throws_type_test.dart delete mode 100644 test/matchers_minified_test.dart delete mode 100644 test/matchers_unminified_test.dart delete mode 100644 test/middle_exception_test.dart delete mode 100644 test/missing_tick_test.dart delete mode 100644 test/nested_groups_setup_teardown_test.dart delete mode 100644 test/prints_matcher_test.dart delete mode 100644 test/protect_async_test.dart delete mode 100644 test/returning_future_test.dart delete mode 100644 test/returning_future_using_runasync_test.dart delete mode 100644 test/runtests_without_tests_test.dart delete mode 100644 test/setup_and_teardown_test.dart delete mode 100644 test/setup_test.dart delete mode 100644 test/single_correct_test.dart delete mode 100644 test/single_failing_test.dart delete mode 100644 test/skipped_soloed_nested_test.dart delete mode 100644 test/teardown_test.dart delete mode 100644 test/test_utils.dart delete mode 100644 test/testcases_immutable_test.dart delete mode 100644 test/throws_matchers_test.dart delete mode 100644 test/with_test_environment_test.dart diff --git a/.status b/.status index b43df788..ff771caf 100644 --- a/.status +++ b/.status @@ -31,54 +31,4 @@ test/runner_test: SkipByDesign test/vm_listener_test: SkipByDesign [ $runtime == safari ] -test/prints_matcher_test: Fail # Issue 4 - -[ $runtime == jsshell ] -test/missing_tick_test: Fail # Timer interface not supported: dartbug.com/7728 -test/nested_groups_setup_teardown_test: RuntimeError # http://dartbug.com/10109 - -[ $compiler == none && ( $runtime == dartium || $runtime == drt || $runtime == ContentShellOnAndroid) ] -# Skip serialization test that explicitly has no library declaration in the -# test on Dartium, which requires all tests to have a library. -test/async_exception_test: RuntimeError # 13921 -test/async_exception_with_future_test: RuntimeError # 13921 -test/async_setup_teardown_test: RuntimeError # 13921 -test/completion_test: RuntimeError # 13921 -test/correct_callback_test: RuntimeError # 13921 -test/exception_test: RuntimeError # 13921 -test/excess_callback_test: RuntimeError # 13921 -test/expect_async_args_test: RuntimeError # 13921 -test/expect_async_test: RuntimeError # 13921 -test/future_matchers_test: RuntimeError # 13921 -test/group_name_test: RuntimeError # 13921 -test/invalid_ops_test: RuntimeError # 13921 -test/late_exception_test: RuntimeError # 13921 -test/middle_exception_test: RuntimeError # 13921 -test/nested_groups_setup_teardown_test: RuntimeError # 13921 -test/prints_matcher_test: RuntimeError # 13921 -test/protect_async_test: RuntimeError # 13921 -test/returning_future_test: RuntimeError # 13921 -test/returning_future_using_runasync_test: RuntimeError # 13921 -test/runtests_without_tests_test: RuntimeError # 13921 -test/setup_and_teardown_test: RuntimeError # 13921 -test/setup_test: RuntimeError # 13921 -test/single_correct_test: RuntimeError # 13921 -test/single_failing_test: RuntimeError # 13921 -test/skipped_soloed_nested_test: RuntimeError # 13921 -test/teardown_test: RuntimeError # 13921 -test/testcases_immutable_test: RuntimeError # 13921 - -[ $compiler == none && $browser ] -test/missing_tick_test: RuntimeError # Expected to fail, due to timeout. - -[ $compiler == dart2js && $minified ] -# The unminified matcher tests test that the real names of Dart types are -# printed. Minified versions of these tests exist that test the behavior when -# minified. -test/*_unminified_test: SkipByDesign # DO NOT COPY THIS UNLESS YOU WORK ON DART2JS - -[ $minified == false ] -# The minified matcher tests test that the minified names of Dart types are -# printed. Unminified versions of these tests exist that test the behavior when -# not minified. -test/*_minified_test: SkipByDesign # DO NOT COPY THIS UNLESS YOU WORK ON DART2JS +test/matcher/prints_test: Fail # Issue 4 diff --git a/CHANGELOG.md b/CHANGELOG.md index be183d2c..c5c1362b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,24 @@ -##0.12.0 +##0.12.0-alpha.0 + +* Added support for a test runner, which can be run via `pub run + unittest:unittest`. By default it runs all files recursively in the `test/` + directory that end in `_test.dart` and aren't in a `packages/` directory. + +* As part of moving to a runner-based model, most test configuration is moving + out of the test file and into the runner. As such, many ancillary APIs are + stubbed out and marked as deprecated. They still exist to make adoption + easier, but they're now no-ops and will be removed before the stable 0.12.0 + release. These APIs include `skip_` and `solo_` functions, `Configuration` and + all its subclasses, `TestCase`, `TestFunction`, `unittestConfiguration`, + `formatStacks`, `filterStacks`, `groupSep`, `logMessage`, `testCases`, + `BREATH_INTERVAL`, `currentTestCase`, `PASS`, `FAIL`, `ERROR`, `filterTests`, + `runTests`, `ensureInitialized`, `setSoloTest`, `enableTest`, `disableTest`, + and `withTestEnvironment`. * Removed `FailureHandler`, `DefaultFailureHandler`, `configureExpectFailureHandler`, and `getOrCreateExpectFailureHandler` which - are exported from the `matcher` package and will be removed. They existed - to enable integration between `unittest` and `matcher` that is being - streamlined. + used to be exported from the `matcher` package. They existed to enable + integration between `unittest` and `matcher` that has been streamlined. * Moved a number of APIs from `matcher` into `unittest`, including: `completes`, `completion`, `ErrorFormatter`, `expect`,`fail`, `prints`, diff --git a/lib/compact_vm_config.dart b/lib/compact_vm_config.dart index 1fadfc1c..fdc42e10 100644 --- a/lib/compact_vm_config.dart +++ b/lib/compact_vm_config.dart @@ -2,213 +2,19 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -/// A test configuration that generates a compact 1-line progress bar. The bar -/// is updated in-place before and after each test is executed. If all tests -/// pass, only a couple of lines are printed in the terminal. If a test fails, -/// the failure is shown and the progress bar continues to be updated below it. +@deprecated library unittest.compact_vm_config; -import 'dart:async'; -import 'dart:io'; -import 'dart:isolate'; - -import 'unittest.dart'; -import 'src/utils.dart'; import 'vm_config.dart'; -const String _GREEN = '\u001b[32m'; -const String _RED = '\u001b[31m'; -const String _NONE = '\u001b[0m'; - +@deprecated const int MAX_LINE = 80; -class CompactVMConfiguration extends VMConfiguration { - // The VM won't shut down if a receive port is open. Use this to make sure - // we correctly wait for asynchronous tests. - ReceivePort _receivePort; - - DateTime _start; - Set<int> _passing = new Set(); - Set<int> _failing = new Set(); - int get _pass => _passing.length; - int get _fail => _failing.length; - - void onInit() { - _receivePort = new ReceivePort(); - // Override and don't call the superclass onInit() to avoid printing the - // "unittest-suite-..." boilerplate. - } - - void onStart() { - _start = new DateTime.now(); - } - - void onTestStart(TestCase test) { - super.onTestStart(test); - _progressLine(test.description); - } - - void onTestResult(TestCase test) { - super.onTestResult(test); - if (test.result == PASS) { - _passing.add(test.id); - _progressLine(test.description); - } else { - _failing.add(test.id); - _progressLine(test.description); - _print(); - if (test.message != '') { - _print(indent(test.message)); - } - - if (test.stackTrace != null) { - _print(indent(test.stackTrace.toString())); - } - } - } - - void onTestResultChanged(TestCase test) { - _passing.remove(test.id); - _failing.add(test.id); - _progressLine(test.description); - _print(); - if (test.message != '') { - _print(indent(test.message)); - } - - if (test.stackTrace != null) { - _print(indent(test.stackTrace.toString())); - } - } - - void onDone(bool success) { - // Override and don't call the superclass onDone() to avoid printing the - // "unittest-suite-..." boilerplate. - Future.wait([stdout.close(), stderr.close()]).then((_) { - _receivePort.close(); - exit(success ? 0 : 1); - }); - } - - void onSummary(int passed, int failed, int errors, List<TestCase> results, - String uncaughtError) { - if (passed == 0 && failed == 0 && errors == 0 && uncaughtError == null) { - _print('\nNo tests ran.'); - } else if (failed == 0 && errors == 0 && uncaughtError == null) { - _progressLine('All tests passed!', _NONE); - _print(); - } else { - _progressLine('Some tests failed.', _RED); - _print(); - if (uncaughtError != null) { - _print('Top-level uncaught error: $uncaughtError'); - } - _print('$passed PASSED, $failed FAILED, $errors ERRORS'); - } - } - - int _lastLength = 0; - - final int _nonVisiblePrefix = 1 + _GREEN.length + _NONE.length; - - void _progressLine(String message, [String color = _NONE]) { - var duration = (new DateTime.now()).difference(_start); - var buffer = new StringBuffer(); - // \r moves back to the beginning of the current line. - buffer.write('\r${_timeString(duration)} '); - buffer.write(_GREEN); - buffer.write('+'); - buffer.write(_pass); - buffer.write(_NONE); - if (_fail != 0) { - buffer.write(_RED); - buffer.write(' -'); - buffer.write(_fail); - buffer.write(_NONE); - } - buffer.write(': '); - buffer.write(color); - - // Ensure the line fits under MAX_LINE. [buffer] includes the color escape - // sequences too. Because these sequences are not visible characters, we - // make sure they are not counted towards the limit. - int nonVisible = _nonVisiblePrefix + - color.length + - (_fail != 0 ? (_RED.length + _NONE.length) : 0); - int len = buffer.length - nonVisible; - buffer.write(_snippet(message, MAX_LINE - len)); - buffer.write(_NONE); - - // Pad the rest of the line so that it looks erased. - len = buffer.length - nonVisible - _NONE.length; - if (len > _lastLength) { - _lastLength = len; - } else { - while (len < _lastLength) { - buffer.write(' '); - _lastLength--; - } - } - stdout.write(buffer.toString()); - } - - String _padTime(int time) => - (time == 0) ? '00' : ((time < 10) ? '0$time' : '$time'); - - String _timeString(Duration duration) { - var min = duration.inMinutes; - var sec = duration.inSeconds % 60; - return '${_padTime(min)}:${_padTime(sec)}'; - } - - String _snippet(String text, int maxLength) { - // Return the full message if it fits - if (text.length <= maxLength) return text; - - // If we can fit the first and last three words, do so. - var words = text.split(' '); - if (words.length > 1) { - int i = words.length; - var len = words.first.length + 4; - do { - len += 1 + words[--i].length; - } while (len <= maxLength && i > 0); - if (len > maxLength || i == 0) i++; - if (i < words.length - 4) { - // Require at least 3 words at the end. - var buffer = new StringBuffer(); - buffer.write(words.first); - buffer.write(' ...'); - for ( ; i < words.length; i++) { - buffer.write(' '); - buffer.write(words[i]); - } - return buffer.toString(); - } - } - - // Otherwise truncate to return the trailing text, but attempt to start at - // the beginning of a word. - var res = text.substring(text.length - maxLength + 4); - var firstSpace = res.indexOf(' '); - if (firstSpace > 0) { - res = res.substring(firstSpace); - } - return '...$res'; - } -} - -// TODO(sigmund): delete when dartbug.com/17269 is fixed (use `print` instead). -_print([value = '']) => stdout.write('$value\n'); - -void useCompactVMConfiguration() { - // If the test is running on the Dart buildbots, we don't want to use this - // config since it's output may not be what the bots expect. - if (Platform.environment['LOGNAME'] == 'chrome-bot') { - return; - } - - unittestConfiguration = _singleton; -} +/// This is a stub class used to preserve compatibility with unittest 0.11.*. +/// +/// It will be removed before the next version is released. +@deprecated +class CompactVMConfiguration extends VMConfiguration {} -final _singleton = new CompactVMConfiguration(); +@deprecated +void useCompactVMConfiguration() {} diff --git a/lib/coverage_controller.js b/lib/coverage_controller.js index 1cf21ae1..11822790 100644 --- a/lib/coverage_controller.js +++ b/lib/coverage_controller.js @@ -2,30 +2,4 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -/** - * Coverage controller logic - used by coverage test harness to embed tests in - * content shell and extract coverage information. - */ - -var LONG_LINE = 60000; - -function onReceive(e) { - if (e.data == 'unittest-suite-done') { - var s = JSON.stringify(top._$jscoverage); - var res = ''; - // conent shell has a bug on lines longer than 2^16, so we split them - while (s.length > LONG_LINE) { - res += s.substr(0, LONG_LINE) + '<br>\n'; - s = s.substr(LONG_LINE); - } - res += s; - window.document.body.innerHTML = res; - window.layoutTestController.notifyDone(); - } -} - -if (window.layoutTestController) { - window.layoutTestController.dumpAsText(); - window.layoutTestController.waitUntilDone(); - window.addEventListener("message", onReceive, false); -} +/** This file is deprecated and will be removed before the next release. */ diff --git a/lib/html_config.dart b/lib/html_config.dart index 210f0f5e..4b8a32f2 100644 --- a/lib/html_config.dart +++ b/lib/html_config.dart @@ -2,174 +2,20 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -/// A simple unit test library for running tests in a browser. +@deprecated library unittest.html_config; -import 'dart:async'; -import 'dart:convert'; -import 'dart:html'; -import 'dart:js' as js; -import 'unittest.dart'; - -/// Creates a table showing tests results in HTML. -void _showResultsInPage(int passed, int failed, int errors, - List<TestCase> results, bool isLayoutTest, String uncaughtError) { - if (isLayoutTest && (passed == results.length) && uncaughtError == null) { - document.body.innerHtml = "PASS"; - } else { - var newBody = new StringBuffer(); - newBody.write("<table class='unittest-table'><tbody>"); - newBody.write(passed == results.length && uncaughtError == null - ? "<tr><td colspan='3' class='unittest-pass'>PASS</td></tr>" - : "<tr><td colspan='3' class='unittest-fail'>FAIL</td></tr>"); - - for (final test_ in results) { - newBody.write(_toHtml(test_)); - } - - if (uncaughtError != null) { - newBody.write('''<tr> - <td>--</td> - <td class="unittest-error">ERROR</td> - <td>Uncaught error: $uncaughtError</td> - </tr>'''); - } - - if (passed == results.length && uncaughtError == null) { - newBody.write(""" - <tr><td colspan='3' class='unittest-pass'> - All ${passed} tests passed - </td></tr>"""); - } else { - newBody.write(""" - <tr><td colspan='3'>Total - <span class='unittest-pass'>${passed} passed</span>, - <span class='unittest-fail'>${failed} failed</span> - <span class='unittest-error'> - ${errors + (uncaughtError == null ? 0 : 1)} errors</span> - </td></tr>"""); - } - newBody.write("</tbody></table>"); - document.body.innerHtml = newBody.toString(); - - window.onHashChange.listen((_) { - // Location may change from individual tests setting the hash tag. - if (window.location.hash != null && - window.location.hash.contains('testFilter')) { - window.location.reload(); - } - }); - } -} - -String _toHtml(TestCase test_) { - if (!test_.isComplete) { - return ''' - <tr> - <td>${test_.id}</td> - <td class="unittest-error">NO STATUS</td> - <td>Test did not complete</td> - </tr>'''; - } - - var html = ''' - <tr> - <td>${test_.id}</td> - <td class="unittest-${test_.result}">${test_.result.toUpperCase()}</td> - <td>Expectation: <a href="#testFilter=${test_.description}">${test_.description}</a>. ${HTML_ESCAPE.convert(test_.message)}</td> - </tr>'''; - - if (test_.stackTrace != null) { - html = '$html<tr><td></td><td colspan="2"><pre>' + - HTML_ESCAPE.convert(test_.stackTrace.toString()) + - '</pre></td></tr>'; - } - - return html; -} +import 'src/simple_configuration.dart'; +/// This is a stub class used to preserve compatibility with unittest 0.11.*. +/// +/// It will be removed before the next version is released. +@deprecated class HtmlConfiguration extends SimpleConfiguration { - /// Whether this is run within dartium layout tests. - final bool _isLayoutTest; - HtmlConfiguration(this._isLayoutTest); - - StreamSubscription<Event> _onErrorSubscription; - StreamSubscription<Event> _onMessageSubscription; - - void _installHandlers() { - if (_onErrorSubscription == null) { - _onErrorSubscription = window.onError.listen((e) { - // Some tests may expect this and have no way to suppress the error. - if (js.context['testExpectsGlobalError'] != true) { - handleExternalError(e, '(DOM callback has errors)'); - } - }); - } - if (_onMessageSubscription == null) { - _onMessageSubscription = - window.onMessage.listen((e) => processMessage(e)); - } - } - - void _uninstallHandlers() { - if (_onErrorSubscription != null) { - _onErrorSubscription.cancel(); - _onErrorSubscription = null; - } - if (_onMessageSubscription != null) { - _onMessageSubscription.cancel(); - _onMessageSubscription = null; - } - } - - void processMessage(e) { - if ('unittest-suite-external-error' == e.data) { - handleExternalError('<unknown>', '(external error detected)'); - } - } - - void onInit() { - // For Dart internal tests, we want to turn off stack frame - // filtering, which we do with this meta-header. - var meta = querySelector('meta[name="dart.unittest"]'); - filterStacks = - meta == null ? true : !meta.content.contains('full-stack-traces'); - _installHandlers(); - window.postMessage('unittest-suite-wait-for-done', '*'); - } - - void onStart() { - // If the URL has a #testFilter=testName then filter tests to that. - // This is used to make it easy to run a single test- but is only intended - // for interactive debugging scenarios. - var hash = window.location.hash; - if (hash != null && hash.length > 1) { - var params = hash.substring(1).split('&'); - for (var param in params) { - var parts = param.split('='); - if (parts.length == 2 && parts[0] == 'testFilter') { - filterTests('^${parts[1]}'); - } - } - } - super.onStart(); - } - - void onSummary(int passed, int failed, int errors, List<TestCase> results, - String uncaughtError) { - _showResultsInPage( - passed, failed, errors, results, _isLayoutTest, uncaughtError); - } - - void onDone(bool success) { - _uninstallHandlers(); - window.postMessage('unittest-suite-done', '*'); - } -} + HtmlConfiguration(bool isLayoutTest); -void useHtmlConfiguration([bool isLayoutTest = false]) { - unittestConfiguration = isLayoutTest ? _singletonLayout : _singletonNotLayout; + void processMessage(e) {} } -final _singletonLayout = new HtmlConfiguration(true); -final _singletonNotLayout = new HtmlConfiguration(false); +@deprecated +void useHtmlConfiguration([bool isLayoutTest]) {} diff --git a/lib/html_enhanced_config.dart b/lib/html_enhanced_config.dart index 0178ccb0..65e14194 100644 --- a/lib/html_enhanced_config.dart +++ b/lib/html_enhanced_config.dart @@ -2,404 +2,20 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -/// A simple unit test library for running tests in a browser. -/// -/// Provides enhanced HTML output with collapsible group headers -/// and other at-a-glance information about the test results. +@deprecated library unittest.html_enhanced_config; -import 'dart:collection' show LinkedHashMap; -import 'dart:convert'; -import 'dart:html'; -import 'unittest.dart'; +import 'src/simple_configuration.dart'; +/// This is a stub class used to preserve compatibility with unittest 0.11.*. +/// +/// It will be removed before the next version is released. +@deprecated class HtmlEnhancedConfiguration extends SimpleConfiguration { - /// Whether this is run within dartium layout tests. - final bool _isLayoutTest; - HtmlEnhancedConfiguration(this._isLayoutTest); - - var _onErrorSubscription = null; - var _onMessageSubscription = null; - - void _installOnErrorHandler() { - if (_onErrorSubscription == null) { - // Listen for uncaught errors. - _onErrorSubscription = window.onError - .listen((e) => handleExternalError(e, '(DOM callback has errors)')); - } - } - - void _installOnMessageHandler() { - if (_onMessageSubscription == null) { - // Listen for errors from JS. - _onMessageSubscription = - window.onMessage.listen((e) => processMessage(e)); - } - } - - void _installHandlers() { - _installOnErrorHandler(); - _installOnMessageHandler(); - } - - void _uninstallHandlers() { - if (_onErrorSubscription != null) { - _onErrorSubscription.cancel(); - _onErrorSubscription = null; - } - if (_onMessageSubscription != null) { - _onMessageSubscription.cancel(); - _onMessageSubscription = null; - } - } - - void processMessage(e) { - if ('unittest-suite-external-error' == e.data) { - handleExternalError('<unknown>', '(external error detected)'); - } - } - - void onInit() { - _installHandlers(); - //initialize and load CSS - final String _CSSID = '_unittestcss_'; - - var cssElement = document.head.querySelector('#${_CSSID}'); - if (cssElement == null) { - cssElement = new StyleElement(); - cssElement.id = _CSSID; - document.head.append(cssElement); - } - - cssElement.text = _htmlTestCSS; - window.postMessage('unittest-suite-wait-for-done', '*'); - } - - void onStart() { - // Listen for uncaught errors. - _installOnErrorHandler(); - } - - void onSummary(int passed, int failed, int errors, List<TestCase> results, - String uncaughtError) { - _showInteractiveResultsInPage( - passed, failed, errors, results, _isLayoutTest, uncaughtError); - } - - void onDone(bool success) { - _uninstallHandlers(); - window.postMessage('unittest-suite-done', '*'); - } - - void _showInteractiveResultsInPage(int passed, int failed, int errors, - List<TestCase> results, bool isLayoutTest, String uncaughtError) { - if (isLayoutTest && passed == results.length) { - document.body.innerHtml = "PASS"; - } else { - // changed the StringBuffer to an Element fragment - Element te = new Element.html('<div class="unittest-table"></div>'); - - te.children.add(new Element.html(passed == results.length - ? "<div class='unittest-overall unittest-pass'>PASS</div>" - : "<div class='unittest-overall unittest-fail'>FAIL</div>")); - - // moved summary to the top since web browsers - // don't auto-scroll to the bottom like consoles typically do. - if (passed == results.length && uncaughtError == null) { - te.children.add(new Element.html(""" - <div class='unittest-pass'>All ${passed} tests passed</div>""")); - } else { - if (uncaughtError != null) { - te.children.add(new Element.html(""" - <div class='unittest-summary'> - <span class='unittest-error'>Uncaught error: $uncaughtError</span> - </div>""")); - } - - te.children.add(new Element.html(""" - <div class='unittest-summary'> - <span class='unittest-pass'>Total ${passed} passed</span>, - <span class='unittest-fail'>${failed} failed</span>, - <span class='unittest-error'> - ${errors + (uncaughtError == null ? 0 : 1)} errors</span> - </div>""")); - } - - te.children.add(new Element.html(""" - <div><button id='btnCollapseAll'>Collapse All</button></div> - """)); - - // handle the click event for the collapse all button - te.querySelector('#btnCollapseAll').onClick.listen((_) { - document - .querySelectorAll('.unittest-row') - .forEach((el) => el.attributes['class'] = el.attributes['class'] - .replaceAll('unittest-row ', 'unittest-row-hidden ')); - }); - - var previousGroup = ''; - var groupPassFail = true; - - // order by group and sort numerically within each group - var groupedBy = new LinkedHashMap<String, List<TestCase>>(); - - for (final t in results) { - if (!groupedBy.containsKey(t.currentGroup)) { - groupedBy[t.currentGroup] = new List<TestCase>(); - } - - groupedBy[t.currentGroup].add(t); - } - - // flatten the list again with tests ordered - List<TestCase> flattened = new List<TestCase>(); - - groupedBy.values.forEach((tList) { - tList.sort((tcA, tcB) => tcA.id - tcB.id); - flattened.addAll(tList); - }); - - var nonAlphanumeric = new RegExp('[^a-z0-9A-Z]'); - - // output group headers and test rows - for (final test_ in flattened) { - - // replace everything but numbers and letters from the group name with - // '_' so we can use in id and class properties. - var safeGroup = test_.currentGroup.replaceAll(nonAlphanumeric, '_'); - - if (test_.currentGroup != previousGroup) { - previousGroup = test_.currentGroup; - - var testsInGroup = results - .where((TestCase t) => t.currentGroup == previousGroup) - .toList(); - var groupTotalTestCount = testsInGroup.length; - var groupTestPassedCount = - testsInGroup.where((TestCase t) => t.result == 'pass').length; - groupPassFail = groupTotalTestCount == groupTestPassedCount; - var passFailClass = "unittest-group-status unittest-group-" - "status-${groupPassFail ? 'pass' : 'fail'}"; - - te.children.add(new Element.html(""" - <div> - <div id='${safeGroup}' - class='unittest-group ${safeGroup} test${safeGroup}'> - <div ${_isIE ? "style='display:inline-block' ": ""} - class='unittest-row-status'> - <div class='$passFailClass'></div> - </div> - <div ${_isIE ? "style='display:inline-block' ": ""}> - ${test_.currentGroup}</div> - - <div ${_isIE ? "style='display:inline-block' ": ""}> - (${groupTestPassedCount}/${groupTotalTestCount})</div> - </div> - </div>""")); - - // 'safeGroup' could be empty - var grp = - (safeGroup == '') ? null : te.querySelector('#${safeGroup}'); - if (grp != null) { - grp.onClick.listen((_) { - var row = document.querySelector('.unittest-row-${safeGroup}'); - if (row.attributes['class'].contains('unittest-row ')) { - document.querySelectorAll('.unittest-row-${safeGroup}').forEach( - (e) => e.attributes['class'] = e.attributes['class'] - .replaceAll('unittest-row ', 'unittest-row-hidden ')); - } else { - document.querySelectorAll('.unittest-row-${safeGroup}').forEach( - (e) => e.attributes['class'] = e.attributes['class'] - .replaceAll('unittest-row-hidden', 'unittest-row')); - } - }); - } - } - - _buildRow(test_, te, safeGroup, !groupPassFail); - } - - document.body.children.clear(); - document.body.children.add(te); - } - } - - void _buildRow(TestCase test_, Element te, String groupID, bool isVisible) { - var background = 'unittest-row-${test_.id % 2 == 0 ? "even" : "odd"}'; - var display = '${isVisible ? "unittest-row" : "unittest-row-hidden"}'; - - addRowElement(id, status, description) { - te.children.add(new Element.html(''' <div> - <div class='$display unittest-row-${groupID} $background'> - <div ${_isIE ? "style='display:inline-block' ": ""} - class='unittest-row-id'>$id</div> - <div ${_isIE ? "style='display:inline-block' ": ""} - class="unittest-row-status unittest-${test_.result}"> - $status</div> - <div ${_isIE ? "style='display:inline-block' ": ""} - class='unittest-row-description'>$description</div> - </div> - </div>''')); - } - - if (!test_.isComplete) { - addRowElement('${test_.id}', 'NO STATUS', 'Test did not complete.'); - return; - } - - addRowElement('${test_.id}', '${test_.result.toUpperCase()}', - '${test_.description}. ${HTML_ESCAPE.convert(test_.message)}'); - - if (test_.stackTrace != null) { - addRowElement('', '', - '<pre>${HTML_ESCAPE.convert(test_.stackTrace.toString())}</pre>'); - } - } - - static bool get _isIE => window.navigator.userAgent.contains('MSIE'); - - String get _htmlTestCSS => ''' - body{ - font-size: 14px; - font-family: 'Open Sans', 'Lucida Sans Unicode', 'Lucida Grande',''' - ''' sans-serif; - background: WhiteSmoke; - } - - .unittest-group - { - background: rgb(75,75,75); - width:98%; - color: WhiteSmoke; - font-weight: bold; - padding: 6px; - cursor: pointer; - - /* Provide some visual separation between groups for IE */ - ${_isIE ? "border-bottom:solid black 1px;": ""} - ${_isIE ? "border-top:solid #777777 1px;": ""} - - background-image: -webkit-linear-gradient(bottom, rgb(50,50,50) 0%, ''' - '''rgb(100,100,100) 100%); - background-image: -moz-linear-gradient(bottom, rgb(50,50,50) 0%, ''' - '''rgb(100,100,100) 100%); - background-image: -ms-linear-gradient(bottom, rgb(50,50,50) 0%, ''' - '''rgb(100,100,100) 100%); - background-image: linear-gradient(bottom, rgb(50,50,50) 0%, ''' - '''rgb(100,100,100) 100%); - - display: -webkit-box; - display: -moz-box; - display: -ms-box; - display: box; - - -webkit-box-orient: horizontal; - -moz-box-orient: horizontal; - -ms-box-orient: horizontal; - box-orient: horizontal; - - -webkit-box-align: center; - -moz-box-align: center; - -ms-box-align: center; - box-align: center; - } - - .unittest-group-status - { - width: 20px; - height: 20px; - border-radius: 20px; - margin-left: 10px; - } - - .unittest-group-status-pass{ - background: Green; - background: ''' - '''-webkit-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%); - background: ''' - '''-moz-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%); - background: ''' - '''-ms-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%); - background: ''' - '''radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%); - } - - .unittest-group-status-fail{ - background: Red; - background: ''' - '''-webkit-radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%); - background: ''' - '''-moz-radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%); - background: ''' - '''-ms-radial-gradient(center, ellipse cover, #AAFFAA 0%,Green 100%); - background: radial-gradient(center, ellipse cover, #FFAAAA 0%,Red 100%); - } - - .unittest-overall{ - font-size: 20px; - } - - .unittest-summary{ - font-size: 18px; - } - - .unittest-pass{ - color: Green; - } - - .unittest-fail, .unittest-error - { - color: Red; - } - - .unittest-row - { - display: -webkit-box; - display: -moz-box; - display: -ms-box; - display: box; - -webkit-box-orient: horizontal; - -moz-box-orient: horizontal; - -ms-box-orient: horizontal; - box-orient: horizontal; - width: 100%; - } - - .unittest-row-hidden - { - display: none; - } - - .unittest-row-odd - { - background: WhiteSmoke; - } - - .unittest-row-even - { - background: #E5E5E5; - } - - .unittest-row-id - { - width: 3em; - } - - .unittest-row-status - { - width: 4em; - } - - .unittest-row-description - { - } - - '''; -} + HtmlEnhancedConfiguration(bool isLayoutTest); -void useHtmlEnhancedConfiguration([bool isLayoutTest = false]) { - unittestConfiguration = isLayoutTest ? _singletonLayout : _singletonNotLayout; + void processMessage(e) {} } -final _singletonLayout = new HtmlEnhancedConfiguration(true); -final _singletonNotLayout = new HtmlEnhancedConfiguration(false); +@deprecated +void useHtmlEnhancedConfiguration([bool isLayoutTest]) {} diff --git a/lib/html_individual_config.dart b/lib/html_individual_config.dart index 263adf98..bc2a0403 100644 --- a/lib/html_individual_config.dart +++ b/lib/html_individual_config.dart @@ -2,51 +2,18 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -/// A unit test library for running groups of tests in a browser, instead of the -/// entire test file. This is especially used for large tests files that have -/// many subtests, so we can mark groups as failing at a finer granularity than -/// the entire test file. -/// -/// To use, import this file, and call [useHtmlIndividualConfiguration] at the -/// start of your set sequence. Important constraint: your group descriptions -/// MUST NOT contain spaces. +@deprecated library unittest.html_individual_config; -import 'dart:html'; -import 'unittest.dart' as unittest; -import 'html_config.dart' as htmlconfig; +import 'html_config.dart'; -class HtmlIndividualConfiguration extends htmlconfig.HtmlConfiguration { +/// This is a stub class used to preserve compatibility with unittest 0.11.*. +/// +/// It will be removed before the next version is released. +@deprecated +class HtmlIndividualConfiguration extends HtmlConfiguration { HtmlIndividualConfiguration(bool isLayoutTest) : super(isLayoutTest); - - void onStart() { - var search = window.location.search; - if (search != '') { - var groups = search - .substring(1) - .split('&') - .where((p) => p.startsWith('group=')) - .toList(); - - if (!groups.isEmpty) { - if (groups.length > 1) { - throw new ArgumentError('More than one "group" parameter provided.'); - } - - var testGroupName = groups.single.split('=')[1]; - var startsWith = "$testGroupName${unittest.groupSep}"; - unittest.filterTests( - (unittest.TestCase tc) => tc.description.startsWith(startsWith)); - } - } - super.onStart(); - } -} - -void useHtmlIndividualConfiguration([bool isLayoutTest = false]) { - unittest.unittestConfiguration = - isLayoutTest ? _singletonLayout : _singletonNotLayout; } -final _singletonLayout = new HtmlIndividualConfiguration(true); -final _singletonNotLayout = new HtmlIndividualConfiguration(false); +@deprecated +void useHtmlIndividualConfiguration([bool isLayoutTest]) {} diff --git a/lib/src/configuration.dart b/lib/src/configuration.dart index d3759667..27969bdc 100644 --- a/lib/src/configuration.dart +++ b/lib/src/configuration.dart @@ -4,67 +4,25 @@ library unittest.configuration; -import 'simple_configuration.dart'; import 'test_case.dart'; -/// Describes the interface used by the unit test system for communicating the -/// results of a test run. -abstract class Configuration { - /// Creates an instance of [SimpleConfiguration]. - factory Configuration() => new SimpleConfiguration(); - - /// Creates an [Configuration] instances that does nothing. - /// - /// For use by subclasses which wish to implement only a subset of features. +/// This is a stub class used to preserve compatibility with unittest 0.11.*. +/// +/// It will be removed before the next version is released. +@deprecated +class Configuration { + Configuration(); Configuration.blank(); - /// If `true`, tests are started automatically once they're finished being - /// defined. - /// - /// Otherwise, [runTests] must be called explicitly after tests are set up. final autoStart = true; - - /// How long a [TestCase] can run before it is considered an error. - /// A [timeout] value of [:null:] means that the limit is infinite. Duration timeout = const Duration(minutes: 2); - - /// Called as soon as the unittest framework becomes initialized. - /// - /// This is done even before tests are added to the test framework. It might - /// be used to determine/debug errors that occur before the test harness - /// starts executing. It is also used to tell the vm or browser that tests are - /// going to be run asynchronously and that the process should wait until they - /// are done. void onInit() {} - - /// Called as soon as the unittest framework starts running. void onStart() {} - - /// Called when each test starts. Useful to show intermediate progress on - /// a test suite. void onTestStart(TestCase testCase) {} - - /// Called when each test is first completed. Useful to show intermediate - /// progress on a test suite. void onTestResult(TestCase testCase) {} - - /// Called when an already completed test changes state. For example: a test - /// that was marked as passing may later be marked as being in error because - /// it still had callbacks being invoked. void onTestResultChanged(TestCase testCase) {} - - /// Handles the logging of messages by a test case. void onLogMessage(TestCase testCase, String message) {} - - /// Called when the unittest framework is done running. [success] indicates - /// whether all tests passed successfully. void onDone(bool success) {} - - /// Called with the result of all test cases. Browser tests commonly override - /// this to reformat the output. - /// - /// When [uncaughtError] is not null, it contains an error that occured outside - /// of tests (e.g. setting up the test). void onSummary(int passed, int failed, int errors, List<TestCase> results, String uncaughtError) {} } diff --git a/lib/src/expected_function.dart b/lib/src/expect_async.dart similarity index 66% rename from lib/src/expected_function.dart rename to lib/src/expect_async.dart index 7373c755..0179f851 100644 --- a/lib/src/expected_function.dart +++ b/lib/src/expect_async.dart @@ -2,11 +2,11 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library unittest.expected_function; +library unittest.expect_async; -import '../unittest.dart'; - -import 'internal_test_case.dart'; +import 'expect.dart'; +import 'invoker.dart'; +import 'state.dart'; /// An object used to detect unpassed arguments. const _PLACEHOLDER = const Object(); @@ -30,7 +30,7 @@ typedef bool _IsDoneCallback(); /// /// The wrapper function is accessible via [func]. It supports up to six /// optional and/or required positional arguments, but no named arguments. -class ExpectedFunction { +class _ExpectedFunction { /// The wrapped callback. final Function _callback; @@ -65,8 +65,8 @@ class ExpectedFunction { /// The number of times the function has been called. int _actualCalls = 0; - /// The test case in which this function was wrapped. - final InternalTestCase _testCase; + /// The test invoker in which this function was wrapped. + final Invoker _invoker; /// Whether this function has been called the requisite number of times. bool _complete; @@ -77,7 +77,7 @@ class ExpectedFunction { /// If passed, [id] is used as a descriptive name fo the function and [reason] /// as a reason it's expected to be called. If [isDone] is passed, the test /// won't be allowed to complete until it returns `true`. - ExpectedFunction(Function callback, int minExpected, int maxExpected, + _ExpectedFunction(Function callback, int minExpected, int maxExpected, {String id, String reason, bool isDone()}) : this._callback = callback, _minExpectedCalls = minExpected, @@ -86,16 +86,17 @@ class ExpectedFunction { : maxExpected, this._isDone = isDone, this._reason = reason == null ? '' : '\n$reason', - this._testCase = currentTestCase as InternalTestCase, + this._invoker = Invoker.current, this._id = _makeCallbackId(id, callback) { - ensureInitialized(); - if (_testCase == null) { - throw new StateError("No valid test. Did you forget to run your test " - "inside a call to test()?"); + if (_invoker == null) { + throw new StateError("[expectAsync] was called outside of a test."); + } else if (maxExpected > 0 && minExpected > maxExpected) { + throw new ArgumentError("max ($maxExpected) may not be less than count " + "($minExpected)."); } if (isDone != null || minExpected > 0) { - _testCase.callbackFunctionsOutstanding++; + _invoker.addOutstandingCallback(); _complete = false; } else { _complete = true; @@ -133,6 +134,7 @@ class ExpectedFunction { if (_callback is _Func1) return _max1; if (_callback is _Func0) return _max0; + _invoker.removeOutstandingCallback(); throw new ArgumentError( 'The wrapped function has more than 6 required arguments'); } @@ -159,22 +161,16 @@ class ExpectedFunction { _run([a0, a1, a2, a3, a4, a5].where((a) => a != _PLACEHOLDER)); /// Runs the wrapped function with [args] and returns its return value. - /// - /// This will pass any errors on to [_testCase] and return `null`. _run(Iterable args) { + // Note that in the old unittest, this returned `null` if it encountered an + // error, where now it just re-throws that error because Zone machinery will + // pass it to the invoker anyway. try { _actualCalls++; - if (_testCase.isComplete) { - // Don't run the callback if the test is done. We don't throw here as - // this is not the current test, but we do mark the old test as having - // an error if it previously passed. - if (_testCase.result == PASS) { - _testCase.error( - 'Callback ${_id}called ($_actualCalls) after test case ' - '${_testCase.description} had already been marked as ' - '${_testCase.result}.$_reason'); - } - return null; + if (_invoker.liveTest.isComplete && + _invoker.liveTest.state.result == Result.success) { + throw 'Callback ${_id}called ($_actualCalls) after test case ' + '${_invoker.liveTest.test.name} had already completed.$_reason'; } else if (_maxExpectedCalls >= 0 && _actualCalls > _maxExpectedCalls) { throw new TestFailure('Callback ${_id}called more times than expected ' '($_maxExpectedCalls).$_reason'); @@ -182,7 +178,7 @@ class ExpectedFunction { return Function.apply(_callback, args.toList()); } catch (error, stackTrace) { - _testCase.registerException(error, stackTrace); + _invoker.handleError(error, stackTrace); return null; } finally { _afterRun(); @@ -198,6 +194,41 @@ class ExpectedFunction { // Mark this callback as complete and remove it from the test case's // oustanding callback count; if that hits zero the test is done. _complete = true; - _testCase.markCallbackComplete(); + _invoker.removeOutstandingCallback(); } } + +/// Indicate that [callback] is expected to be called [count] number of times +/// (by default 1). +/// +/// The unittest framework will wait for the callback to run the [count] times +/// before it considers the current test to be complete. [callback] may take up +/// to six optional or required positional arguments; named arguments are not +/// supported. +/// +/// [max] can be used to specify an upper bound on the number of calls; if this +/// is exceeded the test will fail. If [max] is `0` (the default), the callback +/// is expected to be called exactly [count] times. If [max] is `-1`, the +/// callback is allowed to be called any number of times greater than [count]. +/// +/// Both [id] and [reason] are optional and provide extra information about the +/// callback when debugging. [id] should be the name of the callback, while +/// [reason] should be the reason the callback is expected to be called. +Function expectAsync(Function callback, + {int count: 1, int max: 0, String id, String reason}) => + new _ExpectedFunction(callback, count, max, id: id, reason: reason).func; + +/// Indicate that [callback] is expected to be called until [isDone] returns +/// true. +/// +/// [isDone] is called after each time the function is run. Only when it returns +/// true will the callback be considered complete. [callback] may take up to six +/// optional or required positional arguments; named arguments are not +/// supported. +/// +/// Both [id] and [reason] are optional and provide extra information about the +/// callback when debugging. [id] should be the name of the callback, while +/// [reason] should be the reason the callback is expected to be called. +Function expectAsyncUntil(Function callback, bool isDone(), + {String id, String reason}) => new _ExpectedFunction(callback, 0, -1, + id: id, reason: reason, isDone: isDone).func; diff --git a/lib/src/future_matchers.dart b/lib/src/future_matchers.dart index 5d873383..2ae13eeb 100644 --- a/lib/src/future_matchers.dart +++ b/lib/src/future_matchers.dart @@ -8,7 +8,9 @@ import 'dart:async'; import 'package:matcher/matcher.dart' hide throws, throwsA, expect, fail; -import '../unittest.dart'; +import 'expect.dart'; +import 'invoker.dart'; +import 'utils.dart'; /// Matches a [Future] that completes successfully with a value. /// @@ -43,22 +45,26 @@ class _Completes extends Matcher { bool matches(item, Map matchState) { if (item is! Future) return false; - var done = expectAsync((fn) => fn(), id: _id); + Invoker.current.addOutstandingCallback(); item.then((value) { - done(() { - if (_matcher != null) expect(value, _matcher); - }); + if (_matcher != null) expect(value, _matcher); + Invoker.current.removeOutstandingCallback(); }, onError: (error, trace) { + if (error is TestFailure) { + Invoker.current.handleError(error, trace); + return; + } + var id = _id == '' ? '' : '${_id} '; var reason = 'Expected future ${id}to complete successfully, ' 'but it failed with ${error}'; if (trace != null) { - var stackTrace = trace.toString(); + var stackTrace = terseChain(trace).toString(); stackTrace = ' ${stackTrace.replaceAll('\n', '\n ')}'; reason = '$reason\nStack trace:\n$stackTrace'; } - done(() => fail(reason)); + fail(reason); }); return true; diff --git a/lib/src/group_context.dart b/lib/src/group_context.dart deleted file mode 100644 index 78f347b4..00000000 --- a/lib/src/group_context.dart +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.group_context; - -import 'dart:async'; - -import '../unittest.dart'; - -/// Setup and teardown functions for a group and its parents, the latter -/// for chaining. -class GroupContext { - /// The parent context, or `null`. - final GroupContext parent; - - /// Whether this is the root context. - bool get isRoot => parent == null; - - /// Description text of the current test group. - final String _name; - - /// The set-up function called before each test in a group. - Function get testSetUp => _testSetUp; - Function _testSetUp; - - set testSetUp(Function setUp) { - if (parent == null || parent.testSetUp == null) { - _testSetUp = setUp; - return; - } - - _testSetUp = () { - var f = parent.testSetUp(); - if (f is Future) { - return f.then((_) => setUp()); - } else { - return setUp(); - } - }; - } - - /// The tear-down function called after each test in a group. - Function get testTearDown => _testTearDown; - Function _testTearDown; - - set testTearDown(Function tearDown) { - if (parent == null || parent.testTearDown == null) { - _testTearDown = tearDown; - return; - } - - _testTearDown = () { - var f = tearDown(); - if (f is Future) { - return f.then((_) => parent.testTearDown()); - } else { - return parent.testTearDown(); - } - }; - } - - /// Returns the fully-qualified name of this context. - String get fullName => - (isRoot || parent.isRoot) ? _name : "${parent.fullName}$groupSep$_name"; - - GroupContext.root() - : parent = null, - _name = ''; - - GroupContext(this.parent, this._name) { - _testSetUp = parent.testSetUp; - _testTearDown = parent.testTearDown; - } -} diff --git a/lib/src/internal_test_case.dart b/lib/src/internal_test_case.dart deleted file mode 100644 index 97636661..00000000 --- a/lib/src/internal_test_case.dart +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.internal_test_case; - -import 'dart:async'; - -import '../unittest.dart'; -import 'test_environment.dart'; -import 'utils.dart'; - -/// An implementation of [TestCase] that exposes internal properties for other -/// unittest use. -class InternalTestCase implements TestCase { - final int id; - final String description; - - /// The setup function to call before the test, if any. - Function _setUp; - - /// The teardown function to call after the test, if any. - Function _tearDown; - - /// The body of the test case. - TestFunction _testFunction; - - /// Remaining number of callback functions that must reach a 'done' state - /// before the test completes. - int callbackFunctionsOutstanding = 0; - - /// The error or failure message for the tests. - /// - /// Initially an empty string. - String message = ''; - - /// The result of the test case. - /// - /// If the test case has is completed, this will be one of [PASS], [FAIL], or - /// [ERROR]. Otherwise, it will be `null`. - String result; - - /// Returns whether this test case passed. - bool get passed => result == PASS; - - /// The stack trace for the error that caused this test case to fail, or - /// `null` if it succeeded. - StackTrace stackTrace; - - /// The name of the group within which this test is running. - final String currentGroup; - - /// The time the test case started running. - /// - /// `null` if the test hasn't yet begun running. - DateTime get startTime => _startTime; - DateTime _startTime; - - /// The amount of time the test case took. - /// - /// `null` if the test hasn't finished running. - Duration get runningTime => _runningTime; - Duration _runningTime; - - /// Whether this test is enabled. - /// - /// Disabled tests won't be run. - bool enabled = true; - - /// A completer that will complete when the test is finished. - /// - /// This is only non-`null` when outstanding callbacks exist. - Completer _testComplete; - - /// Whether this test case has finished running. - bool get isComplete => !enabled || result != null; - - InternalTestCase(this.id, this.description, this._testFunction) - : currentGroup = environment.currentContext.fullName, - _setUp = environment.currentContext.testSetUp, - _tearDown = environment.currentContext.testTearDown; - - /// A function that returns another function to handle errors from [Future]s. - /// - /// [stage] is a string description of the stage of testing that failed. - Function _errorHandler(String stage) => (e, stack) { - if (stack == null && e is Error) { - stack = e.stackTrace; - } - if (result == null || result == PASS) { - if (e is TestFailure) { - fail("$e", stack); - } else { - error("$stage failed: Caught $e", stack); - } - } - }; - - /// Performs any associated [_setUp] function and runs the test. - /// - /// Returns a [Future] that can be used to schedule the next test. If the test - /// runs to completion synchronously, or is disabled, null is returned, to - /// tell unittest to schedule the next test immediately. - Future run() { - if (!enabled) return new Future.value(); - - result = stackTrace = null; - message = ''; - - // Avoid calling [new Future] to avoid issue 11911. - return new Future.value().then((_) { - if (_setUp != null) return _setUp(); - }).catchError(_errorHandler('Setup')).then((_) { - // Skip the test if setup failed. - if (result != null) return new Future.value(); - config.onTestStart(this); - _startTime = new DateTime.now(); - _runningTime = null; - callbackFunctionsOutstanding++; - var testReturn = _testFunction(); - // If _testFunction() returned a future, we want to wait for it like we - // would a callback, so if a failure occurs while waiting, we can abort. - if (testReturn is Future) { - callbackFunctionsOutstanding++; - testReturn - .catchError(_errorHandler('Test')) - .whenComplete(markCallbackComplete); - } - }).catchError(_errorHandler('Test')).then((_) { - markCallbackComplete(); - if (result == null) { - // Outstanding callbacks exist; we need to return a Future. - _testComplete = new Completer(); - return _testComplete.future.whenComplete(() { - if (_tearDown != null) { - return _tearDown(); - } - }).catchError(_errorHandler('Teardown')); - } else if (_tearDown != null) { - return _tearDown(); - } - }).catchError(_errorHandler('Teardown')).whenComplete(() { - _setUp = null; - _tearDown = null; - _testFunction = null; - }); - } - - /// Marks the test as having completed with [testResult], which should be one - /// of [PASS], [FAIL], or [ERROR]. - void _complete(String testResult, - [String messageText = '', StackTrace stack]) { - if (runningTime == null) { - // The startTime can be `null` if an error happened during setup. In this - // case we simply report a running time of 0. - if (startTime != null) { - _runningTime = new DateTime.now().difference(startTime); - } else { - _runningTime = const Duration(seconds: 0); - } - } - _setResult(testResult, messageText, stack); - if (_testComplete != null) { - var t = _testComplete; - _testComplete = null; - t.complete(this); - } - } - - // Sets [this]'s fields to reflect the test result, and notifies the current - // configuration that the test has completed. - // - // Returns true if this is the first time the result has been set. - void _setResult(String testResult, String messageText, StackTrace stack) { - message = messageText; - stackTrace = getTrace(stack, formatStacks, filterStacks); - if (stackTrace == null) stackTrace = stack; - if (result == null) { - result = testResult; - config.onTestResult(this); - } else { - result = testResult; - config.onTestResultChanged(this); - } - } - - /// Marks the test as having passed. - void pass() { - _complete(PASS); - } - - void registerException(error, [StackTrace stackTrace]) { - var message = error is TestFailure ? error.message : 'Caught $error'; - if (result == null) { - fail(message, stackTrace); - } else { - this.error(message, stackTrace); - } - } - - /// Marks the test as having failed. - void fail(String messageText, [StackTrace stack]) { - if (result != null) { - var newMessage = result == PASS - ? 'Test failed after initially passing: $messageText' - : 'Test failed more than once: $messageText'; - // TODO(gram): Should we combine the stack with the old one? - _complete(ERROR, newMessage, stack); - } else { - _complete(FAIL, messageText, stack); - } - } - - /// Marks the test as having had an unexpected error. - void error(String messageText, [StackTrace stack]) { - _complete(ERROR, messageText, stack); - } - - /// Indicates that an asynchronous callback has completed, and marks the test - /// as passing if all outstanding callbacks are complete. - void markCallbackComplete() { - callbackFunctionsOutstanding--; - if (callbackFunctionsOutstanding == 0 && !isComplete) pass(); - } - - String toString() => result != null ? "$description: $result" : description; -} diff --git a/lib/src/prints_matcher.dart b/lib/src/prints_matcher.dart index 92efdcac..4a7c0179 100644 --- a/lib/src/prints_matcher.dart +++ b/lib/src/prints_matcher.dart @@ -8,7 +8,8 @@ import 'dart:async'; import 'package:matcher/matcher.dart' hide completes, expect; -import '../unittest.dart'; +import 'future_matchers.dart'; +import 'expect.dart'; /// Matches a [Function] that prints text that matches [matcher]. /// @@ -41,7 +42,16 @@ class _Prints extends Matcher { } return completes.matches(result.then((_) { - expect(buffer.toString(), _matcher); + // Re-run expect() so we get the same formatting as we would without being + // asynchronous. + expect(() { + var actual = buffer.toString(); + if (actual.isEmpty) return; + + // Strip off the final newline because [print] will re-add it. + actual = actual.substring(0, actual.length - 1); + print(actual); + }, this); }), matchState); } diff --git a/lib/src/simple_configuration.dart b/lib/src/simple_configuration.dart index c95550ce..470b10f9 100644 --- a/lib/src/simple_configuration.dart +++ b/lib/src/simple_configuration.dart @@ -4,119 +4,16 @@ library unittest.simple_configuration; -import 'dart:isolate'; - -import '../unittest.dart'; +import 'test_case.dart'; import 'configuration.dart'; -import 'utils.dart'; -/// A configuration that provides hooks to configure the unittest library for -/// different platforms. +/// This is a stub class used to preserve compatibility with unittest 0.11.*. /// -/// This class implements the [Configuration] API in a platform-independent way. -/// Tests that want to take advantage of the platform can create a subclass and -/// override methods from this class. +/// It will be removed before the next version is released. +@deprecated class SimpleConfiguration extends Configuration { - /// A port that keeps the VM alive while we wait for asynchronous tests to - /// finish. - /// - /// The VM won't shut down as long as there's an open receive port. - ReceivePort _receivePort; - - /// If true (the default), throw an exception at the end if any tests failed. bool throwOnTestFailures = true; - - /// The constructor sets up a failure handler for [expect] that redirects - /// [expect] failures to [onExpectFailure]. SimpleConfiguration() : super.blank(); - void onInit() { - // For Dart internal tests, we don't want stack frame filtering. - // We turn it off here in the default config, but by default turn - // it back on in the vm and html configs. - filterStacks = false; - _receivePort = new ReceivePort(); - _postMessage('unittest-suite-wait-for-done'); - } - - /// Called when each test starts. Useful to show intermediate progress on - /// a test suite. Derived classes should call this first before their own - /// override code. - void onTestStart(TestCase testCase) {} - - /// Handles the logging of messages by a test case. - /// - /// The default in this base configuration is to call [print]. - void onLogMessage(TestCase testCase, String message) { - print(message); - } - - /// Format a test result. - String formatResult(TestCase testCase) { - var result = new StringBuffer(); - result.write(testCase.result.toUpperCase()); - result.write(": "); - result.write(testCase.description); - result.write("\n"); - - if (testCase.message != '') { - result.write(indent(testCase.message)); - result.write("\n"); - } - - if (testCase.stackTrace != null) { - result.write(indent(testCase.stackTrace.toString())); - result.write("\n"); - } - return result.toString(); - } - - /// Called with the result of all test cases. - /// - /// The default implementation prints the result summary using [print], - /// formatted with [formatResult]. Browser tests commonly override this to - /// reformat the output. - /// - /// When [uncaughtError] is not null, it contains an error that occured - /// outside of tests (e.g. setting up the test). - void onSummary(int passed, int failed, int errors, List<TestCase> results, - String uncaughtError) { - // Print each test's result. - for (var test in results) { - print(formatResult(test).trim()); - } - - // Show the summary. - print(''); - - if (passed == 0 && failed == 0 && errors == 0 && uncaughtError == null) { - print('No tests found.'); - // This is considered a failure too. - } else if (failed == 0 && errors == 0 && uncaughtError == null) { - print('All $passed tests passed.'); - } else { - if (uncaughtError != null) { - print('Top-level uncaught error: $uncaughtError'); - } - print('$passed PASSED, $failed FAILED, $errors ERRORS'); - } - } - - void onDone(bool success) { - if (success) { - _postMessage('unittest-suite-success'); - _receivePort.close(); - } else { - _receivePort.close(); - if (throwOnTestFailures) { - throw new Exception('Some tests failed.'); - } - } - } - - void _postMessage(String message) { - // In dart2js browser tests, the JavaScript-based test controller - // intercepts calls to print and listens for "secret" messages. - print(message); - } + String formatResult(TestCase testCase) => ""; } diff --git a/lib/src/test_case.dart b/lib/src/test_case.dart index 37596ff5..5a99d1ea 100644 --- a/lib/src/test_case.dart +++ b/lib/src/test_case.dart @@ -4,52 +4,20 @@ library unittest.test_case; -import '../unittest.dart'; - -/// An individual unit test. +/// This is a stub class used to preserve compatibility with unittest 0.11.*. +/// +/// It will be removed before the next version is released. +@deprecated abstract class TestCase { - /// A unique numeric identifier for this test case. int get id; - - /// A description of what the test is specifying. String get description; - - /// The error or failure message for the tests. - /// - /// Initially an empty string. String get message; - - /// The result of the test case. - /// - /// If the test case has is completed, this will be one of [PASS], [FAIL], or - /// [ERROR]. Otherwise, it will be `null`. String get result; - - /// Returns whether this test case passed. bool get passed; - - /// The stack trace for the error that caused this test case to fail, or - /// `null` if it succeeded. StackTrace get stackTrace; - - /// The name of the group within which this test is running. String get currentGroup; - - /// The time the test case started running. - /// - /// `null` if the test hasn't yet begun running. DateTime get startTime; - - /// The amount of time the test case took. - /// - /// `null` if the test hasn't finished running. Duration get runningTime; - - /// Whether this test is enabled. - /// - /// Disabled tests won't be run. bool get enabled; - - /// Whether this test case has finished running. - bool get isComplete => !enabled || result != null; + final isComplete = false; } diff --git a/lib/src/test_environment.dart b/lib/src/test_environment.dart deleted file mode 100644 index 6c205a9f..00000000 --- a/lib/src/test_environment.dart +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.test_environment; - -import 'dart:async'; - -import 'configuration.dart'; -import 'group_context.dart'; -import 'internal_test_case.dart'; - -/// The default unittest environment. -final _defaultEnvironment = new TestEnvironment(); - -/// The current unittest environment. -TestEnvironment get environment { - var environment = Zone.current[#unittest.environment]; - return environment == null ? _defaultEnvironment : environment; -} - -// The current environment's configuration. -Configuration get config => environment.config; - -/// Encapsulates the state of the test environment. -/// -/// This is used by the [withTestEnvironment] method to support multiple -/// invocations of the unittest library within the same application -/// instance. -class TestEnvironment { - /// The environment's configuration. - Configuration config; - - /// The top-level group context. - /// - /// We use a 'dummy' context for the top level to eliminate null checks when - /// querying the context. This allows us to easily support top-level - /// [setUp]/[tearDown] functions as well. - final rootContext = new GroupContext.root(); - - /// The current group context. - GroupContext currentContext; - - /// The [currentTestCaseIndex] represents the index of the currently running - /// test case. - /// - /// If this is -1 it implies the test system is not running. - /// It will be set to [number of test cases] as a short-lived state flagging - /// that the last test has completed. - int currentTestCaseIndex = -1; - - /// The [initialized] variable specifies whether the framework - /// has been initialized. - bool initialized = false; - - /// The time since we last gave asynchronous code a chance to be scheduled. - int lastBreath = new DateTime.now().millisecondsSinceEpoch; - - /// The number of [solo_group]s deep we are currently. - int soloNestingLevel = 0; - - /// Whether we've seen a [solo_test]. - bool soloTestSeen = false; - - /// The list of test cases to run. - final testCases = new List<InternalTestCase>(); - - /// The error message that is printed in the test summary. - String uncaughtErrorMessage; - - TestEnvironment() { - currentContext = rootContext; - } -} diff --git a/lib/src/throws_matcher.dart b/lib/src/throws_matcher.dart index 9790d22a..6807e6d2 100644 --- a/lib/src/throws_matcher.dart +++ b/lib/src/throws_matcher.dart @@ -8,7 +8,9 @@ import 'dart:async'; import 'package:matcher/matcher.dart' hide fail, expect; -import '../unittest.dart'; +import 'expect.dart'; +import 'invoker.dart'; +import 'utils.dart'; /// This can be used to match two kinds of objects: /// @@ -48,26 +50,24 @@ class Throws extends Matcher { bool matches(item, Map matchState) { if (item is! Function && item is! Future) return false; if (item is Future) { - var done = expectAsync((fn) => fn()); - + Invoker.current.addOutstandingCallback(); // Queue up an asynchronous expectation that validates when the future // completes. item.then((value) { - done(() { - fail("Expected future to fail, but succeeded with '$value'."); - }); + fail("Expected future to fail, but succeeded with '$value'."); }, onError: (error, trace) { - done(() { - if (_matcher == null) return; - var reason; - if (trace != null) { - var stackTrace = trace.toString(); - stackTrace = " ${stackTrace.replaceAll("\n", "\n ")}"; - reason = "Actual exception trace:\n$stackTrace"; - } - expect(error, _matcher, reason: reason); - }); - }); + if (_matcher == null) return; + + var reason; + if (trace != null) { + var stackTrace = terseChain(trace).toString(); + stackTrace = " ${stackTrace.replaceAll("\n", "\n ")}"; + reason = "Actual exception trace:\n$stackTrace"; + } + + // Re-run [expect] to get the proper formatting. + expect(() => throw error, this, reason: reason); + }).then((_) => Invoker.current.removeOutstandingCallback()); // It hasn't failed yet. return true; } diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 5c12593e..6b0939fe 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -28,23 +28,6 @@ String getErrorMessage(error) => String indent(String str) => str.replaceAll(new RegExp("^", multiLine: true), " "); -/// A pair of values. -class Pair<E, F> { - final E first; - final F last; - - Pair(this.first, this.last); - - String toString() => '($first, $last)'; - - bool operator ==(other) { - if (other is! Pair) return false; - return other.first == first && other.last == last; - } - - int get hashCode => first.hashCode ^ last.hashCode; -} - /// A regular expression matching the path to a temporary file used to start an /// isolate. /// @@ -64,29 +47,6 @@ Chain terseChain(StackTrace stackTrace) { }, terse: true); } -/// Returns a Trace object from a StackTrace object or a String, or the -/// unchanged input if formatStacks is false; -Trace getTrace(stack, bool formatStacks, bool filterStacks) { - Trace trace; - if (stack == null || !formatStacks) return null; - if (stack is String) { - trace = new Trace.parse(stack); - } else if (stack is StackTrace) { - trace = new Trace.from(stack); - } else { - throw new Exception('Invalid stack type ${stack.runtimeType} for $stack.'); - } - - if (!filterStacks) return trace; - - // Format the stack trace by removing everything above TestCase._runTest, - // which is usually going to be irrelevant. Also fold together unittest and - // core library calls so only the function the user called is visible. - return new Trace(trace.frames.takeWhile((frame) { - return frame.package != 'unittest' || frame.member != 'TestCase._runTest'; - })).terse.foldFrames((frame) => frame.package == 'unittest' || frame.isCore); -} - /// Flattens nested [Iterable]s inside an [Iterable] into a single [List] /// containing only non-[Iterable] elements. List flatten(Iterable nested) { diff --git a/lib/test_controller.js b/lib/test_controller.js index 8a62d6ae..11822790 100644 --- a/lib/test_controller.js +++ b/lib/test_controller.js @@ -2,232 +2,4 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -/** - * Test controller logic - used by unit test harness to embed tests in - * conent shell. - */ - -// Clear the console before every test run - this is Firebug specific code. -if (typeof console == "object" && typeof console.clear == "function") { - console.clear(); -} - -// Some tests may expect and have no way to suppress global errors. -var testExpectsGlobalError = false; -var testSuppressedGlobalErrors = []; - -// Set window onerror to make sure that we catch test harness errors across all -// browsers. -window.onerror = function (message, url, lineNumber) { - if (testExpectsGlobalError) { - testSuppressedGlobalErrors.push({ - message: message - }); - return; - } - if (url) { - showErrorAndExit( - "\n\n" + url + ":" + lineNumber + ":\n" + message + "\n\n"); - } else { - showErrorAndExit(message); - } - window.postMessage('unittest-suite-external-error', '*'); -}; - -// Start Dartium/content_shell, unless we are waiting for HTML Imports to load. -// HTML Imports allows a document to link to other HTMLs documents via -// <link rel=import>. It also allows for those other documents to contain -// <script> tags, which must be run before scripts on the main page. -// We have package:web_components to polyfill this feature, and it will handle -// starting Dartium/content_shell in that case. HTML Imports is used by Polymer, -// but it could be used by itself too. See the specification: -// https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/imports/index.html -if (navigator.webkitStartDart && !window.HTMLImports) { - navigator.webkitStartDart(); -} - -// testRunner is provided by content shell. -// It is not available in browser tests. -var testRunner = window.testRunner || window.layoutTestController; - -var waitForDone = false; - -// Returns the driving window object if available -function getDriverWindow() { - if (window != window.parent) { - // We're running in an iframe. - return window.parent; - } else if (window.opener) { - // We were opened by another window. - return window.opener; - } - return null; -} - -function notifyStart() { - var driver = getDriverWindow(); - if (driver) { - driver.postMessage("STARTING", "*"); - } -} -// We call notifyStart here to notify the encapsulating browser. -notifyStart(); - -function notifyDone() { - if (testRunner) testRunner.notifyDone(); - - // TODO(ricow): REMOVE, debug info, see issue 13292 - if (!testRunner) { - printMessage('Calling notifyDone()'); - } - // To support in browser launching of tests we post back start and result - // messages to the window.opener. - var driver = getDriverWindow(); - if (driver) { - driver.postMessage(window.document.body.innerHTML, "*"); - } -} - -function processMessage(msg) { - if (typeof msg != 'string') return; - // TODO(ricow): REMOVE, debug info, see issue 13292 - if (!testRunner) { - // Filter out ShadowDOM polyfill messages which are random floats. - if (msg != parseFloat(msg)) { - printMessage('processMessage(): ' + msg); - } - } - if (msg == 'unittest-suite-done') { - notifyDone(); - } else if (msg == 'unittest-suite-wait-for-done') { - waitForDone = true; - if (testRunner) { - testRunner.startedDartTest = true; - } - } else if (msg == 'dart-calling-main') { - if (testRunner) { - testRunner.startedDartTest = true; - } - } else if (msg == 'dart-main-done') { - if (!waitForDone) { - printMessage('PASS'); - notifyDone(); - } - } else if (msg == 'unittest-suite-success') { - printMessage('PASS'); - notifyDone(); - } else if (msg == 'unittest-suite-fail') { - showErrorAndExit('Some tests failed.'); - } -} - -function onReceive(e) { - processMessage(e.data); -} - -if (testRunner) { - testRunner.dumpAsText(); - testRunner.waitUntilDone(); -} -window.addEventListener("message", onReceive, false); - -function showErrorAndExit(message) { - if (message) { - printMessage('Error: ' + String(message)); - } - // dart/tools/testing/test_runner.dart is looking for either PASS or - // FAIL in a browser test's output. - printMessage('FAIL'); - notifyDone(); -} - -function onLoad(e) { - // needed for dartium compilation errors. - if (window.compilationError) { - showErrorAndExit(window.compilationError); - } -} - -window.addEventListener("DOMContentLoaded", onLoad, false); - -// Note: before renaming this function, note that it is also included in an -// inlined error handler in the HTML files that wrap DRT tests. -// See: tools/testing/dart/browser_test.dart -function externalError(e) { - // needed for dartium compilation errors. - showErrorAndExit(e && e.message); - window.postMessage('unittest-suite-external-error', '*'); -} - -document.addEventListener('readystatechange', function () { - if (document.readyState != "loaded") return; - // If 'startedDartTest' is not set, that means that the test did not have - // a chance to load. This will happen when a load error occurs in the VM. - // Give the machine time to start up. - setTimeout(function() { - // A window.postMessage might have been enqueued after this timeout. - // Just sleep another time to give the browser the time to process the - // posted message. - setTimeout(function() { - if (testRunner && !testRunner.startedDartTest) { - notifyDone(); - } - }, 0); - }, 50); -}); - -// dart2js will generate code to call this function to handle the Dart -// [print] method. -// -// dartium will invoke this method for [print] calls if the environment variable -// "DART_FORWARDING_PRINT" was set when launching dartium. -// -// Our tests will be wrapped, so we can detect when [main] is called and when -// it has ended. -// The wrapping happens either via "dartMainRunner" (for dart2js) or wrapped -// tests for dartium. -// -// The following messages are handled specially: -// dart-calling-main: signals that the dart [main] function will be invoked -// dart-main-done: signals that the dart [main] function has finished -// unittest-suite-wait-for-done: signals the start of an asynchronous test -// unittest-suite-success: signals the end of an asynchrounous test -// -// These messages are used to communicate with the test and will be posted so -// [processMessage] above can see it. -function dartPrint(msg) { - if ((msg === 'unittest-suite-success') - || (msg === 'unittest-suite-done') - || (msg === 'unittest-suite-wait-for-done') - || (msg === 'dart-calling-main') - || (msg === 'dart-main-done')) { - window.postMessage(msg, '*'); - return; - } - printMessage(msg); -} - -// Prints 'msg' to the console (if available) and to the body of the html -// document. -function printMessage(msg) { - if (typeof console === 'object') console.warn(msg); - var pre = document.createElement('pre'); - pre.appendChild(document.createTextNode(String(msg))); - document.body.appendChild(pre); - document.body.appendChild(document.createTextNode('\n')); -} - -// dart2js will generate code to call this function instead of calling -// Dart [main] directly. The argument is a closure that invokes main. -function dartMainRunner(main) { - dartPrint('dart-calling-main'); - try { - main(); - } catch (e) { - dartPrint(e); - if (e.stack) dartPrint(e.stack); - window.postMessage('unittest-suite-fail', '*'); - return; - } - dartPrint('dart-main-done'); -} +/** This file is deprecated and will be removed before the next release. */ diff --git a/lib/unittest.dart b/lib/unittest.dart index d0083270..0e4be92d 100644 --- a/lib/unittest.dart +++ b/lib/unittest.dart @@ -5,14 +5,15 @@ library unittest; import 'dart:async'; -import 'dart:collection'; + +import 'package:path/path.dart' as p; import 'src/configuration.dart'; -import 'src/expected_function.dart'; -import 'src/group_context.dart'; -import 'src/internal_test_case.dart'; +import 'src/declarer.dart'; +import 'src/console_reporter.dart'; +import 'src/invoker.dart'; +import 'src/suite.dart'; import 'src/test_case.dart'; -import 'src/test_environment.dart'; export 'package:matcher/matcher.dart' hide @@ -44,426 +45,133 @@ export 'package:matcher/matcher.dart' export 'src/configuration.dart'; export 'src/expect.dart'; -export 'src/simple_configuration.dart'; +export 'src/expect_async.dart'; export 'src/future_matchers.dart'; export 'src/prints_matcher.dart'; +export 'src/simple_configuration.dart'; +export 'src/test_case.dart'; export 'src/throws_matcher.dart'; export 'src/throws_matchers.dart'; -export 'src/test_case.dart'; -/// The signature for a function passed to [test]. -typedef dynamic TestFunction(); +/// The global declarer. +/// +/// This is used if a test file is run directly, rather than through the runner. +Declarer _globalDeclarer; + +/// Gets the declarer for the current scope. +/// +/// When using the runner, this returns the [Zone]-scoped declarer that's set by +/// [VmListener]. If the test file is run directly, this returns +/// [_globalDeclarer] (and sets it up on the first call). +Declarer get _declarer { + var declarer = Zone.current[#unittest.declarer]; + if (declarer != null) return declarer; + if (_globalDeclarer != null) return _globalDeclarer; + + // Since there's no Zone-scoped declarer, the test file is being run directly. + // In order to run the tests, we set up our own Declarer via + // [_globalDeclarer], and schedule a microtask to run the tests once they're + // finished being defined. + _globalDeclarer = new Declarer(); + scheduleMicrotask(() { + var suite = new Suite(p.prettyUri(Uri.base), _globalDeclarer.tests); + // TODO(nweiz): Use a reporter that doesn't import dart:io here. + // TODO(nweiz): Set the exit code on the VM when issue 6943 is fixed. + new ConsoleReporter([suite]).run(); + }); + return _globalDeclarer; +} -/// [Configuration] used by the unittest library. -/// -/// Note that if a configuration has not been set, calling this getter will -/// create a default configuration. -Configuration get unittestConfiguration { - if (config == null) environment.config = new Configuration(); - return config; +// TODO(nweiz): This and other top-level functions should throw exceptions if +// they're called after the declarer has finished declaring. +void test(String description, body()) => _declarer.test(description, body); + +void group(String description, void body()) => + _declarer.group(description, body); + +void setUp(callback()) => _declarer.setUp(callback); + +void tearDown(callback()) => _declarer.tearDown(callback); + +/// Handle an error that occurs outside of any test. +void handleExternalError(error, String message, [stackTrace]) { + // TODO(nweiz): handle this better. + registerException(error, stackTrace); } -/// If `true`, stack traces are reformatted to be more readable. +/// Registers an exception that was caught for the current test. +void registerException(error, [StackTrace stackTrace]) => + Invoker.current.handleError(error, stackTrace); + +// What follows are stubs for various top-level names supported by unittest +// 0.11.*. These are preserved for the time being for ease of migration, but +// should be removed before this is released as stable. + +@deprecated +typedef dynamic TestFunction(); + +@deprecated +Configuration unittestConfiguration = new Configuration(); + +@deprecated bool formatStacks = true; -/// If `true`, irrelevant frames are filtered from the stack trace. -/// -/// This does nothing if [formatStacks] is false. +@deprecated bool filterStacks = true; -/// Separator used between group names and test names. +@deprecated String groupSep = ' '; -/// Sets the [Configuration] used by the unittest library. -/// -/// Throws a [StateError] if there is an existing, incompatible value. -void set unittestConfiguration(Configuration value) { - if (identical(config, value)) return; - if (config != null) { - logMessage('Warning: The unittestConfiguration has already been set. New ' - 'unittestConfiguration ignored.'); - } else { - environment.config = value; - } -} - -/// Logs [message] associated with the current test case. -/// -/// Tests should use this instead of [print]. -void logMessage(String message) => - config.onLogMessage(currentTestCase, message); +@deprecated +void logMessage(String message) => print(message); -/// The test cases that have been defined so far. -List<TestCase> get testCases => - new UnmodifiableListView<TestCase>(environment.testCases); +@deprecated +final testCases = []; -/// The interval (in milliseconds) after which a non-microtask asynchronous -/// delay will be scheduled between tests. -/// -/// This is used to avoid starving the DOM or other non-microtask events. +@deprecated const int BREATH_INTERVAL = 200; -/// The [TestCase] currently being executed. -TestCase get currentTestCase => (environment.currentTestCaseIndex >= 0 && - environment.currentTestCaseIndex < testCases.length) - ? testCases[environment.currentTestCaseIndex] - : null; +@deprecated +TestCase get currentTestCase => null; -/// The same as [currentTestCase], but typed as an [InternalTestCase]. -InternalTestCase get _currentTestCase => currentTestCase as InternalTestCase; - -/// The result string for a passing test case. +@deprecated const PASS = 'pass'; -/// The result string for a failing test case. +@deprecated const FAIL = 'fail'; -/// The result string for an test case with an error. +@deprecated const ERROR = 'error'; -/// Creates a new test case with the given description and body. -/// -/// The description will be added to the descriptions of any surrounding -/// [group]s. -void test(String description, TestFunction body) { - _requireNotRunning(); - ensureInitialized(); - - if (environment.soloTestSeen && environment.soloNestingLevel == 0) return; - var testCase = new InternalTestCase( - testCases.length + 1, _fullDescription(description), body); - environment.testCases.add(testCase); -} - -/// Returns [description] with all of its group prefixes prepended. -String _fullDescription(String description) { - var group = environment.currentContext.fullName; - if (description == null) return group; - return group != '' ? '$group$groupSep$description' : description; -} - -/// A convenience function for skipping a test. +@deprecated void skip_test(String spec, TestFunction body) {} -/// Creates a new test case with the given description and body. -/// -/// If [solo_test] is used instead of [test], then all non-solo tests will be -/// disabled. Note that if [solo_group] is used as well, all tests in the group -/// will be enabled, regardless of whether they use [test] or [solo_test], or -/// whether they are in a nested [group] versus [solo_group]. Put another way, -/// if there are any calls to [solo_test] or [solo_group] in a test file, all -/// tests that are not inside a [solo_group] will be disabled unless they are -/// [solo_test]s. -void solo_test(String spec, TestFunction body) { - _requireNotRunning(); - ensureInitialized(); - if (!environment.soloTestSeen) { - environment.soloTestSeen = true; - // This is the first solo-ed test. Discard all tests up to now. - environment.testCases.clear(); - } - environment.soloNestingLevel++; - try { - test(spec, body); - } finally { - environment.soloNestingLevel--; - } -} +@deprecated +void solo_test(String spec, TestFunction body) => test(spec, body); -/// Indicate that [callback] is expected to be called [count] number of times -/// (by default 1). -/// -/// The unittest framework will wait for the callback to run the [count] times -/// before it considers the current test to be complete. Using [expectAsync] -/// will also ensure that errors that occur within [callback] are tracked and -/// reported. [callback] may take up to six optional or required positional -/// arguments; named arguments are not supported. -/// -/// [max] can be used to specify an upper bound on the number of calls; if this -/// is exceeded the test will fail. If [max] is `0` (the default), the callback -/// is expected to be called exactly [count] times. If [max] is `-1`, the -/// callback is allowed to be called any number of times greater than [count]. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -Function expectAsync(Function callback, - {int count: 1, int max: 0, String id, String reason}) => - new ExpectedFunction(callback, count, max, id: id, reason: reason).func; - -/// Indicate that [callback] is expected to be called until [isDone] returns -/// true. -/// -/// [isDone] is called after each time the function is run. Only when it returns -/// true will the callback be considered complete. Using [expectAsyncUntil] will -/// also ensure that errors that occur within [callback] are tracked and -/// reported. [callback] may take up to six optional or required positional -/// arguments; named arguments are not supported. -/// -/// Both [id] and [reason] are optional and provide extra information about the -/// callback when debugging. [id] should be the name of the callback, while -/// [reason] should be the reason the callback is expected to be called. -Function expectAsyncUntil(Function callback, bool isDone(), - {String id, String reason}) => new ExpectedFunction(callback, 0, -1, - id: id, reason: reason, isDone: isDone).func; - -/// Creates a group of tests. -/// -/// A group's description is included in the descriptions of any tests or -/// sub-groups it contains. [setUp] and [tearDown] are also scoped to the -/// containing group. -void group(String description, void body()) { - ensureInitialized(); - _requireNotRunning(); - environment.currentContext = - new GroupContext(environment.currentContext, description); - try { - body(); - } catch (e, trace) { - var stack = (trace == null) ? '' : ': ${trace.toString()}'; - environment.uncaughtErrorMessage = "${e.toString()}$stack"; - } finally { - // Now that the group is over, restore the previous one. - environment.currentContext = environment.currentContext.parent; - } -} - -/// A convenience function for skipping a group of tests. +@deprecated void skip_group(String description, void body()) {} -/// Creates a group of tests. -/// -/// If [solo_group] is used instead of [group], then all tests not declared with -/// [solo_test] or in a [solo_group] will be disabled. Note that all tests in a -/// [solo_group] will be run, regardless of whether they're declared with [test] -/// or [solo_test]. -/// -/// [skip_test] and [skip_group] take precedence over [solo_group]. -void solo_group(String description, void body()) { - _requireNotRunning(); - ensureInitialized(); - if (!environment.soloTestSeen) { - environment.soloTestSeen = true; - // This is the first solo-ed group. Discard all tests up to now. - environment.testCases.clear(); - } - ++environment.soloNestingLevel; - try { - group(description, body); - } finally { - --environment.soloNestingLevel; - } -} +@deprecated +void solo_group(String description, void body()) => group(description, body); -/// Registers a function to be run before tests. -/// -/// This function will be called before each test is run. [callback] may be -/// asynchronous; if so, it must return a [Future]. -/// -/// If this is called within a test group, it applies only to tests in that -/// group. [callback] will be run after any set-up callbacks in parent groups or -/// at the top level. -void setUp(Function callback) { - _requireNotRunning(); - environment.currentContext.testSetUp = callback; -} +@deprecated +void filterTests(testFilter) {} -/// Registers a function to be run after tests. -/// -/// This function will be called after each test is run. [callback] may be -/// asynchronous; if so, it must return a [Future]. -/// -/// If this is called within a test group, it applies only to tests in that -/// group. [callback] will be run before any tear-down callbacks in parent groups or -/// at the top level. -void tearDown(Function callback) { - _requireNotRunning(); - environment.currentContext.testTearDown = callback; -} +@deprecated +void runTests() {} -/// Advance to the next test case. -void _nextTestCase() { - environment.currentTestCaseIndex++; - _runTest(); -} +@deprecated +void ensureInitialized() {} -/// Handle an error that occurs outside of any test. -void handleExternalError(e, String message, [stackTrace]) { - var msg = '$message\nCaught $e'; - - if (currentTestCase != null) { - _currentTestCase.error(msg, stackTrace); - } else { - environment.uncaughtErrorMessage = "$msg: $stackTrace"; - } -} - -/// Remove any tests that match [testFilter]. -/// -/// [testFilter] can be a predicate function, a [RegExp], or a [String]. If it's -/// a function, it's called with each [TestCase]. If it's a [String], it's -/// parsed as a [RegExp] and matched against each [TestCase.description]. -/// -/// This is different from enabling or disabling tests in that it removes the -/// tests completely. -void filterTests(testFilter) { - var filterFunction; - if (testFilter is String) { - var re = new RegExp(testFilter); - filterFunction = (t) => re.hasMatch(t.description); - } else if (testFilter is RegExp) { - filterFunction = (t) => testFilter.hasMatch(t.description); - } else if (testFilter is Function) { - filterFunction = testFilter; - } - environment.testCases.retainWhere(filterFunction); -} +@deprecated +void setSoloTest(int id) {} -/// Runs all queued tests, one at a time. -void runTests() { - _requireNotRunning(); - _ensureInitialized(false); - environment.currentTestCaseIndex = 0; - config.onStart(); - _runTest(); -} +@deprecated +void enableTest(int id) {} -/// Registers an exception that was caught for the current test. -void registerException(error, [StackTrace stackTrace]) => - _currentTestCase.registerException(error, stackTrace); - -/// Runs the next test. -void _runTest() { - if (environment.currentTestCaseIndex >= testCases.length) { - assert(environment.currentTestCaseIndex == testCases.length); - _completeTests(); - return; - } - - var testCase = _currentTestCase; - var f = runZoned(testCase.run, onError: (error, stack) { - // TODO(kevmoo) Do a better job of flagging these are async errors. - // https://code.google.com/p/dart/issues/detail?id=16530 - testCase.registerException(error, stack); - }); +@deprecated +void disableTest(int id) {} - var timer; - var timeout = unittestConfiguration.timeout; - if (timeout != null) { - try { - timer = new Timer(timeout, () { - testCase.error("Test timed out after ${timeout.inSeconds} seconds."); - _nextTestCase(); - }); - } on UnsupportedError catch (e) { - if (e.message != "Timer greater than 0.") rethrow; - // Support running on d8 and jsshell which don't support timers. - } - } - - f.whenComplete(() { - if (timer != null) timer.cancel(); - var now = new DateTime.now().millisecondsSinceEpoch; - if (now - environment.lastBreath >= BREATH_INTERVAL) { - environment.lastBreath = now; - Timer.run(_nextTestCase); - } else { - scheduleMicrotask(_nextTestCase); // Schedule the next test. - } - }); -} - -/// Notify the configuration that the testing has finished. -void _completeTests() { - if (!environment.initialized) return; - - var passed = 0; - var failed = 0; - var errors = 0; - for (var testCase in testCases) { - switch (testCase.result) { - case PASS: - passed++; - break; - case FAIL: - failed++; - break; - case ERROR: - errors++; - break; - } - } - - config.onSummary( - passed, failed, errors, testCases, environment.uncaughtErrorMessage); - config.onDone(passed > 0 && - failed == 0 && - errors == 0 && - environment.uncaughtErrorMessage == null); - environment.initialized = false; - environment.currentTestCaseIndex = -1; -} - -/// Initializes the test environment if it hasn't already been initialized. -void ensureInitialized() { - _ensureInitialized(true); -} - -/// Initializes the test environment. -/// -/// If [configAutoStart] is `true`, schedule a microtask to run the tests. This -/// microtask is expected to run after all the tests are defined. -void _ensureInitialized(bool configAutoStart) { - if (environment.initialized) return; - - environment.initialized = true; - - environment.uncaughtErrorMessage = null; - - unittestConfiguration.onInit(); - - // Immediately queue the suite up. It will run after a timeout (i.e. after - // main() has returned). - if (configAutoStart && config.autoStart) scheduleMicrotask(runTests); -} - -/// Remove all tests other than the one identified by [id]. -void setSoloTest(int id) => - environment.testCases.retainWhere((t) => t.id == id); - -/// Enable the test identified by [id]. -void enableTest(int id) => _setTestEnabledState(id, enable: true); - -/// Disable the test by [id]. -void disableTest(int id) => _setTestEnabledState(id, enable: false); - -/// Enable or disable the test identified by [id]. -void _setTestEnabledState(int id, {bool enable: true}) { - // Try fast path first. - if (testCases.length > id && testCases[id].id == id) { - environment.testCases[id].enabled = enable; - } else { - for (var i = 0; i < testCases.length; i++) { - if (testCases[i].id != id) continue; - environment.testCases[i].enabled = enable; - break; - } - } -} - -/// Throws a [StateError] if tests are running. -void _requireNotRunning() { - if (environment.currentTestCaseIndex == -1) return; - throw new StateError('Not allowed when tests are running.'); -} - -/// Creates a test environment running in its own zone scope. -/// -/// This allows for multiple invocations of the unittest library in the same -/// application instance. This is useful when, for example, creating a test -/// runner application which needs to create a new pristine test environment on -/// each invocation to run a given set of tests. -withTestEnvironment(callback()) { - return runZoned(callback, - zoneValues: {#unittest.environment: new TestEnvironment()}); -} +@deprecated +withTestEnvironment(callback()) => callback(); diff --git a/lib/vm_config.dart b/lib/vm_config.dart index 77e91e35..44d5158d 100644 --- a/lib/vm_config.dart +++ b/lib/vm_config.dart @@ -2,64 +2,25 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -/// A simple unit test library for running tests on the VM. +@deprecated library unittest.vm_config; -import 'dart:async'; -import 'dart:io'; -import 'unittest.dart'; +import 'src/simple_configuration.dart'; +/// This is a stub class used to preserve compatibility with unittest 0.11.*. +/// +/// It will be removed before the next version is released. +@deprecated class VMConfiguration extends SimpleConfiguration { - // Color constants used for generating messages. final String GREEN_COLOR = '\u001b[32m'; final String RED_COLOR = '\u001b[31m'; final String MAGENTA_COLOR = '\u001b[35m'; final String NO_COLOR = '\u001b[0m'; - // We make this public so the user can turn it off if they want. - bool useColor; + bool useColor = false; - VMConfiguration() - : super(), - useColor = stdioType(stdout) == StdioType.TERMINAL; - - String formatResult(TestCase testCase) { - String result = super.formatResult(testCase); - if (useColor) { - if (testCase.result == PASS) { - return "${GREEN_COLOR}${result}${NO_COLOR}"; - } else if (testCase.result == FAIL) { - return "${RED_COLOR}${result}${NO_COLOR}"; - } else if (testCase.result == ERROR) { - return "${MAGENTA_COLOR}${result}${NO_COLOR}"; - } - } - return result; - } - - void onInit() { - super.onInit(); - filterStacks = formatStacks = true; - } - - void onDone(bool success) { - int status; - try { - super.onDone(success); - status = 0; - } catch (ex) { - // A non-zero exit code is used by the test infrastructure to detect - // failure. - status = 1; - } - Future.wait([stdout.close(), stderr.close()]).then((_) { - exit(status); - }); - } -} - -void useVMConfiguration() { - unittestConfiguration = _singleton; + VMConfiguration() : super(); } -final _singleton = new VMConfiguration(); +@deprecated +void useVMConfiguration() {} diff --git a/pubspec.yaml b/pubspec.yaml index 049509e8..869b14cf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,3 @@ dependencies: git: https://github.com/dart-lang/matcher.git dev_dependencies: fake_async: '>=0.1.2 <0.2.0' - metatest: - git: - ref: b6348d7e7f3c5b00a48aa579694457d1abd36b69 - url: https://github.com/dart-lang/metatest diff --git a/test/async_exception_test.dart b/test/async_exception_test.dart deleted file mode 100644 index d81e7de6..00000000 --- a/test/async_exception_test.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.async_exception_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsFail('async errors cause tests to fail', () { - test('async', () { - expectAsync(() {}); - new Future(() { - throw "an error!"; - }); - }); - - test('sync', () { - expectAsync(() {}); - new Future(() { - throw "an error!"; - }); - }); - }); -} diff --git a/test/async_exception_with_future_test.dart b/test/async_exception_with_future_test.dart deleted file mode 100644 index c5c445e1..00000000 --- a/test/async_exception_with_future_test.dart +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.async_exception_with_future_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('async exception with future test', () { - var tearDownHappened = false; - - tearDown(() { - tearDownHappened = true; - }); - - test('test', () { - expect(tearDownHappened, isFalse); - // The "throw" statement below should terminate the test immediately. - // The framework should not wait for the future to complete. - // tearDown should still execute. - new Future.sync(() { - throw "error!"; - }); - return new Completer().future; - }); - - test('follow up', () { - expect(tearDownHappened, isTrue); - }); - }, [ - {'description': 'test', 'message': 'Caught error!', 'result': 'fail',}, - {'description': 'follow up', 'result': 'pass',} - ]); -} diff --git a/test/async_setup_teardown_test.dart b/test/async_setup_teardown_test.dart deleted file mode 100644 index 1068ee70..00000000 --- a/test/async_setup_teardown_test.dart +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.async_setup_teardown; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('good setup/good teardown', () { - setUp(() { - return new Future.value(0); - }); - tearDown(() { - return new Future.value(0); - }); - test('foo1', () {}); - }); - - expectTestResults('good setup/bad teardown', () { - setUp(() { - return new Future.value(0); - }); - tearDown(() { - return new Future.error("Failed to complete tearDown"); - }); - test('foo2', () {}); - }, [ - { - 'result': 'error', - 'message': 'Teardown failed: Caught Failed to complete tearDown' - } - ]); - - expectTestResults('bad setup/good teardown', () { - setUp(() { - return new Future.error("Failed to complete setUp"); - }); - tearDown(() { - return new Future.value(0); - }); - test('foo3', () {}); - }, [ - { - 'result': 'error', - 'message': 'Setup failed: Caught Failed to complete setUp' - } - ]); - - expectTestResults('bad setup/bad teardown', () { - setUp(() { - return new Future.error("Failed to complete setUp"); - }); - tearDown(() { - return new Future.error("Failed to complete tearDown"); - }); - test('foo4', () {}); - }, [ - { - 'result': 'error', - 'message': 'Setup failed: Caught Failed to complete setUp' - } - ]); -} diff --git a/test/breath_test.dart b/test/breath_test.dart deleted file mode 100644 index 244d2d2c..00000000 --- a/test/breath_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.breath_test; - -import 'dart:async'; - -import 'package:unittest/unittest.dart'; - -void main() { - // Test the sync test 'breath' feature of unittest. - - // We use the testStartStopwatch to determine if the 'starve' - // test was executed within a small enough time interval from - // the first test that we are guaranteed the second test is - // running in a microtask. If the second test is running as a - // microtask we are guaranteed the timer scheduled in the - // first test has not been run yet. - var testStartStopwatch = new Stopwatch()..start(); - - group('breath', () { - var sentinel = 0; - - test('initial', () { - Timer.run(() { - sentinel = 1; - }); - }); - - test('starve', () { - // If less than BREATH_INTERVAL time has passed since before - // we started the test group then the previous test's timer - // has not been run (at least this is what we are testing). - if (testStartStopwatch.elapsed.inMilliseconds <= BREATH_INTERVAL) { - expect(sentinel, 0); - } - - // Next we wait for at least BREATH_INTERVAL to guaranteed the - // next (third) test is run using a timer which means it will - // run after the timer scheduled in the first test and hence - // the sentinel should have been set to 1. - var sw = new Stopwatch()..start(); - while (sw.elapsed.inMilliseconds < BREATH_INTERVAL); - }); - - test('breathed', () { - expect(sentinel, 1); - }); - }); -} diff --git a/test/completion_test.dart b/test/completion_test.dart deleted file mode 100644 index 6803e68b..00000000 --- a/test/completion_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.completion_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('completion test', () { - var count = 0; - test('test', () { - var _callback; - _callback = expectAsyncUntil(() { - if (++count < 10) { - new Future.sync(_callback); - } - }, () => (count == 10)); - new Future.sync(_callback); - }); - - test('verify count', () { - expect(count, 10); - }); - }); -} diff --git a/test/console_reporter_test.dart b/test/console_reporter_test.dart index 81661b7d..6a114f31 100644 --- a/test/console_reporter_test.dart +++ b/test/console_reporter_test.dart @@ -23,9 +23,9 @@ void main() { test("runs several successful tests and reports when each completes", () { _expectReport(""" - declarer.test('success 1', () {}); - declarer.test('success 2', () {}); - declarer.test('success 3', () {});""", + test('success 1', () {}); + test('success 2', () {}); + test('success 3', () {});""", """ +0: success 1 +1: success 1 @@ -38,28 +38,28 @@ void main() { test("runs several failing tests and reports when each fails", () { _expectReport(""" - declarer.test('failure 1', () => throw new TestFailure('oh no')); - declarer.test('failure 2', () => throw new TestFailure('oh no')); - declarer.test('failure 3', () => throw new TestFailure('oh no'));""", + test('failure 1', () => throw new TestFailure('oh no')); + test('failure 2', () => throw new TestFailure('oh no')); + test('failure 3', () => throw new TestFailure('oh no'));""", """ +0: failure 1 +0 -1: failure 1 oh no - test.dart 7:42 main.<fn> + test.dart 6:33 main.<fn> dart:isolate _RawReceivePortImpl._handleMessage +0 -1: failure 2 +0 -2: failure 2 oh no - test.dart 8:42 main.<fn> + test.dart 7:33 main.<fn> dart:isolate _RawReceivePortImpl._handleMessage +0 -2: failure 3 +0 -3: failure 3 oh no - test.dart 9:42 main.<fn> + test.dart 8:33 main.<fn> dart:isolate _RawReceivePortImpl._handleMessage @@ -68,15 +68,15 @@ void main() { test("runs failing tests along with successful tests", () { _expectReport(""" - declarer.test('failure 1', () => throw new TestFailure('oh no')); - declarer.test('success 1', () {}); - declarer.test('failure 2', () => throw new TestFailure('oh no')); - declarer.test('success 2', () {});""", + test('failure 1', () => throw new TestFailure('oh no')); + test('success 1', () {}); + test('failure 2', () => throw new TestFailure('oh no')); + test('success 2', () {});""", """ +0: failure 1 +0 -1: failure 1 oh no - test.dart 7:42 main.<fn> + test.dart 6:33 main.<fn> dart:isolate _RawReceivePortImpl._handleMessage @@ -85,7 +85,7 @@ void main() { +1 -1: failure 2 +1 -2: failure 2 oh no - test.dart 9:42 main.<fn> + test.dart 8:33 main.<fn> dart:isolate _RawReceivePortImpl._handleMessage @@ -99,40 +99,40 @@ void main() { // This completer ensures that the test isolate isn't killed until all // errors have been thrown. var completer = new Completer(); - declarer.test('failures', () { + test('failures', () { new Future.microtask(() => throw 'first error'); new Future.microtask(() => throw 'second error'); new Future.microtask(() => throw 'third error'); new Future.microtask(completer.complete); }); - declarer.test('wait', () => completer.future);""", + test('wait', () => completer.future);""", """ +0: failures +0 -1: failures first error - test.dart 11:38 main.<fn>.<fn> + test.dart 10:38 main.<fn>.<fn> dart:isolate _RawReceivePortImpl._handleMessage ===== asynchronous gap =========================== dart:async Future.Future.microtask - test.dart 11:15 main.<fn> + test.dart 10:15 main.<fn> dart:isolate _RawReceivePortImpl._handleMessage second error - test.dart 12:38 main.<fn>.<fn> + test.dart 11:38 main.<fn>.<fn> dart:isolate _RawReceivePortImpl._handleMessage ===== asynchronous gap =========================== dart:async Future.Future.microtask - test.dart 12:15 main.<fn> + test.dart 11:15 main.<fn> dart:isolate _RawReceivePortImpl._handleMessage third error - test.dart 13:38 main.<fn>.<fn> + test.dart 12:38 main.<fn>.<fn> dart:isolate _RawReceivePortImpl._handleMessage ===== asynchronous gap =========================== dart:async Future.Future.microtask - test.dart 13:15 main.<fn> + test.dart 12:15 main.<fn> dart:isolate _RawReceivePortImpl._handleMessage @@ -151,7 +151,6 @@ import 'dart:async'; import 'package:unittest/unittest.dart'; void main() { - var declarer = Zone.current[#unittest.declarer]; $tests } """; diff --git a/test/correct_callback_test.dart b/test/correct_callback_test.dart deleted file mode 100644 index f69766ed..00000000 --- a/test/correct_callback_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.correct_callback_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('correct callback test', () { - var count = 0; - test('test', () => new Future.sync(expectAsync(() { - ++count; - }))); - - test('verify count', () { - expect(count, 1); - }); - }); -} diff --git a/test/exception_test.dart b/test/exception_test.dart deleted file mode 100644 index 137f0a36..00000000 --- a/test/exception_test.dart +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.exception_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('good setup/good teardown', () { - test('test', () { - throw new Exception('Fail.'); - }); - }, [{'result': 'error', 'message': 'Test failed: Caught Exception: Fail.'}]); -} diff --git a/test/excess_callback_test.dart b/test/excess_callback_test.dart deleted file mode 100644 index 2679d17e..00000000 --- a/test/excess_callback_test.dart +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.excess_callback_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - var count = 0; - - expectTestResults('excess callback test', () { - test('test', () { - var _callback0 = expectAsync(() => ++count); - var _callback1 = expectAsync(() => ++count); - var _callback2 = expectAsync(() { - _callback1(); - _callback1(); - _callback0(); - }); - new Future.sync(_callback2); - }); - - test('verify count', () { - expect(count, 1); - }); - }, [ - { - 'description': 'test', - 'message': 'Callback called more times than expected (1).', - 'result': 'fail' - }, - {'description': 'verify count', 'result': 'pass',} - ]); -} diff --git a/test/expect_async_args_test.dart b/test/expect_async_args_test.dart deleted file mode 100644 index 8e814dc9..00000000 --- a/test/expect_async_args_test.dart +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.expect_async_args_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('expect async args', () { - var count = 0; - List<int> _getArgs([a = 0, b = 0, c = 0, d = 0, e = 0, f = 0]) { - count++; - return [a, b, c, d, e, f]; - } - - test('expect async args', () { - expect(expectAsync(_getArgs)(), [0, 0, 0, 0, 0, 0]); - expect(expectAsync(_getArgs)(5), [5, 0, 0, 0, 0, 0]); - expect(expectAsync(_getArgs)(1, 2, 3, 4, 5, 6), [1, 2, 3, 4, 5, 6]); - }); - - test('invoked with too many args', () { - expectAsync(_getArgs)(1, 2, 3, 4, 5, 6, 7); - }); - - test('created with too many args', () { - expectAsync((a1, a2, a3, a4, a5, a6, a7) { - count++; - })(); - }); - - test('verify count', () { - expect(count, 3); - }); - }, [ - {'description': 'expect async args', 'result': 'pass',}, - {'description': 'invoked with too many args', 'result': 'error',}, - {'description': 'created with too many args', 'result': 'error',}, - {'description': 'verify count', 'result': 'pass',} - ]); -} diff --git a/test/expect_async_test.dart b/test/expect_async_test.dart index 844e44b0..a512f988 100644 --- a/test/expect_async_test.dart +++ b/test/expect_async_test.dart @@ -1,100 +1,328 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library unittest.expect_async_test; +import 'package:unittest/src/state.dart'; +import 'package:unittest/unittest.dart'; -import 'dart:async'; +import 'utils.dart'; -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; +void main() { + group("supports a function with this many arguments:", () { + test("0", () { + var callbackRun = false; + return runTest(() { + expectAsync(() { + callbackRun = true; + })(); + }).then((liveTest) { + expectTestPassed(liveTest); + expect(callbackRun, isTrue); + }); + }); -void main() => initTests(_test); + test("1", () { + var callbackRun = false; + return runTest(() { + expectAsync((arg) { + expect(arg, equals(1)); + callbackRun = true; + })(1); + }).then((liveTest) { + expectTestPassed(liveTest); + expect(callbackRun, isTrue); + }); + }); -void _test(message) { - initMetatest(message); + test("2", () { + var callbackRun = false; + return runTest(() { + expectAsync((arg1, arg2) { + expect(arg1, equals(1)); + expect(arg2, equals(2)); + callbackRun = true; + })(1, 2); + }).then((liveTest) { + expectTestPassed(liveTest); + expect(callbackRun, isTrue); + }); + }); - var count = 0; + test("3", () { + var callbackRun = false; + return runTest(() { + expectAsync((arg1, arg2, arg3) { + expect(arg1, equals(1)); + expect(arg2, equals(2)); + expect(arg3, equals(3)); + callbackRun = true; + })(1, 2, 3); + }).then((liveTest) { + expectTestPassed(liveTest); + expect(callbackRun, isTrue); + }); + }); - expectTestsPass('expect async test', () { - test('expectAsync zero params', () { - new Future.sync(expectAsync(() { - ++count; - })); + test("4", () { + var callbackRun = false; + return runTest(() { + expectAsync((arg1, arg2, arg3, arg4) { + expect(arg1, equals(1)); + expect(arg2, equals(2)); + expect(arg3, equals(3)); + expect(arg4, equals(4)); + callbackRun = true; + })(1, 2, 3, 4); + }).then((liveTest) { + expectTestPassed(liveTest); + expect(callbackRun, isTrue); + }); }); - test('expectAsync 1 param', () { - var func = expectAsync((arg) { - expect(arg, 0); - ++count; + test("5", () { + var callbackRun = false; + return runTest(() { + expectAsync((arg1, arg2, arg3, arg4, arg5) { + expect(arg1, equals(1)); + expect(arg2, equals(2)); + expect(arg3, equals(3)); + expect(arg4, equals(4)); + expect(arg5, equals(5)); + callbackRun = true; + })(1, 2, 3, 4, 5); + }).then((liveTest) { + expectTestPassed(liveTest); + expect(callbackRun, isTrue); }); - new Future.sync(() => func(0)); }); - test('expectAsync 2 param', () { - var func = expectAsync((arg0, arg1) { - expect(arg0, 0); - expect(arg1, 1); - ++count; + test("6", () { + var callbackRun = false; + return runTest(() { + expectAsync((arg1, arg2, arg3, arg4, arg5, arg6) { + expect(arg1, equals(1)); + expect(arg2, equals(2)); + expect(arg3, equals(3)); + expect(arg4, equals(4)); + expect(arg5, equals(5)); + expect(arg6, equals(6)); + callbackRun = true; + })(1, 2, 3, 4, 5, 6); + }).then((liveTest) { + expectTestPassed(liveTest); + expect(callbackRun, isTrue); }); - new Future.sync(() => func(0, 1)); }); + }); - test('single arg to Future.catchError', () { - var func = expectAsync((error) { - expect(error, isStateError); - ++count; + group("with optional arguments", () { + test("allows them to be passed", () { + var callbackRun = false; + return runTest(() { + expectAsync(([arg = 1]) { + expect(arg, equals(2)); + callbackRun = true; + })(2); + }).then((liveTest) { + expectTestPassed(liveTest); + expect(callbackRun, isTrue); }); + }); - new Future(() { - throw new StateError('test'); - }).catchError(func); + test("allows them not to be passed", () { + var callbackRun = false; + return runTest(() { + expectAsync(([arg = 1]) { + expect(arg, equals(1)); + callbackRun = true; + })(); + }).then((liveTest) { + expectTestPassed(liveTest); + expect(callbackRun, isTrue); + }); }); + }); + + test("doesn't support a function with 7 arguments", () { + expect(() => expectAsync((_1, _2, _3, _4, _5, _6, _7) {}), + throwsArgumentError); + }); + + group("by default", () { + test("won't allow the test to complete until it's called", () { + return expectTestBlocks( + () => expectAsync(() {}), + (callback) => callback()); + }); + + test("may only be called once", () { + return runTest(() { + var callback = expectAsync(() {}); + callback(); + callback(); + }).then((liveTest) { + expectTestFailed(liveTest, + "Callback called more times than expected (1)."); + }); + }); + }); - test('2 args to Future.catchError', () { - var func = expectAsync((error, stack) { - expect(error, isStateError); - expect(stack is StackTrace, isTrue); - ++count; + group("with count", () { + test("won't allow the test to complete until it's called at least that " + "many times", () { + var liveTest; + var future; + liveTest = createTest(() { + var callback = expectAsync(() {}, count: 3); + future = pumpEventQueue().then((_) { + expect(liveTest.state.status, equals(Status.running)); + callback(); + return pumpEventQueue(); + }).then((_) { + expect(liveTest.state.status, equals(Status.running)); + callback(); + return pumpEventQueue(); + }).then((_) { + expect(liveTest.state.status, equals(Status.running)); + callback(); + }); }); - new Future(() { - throw new StateError('test'); - }).catchError(func); + return liveTest.run().then((_) { + expectTestPassed(liveTest); + // Ensure that the outer test doesn't complete until the inner future + // completes. + return future; + }); + }); + + test("will throw an error if it's called more than that many times", () { + return runTest(() { + var callback = expectAsync(() {}, count: 3); + callback(); + callback(); + callback(); + callback(); + }).then((liveTest) { + expectTestFailed( + liveTest, "Callback called more times than expected (3)."); + }); }); - test('zero of two optional positional args', () { - var func = expectAsync(([arg0 = true, arg1 = true]) { - expect(arg0, isTrue); - expect(arg1, isTrue); - ++count; + group("0,", () { + test("won't block the test's completion", () { + expectAsync(() {}, count: 0); + }); + + test("will throw an error if it's ever called", () { + return runTest(() { + expectAsync(() {}, count: 0)(); + }).then((liveTest) { + expectTestFailed( + liveTest, "Callback called more times than expected (0)."); + }); }); + }); + }); + + group("with max", () { + test("will allow the callback to be called that many times", () { + var callback = expectAsync(() {}, max: 3); + callback(); + callback(); + callback(); + }); - new Future.sync(() => func()); + test("will allow the callback to be called fewer than that many times", () { + var callback = expectAsync(() {}, max: 3); + callback(); }); - test('one of two optional positional args', () { - var func = expectAsync(([arg0 = true, arg1 = true]) { - expect(arg0, isFalse); - expect(arg1, isTrue); - ++count; + test("will throw an error if it's called more than that many times", () { + return runTest(() { + var callback = expectAsync(() {}, max: 3); + callback(); + callback(); + callback(); + callback(); + }).then((liveTest) { + expectTestFailed( + liveTest, "Callback called more times than expected (3)."); }); + }); - new Future.sync(() => func(false)); + test("-1, will allow the callback to be called any number of times", () { + var callback = expectAsync(() {}, max: -1); + for (var i = 0; i < 20; i++) { + callback(); + } }); + }); - test('two of two optional positional args', () { - var func = expectAsync(([arg0 = true, arg1 = true]) { - expect(arg0, isFalse); - expect(arg1, isNull); - ++count; + test("will throw an error if max is less than count", () { + expect(() => expectAsync(() {}, max: 1, count: 2), + throwsArgumentError); + }); + + group("expectAsyncUntil()", () { + test("won't allow the test to complete until isDone returns true", () { + var liveTest; + var future; + liveTest = createTest(() { + var done = false; + var callback = expectAsyncUntil(() {}, () => done); + + future = pumpEventQueue().then((_) { + expect(liveTest.state.status, equals(Status.running)); + callback(); + return pumpEventQueue(); + }).then((_) { + expect(liveTest.state.status, equals(Status.running)); + done = true; + callback(); + }); }); - new Future.sync(() => func(false, null)); + return liveTest.run().then((_) { + expectTestPassed(liveTest); + // Ensure that the outer test doesn't complete until the inner future + // completes. + return future; + }); }); - test('verify count', () { - expect(count, 8); + test("doesn't call isDone until after the callback is called", () { + var callbackRun = false; + expectAsyncUntil(() => callbackRun = true, () { + expect(callbackRun, isTrue); + return true; + })(); + }); + }); + + group("with errors", () { + test("reports them to the current test", () { + return runTest(() { + expectAsync(() => throw new TestFailure('oh no'))(); + }).then((liveTest) { + expectTestFailed(liveTest, 'oh no'); + }); + }); + + test("swallows them and returns null", () { + var returnValue; + var caughtError = false; + return runTest(() { + try { + returnValue = expectAsync(() => throw new TestFailure('oh no'))(); + } on TestFailure catch (_) { + caughtError = true; + } + }).then((liveTest) { + expectTestFailed(liveTest, 'oh no'); + expect(returnValue, isNull); + expect(caughtError, isFalse); + }); }); }); } diff --git a/test/future_matchers_test.dart b/test/future_matchers_test.dart deleted file mode 100644 index c09542c3..00000000 --- a/test/future_matchers_test.dart +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.future_matchers_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('group name test', () { - test('completes - unexpected error', () { - var completer = new Completer(); - completer.completeError('X'); - expect(completer.future, completes); - }); - - test('completes - successfully', () { - var completer = new Completer(); - completer.complete('1'); - expect(completer.future, completes); - }); - - test('throws - unexpected to see normal completion', () { - var completer = new Completer(); - completer.complete('1'); - expect(completer.future, throws); - }); - - test('throws - expected to see exception', () { - var completer = new Completer(); - completer.completeError('X'); - expect(completer.future, throws); - }); - - test('throws - expected to see exception thrown later on', () { - var completer = new Completer(); - var chained = completer.future.then((_) { - throw 'X'; - }); - expect(chained, throws); - completer.complete('1'); - }); - - test('throwsA - unexpected normal completion', () { - var completer = new Completer(); - completer.complete('1'); - expect(completer.future, throwsA(equals('X'))); - }); - - test('throwsA - correct error', () { - var completer = new Completer(); - completer.completeError('X'); - expect(completer.future, throwsA(equals('X'))); - }); - - test('throwsA - wrong error', () { - var completer = new Completer(); - completer.completeError('X'); - expect(completer.future, throwsA(equals('Y'))); - }); - }, [ - { - 'result': 'fail', - 'message': 'Expected future to complete successfully, but it failed with ' - 'X', - }, - {'result': 'pass'}, - { - 'result': 'fail', - 'message': 'Expected future to fail, but succeeded with \'1\'.' - }, - {'result': 'pass'}, - {'result': 'pass'}, - { - 'result': 'fail', - 'message': 'Expected future to fail, but succeeded with \'1\'.' - }, - {'result': 'pass'}, - { - 'result': 'fail', - 'message': '''Expected: 'Y' - Actual: 'X' - Which: is different. -Expected: Y - Actual: X - ^ - Differ at offset 0 -''' - } - ]); -} diff --git a/test/group_name_test.dart b/test/group_name_test.dart deleted file mode 100644 index 3184ffc3..00000000 --- a/test/group_name_test.dart +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.group_name_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('group name test', () { - group('a', () { - test('a', () {}); - group('b', () { - test('b', () {}); - }); - }); - }, [{'description': 'a a'}, {'description': 'a b b'}]); -} diff --git a/test/invalid_ops_test.dart b/test/invalid_ops_test.dart deleted file mode 100644 index d9e05e74..00000000 --- a/test/invalid_ops_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.invalid_ops_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('testcases immutable', () { - test('test', () { - expect(() => test('test', () {}), throwsStateError); - expect(() => solo_test('test', () {}), throwsStateError); - expect(() => group('test', () {}), throwsStateError); - expect(() => solo_group('test', () {}), throwsStateError); - expect(() => setUp(() {}), throwsStateError); - expect(() => tearDown(() {}), throwsStateError); - expect(() => runTests(), throwsStateError); - }); - }); -} diff --git a/test/io.dart b/test/io.dart index fc545621..09bd31df 100644 --- a/test/io.dart +++ b/test/io.dart @@ -17,10 +17,18 @@ String _computePackageDir() => /// Runs the unittest executable with the package root set properly. ProcessResult runUnittest(List<String> args, {String workingDirectory}) { - var allArgs = Platform.executableArguments.toList() - ..add(p.join(packageDir, 'bin/unittest.dart')) - ..add("--package-root=${p.join(packageDir, 'packages')}") - ..addAll(args); + var allArgs = [ + p.join(packageDir, 'bin/unittest.dart'), + "--package-root=${p.join(packageDir, 'packages')}" + ]..addAll(args); + + // TODO(nweiz): Use ScheduledProcess once it's compatible. + return runDart(allArgs, workingDirectory: workingDirectory); +} + +/// Runs Dart. +ProcessResult runDart(List<String> args, {String workingDirectory}) { + var allArgs = Platform.executableArguments.toList()..addAll(args); // TODO(nweiz): Use ScheduledProcess once it's compatible. return Process.runSync(Platform.executable, allArgs, diff --git a/test/late_exception_test.dart b/test/late_exception_test.dart deleted file mode 100644 index 94ad30b3..00000000 --- a/test/late_exception_test.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.late_exception_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('late exception test', () { - var f; - test('testOne', () { - f = expectAsync(() {}); - new Future.sync(f); - }); - test('testTwo', () { - new Future.sync(expectAsync(() { - f(); - })); - }); - }, [ - { - 'description': 'testOne', - 'message': 'Callback called (2) after test case testOne had already been ' - 'marked as pass.', - 'result': 'error', - }, - {'description': 'testTwo', 'result': 'pass',} - ]); -} diff --git a/test/loader_test.dart b/test/loader_test.dart index 47197fbf..9ef3f6e3 100644 --- a/test/loader_test.dart +++ b/test/loader_test.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library unittest.loader; - import 'dart:io'; import 'package:path/path.dart' as p; @@ -23,10 +21,9 @@ import 'dart:async'; import 'package:unittest/unittest.dart'; void main() { - var declarer = Zone.current[#unittest.declarer]; - declarer.test("success", () {}); - declarer.test("failure", () => throw new TestFailure('oh no')); - declarer.test("error", () => throw 'oh no'); + test("success", () {}); + test("failure", () => throw new TestFailure('oh no')); + test("error", () => throw 'oh no'); } """; diff --git a/test/matcher/completion_test.dart b/test/matcher/completion_test.dart new file mode 100644 index 00000000..360a5cf2 --- /dev/null +++ b/test/matcher/completion_test.dart @@ -0,0 +1,100 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; + +import 'package:unittest/unittest.dart'; + +import '../utils.dart'; + +void main() { + group("[completes]", () { + test("blocks the test until the Future completes", () { + return expectTestBlocks(() { + var completer = new Completer(); + expect(completer.future, completes); + return completer; + }, (completer) => completer.complete()); + }); + + test("with an error", () { + return runTest(() { + expect(new Future.error('X'), completes); + }).then((liveTest) { + expectTestFailed(liveTest, startsWith( + "Expected future to complete successfully, but it failed with X")); + }); + }); + + test("with a failure", () { + return runTest(() { + expect(new Future.error(new TestFailure('oh no')), completes); + }).then((liveTest) { + expectTestFailed(liveTest, "oh no"); + }); + }); + + test("with a non-function", () { + return runTest(() { + expect(10, completes); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: completes successfully\n" + " Actual: <10>\n"); + }); + }); + + test("with a successful future", () { + expect(new Future.value('1'), completes); + }); + }); + + group("[completion]", () { + test("blocks the test until the Future completes", () { + return expectTestBlocks(() { + var completer = new Completer(); + expect(completer.future, completion(isNull)); + return completer; + }, (completer) => completer.complete()); + }); + + test("with an error", () { + return runTest(() { + expect(new Future.error('X'), completion(isNull)); + }).then((liveTest) { + expectTestFailed(liveTest, startsWith( + "Expected future to complete successfully, but it failed with X")); + }); + }); + + test("with a failure", () { + return runTest(() { + expect(new Future.error(new TestFailure('oh no')), completion(isNull)); + }).then((liveTest) { + expectTestFailed(liveTest, "oh no"); + }); + }); + + test("with a non-function", () { + return runTest(() { + expect(10, completion(equals(10))); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: completes to a value that <10>\n" + " Actual: <10>\n"); + }); + }); + + test("with an incorrect value", () { + return runTest(() { + expect(new Future.value('a'), completion(equals('b'))); + }).then((liveTest) { + expectTestFailed(liveTest, startsWith( + "Expected: 'b'\n" + " Actual: 'a'\n" + " Which: is different."));; + }); + }); + }); +} diff --git a/test/matcher/prints_test.dart b/test/matcher/prints_test.dart new file mode 100644 index 00000000..23eea0df --- /dev/null +++ b/test/matcher/prints_test.dart @@ -0,0 +1,149 @@ +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; + +import 'package:unittest/unittest.dart'; + +import '../utils.dart'; + +void main() { + group("synchronous", () { + test("passes with an expected print", () { + expect(() => print("Hello, world!"), prints("Hello, world!\n")); + }); + + test("combines multiple prints", () { + expect(() { + print("Hello"); + print("World!"); + }, prints("Hello\nWorld!\n")); + }); + + test("works with a Matcher", () { + expect(() => print("Hello, world!"), prints(contains("Hello"))); + }); + + test("describes a failure nicely", () { + return runTest(() { + expect(() => print("Hello, world!"), prints("Goodbye, world!\n")); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: prints 'Goodbye, world!\\n'\n" + " ''\n" + " Actual: <Closure: () => dynamic>\n" + " Which: printed 'Hello, world!\\n'\n" + " ''\n" + " Which: is different.\n" + "Expected: Goodbye, w ...\n" + " Actual: Hello, wor ...\n" + " ^\n" + " Differ at offset 0\n"); + }); + }); + + test("describes a failure with a non-descriptive Matcher nicely", () { + return runTest(() { + expect(() => print("Hello, world!"), prints(contains("Goodbye"))); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: prints contains 'Goodbye'\n" + " Actual: <Closure: () => dynamic>\n" + " Which: printed 'Hello, world!\\n'\n" + " ''\n"); + }); + }); + + test("describes a failure with no text nicely", () { + return runTest(() { + expect(() {}, prints(contains("Goodbye"))); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: prints contains 'Goodbye'\n" + " Actual: <Closure: () => dynamic>\n" + " Which: printed nothing.\n"); + }); + }); + + test("with a non-function", () { + return runTest(() { + expect(10, prints(contains("Goodbye"))); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: prints contains 'Goodbye'\n" + " Actual: <10>\n"); + }); + }); + }); + + group('asynchronous', () { + test("passes with an expected print", () { + expect(() => new Future(() => print("Hello, world!")), + prints("Hello, world!\n")); + }); + + test("combines multiple prints", () { + expect(() => new Future(() { + print("Hello"); + print("World!"); + }), prints("Hello\nWorld!\n")); + }); + + test("works with a Matcher", () { + expect(() => new Future(() => print("Hello, world!")), + prints(contains("Hello"))); + }); + + test("describes a failure nicely", () { + return runTest(() { + expect(() => new Future(() => print("Hello, world!")), + prints("Goodbye, world!\n")); + }).then((liveTest) { + expectTestFailed(liveTest, startsWith( + "Expected: prints 'Goodbye, world!\\n'\n" + " ''\n" + " Actual: <Closure: () => dynamic>\n" + " Which: printed 'Hello, world!\\n'\n" + " ''\n" + " Which: is different.\n" + "Expected: Goodbye, w ...\n" + " Actual: Hello, wor ...\n" + " ^\n" + " Differ at offset 0")); + }); + }); + + test("describes a failure with a non-descriptive Matcher nicely", () { + return runTest(() { + expect(() => new Future(() => print("Hello, world!")), + prints(contains("Goodbye"))); + }).then((liveTest) { + expectTestFailed(liveTest, startsWith( + "Expected: prints contains 'Goodbye'\n" + " Actual: <Closure: () => dynamic>\n" + " Which: printed 'Hello, world!\\n'\n" + " ''")); + }); + }); + + test("describes a failure with no text nicely", () { + return runTest(() { + expect(() => new Future.value(), prints(contains("Goodbye"))); + }).then((liveTest) { + expectTestFailed(liveTest, startsWith( + "Expected: prints contains 'Goodbye'\n" + " Actual: <Closure: () => dynamic>\n" + " Which: printed nothing.")); + }); + }); + + test("won't let the test end until the Future completes", () { + return expectTestBlocks(() { + var completer = new Completer(); + expect(() => completer.future, prints(isEmpty)); + return completer; + }, (completer) => completer.complete()); + }); + }); +} diff --git a/test/matcher/throws_test.dart b/test/matcher/throws_test.dart new file mode 100644 index 00000000..ef0ff49e --- /dev/null +++ b/test/matcher/throws_test.dart @@ -0,0 +1,149 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; + +import 'package:unittest/unittest.dart'; + +import '../utils.dart'; + +void main() { + group("synchronous", () { + group("[throws]", () { + test("with a function that throws an error", () { + expect(() => throw 'oh no', throws); + }); + + test("with a function that doesn't throw", () { + return runTest(() { + expect(() {}, throws); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: throws\n" + " Actual: <Closure: () => dynamic>\n" + " Which: did not throw\n"); + }); + }); + + test("with a non-function", () { + return runTest(() { + expect(10, throws); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: throws\n" + " Actual: <10>\n" + " Which: is not a Function or Future\n"); + }); + }); + }); + + group("[throwsA]", () { + test("with a function that throws an identical error", () { + expect(() => throw 'oh no', throwsA('oh no')); + }); + + test("with a function that throws a matching error", () { + expect(() => throw new FormatException("bad"), + throwsA(isFormatException)); + }); + + test("with a function that doesn't throw", () { + return runTest(() { + expect(() {}, throwsA('oh no')); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: throws 'oh no'\n" + " Actual: <Closure: () => dynamic>\n" + " Which: did not throw\n"); + }); + }); + + test("with a non-function", () { + return runTest(() { + expect(10, throwsA('oh no')); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: throws 'oh no'\n" + " Actual: <10>\n" + " Which: is not a Function or Future\n"); + }); + }); + + test("with a function that throws the wrong error", () { + return runTest(() { + expect(() => throw 'aw dang', throwsA('oh no')); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected: throws 'oh no'\n" + " Actual: <Closure: () => dynamic>\n" + " Which: threw 'aw dang'\n"); + }); + }); + }); + }); + + group("asynchronous", () { + group("[throws]", () { + test("with a Future that throws an error", () { + expect(new Future.error('oh no'), throws); + }); + + test("with a Future that doesn't throw", () { + return runTest(() { + expect(new Future.value(), throws); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected future to fail, but succeeded with 'null'."); + }); + }); + + test("won't let the test end until the Future completes", () { + return expectTestBlocks(() { + var completer = new Completer(); + expect(completer.future, throws); + return completer; + }, (completer) => completer.completeError('oh no')); + }); + }); + + group("[throwsA]", () { + test("with a Future that throws an identical error", () { + expect(new Future.error('oh no'), throwsA('oh no')); + }); + + test("with a Future that throws a matching error", () { + expect(new Future.error(new FormatException("bad")), + throwsA(isFormatException)); + }); + + test("with a Future that doesn't throw", () { + return runTest(() { + expect(new Future.value(), throwsA('oh no')); + }).then((liveTest) { + expectTestFailed(liveTest, + "Expected future to fail, but succeeded with 'null'."); + }); + }); + + test("with a Future that throws the wrong error", () { + return runTest(() { + expect(new Future.error('aw dang'), throwsA('oh no')); + }).then((liveTest) { + expectTestFailed(liveTest, startsWith( + "Expected: throws 'oh no'\n" + " Actual: <Closure: () => dynamic>\n" + " Which: threw 'aw dang'\n")); + }); + }); + + test("won't let the test end until the Future completes", () { + return expectTestBlocks(() { + var completer = new Completer(); + expect(completer.future, throwsA('oh no')); + return completer; + }, (completer) => completer.completeError('oh no')); + }); + }); + }); +} diff --git a/test/matcher/throws_type_test.dart b/test/matcher/throws_type_test.dart new file mode 100644 index 00000000..0e9a2cef --- /dev/null +++ b/test/matcher/throws_type_test.dart @@ -0,0 +1,178 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:unittest/unittest.dart'; + +import '../utils.dart'; + +void main() { + group('[throwsArgumentError]', () { + test("passes when a ArgumentError is thrown", () { + expect(() => throw new ArgumentError(''), throwsArgumentError); + }); + + test("fails when a non-ArgumentError is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsArgumentError); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws ArgumentError")); + }); + }); + }); + + group('[throwsConcurrentModificationError]', () { + test("passes when a ConcurrentModificationError is thrown", () { + expect(() => throw new ConcurrentModificationError(''), + throwsConcurrentModificationError); + }); + + test("fails when a non-ConcurrentModificationError is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsConcurrentModificationError); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws ConcurrentModificationError")); + }); + }); + }); + + group('[throwsCyclicInitializationError]', () { + test("passes when a CyclicInitializationError is thrown", () { + expect(() => throw new CyclicInitializationError(''), + throwsCyclicInitializationError); + }); + + test("fails when a non-CyclicInitializationError is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsCyclicInitializationError); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws CyclicInitializationError")); + }); + }); + }); + + group('[throwsException]', () { + test("passes when a Exception is thrown", () { + expect(() => throw new Exception(''), throwsException); + }); + + test("fails when a non-Exception is thrown", () { + return runTest(() { + expect(() => throw 'oh no', throwsException); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws Exception")); + }); + }); + }); + + group('[throwsFormatException]', () { + test("passes when a FormatException is thrown", () { + expect(() => throw new FormatException(''), throwsFormatException); + }); + + test("fails when a non-FormatException is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsFormatException); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws FormatException")); + }); + }); + }); + + group('[throwsNoSuchMethodError]', () { + test("passes when a NoSuchMethodError is thrown", () { + expect(() { + throw new NoSuchMethodError(null, #name, null, null); + }, throwsNoSuchMethodError); + }); + + test("fails when a non-NoSuchMethodError is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsNoSuchMethodError); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws NoSuchMethodError")); + }); + }); + }); + + group('[throwsNullThrownError]', () { + test("passes when a NullThrownError is thrown", () { + expect(() => throw null, throwsNullThrownError); + }); + + test("fails when a non-NullThrownError is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsNullThrownError); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws NullThrownError")); + }); + }); + }); + + group('[throwsRangeError]', () { + test("passes when a RangeError is thrown", () { + expect(() => throw new RangeError(''), throwsRangeError); + }); + + test("fails when a non-RangeError is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsRangeError); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws RangeError")); + }); + }); + }); + + group('[throwsStateError]', () { + test("passes when a StateError is thrown", () { + expect(() => throw new StateError(''), throwsStateError); + }); + + test("fails when a non-StateError is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsStateError); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws StateError")); + }); + }); + }); + + group('[throwsUnimplementedError]', () { + test("passes when a UnimplementedError is thrown", () { + expect(() => throw new UnimplementedError(''), throwsUnimplementedError); + }); + + test("fails when a non-UnimplementedError is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsUnimplementedError); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws UnimplementedError")); + }); + }); + }); + + group('[throwsUnsupportedError]', () { + test("passes when a UnsupportedError is thrown", () { + expect(() => throw new UnsupportedError(''), throwsUnsupportedError); + }); + + test("fails when a non-UnsupportedError is thrown", () { + return runTest(() { + expect(() => throw new Exception(), throwsUnsupportedError); + }).then((liveTest) { + expectTestFailed(liveTest, + startsWith("Expected: throws UnsupportedError")); + }); + }); + }); +} diff --git a/test/matchers_minified_test.dart b/test/matchers_minified_test.dart deleted file mode 100644 index 5c38d4cc..00000000 --- a/test/matchers_minified_test.dart +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// This file is for matcher tests that rely on the names of various Dart types. -// These tests normally fail when run in minified dart2js, since the names will -// be mangled. This version of the file is modified to expect minified names. - -library unittest.minified_test; - -import 'package:unittest/unittest.dart'; - -import 'test_utils.dart'; - -// A regexp fragment matching a minified name. -const _MINIFIED_NAME = r"[A-Za-z0-9]{1,3}"; - -void main() { - group('Core matchers', () { - test('throwsFormatException', () { - shouldPass(() { - throw new FormatException(''); - }, throwsFormatException); - shouldFail(() { - throw new Exception(); - }, throwsFormatException, matches(r"Expected: throws FormatException +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw " + _MINIFIED_NAME + r":<Exception>")); - }); - - test('throwsArgumentError', () { - shouldPass(() { - throw new ArgumentError(''); - }, throwsArgumentError); - shouldFail(() { - throw new Exception(); - }, throwsArgumentError, matches(r"Expected: throws ArgumentError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw " + _MINIFIED_NAME + r":<Exception>")); - }); - - test('throwsRangeError', () { - shouldPass(() { - throw new RangeError(0); - }, throwsRangeError); - shouldFail(() { - throw new Exception(); - }, throwsRangeError, matches(r"Expected: throws RangeError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw " + _MINIFIED_NAME + r":<Exception>")); - }); - - test('throwsNoSuchMethodError', () { - shouldPass(() { - throw new NoSuchMethodError(null, const Symbol(''), null, null); - }, throwsNoSuchMethodError); - shouldFail(() { - throw new Exception(); - }, throwsNoSuchMethodError, matches( - r"Expected: throws NoSuchMethodError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw " + _MINIFIED_NAME + r":<Exception>")); - }); - - test('throwsUnimplementedError', () { - shouldPass(() { - throw new UnimplementedError(''); - }, throwsUnimplementedError); - shouldFail(() { - throw new Exception(); - }, throwsUnimplementedError, matches( - r"Expected: throws UnimplementedError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw " + _MINIFIED_NAME + r":<Exception>")); - }); - - test('throwsUnsupportedError', () { - shouldPass(() { - throw new UnsupportedError(''); - }, throwsUnsupportedError); - shouldFail(() { - throw new Exception(); - }, throwsUnsupportedError, matches(r"Expected: throws UnsupportedError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw " + _MINIFIED_NAME + r":<Exception>")); - }); - - test('throwsStateError', () { - shouldPass(() { - throw new StateError(''); - }, throwsStateError); - shouldFail(() { - throw new Exception(); - }, throwsStateError, matches(r"Expected: throws StateError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw " + _MINIFIED_NAME + r":<Exception>")); - }); - }); - - group('Iterable Matchers', () { - test('isEmpty', () { - var d = new SimpleIterable(0); - var e = new SimpleIterable(1); - shouldPass(d, isEmpty); - shouldFail(e, isEmpty, - matches(r"Expected: empty +Actual: " + _MINIFIED_NAME + r":\[1\]")); - }); - - test('isNotEmpty', () { - var d = new SimpleIterable(0); - var e = new SimpleIterable(1); - shouldPass(e, isNotEmpty); - shouldFail(d, isNotEmpty, matches( - r"Expected: non-empty +Actual: " + _MINIFIED_NAME + r":\[\]")); - }); - - test('contains', () { - var d = new SimpleIterable(3); - shouldPass(d, contains(2)); - shouldFail(d, contains(5), matches(r"Expected: contains <5> +" - r"Actual: " + _MINIFIED_NAME + r":\[3, 2, 1\]")); - }); - }); - - group('Feature Matchers', () { - test("Feature Matcher", () { - var w = new Widget(); - w.price = 10; - shouldPass(w, new HasPrice(10)); - shouldPass(w, new HasPrice(greaterThan(0))); - shouldFail(w, new HasPrice(greaterThan(10)), matches( - r"Expected: Widget with a price that is a value greater than " - r"<10> +" - r"Actual: <Instance of '" + _MINIFIED_NAME + r"'> +" - r"Which: has price with value <10> which is not " - r"a value greater than <10>")); - }); - }); -} diff --git a/test/matchers_unminified_test.dart b/test/matchers_unminified_test.dart deleted file mode 100644 index f301dd14..00000000 --- a/test/matchers_unminified_test.dart +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -// This file is for matcher tests that rely on the names of various Dart types. -// These tests will fail when run in minified dart2js, since the names will be -// mangled. A version of this file that works in minified dart2js is in -// matchers_minified_test.dart. - -library unittest.unminified_test; - -import 'package:unittest/unittest.dart'; - -import 'test_utils.dart'; - -void main() { - group('Core matchers', () { - test('throwsFormatException', () { - shouldPass(() { - throw new FormatException(''); - }, throwsFormatException); - shouldFail(() { - throw new Exception(); - }, throwsFormatException, matches(r"Expected: throws FormatException +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw \?:<Exception>")); - }); - - test('throwsArgumentError', () { - shouldPass(() { - throw new ArgumentError(''); - }, throwsArgumentError); - shouldFail(() { - throw new Exception(); - }, throwsArgumentError, matches(r"Expected: throws ArgumentError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw \?:<Exception>")); - }); - - test('throwsRangeError', () { - shouldPass(() { - throw new RangeError(0); - }, throwsRangeError); - shouldFail(() { - throw new Exception(); - }, throwsRangeError, matches(r"Expected: throws RangeError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw \?:<Exception>")); - }); - - test('throwsNoSuchMethodError', () { - shouldPass(() { - throw new NoSuchMethodError(null, const Symbol(''), null, null); - }, throwsNoSuchMethodError); - shouldFail(() { - throw new Exception(); - }, throwsNoSuchMethodError, matches( - r"Expected: throws NoSuchMethodError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw \?:<Exception>")); - }); - - test('throwsUnimplementedError', () { - shouldPass(() { - throw new UnimplementedError(''); - }, throwsUnimplementedError); - shouldFail(() { - throw new Exception(); - }, throwsUnimplementedError, matches( - r"Expected: throws UnimplementedError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw \?:<Exception>")); - }); - - test('throwsUnsupportedError', () { - shouldPass(() { - throw new UnsupportedError(''); - }, throwsUnsupportedError); - shouldFail(() { - throw new Exception(); - }, throwsUnsupportedError, matches(r"Expected: throws UnsupportedError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw \?:<Exception>")); - }); - - test('throwsStateError', () { - shouldPass(() { - throw new StateError(''); - }, throwsStateError); - shouldFail(() { - throw new Exception(); - }, throwsStateError, matches(r"Expected: throws StateError +" - r"Actual: <Closure(: \(\) => dynamic)?> +" - r"Which: threw \?:<Exception>")); - }); - }); - - group('Iterable Matchers', () { - test('isEmpty', () { - var d = new SimpleIterable(0); - var e = new SimpleIterable(1); - shouldPass(d, isEmpty); - shouldFail(e, isEmpty, "Expected: empty " - "Actual: SimpleIterable:[1]"); - }); - - test('isNotEmpty', () { - var d = new SimpleIterable(0); - var e = new SimpleIterable(1); - shouldPass(e, isNotEmpty); - shouldFail(d, isNotEmpty, "Expected: non-empty " - "Actual: SimpleIterable:[]"); - }); - - test('contains', () { - var d = new SimpleIterable(3); - shouldPass(d, contains(2)); - shouldFail(d, contains(5), "Expected: contains <5> " - "Actual: SimpleIterable:[3, 2, 1]"); - }); - }); - - group('Feature Matchers', () { - test("Feature Matcher", () { - var w = new Widget(); - w.price = 10; - shouldPass(w, new HasPrice(10)); - shouldPass(w, new HasPrice(greaterThan(0))); - shouldFail(w, new HasPrice(greaterThan(10)), - "Expected: Widget with a price that is a value greater than <10> " - "Actual: <Instance of 'Widget'> " - "Which: has price with value <10> which is not " - "a value greater than <10>"); - }); - }); -} diff --git a/test/middle_exception_test.dart b/test/middle_exception_test.dart deleted file mode 100644 index 1abd02e2..00000000 --- a/test/middle_exception_test.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.middle_exception_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('late exception test', () { - test('testOne', () { - expect(true, isTrue); - }); - test('testTwo', () { - expect(true, isFalse); - }); - test('testThree', () { - var done = expectAsync(() {}); - new Future.sync(() { - expect(true, isTrue); - done(); - }); - }); - }, [{'result': 'pass'}, {'result': 'fail',}, {'result': 'pass'}]); -} diff --git a/test/missing_tick_test.dart b/test/missing_tick_test.dart deleted file mode 100644 index 81b03315..00000000 --- a/test/missing_tick_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.missing_tick_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message, timeout: const Duration(seconds: 1)); - - expectTestResults('missing tick', () { - test('test that should time out', () { - expectAsync(() {}); - }); - }, [ - { - 'description': 'test that should time out', - 'message': 'Test timed out after 1 seconds.', - 'result': 'error', - } - ]); -} diff --git a/test/nested_groups_setup_teardown_test.dart b/test/nested_groups_setup_teardown_test.dart deleted file mode 100644 index ca09ec62..00000000 --- a/test/nested_groups_setup_teardown_test.dart +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.nested_groups_setup_teardown_test; - -import 'dart:async'; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('nested groups setup/teardown', () { - StringBuffer s = new StringBuffer(); - group('level 1', () { - setUp(makeDelayedSetup(1, s)); - group('level 2', () { - setUp(makeImmediateSetup(2, s)); - tearDown(makeDelayedTeardown(2, s)); - group('level 3', () { - group('level 4', () { - setUp(makeDelayedSetup(4, s)); - tearDown(makeImmediateTeardown(4, s)); - group('level 5', () { - setUp(makeImmediateSetup(5, s)); - group('level 6', () { - tearDown(makeDelayedTeardown(6, s)); - test('inner', () {}); - }); - }); - }); - }); - }); - }); - test('after nest', () { - expect(s.toString(), "l1 U l2 U l4 U l5 U l6 D l4 D l2 D "); - }); - }); -} - -Function makeDelayedSetup(int index, StringBuffer s) => () { - return new Future.delayed(new Duration(milliseconds: 1), () { - s.write('l$index U '); - }); -}; - -Function makeDelayedTeardown(int index, StringBuffer s) => () { - return new Future.delayed(new Duration(milliseconds: 1), () { - s.write('l$index D '); - }); -}; - -Function makeImmediateSetup(int index, StringBuffer s) => () { - s.write('l$index U '); -}; - -Function makeImmediateTeardown(int index, StringBuffer s) => () { - s.write('l$index D '); -}; diff --git a/test/prints_matcher_test.dart b/test/prints_matcher_test.dart deleted file mode 100644 index cdce9df0..00000000 --- a/test/prints_matcher_test.dart +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.prints_matchers_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -/// The VM and dart2js have different toStrings for closures. -final closureToString = (() {}).toString(); - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('synchronous', () { - test("passes with an expected print", () { - expect(() => print("Hello, world!"), prints("Hello, world!\n")); - }); - - test("combines multiple prints", () { - expect(() { - print("Hello"); - print("World!"); - }, prints("Hello\nWorld!\n")); - }); - - test("works with a Matcher", () { - expect(() => print("Hello, world!"), prints(contains("Hello"))); - }); - - test("describes a failure nicely", () { - expect(() => print("Hello, world!"), prints("Goodbye, world!\n")); - }); - - test("describes a failure with a non-descriptive Matcher nicely", () { - expect(() => print("Hello, world!"), prints(contains("Goodbye"))); - }); - - test("describes a failure with no text nicely", () { - expect(() {}, prints(contains("Goodbye"))); - }); - }, [ - {'result': 'pass'}, - {'result': 'pass'}, - {'result': 'pass'}, - { - 'result': 'fail', - 'message': r'''Expected: prints 'Goodbye, world!\n' - '' - Actual: <Closure: () => dynamic> - Which: printed 'Hello, world!\n' - '' - Which: is different. -Expected: Goodbye, w ... - Actual: Hello, wor ... - ^ - Differ at offset 0 -''' - }, - { - 'result': 'fail', - 'message': r'''Expected: prints contains 'Goodbye' - Actual: <Closure: () => dynamic> - Which: printed 'Hello, world!\n' - '' -''' - }, - { - 'result': 'fail', - 'message': r'''Expected: prints contains 'Goodbye' - Actual: <Closure: () => dynamic> - Which: printed nothing. -''' - } - ]); - - expectTestResults('asynchronous', () { - test("passes with an expected print", () { - expect(() => new Future(() => print("Hello, world!")), - prints("Hello, world!\n")); - }); - - test("combines multiple prints", () { - expect(() => new Future(() { - print("Hello"); - print("World!"); - }), prints("Hello\nWorld!\n")); - }); - - test("works with a Matcher", () { - expect(() => new Future(() => print("Hello, world!")), - prints(contains("Hello"))); - }); - - test("describes a failure nicely", () { - expect(() => new Future(() => print("Hello, world!")), - prints("Goodbye, world!\n")); - }); - - test("describes a failure with a non-descriptive Matcher nicely", () { - expect(() => new Future(() => print("Hello, world!")), - prints(contains("Goodbye"))); - }); - - test("describes a failure with no text nicely", () { - expect(() => new Future.value(), prints(contains("Goodbye"))); - }); - }, [ - {'result': 'pass'}, - {'result': 'pass'}, - {'result': 'pass'}, - { - 'result': 'fail', - 'message': startsWith(r'''Expected future to complete successfully, but it failed with Expected: 'Goodbye, world!\n' - '' - Actual: 'Hello, world!\n' - '' - Which: is different. -Expected: Goodbye, w ... - Actual: Hello, wor ... - ^ - Differ at offset 0 -''') - }, - { - 'result': 'fail', - 'message': startsWith(r'''Expected future to complete successfully, but it failed with Expected: contains 'Goodbye' - Actual: 'Hello, world!\n' - '' -''') - }, - { - 'result': 'fail', - 'message': startsWith(r'''Expected future to complete successfully, but it failed with Expected: contains 'Goodbye' - Actual: '' -''') - } - ]); -} diff --git a/test/protect_async_test.dart b/test/protect_async_test.dart deleted file mode 100644 index 19211f2b..00000000 --- a/test/protect_async_test.dart +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.protect_async_test; - -import 'dart:async'; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('protectAsync', () { - test('protectAsync0', () { - var protected = () { - throw new StateError('error during protectAsync0'); - }; - new Future(protected); - }); - - test('protectAsync1', () { - var protected = (arg) { - throw new StateError('error during protectAsync1: $arg'); - }; - new Future(() => protected('one arg')); - }); - - test('protectAsync2', () { - var protected = (arg1, arg2) { - throw new StateError('error during protectAsync2: $arg1, $arg2'); - }; - new Future(() => protected('arg1', 'arg2')); - }); - - test('throw away 1', () { - return new Future(() {}); - }); - }, [ - { - 'result': 'error', - 'message': 'Caught Bad state: error during protectAsync0' - }, - { - 'result': 'error', - 'message': 'Caught Bad state: error during protectAsync1: one arg' - }, - { - 'result': 'error', - 'message': 'Caught Bad state: error during protectAsync2: arg1, arg2' - }, - {'result': 'pass', 'message': ''} - ]); -} diff --git a/test/returning_future_test.dart b/test/returning_future_test.dart deleted file mode 100644 index d59d8c1b..00000000 --- a/test/returning_future_test.dart +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.returning_future_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('returning futures', () { - test("successful", () { - return new Future.sync(() { - expect(true, true); - }); - }); - // We repeat the fail and error tests, because during development - // I had a situation where either worked fine on their own, and - // error/fail worked, but fail/error would time out. - test("error1", () { - var callback = expectAsync(() {}); - var excesscallback = expectAsync(() {}); - return new Future.sync(() { - excesscallback(); - excesscallback(); - excesscallback(); - callback(); - }); - }); - test("fail1", () { - return new Future.sync(() { - expect(true, false); - }); - }); - test("error2", () { - var callback = expectAsync(() {}); - var excesscallback = expectAsync(() {}); - return new Future.sync(() { - excesscallback(); - excesscallback(); - callback(); - }); - }); - test("fail2", () { - return new Future.sync(() { - fail('failure'); - }); - }); - test('foo5', () {}); - }, [ - {'result': 'pass'}, - { - 'result': 'fail', - 'message': 'Callback called more times than expected (1).' - }, - {'result': 'fail', 'message': 'Expected: <false>\n Actual: <true>\n'}, - { - 'result': 'fail', - 'message': 'Callback called more times than expected (1).' - }, - {'result': 'fail', 'message': 'failure'}, - {'result': 'pass'} - ]); -} diff --git a/test/returning_future_using_runasync_test.dart b/test/returning_future_using_runasync_test.dart deleted file mode 100644 index 3ae13c4d..00000000 --- a/test/returning_future_using_runasync_test.dart +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.returning_future_using_runasync_test; - -import 'dart:async'; - -import 'package:metatest/metatest.dart'; -import 'package:unittest/unittest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('test returning future using scheduleMicrotask', () { - test("successful", () { - return new Future.sync(() { - scheduleMicrotask(() { - expect(true, true); - }); - }); - }); - test("fail1", () { - var callback = expectAsync(() {}); - return new Future.sync(() { - scheduleMicrotask(() { - expect(true, false); - callback(); - }); - }); - }); - test('error1', () { - var callback = expectAsync(() {}); - var excesscallback = expectAsync(() {}); - return new Future.sync(() { - scheduleMicrotask(() { - excesscallback(); - excesscallback(); - callback(); - }); - }); - }); - test("fail2", () { - var callback = expectAsync(() {}); - return new Future.sync(() { - scheduleMicrotask(() { - fail('failure'); - callback(); - }); - }); - }); - test('error2', () { - var callback = expectAsync(() {}); - var excesscallback = expectAsync(() {}); - return new Future.sync(() { - scheduleMicrotask(() { - excesscallback(); - excesscallback(); - excesscallback(); - callback(); - }); - }); - }); - test('foo6', () {}); - }, [ - {'description': 'successful', 'result': 'pass',}, - { - 'description': 'fail1', - 'message': 'Expected: <false>\n' ' Actual: <true>\n' '', - 'result': 'fail', - }, - { - 'description': 'error1', - 'message': 'Callback called more times than expected (1).', - 'result': 'fail', - }, - {'description': 'fail2', 'message': 'failure', 'result': 'fail',}, - { - 'description': 'error2', - 'message': 'Callback called more times than expected (1).', - 'result': 'fail', - }, - {'description': 'foo6', 'result': 'pass',} - ]); -} diff --git a/test/runner_test.dart b/test/runner_test.dart index 2b22667f..5dc54392 100644 --- a/test/runner_test.dart +++ b/test/runner_test.dart @@ -15,9 +15,10 @@ String _sandbox; final _success = """ import 'dart:async'; +import 'package:unittest/unittest.dart'; + void main() { - var declarer = Zone.current[#unittest.declarer]; - declarer.test("success", () {}); + test("success", () {}); } """; @@ -27,8 +28,7 @@ import 'dart:async'; import 'package:unittest/unittest.dart'; void main() { - var declarer = Zone.current[#unittest.declarer]; - declarer.test("failure", () => throw new TestFailure("oh no")); + test("failure", () => throw new TestFailure("oh no")); } """; @@ -167,6 +167,15 @@ Usage: pub run unittest:unittest [files or directories...] var result = _runUnittest([]); expect(result.exitCode, equals(0)); }); + + test("directly", () { + new File(p.join(_sandbox, "test.dart")).writeAsStringSync(_success); + var result = _runDart([ + "--package-root=${p.join(packageDir, 'packages')}", + "test.dart" + ]); + expect(result.stdout, contains("All tests passed!")); + }); }); group("runs failing tests", () { @@ -196,8 +205,20 @@ Usage: pub run unittest:unittest [files or directories...] var result = _runUnittest([]); expect(result.exitCode, equals(1)); }); + + test("directly", () { + new File(p.join(_sandbox, "test.dart")).writeAsStringSync(_failure); + var result = _runDart([ + "--package-root=${p.join(packageDir, 'packages')}", + "test.dart" + ]); + expect(result.stdout, contains("Some tests failed.")); + }); }); } ProcessResult _runUnittest(List<String> args) => runUnittest(args, workingDirectory: _sandbox); + +ProcessResult _runDart(List<String> args) => + runDart(args, workingDirectory: _sandbox); diff --git a/test/runtests_without_tests_test.dart b/test/runtests_without_tests_test.dart deleted file mode 100644 index 4257ddaa..00000000 --- a/test/runtests_without_tests_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.runtests_without_tests; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('runTests() without tests', () { - group('no tests', () {}); - }, []); -} diff --git a/test/setup_and_teardown_test.dart b/test/setup_and_teardown_test.dart deleted file mode 100644 index 37121370..00000000 --- a/test/setup_and_teardown_test.dart +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.setup_and_teardown_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('setup and teardown test', () { - var hasSetup = false; - var hasTeardown = false; - - group('a', () { - setUp(() { - hasSetup = true; - }); - tearDown(() { - hasTeardown = true; - }); - test('test', () {}); - }); - - test('verify', () { - expect(hasSetup, isTrue); - expect(hasTeardown, isTrue); - }); - }); -} diff --git a/test/setup_test.dart b/test/setup_test.dart deleted file mode 100644 index ba27b443..00000000 --- a/test/setup_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.setup_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('setup test', () { - group('a', () { - var hasSetup = false; - setUp(() { - hasSetup = true; - }); - test('test', () { - expect(hasSetup, isTrue); - }); - }); - }); -} diff --git a/test/single_correct_test.dart b/test/single_correct_test.dart deleted file mode 100644 index a6b41558..00000000 --- a/test/single_correct_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.single_correct_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('single correct test', () { - test('test', () => expect(2 + 3, equals(5))); - }); -} diff --git a/test/single_failing_test.dart b/test/single_failing_test.dart deleted file mode 100644 index e9c1819b..00000000 --- a/test/single_failing_test.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.single_failing_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsFail('single failing test', () { - test('test', () => expect(2 + 2, equals(5))); - }); -} diff --git a/test/skipped_soloed_nested_test.dart b/test/skipped_soloed_nested_test.dart deleted file mode 100644 index 1e590e7c..00000000 --- a/test/skipped_soloed_nested_test.dart +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.skipped_soloed_nested_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestResults('skipped/soloed nested groups with setup/teardown', () { - StringBuffer s = null; - setUp(() { - if (s == null) s = new StringBuffer(); - }); - test('top level', () { - s.write('A'); - }); - skip_test('skipped top level', () { - s.write('B'); - }); - skip_group('skipped top level group', () { - setUp(() { - s.write('C'); - }); - solo_test('skipped solo nested test', () { - s.write('D'); - }); - }); - group('non-solo group', () { - setUp(() { - s.write('E'); - }); - test('in non-solo group', () { - s.write('F'); - }); - solo_test('solo_test in non-solo group', () { - s.write('G'); - }); - }); - solo_group('solo group', () { - setUp(() { - s.write('H'); - }); - test('solo group non-solo test', () { - s.write('I'); - }); - solo_test('solo group solo test', () { - s.write('J'); - }); - group('nested non-solo group in solo group', () { - test('nested non-solo group non-solo test', () { - s.write('K'); - }); - solo_test('nested non-solo group solo test', () { - s.write('L'); - }); - }); - }); - solo_test('final', () { - expect(s.toString(), "EGHIHJHKHL"); - }); - }, [ - { - 'description': 'non-solo group solo_test in non-solo group', - 'result': 'pass', - }, - {'description': 'solo group solo group non-solo test', 'result': 'pass',}, - {'description': 'solo group solo group solo test', 'result': 'pass',}, - { - 'description': 'solo group nested non-solo group in solo group ' - 'nested non-solo group non-solo test', - 'result': 'pass', - }, - { - 'description': 'solo group nested non-solo group in solo group ' - 'nested non-solo group solo test', - 'result': 'pass', - }, - {'description': 'final', 'result': 'pass',} - ]); -} diff --git a/test/teardown_test.dart b/test/teardown_test.dart deleted file mode 100644 index b1f6ee09..00000000 --- a/test/teardown_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.teardown_test; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('teardown test', () { - var tornDown = false; - - group('a', () { - tearDown(() { - tornDown = true; - }); - test('test', () {}); - }); - - test('expect teardown', () { - expect(tornDown, isTrue); - }); - }); -} diff --git a/test/test_utils.dart b/test/test_utils.dart deleted file mode 100644 index f86631bb..00000000 --- a/test/test_utils.dart +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.test_utils; - -import 'dart:collection'; - -import 'package:unittest/unittest.dart'; - -void shouldFail(value, Matcher matcher, expected) { - var failed = false; - try { - expect(value, matcher); - } on TestFailure catch (err) { - failed = true; - - var _errorString = err.message; - - if (expected is String) { - expect(_errorString, equalsIgnoringWhitespace(expected)); - } else { - expect(_errorString.replaceAll('\n', ''), expected); - } - } - - expect(failed, isTrue, reason: 'Expected to fail.'); -} - -void shouldPass(value, Matcher matcher) { - expect(value, matcher); -} - -class Widget { - int price; -} - -class HasPrice extends CustomMatcher { - HasPrice(matcher) : super("Widget with a price that is", "price", matcher); - featureValueOf(actual) => actual.price; -} - -class SimpleIterable extends IterableBase<int> { - final int count; - - SimpleIterable(this.count); - - bool contains(int val) => count < val ? false : true; - - bool any(bool f(element)) { - for (var i = 0; i <= count; i++) { - if (f(i)) return true; - } - return false; - } - - String toString() => "<[$count]>"; - - Iterator get iterator { - return new _SimpleIterator(count); - } -} - -class _SimpleIterator implements Iterator<int> { - int _count; - int _current; - - _SimpleIterator(this._count); - - bool moveNext() { - if (_count > 0) { - _current = _count; - _count--; - return true; - } - _current = null; - return false; - } - - int get current => _current; -} diff --git a/test/testcases_immutable_test.dart b/test/testcases_immutable_test.dart deleted file mode 100644 index c744fa1d..00000000 --- a/test/testcases_immutable_test.dart +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.testcases_immutable; - -import 'package:unittest/unittest.dart'; - -import 'package:metatest/metatest.dart'; - -void main() => initTests(_test); - -void _test(message) { - initMetatest(message); - - expectTestsPass('testcases immutable', () { - test('test', () { - expect(() => testCases.clear(), throwsUnsupportedError); - expect(() => testCases.removeLast(), throwsUnsupportedError); - }); - }); -} diff --git a/test/throws_matchers_test.dart b/test/throws_matchers_test.dart deleted file mode 100644 index 3ed8d01a..00000000 --- a/test/throws_matchers_test.dart +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.core_matchers_test; - -import 'package:unittest/unittest.dart'; - -import 'test_utils.dart'; - -doesNotThrow() {} -doesThrow() { - throw 'X'; -} - -void main() { - test('throws', () { - shouldFail(doesNotThrow, throws, matches(r"Expected: throws" - r" Actual: <Closure(: \(\) => dynamic " - r"from Function 'doesNotThrow': static\.)?>" - r" Which: did not throw")); - shouldPass(doesThrow, throws); - shouldFail(true, throws, "Expected: throws" - " Actual: <true>" - " Which: is not a Function or Future"); - }); - - test('throwsA', () { - shouldPass(doesThrow, throwsA(equals('X'))); - shouldFail(doesThrow, throwsA(equals('Y')), matches(r"Expected: throws 'Y'" - r" Actual: <Closure(: \(\) => dynamic " - r"from Function 'doesThrow': static\.)?>" - r" Which: threw 'X'")); - }); - - test('throwsA', () { - shouldPass(doesThrow, throwsA(equals('X'))); - shouldFail(doesThrow, throwsA(equals('Y')), matches("Expected: throws 'Y'.*" - "Actual: <Closure.*" - "Which: threw 'X'")); - }); - - group('exception/error matchers', () { - test('throwsCyclicInitializationError', () { - expect(() => _Bicycle.foo, throwsCyclicInitializationError); - }); - - test('throwsConcurrentModificationError', () { - expect(() { - var a = {'foo': 'bar'}; - for (var k in a.keys) { - a.remove(k); - } - }, throwsConcurrentModificationError); - }); - - test('throwsNullThrownError', () { - expect(() => throw null, throwsNullThrownError); - }); - }); -} - -class _Bicycle { - static final foo = bar(); - - static bar() { - return foo + 1; - } -} diff --git a/test/utils.dart b/test/utils.dart index 64a3da71..d8e3e9de 100644 --- a/test/utils.dart +++ b/test/utils.dart @@ -7,10 +7,12 @@ library unittest.test.utils; import 'dart:async'; import 'dart:collection'; +import 'package:unittest/src/invoker.dart'; import 'package:unittest/src/live_test.dart'; import 'package:unittest/src/load_exception.dart'; import 'package:unittest/src/remote_exception.dart'; import 'package:unittest/src/state.dart'; +import 'package:unittest/src/suite.dart'; import 'package:unittest/unittest.dart'; // The last state change detected via [expectStates]. @@ -63,19 +65,102 @@ void expectSingleError(LiveTest liveTest) { } /// Returns a matcher that matches a [TestFailure] with the given [message]. -Matcher isTestFailure(String message) => predicate( - (error) => error is TestFailure && error.message == message, - 'is a TestFailure with message "$message"'); +/// +/// [message] can be a string or a [Matcher]. +Matcher isTestFailure(message) => new _IsTestFailure(wrapMatcher(message)); + +class _IsTestFailure extends Matcher { + final Matcher _message; + + _IsTestFailure(this._message); + + bool matches(item, Map matchState) => + item is TestFailure && _message.matches(item.message, matchState); + + Description describe(Description description) => + description.add('a TestFailure with message ').addDescriptionOf(_message); + + Description describeMismatch(item, Description mismatchDescription, + Map matchState, bool verbose) { + if (item is! TestFailure) { + return mismatchDescription.addDescriptionOf(item) + .add('is not a TestFailure'); + } else { + return mismatchDescription + .add('message ') + .addDescriptionOf(item.message) + .add(' is not ') + .addDescriptionOf(_message); + } + } +} /// Returns a matcher that matches a [RemoteException] with the given [message]. -Matcher isRemoteException(String message) => predicate( - (error) => error is RemoteException && error.message == message, - 'is a RemoteException with message "$message"'); +/// +/// [message] can be a string or a [Matcher]. +Matcher isRemoteException(message) => + new _IsRemoteException(wrapMatcher(message)); + +class _IsRemoteException extends Matcher { + final Matcher _message; + + _IsRemoteException(this._message); + + bool matches(item, Map matchState) => + item is RemoteException && _message.matches(item.message, matchState); + + Description describe(Description description) => + description.add('a RemoteException with message ') + .addDescriptionOf(_message); + + Description describeMismatch(item, Description mismatchDescription, + Map matchState, bool verbose) { + if (item is! RemoteException) { + return mismatchDescription.addDescriptionOf(item) + .add('is not a RemoteException'); + } else { + return mismatchDescription + .add('message ') + .addDescriptionOf(item) + .add(' is not ') + .addDescriptionOf(_message); + } + } +} + +/// Returns a matcher that matches a [LoadException] with the given +/// [innerError]. +/// +/// [innerError] can be a string or a [Matcher]. +Matcher isLoadException(innerError) => + new _IsLoadException(wrapMatcher(innerError)); + +class _IsLoadException extends Matcher { + final Matcher _innerError; + + _IsLoadException(this._innerError); -/// Returns a matcher that matches a [LoadException] with the given [message]. -Matcher isLoadException(String message) => predicate( - (error) => error is LoadException && error.innerError == message, - 'is a LoadException with message "$message"'); + bool matches(item, Map matchState) => + item is LoadException && _innerError.matches(item.innerError, matchState); + + Description describe(Description description) => + description.add('a LoadException with message ') + .addDescriptionOf(_innerError); + + Description describeMismatch(item, Description mismatchDescription, + Map matchState, bool verbose) { + if (item is! LoadException) { + return mismatchDescription.addDescriptionOf(item) + .add('is not a LoadException'); + } else { + return mismatchDescription + .add('inner error ') + .addDescriptionOf(item) + .add(' is not ') + .addDescriptionOf(_innerError); + } + } +} /// Returns a [Future] that completes after pumping the event queue [times] /// times. @@ -90,3 +175,67 @@ Future pumpEventQueue([int times=20]) { // method. return new Future(() => pumpEventQueue(times - 1)); } + +/// Returns a local [LiveTest] that runs [body]. +LiveTest createTest(body()) { + var test = new LocalTest("test", body); + var suite = new Suite("suite", [test]); + return test.load(suite); +} + +/// Runs [body] as a test. +/// +/// Once it completes, returns the [LiveTest] used to run it. +Future<LiveTest> runTest(body()) { + var liveTest = createTest(body); + return liveTest.run().then((_) => liveTest); +} + +/// Asserts that [liveTest] has completed and passed. +/// +/// If the test had any errors, they're surfaced nicely into the outer test. +void expectTestPassed(LiveTest liveTest) { + // Since the test is expected to pass, we forward any current or future errors + // to the outer test, because they're definitely unexpected. + for (var error in liveTest.errors) { + registerException(error.error, error.stackTrace); + } + liveTest.onError.listen((error) { + registerException(error.error, error.stackTrace); + }); + + expect(liveTest.state.status, equals(Status.complete)); + expect(liveTest.state.result, equals(Result.success)); +} + +/// Asserts that [liveTest] failed with a single [TestFailure] whose message +/// matches [message]. +void expectTestFailed(LiveTest liveTest, message) { + expect(liveTest.state.status, equals(Status.complete)); + expect(liveTest.state.result, equals(Result.failure)); + expect(liveTest.errors, hasLength(1)); + expect(liveTest.errors.first.error, isTestFailure(message)); +} + +/// Assert that the [test] callback causes a test to block until [stopBlocking] +/// is called at some later time. +/// +/// [stopBlocking] is passed the return value of [test]. +Future expectTestBlocks(test(), stopBlocking(value)) { + var liveTest; + var future; + liveTest = createTest(() { + var value = test(); + future = pumpEventQueue().then((_) { + expect(liveTest.state.status, equals(Status.running)); + stopBlocking(value); + }); + }); + + return liveTest.run().then((_) { + expectTestPassed(liveTest); + // Ensure that the outer test doesn't complete until the inner future + // completes. + return future; + }); +} diff --git a/test/vm_listener_test.dart b/test/vm_listener_test.dart index 47ee330a..f401a0c7 100644 --- a/test/vm_listener_test.dart +++ b/test/vm_listener_test.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:isolate'; -import 'package:unittest/src/declarer.dart'; import 'package:unittest/src/invoker.dart'; import 'package:unittest/src/isolate_test.dart'; import 'package:unittest/src/live_test.dart'; @@ -17,9 +16,6 @@ import 'package:unittest/unittest.dart'; import 'utils.dart'; -/// The current declarer. -Declarer get _declarer => Zone.current[#unittest.declarer]; - /// An isolate that's been spun up for the current test. /// /// This is tracked so that it can be killed once the test is done. @@ -312,23 +308,23 @@ void _wrongArity(SendPort sendPort) => /// An isolate entrypoint that defines three tests that succeed. void _successfulTests(SendPort sendPort) { VmListener.start(sendPort, () => () { - _declarer.test("successful 1", () {}); - _declarer.test("successful 2", () {}); - _declarer.test("successful 3", () {}); + test("successful 1", () {}); + test("successful 2", () {}); + test("successful 3", () {}); }); } /// An isolate entrypoint that defines a test that fails. void _failingTest(SendPort sendPort) { VmListener.start(sendPort, () => () { - _declarer.test("failure", () => throw new TestFailure('oh no')); + test("failure", () => throw new TestFailure('oh no')); }); } /// An isolate entrypoint that defines a test that fails after succeeding. void _failAfterSucceedTest(SendPort sendPort) { VmListener.start(sendPort, () => () { - _declarer.test("fail after succeed", () { + test("fail after succeed", () { pumpEventQueue().then((_) { throw new TestFailure('oh no'); }); @@ -339,7 +335,7 @@ void _failAfterSucceedTest(SendPort sendPort) { /// An isolate entrypoint that defines a test that fails multiple times. void _multiFailTest(SendPort sendPort) { VmListener.start(sendPort, () => () { - _declarer.test("multiple failures", () { + test("multiple failures", () { Invoker.current.addOutstandingCallback(); new Future(() => throw new TestFailure("one")); new Future(() => throw new TestFailure("two")); @@ -352,14 +348,14 @@ void _multiFailTest(SendPort sendPort) { /// An isolate entrypoint that defines a test that errors. void _errorTest(SendPort sendPort) { VmListener.start(sendPort, () => () { - _declarer.test("error", () => throw 'oh no'); + test("error", () => throw 'oh no'); }); } /// An isolate entrypoint that defines a test that errors after succeeding. void _errorAfterSucceedTest(SendPort sendPort) { VmListener.start(sendPort, () => () { - _declarer.test("error after succeed", () { + test("error after succeed", () { pumpEventQueue().then((_) => throw 'oh no'); }); }); @@ -368,7 +364,7 @@ void _errorAfterSucceedTest(SendPort sendPort) { /// An isolate entrypoint that defines a test that errors multiple times. void _multiErrorTest(SendPort sendPort) { VmListener.start(sendPort, () => () { - _declarer.test("multiple errors", () { + test("multiple errors", () { Invoker.current.addOutstandingCallback(); new Future(() => throw "one"); new Future(() => throw "two"); diff --git a/test/with_test_environment_test.dart b/test/with_test_environment_test.dart deleted file mode 100644 index 456384fa..00000000 --- a/test/with_test_environment_test.dart +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library unittest.with_test_environment_test; - -import 'dart:async'; -import 'package:unittest/unittest.dart'; - -class TestConfiguration extends SimpleConfiguration { - final Completer _completer = new Completer(); - List<TestCase> _results; - - TestConfiguration(); - - void onSummary(int passed, int failed, int errors, List<TestCase> results, - String uncaughtError) { - super.onSummary(passed, failed, errors, results, uncaughtError); - _results = results; - } - - Future get done => _completer.future; - - onDone(success) { - new Future.sync(() => super.onDone(success)) - .then(_completer.complete) - .catchError(_completer.completeError); - } - - bool checkIfTestRan(String testName) { - return _results.any((test) => test.description == testName); - } -} - -Future runUnittests(Function callback) { - TestConfiguration config = unittestConfiguration = new TestConfiguration(); - callback(); - - return config.done; -} - -void runTests() { - test('test', () => expect(2 + 3, equals(5))); -} - -void runTests1() { - test('test1', () => expect(4 + 3, equals(7))); -} - -// Test that we can run two different sets of tests in the same run using the -// withTestEnvironment method. -void main() { - // Check that setting config a second time does not change the configuration - runUnittests(runTests); - var config = unittestConfiguration; - runUnittests(runTests1); - expect(config, unittestConfiguration); - - // Test that we can run both when encapsulating in their own private test - // environment. Also test that the tests actually running are the ones - // scheduled in the runTests/runTests1 methods. - withTestEnvironment(() { - runUnittests(runTests).whenComplete(() { - TestConfiguration config = unittestConfiguration; - expect(config.checkIfTestRan('test'), true); - expect(config.checkIfTestRan('test1'), false); - }); - }); - withTestEnvironment(() { - runUnittests(runTests1).whenComplete(() { - TestConfiguration config = unittestConfiguration; - expect(config.checkIfTestRan('test'), false); - expect(config.checkIfTestRan('test1'), true); - }); - }); - - // Third test that we can run with two nested test environments. - withTestEnvironment(() { - runUnittests(runTests); - withTestEnvironment(() { - runUnittests(runTests1); - }); - }); -} -- GitLab