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 |