OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 part of unittest; |
| 6 |
| 7 /// Represents the state for an individual unit test. |
| 8 /// |
| 9 /// Create by calling [test] or [solo_test]. |
| 10 class TestCase { |
| 11 /// Identifier for this test. |
| 12 final int id; |
| 13 |
| 14 /// A description of what the test is specifying. |
| 15 final String description; |
| 16 |
| 17 /// The setup function to call before the test, if any. |
| 18 Function _setUp; |
| 19 |
| 20 /// The teardown function to call after the test, if any. |
| 21 Function _tearDown; |
| 22 |
| 23 /// The body of the test case. |
| 24 TestFunction _testFunction; |
| 25 |
| 26 /// Remaining number of callbacks functions that must reach a 'done' state |
| 27 /// to wait for before the test completes. |
| 28 int _callbackFunctionsOutstanding = 0; |
| 29 |
| 30 String _message = ''; |
| 31 /// Error or failure message. |
| 32 String get message => _message; |
| 33 |
| 34 String _result; |
| 35 /// One of [PASS], [FAIL], [ERROR], or [:null:] if the test hasn't run yet. |
| 36 String get result => _result; |
| 37 |
| 38 /// Returns whether this test case passed. |
| 39 bool get passed => _result == PASS; |
| 40 |
| 41 StackTrace _stackTrace; |
| 42 /// Stack trace associated with this test, or [:null:] if it succeeded. |
| 43 StackTrace get stackTrace => _stackTrace; |
| 44 |
| 45 /// The group (or groups) under which this test is running. |
| 46 final String currentGroup; |
| 47 |
| 48 DateTime _startTime; |
| 49 DateTime get startTime => _startTime; |
| 50 |
| 51 Duration _runningTime; |
| 52 Duration get runningTime => _runningTime; |
| 53 |
| 54 bool _enabled = true; |
| 55 |
| 56 bool get enabled => _enabled; |
| 57 |
| 58 bool _doneTeardown = false; |
| 59 |
| 60 Completer _testComplete; |
| 61 |
| 62 TestCase._internal(this.id, this.description, this._testFunction) |
| 63 : currentGroup = _environment.currentContext.fullName, |
| 64 _setUp = _environment.currentContext.testSetup, |
| 65 _tearDown = _environment.currentContext.testTeardown; |
| 66 |
| 67 bool get isComplete => !enabled || result != null; |
| 68 |
| 69 Function _errorHandler(String stage) => (e, stack) { |
| 70 if (stack == null && e is Error) { |
| 71 stack = e.stackTrace; |
| 72 } |
| 73 if (result == null || result == PASS) { |
| 74 if (e is TestFailure) { |
| 75 _fail("$e", stack); |
| 76 } else { |
| 77 _error("$stage failed: Caught $e", stack); |
| 78 } |
| 79 } |
| 80 }; |
| 81 |
| 82 /// Perform any associated [_setUp] function and run the test. Returns |
| 83 /// a [Future] that can be used to schedule the next test. If the test runs |
| 84 /// to completion synchronously, or is disabled, null is returned, to |
| 85 /// tell unittest to schedule the next test immediately. |
| 86 Future _run() { |
| 87 if (!enabled) return new Future.value(); |
| 88 |
| 89 _result = _stackTrace = null; |
| 90 _message = ''; |
| 91 |
| 92 // Avoid calling [new Future] to avoid issue 11911. |
| 93 return new Future.value().then((_) { |
| 94 if (_setUp != null) return _setUp(); |
| 95 }).catchError(_errorHandler('Setup')).then((_) { |
| 96 // Skip the test if setup failed. |
| 97 if (result != null) return new Future.value(); |
| 98 _config.onTestStart(this); |
| 99 _startTime = new DateTime.now(); |
| 100 _runningTime = null; |
| 101 ++_callbackFunctionsOutstanding; |
| 102 var testReturn = _testFunction(); |
| 103 // If _testFunction() returned a future, we want to wait for it like we |
| 104 // would a callback, so if a failure occurs while waiting, we can abort. |
| 105 if (testReturn is Future) { |
| 106 ++_callbackFunctionsOutstanding; |
| 107 testReturn.catchError(_errorHandler('Test')) |
| 108 .whenComplete(_markCallbackComplete); |
| 109 } |
| 110 }).catchError(_errorHandler('Test')).then((_) { |
| 111 _markCallbackComplete(); |
| 112 if (result == null) { |
| 113 // Outstanding callbacks exist; we need to return a Future. |
| 114 _testComplete = new Completer(); |
| 115 return _testComplete.future.whenComplete(() { |
| 116 if (_tearDown != null) { |
| 117 return _tearDown(); |
| 118 } |
| 119 }).catchError(_errorHandler('Teardown')); |
| 120 } else if (_tearDown != null) { |
| 121 return _tearDown(); |
| 122 } |
| 123 }).catchError(_errorHandler('Teardown')).whenComplete(() { |
| 124 _setUp = null; |
| 125 _tearDown = null; |
| 126 _testFunction = null; |
| 127 }); |
| 128 } |
| 129 |
| 130 // Set the results, notify the config, and return true if this |
| 131 // is the first time the result is being set. |
| 132 void _setResult(String testResult, String messageText, StackTrace stack) { |
| 133 _message = messageText; |
| 134 _stackTrace = getTrace(stack, formatStacks, filterStacks); |
| 135 if (_stackTrace == null) _stackTrace = stack; |
| 136 if (result == null) { |
| 137 _result = testResult; |
| 138 _config.onTestResult(this); |
| 139 } else { |
| 140 _result = testResult; |
| 141 _config.onTestResultChanged(this); |
| 142 } |
| 143 } |
| 144 |
| 145 void _complete(String testResult, [String messageText = '', |
| 146 StackTrace stack]) { |
| 147 if (runningTime == null) { |
| 148 // The startTime can be `null` if an error happened during setup. In this |
| 149 // case we simply report a running time of 0. |
| 150 if (startTime != null) { |
| 151 _runningTime = new DateTime.now().difference(startTime); |
| 152 } else { |
| 153 _runningTime = const Duration(seconds: 0); |
| 154 } |
| 155 } |
| 156 _setResult(testResult, messageText, stack); |
| 157 if (_testComplete != null) { |
| 158 var t = _testComplete; |
| 159 _testComplete = null; |
| 160 t.complete(this); |
| 161 } |
| 162 } |
| 163 |
| 164 void _pass() { |
| 165 _complete(PASS); |
| 166 } |
| 167 |
| 168 void _fail(String messageText, [StackTrace stack]) { |
| 169 if (result != null) { |
| 170 String newMessage = (result == PASS) |
| 171 ? 'Test failed after initially passing: $messageText' |
| 172 : 'Test failed more than once: $messageText'; |
| 173 // TODO(gram): Should we combine the stack with the old one? |
| 174 _complete(ERROR, newMessage, stack); |
| 175 } else { |
| 176 _complete(FAIL, messageText, stack); |
| 177 } |
| 178 } |
| 179 |
| 180 void _error(String messageText, [StackTrace stack]) { |
| 181 _complete(ERROR, messageText, stack); |
| 182 } |
| 183 |
| 184 void _markCallbackComplete() { |
| 185 if (--_callbackFunctionsOutstanding == 0 && !isComplete) { |
| 186 _pass(); |
| 187 } |
| 188 } |
| 189 |
| 190 String toString() => _result != null ? "$description: $result" : description; |
| 191 } |
OLD | NEW |