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 * Classes and methods for enumerating and preparing tests. | 6 * Classes and methods for enumerating and preparing tests. |
| 7 * | 7 * |
| 8 * This library includes: | 8 * This library includes: |
| 9 * | 9 * |
| 10 * - Creating tests by listing all the Dart files in certain directories, | 10 * - Creating tests by listing all the Dart files in certain directories, |
| 11 * and creating [TestCase]s for those files that meet the relevant criteria. | 11 * and creating [TestCase]s for those files that meet the relevant criteria. |
| 12 * - Preparing tests, including copying files and frameworks to temporary | 12 * - Preparing tests, including copying files and frameworks to temporary |
| 13 * directories, and computing the command line and arguments to be run. | 13 * directories, and computing the command line and arguments to be run. |
| 14 */ | 14 */ |
| 15 library test_suite; | 15 library test_suite; |
| 16 | 16 |
| 17 import "dart:async"; | |
| 17 import "dart:io"; | 18 import "dart:io"; |
| 18 import "dart:isolate"; | 19 import "dart:isolate"; |
| 19 import "status_file_parser.dart"; | 20 import "status_file_parser.dart"; |
| 20 import "test_runner.dart"; | 21 import "test_runner.dart"; |
| 21 import "multitest.dart"; | 22 import "multitest.dart"; |
| 22 import "drt_updater.dart"; | 23 import "drt_updater.dart"; |
| 23 import "dart:uri"; | 24 import "dart:uri"; |
| 24 | 25 |
| 25 part "browser_test.dart"; | 26 part "browser_test.dart"; |
| 26 | 27 |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 57 } | 58 } |
| 58 | 59 |
| 59 /** A completer that waits until all added [Future]s complete. */ | 60 /** A completer that waits until all added [Future]s complete. */ |
| 60 // TODO(rnystrom): Copied from web_components. Remove from here when it gets | 61 // TODO(rnystrom): Copied from web_components. Remove from here when it gets |
| 61 // added to dart:core. (See #6626.) | 62 // added to dart:core. (See #6626.) |
| 62 class FutureGroup { | 63 class FutureGroup { |
| 63 const _FINISHED = -1; | 64 const _FINISHED = -1; |
| 64 int _pending = 0; | 65 int _pending = 0; |
| 65 Completer<List> _completer = new Completer<List>(); | 66 Completer<List> _completer = new Completer<List>(); |
| 66 final List<Future> futures = <Future>[]; | 67 final List<Future> futures = <Future>[]; |
| 68 bool wasCompleted = false; | |
| 67 | 69 |
| 68 /** | 70 /** |
| 69 * Wait for [task] to complete (assuming this barrier has not already been | 71 * Wait for [task] to complete (assuming this barrier has not already been |
| 70 * marked as completed, otherwise you'll get an exception indicating that a | 72 * marked as completed, otherwise you'll get an exception indicating that a |
| 71 * future has already been completed). | 73 * future has already been completed). |
| 72 */ | 74 */ |
| 73 void add(Future task) { | 75 void add(Future task) { |
| 74 if (_pending == _FINISHED) { | 76 if (_pending == _FINISHED) { |
| 75 throw new FutureAlreadyCompleteException(); | 77 throw new Exception("FutureFutureAlreadyCompleteException"); |
| 76 } | 78 } |
| 77 _pending++; | 79 _pending++; |
| 78 futures.add(task); | 80 var handledTaskFuture = task.catchError((e) { |
|
Bill Hesse
2013/01/09 18:21:06
Couldn't you just put parentheses around all this,
kustermann
2013/01/09 18:27:40
I think it is a bit more readable this way.
| |
| 79 task.handleException( | 81 if (!wasCompleted) { |
| 80 (e) => _completer.completeException(e, task.stackTrace)); | 82 _completer.completeError(e.error, task.stackTrace); |
| 81 task.then((_) { | 83 wasCompleted = true; |
| 84 } | |
| 85 }).then((_) { | |
| 82 _pending--; | 86 _pending--; |
| 83 if (_pending == 0) { | 87 if (_pending == 0) { |
| 84 _pending = _FINISHED; | 88 _pending = _FINISHED; |
| 85 _completer.complete(futures); | 89 if (!wasCompleted) { |
| 90 _completer.complete(futures); | |
| 91 wasCompleted = true; | |
| 92 } | |
| 86 } | 93 } |
| 87 }); | 94 }); |
| 95 futures.add(handledTaskFuture); | |
| 88 } | 96 } |
| 89 | 97 |
| 90 Future<List> get future => _completer.future; | 98 Future<List> get future => _completer.future; |
| 91 } | 99 } |
| 92 | 100 |
| 93 /** | 101 /** |
| 94 * A TestSuite represents a collection of tests. It creates a [TestCase] | 102 * A TestSuite represents a collection of tests. It creates a [TestCase] |
| 95 * object for each test to be run, and passes the test cases to a callback. | 103 * object for each test to be run, and passes the test cases to a callback. |
| 96 * | 104 * |
| 97 * Most TestSuites represent a directory or directory tree containing tests, | 105 * Most TestSuites represent a directory or directory tree containing tests, |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 265 p.onExit = (code) { | 273 p.onExit = (code) { |
| 266 if (code < 0) { | 274 if (code < 0) { |
| 267 print("Failed to list tests: $runnerPath --list"); | 275 print("Failed to list tests: $runnerPath --list"); |
| 268 replyTo.send(""); | 276 replyTo.send(""); |
| 269 } else { | 277 } else { |
| 270 processExited = true; | 278 processExited = true; |
| 271 checkDone(); | 279 checkDone(); |
| 272 } | 280 } |
| 273 }; | 281 }; |
| 274 port.close(); | 282 port.close(); |
| 275 }); | 283 }).catchError((e) { |
| 276 processFuture.handleException((e) { | |
| 277 print("Failed to list tests: $runnerPath --list"); | 284 print("Failed to list tests: $runnerPath --list"); |
| 278 replyTo.send(""); | 285 replyTo.send(""); |
| 279 return true; | 286 return true; |
| 280 }); | 287 }); |
| 281 }); | 288 }); |
| 282 } | 289 } |
| 283 | 290 |
| 284 | 291 |
| 285 /** | 292 /** |
| 286 * A specialized [TestSuite] that runs tests written in C to unit test | 293 * A specialized [TestSuite] that runs tests written in C to unit test |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 467 bool isTestFile(String filename) { | 474 bool isTestFile(String filename) { |
| 468 // Use the specified predicate, if provided. | 475 // Use the specified predicate, if provided. |
| 469 if (isTestFilePredicate != null) return isTestFilePredicate(filename); | 476 if (isTestFilePredicate != null) return isTestFilePredicate(filename); |
| 470 | 477 |
| 471 return filename.endsWith("Test.dart"); | 478 return filename.endsWith("Test.dart"); |
| 472 } | 479 } |
| 473 | 480 |
| 474 List<String> additionalOptions(Path filePath) => []; | 481 List<String> additionalOptions(Path filePath) => []; |
| 475 | 482 |
| 476 void forEachTest(TestCaseEvent onTest, Map testCache, [VoidFunction onDone]) { | 483 void forEachTest(TestCaseEvent onTest, Map testCache, [VoidFunction onDone]) { |
| 477 updateDartium().chain((_) { | 484 updateDartium().then((_) { |
| 478 doTest = onTest; | 485 doTest = onTest; |
| 479 | 486 |
| 480 return readExpectations(); | 487 return readExpectations(); |
| 481 }).chain((expectations) { | 488 }).then((expectations) { |
| 482 testExpectations = expectations; | 489 testExpectations = expectations; |
| 483 | 490 |
| 484 // Checked if we have already found and generated the tests for | 491 // Checked if we have already found and generated the tests for |
| 485 // this suite. | 492 // this suite. |
| 486 if (!testCache.containsKey(suiteName)) { | 493 if (!testCache.containsKey(suiteName)) { |
| 487 cachedTests = testCache[suiteName] = []; | 494 cachedTests = testCache[suiteName] = []; |
| 488 return enqueueTests(); | 495 return enqueueTests(); |
| 489 } else { | 496 } else { |
| 490 // We rely on enqueueing completing asynchronously. | 497 // We rely on enqueueing completing asynchronously. |
| 491 return asynchronously(() { | 498 return asynchronously(() { |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 545 ReadTestExpectationsInto(expectations, | 552 ReadTestExpectationsInto(expectations, |
| 546 dartDir.append(statusFilePath).toNativePath(), | 553 dartDir.append(statusFilePath).toNativePath(), |
| 547 configuration, statusFileRead); | 554 configuration, statusFileRead); |
| 548 } | 555 } |
| 549 | 556 |
| 550 return completer.future; | 557 return completer.future; |
| 551 } | 558 } |
| 552 | 559 |
| 553 Future enqueueTests() { | 560 Future enqueueTests() { |
| 554 Directory dir = new Directory.fromPath(suiteDir); | 561 Directory dir = new Directory.fromPath(suiteDir); |
| 555 return dir.exists().chain((exists) { | 562 return dir.exists().then((exists) { |
| 556 if (!exists) { | 563 if (!exists) { |
| 557 print('Directory containing tests missing: ${suiteDir.toNativePath()}'); | 564 print('Directory containing tests missing: ${suiteDir.toNativePath()}'); |
| 558 return new Future.immediate(null); | 565 return new Future.immediate(null); |
| 559 } else { | 566 } else { |
| 560 var group = new FutureGroup(); | 567 var group = new FutureGroup(); |
| 561 enqueueDirectory(dir, group); | 568 enqueueDirectory(dir, group); |
| 562 return group.future; | 569 return group.future; |
| 563 } | 570 } |
| 564 }); | 571 }); |
| 565 } | 572 } |
| (...skipping 747 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1313 // Find the options in the file. | 1320 // Find the options in the file. |
| 1314 List<List> result = new List<List>(); | 1321 List<List> result = new List<List>(); |
| 1315 List<String> dartOptions; | 1322 List<String> dartOptions; |
| 1316 String packageRoot; | 1323 String packageRoot; |
| 1317 bool hasCompileError = contents.contains("@compile-error"); | 1324 bool hasCompileError = contents.contains("@compile-error"); |
| 1318 bool hasRuntimeError = contents.contains("@runtime-error"); | 1325 bool hasRuntimeError = contents.contains("@runtime-error"); |
| 1319 bool isStaticClean = false; | 1326 bool isStaticClean = false; |
| 1320 | 1327 |
| 1321 Iterable<Match> matches = testOptionsRegExp.allMatches(contents); | 1328 Iterable<Match> matches = testOptionsRegExp.allMatches(contents); |
| 1322 for (var match in matches) { | 1329 for (var match in matches) { |
| 1323 result.add(match[1].split(' ').filter((e) => e != '')); | 1330 result.add(match[1].split(' ').where((e) => e != '').toList()); |
| 1324 } | 1331 } |
| 1325 if (result.isEmpty) result.add([]); | 1332 if (result.isEmpty) result.add([]); |
| 1326 | 1333 |
| 1327 matches = dartOptionsRegExp.allMatches(contents); | 1334 matches = dartOptionsRegExp.allMatches(contents); |
| 1328 for (var match in matches) { | 1335 for (var match in matches) { |
| 1329 if (dartOptions != null) { | 1336 if (dartOptions != null) { |
| 1330 throw new Exception( | 1337 throw new Exception( |
| 1331 'More than one "// DartOptions=" line in test $filePath'); | 1338 'More than one "// DartOptions=" line in test $filePath'); |
| 1332 } | 1339 } |
| 1333 dartOptions = match[1].split(' ').filter((e) => e != ''); | 1340 dartOptions = match[1].split(' ').where((e) => e != '').toList(); |
| 1334 } | 1341 } |
| 1335 | 1342 |
| 1336 matches = packageRootRegExp.allMatches(contents); | 1343 matches = packageRootRegExp.allMatches(contents); |
| 1337 for (var match in matches) { | 1344 for (var match in matches) { |
| 1338 if (packageRoot != null) { | 1345 if (packageRoot != null) { |
| 1339 throw new Exception( | 1346 throw new Exception( |
| 1340 'More than one "// PackageRoot=" line in test $filePath'); | 1347 'More than one "// PackageRoot=" line in test $filePath'); |
| 1341 } | 1348 } |
| 1342 packageRoot = match[1]; | 1349 packageRoot = match[1]; |
| 1343 if (packageRoot != 'none') { | 1350 if (packageRoot != 'none') { |
| 1344 // PackageRoot=none means that no package-root option should be given. | 1351 // PackageRoot=none means that no package-root option should be given. |
| 1345 packageRoot = '${filePath.directoryPath.join(new Path(packageRoot))}'; | 1352 packageRoot = '${filePath.directoryPath.join(new Path(packageRoot))}'; |
| 1346 } | 1353 } |
| 1347 } | 1354 } |
| 1348 | 1355 |
| 1349 matches = staticCleanRegExp.allMatches(contents); | 1356 matches = staticCleanRegExp.allMatches(contents); |
| 1350 for (var match in matches) { | 1357 for (var match in matches) { |
| 1351 if (isStaticClean) { | 1358 if (isStaticClean) { |
| 1352 throw new Exception( | 1359 throw new Exception( |
| 1353 'More than one "// @static-clean=" line in test $filePath'); | 1360 'More than one "// @static-clean=" line in test $filePath'); |
| 1354 } | 1361 } |
| 1355 isStaticClean = true; | 1362 isStaticClean = true; |
| 1356 } | 1363 } |
| 1357 | 1364 |
| 1358 List<String> otherScripts = new List<String>(); | 1365 List<String> otherScripts = new List<String>(); |
| 1359 matches = otherScriptsRegExp.allMatches(contents); | 1366 matches = otherScriptsRegExp.allMatches(contents); |
| 1360 for (var match in matches) { | 1367 for (var match in matches) { |
| 1361 otherScripts.addAll(match[1].split(' ').filter((e) => e != '')); | 1368 otherScripts.addAll(match[1].split(' ').where((e) => e != '').toList()); |
| 1362 } | 1369 } |
| 1363 | 1370 |
| 1364 bool isMultitest = multiTestRegExp.hasMatch(contents); | 1371 bool isMultitest = multiTestRegExp.hasMatch(contents); |
| 1365 bool isMultiHtmlTest = multiHtmlTestRegExp.hasMatch(contents); | 1372 bool isMultiHtmlTest = multiHtmlTestRegExp.hasMatch(contents); |
| 1366 bool containsLeadingHash = leadingHashRegExp.hasMatch(contents); | 1373 bool containsLeadingHash = leadingHashRegExp.hasMatch(contents); |
| 1367 Match isolateMatch = isolateStubsRegExp.firstMatch(contents); | 1374 Match isolateMatch = isolateStubsRegExp.firstMatch(contents); |
| 1368 String isolateStubs = isolateMatch != null ? isolateMatch[1] : ''; | 1375 String isolateStubs = isolateMatch != null ? isolateMatch[1] : ''; |
| 1369 bool containsDomImport = domImportRegExp.hasMatch(contents); | 1376 bool containsDomImport = domImportRegExp.hasMatch(contents); |
| 1370 bool isLibraryDefinition = libraryDefinitionRegExp.hasMatch(contents); | 1377 bool isLibraryDefinition = libraryDefinitionRegExp.hasMatch(contents); |
| 1371 bool containsSourceOrImport = sourceOrImportRegExp.hasMatch(contents); | 1378 bool containsSourceOrImport = sourceOrImportRegExp.hasMatch(contents); |
| 1372 int numStaticTypeAnnotations = 0; | 1379 int numStaticTypeAnnotations = 0; |
| 1373 for (var i in staticTypeRegExp.allMatches(contents)) { | 1380 for (var i in staticTypeRegExp.allMatches(contents)) { |
| 1374 numStaticTypeAnnotations++; | 1381 numStaticTypeAnnotations++; |
| 1375 } | 1382 } |
| 1376 int numCompileTimeAnnotations = 0; | 1383 int numCompileTimeAnnotations = 0; |
| 1377 for (var i in compileTimeRegExp.allMatches(contents)) { | 1384 for (var i in compileTimeRegExp.allMatches(contents)) { |
| 1378 numCompileTimeAnnotations++; | 1385 numCompileTimeAnnotations++; |
| 1379 } | 1386 } |
| 1380 | 1387 |
| 1381 // Note: This is brittle. It's the age-old problem of having a context free | 1388 // Note: This is brittle. It's the age-old problem of having a context free |
| 1382 // language but the means to easily identify the construct is a regular | 1389 // language but the means to easily identify the construct is a regular |
| 1383 // expression, aka impossible. Therefore we just make an approximation of | 1390 // expression, aka impossible. Therefore we just make an approximation of |
| 1384 // the number of top-level "group(...)" occurrences. This assumes you import | 1391 // the number of top-level "group(...)" occurrences. This assumes you import |
| 1385 // unittest with no prefix and always directly call "group(". It only uses | 1392 // unittest with no prefix and always directly call "group(". It only uses |
| 1386 // top-level "groups" so tests running nested groups will be no-ops. | 1393 // top-level "groups" so tests running nested groups will be no-ops. |
| 1387 RegExp numTests = new RegExp(r"\s*[^/]\s*group\('[^,']*"); | 1394 RegExp numTests = new RegExp(r"\s*[^/]\s*group\('[^,']*"); |
| 1388 List<String> subtestNames = []; | 1395 List<String> subtestNames = []; |
| 1389 Iterator matchesIter = numTests.allMatches(contents).iterator(); | 1396 Iterator matchesIter = numTests.allMatches(contents).iterator; |
| 1390 while(matchesIter.hasNext && isMultiHtmlTest) { | 1397 while(matchesIter.moveNext() && isMultiHtmlTest) { |
| 1391 String fullMatch = matchesIter.next().group(0); | 1398 String fullMatch = matchesIter.current.group(0); |
| 1392 subtestNames.add(fullMatch.substring(fullMatch.indexOf("'") + 1)); | 1399 subtestNames.add(fullMatch.substring(fullMatch.indexOf("'") + 1)); |
| 1393 } | 1400 } |
| 1394 | 1401 |
| 1395 return { "vmOptions": result, | 1402 return { "vmOptions": result, |
| 1396 "dartOptions": dartOptions, | 1403 "dartOptions": dartOptions, |
| 1397 "packageRoot": packageRoot, | 1404 "packageRoot": packageRoot, |
| 1398 "hasCompileError": hasCompileError, | 1405 "hasCompileError": hasCompileError, |
| 1399 "hasRuntimeError": hasRuntimeError, | 1406 "hasRuntimeError": hasRuntimeError, |
| 1400 "isStaticClean" : isStaticClean, | 1407 "isStaticClean" : isStaticClean, |
| 1401 "otherScripts": otherScripts, | 1408 "otherScripts": otherScripts, |
| (...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1806 * $pass tests are expected to pass | 1813 * $pass tests are expected to pass |
| 1807 * $failOk tests are expected to fail that we won't fix | 1814 * $failOk tests are expected to fail that we won't fix |
| 1808 * $fail tests are expected to fail that we should fix | 1815 * $fail tests are expected to fail that we should fix |
| 1809 * $crash tests are expected to crash that we should fix | 1816 * $crash tests are expected to crash that we should fix |
| 1810 * $timeout tests are allowed to timeout | 1817 * $timeout tests are allowed to timeout |
| 1811 * $compileErrorSkip tests are skipped on browsers due to compile-time error | 1818 * $compileErrorSkip tests are skipped on browsers due to compile-time error |
| 1812 """; | 1819 """; |
| 1813 print(report); | 1820 print(report); |
| 1814 } | 1821 } |
| 1815 } | 1822 } |
| OLD | NEW |