mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:59:47 +00:00
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:
parent
1d92908169
commit
6c897ba077
|
@ -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
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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!);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>[];
|
||||
|
|
|
@ -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]));
|
||||
|
|
|
@ -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 {
|
||||
|
|
69
pkg/testing/lib/src/status_file_parser.dart
Normal file
69
pkg/testing/lib/src/status_file_parser.dart
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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).
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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>());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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.";
|
||||
}
|
|
@ -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.
|
|
@ -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!");
|
||||
}
|
|
@ -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",
|
||||
|
||||
|
|
Loading…
Reference in a new issue