| 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:async'; |
| 18 import "dart:io"; | 18 import 'dart:io'; |
| 19 import "drt_updater.dart"; | |
| 20 import "html_test.dart" as htmlTest; | |
| 21 import "path.dart"; | |
| 22 import "multitest.dart"; | |
| 23 import "status_file_parser.dart"; | |
| 24 import "test_runner.dart"; | |
| 25 import "utils.dart"; | |
| 26 import "http_server.dart" show PREFIX_BUILDDIR, PREFIX_DARTDIR; | |
| 27 | 19 |
| 28 import "compiler_configuration.dart" show | 20 import 'compiler_configuration.dart' show CompilerConfiguration; |
| 29 CommandArtifact, | 21 import 'lib/browser_test.dart'; |
| 30 CompilerConfiguration; | 22 import 'lib/command.dart'; |
| 31 | 23 import 'lib/drt_updater.dart'; |
| 32 import "runtime_configuration.dart" show | 24 import 'lib/html_test.dart' as htmlTest; |
| 33 RuntimeConfiguration; | 25 import 'lib/http_server.dart' show PREFIX_BUILDDIR, PREFIX_DARTDIR; |
| 34 | 26 import 'lib/multitest.dart'; |
| 35 import 'browser_test.dart'; | 27 import 'lib/path.dart'; |
| 28 import 'lib/status_file_parser.dart'; |
| 29 import 'lib/summary_report.dart'; |
| 30 import 'lib/test_case.dart'; |
| 31 import 'lib/test_information.dart'; |
| 32 import 'lib/test_utils.dart'; |
| 33 import 'lib/utils.dart'; |
| 34 import 'runtime_configuration.dart' show CommandArtifact, RuntimeConfiguration; |
| 36 | 35 |
| 37 | 36 |
| 38 RegExp multiHtmlTestGroupRegExp = new RegExp(r"\s*[^/]\s*group\('[^,']*"); | 37 RegExp multiHtmlTestGroupRegExp = new RegExp(r"\s*[^/]\s*group\('[^,']*"); |
| 39 RegExp multiHtmlTestRegExp = new RegExp(r"useHtmlIndividualConfiguration()"); | 38 RegExp multiHtmlTestRegExp = new RegExp(r"useHtmlIndividualConfiguration()"); |
| 40 // Require at least one non-space character before '///' | 39 // Require at least one non-space character before '///' |
| 41 RegExp multiTestRegExp = new RegExp(r"\S *" | 40 RegExp multiTestRegExp = new RegExp(r"\S *" |
| 42 r"/// \w+:(.*)"); | 41 r"/// \w+:(.*)"); |
| 43 RegExp dartExtension = new RegExp(r'\.dart$'); | |
| 44 | 42 |
| 45 /** | 43 /** |
| 46 * A simple function that tests [arg] and returns `true` or `false`. | 44 * A simple function that tests [arg] and returns `true` or `false`. |
| 47 */ | 45 */ |
| 48 typedef bool Predicate<T>(T arg); | 46 typedef bool Predicate<T>(T arg); |
| 49 | 47 |
| 50 typedef void CreateTest(Path filePath, | |
| 51 Path originTestPath, | |
| 52 bool hasCompileError, | |
| 53 bool hasRuntimeError, | |
| 54 {bool isNegativeIfChecked, | |
| 55 bool hasCompileErrorIfChecked, | |
| 56 bool hasStaticWarning, | |
| 57 String multitestKey}); | |
| 58 | |
| 59 typedef void VoidFunction(); | 48 typedef void VoidFunction(); |
| 60 | 49 |
| 61 /** | 50 /** |
| 62 * Calls [function] asynchronously. Returns a future that completes with the | 51 * Calls [function] asynchronously. Returns a future that completes with the |
| 63 * result of the function. If the function is `null`, returns a future that | 52 * result of the function. If the function is `null`, returns a future that |
| 64 * completes immediately with `null`. | 53 * completes immediately with `null`. |
| 65 */ | 54 */ |
| 66 Future asynchronously(function()) { | 55 Future asynchronously(function()) { |
| 67 if (function == null) return new Future.value(null); | 56 if (function == null) return new Future.value(null); |
| 68 | 57 |
| (...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 473 Map buildPubspecDependencyOverrides(Map packageDirectories) { | 462 Map buildPubspecDependencyOverrides(Map packageDirectories) { |
| 474 Map overrides = {}; | 463 Map overrides = {}; |
| 475 packageDirectories.forEach((String packageName, String fullPath) { | 464 packageDirectories.forEach((String packageName, String fullPath) { |
| 476 overrides[packageName] = { 'path' : fullPath }; | 465 overrides[packageName] = { 'path' : fullPath }; |
| 477 }); | 466 }); |
| 478 return overrides; | 467 return overrides; |
| 479 } | 468 } |
| 480 | 469 |
| 481 } | 470 } |
| 482 | 471 |
| 483 | |
| 484 Future<Iterable<String>> ccTestLister(String runnerPath) { | 472 Future<Iterable<String>> ccTestLister(String runnerPath) { |
| 485 return Process.run(runnerPath, ["--list"]).then((ProcessResult result) { | 473 return Process.run(runnerPath, ["--list"]).then((ProcessResult result) { |
| 486 if (result.exitCode != 0) { | 474 if (result.exitCode != 0) { |
| 487 throw "Failed to list tests: '$runnerPath --list'. " | 475 throw "Failed to list tests: '$runnerPath --list'. " |
| 488 "Process exited with ${result.exitCode}"; | 476 "Process exited with ${result.exitCode}"; |
| 489 } | 477 } |
| 490 return result.stdout | 478 return result.stdout |
| 491 .split('\n') | 479 .split('\n') |
| 492 .map((line) => line.trim()) | 480 .map((line) => line.trim()) |
| 493 .where((name) => name.length > 0); | 481 .where((name) => name.length > 0); |
| 494 }); | 482 }); |
| 495 } | 483 } |
| 496 | 484 |
| 497 | |
| 498 /** | 485 /** |
| 499 * A specialized [TestSuite] that runs tests written in C to unit test | 486 * A specialized [TestSuite] that runs tests written in C to unit test |
| 500 * the Dart virtual machine and its API. | 487 * the Dart virtual machine and its API. |
| 501 * | 488 * |
| 502 * The tests are compiled into a monolithic executable by the build step. | 489 * The tests are compiled into a monolithic executable by the build step. |
| 503 * The executable lists its tests when run with the --list command line flag. | 490 * The executable lists its tests when run with the --list command line flag. |
| 504 * Individual tests are run by specifying them on the command line. | 491 * Individual tests are run by specifying them on the command line. |
| 505 */ | 492 */ |
| 506 class CCTestSuite extends TestSuite { | 493 class CCTestSuite extends TestSuite { |
| 507 final String testPrefix; | 494 final String testPrefix; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 560 doTest = null; | 547 doTest = null; |
| 561 if (onDone != null) onDone(); | 548 if (onDone != null) onDone(); |
| 562 }).catchError((error) { | 549 }).catchError((error) { |
| 563 print("Fatal error occured: $error"); | 550 print("Fatal error occured: $error"); |
| 564 exit(1); | 551 exit(1); |
| 565 }); | 552 }); |
| 566 }); | 553 }); |
| 567 } | 554 } |
| 568 } | 555 } |
| 569 | 556 |
| 570 | |
| 571 class TestInformation { | |
| 572 Path filePath; | |
| 573 Path originTestPath; | |
| 574 Map optionsFromFile; | |
| 575 bool hasCompileError; | |
| 576 bool hasRuntimeError; | |
| 577 bool isNegativeIfChecked; | |
| 578 bool hasCompileErrorIfChecked; | |
| 579 bool hasStaticWarning; | |
| 580 String multitestKey; | |
| 581 | |
| 582 TestInformation(this.filePath, this.originTestPath, this.optionsFromFile, | |
| 583 this.hasCompileError, this.hasRuntimeError, | |
| 584 this.isNegativeIfChecked, this.hasCompileErrorIfChecked, | |
| 585 this.hasStaticWarning, | |
| 586 {this.multitestKey: ''}) { | |
| 587 assert(filePath.isAbsolute); | |
| 588 } | |
| 589 } | |
| 590 | |
| 591 | |
| 592 class HtmlTestInformation extends TestInformation { | |
| 593 List<String> expectedMessages; | |
| 594 List<String> scripts; | |
| 595 | |
| 596 HtmlTestInformation(Path filePath, this.expectedMessages, this.scripts) | |
| 597 : super(filePath, filePath, | |
| 598 {'isMultitest': false, 'isMultiHtmlTest': false}, | |
| 599 false, false, false, false, false) {} | |
| 600 } | |
| 601 | |
| 602 | |
| 603 /** | 557 /** |
| 604 * A standard [TestSuite] implementation that searches for tests in a | 558 * A standard [TestSuite] implementation that searches for tests in a |
| 605 * directory, and creates [TestCase]s that compile and/or run them. | 559 * directory, and creates [TestCase]s that compile and/or run them. |
| 606 */ | 560 */ |
| 607 class StandardTestSuite extends TestSuite { | 561 class StandardTestSuite extends TestSuite { |
| 608 final Path suiteDir; | 562 final Path suiteDir; |
| 609 final List<String> statusFilePaths; | 563 final List<String> statusFilePaths; |
| 610 TestExpectations testExpectations; | 564 TestExpectations testExpectations; |
| 611 List<TestInformation> cachedTests; | 565 List<TestInformation> cachedTests; |
| 612 final Path dartDir; | 566 final Path dartDir; |
| (...skipping 1396 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2009 ReadTestExpectations(statusFiles, configuration).then((expectations) { | 1963 ReadTestExpectations(statusFiles, configuration).then((expectations) { |
| 2010 Future.wait([discoverPackagesInRepository(), | 1964 Future.wait([discoverPackagesInRepository(), |
| 2011 discoverSamplesInRepository()]).then((List results) { | 1965 discoverSamplesInRepository()]).then((List results) { |
| 2012 Map packageDirectories = results[0]; | 1966 Map packageDirectories = results[0]; |
| 2013 Map sampleDirectories = results[1]; | 1967 Map sampleDirectories = results[1]; |
| 2014 enqueueTestCases(packageDirectories, sampleDirectories, expectations); | 1968 enqueueTestCases(packageDirectories, sampleDirectories, expectations); |
| 2015 }); | 1969 }); |
| 2016 }); | 1970 }); |
| 2017 } | 1971 } |
| 2018 } | 1972 } |
| 2019 | |
| 2020 | |
| 2021 class LastModifiedCache { | |
| 2022 Map<String, DateTime> _cache = <String, DateTime>{}; | |
| 2023 | |
| 2024 /** | |
| 2025 * Returns the last modified date of the given [uri]. | |
| 2026 * | |
| 2027 * The return value will be cached for future queries. If [uri] is a local | |
| 2028 * file, it's last modified [Date] will be returned. If the file does not | |
| 2029 * exist, null will be returned instead. | |
| 2030 * In case [uri] is not a local file, this method will always return | |
| 2031 * the current date. | |
| 2032 */ | |
| 2033 DateTime getLastModified(Uri uri) { | |
| 2034 if (uri.scheme == "file") { | |
| 2035 if (_cache.containsKey(uri.path)) { | |
| 2036 return _cache[uri.path]; | |
| 2037 } | |
| 2038 var file = new File(new Path(uri.path).toNativePath()); | |
| 2039 _cache[uri.path] = file.existsSync() ? file.lastModifiedSync() : null; | |
| 2040 return _cache[uri.path]; | |
| 2041 } | |
| 2042 return new DateTime.now(); | |
| 2043 } | |
| 2044 } | |
| 2045 | |
| 2046 | |
| 2047 class ExistsCache { | |
| 2048 Map<String, bool> _cache = <String, bool>{}; | |
| 2049 | |
| 2050 /** | |
| 2051 * Returns true if the file in [path] exists, false otherwise. | |
| 2052 * | |
| 2053 * The information will be cached. | |
| 2054 */ | |
| 2055 bool doesFileExist(String path) { | |
| 2056 if (!_cache.containsKey(path)) { | |
| 2057 _cache[path] = new File(path).existsSync(); | |
| 2058 } | |
| 2059 return _cache[path]; | |
| 2060 } | |
| 2061 } | |
| 2062 | |
| 2063 | |
| 2064 class TestUtils { | |
| 2065 /** | |
| 2066 * Any script using TestUtils must set dartDirUri to a file:// URI | |
| 2067 * pointing to the root of the Dart checkout. | |
| 2068 */ | |
| 2069 static setDartDirUri(uri) { | |
| 2070 dartDirUri = uri; | |
| 2071 dartDir = new Path(uri.toFilePath()); | |
| 2072 } | |
| 2073 static Uri dartDirUri; | |
| 2074 static Path dartDir; | |
| 2075 static LastModifiedCache lastModifiedCache = new LastModifiedCache(); | |
| 2076 static ExistsCache existsCache = new ExistsCache(); | |
| 2077 static Path currentWorkingDirectory = | |
| 2078 new Path(Directory.current.path); | |
| 2079 | |
| 2080 /** | |
| 2081 * Creates a directory using a [relativePath] to an existing | |
| 2082 * [base] directory if that [relativePath] does not already exist. | |
| 2083 */ | |
| 2084 static Directory mkdirRecursive(Path base, Path relativePath) { | |
| 2085 if (relativePath.isAbsolute) { | |
| 2086 base = new Path('/'); | |
| 2087 } | |
| 2088 Directory dir = new Directory(base.toNativePath()); | |
| 2089 assert(dir.existsSync()); | |
| 2090 var segments = relativePath.segments(); | |
| 2091 for (String segment in segments) { | |
| 2092 base = base.append(segment); | |
| 2093 if (base.toString() == "/$segment" && | |
| 2094 segment.length == 2 && | |
| 2095 segment.endsWith(':')) { | |
| 2096 // Skip the directory creation for a path like "/E:". | |
| 2097 continue; | |
| 2098 } | |
| 2099 dir = new Directory(base.toNativePath()); | |
| 2100 if (!dir.existsSync()) { | |
| 2101 dir.createSync(); | |
| 2102 } | |
| 2103 assert(dir.existsSync()); | |
| 2104 } | |
| 2105 return dir; | |
| 2106 } | |
| 2107 | |
| 2108 /** | |
| 2109 * Copy a [source] file to a new place. | |
| 2110 * Assumes that the directory for [dest] already exists. | |
| 2111 */ | |
| 2112 static Future copyFile(Path source, Path dest) { | |
| 2113 return new File(source.toNativePath()).openRead() | |
| 2114 .pipe(new File(dest.toNativePath()).openWrite()); | |
| 2115 } | |
| 2116 | |
| 2117 static Future copyDirectory(String source, String dest) { | |
| 2118 source = new Path(source).toNativePath(); | |
| 2119 dest = new Path(dest).toNativePath(); | |
| 2120 | |
| 2121 var executable = 'cp'; | |
| 2122 var args = ['-Rp', source, dest]; | |
| 2123 if (Platform.operatingSystem == 'windows') { | |
| 2124 executable = 'xcopy'; | |
| 2125 args = [source, dest, '/e', '/i']; | |
| 2126 } | |
| 2127 return Process.run(executable, args).then((ProcessResult result) { | |
| 2128 if (result.exitCode != 0) { | |
| 2129 throw new Exception("Failed to execute '$executable " | |
| 2130 "${args.join(' ')}'."); | |
| 2131 } | |
| 2132 }); | |
| 2133 } | |
| 2134 | |
| 2135 static Future deleteDirectory(String path) { | |
| 2136 // We are seeing issues with long path names on windows when | |
| 2137 // deleting them. Use the system tools to delete our long paths. | |
| 2138 // See issue 16264. | |
| 2139 if (Platform.operatingSystem == 'windows') { | |
| 2140 var native_path = new Path(path).toNativePath(); | |
| 2141 // Running this in a shell sucks, but rmdir is not part of the standard | |
| 2142 // path. | |
| 2143 return Process.run('rmdir', ['/s', '/q', native_path], runInShell: true) | |
| 2144 .then((ProcessResult result) { | |
| 2145 if (result.exitCode != 0) { | |
| 2146 throw new Exception('Can\'t delete path $native_path. ' | |
| 2147 'This path might be too long'); | |
| 2148 } | |
| 2149 }); | |
| 2150 } else { | |
| 2151 var dir = new Directory(path); | |
| 2152 return dir.delete(recursive: true); | |
| 2153 } | |
| 2154 } | |
| 2155 | |
| 2156 static Path debugLogfile() { | |
| 2157 return new Path(".debug.log"); | |
| 2158 } | |
| 2159 | |
| 2160 static String flakyFileName() { | |
| 2161 // If a flaky test did fail, infos about it (i.e. test name, stdin, stdout) | |
| 2162 // will be written to this file. This is useful for the debugging of | |
| 2163 // flaky tests. | |
| 2164 // When running on a built bot, the file can be made visible in the | |
| 2165 // waterfall UI. | |
| 2166 return ".flaky.log"; | |
| 2167 } | |
| 2168 | |
| 2169 static String testOutcomeFileName() { | |
| 2170 // If test.py was invoked with '--write-test-outcome-log it will write | |
| 2171 // test outcomes to this file. | |
| 2172 return ".test-outcome.log"; | |
| 2173 } | |
| 2174 | |
| 2175 static void ensureExists(String filename, Map configuration) { | |
| 2176 if (!configuration['list'] && !existsCache.doesFileExist(filename)) { | |
| 2177 throw "'$filename' does not exist"; | |
| 2178 } | |
| 2179 } | |
| 2180 | |
| 2181 static Path absolutePath(Path path) { | |
| 2182 if (!path.isAbsolute) { | |
| 2183 return currentWorkingDirectory.join(path); | |
| 2184 } | |
| 2185 return path; | |
| 2186 } | |
| 2187 | |
| 2188 static String outputDir(Map configuration) { | |
| 2189 var result = ''; | |
| 2190 var system = configuration['system']; | |
| 2191 if (system == 'linux') { | |
| 2192 result = 'out/'; | |
| 2193 } else if (system == 'macos') { | |
| 2194 result = 'xcodebuild/'; | |
| 2195 } else if (system == 'windows') { | |
| 2196 result = 'build/'; | |
| 2197 } | |
| 2198 return result; | |
| 2199 } | |
| 2200 | |
| 2201 static List<String> standardOptions(Map configuration) { | |
| 2202 List args = ["--ignore-unrecognized-flags"]; | |
| 2203 if (configuration["checked"]) { | |
| 2204 args.add('--enable_asserts'); | |
| 2205 args.add("--enable_type_checks"); | |
| 2206 } | |
| 2207 String compiler = configuration["compiler"]; | |
| 2208 if (compiler == "dart2js" || compiler == "dart2dart") { | |
| 2209 args = []; | |
| 2210 if (configuration["checked"]) { | |
| 2211 args.add('--enable-checked-mode'); | |
| 2212 } | |
| 2213 // args.add("--verbose"); | |
| 2214 if (!isBrowserRuntime(configuration['runtime'])) { | |
| 2215 args.add("--allow-mock-compilation"); | |
| 2216 args.add("--categories=all"); | |
| 2217 } | |
| 2218 } | |
| 2219 if ((compiler == "dart2js" || compiler == "dart2dart") && | |
| 2220 configuration["minified"]) { | |
| 2221 args.add("--minify"); | |
| 2222 } | |
| 2223 if (compiler == "dartanalyzer" || compiler == "dart2analyzer") { | |
| 2224 args.add("--show-package-warnings"); | |
| 2225 args.add("--enable-async"); | |
| 2226 } | |
| 2227 return args; | |
| 2228 } | |
| 2229 | |
| 2230 static bool isBrowserRuntime(String runtime) { | |
| 2231 const BROWSERS = const [ | |
| 2232 'drt', | |
| 2233 'dartium', | |
| 2234 'ie9', | |
| 2235 'ie10', | |
| 2236 'ie11', | |
| 2237 'safari', | |
| 2238 'opera', | |
| 2239 'chrome', | |
| 2240 'ff', | |
| 2241 'chromeOnAndroid', | |
| 2242 'safarimobilesim', | |
| 2243 'ContentShellOnAndroid', | |
| 2244 'DartiumOnAndroid' | |
| 2245 ]; | |
| 2246 return BROWSERS.contains(runtime); | |
| 2247 } | |
| 2248 | |
| 2249 static bool isJsCommandLineRuntime(String runtime) => | |
| 2250 const ['d8', 'jsshell'].contains(runtime); | |
| 2251 | |
| 2252 static bool isCommandLineAnalyzer(String compiler) => | |
| 2253 compiler == 'dartanalyzer' || compiler == 'dart2analyzer'; | |
| 2254 | |
| 2255 static String buildDir(Map configuration) { | |
| 2256 // FIXME(kustermann,ricow): Our code assumes that the returned 'buildDir' | |
| 2257 // is relative to the current working directory. | |
| 2258 // Thus, if we pass in an absolute path (e.g. '--build-directory=/tmp/out') | |
| 2259 // we get into trouble. | |
| 2260 if (configuration['build_directory'] != '') { | |
| 2261 return configuration['build_directory']; | |
| 2262 } | |
| 2263 | |
| 2264 return "${outputDir(configuration)}${configurationDir(configuration)}"; | |
| 2265 } | |
| 2266 | |
| 2267 static getValidOutputDir(Map configuration, String mode, String arch) { | |
| 2268 // We allow our code to have been cross compiled, i.e., that there | |
| 2269 // is an X in front of the arch. We don't allow both a cross compiled | |
| 2270 // and a normal version to be present (except if you specifically pass | |
| 2271 // in the build_directory). | |
| 2272 var normal = '$mode$arch'; | |
| 2273 var cross = '${mode}X$arch'; | |
| 2274 var outDir = outputDir(configuration); | |
| 2275 var normalDir = new Directory(new Path('$outDir$normal').toNativePath()); | |
| 2276 var crossDir = new Directory(new Path('$outDir$cross').toNativePath()); | |
| 2277 if (normalDir.existsSync() && crossDir.existsSync()) { | |
| 2278 throw "You can't have both $normalDir and $crossDir, we don't know which" | |
| 2279 " binary to use"; | |
| 2280 } | |
| 2281 if (crossDir.existsSync()) { | |
| 2282 return cross; | |
| 2283 } | |
| 2284 return normal; | |
| 2285 } | |
| 2286 | |
| 2287 static String configurationDir(Map configuration) { | |
| 2288 // For regular dart checkouts, the configDir by default is mode+arch. | |
| 2289 // For Dartium, the configDir by default is mode (as defined by the Chrome | |
| 2290 // build setup). We can detect this because in the dartium checkout, the | |
| 2291 // "output" directory is a sibling of the dart directory instead of a child. | |
| 2292 var mode = (configuration['mode'] == 'debug') ? 'Debug' : 'Release'; | |
| 2293 var arch = configuration['arch'].toUpperCase(); | |
| 2294 if (currentWorkingDirectory != dartDir) { | |
| 2295 return getValidOutputDir(configuration, mode, arch); | |
| 2296 } else { | |
| 2297 return mode; | |
| 2298 } | |
| 2299 } | |
| 2300 | |
| 2301 /** | |
| 2302 * Returns the path to the dart binary checked into the repo, used for | |
| 2303 * bootstrapping test.dart. | |
| 2304 */ | |
| 2305 static Path get dartTestExecutable { | |
| 2306 var path = '$dartDir/tools/testing/bin/' | |
| 2307 '${Platform.operatingSystem}/dart'; | |
| 2308 if (Platform.operatingSystem == 'windows') { | |
| 2309 path = '$path.exe'; | |
| 2310 } | |
| 2311 return new Path(path); | |
| 2312 } | |
| 2313 | |
| 2314 /** | |
| 2315 * Gets extra options under [key] passed to the testing script. | |
| 2316 */ | |
| 2317 static List<String> getExtraOptions(Map configuration, String key) { | |
| 2318 if (configuration[key] == null) return <String>[]; | |
| 2319 return configuration[key] | |
| 2320 .split(" ") | |
| 2321 .map((s) => s.trim()) | |
| 2322 .where((s) => s.isNotEmpty) | |
| 2323 .toList(); | |
| 2324 } | |
| 2325 | |
| 2326 /** | |
| 2327 * Gets extra vm options passed to the testing script. | |
| 2328 */ | |
| 2329 static List<String> getExtraVmOptions(Map configuration) => | |
| 2330 getExtraOptions(configuration, 'vm_options'); | |
| 2331 | |
| 2332 static String getShortName(String path) { | |
| 2333 final PATH_REPLACEMENTS = const { | |
| 2334 "pkg_polymer_e2e_test_bad_import_test": "polymer_bi", | |
| 2335 "pkg_polymer_e2e_test_canonicalization_test": "polymer_c16n", | |
| 2336 "pkg_polymer_e2e_test_experimental_boot_test": "polymer_boot", | |
| 2337 "pkg_polymer_e2e_test_good_import_test": "polymer_gi", | |
| 2338 "tests_co19_src_Language_12_Expressions_14_Function_Invocation_": | |
| 2339 "co19_fn_invoke_", | |
| 2340 "tests_co19_src_LayoutTests_fast_css_getComputedStyle_getComputedStyle-": | |
| 2341 "co19_css_getComputedStyle_", | |
| 2342 "tests_co19_src_LayoutTests_fast_dom_Document_CaretRangeFromPoint_" | |
| 2343 "caretRangeFromPoint-": "co19_caretrangefrompoint_", | |
| 2344 "tests_co19_src_LayoutTests_fast_dom_Document_CaretRangeFromPoint_" | |
| 2345 "hittest-relative-to-viewport_": "co19_caretrange_hittest_", | |
| 2346 "tests_co19_src_LayoutTests_fast_dom_HTMLLinkElement_link-onerror-" | |
| 2347 "stylesheet-with-": "co19_dom_link-", | |
| 2348 "tests_co19_src_LayoutTests_fast_dom_": "co19_dom", | |
| 2349 "tests_co19_src_LayoutTests_fast_canvas_webgl": "co19_canvas_webgl", | |
| 2350 "tests_co19_src_LibTest_core_AbstractClassInstantiationError_" | |
| 2351 "AbstractClassInstantiationError_": "co19_abstract_class_", | |
| 2352 "tests_co19_src_LibTest_core_IntegerDivisionByZeroException_" | |
| 2353 "IntegerDivisionByZeroException_": "co19_division_by_zero", | |
| 2354 "tests_co19_src_WebPlatformTest_html_dom_documents_dom-tree-accessors_": | |
| 2355 "co19_dom_accessors_", | |
| 2356 "tests_co19_src_WebPlatformTest_html_semantics_embedded-content_" | |
| 2357 "media-elements_": "co19_media_elements", | |
| 2358 "tests_co19_src_WebPlatformTest_html_semantics_": "co19_semantics_", | |
| 2359 | |
| 2360 "tests_co19_src_WebPlatformTest_html-templates_additions-to-" | |
| 2361 "the-steps-to-clone-a-node_": "co19_htmltemplates_clone_", | |
| 2362 "tests_co19_src_WebPlatformTest_html-templates_definitions_" | |
| 2363 "template-contents-owner": "co19_htmltemplates_contents", | |
| 2364 "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" | |
| 2365 "templates_additions-to-": "co19_htmltemplates_add_", | |
| 2366 "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" | |
| 2367 "templates_appending-to-a-template_": "co19_htmltemplates_append_", | |
| 2368 "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" | |
| 2369 "templates_clearing-the-stack-back-to-a-given-context_": | |
| 2370 "co19_htmltemplates_clearstack_", | |
| 2371 "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" | |
| 2372 "templates_creating-an-element-for-the-token_": | |
| 2373 "co19_htmltemplates_create_", | |
| 2374 "tests_co19_src_WebPlatformTest_html-templates_template-element" | |
| 2375 "_template-": "co19_htmltemplates_element-", | |
| 2376 "tests_co19_src_WebPlatformTest_html-templates_": "co19_htmltemplate_", | |
| 2377 | |
| 2378 "tests_co19_src_WebPlatformTest_shadow-dom_shadow-trees_": | |
| 2379 "co19_shadow-trees_", | |
| 2380 "tests_co19_src_WebPlatformTest_shadow-dom_elements-and-dom-objects_": | |
| 2381 "co19_shadowdom_", | |
| 2382 "tests_co19_src_WebPlatformTest_shadow-dom_html-elements-in-" | |
| 2383 "shadow-trees_": "co19_shadow_html_", | |
| 2384 "tests_co19_src_WebPlatformTest_html_webappapis_system-state-and-" | |
| 2385 "capabilities_the-navigator-object": "co19_webappapis_navigator_", | |
| 2386 "tests_co19_src_WebPlatformTest_DOMEvents_approved_": "co19_dom_approved_" | |
| 2387 }; | |
| 2388 | |
| 2389 // Some tests are already in [build_dir]/generated_tests. | |
| 2390 String GEN_TESTS = 'generated_tests/'; | |
| 2391 if (path.contains(GEN_TESTS)) { | |
| 2392 int index = path.indexOf(GEN_TESTS) + GEN_TESTS.length; | |
| 2393 path = 'multitest/${path.substring(index)}'; | |
| 2394 } | |
| 2395 path = path.replaceAll('/', '_'); | |
| 2396 final int WINDOWS_SHORTEN_PATH_LIMIT = 58; | |
| 2397 if (Platform.operatingSystem == 'windows' && | |
| 2398 path.length > WINDOWS_SHORTEN_PATH_LIMIT) { | |
| 2399 for (var key in PATH_REPLACEMENTS.keys) { | |
| 2400 if (path.startsWith(key)) { | |
| 2401 path = path.replaceFirst(key, PATH_REPLACEMENTS[key]); | |
| 2402 break; | |
| 2403 } | |
| 2404 } | |
| 2405 } | |
| 2406 return path; | |
| 2407 } | |
| 2408 } | |
| 2409 | |
| 2410 | |
| 2411 class SummaryReport { | |
| 2412 static int total = 0; | |
| 2413 static int skipped = 0; | |
| 2414 static int skippedByDesign = 0; | |
| 2415 static int noCrash = 0; | |
| 2416 static int flakyCrash = 0; | |
| 2417 static int pass = 0; | |
| 2418 static int failOk = 0; | |
| 2419 static int fail = 0; | |
| 2420 static int crash = 0; | |
| 2421 static int timeout = 0; | |
| 2422 static int compileErrorSkip = 0; | |
| 2423 | |
| 2424 static List<TestCase> nonStandardTestCases = []; | |
| 2425 | |
| 2426 static void add(TestCase testCase) { | |
| 2427 var expectations = testCase.expectedOutcomes; | |
| 2428 | |
| 2429 bool containsFail = expectations.any( | |
| 2430 (expectation) => expectation.canBeOutcomeOf(Expectation.FAIL)); | |
| 2431 bool containsPass = expectations.contains(Expectation.PASS); | |
| 2432 bool containsSkip = expectations.contains(Expectation.SKIP); | |
| 2433 bool containsSkipByDesign = | |
| 2434 expectations.contains(Expectation.SKIP_BY_DESIGN); | |
| 2435 bool containsCrash = expectations.contains(Expectation.CRASH); | |
| 2436 bool containsOK = expectations.contains(Expectation.OK); | |
| 2437 bool containsSlow = expectations.contains(Expectation.SLOW); | |
| 2438 bool containsTimeout = expectations.contains(Expectation.TIMEOUT); | |
| 2439 | |
| 2440 ++total; | |
| 2441 if (containsSkip) { | |
| 2442 ++skipped; | |
| 2443 } else if (containsSkipByDesign) { | |
| 2444 ++skipped; | |
| 2445 ++skippedByDesign; | |
| 2446 } else { | |
| 2447 // We don't do if-else below because the buckets should be exclusive. | |
| 2448 // We keep a count around to guarantee that | |
| 2449 int markers = 0; | |
| 2450 | |
| 2451 // Counts the number of flaky tests. | |
| 2452 if (containsFail && containsPass && !containsCrash && !containsOK) { | |
| 2453 ++noCrash; | |
| 2454 ++markers; | |
| 2455 } | |
| 2456 if (containsCrash && !containsOK && expectations.length > 1) { | |
| 2457 ++flakyCrash; | |
| 2458 ++markers; | |
| 2459 } | |
| 2460 if ((containsPass && expectations.length == 1) || | |
| 2461 (containsPass && containsSlow && expectations.length == 2)) { | |
| 2462 ++pass; | |
| 2463 ++markers; | |
| 2464 } | |
| 2465 if (containsFail && containsOK) { | |
| 2466 ++failOk; | |
| 2467 ++markers; | |
| 2468 } | |
| 2469 if ((containsFail && expectations.length == 1) || | |
| 2470 (containsFail && containsSlow && expectations.length == 2)) { | |
| 2471 ++fail; | |
| 2472 ++markers; | |
| 2473 } | |
| 2474 if ((containsCrash && expectations.length == 1) || | |
| 2475 (containsCrash && containsSlow && expectations.length == 2)) { | |
| 2476 ++crash; | |
| 2477 ++markers; | |
| 2478 } | |
| 2479 if (containsTimeout && expectations.length == 1) { | |
| 2480 ++timeout; | |
| 2481 ++markers; | |
| 2482 } | |
| 2483 if (markers != 1) { | |
| 2484 nonStandardTestCases.add(testCase); | |
| 2485 } | |
| 2486 } | |
| 2487 } | |
| 2488 | |
| 2489 static void addCompileErrorSkipTest() { | |
| 2490 total++; | |
| 2491 compileErrorSkip++; | |
| 2492 } | |
| 2493 | |
| 2494 static void printReport() { | |
| 2495 if (total == 0) return; | |
| 2496 var bogus = nonStandardTestCases.length; | |
| 2497 String report = """Total: $total tests | |
| 2498 * $skipped tests will be skipped ($skippedByDesign skipped by design) | |
| 2499 * $noCrash tests are expected to be flaky but not crash | |
| 2500 * $flakyCrash tests are expected to flaky crash | |
| 2501 * $pass tests are expected to pass | |
| 2502 * $failOk tests are expected to fail that we won't fix | |
| 2503 * $fail tests are expected to fail that we should fix | |
| 2504 * $crash tests are expected to crash that we should fix | |
| 2505 * $timeout tests are allowed to timeout | |
| 2506 * $compileErrorSkip tests are skipped on browsers due to compile-time error | |
| 2507 * $bogus could not be categorized or are in multiple categories | |
| 2508 """; | |
| 2509 print(report); | |
| 2510 } | |
| 2511 } | |
| OLD | NEW |