Index: tools/testing/dart/test_suite.dart |
diff --git a/tools/testing/dart/test_suite.dart b/tools/testing/dart/test_suite.dart |
index 3f1d961b1ac9a3e21ac7894a8377cd38a0514422..10d3beacceebee5ba0a7460c8ee542c12b480c21 100644 |
--- a/tools/testing/dart/test_suite.dart |
+++ b/tools/testing/dart/test_suite.dart |
@@ -14,25 +14,24 @@ |
*/ |
library test_suite; |
-import "dart:async"; |
-import "dart:io"; |
-import "drt_updater.dart"; |
-import "html_test.dart" as htmlTest; |
-import "path.dart"; |
-import "multitest.dart"; |
-import "status_file_parser.dart"; |
-import "test_runner.dart"; |
-import "utils.dart"; |
-import "http_server.dart" show PREFIX_BUILDDIR, PREFIX_DARTDIR; |
- |
-import "compiler_configuration.dart" show |
- CommandArtifact, |
- CompilerConfiguration; |
- |
-import "runtime_configuration.dart" show |
- RuntimeConfiguration; |
- |
-import 'browser_test.dart'; |
+import 'dart:async'; |
+import 'dart:io'; |
+ |
+import 'compiler_configuration.dart' show CompilerConfiguration; |
+import 'lib/browser_test.dart'; |
+import 'lib/command.dart'; |
+import 'lib/drt_updater.dart'; |
+import 'lib/html_test.dart' as htmlTest; |
+import 'lib/http_server.dart' show PREFIX_BUILDDIR, PREFIX_DARTDIR; |
+import 'lib/multitest.dart'; |
+import 'lib/path.dart'; |
+import 'lib/status_file_parser.dart'; |
+import 'lib/summary_report.dart'; |
+import 'lib/test_case.dart'; |
+import 'lib/test_information.dart'; |
+import 'lib/test_utils.dart'; |
+import 'lib/utils.dart'; |
+import 'runtime_configuration.dart' show CommandArtifact, RuntimeConfiguration; |
RegExp multiHtmlTestGroupRegExp = new RegExp(r"\s*[^/]\s*group\('[^,']*"); |
@@ -40,22 +39,12 @@ RegExp multiHtmlTestRegExp = new RegExp(r"useHtmlIndividualConfiguration()"); |
// Require at least one non-space character before '///' |
RegExp multiTestRegExp = new RegExp(r"\S *" |
r"/// \w+:(.*)"); |
-RegExp dartExtension = new RegExp(r'\.dart$'); |
/** |
* A simple function that tests [arg] and returns `true` or `false`. |
*/ |
typedef bool Predicate<T>(T arg); |
-typedef void CreateTest(Path filePath, |
- Path originTestPath, |
- bool hasCompileError, |
- bool hasRuntimeError, |
- {bool isNegativeIfChecked, |
- bool hasCompileErrorIfChecked, |
- bool hasStaticWarning, |
- String multitestKey}); |
- |
typedef void VoidFunction(); |
/** |
@@ -480,7 +469,6 @@ abstract class TestSuite { |
} |
- |
Future<Iterable<String>> ccTestLister(String runnerPath) { |
return Process.run(runnerPath, ["--list"]).then((ProcessResult result) { |
if (result.exitCode != 0) { |
@@ -494,7 +482,6 @@ Future<Iterable<String>> ccTestLister(String runnerPath) { |
}); |
} |
- |
/** |
* A specialized [TestSuite] that runs tests written in C to unit test |
* the Dart virtual machine and its API. |
@@ -567,39 +554,6 @@ class CCTestSuite extends TestSuite { |
} |
} |
- |
-class TestInformation { |
- Path filePath; |
- Path originTestPath; |
- Map optionsFromFile; |
- bool hasCompileError; |
- bool hasRuntimeError; |
- bool isNegativeIfChecked; |
- bool hasCompileErrorIfChecked; |
- bool hasStaticWarning; |
- String multitestKey; |
- |
- TestInformation(this.filePath, this.originTestPath, this.optionsFromFile, |
- this.hasCompileError, this.hasRuntimeError, |
- this.isNegativeIfChecked, this.hasCompileErrorIfChecked, |
- this.hasStaticWarning, |
- {this.multitestKey: ''}) { |
- assert(filePath.isAbsolute); |
- } |
-} |
- |
- |
-class HtmlTestInformation extends TestInformation { |
- List<String> expectedMessages; |
- List<String> scripts; |
- |
- HtmlTestInformation(Path filePath, this.expectedMessages, this.scripts) |
- : super(filePath, filePath, |
- {'isMultitest': false, 'isMultiHtmlTest': false}, |
- false, false, false, false, false) {} |
-} |
- |
- |
/** |
* A standard [TestSuite] implementation that searches for tests in a |
* directory, and creates [TestCase]s that compile and/or run them. |
@@ -2016,496 +1970,3 @@ class PkgBuildTestSuite extends TestSuite { |
}); |
} |
} |
- |
- |
-class LastModifiedCache { |
- Map<String, DateTime> _cache = <String, DateTime>{}; |
- |
- /** |
- * Returns the last modified date of the given [uri]. |
- * |
- * The return value will be cached for future queries. If [uri] is a local |
- * file, it's last modified [Date] will be returned. If the file does not |
- * exist, null will be returned instead. |
- * In case [uri] is not a local file, this method will always return |
- * the current date. |
- */ |
- DateTime getLastModified(Uri uri) { |
- if (uri.scheme == "file") { |
- if (_cache.containsKey(uri.path)) { |
- return _cache[uri.path]; |
- } |
- var file = new File(new Path(uri.path).toNativePath()); |
- _cache[uri.path] = file.existsSync() ? file.lastModifiedSync() : null; |
- return _cache[uri.path]; |
- } |
- return new DateTime.now(); |
- } |
-} |
- |
- |
-class ExistsCache { |
- Map<String, bool> _cache = <String, bool>{}; |
- |
- /** |
- * Returns true if the file in [path] exists, false otherwise. |
- * |
- * The information will be cached. |
- */ |
- bool doesFileExist(String path) { |
- if (!_cache.containsKey(path)) { |
- _cache[path] = new File(path).existsSync(); |
- } |
- return _cache[path]; |
- } |
-} |
- |
- |
-class TestUtils { |
- /** |
- * Any script using TestUtils must set dartDirUri to a file:// URI |
- * pointing to the root of the Dart checkout. |
- */ |
- static setDartDirUri(uri) { |
- dartDirUri = uri; |
- dartDir = new Path(uri.toFilePath()); |
- } |
- static Uri dartDirUri; |
- static Path dartDir; |
- static LastModifiedCache lastModifiedCache = new LastModifiedCache(); |
- static ExistsCache existsCache = new ExistsCache(); |
- static Path currentWorkingDirectory = |
- new Path(Directory.current.path); |
- |
- /** |
- * Creates a directory using a [relativePath] to an existing |
- * [base] directory if that [relativePath] does not already exist. |
- */ |
- static Directory mkdirRecursive(Path base, Path relativePath) { |
- if (relativePath.isAbsolute) { |
- base = new Path('/'); |
- } |
- Directory dir = new Directory(base.toNativePath()); |
- assert(dir.existsSync()); |
- var segments = relativePath.segments(); |
- for (String segment in segments) { |
- base = base.append(segment); |
- if (base.toString() == "/$segment" && |
- segment.length == 2 && |
- segment.endsWith(':')) { |
- // Skip the directory creation for a path like "/E:". |
- continue; |
- } |
- dir = new Directory(base.toNativePath()); |
- if (!dir.existsSync()) { |
- dir.createSync(); |
- } |
- assert(dir.existsSync()); |
- } |
- return dir; |
- } |
- |
- /** |
- * Copy a [source] file to a new place. |
- * Assumes that the directory for [dest] already exists. |
- */ |
- static Future copyFile(Path source, Path dest) { |
- return new File(source.toNativePath()).openRead() |
- .pipe(new File(dest.toNativePath()).openWrite()); |
- } |
- |
- static Future copyDirectory(String source, String dest) { |
- source = new Path(source).toNativePath(); |
- dest = new Path(dest).toNativePath(); |
- |
- var executable = 'cp'; |
- var args = ['-Rp', source, dest]; |
- if (Platform.operatingSystem == 'windows') { |
- executable = 'xcopy'; |
- args = [source, dest, '/e', '/i']; |
- } |
- return Process.run(executable, args).then((ProcessResult result) { |
- if (result.exitCode != 0) { |
- throw new Exception("Failed to execute '$executable " |
- "${args.join(' ')}'."); |
- } |
- }); |
- } |
- |
- static Future deleteDirectory(String path) { |
- // We are seeing issues with long path names on windows when |
- // deleting them. Use the system tools to delete our long paths. |
- // See issue 16264. |
- if (Platform.operatingSystem == 'windows') { |
- var native_path = new Path(path).toNativePath(); |
- // Running this in a shell sucks, but rmdir is not part of the standard |
- // path. |
- return Process.run('rmdir', ['/s', '/q', native_path], runInShell: true) |
- .then((ProcessResult result) { |
- if (result.exitCode != 0) { |
- throw new Exception('Can\'t delete path $native_path. ' |
- 'This path might be too long'); |
- } |
- }); |
- } else { |
- var dir = new Directory(path); |
- return dir.delete(recursive: true); |
- } |
- } |
- |
- static Path debugLogfile() { |
- return new Path(".debug.log"); |
- } |
- |
- static String flakyFileName() { |
- // If a flaky test did fail, infos about it (i.e. test name, stdin, stdout) |
- // will be written to this file. This is useful for the debugging of |
- // flaky tests. |
- // When running on a built bot, the file can be made visible in the |
- // waterfall UI. |
- return ".flaky.log"; |
- } |
- |
- static String testOutcomeFileName() { |
- // If test.py was invoked with '--write-test-outcome-log it will write |
- // test outcomes to this file. |
- return ".test-outcome.log"; |
- } |
- |
- static void ensureExists(String filename, Map configuration) { |
- if (!configuration['list'] && !existsCache.doesFileExist(filename)) { |
- throw "'$filename' does not exist"; |
- } |
- } |
- |
- static Path absolutePath(Path path) { |
- if (!path.isAbsolute) { |
- return currentWorkingDirectory.join(path); |
- } |
- return path; |
- } |
- |
- static String outputDir(Map configuration) { |
- var result = ''; |
- var system = configuration['system']; |
- if (system == 'linux') { |
- result = 'out/'; |
- } else if (system == 'macos') { |
- result = 'xcodebuild/'; |
- } else if (system == 'windows') { |
- result = 'build/'; |
- } |
- return result; |
- } |
- |
- static List<String> standardOptions(Map configuration) { |
- List args = ["--ignore-unrecognized-flags"]; |
- if (configuration["checked"]) { |
- args.add('--enable_asserts'); |
- args.add("--enable_type_checks"); |
- } |
- String compiler = configuration["compiler"]; |
- if (compiler == "dart2js" || compiler == "dart2dart") { |
- args = []; |
- if (configuration["checked"]) { |
- args.add('--enable-checked-mode'); |
- } |
- // args.add("--verbose"); |
- if (!isBrowserRuntime(configuration['runtime'])) { |
- args.add("--allow-mock-compilation"); |
- args.add("--categories=all"); |
- } |
- } |
- if ((compiler == "dart2js" || compiler == "dart2dart") && |
- configuration["minified"]) { |
- args.add("--minify"); |
- } |
- if (compiler == "dartanalyzer" || compiler == "dart2analyzer") { |
- args.add("--show-package-warnings"); |
- args.add("--enable-async"); |
- } |
- return args; |
- } |
- |
- static bool isBrowserRuntime(String runtime) { |
- const BROWSERS = const [ |
- 'drt', |
- 'dartium', |
- 'ie9', |
- 'ie10', |
- 'ie11', |
- 'safari', |
- 'opera', |
- 'chrome', |
- 'ff', |
- 'chromeOnAndroid', |
- 'safarimobilesim', |
- 'ContentShellOnAndroid', |
- 'DartiumOnAndroid' |
- ]; |
- return BROWSERS.contains(runtime); |
- } |
- |
- static bool isJsCommandLineRuntime(String runtime) => |
- const ['d8', 'jsshell'].contains(runtime); |
- |
- static bool isCommandLineAnalyzer(String compiler) => |
- compiler == 'dartanalyzer' || compiler == 'dart2analyzer'; |
- |
- static String buildDir(Map configuration) { |
- // FIXME(kustermann,ricow): Our code assumes that the returned 'buildDir' |
- // is relative to the current working directory. |
- // Thus, if we pass in an absolute path (e.g. '--build-directory=/tmp/out') |
- // we get into trouble. |
- if (configuration['build_directory'] != '') { |
- return configuration['build_directory']; |
- } |
- |
- return "${outputDir(configuration)}${configurationDir(configuration)}"; |
- } |
- |
- static getValidOutputDir(Map configuration, String mode, String arch) { |
- // We allow our code to have been cross compiled, i.e., that there |
- // is an X in front of the arch. We don't allow both a cross compiled |
- // and a normal version to be present (except if you specifically pass |
- // in the build_directory). |
- var normal = '$mode$arch'; |
- var cross = '${mode}X$arch'; |
- var outDir = outputDir(configuration); |
- var normalDir = new Directory(new Path('$outDir$normal').toNativePath()); |
- var crossDir = new Directory(new Path('$outDir$cross').toNativePath()); |
- if (normalDir.existsSync() && crossDir.existsSync()) { |
- throw "You can't have both $normalDir and $crossDir, we don't know which" |
- " binary to use"; |
- } |
- if (crossDir.existsSync()) { |
- return cross; |
- } |
- return normal; |
- } |
- |
- static String configurationDir(Map configuration) { |
- // For regular dart checkouts, the configDir by default is mode+arch. |
- // For Dartium, the configDir by default is mode (as defined by the Chrome |
- // build setup). We can detect this because in the dartium checkout, the |
- // "output" directory is a sibling of the dart directory instead of a child. |
- var mode = (configuration['mode'] == 'debug') ? 'Debug' : 'Release'; |
- var arch = configuration['arch'].toUpperCase(); |
- if (currentWorkingDirectory != dartDir) { |
- return getValidOutputDir(configuration, mode, arch); |
- } else { |
- return mode; |
- } |
- } |
- |
- /** |
- * Returns the path to the dart binary checked into the repo, used for |
- * bootstrapping test.dart. |
- */ |
- static Path get dartTestExecutable { |
- var path = '$dartDir/tools/testing/bin/' |
- '${Platform.operatingSystem}/dart'; |
- if (Platform.operatingSystem == 'windows') { |
- path = '$path.exe'; |
- } |
- return new Path(path); |
- } |
- |
- /** |
- * Gets extra options under [key] passed to the testing script. |
- */ |
- static List<String> getExtraOptions(Map configuration, String key) { |
- if (configuration[key] == null) return <String>[]; |
- return configuration[key] |
- .split(" ") |
- .map((s) => s.trim()) |
- .where((s) => s.isNotEmpty) |
- .toList(); |
- } |
- |
- /** |
- * Gets extra vm options passed to the testing script. |
- */ |
- static List<String> getExtraVmOptions(Map configuration) => |
- getExtraOptions(configuration, 'vm_options'); |
- |
- static String getShortName(String path) { |
- final PATH_REPLACEMENTS = const { |
- "pkg_polymer_e2e_test_bad_import_test": "polymer_bi", |
- "pkg_polymer_e2e_test_canonicalization_test": "polymer_c16n", |
- "pkg_polymer_e2e_test_experimental_boot_test": "polymer_boot", |
- "pkg_polymer_e2e_test_good_import_test": "polymer_gi", |
- "tests_co19_src_Language_12_Expressions_14_Function_Invocation_": |
- "co19_fn_invoke_", |
- "tests_co19_src_LayoutTests_fast_css_getComputedStyle_getComputedStyle-": |
- "co19_css_getComputedStyle_", |
- "tests_co19_src_LayoutTests_fast_dom_Document_CaretRangeFromPoint_" |
- "caretRangeFromPoint-": "co19_caretrangefrompoint_", |
- "tests_co19_src_LayoutTests_fast_dom_Document_CaretRangeFromPoint_" |
- "hittest-relative-to-viewport_": "co19_caretrange_hittest_", |
- "tests_co19_src_LayoutTests_fast_dom_HTMLLinkElement_link-onerror-" |
- "stylesheet-with-": "co19_dom_link-", |
- "tests_co19_src_LayoutTests_fast_dom_": "co19_dom", |
- "tests_co19_src_LayoutTests_fast_canvas_webgl": "co19_canvas_webgl", |
- "tests_co19_src_LibTest_core_AbstractClassInstantiationError_" |
- "AbstractClassInstantiationError_": "co19_abstract_class_", |
- "tests_co19_src_LibTest_core_IntegerDivisionByZeroException_" |
- "IntegerDivisionByZeroException_": "co19_division_by_zero", |
- "tests_co19_src_WebPlatformTest_html_dom_documents_dom-tree-accessors_": |
- "co19_dom_accessors_", |
- "tests_co19_src_WebPlatformTest_html_semantics_embedded-content_" |
- "media-elements_": "co19_media_elements", |
- "tests_co19_src_WebPlatformTest_html_semantics_": "co19_semantics_", |
- |
- "tests_co19_src_WebPlatformTest_html-templates_additions-to-" |
- "the-steps-to-clone-a-node_": "co19_htmltemplates_clone_", |
- "tests_co19_src_WebPlatformTest_html-templates_definitions_" |
- "template-contents-owner": "co19_htmltemplates_contents", |
- "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" |
- "templates_additions-to-": "co19_htmltemplates_add_", |
- "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" |
- "templates_appending-to-a-template_": "co19_htmltemplates_append_", |
- "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" |
- "templates_clearing-the-stack-back-to-a-given-context_": |
- "co19_htmltemplates_clearstack_", |
- "tests_co19_src_WebPlatformTest_html-templates_parsing-html-" |
- "templates_creating-an-element-for-the-token_": |
- "co19_htmltemplates_create_", |
- "tests_co19_src_WebPlatformTest_html-templates_template-element" |
- "_template-": "co19_htmltemplates_element-", |
- "tests_co19_src_WebPlatformTest_html-templates_": "co19_htmltemplate_", |
- |
- "tests_co19_src_WebPlatformTest_shadow-dom_shadow-trees_": |
- "co19_shadow-trees_", |
- "tests_co19_src_WebPlatformTest_shadow-dom_elements-and-dom-objects_": |
- "co19_shadowdom_", |
- "tests_co19_src_WebPlatformTest_shadow-dom_html-elements-in-" |
- "shadow-trees_": "co19_shadow_html_", |
- "tests_co19_src_WebPlatformTest_html_webappapis_system-state-and-" |
- "capabilities_the-navigator-object": "co19_webappapis_navigator_", |
- "tests_co19_src_WebPlatformTest_DOMEvents_approved_": "co19_dom_approved_" |
- }; |
- |
- // Some tests are already in [build_dir]/generated_tests. |
- String GEN_TESTS = 'generated_tests/'; |
- if (path.contains(GEN_TESTS)) { |
- int index = path.indexOf(GEN_TESTS) + GEN_TESTS.length; |
- path = 'multitest/${path.substring(index)}'; |
- } |
- path = path.replaceAll('/', '_'); |
- final int WINDOWS_SHORTEN_PATH_LIMIT = 58; |
- if (Platform.operatingSystem == 'windows' && |
- path.length > WINDOWS_SHORTEN_PATH_LIMIT) { |
- for (var key in PATH_REPLACEMENTS.keys) { |
- if (path.startsWith(key)) { |
- path = path.replaceFirst(key, PATH_REPLACEMENTS[key]); |
- break; |
- } |
- } |
- } |
- return path; |
- } |
-} |
- |
- |
-class SummaryReport { |
- static int total = 0; |
- static int skipped = 0; |
- static int skippedByDesign = 0; |
- static int noCrash = 0; |
- static int flakyCrash = 0; |
- static int pass = 0; |
- static int failOk = 0; |
- static int fail = 0; |
- static int crash = 0; |
- static int timeout = 0; |
- static int compileErrorSkip = 0; |
- |
- static List<TestCase> nonStandardTestCases = []; |
- |
- static void add(TestCase testCase) { |
- var expectations = testCase.expectedOutcomes; |
- |
- bool containsFail = expectations.any( |
- (expectation) => expectation.canBeOutcomeOf(Expectation.FAIL)); |
- bool containsPass = expectations.contains(Expectation.PASS); |
- bool containsSkip = expectations.contains(Expectation.SKIP); |
- bool containsSkipByDesign = |
- expectations.contains(Expectation.SKIP_BY_DESIGN); |
- bool containsCrash = expectations.contains(Expectation.CRASH); |
- bool containsOK = expectations.contains(Expectation.OK); |
- bool containsSlow = expectations.contains(Expectation.SLOW); |
- bool containsTimeout = expectations.contains(Expectation.TIMEOUT); |
- |
- ++total; |
- if (containsSkip) { |
- ++skipped; |
- } else if (containsSkipByDesign) { |
- ++skipped; |
- ++skippedByDesign; |
- } else { |
- // We don't do if-else below because the buckets should be exclusive. |
- // We keep a count around to guarantee that |
- int markers = 0; |
- |
- // Counts the number of flaky tests. |
- if (containsFail && containsPass && !containsCrash && !containsOK) { |
- ++noCrash; |
- ++markers; |
- } |
- if (containsCrash && !containsOK && expectations.length > 1) { |
- ++flakyCrash; |
- ++markers; |
- } |
- if ((containsPass && expectations.length == 1) || |
- (containsPass && containsSlow && expectations.length == 2)) { |
- ++pass; |
- ++markers; |
- } |
- if (containsFail && containsOK) { |
- ++failOk; |
- ++markers; |
- } |
- if ((containsFail && expectations.length == 1) || |
- (containsFail && containsSlow && expectations.length == 2)) { |
- ++fail; |
- ++markers; |
- } |
- if ((containsCrash && expectations.length == 1) || |
- (containsCrash && containsSlow && expectations.length == 2)) { |
- ++crash; |
- ++markers; |
- } |
- if (containsTimeout && expectations.length == 1) { |
- ++timeout; |
- ++markers; |
- } |
- if (markers != 1) { |
- nonStandardTestCases.add(testCase); |
- } |
- } |
- } |
- |
- static void addCompileErrorSkipTest() { |
- total++; |
- compileErrorSkip++; |
- } |
- |
- static void printReport() { |
- if (total == 0) return; |
- var bogus = nonStandardTestCases.length; |
- String report = """Total: $total tests |
- * $skipped tests will be skipped ($skippedByDesign skipped by design) |
- * $noCrash tests are expected to be flaky but not crash |
- * $flakyCrash tests are expected to flaky crash |
- * $pass tests are expected to pass |
- * $failOk tests are expected to fail that we won't fix |
- * $fail tests are expected to fail that we should fix |
- * $crash tests are expected to crash that we should fix |
- * $timeout tests are allowed to timeout |
- * $compileErrorSkip tests are skipped on browsers due to compile-time error |
- * $bogus could not be categorized or are in multiple categories |
-"""; |
- print(report); |
- } |
-} |