Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * The DartWrapTask generates a Dart wrapper for a test file, that has a | 6 * The DartWrapTask generates a Dart wrapper for a test file, that has a |
| 7 * test Configuration customized for the options specified by the user. | 7 * test Configuration customized for the options specified by the user. |
| 8 */ | 8 */ |
| 9 class DartWrapTask extends PipelineTask { | 9 class DartWrapTask extends PipelineTask { |
| 10 final String _sourceFileTemplate; | 10 final String _sourceFileTemplate; |
| 11 final String _tempDartFileTemplate; | 11 final String _tempDartFileTemplate; |
| 12 | 12 |
| 13 DartWrapTask(this._sourceFileTemplate, this._tempDartFileTemplate); | 13 DartWrapTask(this._sourceFileTemplate, this._tempDartFileTemplate); |
| 14 | 14 |
| 15 void execute(Path testfile, List stdout, List stderr, bool logging, | 15 void execute(Path testfile, List stdout, List stderr, bool logging, |
| 16 Function exitHandler) { | 16 Function exitHandler) { |
| 17 // Get the source test file and canonicalize the path. | 17 // Get the source test file and canonicalize the path. |
| 18 var sourceName = makePathAbsolute( | 18 var sourceName = makePathAbsolute( |
| 19 expandMacros(_sourceFileTemplate, testfile)); | 19 expandMacros(_sourceFileTemplate, testfile)); |
| 20 // Get the destination file. | 20 // Get the destination file. |
| 21 var destFile = expandMacros(_tempDartFileTemplate, testfile); | 21 var destFile = expandMacros(_tempDartFileTemplate, testfile); |
| 22 | 22 |
| 23 var p = new Path(sourceName); | 23 var p = new Path(sourceName); |
| 24 if (isLayoutRenderTest(sourceName) || config.generateRenders) { | 24 |
| 25 makeLayoutTestWrapper(sourceName, destFile, p.filenameWithoutExtension); | 25 if (config.layoutText || config.layoutPixel) { |
| 26 exitHandler(0); | 26 // Layout tests are more complicated, as we have a 'meta' |
| 27 return; | 27 // wrapped Dart program that in turn invokes DRT to run the |
|
Siggi Cherem (dart-lang)
2012/09/14 20:01:23
we've talked about this before, but I think we sho
| |
| 28 // normal wrapped Dart program, once for each test. So we generate | |
| 29 // a layout test wrapper file for the real tests and a controller | |
| 30 // wrapper to manage overall execution. | |
| 31 var childFile = '${destFile.substring(0, destFile.length-5)}-child.dart'; | |
|
Siggi Cherem (dart-lang)
2012/09/14 20:01:23
style: please add spaces around operators (destFil
gram
2012/09/19 23:24:38
Done.
| |
| 32 makeLayoutTestWrapper(sourceName, childFile, | |
| 33 p.filenameWithoutExtension); | |
| 34 makeLayoutTestControllerWrapper(sourceName, destFile, childFile, | |
| 35 p.filenameWithoutExtension, | |
| 36 sourceName.substring(0, sourceName.length-5)); // Strip .dart. | |
|
Siggi Cherem (dart-lang)
2012/09/14 20:01:23
ditto
gram
2012/09/19 23:24:38
Done.
| |
| 37 } else { | |
| 38 makeNonLayoutTestWrapper(sourceName, destFile, | |
| 39 p.filenameWithoutExtension); | |
| 28 } | 40 } |
| 41 exitHandler(0); | |
| 42 } | |
| 43 | |
| 44 void makeNonLayoutTestWrapper(String sourceName, String destFile, | |
| 45 String sourceNameWithoutExtension) { | |
| 29 | 46 |
| 30 // Working buffer for the Dart wrapper. | 47 // Working buffer for the Dart wrapper. |
| 31 StringBuffer sbuf = new StringBuffer(); | 48 StringBuffer sbuf = new StringBuffer(); |
| 32 | 49 |
| 33 // Add the common header stuff. | 50 // Add the common header stuff. |
| 34 sbuf.add(directives(p.filenameWithoutExtension, | 51 sbuf.add(directives(sourceNameWithoutExtension, |
| 35 config.unittestPath, | 52 config.unittestPath, |
| 36 sourceName)); | 53 sourceName)); |
| 37 | 54 |
| 38 // Add the test configuration and determine the action function. | 55 // Add the test configuration and determine the action function. |
| 39 var action; | 56 var action; |
| 40 if (config.listTests) { | 57 if (config.listTests) { |
| 41 action = 'listTests'; | 58 action = 'listTests'; |
| 42 sbuf.add(barebonesConfig()); | 59 sbuf.add(barebonesConfig()); |
| 43 sbuf.add(listTestsFunction); | 60 sbuf.add(listTestsFunction); |
| 44 sbuf.add(formatListMessageFunction(config.listFormat)); | 61 sbuf.add(formatListMessageFunction(config.listFormat)); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 91 } else { | 108 } else { |
| 92 sbuf.add(filterTestFunction(config.excludeFilter, 'false')); | 109 sbuf.add(filterTestFunction(config.excludeFilter, 'false')); |
| 93 } | 110 } |
| 94 } | 111 } |
| 95 | 112 |
| 96 // Add the common trailer stuff. | 113 // Add the common trailer stuff. |
| 97 sbuf.add(dartMain(sourceName, action, config.filtering)); | 114 sbuf.add(dartMain(sourceName, action, config.filtering)); |
| 98 | 115 |
| 99 // Save the Dart file. | 116 // Save the Dart file. |
| 100 createFile(destFile, sbuf.toString()); | 117 createFile(destFile, sbuf.toString()); |
| 101 exitHandler(0); | |
| 102 } | 118 } |
| 103 | 119 |
| 104 void cleanup(Path testfile, List stdout, List stderr, | 120 void cleanup(Path testfile, List stdout, List stderr, |
| 105 bool logging, bool keepFiles) { | 121 bool logging, bool keepFiles) { |
| 106 deleteFiles([_tempDartFileTemplate], testfile, logging, keepFiles, stdout); | 122 deleteFiles([_tempDartFileTemplate], testfile, logging, keepFiles, stdout); |
| 107 } | 123 } |
| 108 | 124 |
| 109 void makeLayoutTestWrapper(String sourceName, String destFile, | 125 void makeLayoutTestWrapper(String sourceName, String destFile, |
| 110 String libraryName) { | 126 String libraryName) { |
| 111 StringBuffer sbuf = new StringBuffer(); | 127 StringBuffer sbuf = new StringBuffer(); |
| 112 var cfg = config.unittestPath. | |
| 113 replaceAll('unittest.dart', 'html_layout_config.dart'); | |
| 114 sbuf.add(""" | 128 sbuf.add(""" |
| 115 #library('$libraryName'); | 129 #library('$libraryName'); |
| 116 #import('dart:math'); | 130 #import('dart:math'); |
| 117 #import('dart:isolate'); | 131 #import('dart:isolate'); |
| 118 #import('dart:html'); | 132 #import('dart:html'); |
| 133 #import('dart:uri'); | |
| 119 #import('${config.unittestPath}', prefix:'unittest'); | 134 #import('${config.unittestPath}', prefix:'unittest'); |
| 120 #import('$cfg', prefix:'unittest'); | |
| 121 #import('$sourceName', prefix: 'test'); | 135 #import('$sourceName', prefix: 'test'); |
| 122 """); | 136 """); |
| 137 | |
| 123 // Add the filter, if applicable. | 138 // Add the filter, if applicable. |
| 124 if (config.filtering) { | 139 if (config.filtering) { |
| 125 if (config.includeFilter.length > 0) { | 140 if (config.includeFilter.length > 0) { |
| 126 sbuf.add(filterTestFunction(config.includeFilter, 'true')); | 141 sbuf.add(filterTestFunction(config.includeFilter, 'true')); |
| 127 } else { | 142 } else { |
| 128 sbuf.add(filterTestFunction(config.excludeFilter, 'false')); | 143 sbuf.add(filterTestFunction(config.excludeFilter, 'false')); |
| 129 } | 144 } |
| 130 } | 145 } |
| 131 sbuf.add(""" | 146 sbuf.add(""" |
| 147 | |
| 148 class LayoutTestConfiguration extends unittest.Configuration { | |
| 149 get autoStart => false; | |
| 150 void onTestResult(TestCase testCase) { | |
| 151 window.postMessage('done', '*'); // Unblock DRT | |
| 152 } | |
| 153 } | |
| 154 | |
| 132 main() { | 155 main() { |
| 133 unittest.groupSep = '###'; | 156 unittest.groupSep = '###'; |
| 134 unittest.useHtmlLayoutConfiguration(); | 157 unittest.configure(new LayoutTestConfiguration()); |
| 135 unittest.group('', test.main); | 158 // Create the set of test cases. |
| 136 ${config.filtering ? 'unittest.filterTests(filterTest);' : ''} | 159 unittest.group('', test.main); |
| 137 if (window.location.search == '') unittest.runTests(); | 160 // Do any user-specified test filtering. |
| 161 ${config.filtering ? 'unittest.filterTests(filterTest);' : ''} | |
| 162 // Filter to the test number in the search query. | |
| 163 var testNum = parseInt(window.location.search.substring(6)); | |
| 164 if (testNum < 0 || testNum >= unittest.testCases.length) { | |
| 165 print('#TEST NONEXISTENT'); | |
| 166 } else { | |
| 167 var name = unittest.testCases[testNum].description; | |
| 168 print('#TEST \$name'); | |
| 169 unittest.filterTests(name); | |
| 170 // Run the test. | |
| 171 unittest.runTests(); | |
| 172 } | |
| 138 } | 173 } |
| 139 """); | 174 """); |
| 140 // Save the Dart file. | 175 // Save the Dart file. |
| 141 createFile(destFile, sbuf.toString()); | 176 createFile(destFile, sbuf.toString()); |
| 142 } | 177 } |
| 143 | 178 |
| 179 void makeLayoutTestControllerWrapper(String sourceName, | |
| 180 String destName, String childName, String libraryName, | |
| 181 String expectedDirectory) { | |
| 182 if (config.regenerate) { | |
| 183 var d = new Directory(expectedDirectory); | |
| 184 if (!d.existsSync()) { | |
| 185 d.createSync(); | |
| 186 } | |
| 187 } | |
| 188 StringBuffer sbuf = new StringBuffer(); | |
| 189 var htmlFile = childName.replaceFirst('-child.dart', '.html'); | |
| 190 // Add common prefix. | |
| 191 sbuf.add(""" | |
|
Siggi Cherem (dart-lang)
2012/09/14 20:01:23
could we move a lot of this 'generated code' into
| |
| 192 #library('$libraryName'); | |
| 193 #import('dart:uri'); | |
| 194 #import('dart:io'); | |
| 195 #import('dart:math'); | |
| 196 | |
| 197 var label; | |
| 198 | |
| 199 main() { | |
| 200 runTest(0); | |
| 201 } | |
| 202 | |
| 203 var passCount = 0, failCount = 0, errorCount = 0; | |
| 204 | |
| 205 outputResult(start, label, result, [message = '']) { | |
| 206 var idx = label.lastIndexOf('###'); | |
| 207 var group = '', test = ''; | |
| 208 if (idx >= 0) { | |
| 209 group = '\${label.substring(0, idx).replaceAll("###", " ")} '; | |
| 210 test = '\${label.substring(idx+3)} '; | |
| 211 } else { | |
| 212 test = '\$label '; | |
| 213 } | |
| 214 var elapsed = ''; | |
| 215 if (${config.includeTime}) { | |
| 216 var end = new Date.now(); | |
| 217 double duration = (end - start).inMilliseconds.toDouble(); | |
| 218 duration /= 1000; | |
| 219 elapsed = '\${duration.toStringAsFixed(3)}s '; | |
| 220 } | |
| 221 tprint(formatMessage('$sourceName ', group, test, elapsed, result, message)); | |
| 222 } | |
| 223 | |
| 224 pass(start, label) { | |
| 225 ++passCount; | |
| 226 outputResult(start, label, 'pass'); | |
| 227 } | |
| 228 | |
| 229 fail(start, label, message) { | |
| 230 ++failCount; | |
| 231 outputResult(start, label, 'fail', message); | |
| 232 } | |
| 233 | |
| 234 error(start, label, message) { | |
| 235 ++errorCount; | |
| 236 outputResult(start, label, 'error', message); | |
| 237 } | |
| 238 | |
| 239 complete() { | |
| 240 printSummary('$sourceName', passCount, failCount, errorCount); | |
| 241 exit(failCount > -0 ? -1 : 0); | |
| 242 } | |
| 243 | |
| 244 """); | |
| 245 | |
| 246 sbuf.add(nonBrowserTestPrintFunction); | |
| 247 sbuf.add(formatMessageFunction(config.passFormat, | |
| 248 config.failFormat, | |
| 249 config.errorFormat)); | |
| 250 | |
| 251 sbuf.add(config.produceSummary ? | |
| 252 printSummaryFunction : stubPrintSummaryFunction); | |
| 253 | |
| 254 if (config.layoutText) { | |
| 255 sbuf.add(""" | |
| 256 runTest(testNum) { | |
| 257 var url = 'file://$htmlFile?test=\$testNum'; | |
| 258 var stdout = new List(); | |
| 259 Date start = new Date.now(); | |
| 260 var process = Process.start('${config.drtPath}', [ url ]); | |
| 261 StringInputStream stdoutStringStream = new StringInputStream(process.stdout); | |
| 262 stdoutStringStream.onLine = () { | |
| 263 if (stdoutStringStream.closed) return; | |
| 264 var line = stdoutStringStream.readLine(); | |
| 265 while (null != line) { | |
| 266 stdout.add(line); | |
| 267 line = stdoutStringStream.readLine(); | |
| 268 } | |
| 269 }; | |
| 270 process.onExit = (exitCode) { | |
| 271 process.close(); | |
| 272 if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) { | |
| 273 stdout.removeLast(); | |
| 274 } | |
| 275 var done = false; | |
| 276 var i = 0; | |
| 277 var label = null; | |
| 278 var labelMarker = 'CONSOLE MESSAGE: #TEST '; | |
| 279 var contentMarker = 'layer at '; | |
| 280 while (i < stdout.length) { | |
| 281 if (label == null && stdout[i].startsWith(labelMarker)) { | |
| 282 label = stdout[i].substring(labelMarker.length); | |
| 283 if (label == 'NONEXISTENT') { | |
| 284 complete(); | |
| 285 } | |
| 286 } else if (stdout[i].startsWith(contentMarker)) { | |
| 287 if (label == null) { | |
| 288 complete(); | |
| 289 } | |
| 290 var expectedFileName = | |
| 291 '$expectedDirectory${Platform.pathSeparator}' | |
| 292 '\${label.replaceAll("###", "_") | |
| 293 .replaceAll(const RegExp("[^A-Za-z0-9]"),"_")}.txt'; | |
| 294 var expected = new File(expectedFileName); | |
| 295 if (${config.regenerate}) { | |
| 296 var ostream = expected.openOutputStream(FileMode.WRITE); | |
| 297 while (i < stdout.length) { | |
| 298 ostream.writeString(stdout[i]); | |
| 299 ostream.writeString('\\n'); | |
| 300 i++; | |
| 301 } | |
| 302 ostream.close(); | |
| 303 pass(start, label); | |
| 304 } else { | |
| 305 if (!expected.existsSync()) { | |
| 306 fail(start, label, 'No expectation file'); | |
| 307 } else { | |
| 308 var lines = expected.readAsLinesSync(); | |
| 309 if (lines.length != stdout.length - i) { | |
| 310 fail(start, label, 'Expectation file has wrong length'); | |
| 311 } else { | |
| 312 var match = true; | |
| 313 for (var j = 0; j < lines.length; j++) { | |
| 314 if (lines[j] != stdout[i+j]) { | |
| 315 fail(start, label, 'Expectation differs at line \${j+1}'); | |
| 316 match = false; | |
| 317 break; | |
| 318 } | |
| 319 } | |
| 320 if (match) pass(start, label); | |
| 321 } | |
| 322 } | |
| 323 } | |
| 324 done = true; | |
| 325 break; | |
| 326 } | |
| 327 i++; | |
| 328 } | |
| 329 if (label != null) { | |
| 330 if (!done) error(start, label, 'Failed to parse output'); | |
| 331 runTest(testNum+1); | |
| 332 } | |
| 333 }; | |
| 334 } | |
| 335 """); | |
| 336 } else { | |
| 337 sbuf.add(""" | |
| 338 runTest(testNum) { | |
| 339 var url = 'file://$htmlFile?test=\$testNum'; | |
| 340 var stdout = new List(); | |
| 341 Date start = new Date.now(); | |
| 342 var process = Process.start('${config.drtPath}', [ "\$url'-p" ]); | |
| 343 ListInputStream stdoutStream = process.stdout; | |
| 344 stdoutStream.onData = () { | |
| 345 if (!stdoutStream.closed) { | |
| 346 var data = stdoutStream.read(); | |
| 347 stdout.addAll(data); | |
| 348 } | |
| 349 }; | |
| 350 stdoutStream.onError = (e) { | |
| 351 print(e); | |
| 352 }; | |
| 353 process.onExit = (exitCode) { | |
| 354 stdout.addAll(process.stdout.read()); | |
| 355 process.close(); | |
| 356 var labelMarker = 'CONSOLE MESSAGE: #TEST '; | |
| 357 var contentMarker = 'Content-Length: '; | |
| 358 var eol = '\\n'.charCodeAt(0); | |
| 359 var pos = -1; | |
| 360 var label = null; | |
| 361 var done = false; | |
| 362 | |
| 363 while(pos < stdout.length) { | |
| 364 var idx = stdout.indexOf(eol, ++pos); | |
| 365 if (idx < 0) break; | |
| 366 StringBuffer sb = new StringBuffer(); | |
| 367 for (var i = pos; i < idx; i++) { | |
| 368 sb.addCharCode(stdout[i]); | |
| 369 } | |
| 370 var line = sb.toString(); | |
| 371 | |
| 372 if (label == null && line.startsWith(labelMarker)) { | |
| 373 label = line.substring(labelMarker.length); | |
| 374 if (label == 'NONEXISTENT') { | |
| 375 complete(); | |
| 376 } | |
| 377 } else if (line.startsWith(contentMarker)) { | |
| 378 if (label == null) { | |
| 379 complete(); | |
| 380 } | |
| 381 var len = parseInt(line.substring(contentMarker.length)); | |
| 382 pos = idx+1; | |
| 383 var expectedFileName = | |
| 384 '$expectedDirectory${Platform.pathSeparator}' | |
| 385 '\${label.replaceAll("###","_"). | |
| 386 replaceAll(const RegExp("[^A-Za-z0-9]"),"_")}.png'; | |
| 387 var expected = new File(expectedFileName); | |
| 388 if (${config.regenerate}) { | |
| 389 var ostream = expected.openOutputStream(FileMode.WRITE); | |
| 390 ostream.writeFrom(stdout, pos, len); | |
| 391 ostream.close(); | |
| 392 pass(start, label); | |
| 393 } else { | |
| 394 if (!expected.existsSync()) { | |
| 395 fail(start, label, 'No expectation file'); | |
| 396 } else { | |
| 397 var bytes = expected.readAsBytesSync(); | |
| 398 if (bytes.length != len) { | |
| 399 fail(start, label, 'Expectation file has wrong length'); | |
| 400 } else { | |
| 401 var match = true; | |
| 402 for (var j = 0; j < len; j++) { | |
| 403 if (bytes[j] != stdout[pos+j]) { | |
| 404 fail(start, label, 'Expectation differs at byte \${j+1}'); | |
| 405 match = false; | |
| 406 break; | |
| 407 } | |
| 408 } | |
| 409 if (match) pass(start, label); | |
| 410 } | |
| 411 } | |
| 412 } | |
| 413 done = true; | |
| 414 break; | |
| 415 } | |
| 416 pos = idx; | |
| 417 } | |
| 418 if (label != null) { | |
| 419 if (!done) error(start, label, 'Failed to parse output'); | |
| 420 runTest(testNum+1); | |
| 421 } | |
| 422 }; | |
| 423 } | |
| 424 """); | |
| 425 } | |
| 426 createFile(destName, sbuf.toString()); | |
| 427 } | |
| 428 | |
| 144 String directives(String library, String unittest, String sourceName) { | 429 String directives(String library, String unittest, String sourceName) { |
| 145 return """ | 430 return """ |
| 146 #library('$library'); | 431 #library('$library'); |
| 147 #import('dart:math'); | 432 #import('dart:math'); |
| 148 #import('dart:isolate'); | 433 #import('dart:isolate'); |
| 149 #import('$unittest', prefix:'unittest'); | 434 #import('$unittest', prefix:'unittest'); |
| 150 #import('$sourceName', prefix: 'test'); | 435 #import('$sourceName', prefix: 'test'); |
| 151 """; | 436 """; |
| 152 } | 437 } |
| 153 | 438 |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 251 var message = (t.message.length > 0) ? '\$t.message ' : ''; | 536 var message = (t.message.length > 0) ? '\$t.message ' : ''; |
| 252 var duration = elapsed(t); | 537 var duration = elapsed(t); |
| 253 tprint(formatMessage(source, '\$groupName ', '\$testName ', | 538 tprint(formatMessage(source, '\$groupName ', '\$testName ', |
| 254 duration, t.result, message, stack)); | 539 duration, t.result, message, stack)); |
| 255 } | 540 } |
| 256 """; | 541 """; |
| 257 | 542 |
| 258 // A function to print the test summary. | 543 // A function to print the test summary. |
| 259 final String printSummaryFunction = """ | 544 final String printSummaryFunction = """ |
| 260 void printSummary(String testFile, int passed, int failed, int errors, | 545 void printSummary(String testFile, int passed, int failed, int errors, |
| 261 String uncaughtError) { | 546 [String uncaughtError = '']) { |
| 262 tprint(''); | 547 tprint(''); |
| 263 if (passed == 0 && failed == 0 && errors == 0) { | 548 if (passed == 0 && failed == 0 && errors == 0) { |
| 264 tprint('\$testFile: No tests found.'); | 549 tprint('\$testFile: No tests found.'); |
| 265 } else if (failed == 0 && errors == 0 && uncaughtError == null) { | 550 } else if (failed == 0 && errors == 0 && uncaughtError == null) { |
| 266 tprint('\$testFile: All \$passed tests passed.'); | 551 tprint('\$testFile: All \$passed tests passed.'); |
| 267 } else { | 552 } else { |
| 268 if (uncaughtError != null) { | 553 if (uncaughtError != null) { |
| 269 tprint('\$testFile: Top-level uncaught error: \$uncaughtError'); | 554 tprint('\$testFile: Top-level uncaught error: \$uncaughtError'); |
| 270 } | 555 } |
| 271 tprint('\$testFile: \$passed PASSED, \$failed FAILED, \$errors ERRORS'); | 556 tprint('\$testFile: \$passed PASSED, \$failed FAILED, \$errors ERRORS'); |
| 272 } | 557 } |
| 273 } | 558 } |
| 274 """; | 559 """; |
| 275 | 560 |
| 276 final String stubPrintSummaryFunction = """ | 561 final String stubPrintSummaryFunction = """ |
| 277 void printSummary(String testFile, int passed, int failed, int errors, | 562 void printSummary(String testFile, int passed, int failed, int errors, |
| 278 String uncaughtError) { | 563 [String uncaughtError = '']) { |
| 279 } | 564 } |
| 280 """; | 565 """; |
| 281 | 566 |
| 282 // A function to print all test results. | 567 // A function to print all test results. |
| 283 final String printAllTestResultsFunction = """ | 568 final String printAllTestResultsFunction = """ |
| 284 void printResults(testfile, List<unittest.TestCase> results) { | 569 void printResults(testfile, List<unittest.TestCase> results) { |
| 285 for (final testCase in results) { | 570 for (final testCase in results) { |
| 286 dumpTestResult('\$testfile ', testCase); | 571 dumpTestResult('\$testfile ', testCase); |
| 287 } | 572 } |
| 288 } | 573 } |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 477 """; | 762 """; |
| 478 | 763 |
| 479 // Code for running all tests in the normal (non-isolate) way. | 764 // Code for running all tests in the normal (non-isolate) way. |
| 480 final String runTestsFunction = """ | 765 final String runTestsFunction = """ |
| 481 runTests() { | 766 runTests() { |
| 482 unittest.runTests(); | 767 unittest.runTests(); |
| 483 } | 768 } |
| 484 """; | 769 """; |
| 485 | 770 |
| 486 } | 771 } |
| OLD | NEW |