| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, 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 test_controller; | |
| 6 | |
| 7 /** Path to DRT executable. */ | |
| 8 String drt; | |
| 9 | |
| 10 /** Whether to include elapsed time. */ | |
| 11 bool includeTime; | |
| 12 | |
| 13 /** Whether to regenerate layout test files. */ | |
| 14 bool regenerate; | |
| 15 | |
| 16 /** Whether to output test summary. */ | |
| 17 bool summarize; | |
| 18 | |
| 19 /** Whether to print results immediately as they come in. */ | |
| 20 bool immediate; | |
| 21 | |
| 22 /** Format strings to use for test result messages. */ | |
| 23 String listFormat, passFormat, failFormat, errorFormat; | |
| 24 | |
| 25 /** Path of the running test file. */ | |
| 26 String testfile; | |
| 27 | |
| 28 /** The filters must be set by the caller. */ | |
| 29 List includeFilters; | |
| 30 List excludeFilters; | |
| 31 | |
| 32 /** The print function to use. */ | |
| 33 Function tprint; | |
| 34 | |
| 35 /** A callback function to notify the caller we are done. */ | |
| 36 Function notifyDone; | |
| 37 | |
| 38 /** The action function to use. */ | |
| 39 Function action; | |
| 40 | |
| 41 /** | |
| 42 * A special marker string used to separate group names and | |
| 43 * identify non-debug output. | |
| 44 */ | |
| 45 final marker = '###'; | |
| 46 | |
| 47 class Macros { | |
| 48 static const String testTime = '<TIME>'; | |
| 49 static const String testfile = '<FILENAME>'; | |
| 50 static const String testGroup = '<GROUPNAME>'; | |
| 51 static const String testDescription = '<TESTNAME>'; | |
| 52 static const String testMessage = '<MESSAGE>'; | |
| 53 static const String testStacktrace = '<STACK>'; | |
| 54 } | |
| 55 | |
| 56 class TestRunnerConfiguration extends SimpleConfiguration { | |
| 57 get name => 'Minimal test runner configuration'; | |
| 58 get autoStart => false; | |
| 59 | |
| 60 void onInit() {} | |
| 61 | |
| 62 String formatMessage(filename, groupname, | |
| 63 [ testname = '', testTime = '', result = '', | |
| 64 message = '', stack = '' ]) { | |
| 65 var format = errorFormat; | |
| 66 if (result == 'pass') format = passFormat; | |
| 67 else if (result == 'fail') format = failFormat; | |
| 68 return format. | |
| 69 replaceAll(Macros.testTime, testTime). | |
| 70 replaceAll(Macros.testfile, filename). | |
| 71 replaceAll(Macros.testGroup, groupname). | |
| 72 replaceAll(Macros.testDescription, testname). | |
| 73 replaceAll(Macros.testMessage, message). | |
| 74 replaceAll(Macros.testStacktrace, stack); | |
| 75 } | |
| 76 | |
| 77 String elapsed(TestCase t) { | |
| 78 if (includeTime) { | |
| 79 double duration = t.runningTime.inMilliseconds.toDouble(); | |
| 80 duration /= 1000; | |
| 81 return '${duration.toStringAsFixed(3)}s '; | |
| 82 } else { | |
| 83 return ''; | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 void dumpTestResult(source, TestCase t) { | |
| 88 var groupName = '', testName = ''; | |
| 89 var idx = t.description.lastIndexOf(marker); | |
| 90 if (idx >= 0) { | |
| 91 groupName = t.description.substring(0, idx).replaceAll(marker, ' '); | |
| 92 testName = t.description.substring(idx+3); | |
| 93 } else { | |
| 94 testName = t.description; | |
| 95 } | |
| 96 var stack = (t.stackTrace == null) ? '' : '${t.stackTrace} '; | |
| 97 var message = (t.message.length > 0) ? '${t.message} ' : ''; | |
| 98 var duration = elapsed(t); | |
| 99 tprint(formatMessage(source, '$groupName ', '$testName ', | |
| 100 duration, t.result, message, stack)); | |
| 101 } | |
| 102 | |
| 103 void onTestResult(TestCase testCase) { | |
| 104 if (immediate) { | |
| 105 dumpTestResult('$testfile ', testCase); | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 void printSummary(int passed, int failed, int errors, | |
| 110 [String uncaughtError = '']) { | |
| 111 tprint(''); | |
| 112 if (passed == 0 && failed == 0 && errors == 0) { | |
| 113 tprint('$testfile: No tests found.'); | |
| 114 } else if (failed == 0 && errors == 0 && uncaughtError == null) { | |
| 115 tprint('$testfile: All $passed tests passed.'); | |
| 116 } else { | |
| 117 if (uncaughtError != null) { | |
| 118 tprint('$testfile: Top-level uncaught error: $uncaughtError'); | |
| 119 } | |
| 120 tprint('$testfile: $passed PASSED, $failed FAILED, $errors ERRORS'); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 void onSummary(int passed, int failed, int errors, | |
| 125 List<TestCase> results, String uncaughtError) { | |
| 126 if (!immediate) { | |
| 127 for (final testCase in results) { | |
| 128 dumpTestResult('$testfile ', testCase); | |
| 129 } | |
| 130 } | |
| 131 if (summarize) { | |
| 132 printSummary(passed, failed, errors, uncaughtError); | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 void onDone(bool success) { | |
| 137 if (notifyDone != null) { | |
| 138 notifyDone(success ? 0 : -1); | |
| 139 } | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 String formatListMessage(filename, groupname, [ testname = '']) { | |
| 144 return listFormat. | |
| 145 replaceAll(Macros.testfile, filename). | |
| 146 replaceAll(Macros.testGroup, groupname). | |
| 147 replaceAll(Macros.testDescription, testname); | |
| 148 } | |
| 149 | |
| 150 listGroups() { | |
| 151 List tests = testCases; | |
| 152 Map groups = {}; | |
| 153 for (var t in tests) { | |
| 154 var groupName, testName = ''; | |
| 155 var idx = t.description.lastIndexOf(marker); | |
| 156 if (idx >= 0) { | |
| 157 groupName = t.description.substring(0, idx).replaceAll(marker, ' '); | |
| 158 if (!groups.containsKey(groupName)) { | |
| 159 groups[groupName] = ''; | |
| 160 } | |
| 161 } | |
| 162 } | |
| 163 for (var g in groups.keys) { | |
| 164 var msg = formatListMessage('$testfile ', '$g '); | |
| 165 print('$marker$msg'); | |
| 166 } | |
| 167 if (notifyDone != null) { | |
| 168 notifyDone(0); | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 listTests() { | |
| 173 List tests = testCases; | |
| 174 for (var t in tests) { | |
| 175 var groupName, testName = ''; | |
| 176 var idx = t.description.lastIndexOf(marker); | |
| 177 if (idx >= 0) { | |
| 178 groupName = t.description.substring(0, idx).replaceAll(marker, ' '); | |
| 179 testName = t.description.substring(idx+3); | |
| 180 } else { | |
| 181 groupName = ''; | |
| 182 testName = t.description; | |
| 183 } | |
| 184 var msg = formatListMessage('$testfile ', '$groupName ', '$testName '); | |
| 185 print('$marker$msg'); | |
| 186 } | |
| 187 if (notifyDone != null) { | |
| 188 notifyDone(0); | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 // Support for running in isolates. | |
| 193 | |
| 194 class TestRunnerChildConfiguration extends SimpleConfiguration { | |
| 195 get name => 'Test runner child configuration'; | |
| 196 get autoStart => false; | |
| 197 | |
| 198 void onSummary(int passed, int failed, int errors, | |
| 199 List<TestCase> results, String uncaughtError) { | |
| 200 TestCase test = results[0]; | |
| 201 parentPort.send([test.result, test.runningTime.inMilliseconds, | |
| 202 test.message, test.stackTrace.toString()]); | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 var parentPort; | |
| 207 runChildTest(message) { | |
| 208 var testName = message[0]; | |
| 209 parentPort = message[1]; | |
| 210 unittestConfiguration = new TestRunnerChildConfiguration(); | |
| 211 groupSep = marker; | |
| 212 group('', test.main); | |
| 213 filterTests(testName); | |
| 214 runTests(); | |
| 215 } | |
| 216 | |
| 217 isolatedTestParentWrapper(testCase) => () { | |
| 218 ReceivePort response = new ReceivePort(); | |
| 219 return Isolate.spawn(runChildTest, [testCase.description, response.sendPort]) | |
| 220 .then((_) => response.first) | |
| 221 .then((results) { | |
| 222 var result = results[0]; | |
| 223 var duration = new Duration(milliseconds: results[1]); | |
| 224 var message = results[2]; | |
| 225 var stack = results[3]; | |
| 226 if (result == 'fail') { | |
| 227 testCase.fail(message, stack); | |
| 228 } else if (result == 'error') { | |
| 229 testCase.error(message, stack); | |
| 230 } | |
| 231 }); | |
| 232 }; | |
| 233 | |
| 234 runIsolateTests() { | |
| 235 // Replace each test with a wrapped version first. | |
| 236 for (var i = 0; i < testCases.length; i++) { | |
| 237 testCases[i].testFunction = isolatedTestParentWrapper(testCases[i]); | |
| 238 } | |
| 239 runTests(); | |
| 240 } | |
| 241 | |
| 242 // Main | |
| 243 | |
| 244 filterTest(t) { | |
| 245 var name = t.description.replaceAll(marker, " "); | |
| 246 if (includeFilters.length > 0) { | |
| 247 for (var f in includeFilters) { | |
| 248 if (name.indexOf(f) >= 0) return true; | |
| 249 } | |
| 250 return false; | |
| 251 } else if (excludeFilters.length > 0) { | |
| 252 for (var f in excludeFilters) { | |
| 253 if (name.indexOf(f) >= 0) return false; | |
| 254 } | |
| 255 return true; | |
| 256 } else { | |
| 257 return true; | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 process(testMain, action) { | |
| 262 groupSep = marker; | |
| 263 unittestConfiguration = new TestRunnerConfiguration(); | |
| 264 group('', testMain); | |
| 265 // Do any user-specified test filtering. | |
| 266 filterTests(filterTest); | |
| 267 action(); | |
| 268 } | |
| OLD | NEW |