Remove unused options from package:testing (batch 2)

Change-Id: I7f9227f3b00d84db5c29e8e5da88dba69def91cc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/346760
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Jens Johansen 2024-01-19 08:15:03 +00:00 committed by Commit Queue
parent 1d92908169
commit 6c897ba077
22 changed files with 137 additions and 1056 deletions

View file

@ -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

View file

@ -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<String> arguments) => run_tests.main(arguments);

View file

@ -4,4 +4,4 @@
import "package:testing/src/run_tests.dart" as run_tests;
main(List<String> arguments) => run_tests.main(arguments);
Future<void> main(List<String> arguments) => run_tests.main(arguments);

View file

@ -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<ChainContext> createContext(
Chain suite, Map<String, String> environment) async {
return VmContext();
}
class VmContext extends ChainContext {
@override
final List<Step> steps = const <Step>[DartVmStep()];
}
class DartVmStep extends Step<FileBasedTestDescription, int, VmContext> {
const DartVmStep();
@override
String get name => "Dart VM";
@override
Future<Result<int>> run(
FileBasedTestDescription input, VmContext context) async {
StdioProcess process = await StdioProcess.run("dart", [input.file.path]);
return process.toResult();
}
}
main(List<String> arguments) => runMe(arguments, createContext);

View file

@ -105,7 +105,7 @@ class AnalyzerDiagnostic {
List<String> parts = <String>[];
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<void> analyzeUris(
Process process = await startDart(
analyzer, const <String>["--batch"], dartArguments..remove("-c"));
process.stdin.writeln(arguments.join(" "));
process.stdin.close();
await process.stdin.close();
bool hasOutput = false;
Set<String> seen = <String>{};
processAnalyzerOutput(Stream<AnalyzerDiagnostic> diagnostics) async {
Future<void> processAnalyzerOutput(
Stream<AnalyzerDiagnostic> diagnostics) async {
await for (AnalyzerDiagnostic diagnostic in diagnostics) {
if (diagnostic.uri != null) {
String path = toFilePath(diagnostic.uri!);

View file

@ -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(
<String>[suite.statusFile!.toFilePath()], {}, expectationSet);
TestExpectations expectations = readTestExpectations(
<String>[suite.statusFile!.toFilePath()], expectationSet);
Stream<TestDescription> stream = list(suite);
List<TestDescription> descriptions = await stream.toList();
descriptions.sort();
@ -240,7 +239,7 @@ abstract class ChainContext {
print("${suite.name}/${description.shortName}: ${result.outcome}");
});
}
postRun();
await postRun();
}
Stream<TestDescription> list(Chain suite) async* {
@ -360,10 +359,6 @@ class Result<O> {
logs.add(log);
}
Result<O> copyWithOutcome(Expectation outcome) {
return Result<O>(output, outcome, error, trace: trace)..logs.addAll(logs);
}
Result<O2> copyWithOutput<O2>(O2 output) {
return Result<O2>(output, outcome, error,
trace: trace,

View file

@ -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<String> 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<Process> startDart(Uri program,
[List<String>? arguments, List<String>? vmArguments]) {
List<String> allArguments = <String>[];

View file

@ -133,7 +133,7 @@ Future<void> 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]));

View file

@ -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<String> arguments) => withErrorHandling(() async {
CommandLine cl = CommandLine.parse(arguments);
if (cl.verbose) {
enableVerboseOutput();
Future<void> main(List<String> arguments) {
return withErrorHandling(() async {
CommandLine cl = CommandLine.parse(arguments);
if (cl.verbose) {
enableVerboseOutput();
}
Map<String, String> 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<String, String> 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<void> runTests(Map<String, Function> tests) =>
withErrorHandling<void>(() async {

View file

@ -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<String> 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<String> lineSplit = line.split(":");
if (lineSplit.length != 2) {
throw "Unsupported line: '$line'";
}
String name = lineSplit[0];
List<String> 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<String, Set<Expectation>> _map = {};
TestExpectations(this.expectationSet);
void add(String name, List<String> allowedStatus) {
Set<Expectation> expectations = (_map[name] ??= {});
for (String status in allowedStatus) {
expectations.add(expectationSet[status]);
}
}
Set<Expectation> expectations(String filename) {
Set<Expectation> result = _map[filename] ?? {};
// If no expectations were found the expectation is that the test
// passes.
if (result.isEmpty) {
result.add(Expectation.pass);
}
return result;
}
}

View file

@ -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.
-->
Files in this directory are copied from
[test.dart](https://github.com/dart-lang/sdk/tree/master/tools/testing/dart).

View file

@ -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<String> baseSegments = base.canonicalize().segments();
List<String> 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 = <String>[];
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<String> segments() {
List<String> 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);
}
}

View file

@ -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<String> tokens;
Tokenizer(this.expression) : tokens = <String>[];
// Tokens are : "(", ")", "$", ",", "&&", "||", "==", "!=", and (maximal) \w+.
static final testRegexp =
RegExp(r"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$");
static final regexp = RegExp(r"[()$,]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
List<String> 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<String, String> environment);
}
abstract class SetExpression {
Set<String> evaluate(Map<String, String> 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<String> evaluate(environment) {
Set<String> 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<String> evaluate(environment) =>
right.evaluate(environment) ? left.evaluate(environment) : <String>{};
@override
String toString() => "($left if $right)";
}
class SetConstant implements SetExpression {
String value;
SetConstant(String v) : value = v.toLowerCase();
@override
Set<String> evaluate(environment) => <String>{value};
@override
String toString() => value;
}
// An iterator that allows peeking at the current token.
class Scanner {
List<String> 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);
}
}
}

View file

@ -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<TestRule> testRules;
final int lineNumber;
Section.always(this.statusFile, this.lineNumber)
: condition = null,
testRules = <TestRule>[];
Section(this.statusFile, this.condition, this.lineNumber)
: testRules = <TestRule>[];
bool isEnabled(Map<String, String> environment) =>
condition == null || condition!.evaluate(environment);
@override
String toString() {
return "Section: $condition";
}
}
Future<TestExpectations> readTestExpectations(List<String> statusFilePaths,
Map<String, String> environment, ExpectationSet expectationSet) {
var testExpectations = TestExpectations(expectationSet);
return Future.wait(statusFilePaths.map((String statusFile) {
return readTestExpectationsInto(testExpectations, statusFile, environment);
})).then((_) => testExpectations);
}
Future<void> readTestExpectationsInto(TestExpectations expectations,
String statusFilePath, Map<String, String> environment) {
var completer = Completer();
List<Section> sections = <Section>[];
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<Section> 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<String> lines = file
.openRead()
.cast<List<int>>()
.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<String> 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<String> 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<Expectation>.
// 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<String, Set<Expectation>> _cachedSets = {};
final ExpectationSet expectationSet;
final Map<String, Set<Expectation>> _map;
bool _preprocessed = false;
Map<String, RegExp>? _regExpCache;
Map<String, List<RegExp>>? _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<String, String> 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<Expectation> expectations(String filename) {
var result = <Expectation>{};
var splitFilename = filename.split('/');
// Create mapping from keys to list of RegExps once and for all.
_preprocessForMatching();
_map.forEach((key, expectation) {
List<RegExp> 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<RegExp>.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;
}
}

View file

@ -18,13 +18,8 @@ abstract class TestDescription implements Comparable<TestDescription> {
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<String>? multitestExpectations;
FileBasedTestDescription(this.root, this.file, {this.output});
FileBasedTestDescription(this.root, this.file);
@override
Uri get uri => file.uri;

View file

@ -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<Uri> get urisToAnalyze => analyze.uris;
List<RegExp> get excludedFromAnalysis => analyze.exclude;
Iterable<Dart> get dartSuites {
return List<Dart>.from(suites.whereType<Dart>());
}
Iterable<Chain> get toolChains {
return List<Chain>.from(suites.whereType<Chain>());
}

View file

@ -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);

View file

@ -4,7 +4,7 @@
import "package:testing/src/run_tests.dart" as testing show main;
main() {
Future<void> main() {
// This method is async, but keeps a port open to prevent the VM from exiting
// prematurely.
return testing.main(

View file

@ -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.";
}

View file

@ -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.

View file

@ -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!");
}

View file

@ -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",