Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011, 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 | |
|
Emily Fortuna
2012/04/20 20:06:19
whoohoo!
| |
| 5 #library("TestFramework"); | |
| 6 #import("dart:coreimpl"); | |
| 7 #import("dart:isolate"); | |
| 8 | |
| 9 | |
| 10 typedef void AsynchronousTestFunction(TestExpectation check); | |
| 11 | |
| 12 | |
| 13 void runTests(List tests, [bool guarded = true]) { | |
| 14 TestRunner runner = new TestRunner(new TestSuite(tests), guarded); | |
| 15 TestResult result = new TestResult(runner); | |
| 16 runner.run(result); | |
| 17 } | |
| 18 | |
| 19 | |
| 20 class TestSuite { | |
| 21 | |
| 22 TestSuite([List tests = const []]) : testCases = <TestCase>[] { | |
| 23 for (var test in tests) { | |
| 24 addTest(test); | |
| 25 } | |
| 26 } | |
| 27 | |
| 28 void addTest(var test) { | |
| 29 if (test is Function) { | |
| 30 addAsynchronousTestCase(test); | |
| 31 } else { | |
| 32 test.addToTestSuite(this); | |
| 33 } | |
| 34 } | |
| 35 | |
| 36 void addTestCase(TestCase test) { | |
| 37 testCases.add(test); | |
| 38 } | |
| 39 | |
| 40 void addAsynchronousTestCase(AsynchronousTestFunction test) { | |
| 41 addTestCase(new AsynchronousTestCase(test)); | |
| 42 } | |
| 43 | |
| 44 void run(TestResult result) { | |
| 45 for (TestCase test in testCases) { | |
| 46 test.run(result); | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 final List<TestCase> testCases; | |
| 51 | |
| 52 } | |
| 53 | |
| 54 | |
| 55 class TestCase { | |
| 56 | |
| 57 TestCase(); | |
| 58 | |
| 59 void setUp() { } | |
| 60 abstract void performTest(); | |
| 61 void tearDown() { } | |
| 62 | |
| 63 void run(TestResult result) { | |
| 64 setUp(); | |
| 65 result.runGuarded(this, () { | |
| 66 performTest(); | |
| 67 tearDown(); | |
| 68 }); | |
| 69 } | |
| 70 | |
| 71 void addToTestSuite(TestSuite suite) { | |
| 72 suite.addTestCase(this); | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 | |
| 77 class TestResult { | |
| 78 | |
| 79 TestResult(this.runner) : errors = [], failures = []; | |
| 80 | |
| 81 void error(String message, TestCase testCase) { | |
| 82 errors.add([message, testCase]); | |
| 83 } | |
| 84 | |
| 85 void failure(String message, TestCase testCase) { | |
| 86 failures.add([message, testCase]); | |
| 87 } | |
| 88 | |
| 89 runGuarded(TestCase testCase, Function fn) { | |
| 90 if (!runner.guarded) { | |
| 91 return fn(); | |
| 92 } | |
| 93 var result = null; | |
| 94 try { | |
| 95 result = fn(); | |
| 96 } catch (ExpectException exception) { | |
| 97 failure(exception.toString(), testCase); | |
| 98 testCase.tearDown(); | |
| 99 } catch (var exception) { | |
| 100 error(exception.toString(), testCase); | |
| 101 testCase.tearDown(); | |
| 102 } | |
| 103 return result; | |
| 104 } | |
| 105 | |
| 106 bool hasDefects() { | |
| 107 return !(errors.isEmpty() && failures.isEmpty()); | |
| 108 } | |
| 109 | |
| 110 final TestRunner runner; | |
| 111 final List errors; | |
| 112 final List failures; | |
| 113 | |
| 114 } | |
| 115 | |
| 116 | |
| 117 class TestRunner { | |
| 118 | |
| 119 TestRunner(this.suite, this.guarded); | |
| 120 | |
| 121 void run(TestResult result) { | |
| 122 if (waitForDoneCallback !== null) { | |
| 123 waitForDoneCallback(); | |
| 124 } | |
| 125 suite.run(result); | |
| 126 if (AsynchronousTestCase.running == 0) { | |
| 127 done(result); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 void done(TestResult result) { | |
| 132 if (result.hasDefects()) { | |
| 133 printDefects(result); | |
| 134 Expect.fail("Test suite failed."); | |
| 135 } | |
| 136 if (doneCallback !== null) { | |
| 137 doneCallback(); | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 void printDefects(TestResult result) { | |
| 142 printDefectList("Errors", result.errors); | |
| 143 printDefectList("Failures", result.failures); | |
| 144 } | |
| 145 | |
| 146 static void printDefectList(String type, List defects) { | |
| 147 if (!defects.isEmpty()) { | |
| 148 print("$type #${defects.length}:"); | |
| 149 for (List defect in defects) { | |
| 150 print(" - ${defect[0]}"); | |
| 151 } | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 final TestSuite suite; | |
| 156 final bool guarded; | |
| 157 static Function waitForDoneCallback; | |
| 158 static Function doneCallback; | |
| 159 | |
| 160 } | |
| 161 | |
| 162 | |
| 163 class TestExpectation { | |
| 164 | |
| 165 TestExpectation(this.testCase, this.result); | |
| 166 | |
| 167 void succeeded() { | |
| 168 Expect.equals(0, pendingCallbacks); | |
| 169 hasSucceeded = true; | |
| 170 testCase.tearDown(); | |
| 171 } | |
| 172 | |
| 173 void failed() { | |
| 174 testCase.tearDown(); | |
| 175 } | |
| 176 | |
| 177 Future completes(Future future) { | |
| 178 Completer completer = new Completer(); | |
| 179 future.then(runs1((value) { | |
| 180 completer.complete(value); | |
| 181 })); | |
| 182 return completer.future; | |
| 183 } | |
| 184 | |
| 185 Function runs0(Function fn) { | |
| 186 bool ran = false; // We only check that the function is executed once. | |
| 187 pendingCallbacks++; | |
| 188 return () { | |
| 189 if (!ran) pendingCallbacks--; | |
| 190 ran = true; | |
| 191 return result.runGuarded(testCase, () => fn()); | |
| 192 }; | |
| 193 } | |
| 194 | |
| 195 Function runs1(Function fn) { | |
| 196 bool ran = false; // We only check that the function is executed once. | |
| 197 pendingCallbacks++; | |
| 198 return (a0) { | |
| 199 if (!ran) pendingCallbacks--; | |
| 200 ran = true; | |
| 201 return result.runGuarded(testCase, () => fn(a0)); | |
| 202 }; | |
| 203 } | |
| 204 | |
| 205 Function runs2(Function fn) { | |
| 206 bool ran = false; // We only check that the function is executed once. | |
| 207 pendingCallbacks++; | |
| 208 return (a0, a1) { | |
| 209 if (!ran) pendingCallbacks--; | |
| 210 ran = true; | |
| 211 return result.runGuarded(testCase, () => fn(a0, a1)); | |
| 212 }; | |
| 213 } | |
| 214 | |
| 215 bool hasPendingCallbacks() { | |
| 216 return pendingCallbacks > 0; | |
| 217 } | |
| 218 | |
| 219 final AsynchronousTestCase testCase; | |
| 220 final TestResult result; | |
| 221 | |
| 222 int pendingCallbacks = 0; | |
| 223 bool hasSucceeded = false; | |
| 224 | |
| 225 } | |
| 226 | |
| 227 | |
| 228 class AsynchronousTestCase extends TestCase { | |
| 229 | |
| 230 AsynchronousTestCase(this.test) : super(); | |
| 231 | |
| 232 void run(TestResult result) { | |
| 233 setUp(); | |
| 234 result.runGuarded(this, () { | |
| 235 addRunning(result); | |
| 236 TestExpectation expect = new TestExpectation(this, result); | |
| 237 test(expect); | |
| 238 if (!expect.hasPendingCallbacks()) { | |
| 239 Expect.isTrue(expect.hasSucceeded); | |
| 240 tearDown(); | |
| 241 } | |
| 242 }); | |
| 243 } | |
| 244 | |
| 245 void tearDown() { | |
| 246 removeRunning(); | |
| 247 } | |
| 248 | |
| 249 void addRunning(TestResult result) { | |
| 250 if (running++ == 0) { | |
| 251 final port = new ReceivePort(); | |
| 252 port.receive((message, replyTo) { | |
| 253 port.close(); | |
| 254 result.runner.done(result); | |
| 255 }); | |
| 256 keepalive = port; | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 void removeRunning() { | |
| 261 if (--running == 0) { | |
| 262 keepalive.toSendPort().send(null, null); | |
| 263 keepalive = null; | |
| 264 } | |
| 265 } | |
| 266 | |
| 267 // AsynchronousTestCase.run() calls variable test, not function performTest. | |
| 268 void performTest() { | |
| 269 Expect.fail('performTest called in AsynchronousTestCase'); | |
| 270 } | |
| 271 | |
| 272 AsynchronousTestFunction test; | |
| 273 | |
| 274 static int running = 0; | |
| 275 static ReceivePort keepalive = null; | |
| 276 | |
| 277 } | |
| OLD | NEW |