OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, 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 library unittest.internal_test_case; |
| 6 |
| 7 import 'dart:async'; |
| 8 |
| 9 import '../unittest.dart'; |
| 10 import 'test_environment.dart'; |
| 11 import 'utils.dart'; |
| 12 |
| 13 /// An implementation of [TestCase] that exposes internal properties for other |
| 14 /// unittest use. |
| 15 class InternalTestCase implements TestCase { |
| 16 final int id; |
| 17 final String description; |
| 18 |
| 19 /// The setup function to call before the test, if any. |
| 20 Function _setUp; |
| 21 |
| 22 /// The teardown function to call after the test, if any. |
| 23 Function _tearDown; |
| 24 |
| 25 /// The body of the test case. |
| 26 TestFunction _testFunction; |
| 27 |
| 28 /// Remaining number of callback functions that must reach a 'done' state |
| 29 /// before the test completes. |
| 30 int callbackFunctionsOutstanding = 0; |
| 31 |
| 32 /// The error or failure message for the tests. |
| 33 /// |
| 34 /// Initially an empty string. |
| 35 String message = ''; |
| 36 |
| 37 /// The result of the test case. |
| 38 /// |
| 39 /// If the test case has is completed, this will be one of [PASS], [FAIL], or |
| 40 /// [ERROR]. Otherwise, it will be `null`. |
| 41 String result; |
| 42 |
| 43 /// Returns whether this test case passed. |
| 44 bool get passed => result == PASS; |
| 45 |
| 46 /// The stack trace for the error that caused this test case to fail, or |
| 47 /// `null` if it succeeded. |
| 48 StackTrace stackTrace; |
| 49 |
| 50 /// The name of the group within which this test is running. |
| 51 final String currentGroup; |
| 52 |
| 53 /// The time the test case started running. |
| 54 /// |
| 55 /// `null` if the test hasn't yet begun running. |
| 56 DateTime get startTime => _startTime; |
| 57 DateTime _startTime; |
| 58 |
| 59 /// The amount of time the test case took. |
| 60 /// |
| 61 /// `null` if the test hasn't finished running. |
| 62 Duration get runningTime => _runningTime; |
| 63 Duration _runningTime; |
| 64 |
| 65 /// Whether this test is enabled. |
| 66 /// |
| 67 /// Disabled tests won't be run. |
| 68 bool enabled = true; |
| 69 |
| 70 /// A completer that will complete when the test is finished. |
| 71 /// |
| 72 /// This is only non-`null` when outstanding callbacks exist. |
| 73 Completer _testComplete; |
| 74 |
| 75 /// Whether this test case has finished running. |
| 76 bool get isComplete => !enabled || result != null; |
| 77 |
| 78 InternalTestCase(this.id, this.description, this._testFunction) |
| 79 : currentGroup = environment.currentContext.fullName, |
| 80 _setUp = environment.currentContext.testSetUp, |
| 81 _tearDown = environment.currentContext.testTearDown; |
| 82 |
| 83 /// A function that returns another function to handle errors from [Future]s. |
| 84 /// |
| 85 /// [stage] is a string description of the stage of testing that failed. |
| 86 Function _errorHandler(String stage) => (e, stack) { |
| 87 if (stack == null && e is Error) { |
| 88 stack = e.stackTrace; |
| 89 } |
| 90 if (result == null || result == PASS) { |
| 91 if (e is TestFailure) { |
| 92 fail("$e", stack); |
| 93 } else { |
| 94 error("$stage failed: Caught $e", stack); |
| 95 } |
| 96 } |
| 97 }; |
| 98 |
| 99 /// Performs any associated [_setUp] function and runs the test. |
| 100 /// |
| 101 /// Returns a [Future] that can be used to schedule the next test. If the test |
| 102 /// runs to completion synchronously, or is disabled, null is returned, to |
| 103 /// tell unittest to schedule the next test immediately. |
| 104 Future run() { |
| 105 if (!enabled) return new Future.value(); |
| 106 |
| 107 result = stackTrace = null; |
| 108 message = ''; |
| 109 |
| 110 // Avoid calling [new Future] to avoid issue 11911. |
| 111 return new Future.value().then((_) { |
| 112 if (_setUp != null) return _setUp(); |
| 113 }).catchError(_errorHandler('Setup')).then((_) { |
| 114 // Skip the test if setup failed. |
| 115 if (result != null) return new Future.value(); |
| 116 config.onTestStart(this); |
| 117 _startTime = new DateTime.now(); |
| 118 _runningTime = null; |
| 119 callbackFunctionsOutstanding++; |
| 120 var testReturn = _testFunction(); |
| 121 // If _testFunction() returned a future, we want to wait for it like we |
| 122 // would a callback, so if a failure occurs while waiting, we can abort. |
| 123 if (testReturn is Future) { |
| 124 callbackFunctionsOutstanding++; |
| 125 testReturn |
| 126 .catchError(_errorHandler('Test')) |
| 127 .whenComplete(markCallbackComplete); |
| 128 } |
| 129 }).catchError(_errorHandler('Test')).then((_) { |
| 130 markCallbackComplete(); |
| 131 if (result == null) { |
| 132 // Outstanding callbacks exist; we need to return a Future. |
| 133 _testComplete = new Completer(); |
| 134 return _testComplete.future.whenComplete(() { |
| 135 if (_tearDown != null) { |
| 136 return _tearDown(); |
| 137 } |
| 138 }).catchError(_errorHandler('Teardown')); |
| 139 } else if (_tearDown != null) { |
| 140 return _tearDown(); |
| 141 } |
| 142 }).catchError(_errorHandler('Teardown')).whenComplete(() { |
| 143 _setUp = null; |
| 144 _tearDown = null; |
| 145 _testFunction = null; |
| 146 }); |
| 147 } |
| 148 |
| 149 /// Marks the test as having completed with [testResult], which should be one |
| 150 /// of [PASS], [FAIL], or [ERROR]. |
| 151 void _complete(String testResult, |
| 152 [String messageText = '', StackTrace stack]) { |
| 153 if (runningTime == null) { |
| 154 // The startTime can be `null` if an error happened during setup. In this |
| 155 // case we simply report a running time of 0. |
| 156 if (startTime != null) { |
| 157 _runningTime = new DateTime.now().difference(startTime); |
| 158 } else { |
| 159 _runningTime = const Duration(seconds: 0); |
| 160 } |
| 161 } |
| 162 _setResult(testResult, messageText, stack); |
| 163 if (_testComplete != null) { |
| 164 var t = _testComplete; |
| 165 _testComplete = null; |
| 166 t.complete(this); |
| 167 } |
| 168 } |
| 169 |
| 170 // Sets [this]'s fields to reflect the test result, and notifies the current |
| 171 // configuration that the test has completed. |
| 172 // |
| 173 // Returns true if this is the first time the result has been set. |
| 174 void _setResult(String testResult, String messageText, StackTrace stack) { |
| 175 message = messageText; |
| 176 stackTrace = getTrace(stack, formatStacks, filterStacks); |
| 177 if (stackTrace == null) stackTrace = stack; |
| 178 if (result == null) { |
| 179 result = testResult; |
| 180 config.onTestResult(this); |
| 181 } else { |
| 182 result = testResult; |
| 183 config.onTestResultChanged(this); |
| 184 } |
| 185 } |
| 186 |
| 187 /// Marks the test as having passed. |
| 188 void pass() { |
| 189 _complete(PASS); |
| 190 } |
| 191 |
| 192 void registerException(error, [StackTrace stackTrace]) { |
| 193 var message = error is TestFailure ? error.message : 'Caught $error'; |
| 194 if (result == null) { |
| 195 fail(message, stackTrace); |
| 196 } else { |
| 197 this.error(message, stackTrace); |
| 198 } |
| 199 } |
| 200 |
| 201 /// Marks the test as having failed. |
| 202 void fail(String messageText, [StackTrace stack]) { |
| 203 if (result != null) { |
| 204 var newMessage = result == PASS |
| 205 ? 'Test failed after initially passing: $messageText' |
| 206 : 'Test failed more than once: $messageText'; |
| 207 // TODO(gram): Should we combine the stack with the old one? |
| 208 _complete(ERROR, newMessage, stack); |
| 209 } else { |
| 210 _complete(FAIL, messageText, stack); |
| 211 } |
| 212 } |
| 213 |
| 214 /// Marks the test as having had an unexpected error. |
| 215 void error(String messageText, [StackTrace stack]) { |
| 216 _complete(ERROR, messageText, stack); |
| 217 } |
| 218 |
| 219 /// Indicates that an asynchronous callback has completed, and marks the test |
| 220 /// as passing if all outstanding callbacks are complete. |
| 221 void markCallbackComplete() { |
| 222 callbackFunctionsOutstanding--; |
| 223 if (callbackFunctionsOutstanding == 0 && !isComplete) pass(); |
| 224 } |
| 225 |
| 226 String toString() => result != null ? "$description: $result" : description; |
| 227 } |
OLD | NEW |