diff --git a/pkg/testing/analysis_options.yaml b/pkg/testing/analysis_options.yaml index e306536c160..2295ff84b29 100644 --- a/pkg/testing/analysis_options.yaml +++ b/pkg/testing/analysis_options.yaml @@ -2,9 +2,25 @@ # 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. -include: package:lints/recommended.yaml - analyzer: errors: # Allow having TODOs in the code todo: ignore + +linter: + rules: + - collection_methods_unrelated_type + - curly_braces_in_flow_control_structures + - prefer_adjacent_string_concatenation + - unawaited_futures + - avoid_void_async + - recursive_getters + - avoid_empty_else + - empty_statements + - valid_regexps + - package_api_docs + - lines_longer_than_80_chars + - unrelated_type_equality_checks + - annotate_overrides + - always_declare_return_types + # - always_specify_types diff --git a/pkg/testing/bin/run_tests.dart b/pkg/testing/bin/run_tests.dart deleted file mode 100755 index 726598df856..00000000000 --- a/pkg/testing/bin/run_tests.dart +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env dart -c -// 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. - -import "package:testing/src/run_tests.dart" as run_tests; - -main(List arguments) => run_tests.main(arguments); diff --git a/pkg/testing/bin/testing.dart b/pkg/testing/bin/testing.dart index dd05e738519..a626a67f5a2 100644 --- a/pkg/testing/bin/testing.dart +++ b/pkg/testing/bin/testing.dart @@ -4,4 +4,4 @@ import "package:testing/src/run_tests.dart" as run_tests; -main(List arguments) => run_tests.main(arguments); +Future main(List arguments) => run_tests.main(arguments); diff --git a/pkg/testing/lib/dart_vm_suite.dart b/pkg/testing/lib/dart_vm_suite.dart deleted file mode 100644 index ec6fb890f8c..00000000000 --- a/pkg/testing/lib/dart_vm_suite.dart +++ /dev/null @@ -1,33 +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.dart_vm_suite; - -import 'testing.dart'; - -Future createContext( - Chain suite, Map environment) async { - return VmContext(); -} - -class VmContext extends ChainContext { - @override - final List steps = const [DartVmStep()]; -} - -class DartVmStep extends Step { - const DartVmStep(); - - @override - String get name => "Dart VM"; - - @override - Future> run( - FileBasedTestDescription input, VmContext context) async { - StdioProcess process = await StdioProcess.run("dart", [input.file.path]); - return process.toResult(); - } -} - -main(List arguments) => runMe(arguments, createContext); diff --git a/pkg/testing/lib/src/analyze.dart b/pkg/testing/lib/src/analyze.dart index 0ee62832654..4dde1fef16e 100644 --- a/pkg/testing/lib/src/analyze.dart +++ b/pkg/testing/lib/src/analyze.dart @@ -105,7 +105,7 @@ class AnalyzerDiagnostic { List parts = []; int start = 0; int index = line.indexOf(potentialSplitPattern); - addPart() { + void addPart() { parts.add(line .substring(start, index == -1 ? null : index) .replaceAllMapped(unescapePattern, (Match m) => m[1]!)); @@ -240,12 +240,13 @@ Future analyzeUris( Process process = await startDart( analyzer, const ["--batch"], dartArguments..remove("-c")); process.stdin.writeln(arguments.join(" ")); - process.stdin.close(); + await process.stdin.close(); bool hasOutput = false; Set seen = {}; - processAnalyzerOutput(Stream diagnostics) async { + Future processAnalyzerOutput( + Stream diagnostics) async { await for (AnalyzerDiagnostic diagnostic in diagnostics) { if (diagnostic.uri != null) { String path = toFilePath(diagnostic.uri!); diff --git a/pkg/testing/lib/src/chain.dart b/pkg/testing/lib/src/chain.dart index ccdc564fa9f..99926f460e1 100644 --- a/pkg/testing/lib/src/chain.dart +++ b/pkg/testing/lib/src/chain.dart @@ -14,8 +14,7 @@ import 'suite.dart' show Suite; import '../testing.dart' show FileBasedTestDescription, TestDescription; -import 'test_dart/status_file_parser.dart' - show readTestExpectations, TestExpectations; +import 'status_file_parser.dart' show readTestExpectations, TestExpectations; import 'zone_helper.dart' show runGuarded; @@ -105,8 +104,8 @@ abstract class ChainContext { .where((s) => s.endsWith('...')) .map((s) => s.substring(0, s.length - 3)) .toList(); - TestExpectations expectations = await readTestExpectations( - [suite.statusFile!.toFilePath()], {}, expectationSet); + TestExpectations expectations = readTestExpectations( + [suite.statusFile!.toFilePath()], expectationSet); Stream stream = list(suite); List descriptions = await stream.toList(); descriptions.sort(); @@ -240,7 +239,7 @@ abstract class ChainContext { print("${suite.name}/${description.shortName}: ${result.outcome}"); }); } - postRun(); + await postRun(); } Stream list(Chain suite) async* { @@ -360,10 +359,6 @@ class Result { logs.add(log); } - Result copyWithOutcome(Expectation outcome) { - return Result(output, outcome, error, trace: trace)..logs.addAll(logs); - } - Result copyWithOutput(O2 output) { return Result(output, outcome, error, trace: trace, diff --git a/pkg/testing/lib/src/discover.dart b/pkg/testing/lib/src/discover.dart index 2e39f51b0c5..6ec493a4078 100644 --- a/pkg/testing/lib/src/discover.dart +++ b/pkg/testing/lib/src/discover.dart @@ -12,8 +12,6 @@ import '../testing.dart' show FileBasedTestDescription; final Uri packageConfig = computePackageConfig(); -final Uri dartSdk = computeDartSdk(); - /// Common arguments when running a dart program. Returns a copy that can /// safely be modified by caller. List get dartArguments => @@ -65,24 +63,6 @@ Uri computePackageConfig() { return Uri.base.resolve(".dart_tool/package_config.json"); } -// TODO(eernst): Use `bool.hasEnvironment` below when possible; -// for now we use a dual `defaultValue` rewrite. -const _dartSdk = (String.fromEnvironment("DART_SDK", defaultValue: "1") == - String.fromEnvironment("DART_SDK", defaultValue: "2")) - ? String.fromEnvironment("DART_SDK") - : null; - -Uri computeDartSdk() { - String? dartSdkPath = Platform.environment["DART_SDK"] ?? _dartSdk; - if (dartSdkPath != null) { - return Uri.base.resolveUri(Uri.file(dartSdkPath)); - } else { - return Uri.base - .resolveUri(Uri.file(Platform.resolvedExecutable)) - .resolve("../"); - } -} - Future startDart(Uri program, [List? arguments, List? vmArguments]) { List allArguments = []; diff --git a/pkg/testing/lib/src/run.dart b/pkg/testing/lib/src/run.dart index ef0301a1f3d..4b81b6863db 100644 --- a/pkg/testing/lib/src/run.dart +++ b/pkg/testing/lib/src/run.dart @@ -133,7 +133,7 @@ Future runProgram(String program, Uri packages) async { await for (var _ in exitPort) { exitPort.close(); } - subscription.cancel(); + await subscription.cancel(); return error == null ? null : Future.error(error![0], StackTrace.fromString(error![1])); diff --git a/pkg/testing/lib/src/run_tests.dart b/pkg/testing/lib/src/run_tests.dart index 6dbf4242512..e9500d63836 100644 --- a/pkg/testing/lib/src/run_tests.dart +++ b/pkg/testing/lib/src/run_tests.dart @@ -71,7 +71,8 @@ class CommandLine { .map((String option) => option.substring(configPrefix.length)) .toList(); if (configurationPaths.length > 1) { - return fail("Only one --config option is supported"); + fail("Only one --config option is supported"); + return null; } String configurationPath; if (configurationPaths.length == 1) { @@ -94,17 +95,18 @@ class CommandLine { }).toList(); switch (candidates.length) { case 0: - return fail("Couldn't locate: '$configurationPath'."); + fail("Couldn't locate: '$configurationPath'."); + return null; case 1: configurationPath = candidates.single.path; break; default: - return fail( - "Usage: run_tests.dart [$configPrefix=configuration_file]\n" + fail("Usage: run_tests.dart [$configPrefix=configuration_file]\n" "Where configuration_file is one of:\n " "${candidates.map((file) => file.path).join('\n ')}"); + return null; } } } @@ -114,7 +116,8 @@ class CommandLine { Uri? configuration = await Isolate.resolvePackageUri(Uri.base.resolve(configurationPath)); if (configuration == null || !await File.fromUri(configuration).exists()) { - return fail("Couldn't locate: '$configurationPath'."); + fail("Couldn't locate: '$configurationPath'."); + return null; } return configuration; } @@ -134,38 +137,40 @@ class CommandLine { } } -fail(String message) { +void fail(String message) { print(message); io.exitCode = 1; return null; } -main(List arguments) => withErrorHandling(() async { - CommandLine cl = CommandLine.parse(arguments); - if (cl.verbose) { - enableVerboseOutput(); +Future main(List arguments) { + return withErrorHandling(() async { + CommandLine cl = CommandLine.parse(arguments); + if (cl.verbose) { + enableVerboseOutput(); + } + Map environment = cl.environment; + Uri? configuration = await cl.configuration; + if (configuration == null) return; + if (!isVerbose) { + print("Use --verbose to display more details."); + } + TestRoot root = await TestRoot.fromUri(configuration); + SuiteRunner runner = SuiteRunner( + root.suites, environment, cl.selectors, cl.selectedSuites, cl.skip); + String? program = await runner.generateDartProgram(); + bool hasAnalyzerSuites = await runner.analyze(root.packages); + Stopwatch sw = Stopwatch()..start(); + if (program == null) { + if (!hasAnalyzerSuites) { + fail("No tests configured."); } - Map environment = cl.environment; - Uri? configuration = await cl.configuration; - if (configuration == null) return; - if (!isVerbose) { - print("Use --verbose to display more details."); - } - TestRoot root = await TestRoot.fromUri(configuration); - SuiteRunner runner = SuiteRunner( - root.suites, environment, cl.selectors, cl.selectedSuites, cl.skip); - String? program = await runner.generateDartProgram(); - bool hasAnalyzerSuites = await runner.analyze(root.packages); - Stopwatch sw = Stopwatch()..start(); - if (program == null) { - if (!hasAnalyzerSuites) { - fail("No tests configured."); - } - } else { - await runProgram(program, root.packages); - } - print("Running tests took: ${sw.elapsed}."); - }); + } else { + await runProgram(program, root.packages); + } + print("Running tests took: ${sw.elapsed}."); + }); +} Future runTests(Map tests) => withErrorHandling(() async { diff --git a/pkg/testing/lib/src/status_file_parser.dart b/pkg/testing/lib/src/status_file_parser.dart new file mode 100644 index 00000000000..a3b2afc7e49 --- /dev/null +++ b/pkg/testing/lib/src/status_file_parser.dart @@ -0,0 +1,69 @@ +// Copyright (c) 2012, 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 test_dart_copy.status_file_parser; + +import "dart:io"; + +import 'expectation.dart' show Expectation, ExpectationSet; + +TestExpectations readTestExpectations( + List statusFilePaths, ExpectationSet expectationSet) { + TestExpectations testExpectations = TestExpectations(expectationSet); + for (String path in statusFilePaths) { + readTestExpectationsInto(testExpectations, path); + } + return testExpectations; +} + +void readTestExpectationsInto( + TestExpectations expectations, String statusFilePath) { + File file = File(statusFilePath); + for (String line in file.readAsLinesSync()) { + // Remove comments if any. + int index = line.indexOf("#"); + if (index >= 0) { + line = line.substring(0, index); + } + line = line.trim(); + if (line.isEmpty) continue; + + // Line should look lie "testName: status1, status2, etc". + List lineSplit = line.split(":"); + if (lineSplit.length != 2) { + throw "Unsupported line: '$line'"; + } + + String name = lineSplit[0]; + List allowedStatus = lineSplit[1].trim().split(","); + for (int i = 0; i < allowedStatus.length; i++) { + allowedStatus[i] = allowedStatus[i].trim(); + } + expectations.add(name, allowedStatus); + } +} + +class TestExpectations { + final ExpectationSet expectationSet; + final Map> _map = {}; + + TestExpectations(this.expectationSet); + + void add(String name, List allowedStatus) { + Set expectations = (_map[name] ??= {}); + for (String status in allowedStatus) { + expectations.add(expectationSet[status]); + } + } + + Set expectations(String filename) { + Set result = _map[filename] ?? {}; + // If no expectations were found the expectation is that the test + // passes. + if (result.isEmpty) { + result.add(Expectation.pass); + } + return result; + } +} diff --git a/pkg/testing/lib/src/test_dart/README.md b/pkg/testing/lib/src/test_dart/README.md deleted file mode 100644 index dfa0d1e5158..00000000000 --- a/pkg/testing/lib/src/test_dart/README.md +++ /dev/null @@ -1,7 +0,0 @@ - -Files in this directory are copied from -[test.dart](https://github.com/dart-lang/sdk/tree/master/tools/testing/dart). diff --git a/pkg/testing/lib/src/test_dart/path.dart b/pkg/testing/lib/src/test_dart/path.dart deleted file mode 100644 index 6899de156b4..00000000000 --- a/pkg/testing/lib/src/test_dart/path.dart +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright (c) 2012, 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 test_dart_copy.legacy_path; - -import 'dart:io'; -import 'dart:math'; - -// TODO: Remove this class, and use the URI class for all path manipulation. -class Path { - final String _path; - final bool isWindowsShare; - - Path(String source) - : _path = _clean(source), - isWindowsShare = _isWindowsShare(source); - - Path.raw(String source) - : _path = source, - isWindowsShare = false; - - Path._internal(this._path, this.isWindowsShare); - - static String _clean(String source) { - if (Platform.operatingSystem == 'windows') return _cleanWindows(source); - // Remove trailing slash from directories: - if (source.length > 1 && source.endsWith('/')) { - return source.substring(0, source.length - 1); - } - return source; - } - - static String _cleanWindows(String source) { - // Change \ to /. - var clean = source.replaceAll('\\', '/'); - // Add / before initial [Drive letter]: - if (clean.length >= 2 && clean[1] == ':') { - clean = '/$clean'; - } - if (_isWindowsShare(source)) { - return clean.substring(1, clean.length); - } - return clean; - } - - static bool _isWindowsShare(String source) { - return Platform.operatingSystem == 'windows' && source.startsWith('\\\\'); - } - - @override - int get hashCode => _path.hashCode; - - @override - bool operator ==(Object other) { - return other is Path && _path == other._path; - } - - bool get isEmpty => _path.isEmpty; - bool get isAbsolute => _path.startsWith('/'); - bool get hasTrailingSeparator => _path.endsWith('/'); - - @override - String toString() => _path; - - Path relativeTo(Path base) { - // Returns a path "relative" such that - // base.join(relative) == this.canonicalize. - // Throws exception if an impossible case is reached. - if (base.isAbsolute != isAbsolute || - base.isWindowsShare != isWindowsShare) { - throw ArgumentError("Invalid case of Path.relativeTo(base):\n" - " Path and base must both be relative, or both absolute.\n" - " Arguments: $_path.relativeTo($base)"); - } - - var basePath = base.toString(); - // Handle drive letters specially on Windows. - if (base.isAbsolute && Platform.operatingSystem == 'windows') { - bool baseHasDrive = - basePath.length >= 4 && basePath[2] == ':' && basePath[3] == '/'; - bool pathHasDrive = - _path.length >= 4 && _path[2] == ':' && _path[3] == '/'; - if (baseHasDrive && pathHasDrive) { - int baseDrive = basePath.codeUnitAt(1) | 32; // Convert to uppercase. - if (baseDrive >= 'a'.codeUnitAt(0) && - baseDrive <= 'z'.codeUnitAt(0) && - baseDrive == (_path.codeUnitAt(1) | 32)) { - if (basePath[1] != _path[1]) { - // Replace the drive letter in basePath with that from _path. - basePath = '/${_path[1]}:/${basePath.substring(4)}'; - base = Path(basePath); - } - } else { - throw ArgumentError("Invalid case of Path.relativeTo(base):\n" - " Base path and target path are on different Windows drives.\n" - " Arguments: $_path.relativeTo($base)"); - } - } else if (baseHasDrive != pathHasDrive) { - throw ArgumentError("Invalid case of Path.relativeTo(base):\n" - " Base path must start with a drive letter if and " - "only if target path does.\n" - " Arguments: $_path.relativeTo($base)"); - } - } - if (_path.startsWith(basePath)) { - if (_path == basePath) return Path('.'); - // There must be a '/' at the end of the match, or immediately after. - int matchEnd = basePath.length; - if (_path[matchEnd - 1] == '/' || _path[matchEnd] == '/') { - // Drop any extra '/' characters at matchEnd - while (matchEnd < _path.length && _path[matchEnd] == '/') { - matchEnd++; - } - return Path(_path.substring(matchEnd)).canonicalize(); - } - } - - List baseSegments = base.canonicalize().segments(); - List pathSegments = canonicalize().segments(); - if (baseSegments.length == 1 && baseSegments[0] == '.') { - baseSegments = []; - } - if (pathSegments.length == 1 && pathSegments[0] == '.') { - pathSegments = []; - } - int common = 0; - int length = min(pathSegments.length, baseSegments.length); - while (common < length && pathSegments[common] == baseSegments[common]) { - common++; - } - final segments = []; - - if (common < baseSegments.length && baseSegments[common] == '..') { - throw ArgumentError("Invalid case of Path.relativeTo(base):\n" - " Base path has more '..'s than path does.\n" - " Arguments: $_path.relativeTo($base)"); - } - for (int i = common; i < baseSegments.length; i++) { - segments.add('..'); - } - for (int i = common; i < pathSegments.length; i++) { - segments.add(pathSegments[i]); - } - if (segments.isEmpty) { - segments.add('.'); - } - if (hasTrailingSeparator) { - segments.add(''); - } - return Path(segments.join('/')); - } - - Path join(Path further) { - if (further.isAbsolute) { - throw ArgumentError("Path.join called with absolute Path as argument."); - } - if (isEmpty) { - return further.canonicalize(); - } - if (hasTrailingSeparator) { - var joined = Path._internal('$_path$further', isWindowsShare); - return joined.canonicalize(); - } - var joined = Path._internal('$_path/$further', isWindowsShare); - return joined.canonicalize(); - } - - // Note: The URI RFC names for canonicalize, join, and relativeTo - // are normalize, resolve, and relativize. But resolve and relativize - // drop the last segment of the base path (the filename), on URIs. - Path canonicalize() { - if (isCanonical) return this; - return makeCanonical(); - } - - bool get isCanonical { - // Contains no consecutive path separators. - // Contains no segments that are '.'. - // Absolute paths have no segments that are '..'. - // All '..' segments of a relative path are at the beginning. - if (isEmpty) return false; // The canonical form of '' is '.'. - if (_path == '.') return true; - List segs = _path.split('/'); // Don't mask the getter 'segments'. - if (segs[0] == '') { - // Absolute path - segs[0] = null; // Faster than removeRange(). - } else { - // A canonical relative path may start with .. segments. - for (int pos = 0; pos < segs.length && segs[pos] == '..'; ++pos) { - segs[pos] = null; - } - } - if (segs.last == '') segs.removeLast(); // Path ends with /. - // No remaining segments can be ., .., or empty. - return !segs.any((s) => s == '' || s == '.' || s == '..'); - } - - Path makeCanonical() { - bool isAbs = isAbsolute; - List segs = segments(); - String? drive; - if (isAbs && segs.isNotEmpty && segs[0].length == 2 && segs[0][1] == ':') { - drive = segs[0]; - segs.removeRange(0, 1); - } - List newSegs = []; - for (String segment in segs) { - switch (segment) { - case '..': - // Absolute paths drop leading .. markers, including after a drive. - if (newSegs.isEmpty) { - if (isAbs) { - // Do nothing: drop the segment. - } else { - newSegs.add('..'); - } - } else if (newSegs.last == '..') { - newSegs.add('..'); - } else { - newSegs.removeLast(); - } - break; - case '.': - case '': - // Do nothing - drop the segment. - break; - default: - newSegs.add(segment); - break; - } - } - - List segmentsToJoin = []; - if (isAbs) { - segmentsToJoin.add(''); - if (drive != null) { - segmentsToJoin.add(drive); - } - } - - if (newSegs.isEmpty) { - if (isAbs) { - segmentsToJoin.add(''); - } else { - segmentsToJoin.add('.'); - } - } else { - segmentsToJoin.addAll(newSegs); - if (hasTrailingSeparator) { - segmentsToJoin.add(''); - } - } - return Path._internal(segmentsToJoin.join('/'), isWindowsShare); - } - - String toNativePath() { - if (isEmpty) return '.'; - if (Platform.operatingSystem == 'windows') { - String nativePath = _path; - // Drop '/' before a drive letter. - if (nativePath.length >= 3 && - nativePath.startsWith('/') && - nativePath[2] == ':') { - nativePath = nativePath.substring(1); - } - nativePath = nativePath.replaceAll('/', '\\'); - if (isWindowsShare) { - return '\\$nativePath'; - } - return nativePath; - } - return _path; - } - - List segments() { - List result = _path.split('/'); - if (isAbsolute) result.removeRange(0, 1); - if (hasTrailingSeparator) result.removeLast(); - return result; - } - - Path append(String finalSegment) { - if (isEmpty) { - return Path._internal(finalSegment, isWindowsShare); - } else if (hasTrailingSeparator) { - return Path._internal('$_path$finalSegment', isWindowsShare); - } else { - return Path._internal('$_path/$finalSegment', isWindowsShare); - } - } - - String get filenameWithoutExtension { - var name = filename; - if (name == '.' || name == '..') return name; - int pos = name.lastIndexOf('.'); - return (pos < 0) ? name : name.substring(0, pos); - } - - String get extension { - var name = filename; - int pos = name.lastIndexOf('.'); - return (pos < 0) ? '' : name.substring(pos + 1); - } - - Path get directoryPath { - int pos = _path.lastIndexOf('/'); - if (pos < 0) return Path(''); - while (pos > 0 && _path[pos - 1] == '/') { - --pos; - } - var dirPath = (pos > 0) ? _path.substring(0, pos) : '/'; - return Path._internal(dirPath, isWindowsShare); - } - - String get filename { - int pos = _path.lastIndexOf('/'); - return _path.substring(pos + 1); - } -} diff --git a/pkg/testing/lib/src/test_dart/status_expression.dart b/pkg/testing/lib/src/test_dart/status_expression.dart deleted file mode 100644 index 53f60467db0..00000000000 --- a/pkg/testing/lib/src/test_dart/status_expression.dart +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright (c) 2011, 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 test_dart_copy.status_expression; - -/// Parse and evaluate expressions in a .status file for Dart and V8. -/// There are set expressions and Boolean expressions in a .status file. -/// The grammar is: -/// BooleanExpression := $variableName == value | $variableName != value | -/// $variableName | (BooleanExpression) | -/// BooleanExpression && BooleanExpression | -/// BooleanExpression || BooleanExpression -/// -/// SetExpression := value | (SetExpression) | -/// SetExpression || SetExpression | -/// SetExpression if BooleanExpression | -/// SetExpression , SetExpression -/// -/// Productions are listed in order of precedence, and the || and , operators -/// both evaluate to set union, but with different precedence. -/// -/// Values and variableNames are non-empty strings of word characters, matching -/// the RegExp \w+. -/// -/// Expressions evaluate as expected, with values of variables found in -/// an environment passed to the evaluator. The SetExpression "value" -/// evaluates to a singleton set containing that value. "A if B" evaluates -/// to A if B is true, and to the empty set if B is false. - -class ExprEvaluationException { - String error; - - ExprEvaluationException(this.error); - - @override - toString() => error; -} - -class Token { - static const String $leftParan = "("; - static const String $rightParen = ")"; - static const String $dollarSign = r"$"; - static const String $comma = ","; - static const String $equals = "=="; - static const String $notEquals = "!="; - static const String $and = "&&"; - static const String $or = "||"; -} - -class Tokenizer { - String expression; - List tokens; - - Tokenizer(this.expression) : tokens = []; - - // Tokens are : "(", ")", "$", ",", "&&", "||", "==", "!=", and (maximal) \w+. - static final testRegexp = - RegExp(r"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$"); - static final regexp = RegExp(r"[()$,]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+"); - - List tokenize() { - if (!testRegexp.hasMatch(expression)) { - throw FormatException("Syntax error in '$expression'"); - } - for (Match match in regexp.allMatches(expression)) { - tokens.add(match[0]!); - } - return tokens; - } -} - -abstract class BooleanExpression { - bool evaluate(Map environment); -} - -abstract class SetExpression { - Set evaluate(Map environment); -} - -class Comparison implements BooleanExpression { - TermVariable left; - TermConstant right; - bool negate; - - Comparison(this.left, this.right, this.negate); - - @override - bool evaluate(environment) { - return negate != - (left.termValue(environment) == right.termValue(environment)); - } - - @override - String toString() => - "(\$${left.name} ${negate ? '!=' : '=='} ${right.value})"; -} - -class TermVariable { - String name; - - TermVariable(this.name); - - String termValue(environment) { - var value = environment[name]; - if (value == null) { - throw ExprEvaluationException("Could not find '$name' in environment " - "while evaluating status file expression."); - } - return value.toString(); - } -} - -class TermConstant { - String value; - - TermConstant(this.value); - - String termValue(environment) => value; -} - -class BooleanVariable implements BooleanExpression { - TermVariable variable; - - BooleanVariable(this.variable); - - @override - bool evaluate(environment) => variable.termValue(environment) == 'true'; - @override - String toString() => "(bool \$${variable.name})"; -} - -class BooleanOperation implements BooleanExpression { - String op; - BooleanExpression left; - BooleanExpression right; - - BooleanOperation(this.op, this.left, this.right); - - @override - bool evaluate(environment) => (op == Token.$and) - ? left.evaluate(environment) && right.evaluate(environment) - : left.evaluate(environment) || right.evaluate(environment); - @override - String toString() => "($left $op $right)"; -} - -class SetUnion implements SetExpression { - SetExpression left; - SetExpression right; - - SetUnion(this.left, this.right); - - // Overwrites left.evaluate(env). - // Set.addAll does not return this. - @override - Set evaluate(environment) { - Set result = left.evaluate(environment); - result.addAll(right.evaluate(environment)); - return result; - } - - @override - String toString() => "($left || $right)"; -} - -class SetIf implements SetExpression { - SetExpression left; - BooleanExpression right; - - SetIf(this.left, this.right); - - @override - Set evaluate(environment) => - right.evaluate(environment) ? left.evaluate(environment) : {}; - @override - String toString() => "($left if $right)"; -} - -class SetConstant implements SetExpression { - String value; - - SetConstant(String v) : value = v.toLowerCase(); - - @override - Set evaluate(environment) => {value}; - @override - String toString() => value; -} - -// An iterator that allows peeking at the current token. -class Scanner { - List tokens; - late Iterator tokenIterator; - String? current; - - Scanner(this.tokens) { - tokenIterator = tokens.iterator; - advance(); - } - - bool hasMore() => current != null; - - void advance() { - current = tokenIterator.moveNext() ? tokenIterator.current : null; - } -} - -class ExpressionParser { - Scanner scanner; - - ExpressionParser(this.scanner); - - SetExpression parseSetExpression() => parseSetUnion(); - - SetExpression parseSetUnion() { - SetExpression left = parseSetIf(); - while (scanner.hasMore() && scanner.current == Token.$comma) { - scanner.advance(); - SetExpression right = parseSetIf(); - left = SetUnion(left, right); - } - return left; - } - - SetExpression parseSetIf() { - SetExpression left = parseSetOr(); - while (scanner.hasMore() && scanner.current == "if") { - scanner.advance(); - BooleanExpression right = parseBooleanExpression(); - left = SetIf(left, right); - } - return left; - } - - SetExpression parseSetOr() { - SetExpression left = parseSetAtomic(); - while (scanner.hasMore() && scanner.current == Token.$or) { - scanner.advance(); - SetExpression right = parseSetAtomic(); - left = SetUnion(left, right); - } - return left; - } - - SetExpression parseSetAtomic() { - if (scanner.current == Token.$leftParan) { - scanner.advance(); - SetExpression value = parseSetExpression(); - if (scanner.current != Token.$rightParen) { - throw FormatException("Missing right parenthesis in expression"); - } - scanner.advance(); - return value; - } - if (!RegExp(r"^\w+$").hasMatch(scanner.current!)) { - throw FormatException( - "Expected identifier in expression, got ${scanner.current}"); - } - SetExpression value = SetConstant(scanner.current!); - scanner.advance(); - return value; - } - - BooleanExpression parseBooleanExpression() => parseBooleanOr(); - - BooleanExpression parseBooleanOr() { - BooleanExpression left = parseBooleanAnd(); - while (scanner.hasMore() && scanner.current == Token.$or) { - scanner.advance(); - BooleanExpression right = parseBooleanAnd(); - left = BooleanOperation(Token.$or, left, right); - } - return left; - } - - BooleanExpression parseBooleanAnd() { - BooleanExpression left = parseBooleanAtomic(); - while (scanner.hasMore() && scanner.current == Token.$and) { - scanner.advance(); - BooleanExpression right = parseBooleanAtomic(); - left = BooleanOperation(Token.$and, left, right); - } - return left; - } - - BooleanExpression parseBooleanAtomic() { - if (scanner.current == Token.$leftParan) { - scanner.advance(); - BooleanExpression value = parseBooleanExpression(); - if (scanner.current != Token.$rightParen) { - throw FormatException("Missing right parenthesis in expression"); - } - scanner.advance(); - return value; - } - - // The only atomic booleans are of the form $variable == value or - // of the form $variable. - if (scanner.current != Token.$dollarSign) { - throw FormatException( - "Expected \$ in expression, got ${scanner.current}"); - } - scanner.advance(); - if (!RegExp(r"^\w+$").hasMatch(scanner.current!)) { - throw FormatException( - "Expected identifier in expression, got ${scanner.current}"); - } - TermVariable left = TermVariable(scanner.current!); - scanner.advance(); - if (scanner.current == Token.$equals || - scanner.current == Token.$notEquals) { - bool negate = scanner.current == Token.$notEquals; - scanner.advance(); - if (!RegExp(r"^\w+$").hasMatch(scanner.current!)) { - throw FormatException( - "Expected value in expression, got ${scanner.current}"); - } - TermConstant right = TermConstant(scanner.current!); - scanner.advance(); - return Comparison(left, right, negate); - } else { - return BooleanVariable(left); - } - } -} diff --git a/pkg/testing/lib/src/test_dart/status_file_parser.dart b/pkg/testing/lib/src/test_dart/status_file_parser.dart deleted file mode 100644 index df0eda15437..00000000000 --- a/pkg/testing/lib/src/test_dart/status_file_parser.dart +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) 2012, 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 test_dart_copy.status_file_parser; - -import "dart:async"; -import "dart:convert" show LineSplitter, utf8; -import "dart:io"; - -import "path.dart"; -import "status_expression.dart"; - -import '../expectation.dart' show Expectation, ExpectationSet; - -final RegExp splitComment = RegExp("^([^#]*)(#.*)?\$"); -final RegExp headerPattern = RegExp(r"^\[([^\]]+)\]"); -final RegExp rulePattern = RegExp(r"\s*([^: ]*)\s*:(.*)"); -final RegExp issueNumberPattern = RegExp("[Ii]ssue ([0-9]+)"); - -class StatusFile { - final Path location; - - StatusFile(this.location); -} - -// TODO(whesse): Implement configuration_info library that contains data -// structures for test configuration, including Section. -class Section { - final StatusFile statusFile; - - final BooleanExpression? condition; - final List testRules; - final int lineNumber; - - Section.always(this.statusFile, this.lineNumber) - : condition = null, - testRules = []; - Section(this.statusFile, this.condition, this.lineNumber) - : testRules = []; - - bool isEnabled(Map environment) => - condition == null || condition!.evaluate(environment); - - @override - String toString() { - return "Section: $condition"; - } -} - -Future readTestExpectations(List statusFilePaths, - Map environment, ExpectationSet expectationSet) { - var testExpectations = TestExpectations(expectationSet); - return Future.wait(statusFilePaths.map((String statusFile) { - return readTestExpectationsInto(testExpectations, statusFile, environment); - })).then((_) => testExpectations); -} - -Future readTestExpectationsInto(TestExpectations expectations, - String statusFilePath, Map environment) { - var completer = Completer(); - List
sections =
[]; - - void sectionsRead() { - for (Section section in sections) { - if (section.isEnabled(environment)) { - for (var rule in section.testRules) { - expectations.addRule(rule, environment); - } - } - } - completer.complete(); - } - - readConfigurationInto(Path(statusFilePath), sections, sectionsRead); - return completer.future; -} - -void readConfigurationInto( - Path path, List
sections, void Function() onDone) { - StatusFile statusFile = StatusFile(path); - File file = File(path.toNativePath()); - if (!file.existsSync()) { - throw Exception('Cannot find test status file $path'); - } - int lineNumber = 0; - Stream lines = file - .openRead() - .cast>() - .transform(utf8.decoder) - .transform(LineSplitter()); - - Section currentSection = Section.always(statusFile, -1); - sections.add(currentSection); - - lines.listen((String line) { - lineNumber++; - Match? match = splitComment.firstMatch(line); - line = (match == null) ? "" : match[1]!; - line = line.trim(); - if (line.isEmpty) return; - - // Extract the comment to get the issue number if needed. - String comment = (match == null || match[2] == null) ? "" : match[2]!; - - match = headerPattern.firstMatch(line); - if (match != null) { - String conditionString = match[1]!.trim(); - List tokens = Tokenizer(conditionString).tokenize(); - ExpressionParser parser = ExpressionParser(Scanner(tokens)); - currentSection = - Section(statusFile, parser.parseBooleanExpression(), lineNumber); - sections.add(currentSection); - return; - } - - match = rulePattern.firstMatch(line); - if (match != null) { - String name = match[1]!.trim(); - // TODO(whesse): Handle test names ending in a wildcard (*). - String expressionString = match[2]!.trim(); - List tokens = Tokenizer(expressionString).tokenize(); - SetExpression expression = - ExpressionParser(Scanner(tokens)).parseSetExpression(); - - // Look for issue number in comment. - String? issueString; - match = issueNumberPattern.firstMatch(comment); - if (match != null) { - issueString = match[1] ?? match[2]; - } - int? issue = issueString != null ? int.parse(issueString) : null; - currentSection.testRules - .add(TestRule(name, expression, issue, lineNumber)); - return; - } - - print("unmatched line: $line"); - }, onDone: onDone); -} - -class TestRule { - String name; - SetExpression expression; - int? issue; - int lineNumber; - - TestRule(this.name, this.expression, this.issue, this.lineNumber); - - bool get hasIssue => issue != null; - - @override - String toString() => 'TestRule($name, $expression, $issue)'; -} - -class TestExpectations { - // Only create one copy of each Set. - // We just use .toString as a key, so we may make a few - // sets that only differ in their toString element order. - static final Map> _cachedSets = {}; - - final ExpectationSet expectationSet; - - final Map> _map; - bool _preprocessed = false; - Map? _regExpCache; - Map>? _keyToRegExps; - - /// Create a TestExpectations object. See the [expectations] method - /// for an explanation of matching. - TestExpectations(this.expectationSet) : _map = {}; - - /// Add a rule to the expectations. - void addRule(TestRule testRule, Map environment) { - // Once we have started using the expectations we cannot add more - // rules. - if (_preprocessed) { - throw "TestExpectations.addRule: cannot add more rules"; - } - var names = testRule.expression.evaluate(environment); - var expectations = names.map((name) => expectationSet[name]); - _map.putIfAbsent(testRule.name, () => {}).addAll(expectations); - } - - /// Compute the expectations for a test based on the filename. - /// - /// For every (key, expectation) pair. Match the key with the file - /// name. Return the union of the expectations for all the keys - /// that match. - /// - /// Normal matching splits the key and the filename into path - /// components and checks that the anchored regular expression - /// "^$keyComponent\$" matches the corresponding filename component. - Set expectations(String filename) { - var result = {}; - var splitFilename = filename.split('/'); - - // Create mapping from keys to list of RegExps once and for all. - _preprocessForMatching(); - - _map.forEach((key, expectation) { - List regExps = _keyToRegExps![key]!; - if (regExps.length > splitFilename.length) return; - for (var i = 0; i < regExps.length; i++) { - if (!regExps[i].hasMatch(splitFilename[i])) return; - } - // If all components of the status file key matches the filename - // add the expectations to the result. - result.addAll(expectation); - }); - - // If no expectations were found the expectation is that the test - // passes. - if (result.isEmpty) { - result.add(Expectation.pass); - } - return _cachedSets.putIfAbsent(result.toString(), () => result); - } - - // Preprocess the expectations for matching against - // filenames. Generate lists of regular expressions once and for all - // for each key. - void _preprocessForMatching() { - if (_preprocessed) return; - - _keyToRegExps = {}; - _regExpCache = {}; - - _map.forEach((key, expectations) { - if (_keyToRegExps![key] != null) return; - var splitKey = key.split('/'); - var regExps = List.generate(splitKey.length, (int i) { - var component = splitKey[i]; - var regExp = _regExpCache![component]; - if (regExp == null) { - var pattern = "^${splitKey[i]}\$".replaceAll('*', '.*'); - regExp = RegExp(pattern); - _regExpCache![component] = regExp; - } - return regExp; - }, growable: false); - _keyToRegExps![key] = regExps; - }); - - _regExpCache = null; - _preprocessed = true; - } -} diff --git a/pkg/testing/lib/src/test_description.dart b/pkg/testing/lib/src/test_description.dart index 065f47a4f39..e6852a0e903 100644 --- a/pkg/testing/lib/src/test_description.dart +++ b/pkg/testing/lib/src/test_description.dart @@ -18,13 +18,8 @@ abstract class TestDescription implements Comparable { class FileBasedTestDescription extends TestDescription { final Uri root; final File file; - final Uri? output; - /// If non-null, this is a generated multitest, and the set contains the - /// expected outcomes. - Set? multitestExpectations; - - FileBasedTestDescription(this.root, this.file, {this.output}); + FileBasedTestDescription(this.root, this.file); @override Uri get uri => file.uri; diff --git a/pkg/testing/lib/src/test_root.dart b/pkg/testing/lib/src/test_root.dart index fad874c64c0..c23b6ac688e 100644 --- a/pkg/testing/lib/src/test_root.dart +++ b/pkg/testing/lib/src/test_root.dart @@ -14,7 +14,7 @@ import '../testing.dart' show Chain; import 'analyze.dart' show Analyze; -import 'suite.dart' show Dart, Suite; +import 'suite.dart' show Suite; /// Records properties of a test root. The information is read from a JSON file. /// @@ -53,12 +53,6 @@ class TestRoot { List get urisToAnalyze => analyze.uris; - List get excludedFromAnalysis => analyze.exclude; - - Iterable get dartSuites { - return List.from(suites.whereType()); - } - Iterable get toolChains { return List.from(suites.whereType()); } diff --git a/pkg/testing/lib/src/zone_helper.dart b/pkg/testing/lib/src/zone_helper.dart index a8b0c53a6a7..0c031d0627e 100644 --- a/pkg/testing/lib/src/zone_helper.dart +++ b/pkg/testing/lib/src/zone_helper.dart @@ -28,7 +28,7 @@ Future runGuarded( Completer completer = Completer(); - handleUncaughtError(error, StackTrace stackTrace) { + void handleUncaughtError(error, StackTrace stackTrace) { StdoutLogger().logUncaughtError(error, stackTrace); if (!completer.isCompleted) { completer.completeError(error, stackTrace); diff --git a/pkg/testing/test/analyze_test.dart b/pkg/testing/test/analyze_test.dart index 4756ada0482..92e7bb56036 100644 --- a/pkg/testing/test/analyze_test.dart +++ b/pkg/testing/test/analyze_test.dart @@ -4,7 +4,7 @@ import "package:testing/src/run_tests.dart" as testing show main; -main() { +Future main() { // This method is async, but keeps a port open to prevent the VM from exiting // prematurely. return testing.main( diff --git a/pkg/testing/test/dart_sdk_negative_test.dart b/pkg/testing/test/dart_sdk_negative_test.dart deleted file mode 100644 index af9232b6b72..00000000000 --- a/pkg/testing/test/dart_sdk_negative_test.dart +++ /dev/null @@ -1,7 +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. - -main() { - throw "This is a negative test."; -} diff --git a/pkg/testing/test/dart_vm_suite.status b/pkg/testing/test/dart_vm_suite.status deleted file mode 100644 index a200a5208de..00000000000 --- a/pkg/testing/test/dart_vm_suite.status +++ /dev/null @@ -1,3 +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. diff --git a/pkg/testing/test/hello_test.dart b/pkg/testing/test/hello_test.dart deleted file mode 100644 index 800a26c0b1b..00000000000 --- a/pkg/testing/test/hello_test.dart +++ /dev/null @@ -1,7 +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. - -main() { - print("Hello, World!"); -} diff --git a/pkg/testing/testing.json b/pkg/testing/testing.json index a9e84893684..5b8a9f738de 100644 --- a/pkg/testing/testing.json +++ b/pkg/testing/testing.json @@ -5,21 +5,6 @@ "packages": "../../.dart_tool/package_config.json", - "suites": [ - { - "name": "dart_vm", - "kind": "Chain", - "source": "package:testing/dart_vm_suite.dart", - "path": "test/", - "status": "test/dart_vm_suite.status", - "pattern": [ - "\\.dart$" - ], - "exclude": [ - ] - } - ], - "analyze": { "options": "analysis_options.yaml",