| 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 |