| Index: pkg/unittest/lib/unittest.dart
|
| diff --git a/pkg/unittest/lib/unittest.dart b/pkg/unittest/lib/unittest.dart
|
| index 05eca70f9fcc558fda37b68ee17d2ea35d2c2d3b..0eb98c73488707bf09aad0ac1514dea6ff769d1f 100644
|
| --- a/pkg/unittest/lib/unittest.dart
|
| +++ b/pkg/unittest/lib/unittest.dart
|
| @@ -151,8 +151,29 @@ part 'src/simple_configuration.dart';
|
| part 'src/group_context.dart';
|
| part 'src/spread_args_helper.dart';
|
| part 'src/test_case.dart';
|
| +part 'src/test_environment.dart';
|
|
|
| -Configuration _config;
|
| +const Symbol _TEST_ENVIRONMENT = #test_environment;
|
| +
|
| +final TestEnvironment _defaultEnvironment = new TestEnvironment();
|
| +
|
| +/**
|
| + * Internal getter for the current unittest config.
|
| + */
|
| +TestEnvironment get _environment {
|
| + var environment = Zone.current[_TEST_ENVIRONMENT];
|
| + if (environment == null) {
|
| + return _defaultEnvironment;
|
| + }
|
| + return environment;
|
| +}
|
| +
|
| +// Convenience getter/setter for the current environment's config.
|
| +Configuration get _config => _environment.config;
|
| +void set _config(var config) { _environment.config = config; }
|
| +
|
| +// Convenience getter for the current environment's test cases.
|
| +List<TestCase> get _testCases => _environment.testCases;
|
|
|
| /// [Configuration] used by the unittest library. Note that if a
|
| /// configuration has not been set, calling this getter will create
|
| @@ -184,48 +205,20 @@ void logMessage(String message) =>
|
| /// Separator used between group names and test names.
|
| String groupSep = ' ';
|
|
|
| -final List<TestCase> _testCases = new List<TestCase>();
|
| -
|
| /// Tests executed in this suite.
|
| -final List<TestCase> testCases = new UnmodifiableListView<TestCase>(_testCases);
|
| +List<TestCase> get testCases => new UnmodifiableListView<TestCase>(_testCases);
|
|
|
| /// Interval (in msecs) after which synchronous tests will insert an async
|
| /// delay to allow DOM or other updates.
|
| const int BREATH_INTERVAL = 200;
|
|
|
| -/// The set of tests to run can be restricted by using [solo_test] and
|
| -/// [solo_group].
|
| -/// As groups can be nested we use a counter to keep track of the nest level
|
| -/// of soloing, and a flag to tell if we have seen any solo tests.
|
| -int _soloNestingLevel = 0;
|
| -bool _soloTestSeen = false;
|
| -
|
| -// 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();
|
| -_GroupContext _currentContext = _rootContext;
|
| -
|
| -/// Represents the index of the currently running test case
|
| -/// == -1 implies the test system is not running
|
| -/// == [number of test cases] is a short-lived state flagging that the last test
|
| -/// has completed
|
| -int _currentTestCaseIndex = -1;
|
| -
|
| /// [TestCase] currently being executed.
|
| TestCase get currentTestCase =>
|
| - (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length)
|
| - ? testCases[_currentTestCaseIndex]
|
| + (_environment.currentTestCaseIndex >= 0 &&
|
| + _environment.currentTestCaseIndex < testCases.length)
|
| + ? testCases[_environment.currentTestCaseIndex]
|
| : null;
|
|
|
| -/// Whether the framework is in an initialized state.
|
| -bool _initialized = false;
|
| -
|
| -String _uncaughtErrorMessage = null;
|
| -
|
| -/// Time since we last gave non-sync code a chance to be scheduled.
|
| -int _lastBreath = new DateTime.now().millisecondsSinceEpoch;
|
| -
|
| /* Test case result strings. */
|
| // TODO(gram) we should change these constants to use a different string
|
| // (so that writing 'FAIL' in the middle of a test doesn't
|
| @@ -244,7 +237,7 @@ const ERROR = 'error';
|
| void test(String spec, TestFunction body) {
|
| _requireNotRunning();
|
| ensureInitialized();
|
| - if (!_soloTestSeen || _soloNestingLevel > 0) {
|
| + if (!_environment.soloTestSeen || _environment.soloNestingLevel > 0) {
|
| var testcase = new TestCase._internal(testCases.length + 1, _fullSpec(spec),
|
| body);
|
| _testCases.add(testcase);
|
| @@ -271,16 +264,16 @@ void skip_test(String spec, TestFunction body) {}
|
| void solo_test(String spec, TestFunction body) {
|
| _requireNotRunning();
|
| ensureInitialized();
|
| - if (!_soloTestSeen) {
|
| - _soloTestSeen = true;
|
| + if (!_environment.soloTestSeen) {
|
| + _environment.soloTestSeen = true;
|
| // This is the first solo-ed test. Discard all tests up to now.
|
| _testCases.clear();
|
| }
|
| - ++_soloNestingLevel;
|
| + ++_environment.soloNestingLevel;
|
| try {
|
| test(spec, body);
|
| } finally {
|
| - --_soloNestingLevel;
|
| + --_environment.soloNestingLevel;
|
| }
|
| }
|
|
|
| @@ -317,15 +310,15 @@ Function expectAsyncUntil(Function callback, bool isDone(), {String id}) =>
|
| void group(String description, void body()) {
|
| ensureInitialized();
|
| _requireNotRunning();
|
| - _currentContext = new _GroupContext(_currentContext, description);
|
| + _environment.currentContext = new _GroupContext(_environment.currentContext, description);
|
| try {
|
| body();
|
| } catch (e, trace) {
|
| var stack = (trace == null) ? '' : ': ${trace.toString()}';
|
| - _uncaughtErrorMessage = "${e.toString()}$stack";
|
| + _environment.uncaughtErrorMessage = "${e.toString()}$stack";
|
| } finally {
|
| // Now that the group is over, restore the previous one.
|
| - _currentContext = _currentContext.parent;
|
| + _environment.currentContext = _environment.currentContext.parent;
|
| }
|
| }
|
|
|
| @@ -336,16 +329,16 @@ void skip_group(String description, void body()) {}
|
| void solo_group(String description, void body()) {
|
| _requireNotRunning();
|
| ensureInitialized();
|
| - if (!_soloTestSeen) {
|
| - _soloTestSeen = true;
|
| + if (!_environment.soloTestSeen) {
|
| + _environment.soloTestSeen = true;
|
| // This is the first solo-ed group. Discard all tests up to now.
|
| _testCases.clear();
|
| }
|
| - ++_soloNestingLevel;
|
| + ++_environment.soloNestingLevel;
|
| try {
|
| group(description, body);
|
| } finally {
|
| - --_soloNestingLevel;
|
| + --_environment.soloNestingLevel;
|
| }
|
| }
|
|
|
| @@ -356,7 +349,7 @@ void solo_group(String description, void body()) {
|
| /// case it must return a [Future].
|
| void setUp(Function setupTest) {
|
| _requireNotRunning();
|
| - _currentContext.testSetup = setupTest;
|
| + _environment.currentContext.testSetup = setupTest;
|
| }
|
|
|
| /// Register a [tearDown] function for a test [group]. This function will
|
| @@ -367,12 +360,12 @@ void setUp(Function setupTest) {
|
| /// case it must return a [Future].
|
| void tearDown(Function teardownTest) {
|
| _requireNotRunning();
|
| - _currentContext.testTeardown = teardownTest;
|
| + _environment.currentContext.testTeardown = teardownTest;
|
| }
|
|
|
| /// Advance to the next test case.
|
| void _nextTestCase() {
|
| - _currentTestCaseIndex++;
|
| + _environment.currentTestCaseIndex++;
|
| _runTest();
|
| }
|
|
|
| @@ -385,7 +378,7 @@ void handleExternalError(e, String message, [stack]) {
|
| if (currentTestCase != null) {
|
| currentTestCase._error(msg, stack);
|
| } else {
|
| - _uncaughtErrorMessage = "$msg: $stack";
|
| + _environment.uncaughtErrorMessage = "$msg: $stack";
|
| }
|
| }
|
|
|
| @@ -409,7 +402,7 @@ void filterTests(testFilter) {
|
| void runTests() {
|
| _requireNotRunning();
|
| _ensureInitialized(false);
|
| - _currentTestCaseIndex = 0;
|
| + _environment.currentTestCaseIndex = 0;
|
| _config.onStart();
|
| _runTest();
|
| }
|
| @@ -431,11 +424,11 @@ void _registerException(TestCase testCase, e, [trace]) {
|
|
|
| /// Runs the next test.
|
| void _runTest() {
|
| - if (_currentTestCaseIndex >= testCases.length) {
|
| - assert(_currentTestCaseIndex == testCases.length);
|
| + if (_environment.currentTestCaseIndex >= testCases.length) {
|
| + assert(_environment.currentTestCaseIndex == testCases.length);
|
| _completeTests();
|
| } else {
|
| - var testCase = testCases[_currentTestCaseIndex];
|
| + var testCase = testCases[_environment.currentTestCaseIndex];
|
| Future 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
|
| @@ -459,8 +452,8 @@ void _runTest() {
|
| f.whenComplete(() {
|
| if (timer != null) timer.cancel();
|
| var now = new DateTime.now().millisecondsSinceEpoch;
|
| - if ((now - _lastBreath) >= BREATH_INTERVAL) {
|
| - _lastBreath = now;
|
| + if ((now - _environment.lastBreath) >= BREATH_INTERVAL) {
|
| + _environment.lastBreath = now;
|
| Timer.run(_nextTestCase);
|
| } else {
|
| scheduleMicrotask(_nextTestCase); // Schedule the next test.
|
| @@ -471,7 +464,7 @@ void _runTest() {
|
|
|
| /// Publish results on the page and notify controller.
|
| void _completeTests() {
|
| - if (!_initialized) return;
|
| + if (!_environment.initialized) return;
|
| int passed = 0;
|
| int failed = 0;
|
| int errors = 0;
|
| @@ -483,15 +476,16 @@ void _completeTests() {
|
| case ERROR: errors++; break;
|
| }
|
| }
|
| - _config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage);
|
| + _config.onSummary(passed, failed, errors, testCases,
|
| + _environment.uncaughtErrorMessage);
|
| _config.onDone(passed > 0 && failed == 0 && errors == 0 &&
|
| - _uncaughtErrorMessage == null);
|
| - _initialized = false;
|
| - _currentTestCaseIndex = -1;
|
| + _environment.uncaughtErrorMessage == null);
|
| + _environment.initialized = false;
|
| + _environment.currentTestCaseIndex = -1;
|
| }
|
|
|
| String _fullSpec(String spec) {
|
| - var group = '${_currentContext.fullName}';
|
| + var group = '${_environment.currentContext.fullName}';
|
| if (spec == null) return group;
|
| return group != '' ? '$group$groupSep$spec' : spec;
|
| }
|
| @@ -502,14 +496,14 @@ void ensureInitialized() {
|
| }
|
|
|
| void _ensureInitialized(bool configAutoStart) {
|
| - if (_initialized) {
|
| + if (_environment.initialized) {
|
| return;
|
| }
|
| - _initialized = true;
|
| + _environment.initialized = true;
|
| // Hook our async guard into the matcher library.
|
| wrapAsync = (f, [id]) => expectAsync(f, id: id);
|
|
|
| - _uncaughtErrorMessage = null;
|
| + _environment.uncaughtErrorMessage = null;
|
|
|
| unittestConfiguration.onInit();
|
|
|
| @@ -558,7 +552,14 @@ bool formatStacks = true;
|
| bool filterStacks = true;
|
|
|
| void _requireNotRunning() {
|
| - if (_currentTestCaseIndex != -1) {
|
| + if (_environment.currentTestCaseIndex != -1) {
|
| throw new StateError('Not allowed when tests are running.');
|
| }
|
| }
|
| +
|
| +/// Method to support multiple invocations of the unittest library in the same
|
| +/// application instance.
|
| +dynamic withTestEnvironment(callback()) {
|
| + return runZoned(callback,
|
| + zoneValues: <Symbol, Object>{_TEST_ENVIRONMENT: new TestEnvironment()});
|
| +}
|
|
|