Index: pkg/metatest/lib/metatest.dart |
diff --git a/pkg/scheduled_test/test/metatest.dart b/pkg/metatest/lib/metatest.dart |
similarity index 65% |
rename from pkg/scheduled_test/test/metatest.dart |
rename to pkg/metatest/lib/metatest.dart |
index 3c0cd9acb39f8189e56dcc284fac685d4ddd79f9..c8b5660a9bc750ce331d9452322bb18c27ee3ccf 100644 |
--- a/pkg/scheduled_test/test/metatest.dart |
+++ b/pkg/metatest/lib/metatest.dart |
@@ -1,4 +1,4 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
+// 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. |
@@ -10,14 +10,88 @@ |
library metatest; |
import 'dart:async'; |
-import 'dart:io'; |
import 'dart:isolate'; |
-import 'package:path/path.dart' as path; |
import 'package:unittest/unittest.dart'; |
-import 'package:scheduled_test/scheduled_test.dart' as scheduled_test; |
-import 'utils.dart'; |
+import 'src/utils.dart'; |
+ |
+/// Whether or not we're running in a child isolate that's supposed to run a |
+/// test. |
+bool _inChildIsolate; |
+ |
+/// The port with which the child isolate should communicate with the parent |
+/// isolate. |
+/// |
+/// `null` in the parent isolate. |
+SendPort _replyTo; |
+ |
+/// The only value of the configuration used in metatest. |
+final _metaConfiguration = new _MetaConfiguration(); |
+ |
+/// The function holding the tests to be run. |
+Function _testBody; |
+ |
+/// The description of the test to run in the child isolate. |
+/// |
+/// `null` in the parent isolate. |
+String _testToRun; |
+ |
+/// Stores the optional timeout used to override the default unittest timeout. |
+Duration _timeoutOverride; |
+ |
+/// Runs [setUpFn] before every metatest. |
+/// |
+/// Note that [setUpFn] will be overwritten if the test itself calls [setUp]. |
+void metaSetUp(void setUpFn()) { |
+ if (_inChildIsolate) setUp(setUpFn); |
+} |
+ |
+/// Runs a set of tests defined in `body` and checks the result by comparing |
+/// with values in `expectedResults`. |
+/// |
+/// [expectedResults] is a list which should have a [Map] value for each test |
+/// that is run. Each [Map] key corresponds to values from a completed test |
+/// case: "description", "message", "result", and "stackTrace". |
+/// |
+/// The value of "result" can be one of: 'pass', 'fail', or 'error'. |
+/// |
+/// The value for "stackTrace" is the [String] 'null' if the property is `null` |
+/// on the source test case. Otherwise, it is the output of `toString`. The |
+/// format is not guaranteed. |
+/// |
+/// Here's an example of a `expectedResults` value for two tests, where the |
+/// where the first fails and the second passes. |
+/// |
+/// ```dart |
+/// [{ |
+/// 'description': 'test', |
+/// 'message': 'Caught error!', |
+/// 'result': 'fail', |
+/// }, { |
+/// 'description': 'follow up', |
+/// 'result': 'pass', |
+/// }] |
+/// ``` |
+void expectTestResults(String description, void body(), |
+ List<Map> expectedResults) { |
+ _setUpTest(description, body, (resultsMap) { |
+ var list = resultsMap['results']; |
+ expect(list, hasLength(expectedResults.length), |
+ reason: 'The number of tests run does not match the number of expected' |
+ ' results.'); |
+ |
+ for (var i = 0; i < list.length; i++) { |
+ var expectedMap = expectedResults[i]; |
+ var map = list[i]; |
+ |
+ expectedMap.forEach((key, value) { |
+ expect(map, containsPair(key, value), reason: 'A test did not match the' |
+ ' expected value for "$key" at index $i.'); |
+ }); |
+ } |
+ }); |
+} |
/// Declares a test with the given [description] and [body]. [body] corresponds |
/// to the `main` method of a test file, and will be run in an isolate. By |
@@ -26,12 +100,12 @@ import 'utils.dart'; |
void expectTestsPass(String description, void body(), {List<String> passing}) { |
_setUpTest(description, body, (results) { |
if (_hasError(results)) { |
- throw 'Expected all tests to pass, but got error(s):\n' |
- '${_summarizeTests(results)}'; |
+ fail('Expected all tests to pass, but got error(s):\n' |
+ '${_summarizeTests(results)}'); |
} else if (passing == null) { |
if (results['failed'] != 0) { |
- throw 'Expected all tests to pass, but some failed:\n' |
- '${_summarizeTests(results)}'; |
+ fail('Expected all tests to pass, but some failed:\n' |
+ '${_summarizeTests(results)}'); |
} |
} else { |
var shouldPass = new Set.from(passing); |
@@ -42,7 +116,7 @@ void expectTestsPass(String description, void body(), {List<String> passing}) { |
if (!shouldPass.containsAll(didPass) || |
!didPass.containsAll(shouldPass)) { |
String stringify(Set<String> tests) => |
- '{${tests.map((t) => '"$t"').join(', ')}}'; |
+ '{${tests.map((t) => '"$t"').join(', ')}}'; |
fail('Expected exactly ${stringify(shouldPass)} to pass, but ' |
'${stringify(didPass)} passed.\n' |
@@ -52,38 +126,6 @@ void expectTestsPass(String description, void body(), {List<String> passing}) { |
}); |
} |
-/// Declares a test with the given [description] and [body]. |
-/// |
-/// [body] corresponds |
-/// to the `main` method of a test file, and will be run in an isolate. |
-/// |
-/// All tests must have an expected result in [expectedResults]. |
-void expectTests(String description, void body(), |
- Map<String, String> expectedResults) { |
- _setUpTest(description, body, (results) { |
- expectedResults = new Map.from(expectedResults); |
- |
- for (var testResult in results['results']) { |
- var description = testResult['description']; |
- |
- expect(expectedResults, contains(description), |
- reason: '"$description" did not have an expected result set.\n' |
- '${_summarizeTests(results)}'); |
- |
- var result = testResult['result']; |
- |
- expect(expectedResults, containsPair(description, result), |
- reason: 'The test "$description" not not have the expected result.\n' |
- '${_summarizeTests(results)}'); |
- |
- expectedResults.remove(description); |
- } |
- expect(expectedResults, isEmpty, |
- reason: 'Unexpected additional test results\n' |
- '${_summarizeTests(results)}'); |
- }); |
-} |
- |
/// Declares a test with the given [description] and [body]. [body] corresponds |
/// to the `main` method of a test file, and will be run in an isolate. Expects |
/// all tests defined by [body] to fail. |
@@ -99,45 +141,27 @@ void expectTestsFail(String description, void body()) { |
}); |
} |
-/// Runs [setUpFn] before every metatest. Note that [setUpFn] will be |
-/// overwritten if the test itself calls [setUp]. |
-void metaSetUp(void setUpFn()) { |
- if (_inChildIsolate) scheduled_test.setUp(setUpFn); |
-} |
- |
/// Sets up a test with the given [description] and [body]. After the test runs, |
/// calls [validate] with the result map. |
-void _setUpTest(String description, void body(), void validate(Map)) { |
+void _setUpTest(String description, void body(), void validate(Map map)) { |
if (_inChildIsolate) { |
_ensureInitialized(); |
if (_testToRun == description) body(); |
} else { |
test(description, () { |
- expect(_runInIsolate(description).then(validate), completes); |
+ return _runInIsolate(description).then(validate); |
}); |
} |
} |
-/// The description of the test to run in the child isolate. |
-/// |
-/// `null` in the parent isolate. |
-String _testToRun; |
- |
-/// The port with which the child isolate should communicate with the parent |
-/// isolate. |
-/// |
-/// `null` in the parent isolate. |
-SendPort _replyTo; |
- |
-/// Whether or not we're running in a child isolate that's supposed to run a |
-/// test. |
-bool _inChildIsolate; |
- |
/// Initialize metatest. |
/// |
/// [message] should be the second argument to [main]. It's used to determine |
/// whether this test is in the parent isolate or a child isolate. |
-void initMetatest(message) { |
+/// |
+/// [timeout], when specified, overrides the default timeout for unittest. |
+void initMetatest(message, {Duration timeout}) { |
+ _timeoutOverride = timeout; |
if (message == null) { |
_inChildIsolate = false; |
} else { |
@@ -147,22 +171,27 @@ void initMetatest(message) { |
} |
} |
-/// Runs the test described by [description] in its own isolate. Returns a map |
-/// describing the results of that test run. |
+// TODO(kevmoo) We need to capture the main method to allow running in an |
+// isolate. There is no mechanism to capture the current executing URI between |
+// browser and vm. Issue 1145 and/or Issue 8440 |
+void initTests(void testBody(message)) { |
+ _testBody = testBody; |
+ _testBody(null); |
+} |
+ |
+/// Runs the test described by [description] in its own isolate. |
+/// |
+/// Returns a map describing the results of that test run. |
Future<Map> _runInIsolate(String description) { |
+ if (_testBody == null) { |
+ throw new StateError('initTests was not called.'); |
+ } |
+ |
var replyPort = new ReceivePort(); |
- // TODO(nweiz): Don't use path here once issue 8440 is fixed. |
- var uri = path.toUri(path.absolute(path.fromUri(Platform.script))); |
- return Isolate.spawnUri(uri, [], { |
+ return Isolate.spawn(_testBody, { |
'testToRun': description, |
'replyTo': replyPort.sendPort |
- }).then((_) { |
- // TODO(nweiz): Remove this timeout once issue 8875 is fixed and we can |
- // capture top-level exceptions. |
- return timeout(replyPort.first, 30 * 1000, () { |
- throw 'Timed out waiting for test to complete.'; |
- }); |
- }); |
+ }).then((_) => replyPort.first); |
} |
/// Returns whether [results] (a test result map) describes a test run in which |
@@ -211,11 +240,12 @@ String _indent(String str) { |
/// Ensure that the metatest configuration is loaded. |
void _ensureInitialized() { |
- unittestConfiguration = _singleton; |
+ unittestConfiguration = _metaConfiguration; |
+ if (_timeoutOverride != null) { |
+ unittestConfiguration.timeout = _timeoutOverride; |
+ } |
} |
-final _singleton = new _MetaConfiguration(); |
- |
/// Special test configuration for use within the child isolates. This hides all |
/// output and reports data back to the parent isolate. |
class _MetaConfiguration extends Configuration { |