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 library unittest.simple_configuration; | |
6 | |
7 import 'dart:isolate'; | |
8 | |
9 import 'matcher.dart' | |
10 show DefaultFailureHandler, configureExpectFailureHandler, TestFailure; | |
11 | |
12 import '../unittest.dart'; | |
13 import 'internal_test_case.dart'; | |
14 import 'utils.dart'; | |
15 | |
16 // A custom failure handler for [expect] that routes failures to the | |
17 // [SimpleConfiguration]. | |
18 class _ExpectFailureHandler extends DefaultFailureHandler { | |
19 final SimpleConfiguration _config; | |
20 | |
21 _ExpectFailureHandler(this._config); | |
22 | |
23 void fail(String reason) { | |
24 _config.onExpectFailure(reason); | |
25 } | |
26 } | |
27 | |
28 /// A configuration that provides hooks to configure the unittest library for | |
29 /// different platforms. | |
30 /// | |
31 /// This class implements the [Configuration] API in a platform-independent way. | |
32 /// Tests that want to take advantage of the platform can create a subclass and | |
33 /// override methods from this class. | |
34 class SimpleConfiguration extends Configuration { | |
35 /// A port that keeps the VM alive while we wait for asynchronous tests to | |
36 /// finish. | |
37 /// | |
38 /// The VM won't shut down as long as there's an open receive port. | |
39 ReceivePort _receivePort; | |
40 | |
41 /// The name of the configuration. | |
42 /// | |
43 /// Subclasses can override this with something useful for diagnostics. It's | |
44 /// particularly useful for parent/child configurations such as layout tests. | |
45 final name = 'Configuration'; | |
46 | |
47 /// If true (the default), throw an exception once all tests have run if any f
ailed. | |
48 bool throwOnTestFailures = true; | |
49 | |
50 /// If true (the default), then tests will stop after the first failed | |
51 /// [expect]. | |
52 /// | |
53 /// If false, failed [expect]s will not cause the test to stop. Other | |
54 /// exceptions will still terminate the test. | |
55 bool stopTestOnExpectFailure = true; | |
56 | |
57 // If [stopTestOnExpectFailure] is false, the list of failed [expect]s. | |
58 final _testLogBuffer = <Pair<String, StackTrace>>[]; | |
59 | |
60 /// The constructor sets up a failure handler for [expect] that redirects | |
61 /// [expect] failures to [onExpectFailure]. | |
62 SimpleConfiguration() : super.blank() { | |
63 configureExpectFailureHandler(new _ExpectFailureHandler(this)); | |
64 } | |
65 | |
66 void onInit() { | |
67 // For Dart internal tests, we don't want stack frame filtering. | |
68 // We turn it off here in the default config, but by default turn | |
69 // it back on in the vm and html configs. | |
70 filterStacks = false; | |
71 _receivePort = new ReceivePort(); | |
72 _postMessage('unittest-suite-wait-for-done'); | |
73 } | |
74 | |
75 /// Called when a test starts. | |
76 /// | |
77 /// Derived classes should call this first before their own override code. | |
78 void onTestStart(TestCase testCase) => _testLogBuffer.clear(); | |
79 | |
80 /// Called when a test completes. | |
81 /// | |
82 /// Derived classes should call this first before their own override code. | |
83 void onTestResult(TestCase externalTestCase) { | |
84 if (stopTestOnExpectFailure || _testLogBuffer.isEmpty) return; | |
85 | |
86 var testCase = externalTestCase as InternalTestCase; | |
87 | |
88 // Write the message/stack pairs up to the last pairs. | |
89 var reason = new StringBuffer(); | |
90 for (var reasonAndTrace in _testLogBuffer.take(_testLogBuffer.length - 1)) { | |
91 reason.write(reasonAndTrace.first); | |
92 reason.write('\n'); | |
93 reason.write(reasonAndTrace.last); | |
94 reason.write('\n'); | |
95 } | |
96 | |
97 var lastReasonAndTrace = _testLogBuffer.last; | |
98 // Write the last message. | |
99 reason.write(lastReasonAndTrace.first); | |
100 if (testCase.result == PASS) { | |
101 testCase.result = FAIL; | |
102 testCase.message = reason.toString(); | |
103 // Use the last stack as the overall failure stack. | |
104 testCase.stackTrace = lastReasonAndTrace.last; | |
105 } else { | |
106 // Add the last stack to the message; we have a further stack | |
107 // caused by some other failure. | |
108 reason.write(lastReasonAndTrace.last); | |
109 reason.write('\n'); | |
110 // Add the existing reason to the end of the expect log to | |
111 // create the final message. | |
112 testCase.message = '${reason.toString()}\n${testCase.message}'; | |
113 } | |
114 } | |
115 | |
116 /// Handles the logging of messages by a test case. | |
117 /// | |
118 /// The default in this base configuration is to call [print]. | |
119 void onLogMessage(TestCase testCase, String message) { | |
120 print(message); | |
121 } | |
122 | |
123 /// Handles failures from [expect]. | |
124 /// | |
125 /// If [stopTestOnExpectFailure] is true, this throws a [TestFailure]. | |
126 /// Otherwise, this stores the error. | |
127 void onExpectFailure(String reason) { | |
128 if (stopTestOnExpectFailure) throw new TestFailure(reason); | |
129 | |
130 try { | |
131 throw ''; | |
132 } catch (_, stack) { | |
133 var trace = getTrace(stack, formatStacks, filterStacks); | |
134 if (trace == null) trace = stack; | |
135 _testLogBuffer.add(new Pair<String, StackTrace>(reason, trace)); | |
136 } | |
137 } | |
138 | |
139 /// Returns a formatted string description of a test result. | |
140 String formatResult(TestCase testCase) { | |
141 var result = new StringBuffer(); | |
142 result.write(testCase.result.toUpperCase()); | |
143 result.write(": "); | |
144 result.write(testCase.description); | |
145 result.write("\n"); | |
146 | |
147 if (testCase.message != '') { | |
148 result.write(indent(testCase.message)); | |
149 result.write("\n"); | |
150 } | |
151 | |
152 if (testCase.stackTrace != null) { | |
153 result.write(indent(testCase.stackTrace.toString())); | |
154 result.write("\n"); | |
155 } | |
156 return result.toString(); | |
157 } | |
158 | |
159 /// Called with the result of all test cases. | |
160 /// | |
161 /// The default implementation prints the result summary using [print], | |
162 /// formatted with [formatResult]. Browser tests commonly override this to | |
163 /// reformat the output. | |
164 /// | |
165 /// When [uncaughtError] is not null, it contains an error that occured | |
166 /// outside of tests (e.g. setting up the test). | |
167 void onSummary(int passed, int failed, int errors, List<TestCase> results, | |
168 String uncaughtError) { | |
169 // Print each test's result. | |
170 for (var test in results) { | |
171 print(formatResult(test).trim()); | |
172 } | |
173 | |
174 // Show the summary. | |
175 print(''); | |
176 | |
177 if (passed == 0 && failed == 0 && errors == 0 && uncaughtError == null) { | |
178 print('No tests found.'); | |
179 // This is considered a failure too. | |
180 } else if (failed == 0 && errors == 0 && uncaughtError == null) { | |
181 print('All $passed tests passed.'); | |
182 } else { | |
183 if (uncaughtError != null) { | |
184 print('Top-level uncaught error: $uncaughtError'); | |
185 } | |
186 print('$passed PASSED, $failed FAILED, $errors ERRORS'); | |
187 } | |
188 } | |
189 | |
190 void onDone(bool success) { | |
191 if (success) { | |
192 _postMessage('unittest-suite-success'); | |
193 _receivePort.close(); | |
194 } else { | |
195 _receivePort.close(); | |
196 if (throwOnTestFailures) { | |
197 throw new Exception('Some tests failed.'); | |
198 } | |
199 } | |
200 } | |
201 | |
202 void _postMessage(String message) { | |
203 // In dart2js browser tests, the JavaScript-based test controller | |
204 // intercepts calls to print and listens for "secret" messages. | |
205 print(message); | |
206 } | |
207 } | |
OLD | NEW |