diff --git a/pkg/dev_compiler/test/sourcemap/testing.json b/pkg/dev_compiler/test/sourcemap/testing.json index 18035bdcffa..3194fc53401 100644 --- a/pkg/dev_compiler/test/sourcemap/testing.json +++ b/pkg/dev_compiler/test/sourcemap/testing.json @@ -8,7 +8,7 @@ "name": "sourcemaps", "kind": "Chain", "source": "sourcemaps_suite.dart", - "path": "testfiles", + "root": "testfiles", "status": "sourcemaps.status", "pattern": [ "\\.dart$", @@ -21,7 +21,7 @@ "name": "stacktrace", "kind": "Chain", "source": "stacktrace_suite.dart", - "path": "stacktrace_testfiles", + "root": "stacktrace_testfiles", "status": "stacktrace.status", "pattern": [ "\\.dart$" diff --git a/pkg/front_end/test/dartdoctest_suite.dart b/pkg/front_end/test/dartdoctest_suite.dart index 229116aabe2..b6ba86f3738 100644 --- a/pkg/front_end/test/dartdoctest_suite.dart +++ b/pkg/front_end/test/dartdoctest_suite.dart @@ -29,12 +29,15 @@ class Context extends ChainContext { ]; @override - Stream list(Chain suite) async* { - await for (TestDescription entry in super.list(suite)) { + Future> list(Chain suite) async { + List result = []; + for (TestDescription entry in await super.list(suite)) { List tests = await dartDocTest.extractTestsFromUri(entry.uri); if (tests.isEmpty) continue; - yield new DartDocTestTestDescription(entry.shortName, entry.uri, tests); + result.add( + new DartDocTestTestDescription(entry.shortName, entry.uri, tests)); } + return result; } DartDocTest dartDocTest = new DartDocTest(); diff --git a/pkg/front_end/test/fasta/messages_suite.dart b/pkg/front_end/test/fasta/messages_suite.dart index 0cb17fcbf34..970e0f02ae1 100644 --- a/pkg/front_end/test/fasta/messages_suite.dart +++ b/pkg/front_end/test/fasta/messages_suite.dart @@ -130,8 +130,9 @@ class MessageTestSuite extends ChainContext { /// failure by the [Validate] step that can be suppressed via the status /// file. @override - Stream list(Chain suite) async* { - Uri uri = suite.uri.resolve("messages.yaml"); + Future> list(Chain suite) { + List result = []; + Uri uri = suite.root.resolve("messages.yaml"); File file = new File.fromUri(uri); String fileContent = file.readAsStringSync(); YamlMap messages = loadYamlNode(fileContent, sourceUrl: uri) as YamlMap; @@ -425,81 +426,81 @@ class MessageTestSuite extends ChainContext { if (!fastOnly) { for (Example example in examples) { - yield createDescription(example.name, example, null); + result.add(createDescription(example.name, example, null)); } // "Wrap" example as a part. for (Example example in examples) { - yield createDescription( + result.add(createDescription( "part_wrapped_${example.name}", new PartWrapExample("part_wrapped_${example.name}", name, exampleAllowMoreCodes, example), - null); + null)); } } - yield createDescription( + result.add(createDescription( "knownKeys", null, unknownKeys.isNotEmpty ? "Unknown keys: ${unknownKeys.join(' ')}." - : null); + : null)); - yield createDescription( + result.add(createDescription( 'hasPublishedDocs', null, badHasPublishedDocsValue.isNotEmpty ? "Bad hasPublishedDocs value (only 'true' supported) in:" " ${badHasPublishedDocsValue.join(', ')}" - : null); + : null)); - yield createDescription( + result.add(createDescription( "severity", null, badSeverity != null ? "Unknown severity: '${badSeverity.value}'." : null, - location: badSeverity?.span.start); + location: badSeverity?.span.start)); - yield createDescription( + result.add(createDescription( "unnecessarySeverity", null, unnecessarySeverity != null ? "The 'ERROR' severity is the default and not necessary." : null, - location: unnecessarySeverity?.span.start); + location: unnecessarySeverity?.span.start)); - yield createDescription( + result.add(createDescription( "spelling", null, spellingMessages != null ? spellingMessages.join("\n") + spellingPostMessage - : null); + : null)); bool exampleAndAnalyzerCodeRequired = severity != Severity.context && severity != Severity.internalProblem && severity != Severity.ignored; - yield createDescription( + result.add(createDescription( "externalExample", null, exampleAndAnalyzerCodeRequired && externalTest != null && - !(new File.fromUri(suite.uri.resolve(externalTest)) + !(new File.fromUri(suite.root.resolve(externalTest)) .existsSync()) ? "Given external example for $name points to a nonexisting file " - "(${suite.uri.resolve(externalTest)})." - : null); + "(${suite.root.resolve(externalTest)})." + : null)); - yield createDescription( + result.add(createDescription( "example", null, exampleAndAnalyzerCodeRequired && examples.isEmpty && externalTest == null ? "No example for $name, please add at least one example." - : null); + : null)); - yield createDescription( + result.add(createDescription( "analyzerCode", null, exampleAndAnalyzerCodeRequired && @@ -510,8 +511,9 @@ class MessageTestSuite extends ChainContext { " /dart-sdk/bin/dartanalyzer --format=machine" " on an example to find the code." " The code is printed just before the file name." - : null); + : null)); } + return Future.value(result); } String formatProblems( diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart index caab5b1dee8..3cf773eaddd 100644 --- a/pkg/front_end/test/fasta/testing/suite.dart +++ b/pkg/front_end/test/fasta/testing/suite.dart @@ -491,7 +491,7 @@ class FastaContext extends ChainContext with MatchContext { platformBinaries = '$platformBinaries/'; } return new Future.value(new FastaContext( - suite.uri, + suite.root, vm, platformBinaries == null ? computePlatformBinariesLocation(forceBuildDir: true) diff --git a/pkg/front_end/test/fasta/textual_outline_suite.dart b/pkg/front_end/test/fasta/textual_outline_suite.dart index 4bc6da1a79c..bca7cf6882b 100644 --- a/pkg/front_end/test/fasta/textual_outline_suite.dart +++ b/pkg/front_end/test/fasta/textual_outline_suite.dart @@ -54,7 +54,7 @@ const List> EXPECTATIONS = [ ]; Future createContext(Chain suite, Map environment) { - return new Future.value(new Context(suite.uri, environment)); + return new Future.value(new Context(suite.root, environment)); } void main([List arguments = const []]) => internalMain( diff --git a/pkg/front_end/test/incremental_suite.dart b/pkg/front_end/test/incremental_suite.dart index 41e6474504f..cadc4b6f20e 100644 --- a/pkg/front_end/test/incremental_suite.dart +++ b/pkg/front_end/test/incremental_suite.dart @@ -511,17 +511,19 @@ class Context extends ChainContext { Context(this.updateExpectations, this.breakBetween, this.skipTests); @override - Stream list(Chain suite) { - if (skipTests.isEmpty) return super.list(suite); - return filterSkipped(super.list(suite)); + Future> list(Chain suite) async { + if (skipTests.isEmpty) return await super.list(suite); + return filterSkipped(await super.list(suite)); } - Stream filterSkipped(Stream all) async* { - await for (TestDescription testDescription in all) { + List filterSkipped(List all) { + List result = []; + for (TestDescription testDescription in all) { if (!skipTests.contains(testDescription.shortName)) { - yield testDescription; + result.add(testDescription); } } + return result; } @override diff --git a/pkg/front_end/test/lint_suite.dart b/pkg/front_end/test/lint_suite.dart index 7a458a27226..0d9b183b2a7 100644 --- a/pkg/front_end/test/lint_suite.dart +++ b/pkg/front_end/test/lint_suite.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'dart:io' show Directory, File, FileSystemEntity; +import 'dart:io' show File; import 'dart:typed_data' show Uint8List; import 'package:_fe_analyzer_shared/src/parser/listener.dart' show Listener; @@ -23,7 +23,7 @@ import 'package:testing/testing.dart' show Chain, ChainContext, Result, Step, TestDescription; import 'fasta/suite_utils.dart'; -import 'testing_utils.dart' show checkEnvironment, getGitFiles; +import 'testing_utils.dart' show checkEnvironment, filterList; void main([List arguments = const []]) => internalMain(createContext, arguments: arguments, @@ -82,55 +82,43 @@ class Context extends ChainContext { ]; @override - Stream list(Chain suite) async* { - late Set gitFiles; - if (onlyInGit) { - gitFiles = await getGitFiles(suite.uri); - } + Future> list(Chain suite) async { + String rootString = "${suite.root}"; + Uri apiUnstableUri = + Uri.base.resolve("pkg/front_end/lib/src/api_unstable/"); + String apiUnstableString = apiUnstableUri.toString(); - Directory testRoot = new Directory.fromUri(suite.uri); - if (await testRoot.exists()) { - Stream files = - testRoot.list(recursive: true, followLinks: false); - await for (FileSystemEntity entity in files) { - if (entity is! File) continue; - String path = entity.uri.path; - if (suite.exclude.any((RegExp r) => path.contains(r))) continue; - if (suite.pattern.any((RegExp r) => path.contains(r))) { - if (onlyInGit && !gitFiles.contains(entity.uri)) continue; - Uri root = suite.uri; - String baseName = "${entity.uri}".substring("$root".length); - baseName = baseName.substring(0, baseName.length - ".dart".length); - LintTestCache cache = new LintTestCache(); + List result = []; + for (TestDescription description + in await filterList(suite, onlyInGit, await super.list(suite))) { + String baseName = "${description.uri}".substring(rootString.length); + baseName = baseName.substring(0, baseName.length - ".dart".length); + LintTestCache cache = new LintTestCache(); - yield new LintTestDescription( - "$baseName/ExplicitType", - entity.uri, - cache, - new ExplicitTypeLintListener(), - ); + result.add(new LintTestDescription( + "$baseName/ExplicitType", + description.uri, + cache, + new ExplicitTypeLintListener(), + )); - yield new LintTestDescription( - "$baseName/ImportsTwice", - entity.uri, - cache, - new ImportsTwiceLintListener(), - ); + result.add(new LintTestDescription( + "$baseName/ImportsTwice", + description.uri, + cache, + new ImportsTwiceLintListener(), + )); - String apiUnstableUri = "pkg/front_end/lib/src/api_unstable/"; - if (!entity.uri.toString().contains(apiUnstableUri.toString())) { - yield new LintTestDescription( - "$baseName/Exports", - entity.uri, - cache, - new ExportsLintListener(), - ); - } - } + if (!description.uri.toString().startsWith(apiUnstableString)) { + result.add(new LintTestDescription( + "$baseName/Exports", + description.uri, + cache, + new ExportsLintListener(), + )); } - } else { - throw "${suite.uri} isn't a directory"; } + return result; } } diff --git a/pkg/front_end/test/spelling_test_base.dart b/pkg/front_end/test/spelling_test_base.dart index 7da01a41e42..bdd9874934a 100644 --- a/pkg/front_end/test/spelling_test_base.dart +++ b/pkg/front_end/test/spelling_test_base.dart @@ -47,8 +47,8 @@ abstract class SpellContext extends ChainContext { Set reportedWordsDenylisted = {}; @override - Stream list(Chain suite) { - return filterList(suite, onlyInGit, super.list(suite)); + Future> list(Chain suite) async { + return filterList(suite, onlyInGit, await super.list(suite)); } @override diff --git a/pkg/front_end/test/spelling_test_external_targets.dart b/pkg/front_end/test/spelling_test_external_targets.dart index c2eef74d7da..c00e62e4dc9 100644 --- a/pkg/front_end/test/spelling_test_external_targets.dart +++ b/pkg/front_end/test/spelling_test_external_targets.dart @@ -2,10 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'dart:io' show Directory, File, FileSystemEntity; - -import 'package:testing/testing.dart' - show Chain, FileBasedTestDescription, TestDescription, runMe; +import 'package:testing/testing.dart' show Chain, runMe; import 'spelling_test_base.dart' show SpellContext; @@ -40,25 +37,4 @@ class SpellContextExternal extends SpellContext { @override String get repoRelativeSuitePath => "pkg/front_end/test/spelling_test_external_targets.dart"; - - @override - Stream list(Chain suite) async* { - for (String subdir in const ["pkg/", "sdk/"]) { - Directory testRoot = new Directory.fromUri(suite.uri.resolve(subdir)); - if (await testRoot.exists()) { - Stream files = - testRoot.list(recursive: true, followLinks: false); - await for (FileSystemEntity entity in files) { - if (entity is! File) continue; - String path = entity.uri.path; - if (suite.exclude.any((RegExp r) => path.contains(r))) continue; - if (suite.pattern.any((RegExp r) => path.contains(r))) { - yield new FileBasedTestDescription(suite.uri, entity); - } - } - } else { - throw "${suite.uri} isn't a directory"; - } - } - } } diff --git a/pkg/front_end/test/testing_utils.dart b/pkg/front_end/test/testing_utils.dart index f4f88d15599..d214743bf3b 100644 --- a/pkg/front_end/test/testing_utils.dart +++ b/pkg/front_end/test/testing_utils.dart @@ -6,18 +6,22 @@ import 'dart:io' show Directory, Process, ProcessResult; import 'package:testing/testing.dart' show Chain, TestDescription; -Stream filterList( - Chain suite, bool onlyInGit, Stream base) async* { - Set? gitFiles; +Future> filterList( + Chain suite, bool onlyInGit, List base) async { + Set gitFiles = {}; if (onlyInGit) { - gitFiles = await getGitFiles(suite.uri); + for (Uri subRoot in suite.subRoots) { + gitFiles.addAll(await getGitFiles(subRoot)); + } } - await for (TestDescription description in base) { - if (onlyInGit && !gitFiles!.contains(description.uri)) { + List result = []; + for (TestDescription description in base) { + if (onlyInGit && !gitFiles.contains(description.uri)) { continue; } - yield description; + result.add(description); } + return result; } Future> getGitFiles(Uri uri) async { diff --git a/pkg/front_end/testing.json b/pkg/front_end/testing.json index 1b400e461ed..bed1bdff0bf 100644 --- a/pkg/front_end/testing.json +++ b/pkg/front_end/testing.json @@ -8,20 +8,18 @@ "name": "messages", "kind": "Chain", "source": "test/fasta/messages_suite.dart", - "path": "./", - "status": "messages.status", - "pattern": [], - "exclude": [] + "root": "./", + "status": "messages.status" }, { "name": "textual_outline", "kind": "Chain", "source": "test/fasta/textual_outline_suite.dart", - "path": "testcases/", + "root": "testcases/", "status": "testcases/textual_outline.status", - "pattern": [ - "\\.dart$", - "\\.crash_dart$" + "includeEndsWith": [ + ".dart", + ".crash_dart" ], "exclude": [ "/testcases/.*_part[0-9]*\\.dart$", @@ -34,11 +32,11 @@ "name": "outline", "kind": "Chain", "source": "test/fasta/outline_suite.dart", - "path": "testcases/", + "root": "testcases/", "status": "testcases/outline.status", - "pattern": [ - "\\.dart$", - "\\.crash_dart$" + "includeEndsWith": [ + ".dart", + ".crash_dart" ], "exclude": [ "/testcases/.*_part[0-9]*\\.dart$", @@ -52,11 +50,11 @@ "name": "strong", "kind": "Chain", "source": "test/fasta/strong_suite.dart", - "path": "testcases/", + "root": "testcases/", "status": "testcases/strong.status", - "pattern": [ - "\\.dart$", - "\\.crash_dart$" + "includeEndsWith": [ + ".dart", + ".crash_dart" ], "exclude": [ "/testcases/.*_part[0-9]*\\.dart$", @@ -70,11 +68,11 @@ "name": "modular", "kind": "Chain", "source": "test/fasta/modular_suite.dart", - "path": "testcases/", + "root": "testcases/", "status": "testcases/modular.status", - "pattern": [ - "\\.dart$", - "\\.crash_dart$" + "includeEndsWith": [ + ".dart", + ".crash_dart" ], "exclude": [ "/testcases/.*_part[0-9]*\\.dart$", @@ -88,11 +86,11 @@ "name": "weak", "kind": "Chain", "source": "test/fasta/weak_suite.dart", - "path": "testcases/", + "root": "testcases/", "status": "testcases/weak.status", - "pattern": [ - "\\.dart$", - "\\.crash_dart$" + "includeEndsWith": [ + ".dart", + ".crash_dart" ], "exclude": [ "/testcases/.*_part[0-9]*\\.dart$", @@ -106,40 +104,37 @@ "name": "incremental_dartino", "kind": "Chain", "source": "test/fasta/incremental_dartino_suite.dart", - "path": "testcases/dartino", + "root": "testcases/dartino", "status": "testcases/incremental_dartino.status", - "pattern": [ - "\\.incremental\\.yaml$" - ], - "exclude": [] + "includeEndsWith": [ + ".incremental.yaml" + ] }, { "name": "expression", "kind": "Chain", "source": "test/fasta/expression_suite.dart", - "path": "testcases/expression/", + "root": "testcases/expression/", "status": "testcases/expression.status", - "pattern": [ - "\\.expression\\.yaml$" - ], - "exclude": [] + "includeEndsWith": [ + ".expression.yaml" + ] }, { "name": "incremental", "kind": "Chain", "source": "test/incremental_suite.dart", - "path": "testcases/incremental/", + "root": "testcases/incremental/", "status": "testcases/incremental.status", - "pattern": [ - "\\.yaml$" - ], - "exclude": [] + "includeEndsWith": [ + ".yaml" + ] }, { "name": "incremental_bulk_compiler_smoke", "kind": "Chain", "source": "test/incremental_bulk_compiler_smoke_suite.dart", - "path": "../../tests/", + "root": "../../tests/", "status": "testcases/incremental_bulk_compiler_smoke.status", "pattern": [ "/language/accessor_conflict_export2_test\\.dart$", @@ -160,78 +155,78 @@ "/language/script1_negative_test\\.dart$", "/language/script2_negative_test\\.dart$", "/language/unbalanced_brace_test\\.dart$" - ], - "exclude": [] + ] }, { "name": "incremental_bulk_compiler_full", "kind": "Chain", "source": "test/incremental_bulk_compiler_full.dart", - "path": "../../tests/", + "root": "../../tests/language/", "status": "testcases/incremental_bulk_compiler_full.status", - "pattern": [ - "language/.*_test\\.dart$" - ], - "exclude": [] + "includeEndsWith": [ + "_test.dart" + ] }, { "name": "parser", "kind": "Chain", "source": "test/parser_suite.dart", - "path": "parser_testcases/", + "root": "parser_testcases/", "status": "parser_testcases/parser.status", - "pattern": [ - "\\.dart$", - "\\.crash_dart$" - ], - "exclude": [] + "includeEndsWith": [ + ".dart", + ".crash_dart" + ] }, { "name": "outline_extractor", "kind": "Chain", "source": "test/outline_extractor_suite.dart", - "path": "outline_extraction_testcases/", + "root": "outline_extraction_testcases/", "status": "outline_extraction_testcases/outline_extractor.status", - "pattern": [ - "main\\.dart$" - ], - "exclude": [] + "includeEndsWith": [ + "main.dart" + ] }, { "name": "parser_equivalence", "kind": "Chain", "source": "test/parser_equivalence_suite.dart", - "path": "parser_testcases/", + "root": "parser_testcases/", "status": "parser_testcases/parser_equivalence.status", - "pattern": [ - "\\.equivalence_info$" - ], - "exclude": [] + "includeEndsWith": [ + ".equivalence_info" + ] }, { "name": "parser_all", "kind": "Chain", "source": "test/parser_all_suite.dart", - "path": "../../", - "status": "parser_testcases/parser_all.status", - "pattern": [ - "pkg/front_end/.*\\.dart$", - "pkg/front_end/.*\\.crash_dart$", - "/tests/.*\\.dart$" + "root": "../../", + "subRoots": [ + "pkg/front_end/", + "tests/" ], - "exclude": [] + "status": "parser_testcases/parser_all.status", + "includeEndsWith": [ + ".dart", + ".crash_dart" + ] }, { "name": "lint", "kind": "Chain", "source": "test/lint_suite.dart", - "path": "../", + "root": "../", + "subRoots": [ + "_fe_analyzer_shared/lib/", + "kernel/lib/", + "front_end/lib/", + "frontend_server/" + ], "status": "test/lint_test.status", - "pattern": [ - "_fe_analyzer_shared/lib/.*\\.dart$", - "kernel/lib/.*\\.dart$", - "front_end/lib/.*\\.dart$", - "frontend_server/.*\\.dart$" + "includeEndsWith": [ + ".dart" ], "exclude": [ "kernel/lib/transformations/.*\\.dart$", @@ -243,28 +238,33 @@ "name": "dartdoctest", "kind": "Chain", "source": "test/dartdoctest_suite.dart", - "path": "../", - "status": "test/dartdoctest_suite.status", - "pattern": [ - "_fe_analyzer_shared/.*\\.dart$", - "kernel/.*\\.dart$", - "front_end/.*\\.dart$" + "root": "../", + "subRoots": [ + "_fe_analyzer_shared/", + "kernel/", + "front_end/" ], - "exclude": [] + "includeEndsWith": [ + ".dart" + ], + "status": "test/dartdoctest_suite.status" }, { "name": "spelling_test_src", "kind": "Chain", "source": "test/spelling_test_src_suite.dart", - "path": "../", + "root": "../", + "subRoots": [ + "_fe_analyzer_shared/lib/", + "front_end/lib/", + "kernel/lib/", + "kernel/bin/", + "frontend_server/lib/", + "frontend_server/bin/" + ], "status": "test/spelling_test.status", - "pattern": [ - "_fe_analyzer_shared/lib/.*\\.dart$", - "front_end/lib/.*\\.dart$", - "kernel/lib/.*\\.dart$", - "kernel/bin/.*\\.dart$", - "frontend_server/lib/.*\\.dart$", - "frontend_server/bin/.*\\.dart$" + "includeEndsWith": [ + ".dart" ], "exclude": [ "_fe_analyzer_shared/lib/src/messages/codes_generated\\.dart$", @@ -275,10 +275,10 @@ "name": "spelling_test_not_src", "kind": "Chain", "source": "test/spelling_test_not_src_suite.dart", - "path": ".", + "root": ".", "status": "test/spelling_test.status", - "pattern": [ - ".*\\.dart$" + "includeEndsWith": [ + ".dart" ], "exclude": [ "lib/", @@ -326,62 +326,14 @@ "name": "spelling_test_external_targets", "kind": "Chain", "source": "test/spelling_test_external_targets.dart", - "path": "../../", - "status": "test/spelling_test.status", - "pattern": [ - ".*\\.dart$" + "root": "../../", + "subRoots": [ + "pkg/", + "sdk/" ], - "exclude": [] - }, - { - "note": "Tests dart2js fully, excluding browser-only tests.", - "name": "dart2js", - "kind": "test_dart", - "arch": "x64", - "mode": "release", - "common": "--time -pcolor --report -ax64 -mrelease --write-result-log", - "command-lines": [ - "--checked dart2js", - "-cdart2js -rd8 --exclude-suite=observatory_ui", - "-cdart2js -rd8 web" - ] - }, - { - "note": "Minimal testing of Fasta.", - "name": "fasta_min", - "kind": "test_dart", - "arch": "x64", - "mode": "release", - "common": "--time -pcolor --report -ax64 -mrelease --write-result-log", - "command-lines": [ - "-t240 pkg/(kernel|front_end|fasta) --checked", - "-t240 web/analyze_test", - "-cdartk -rvm" - ] - }, - { - "note": "Tests Fasta fully, including the above dart2js tests.", - "name": "fasta_max", - "kind": "test_dart", - "arch": "x64", - "mode": "release", - "common": "--time -pcolor --report -ax64 -mrelease --write-result-log", - "command-lines": [ - "-t240 --checked pkg/(kernel|front_end|fasta) dart2js", - "-cdartk -rvm", - "-cdart2js -rd8 --exclude-suite=observatory_ui", - "-cdart2js -rd8 web" - ] - }, - { - "note": "Runs dart2js in a mode where it invokes Fasta.", - "name": "dart2js_with_kernel", - "kind": "test_dart", - "arch": "x64", - "mode": "release", - "common": "--time -pcolor --report -ax64 -mrelease --write-result-log", - "command-lines": [ - "-cdart2js -rd8 --use-sdk --minified language corelib" + "status": "test/spelling_test.status", + "includeEndsWith": [ + ".dart" ] } ], diff --git a/pkg/testing/lib/src/chain.dart b/pkg/testing/lib/src/chain.dart index 99926f460e1..052d61517b6 100644 --- a/pkg/testing/lib/src/chain.dart +++ b/pkg/testing/lib/src/chain.dart @@ -4,8 +4,6 @@ library testing.chain; -import 'dart:async' show Future, Stream; - import 'dart:convert' show json, JsonEncoder; import 'dart:io' show Directory, File, FileSystemEntity, exitCode; @@ -31,27 +29,50 @@ typedef CreateContext = Future Function( class Chain extends Suite { final Uri source; - final Uri uri; + final Uri root; + + final List subRoots; + + final List includeEndsWith; final List pattern; final List exclude; - Chain(String name, String kind, this.source, this.uri, Uri statusFile, - this.pattern, this.exclude) + Chain(String name, String kind, this.source, this.root, this.subRoots, + Uri statusFile, this.includeEndsWith, this.pattern, this.exclude) : super(name, kind, statusFile); factory Chain.fromJsonMap(Uri base, Map json, String name, String kind) { Uri source = base.resolve(json["source"]); - String path = json["path"]; - if (!path.endsWith("/")) { - path += "/"; + String root = json["root"]; + if (!root.endsWith("/")) { + root += "/"; + } + Uri rootUri = base.resolve(root); + List subRoots = []; + List? subRootsList = json["subRoots"]; + if (subRootsList != null) { + for (String subRoot in subRootsList) { + if (!subRoot.endsWith("/")) { + subRoot += "/"; + } + subRoots.add(rootUri.resolve(subRoot)); + } + } else { + subRoots.add(rootUri); } - Uri uri = base.resolve(path); Uri statusFile = base.resolve(json["status"]); - List pattern = [for (final p in json['pattern']) RegExp(p)]; - List exclude = [for (final e in json['exclude']) RegExp(e)]; - return Chain(name, kind, source, uri, statusFile, pattern, exclude); + List includeEndsWith = + List.from(json['includeEndsWith'] ?? const []); + List pattern = [ + for (final p in json['pattern'] ?? const []) new RegExp(p) + ]; + List exclude = [ + for (final e in json['exclude'] ?? const []) new RegExp(e) + ]; + return Chain(name, kind, source, rootUri, subRoots, statusFile, + includeEndsWith, pattern, exclude); } void writeImportOn(StringSink sink) { @@ -78,9 +99,10 @@ class Chain extends Suite { "name": name, "kind": kind, "source": "$source", - "path": "$uri", + "root": "$root", "status": "$statusFile", "pattern": [for (final r in pattern) r.pattern], + "includeEndsWith": includeEndsWith, "exclude": [for (final r in exclude) r.pattern], }; } @@ -106,8 +128,7 @@ abstract class ChainContext { .toList(); TestExpectations expectations = readTestExpectations( [suite.statusFile!.toFilePath()], expectationSet); - Stream stream = list(suite); - List descriptions = await stream.toList(); + List descriptions = await list(suite); descriptions.sort(); if (shards > 1) { List shardDescriptions = []; @@ -242,22 +263,35 @@ abstract class ChainContext { await postRun(); } - Stream list(Chain suite) async* { - Directory testRoot = Directory.fromUri(suite.uri); - if (await testRoot.exists()) { - Stream files = - testRoot.list(recursive: true, followLinks: false); - await for (FileSystemEntity entity in files) { - if (entity is! File) continue; - String path = entity.uri.path; - if (suite.exclude.any((RegExp r) => path.contains(r))) continue; - if (suite.pattern.any((RegExp r) => path.contains(r))) { - yield FileBasedTestDescription(suite.uri, entity); + Future> list(Chain suite) async { + List result = []; + for (Uri subRoot in suite.subRoots) { + Directory testRoot = Directory.fromUri(subRoot); + if (testRoot.existsSync()) { + for (FileSystemEntity entity + in testRoot.listSync(recursive: true, followLinks: false)) { + if (entity is! File) continue; + // Use `.uri.path` instead of just `.path` to ensure forward slashes. + String path = entity.uri.path; + + if (suite.exclude.any((RegExp r) => path.contains(r))) continue; + + bool include = false; + if (suite.includeEndsWith.any((String end) => path.endsWith(end))) { + include = true; + } + if (!include && suite.pattern.any((RegExp r) => path.contains(r))) { + include = true; + } + if (include) { + result.add(new FileBasedTestDescription(suite.root, entity)); + } } + } else { + throw "$subRoot isn't a directory"; } - } else { - throw "${suite.uri} isn't a directory"; } + return result; } Set processExpectedOutcomes( diff --git a/pkg/testing/lib/src/run.dart b/pkg/testing/lib/src/run.dart index 4b81b6863db..bd5e66efe5e 100644 --- a/pkg/testing/lib/src/run.dart +++ b/pkg/testing/lib/src/run.dart @@ -18,17 +18,14 @@ import 'error_handling.dart' show withErrorHandling; import 'chain.dart' show CreateContext; -import '../testing.dart' - show Chain, ChainContext, FileBasedTestDescription, listTests; +import '../testing.dart' show Chain, ChainContext; import 'analyze.dart' show Analyze; import 'log.dart' show enableVerboseOutput, isVerbose, Logger, splitLines, StdoutLogger; -import 'suite.dart' show Dart, Suite; - -import 'test_dart.dart' show TestDart; +import 'suite.dart' show Suite; import 'zone_helper.dart' show acknowledgeControlMessages; @@ -168,30 +165,12 @@ class SuiteRunner { StringBuffer chain = StringBuffer(); bool hasRunnableTests = false; - await for (FileBasedTestDescription description in listDescriptions()) { - hasRunnableTests = true; - description.writeImportOn(imports); - description.writeClosureOn(dart); - } - await for (Chain suite in listChainSuites()) { hasRunnableTests = true; suite.writeImportOn(imports); suite.writeClosureOn(chain); } - bool isFirstTestDartSuite = true; - for (TestDart suite in listTestDartSuites()) { - if (shouldRunSuite(suite)) { - hasRunnableTests = true; - if (isFirstTestDartSuite) { - suite.writeFirstImportOn(imports); - } - isFirstTestDartSuite = false; - suite.writeRunCommandOn(chain); - } - } - if (!hasRunnableTests) return null; return """ @@ -234,22 +213,6 @@ Future main() async { return hasAnalyzerSuites; } - Stream listDescriptions() async* { - for (Dart suite in suites.whereType()) { - await for (FileBasedTestDescription description - in listTests([suite.uri], pattern: "")) { - testUris.add((await Isolate.resolvePackageUri(description.uri))!); - if (shouldRunSuite(suite)) { - String path = description.file.uri.path; - if (suite.exclude.any((RegExp r) => path.contains(r))) continue; - if (suite.pattern.any((RegExp r) => path.contains(r))) { - yield description; - } - } - } - } - } - Stream listChainSuites() async* { for (Chain suite in suites.whereType()) { testUris.add((await Isolate.resolvePackageUri(suite.source))!); @@ -259,10 +222,6 @@ Future main() async { } } - Iterable listTestDartSuites() { - return suites.whereType(); - } - Iterable listAnalyzerSuites() { return suites.whereType(); } diff --git a/pkg/testing/lib/src/suite.dart b/pkg/testing/lib/src/suite.dart index 471b91f0ce9..66d9e79eabf 100644 --- a/pkg/testing/lib/src/suite.dart +++ b/pkg/testing/lib/src/suite.dart @@ -6,8 +6,6 @@ library testing.suite; import 'chain.dart' show Chain; -import 'test_dart.dart' show TestDart; - /// Records the properties of a test suite. abstract class Suite { final String name; @@ -22,15 +20,9 @@ abstract class Suite { String kind = json["kind"].toLowerCase(); String name = json["name"]; switch (kind) { - case "dart": - return Dart.fromJsonMap(base, json, name); - case "chain": return Chain.fromJsonMap(base, json, name, kind); - case "test_dart": - return TestDart.fromJsonMap(base, json, name, kind); - default: throw "Suite '$name' has unknown kind '$kind'."; } @@ -39,52 +31,3 @@ abstract class Suite { @override String toString() => "Suite($name, $kind)"; } - -/// A suite of standalone tests. The tests are combined and run as one program. -/// -/// A standalone test is a test with a `main` method. The test is considered -/// successful if main doesn't throw an error (or if `main` returns a future, -/// that future completes without errors). -/// -/// The tests are combined by generating a Dart file which imports all the main -/// methods and calls them sequentially. -/// -/// Example JSON configuration: -/// -/// { -/// "name": "test", -/// "kind": "Dart", -/// # Root directory of tests in this suite. -/// "path": "test/", -/// # Files in `path` that match any of the following regular expressions -/// # are considered to be part of this suite. -/// "pattern": [ -/// "_test.dart$" -/// ], -/// # Except if they match any of the following regular expressions. -/// "exclude": [ -/// "/golden/" -/// ] -/// } -class Dart extends Suite { - final Uri uri; - - final List pattern; - - final List exclude; - - Dart(String name, this.uri, this.pattern, this.exclude) - : super(name, "dart", null); - - factory Dart.fromJsonMap(Uri base, Map json, String name) { - Uri uri = base.resolve(json["path"]); - List pattern = - List.from(json["pattern"].map((String p) => RegExp(p))); - List exclude = - List.from(json["exclude"].map((String p) => RegExp(p))); - return Dart(name, uri, pattern, exclude); - } - - @override - String toString() => "Dart($name, $uri, $pattern, $exclude)"; -} diff --git a/pkg/testing/lib/src/test_dart.dart b/pkg/testing/lib/src/test_dart.dart deleted file mode 100644 index 2b97a9dc263..00000000000 --- a/pkg/testing/lib/src/test_dart.dart +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library testing.test_dart; - -import 'dart:convert' show json; - -import 'dart:io' show Platform; - -import 'suite.dart' show Suite; - -/// A suite that runs test.dart. -class TestDart extends Suite { - final String common; - - final String processes; - - final List commandLines; - - TestDart(String name, this.common, this.processes, this.commandLines) - : super( - name, - "test_dart", - // This suite doesn't know what it's status file is because - // test.dart doesn't know. - null); - - factory TestDart.fromJsonMap(Uri base, Map json, String name, String kind) { - String common = json["common"] ?? ""; - String processes = json["processes"] ?? "-j${Platform.numberOfProcessors}"; - List commandLines = json["command-lines"] == null - ? List.from(json["command-lines"]) - : []; - return TestDart(name, common, processes, commandLines); - } - - void writeFirstImportOn(StringSink sink) { - sink.writeln("import 'dart:io' as io;"); - sink.writeln( - "import 'package:testing/src/stdio_process.dart' show StdioProcess;"); - } - - void writeRunCommandOn(StringSink sink) { - Uri dartVm; - if (Platform.isMacOS || Platform.isLinux) { - dartVm = Uri.base.resolve("tools/sdks/dart-sdk/bin/dart"); - } else if (Platform.isWindows) { - dartVm = Uri.base.resolve("tools/sdks/dart-sdk/bin/dart.exe"); - } else { - throw "Operating system not supported: ${Platform.operatingSystem}"; - } - List processedArguments = []; - processedArguments.add(Uri.base - .resolve("pkg/test_runner/bin/package_testing_support.dart") - .toFilePath()); - for (String commandLine in commandLines) { - String arguments = common; - arguments += " $processes"; - arguments += " $commandLine"; - processedArguments.add(arguments); - } - String executable = json.encode(dartVm.toFilePath()); - String arguments = json.encode(processedArguments); - sink.write(""" - { - print('Running $arguments'); - StdioProcess process = await StdioProcess.run($executable, $arguments, - suppressOutput: false, timeout: null); - if (process.exitCode != 0) { - print(process.output); - io.exitCode = 1; - } - } -"""); - } - - @override - String toString() { - return "TestDart($name, ${json.encode(common)}, ${json.encode(processes)}," - " ${json.encode(commandLines)})"; - } -}