[pkg/testing] analyze using package:lints
Change-Id: If43bc64029ac00575dc154b797fc7630d4dac82e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/250764 Commit-Queue: Devon Carew <devoncarew@google.com> Reviewed-by: Nate Bosch <nbosch@google.com>
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
# 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/core.yaml
|
||||
|
||||
analyzer:
|
||||
errors:
|
||||
# Allow having TODOs in the code
|
||||
|
||||
@@ -8,11 +8,11 @@ import 'testing.dart';
|
||||
|
||||
Future<ChainContext> createContext(
|
||||
Chain suite, Map<String, String> environment) async {
|
||||
return new VmContext();
|
||||
return VmContext();
|
||||
}
|
||||
|
||||
class VmContext extends ChainContext {
|
||||
final List<Step> steps = const <Step>[const DartVmStep()];
|
||||
final List<Step> steps = const <Step>[DartVmStep()];
|
||||
}
|
||||
|
||||
class DartVmStep extends Step<FileBasedTestDescription, int, VmContext> {
|
||||
|
||||
@@ -33,7 +33,7 @@ class Analyze extends Suite {
|
||||
: super("analyze", "analyze", null);
|
||||
|
||||
Future<Null> run(Uri packages, List<Uri>? extraUris) {
|
||||
List<Uri> allUris = new List<Uri>.from(uris);
|
||||
List<Uri> allUris = List<Uri>.from(uris);
|
||||
if (extraUris != null) {
|
||||
allUris.addAll(extraUris);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ class Analyze extends Suite {
|
||||
}).toList();
|
||||
|
||||
List<RegExp> exclude =
|
||||
json["exclude"].map<RegExp>((p) => new RegExp(p)).toList();
|
||||
json["exclude"].map<RegExp>((p) => RegExp(p)).toList();
|
||||
|
||||
Map? gitGrep = json["git grep"];
|
||||
List<String>? gitGrepPathspecs;
|
||||
@@ -60,12 +60,13 @@ class Analyze extends Suite {
|
||||
if (gitGrep != null) {
|
||||
gitGrepPathspecs = gitGrep["pathspecs"] == null
|
||||
? const <String>["."]
|
||||
: new List<String>.from(gitGrep["pathspecs"]);
|
||||
if (gitGrep["patterns"] != null)
|
||||
gitGrepPatterns = new List<String>.from(gitGrep["patterns"]);
|
||||
: List<String>.from(gitGrep["pathspecs"]);
|
||||
if (gitGrep["patterns"] != null) {
|
||||
gitGrepPatterns = List<String>.from(gitGrep["patterns"]);
|
||||
}
|
||||
}
|
||||
|
||||
return new Analyze(
|
||||
return Analyze(
|
||||
optionsUri, uris, exclude, gitGrepPathspecs, gitGrepPatterns);
|
||||
}
|
||||
|
||||
@@ -89,9 +90,9 @@ class AnalyzerDiagnostic {
|
||||
|
||||
final String message;
|
||||
|
||||
static final Pattern potentialSplitPattern = new RegExp(r"\\|\|");
|
||||
static final Pattern potentialSplitPattern = RegExp(r"\\|\|");
|
||||
|
||||
static final Pattern unescapePattern = new RegExp(r"\\(.)");
|
||||
static final Pattern unescapePattern = RegExp(r"\\(.)");
|
||||
|
||||
AnalyzerDiagnostic(this.kind, this.detailedKind, this.code, this.uri,
|
||||
this.line, this.startColumn, this.endColumn, this.message);
|
||||
@@ -120,13 +121,13 @@ class AnalyzerDiagnostic {
|
||||
}
|
||||
addPart();
|
||||
if (parts.length != 8) {
|
||||
return new AnalyzerDiagnostic.malformed(line);
|
||||
return AnalyzerDiagnostic.malformed(line);
|
||||
}
|
||||
return new AnalyzerDiagnostic(
|
||||
return AnalyzerDiagnostic(
|
||||
parts[0],
|
||||
parts[1],
|
||||
parts[2],
|
||||
Uri.base.resolveUri(new Uri.file(parts[3])),
|
||||
Uri.base.resolveUri(Uri.file(parts[3])),
|
||||
int.parse(parts[4]),
|
||||
int.parse(parts[5]),
|
||||
int.parse(parts[6]),
|
||||
@@ -145,10 +146,10 @@ class AnalyzerDiagnostic {
|
||||
Stream<AnalyzerDiagnostic> parseAnalyzerOutput(
|
||||
Stream<List<int>> stream) async* {
|
||||
Stream<String> lines =
|
||||
stream.transform(utf8.decoder).transform(new LineSplitter());
|
||||
stream.transform(utf8.decoder).transform(LineSplitter());
|
||||
await for (String line in lines) {
|
||||
if (line.startsWith(">>> ")) continue;
|
||||
yield new AnalyzerDiagnostic.fromLine(line);
|
||||
yield AnalyzerDiagnostic.fromLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +164,7 @@ Future<Null> analyzeUris(
|
||||
if (uris.isEmpty) return;
|
||||
String topLevel;
|
||||
try {
|
||||
topLevel = new Uri.directory(
|
||||
topLevel = Uri.directory(
|
||||
(await git("rev-parse", <String>["--show-toplevel"])).trimRight())
|
||||
.toFilePath(windows: false);
|
||||
} catch (e) {
|
||||
@@ -183,17 +184,17 @@ Future<Null> analyzeUris(
|
||||
: path;
|
||||
}
|
||||
|
||||
Set<String> filesToAnalyze = new Set<String>();
|
||||
Set<String> filesToAnalyze = Set<String>();
|
||||
|
||||
for (Uri uri in uris) {
|
||||
if (await new Directory.fromUri(uri).exists()) {
|
||||
await for (FileSystemEntity entity in new Directory.fromUri(uri)
|
||||
.list(recursive: true, followLinks: false)) {
|
||||
if (await Directory.fromUri(uri).exists()) {
|
||||
await for (FileSystemEntity entity
|
||||
in Directory.fromUri(uri).list(recursive: true, followLinks: false)) {
|
||||
if (entity is File && entity.path.endsWith(".dart")) {
|
||||
filesToAnalyze.add(toFilePath(entity.uri));
|
||||
}
|
||||
}
|
||||
} else if (await new File.fromUri(uri).exists()) {
|
||||
} else if (await File.fromUri(uri).exists()) {
|
||||
filesToAnalyze.add(toFilePath(uri));
|
||||
} else {
|
||||
throw "File not found: ${uri}";
|
||||
@@ -212,7 +213,7 @@ Future<Null> analyzeUris(
|
||||
|
||||
const String analyzerPath = "pkg/analyzer_cli/bin/analyzer.dart";
|
||||
Uri analyzer = Uri.base.resolve(analyzerPath);
|
||||
if (!await new File.fromUri(analyzer).exists()) {
|
||||
if (!await File.fromUri(analyzer).exists()) {
|
||||
throw "Couldn't find '$analyzerPath' in '${toFilePath(Uri.base)}'";
|
||||
}
|
||||
List<String> arguments = <String>[
|
||||
@@ -233,14 +234,14 @@ Future<Null> analyzeUris(
|
||||
} else {
|
||||
print("Running dartanalyzer.");
|
||||
}
|
||||
Stopwatch sw = new Stopwatch()..start();
|
||||
Stopwatch sw = Stopwatch()..start();
|
||||
Process process = await startDart(
|
||||
analyzer, const <String>["--batch"], dartArguments..remove("-c"));
|
||||
process.stdin.writeln(arguments.join(" "));
|
||||
process.stdin.close();
|
||||
|
||||
bool hasOutput = false;
|
||||
Set<String> seen = new Set<String>();
|
||||
Set<String> seen = Set<String>();
|
||||
|
||||
processAnalyzerOutput(Stream<AnalyzerDiagnostic> diagnostics) async {
|
||||
await for (AnalyzerDiagnostic diagnostic in diagnostics) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import 'suite.dart' show Suite;
|
||||
import '../testing.dart' show FileBasedTestDescription, TestDescription;
|
||||
|
||||
import 'test_dart/status_file_parser.dart'
|
||||
show ReadTestExpectations, TestExpectations;
|
||||
show readTestExpectations, TestExpectations;
|
||||
|
||||
import 'zone_helper.dart' show runGuarded;
|
||||
|
||||
@@ -27,7 +27,7 @@ import 'multitest.dart' show MultitestTransformer, isError;
|
||||
|
||||
import 'expectation.dart' show Expectation, ExpectationGroup, ExpectationSet;
|
||||
|
||||
typedef Future<ChainContext> CreateContext(
|
||||
typedef CreateContext = Future<ChainContext> Function(
|
||||
Chain suite, Map<String, String> environment);
|
||||
|
||||
/// A test suite for tool chains, for example, a compiler.
|
||||
@@ -55,11 +55,11 @@ class Chain extends Suite {
|
||||
Uri uri = base.resolve(path);
|
||||
Uri statusFile = base.resolve(json["status"]);
|
||||
List<RegExp> pattern =
|
||||
json["pattern"].map<RegExp>((p) => new RegExp(p)).toList();
|
||||
json["pattern"].map<RegExp>((p) => RegExp(p)).toList();
|
||||
List<RegExp> exclude =
|
||||
json["exclude"].map<RegExp>((p) => new RegExp(p)).toList();
|
||||
json["exclude"].map<RegExp>((p) => RegExp(p)).toList();
|
||||
bool processMultitests = json["process-multitests"] ?? false;
|
||||
return new Chain(name, kind, source, uri, statusFile, pattern, exclude,
|
||||
return Chain(name, kind, source, uri, statusFile, pattern, exclude,
|
||||
processMultitests);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ class Chain extends Suite {
|
||||
sink.writeln(".createContext, environment, selectors, r'''");
|
||||
const String jsonExtraIndent = " ";
|
||||
sink.write(jsonExtraIndent);
|
||||
sink.writeAll(splitLines(new JsonEncoder.withIndent(" ").convert(this)),
|
||||
sink.writeAll(splitLines(JsonEncoder.withIndent(" ").convert(this)),
|
||||
jsonExtraIndent);
|
||||
sink.writeln("''');");
|
||||
}
|
||||
@@ -106,7 +106,7 @@ abstract class ChainContext {
|
||||
Future<Null> run(Chain suite, Set<String> selectors,
|
||||
{int shards = 1,
|
||||
int shard = 0,
|
||||
Logger logger: const StdoutLogger()}) async {
|
||||
Logger logger = const StdoutLogger()}) async {
|
||||
assert(shards >= 1, "Invalid shards count: $shards");
|
||||
assert(0 <= shard && shard < shards,
|
||||
"Invalid shard index: $shard, not in range [0,$shards[.");
|
||||
@@ -114,11 +114,11 @@ abstract class ChainContext {
|
||||
.where((s) => s.endsWith('...'))
|
||||
.map((s) => s.substring(0, s.length - 3))
|
||||
.toList();
|
||||
TestExpectations expectations = await ReadTestExpectations(
|
||||
TestExpectations expectations = await readTestExpectations(
|
||||
<String>[suite.statusFile!.toFilePath()], {}, expectationSet);
|
||||
Stream<TestDescription> stream = list(suite);
|
||||
if (suite.processMultitests) {
|
||||
stream = stream.transform(new MultitestTransformer());
|
||||
stream = stream.transform(MultitestTransformer());
|
||||
}
|
||||
List<TestDescription> descriptions = await stream.toList();
|
||||
descriptions.sort();
|
||||
@@ -156,7 +156,7 @@ abstract class ChainContext {
|
||||
}
|
||||
}
|
||||
if (shouldSkip) continue;
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
final StringBuffer sb = StringBuffer();
|
||||
final Step? lastStep = steps.isNotEmpty ? steps.last : null;
|
||||
final Iterator<Step> iterator = steps.iterator;
|
||||
|
||||
@@ -199,7 +199,7 @@ abstract class ChainContext {
|
||||
}
|
||||
}, printLineOnStdout: sb.writeln);
|
||||
} else {
|
||||
future = new Future.value(null);
|
||||
future = Future.value(null);
|
||||
}
|
||||
future = future.then((_currentResult) async {
|
||||
Result? currentResult = _currentResult;
|
||||
@@ -260,7 +260,7 @@ abstract class ChainContext {
|
||||
}
|
||||
|
||||
Stream<TestDescription> list(Chain suite) async* {
|
||||
Directory testRoot = new Directory.fromUri(suite.uri);
|
||||
Directory testRoot = Directory.fromUri(suite.uri);
|
||||
if (await testRoot.exists()) {
|
||||
Stream<FileSystemEntity> files =
|
||||
testRoot.list(recursive: true, followLinks: false);
|
||||
@@ -269,7 +269,7 @@ abstract class ChainContext {
|
||||
String path = entity.uri.path;
|
||||
if (suite.exclude.any((RegExp r) => path.contains(r))) continue;
|
||||
if (suite.pattern.any((RegExp r) => path.contains(r))) {
|
||||
yield new FileBasedTestDescription(suite.uri, entity);
|
||||
yield FileBasedTestDescription(suite.uri, entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -356,15 +356,15 @@ abstract class Step<I, O, C extends ChainContext> {
|
||||
Future<Result<O>> run(I input, C context);
|
||||
|
||||
Result<O> unhandledError(error, StackTrace trace) {
|
||||
return new Result<O>.crash(error, trace);
|
||||
return Result<O>.crash(error, trace);
|
||||
}
|
||||
|
||||
Result<O> pass(O output) => new Result<O>.pass(output);
|
||||
Result<O> pass(O output) => Result<O>.pass(output);
|
||||
|
||||
Result<O> crash(error, StackTrace trace) => new Result<O>.crash(error, trace);
|
||||
Result<O> crash(error, StackTrace trace) => Result<O>.crash(error, trace);
|
||||
|
||||
Result<O> fail(O output, [error, StackTrace? trace]) {
|
||||
return new Result<O>.fail(output, error, trace);
|
||||
return Result<O>.fail(output, error, trace);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ class Result<O> {
|
||||
|
||||
final Expectation outcome;
|
||||
|
||||
final error;
|
||||
final Object? error;
|
||||
|
||||
final StackTrace? trace;
|
||||
|
||||
@@ -389,10 +389,14 @@ class Result<O> {
|
||||
///
|
||||
final bool canBeFixWithUpdateExpectations;
|
||||
|
||||
Result(this.output, this.outcome, this.error,
|
||||
{this.trace,
|
||||
this.autoFixCommand,
|
||||
this.canBeFixWithUpdateExpectations: false});
|
||||
Result(
|
||||
this.output,
|
||||
this.outcome,
|
||||
this.error, {
|
||||
this.trace,
|
||||
this.autoFixCommand,
|
||||
this.canBeFixWithUpdateExpectations = false,
|
||||
});
|
||||
|
||||
Result.pass(O output) : this(output, Expectation.Pass, null);
|
||||
|
||||
@@ -411,12 +415,11 @@ class Result<O> {
|
||||
}
|
||||
|
||||
Result<O> copyWithOutcome(Expectation outcome) {
|
||||
return new Result<O>(output, outcome, error, trace: trace)
|
||||
..logs.addAll(logs);
|
||||
return Result<O>(output, outcome, error, trace: trace)..logs.addAll(logs);
|
||||
}
|
||||
|
||||
Result<O2> copyWithOutput<O2>(O2 output) {
|
||||
return new Result<O2>(output, outcome, error,
|
||||
return Result<O2>(output, outcome, error,
|
||||
trace: trace,
|
||||
autoFixCommand: autoFixCommand,
|
||||
canBeFixWithUpdateExpectations: canBeFixWithUpdateExpectations)
|
||||
@@ -428,8 +431,7 @@ class Result<O> {
|
||||
Future<Null> runChain(CreateContext f, Map<String, String> environment,
|
||||
Set<String> selectors, String jsonText) {
|
||||
return withErrorHandling(() async {
|
||||
Chain suite =
|
||||
new Suite.fromJsonMap(Uri.base, json.decode(jsonText)) as Chain;
|
||||
Chain suite = Suite.fromJsonMap(Uri.base, json.decode(jsonText)) as Chain;
|
||||
print("Running ${suite.name}");
|
||||
ChainContext context = await f(suite, environment);
|
||||
return context.run(suite, selectors);
|
||||
|
||||
@@ -22,11 +22,11 @@ List<String> get dartArguments =>
|
||||
Stream<FileBasedTestDescription> listTests(List<Uri> testRoots,
|
||||
{Pattern? pattern}) {
|
||||
StreamController<FileBasedTestDescription> controller =
|
||||
new StreamController<FileBasedTestDescription>();
|
||||
StreamController<FileBasedTestDescription>();
|
||||
Map<Uri, StreamSubscription?> subscriptions = <Uri, StreamSubscription>{};
|
||||
for (Uri testRootUri in testRoots) {
|
||||
subscriptions[testRootUri] = null;
|
||||
Directory testRoot = new Directory.fromUri(testRootUri);
|
||||
Directory testRoot = Directory.fromUri(testRootUri);
|
||||
testRoot.exists().then((bool exists) {
|
||||
if (exists) {
|
||||
Stream<FileSystemEntity> stream =
|
||||
@@ -75,10 +75,10 @@ const _dartSdk = (String.fromEnvironment("DART_SDK", defaultValue: "1") ==
|
||||
Uri computeDartSdk() {
|
||||
String? dartSdkPath = Platform.environment["DART_SDK"] ?? _dartSdk;
|
||||
if (dartSdkPath != null) {
|
||||
return Uri.base.resolveUri(new Uri.file(dartSdkPath));
|
||||
return Uri.base.resolveUri(Uri.file(dartSdkPath));
|
||||
} else {
|
||||
return Uri.base
|
||||
.resolveUri(new Uri.file(Platform.resolvedExecutable))
|
||||
.resolveUri(Uri.file(Platform.resolvedExecutable))
|
||||
.resolve("../");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import 'dart:isolate' show ReceivePort;
|
||||
import 'log.dart';
|
||||
|
||||
Future<T?> withErrorHandling<T>(Future<T> f(), {Logger? logger}) async {
|
||||
final ReceivePort port = new ReceivePort();
|
||||
final ReceivePort port = ReceivePort();
|
||||
try {
|
||||
return await f();
|
||||
} catch (e, trace) {
|
||||
|
||||
@@ -13,20 +13,16 @@ library testing.expectation;
|
||||
/// use the canonical expectation instead of a more specific one. Note this
|
||||
/// isn't implemented yet.
|
||||
class Expectation {
|
||||
static const Expectation Pass =
|
||||
const Expectation("Pass", ExpectationGroup.Pass);
|
||||
static const Expectation Pass = Expectation("Pass", ExpectationGroup.Pass);
|
||||
|
||||
static const Expectation Crash =
|
||||
const Expectation("Crash", ExpectationGroup.Crash);
|
||||
static const Expectation Crash = Expectation("Crash", ExpectationGroup.Crash);
|
||||
|
||||
static const Expectation Timeout =
|
||||
const Expectation("Timeout", ExpectationGroup.Timeout);
|
||||
Expectation("Timeout", ExpectationGroup.Timeout);
|
||||
|
||||
static const Expectation Fail =
|
||||
const Expectation("Fail", ExpectationGroup.Fail);
|
||||
static const Expectation Fail = Expectation("Fail", ExpectationGroup.Fail);
|
||||
|
||||
static const Expectation Skip =
|
||||
const Expectation("Skip", ExpectationGroup.Skip);
|
||||
static const Expectation Skip = Expectation("Skip", ExpectationGroup.Skip);
|
||||
|
||||
final String name;
|
||||
|
||||
@@ -61,18 +57,17 @@ class Expectation {
|
||||
}
|
||||
|
||||
class ExpectationSet {
|
||||
static const ExpectationSet Default =
|
||||
const ExpectationSet(const <String, Expectation>{
|
||||
static const ExpectationSet Default = ExpectationSet(<String, Expectation>{
|
||||
"pass": Expectation.Pass,
|
||||
"crash": Expectation.Crash,
|
||||
"timeout": Expectation.Timeout,
|
||||
"fail": Expectation.Fail,
|
||||
"skip": Expectation.Skip,
|
||||
"missingcompiletimeerror":
|
||||
const Expectation("MissingCompileTimeError", ExpectationGroup.Fail),
|
||||
Expectation("MissingCompileTimeError", ExpectationGroup.Fail),
|
||||
"missingruntimeerror":
|
||||
const Expectation("MissingRuntimeError", ExpectationGroup.Fail),
|
||||
"runtimeerror": const Expectation("RuntimeError", ExpectationGroup.Fail),
|
||||
Expectation("MissingRuntimeError", ExpectationGroup.Fail),
|
||||
"runtimeerror": Expectation("RuntimeError", ExpectationGroup.Fail),
|
||||
});
|
||||
|
||||
final Map<String, Expectation> internalMap;
|
||||
@@ -86,7 +81,7 @@ class ExpectationSet {
|
||||
|
||||
factory ExpectationSet.fromJsonList(List data) {
|
||||
Map<String, Expectation> internalMap =
|
||||
new Map<String, Expectation>.from(Default.internalMap);
|
||||
Map<String, Expectation>.from(Default.internalMap);
|
||||
for (Map map in data) {
|
||||
String? name;
|
||||
String? group;
|
||||
@@ -112,14 +107,14 @@ class ExpectationSet {
|
||||
if (group == null) {
|
||||
throw "No group provided in '$map'";
|
||||
}
|
||||
Expectation expectation = new Expectation(name!, groupFromString(group!));
|
||||
Expectation expectation = Expectation(name!, groupFromString(group!));
|
||||
name = name!.toLowerCase();
|
||||
if (internalMap.containsKey(name)) {
|
||||
throw "Duplicated expectation name: '$name'.";
|
||||
}
|
||||
internalMap[name!] = expectation;
|
||||
}
|
||||
return new ExpectationSet(internalMap);
|
||||
return ExpectationSet(internalMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ final String cursorUp = enableAnsiEscapes ? cursorUpCodes : "";
|
||||
|
||||
final String eraseLine = enableAnsiEscapes ? eraseLineCodes : "";
|
||||
|
||||
final Stopwatch wallclock = new Stopwatch()..start();
|
||||
final Stopwatch wallclock = Stopwatch()..start();
|
||||
|
||||
bool _isVerbose = const bool.fromEnvironment("verbose");
|
||||
|
||||
@@ -195,13 +195,13 @@ class StdoutLogger implements Logger {
|
||||
void noticeFrameworkCatchError(error, StackTrace stackTrace) {}
|
||||
}
|
||||
|
||||
String pad(Object o, int pad, {String filler: " "}) {
|
||||
String pad(Object o, int pad, {String filler = " "}) {
|
||||
String result = (filler * pad) + "$o";
|
||||
return result.substring(result.length - pad);
|
||||
}
|
||||
|
||||
String numberedLines(String text) {
|
||||
StringBuffer result = new StringBuffer();
|
||||
StringBuffer result = StringBuffer();
|
||||
int lineNumber = 1;
|
||||
List<String> lines = splitLines(text);
|
||||
int pad = "${lines.length}".length;
|
||||
@@ -217,5 +217,5 @@ String numberedLines(String text) {
|
||||
}
|
||||
|
||||
List<String> splitLines(String text) {
|
||||
return text.split(new RegExp('^', multiLine: true));
|
||||
return text.split(RegExp('^', multiLine: true));
|
||||
}
|
||||
|
||||
@@ -26,10 +26,10 @@ bool isCheckedModeError(Set<String> expectations) {
|
||||
|
||||
class MultitestTransformer
|
||||
extends StreamTransformerBase<TestDescription, TestDescription> {
|
||||
static RegExp multitestMarker = new RegExp(r"//[#/]");
|
||||
static RegExp multitestMarker = RegExp(r"//[#/]");
|
||||
static int _multitestMarkerLength = 3;
|
||||
|
||||
static const List<String> validOutcomesList = const <String>[
|
||||
static const List<String> validOutcomesList = <String>[
|
||||
"ok",
|
||||
"syntax error",
|
||||
"compile-time error",
|
||||
@@ -39,8 +39,7 @@ class MultitestTransformer
|
||||
"checked mode compile-time error",
|
||||
];
|
||||
|
||||
static final Set<String> validOutcomes =
|
||||
new Set<String>.from(validOutcomesList);
|
||||
static final Set<String> validOutcomes = Set<String>.from(validOutcomesList);
|
||||
|
||||
Stream<TestDescription> bind(Stream<TestDescription> stream) async* {
|
||||
List<String> errors = <String>[];
|
||||
@@ -70,7 +69,7 @@ class MultitestTransformer
|
||||
"none": linesWithoutAnnotations,
|
||||
};
|
||||
Map<String, Set<String>> outcomes = <String, Set<String>>{
|
||||
"none": new Set<String>(),
|
||||
"none": Set<String>(),
|
||||
};
|
||||
int lineNumber = 0;
|
||||
for (String line in splitLines(contents!)) {
|
||||
@@ -97,11 +96,11 @@ class MultitestTransformer
|
||||
}
|
||||
}
|
||||
if (subtestName != null) {
|
||||
List<String> lines = testsAsLines.putIfAbsent(subtestName,
|
||||
() => new List<String>.from(linesWithoutAnnotations));
|
||||
List<String> lines = testsAsLines.putIfAbsent(
|
||||
subtestName, () => List<String>.from(linesWithoutAnnotations));
|
||||
lines.add(line);
|
||||
Set<String> subtestOutcomes =
|
||||
outcomes.putIfAbsent(subtestName, () => new Set<String>());
|
||||
outcomes.putIfAbsent(subtestName, () => Set<String>());
|
||||
if (subtestOutcomesList!.length != 1 ||
|
||||
subtestOutcomesList.single != "continued") {
|
||||
for (String outcome in subtestOutcomesList) {
|
||||
@@ -123,14 +122,14 @@ class MultitestTransformer
|
||||
}
|
||||
Uri root = Uri.base.resolve("generated/");
|
||||
Directory generated =
|
||||
new Directory.fromUri(root.resolve(multitest.shortName));
|
||||
Directory.fromUri(root.resolve(multitest.shortName));
|
||||
generated = await generated.create(recursive: true);
|
||||
for (MapEntry<String, List<String>> entry in testsAsLines.entries) {
|
||||
String name = entry.key;
|
||||
List<String> lines = entry.value;
|
||||
Uri uri = generated.uri.resolve("${name}_generated.dart");
|
||||
FileBasedTestDescription subtest =
|
||||
new FileBasedTestDescription(root, new File.fromUri(uri));
|
||||
FileBasedTestDescription(root, File.fromUri(uri));
|
||||
subtest.multitestExpectations = outcomes[name];
|
||||
await subtest.file.writeAsString(lines.join(""));
|
||||
yield subtest;
|
||||
|
||||
@@ -54,7 +54,7 @@ Future<Null> runMe(List<String> arguments, CreateContext f,
|
||||
Uri? me,
|
||||
int shards = 1,
|
||||
int shard = 0,
|
||||
Logger logger: const StdoutLogger()}) {
|
||||
Logger logger = const StdoutLogger()}) {
|
||||
me ??= Platform.script;
|
||||
return withErrorHandling(() async {
|
||||
TestRoot testRoot = await computeTestRoot(configurationPath, me);
|
||||
@@ -63,7 +63,7 @@ Future<Null> runMe(List<String> arguments, CreateContext f,
|
||||
for (Chain suite in testRoot.toolChains) {
|
||||
if (me == suite.source) {
|
||||
ChainContext context = await f(suite, cl.environment);
|
||||
await context.run(suite, new Set<String>.from(cl.selectors),
|
||||
await context.run(suite, Set<String>.from(cl.selectors),
|
||||
shards: shards, shard: shard, logger: logger);
|
||||
}
|
||||
}
|
||||
@@ -104,8 +104,8 @@ Future<Null> run(List<String> arguments, List<String> suiteNames,
|
||||
List<Suite> suites = root.suites
|
||||
.where((Suite suite) => suiteNames.contains(suite.name))
|
||||
.toList();
|
||||
SuiteRunner runner = new SuiteRunner(suites, <String, String>{},
|
||||
const <String>[], new Set<String>(), new Set<String>());
|
||||
SuiteRunner runner = SuiteRunner(suites, <String, String>{},
|
||||
const <String>[], Set<String>(), Set<String>());
|
||||
String? program = await runner.generateDartProgram();
|
||||
await runner.analyze(root.packages);
|
||||
if (program != null) {
|
||||
@@ -117,8 +117,8 @@ Future<Null> run(List<String> arguments, List<String> suiteNames,
|
||||
Future<Null> runProgram(String program, Uri packages) async {
|
||||
const StdoutLogger().logMessage("Running:");
|
||||
const StdoutLogger().logNumberedLines(program);
|
||||
Uri dataUri = new Uri.dataFromString(program);
|
||||
ReceivePort exitPort = new ReceivePort();
|
||||
Uri dataUri = Uri.dataFromString(program);
|
||||
ReceivePort exitPort = ReceivePort();
|
||||
Isolate isolate = await Isolate.spawnUri(dataUri, <String>[], null,
|
||||
paused: true,
|
||||
onExit: exitPort.sendPort,
|
||||
@@ -137,7 +137,7 @@ Future<Null> runProgram(String program, Uri packages) async {
|
||||
subscription.cancel();
|
||||
return error == null
|
||||
? null
|
||||
: new Future<Null>.error(error![0], new StackTrace.fromString(error![1]));
|
||||
: Future<Null>.error(error![0], StackTrace.fromString(error![1]));
|
||||
}
|
||||
|
||||
class SuiteRunner {
|
||||
@@ -164,9 +164,9 @@ class SuiteRunner {
|
||||
|
||||
Future<String?> generateDartProgram() async {
|
||||
testUris.clear();
|
||||
StringBuffer imports = new StringBuffer();
|
||||
StringBuffer dart = new StringBuffer();
|
||||
StringBuffer chain = new StringBuffer();
|
||||
StringBuffer imports = StringBuffer();
|
||||
StringBuffer dart = StringBuffer();
|
||||
StringBuffer chain = StringBuffer();
|
||||
bool hasRunnableTests = false;
|
||||
|
||||
await for (FileBasedTestDescription description in listDescriptions()) {
|
||||
|
||||
@@ -33,7 +33,7 @@ class CommandLine {
|
||||
Set<String> get skip => commaSeparated("--skip=");
|
||||
|
||||
Set<String> commaSeparated(String prefix) {
|
||||
return new Set<String>.from(options.expand((String s) {
|
||||
return Set<String>.from(options.expand((String s) {
|
||||
if (!s.startsWith(prefix)) return const [];
|
||||
s = s.substring(prefix.length);
|
||||
return s.split(",");
|
||||
@@ -76,7 +76,7 @@ class CommandLine {
|
||||
String configurationPath;
|
||||
if (configurationPaths.length == 1) {
|
||||
configurationPath = configurationPaths.single;
|
||||
File file = new File(configurationPath);
|
||||
File file = File(configurationPath);
|
||||
if (await file.exists()) {
|
||||
// If [configurationPath] exists as a file, use the absolute URI. This
|
||||
// handles absolute paths on Windows.
|
||||
@@ -84,8 +84,8 @@ class CommandLine {
|
||||
}
|
||||
} else {
|
||||
configurationPath = "testing.json";
|
||||
if (!await new File(configurationPath).exists()) {
|
||||
Directory test = new Directory("test");
|
||||
if (!await File(configurationPath).exists()) {
|
||||
Directory test = Directory("test");
|
||||
if (await test.exists()) {
|
||||
List<FileSystemEntity> candidates = await test
|
||||
.list(recursive: true, followLinks: false)
|
||||
@@ -113,8 +113,7 @@ class CommandLine {
|
||||
.logMessage("Reading configuration file '$configurationPath'.");
|
||||
Uri? configuration =
|
||||
await Isolate.resolvePackageUri(Uri.base.resolve(configurationPath));
|
||||
if (configuration == null ||
|
||||
!await new File.fromUri(configuration).exists()) {
|
||||
if (configuration == null || !await File.fromUri(configuration).exists()) {
|
||||
return fail("Couldn't locate: '$configurationPath'.");
|
||||
}
|
||||
return configuration;
|
||||
@@ -124,14 +123,14 @@ class CommandLine {
|
||||
int index = arguments.indexOf("--");
|
||||
Set<String> options;
|
||||
if (index != -1) {
|
||||
options = new Set<String>.from(arguments.getRange(0, index));
|
||||
options = Set<String>.from(arguments.getRange(0, index));
|
||||
arguments = arguments.sublist(index + 1);
|
||||
} else {
|
||||
options = arguments.where((argument) => argument.startsWith("-")).toSet();
|
||||
arguments =
|
||||
arguments.where((argument) => !argument.startsWith("-")).toList();
|
||||
}
|
||||
return new CommandLine(options, arguments);
|
||||
return CommandLine(options, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,11 +152,11 @@ main(List<String> arguments) => withErrorHandling(() async {
|
||||
print("Use --verbose to display more details.");
|
||||
}
|
||||
TestRoot root = await TestRoot.fromUri(configuration);
|
||||
SuiteRunner runner = new SuiteRunner(
|
||||
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 = new Stopwatch()..start();
|
||||
Stopwatch sw = Stopwatch()..start();
|
||||
if (program == null) {
|
||||
if (!hasAnalyzerSuites) {
|
||||
fail("No tests configured.");
|
||||
@@ -174,7 +173,7 @@ Future<void> runTests(Map<String, Function> tests) =>
|
||||
for (String name in tests.keys) {
|
||||
const StdoutLogger()
|
||||
.logTestStart(completed, 0, tests.length, null, null);
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuffer sb = StringBuffer();
|
||||
try {
|
||||
await runGuarded(() {
|
||||
print("Running test $name");
|
||||
|
||||
@@ -23,17 +23,17 @@ class StdioProcess {
|
||||
|
||||
StdioProcess(this.exitCode, this.output);
|
||||
|
||||
Result<int> toResult({int expected: 0}) {
|
||||
Result<int> toResult({int expected = 0}) {
|
||||
if (exitCode == expected) {
|
||||
return new Result<int>.pass(exitCode);
|
||||
return Result<int>.pass(exitCode);
|
||||
} else {
|
||||
return new Result<int>(
|
||||
return Result<int>(
|
||||
exitCode, ExpectationSet.Default["RuntimeError"], output);
|
||||
}
|
||||
}
|
||||
|
||||
static StreamTransformer<String, String> transformToStdio(Stdout stdio) {
|
||||
return new StreamTransformer<String, String>.fromHandlers(
|
||||
return StreamTransformer<String, String>.fromHandlers(
|
||||
handleData: (String data, EventSink<String> sink) {
|
||||
sink.add(data);
|
||||
stdio.write(data);
|
||||
@@ -42,15 +42,15 @@ class StdioProcess {
|
||||
|
||||
static Future<StdioProcess> run(String executable, List<String> arguments,
|
||||
{String? input,
|
||||
Duration? timeout: const Duration(seconds: 60),
|
||||
bool suppressOutput: true,
|
||||
bool runInShell: false}) async {
|
||||
Duration? timeout = const Duration(seconds: 60),
|
||||
bool suppressOutput = true,
|
||||
bool runInShell = false}) async {
|
||||
Process process =
|
||||
await Process.start(executable, arguments, runInShell: runInShell);
|
||||
Timer? timer;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
StringBuffer sb = StringBuffer();
|
||||
if (timeout != null) {
|
||||
timer = new Timer(timeout, () {
|
||||
timer = Timer(timeout, () {
|
||||
sb.write("Process timed out: ");
|
||||
sb.write(executable);
|
||||
sb.write(" ");
|
||||
@@ -58,7 +58,7 @@ class StdioProcess {
|
||||
sb.writeln();
|
||||
sb.writeln("Sending SIGTERM to process");
|
||||
process.kill();
|
||||
timer = new Timer(const Duration(seconds: 10), () {
|
||||
timer = Timer(const Duration(seconds: 10), () {
|
||||
sb.writeln("Sending SIGKILL to process");
|
||||
process.kill(ProcessSignal.sigkill);
|
||||
});
|
||||
@@ -75,13 +75,15 @@ class StdioProcess {
|
||||
stdoutStream = stdoutStream.transform(transformToStdio(io.stdout));
|
||||
stderrStream = stderrStream.transform(transformToStdio(io.stderr));
|
||||
}
|
||||
Future<List<String>> stdoutFuture = stdoutStream.toList() as Future<List<String>>;
|
||||
Future<List<String>> stderrFuture = stderrStream.toList() as Future<List<String>>;
|
||||
Future<List<String>> stdoutFuture =
|
||||
stdoutStream.toList() as Future<List<String>>;
|
||||
Future<List<String>> stderrFuture =
|
||||
stderrStream.toList() as Future<List<String>>;
|
||||
int exitCode = await process.exitCode;
|
||||
timer?.cancel();
|
||||
sb.writeAll(await stdoutFuture);
|
||||
sb.writeAll(await stderrFuture);
|
||||
await closeFuture;
|
||||
return new StdioProcess(exitCode, "$sb");
|
||||
return StdioProcess(exitCode, "$sb");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ abstract class Suite {
|
||||
String name = json["name"];
|
||||
switch (kind) {
|
||||
case "dart":
|
||||
return new Dart.fromJsonMap(base, json, name);
|
||||
return Dart.fromJsonMap(base, json, name);
|
||||
|
||||
case "chain":
|
||||
return new Chain.fromJsonMap(base, json, name, kind);
|
||||
return Chain.fromJsonMap(base, json, name, kind);
|
||||
|
||||
case "test_dart":
|
||||
return new TestDart.fromJsonMap(base, json, name, kind);
|
||||
return TestDart.fromJsonMap(base, json, name, kind);
|
||||
|
||||
default:
|
||||
throw "Suite '$name' has unknown kind '$kind'.";
|
||||
@@ -78,10 +78,10 @@ class Dart extends Suite {
|
||||
factory Dart.fromJsonMap(Uri base, Map json, String name) {
|
||||
Uri uri = base.resolve(json["path"]);
|
||||
List<RegExp> pattern =
|
||||
new List<RegExp>.from(json["pattern"].map((String p) => new RegExp(p)));
|
||||
List<RegExp>.from(json["pattern"].map((String p) => RegExp(p)));
|
||||
List<RegExp> exclude =
|
||||
new List<RegExp>.from(json["exclude"].map((String p) => new RegExp(p)));
|
||||
return new Dart(name, uri, pattern, exclude);
|
||||
List<RegExp>.from(json["exclude"].map((String p) => RegExp(p)));
|
||||
return Dart(name, uri, pattern, exclude);
|
||||
}
|
||||
|
||||
String toString() => "Dart($name, $uri, $pattern, $exclude)";
|
||||
|
||||
@@ -30,9 +30,9 @@ class TestDart extends Suite {
|
||||
String common = json["common"] ?? "";
|
||||
String processes = json["processes"] ?? "-j${Platform.numberOfProcessors}";
|
||||
List<String> commandLines = json["command-lines"] == null
|
||||
? new List<String>.from(json["command-lines"])
|
||||
? List<String>.from(json["command-lines"])
|
||||
: <String>[];
|
||||
return new TestDart(name, common, processes, commandLines);
|
||||
return TestDart(name, common, processes, commandLines);
|
||||
}
|
||||
|
||||
void writeFirstImportOn(StringSink sink) {
|
||||
|
||||
@@ -49,6 +49,12 @@ class Path {
|
||||
}
|
||||
|
||||
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('/');
|
||||
@@ -61,7 +67,7 @@ class Path {
|
||||
// Throws exception if an impossible case is reached.
|
||||
if (base.isAbsolute != isAbsolute ||
|
||||
base.isWindowsShare != isWindowsShare) {
|
||||
throw new ArgumentError("Invalid case of Path.relativeTo(base):\n"
|
||||
throw ArgumentError("Invalid case of Path.relativeTo(base):\n"
|
||||
" Path and base must both be relative, or both absolute.\n"
|
||||
" Arguments: $_path.relativeTo($base)");
|
||||
}
|
||||
@@ -81,22 +87,22 @@ class Path {
|
||||
if (basePath[1] != _path[1]) {
|
||||
// Replace the drive letter in basePath with that from _path.
|
||||
basePath = '/${_path[1]}:/${basePath.substring(4)}';
|
||||
base = new Path(basePath);
|
||||
base = Path(basePath);
|
||||
}
|
||||
} else {
|
||||
throw new ArgumentError("Invalid case of Path.relativeTo(base):\n"
|
||||
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 new ArgumentError("Invalid case of Path.relativeTo(base):\n"
|
||||
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 new Path('.');
|
||||
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] == '/') {
|
||||
@@ -104,7 +110,7 @@ class Path {
|
||||
while (matchEnd < _path.length && _path[matchEnd] == '/') {
|
||||
matchEnd++;
|
||||
}
|
||||
return new Path(_path.substring(matchEnd)).canonicalize();
|
||||
return Path(_path.substring(matchEnd)).canonicalize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +130,7 @@ class Path {
|
||||
final segments = <String>[];
|
||||
|
||||
if (common < baseSegments.length && baseSegments[common] == '..') {
|
||||
throw new ArgumentError("Invalid case of Path.relativeTo(base):\n"
|
||||
throw ArgumentError("Invalid case of Path.relativeTo(base):\n"
|
||||
" Base path has more '..'s than path does.\n"
|
||||
" Arguments: $_path.relativeTo($base)");
|
||||
}
|
||||
@@ -140,22 +146,21 @@ class Path {
|
||||
if (hasTrailingSeparator) {
|
||||
segments.add('');
|
||||
}
|
||||
return new Path(segments.join('/'));
|
||||
return Path(segments.join('/'));
|
||||
}
|
||||
|
||||
Path join(Path further) {
|
||||
if (further.isAbsolute) {
|
||||
throw new ArgumentError(
|
||||
"Path.join called with absolute Path as argument.");
|
||||
throw ArgumentError("Path.join called with absolute Path as argument.");
|
||||
}
|
||||
if (isEmpty) {
|
||||
return further.canonicalize();
|
||||
}
|
||||
if (hasTrailingSeparator) {
|
||||
var joined = new Path._internal('$_path${further}', isWindowsShare);
|
||||
var joined = Path._internal('$_path${further}', isWindowsShare);
|
||||
return joined.canonicalize();
|
||||
}
|
||||
var joined = new Path._internal('$_path/${further}', isWindowsShare);
|
||||
var joined = Path._internal('$_path/${further}', isWindowsShare);
|
||||
return joined.canonicalize();
|
||||
}
|
||||
|
||||
@@ -193,7 +198,7 @@ class Path {
|
||||
bool isAbs = isAbsolute;
|
||||
List segs = segments();
|
||||
String? drive;
|
||||
if (isAbs && !segs.isEmpty && segs[0].length == 2 && segs[0][1] == ':') {
|
||||
if (isAbs && segs.isNotEmpty && segs[0].length == 2 && segs[0][1] == ':') {
|
||||
drive = segs[0];
|
||||
segs.removeRange(0, 1);
|
||||
}
|
||||
@@ -244,7 +249,7 @@ class Path {
|
||||
segmentsToJoin.add('');
|
||||
}
|
||||
}
|
||||
return new Path._internal(segmentsToJoin.join('/'), isWindowsShare);
|
||||
return Path._internal(segmentsToJoin.join('/'), isWindowsShare);
|
||||
}
|
||||
|
||||
String toNativePath() {
|
||||
@@ -275,11 +280,11 @@ class Path {
|
||||
|
||||
Path append(String finalSegment) {
|
||||
if (isEmpty) {
|
||||
return new Path._internal(finalSegment, isWindowsShare);
|
||||
return Path._internal(finalSegment, isWindowsShare);
|
||||
} else if (hasTrailingSeparator) {
|
||||
return new Path._internal('$_path$finalSegment', isWindowsShare);
|
||||
return Path._internal('$_path$finalSegment', isWindowsShare);
|
||||
} else {
|
||||
return new Path._internal('$_path/$finalSegment', isWindowsShare);
|
||||
return Path._internal('$_path/$finalSegment', isWindowsShare);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,10 +303,12 @@ class Path {
|
||||
|
||||
Path get directoryPath {
|
||||
int pos = _path.lastIndexOf('/');
|
||||
if (pos < 0) return new Path('');
|
||||
while (pos > 0 && _path[pos - 1] == '/') --pos;
|
||||
if (pos < 0) return Path('');
|
||||
while (pos > 0 && _path[pos - 1] == '/') {
|
||||
--pos;
|
||||
}
|
||||
var dirPath = (pos > 0) ? _path.substring(0, pos) : '/';
|
||||
return new Path._internal(dirPath, isWindowsShare);
|
||||
return Path._internal(dirPath, isWindowsShare);
|
||||
}
|
||||
|
||||
String get filename {
|
||||
|
||||
@@ -4,31 +4,29 @@
|
||||
|
||||
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.
|
||||
*/
|
||||
/// 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;
|
||||
@@ -57,12 +55,12 @@ class Tokenizer {
|
||||
|
||||
// Tokens are : "(", ")", "$", ",", "&&", "||", "==", "!=", and (maximal) \w+.
|
||||
static final testRegexp =
|
||||
new RegExp(r"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$");
|
||||
static final regexp = new RegExp(r"[()$,]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
|
||||
RegExp(r"^([()$\w\s,]|(\&\&)|(\|\|)|(\=\=)|(\!\=))+$");
|
||||
static final regexp = RegExp(r"[()$,]|(\&\&)|(\|\|)|(\=\=)|(\!\=)|\w+");
|
||||
|
||||
List<String> tokenize() {
|
||||
if (!testRegexp.hasMatch(expression)) {
|
||||
throw new FormatException("Syntax error in '$expression'");
|
||||
throw FormatException("Syntax error in '$expression'");
|
||||
}
|
||||
for (Match match in regexp.allMatches(expression)) {
|
||||
tokens.add(match[0]!);
|
||||
@@ -103,7 +101,7 @@ class TermVariable {
|
||||
String termValue(environment) {
|
||||
var value = environment[name];
|
||||
if (value == null) {
|
||||
throw new ExprEvaluationException("Could not find '$name' in environment "
|
||||
throw ExprEvaluationException("Could not find '$name' in environment "
|
||||
"while evaluating status file expression.");
|
||||
}
|
||||
return value.toString();
|
||||
@@ -163,9 +161,8 @@ class SetIf implements SetExpression {
|
||||
|
||||
SetIf(this.left, this.right);
|
||||
|
||||
Set<String> evaluate(environment) => right.evaluate(environment)
|
||||
? left.evaluate(environment)
|
||||
: new Set<String>();
|
||||
Set<String> evaluate(environment) =>
|
||||
right.evaluate(environment) ? left.evaluate(environment) : Set<String>();
|
||||
String toString() => "($left if $right)";
|
||||
}
|
||||
|
||||
@@ -174,7 +171,7 @@ class SetConstant implements SetExpression {
|
||||
|
||||
SetConstant(String v) : value = v.toLowerCase();
|
||||
|
||||
Set<String> evaluate(environment) => new Set<String>.from([value]);
|
||||
Set<String> evaluate(environment) => Set<String>.from([value]);
|
||||
String toString() => value;
|
||||
}
|
||||
|
||||
@@ -208,7 +205,7 @@ class ExpressionParser {
|
||||
while (scanner.hasMore() && scanner.current == Token.UNION) {
|
||||
scanner.advance();
|
||||
SetExpression right = parseSetIf();
|
||||
left = new SetUnion(left, right);
|
||||
left = SetUnion(left, right);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
@@ -218,7 +215,7 @@ class ExpressionParser {
|
||||
while (scanner.hasMore() && scanner.current == "if") {
|
||||
scanner.advance();
|
||||
BooleanExpression right = parseBooleanExpression();
|
||||
left = new SetIf(left, right);
|
||||
left = SetIf(left, right);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
@@ -228,7 +225,7 @@ class ExpressionParser {
|
||||
while (scanner.hasMore() && scanner.current == Token.OR) {
|
||||
scanner.advance();
|
||||
SetExpression right = parseSetAtomic();
|
||||
left = new SetUnion(left, right);
|
||||
left = SetUnion(left, right);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
@@ -238,16 +235,16 @@ class ExpressionParser {
|
||||
scanner.advance();
|
||||
SetExpression value = parseSetExpression();
|
||||
if (scanner.current != Token.RIGHT_PAREN) {
|
||||
throw new FormatException("Missing right parenthesis in expression");
|
||||
throw FormatException("Missing right parenthesis in expression");
|
||||
}
|
||||
scanner.advance();
|
||||
return value;
|
||||
}
|
||||
if (!new RegExp(r"^\w+$").hasMatch(scanner.current!)) {
|
||||
throw new FormatException(
|
||||
if (!RegExp(r"^\w+$").hasMatch(scanner.current!)) {
|
||||
throw FormatException(
|
||||
"Expected identifier in expression, got ${scanner.current}");
|
||||
}
|
||||
SetExpression value = new SetConstant(scanner.current!);
|
||||
SetExpression value = SetConstant(scanner.current!);
|
||||
scanner.advance();
|
||||
return value;
|
||||
}
|
||||
@@ -259,7 +256,7 @@ class ExpressionParser {
|
||||
while (scanner.hasMore() && scanner.current == Token.OR) {
|
||||
scanner.advance();
|
||||
BooleanExpression right = parseBooleanAnd();
|
||||
left = new BooleanOperation(Token.OR, left, right);
|
||||
left = BooleanOperation(Token.OR, left, right);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
@@ -269,7 +266,7 @@ class ExpressionParser {
|
||||
while (scanner.hasMore() && scanner.current == Token.AND) {
|
||||
scanner.advance();
|
||||
BooleanExpression right = parseBooleanAtomic();
|
||||
left = new BooleanOperation(Token.AND, left, right);
|
||||
left = BooleanOperation(Token.AND, left, right);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
@@ -279,7 +276,7 @@ class ExpressionParser {
|
||||
scanner.advance();
|
||||
BooleanExpression value = parseBooleanExpression();
|
||||
if (scanner.current != Token.RIGHT_PAREN) {
|
||||
throw new FormatException("Missing right parenthesis in expression");
|
||||
throw FormatException("Missing right parenthesis in expression");
|
||||
}
|
||||
scanner.advance();
|
||||
return value;
|
||||
@@ -288,29 +285,29 @@ class ExpressionParser {
|
||||
// The only atomic booleans are of the form $variable == value or
|
||||
// of the form $variable.
|
||||
if (scanner.current != Token.DOLLAR_SYMBOL) {
|
||||
throw new FormatException(
|
||||
throw FormatException(
|
||||
"Expected \$ in expression, got ${scanner.current}");
|
||||
}
|
||||
scanner.advance();
|
||||
if (!new RegExp(r"^\w+$").hasMatch(scanner.current!)) {
|
||||
throw new FormatException(
|
||||
if (!RegExp(r"^\w+$").hasMatch(scanner.current!)) {
|
||||
throw FormatException(
|
||||
"Expected identifier in expression, got ${scanner.current}");
|
||||
}
|
||||
TermVariable left = new TermVariable(scanner.current!);
|
||||
TermVariable left = TermVariable(scanner.current!);
|
||||
scanner.advance();
|
||||
if (scanner.current == Token.EQUALS ||
|
||||
scanner.current == Token.NOT_EQUALS) {
|
||||
bool negate = scanner.current == Token.NOT_EQUALS;
|
||||
scanner.advance();
|
||||
if (!new RegExp(r"^\w+$").hasMatch(scanner.current!)) {
|
||||
throw new FormatException(
|
||||
if (!RegExp(r"^\w+$").hasMatch(scanner.current!)) {
|
||||
throw FormatException(
|
||||
"Expected value in expression, got ${scanner.current}");
|
||||
}
|
||||
TermConstant right = new TermConstant(scanner.current!);
|
||||
TermConstant right = TermConstant(scanner.current!);
|
||||
scanner.advance();
|
||||
return new Comparison(left, right, negate);
|
||||
return Comparison(left, right, negate);
|
||||
} else {
|
||||
return new BooleanVariable(left);
|
||||
return BooleanVariable(left);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ import "status_expression.dart";
|
||||
|
||||
import '../expectation.dart' show Expectation, ExpectationSet;
|
||||
|
||||
final RegExp SplitComment = new RegExp("^([^#]*)(#.*)?\$");
|
||||
final RegExp HeaderPattern = new RegExp(r"^\[([^\]]+)\]");
|
||||
final RegExp RulePattern = new RegExp(r"\s*([^: ]*)\s*:(.*)");
|
||||
final RegExp IssueNumberPattern = new RegExp("[Ii]ssue ([0-9]+)");
|
||||
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;
|
||||
@@ -47,17 +47,17 @@ class Section {
|
||||
}
|
||||
}
|
||||
|
||||
Future<TestExpectations> ReadTestExpectations(List<String> statusFilePaths,
|
||||
Future<TestExpectations> readTestExpectations(List<String> statusFilePaths,
|
||||
Map<String, String> environment, ExpectationSet expectationSet) {
|
||||
var testExpectations = new TestExpectations(expectationSet);
|
||||
var testExpectations = TestExpectations(expectationSet);
|
||||
return Future.wait(statusFilePaths.map((String statusFile) {
|
||||
return ReadTestExpectationsInto(testExpectations, statusFile, environment);
|
||||
return readTestExpectationsInto(testExpectations, statusFile, environment);
|
||||
})).then((_) => testExpectations);
|
||||
}
|
||||
|
||||
Future<void> ReadTestExpectationsInto(TestExpectations expectations,
|
||||
Future<void> readTestExpectationsInto(TestExpectations expectations,
|
||||
String statusFilePath, Map<String, String> environment) {
|
||||
var completer = new Completer();
|
||||
var completer = Completer();
|
||||
List<Section> sections = <Section>[];
|
||||
|
||||
void sectionsRead() {
|
||||
@@ -71,29 +71,29 @@ Future<void> ReadTestExpectationsInto(TestExpectations expectations,
|
||||
completer.complete();
|
||||
}
|
||||
|
||||
ReadConfigurationInto(new Path(statusFilePath), sections, sectionsRead);
|
||||
readConfigurationInto(Path(statusFilePath), sections, sectionsRead);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
void ReadConfigurationInto(Path path, List<Section> sections, void onDone()) {
|
||||
StatusFile statusFile = new StatusFile(path);
|
||||
File file = new File(path.toNativePath());
|
||||
void readConfigurationInto(Path path, List<Section> sections, void onDone()) {
|
||||
StatusFile statusFile = StatusFile(path);
|
||||
File file = File(path.toNativePath());
|
||||
if (!file.existsSync()) {
|
||||
throw new Exception('Cannot find test status file $path');
|
||||
throw Exception('Cannot find test status file $path');
|
||||
}
|
||||
int lineNumber = 0;
|
||||
Stream<String> lines = file
|
||||
.openRead()
|
||||
.cast<List<int>>()
|
||||
.transform(utf8.decoder)
|
||||
.transform(new LineSplitter());
|
||||
.transform(LineSplitter());
|
||||
|
||||
Section currentSection = new Section.always(statusFile, -1);
|
||||
Section currentSection = Section.always(statusFile, -1);
|
||||
sections.add(currentSection);
|
||||
|
||||
lines.listen((String line) {
|
||||
lineNumber++;
|
||||
Match? match = SplitComment.firstMatch(line);
|
||||
Match? match = splitComment.firstMatch(line);
|
||||
line = (match == null) ? "" : match[1]!;
|
||||
line = line.trim();
|
||||
if (line.isEmpty) return;
|
||||
@@ -101,36 +101,36 @@ void ReadConfigurationInto(Path path, List<Section> sections, void onDone()) {
|
||||
// Extract the comment to get the issue number if needed.
|
||||
String comment = (match == null || match[2] == null) ? "" : match[2]!;
|
||||
|
||||
match = HeaderPattern.firstMatch(line);
|
||||
match = headerPattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
String condition_string = match[1]!.trim();
|
||||
List<String> tokens = new Tokenizer(condition_string).tokenize();
|
||||
ExpressionParser parser = new ExpressionParser(new Scanner(tokens));
|
||||
String conditionString = match[1]!.trim();
|
||||
List<String> tokens = Tokenizer(conditionString).tokenize();
|
||||
ExpressionParser parser = ExpressionParser(Scanner(tokens));
|
||||
currentSection =
|
||||
new Section(statusFile, parser.parseBooleanExpression(), lineNumber);
|
||||
Section(statusFile, parser.parseBooleanExpression(), lineNumber);
|
||||
sections.add(currentSection);
|
||||
return;
|
||||
}
|
||||
|
||||
match = RulePattern.firstMatch(line);
|
||||
match = rulePattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
String name = match[1]!.trim();
|
||||
// TODO(whesse): Handle test names ending in a wildcard (*).
|
||||
String expression_string = match[2]!.trim();
|
||||
List<String> tokens = new Tokenizer(expression_string).tokenize();
|
||||
String expressionString = match[2]!.trim();
|
||||
List<String> tokens = Tokenizer(expressionString).tokenize();
|
||||
SetExpression expression =
|
||||
new ExpressionParser(new Scanner(tokens)).parseSetExpression();
|
||||
ExpressionParser(Scanner(tokens)).parseSetExpression();
|
||||
|
||||
// Look for issue number in comment.
|
||||
String? issueString = null;
|
||||
match = IssueNumberPattern.firstMatch(comment);
|
||||
match = issueNumberPattern.firstMatch(comment);
|
||||
if (match != null) {
|
||||
issueString = match[1];
|
||||
if (issueString == null) issueString = match[2];
|
||||
}
|
||||
int? issue = issueString != null ? int.parse(issueString) : null;
|
||||
currentSection.testRules
|
||||
.add(new TestRule(name, expression, issue, lineNumber));
|
||||
.add(TestRule(name, expression, issue, lineNumber));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -164,15 +164,11 @@ class TestExpectations {
|
||||
Map<String, RegExp>? _regExpCache;
|
||||
Map<String, List<RegExp>>? _keyToRegExps;
|
||||
|
||||
/**
|
||||
* Create a TestExpectations object. See the [expectations] method
|
||||
* for an explanation of matching.
|
||||
*/
|
||||
/// Create a TestExpectations object. See the [expectations] method
|
||||
/// for an explanation of matching.
|
||||
TestExpectations(this.expectationSet) : _map = {};
|
||||
|
||||
/**
|
||||
* Add a rule to the expectations.
|
||||
*/
|
||||
/// 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.
|
||||
@@ -181,22 +177,20 @@ class TestExpectations {
|
||||
}
|
||||
var names = testRule.expression.evaluate(environment);
|
||||
var expectations = names.map((name) => expectationSet[name]);
|
||||
_map.putIfAbsent(testRule.name, () => new Set()).addAll(expectations);
|
||||
_map.putIfAbsent(testRule.name, () => Set()).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.
|
||||
*/
|
||||
/// 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 = new Set<Expectation>();
|
||||
var result = Set<Expectation>();
|
||||
var splitFilename = filename.split('/');
|
||||
|
||||
// Create mapping from keys to list of RegExps once and for all.
|
||||
@@ -233,12 +227,12 @@ class TestExpectations {
|
||||
_map.forEach((key, expectations) {
|
||||
if (_keyToRegExps![key] != null) return;
|
||||
var splitKey = key.split('/');
|
||||
var regExps = new List<RegExp>.generate(splitKey.length, (int i) {
|
||||
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 = new RegExp(pattern);
|
||||
regExp = RegExp(pattern);
|
||||
_regExpCache![component] = regExp;
|
||||
}
|
||||
return regExp;
|
||||
|
||||
@@ -63,7 +63,7 @@ class FileBasedTestDescription extends TestDescription {
|
||||
} else if (path.contains(pattern)) {
|
||||
hasMatch = true;
|
||||
}
|
||||
return hasMatch ? new FileBasedTestDescription(root, entity) : null;
|
||||
return hasMatch ? FileBasedTestDescription(root, entity) : null;
|
||||
}
|
||||
|
||||
String formatError(String message) {
|
||||
|
||||
@@ -56,11 +56,11 @@ class TestRoot {
|
||||
List<RegExp> get excludedFromAnalysis => analyze.exclude;
|
||||
|
||||
Iterable<Dart> get dartSuites {
|
||||
return new List<Dart>.from(suites.where((Suite suite) => suite is Dart));
|
||||
return List<Dart>.from(suites.whereType<Dart>());
|
||||
}
|
||||
|
||||
Iterable<Chain> get toolChains {
|
||||
return new List<Chain>.from(suites.where((Suite suite) => suite is Chain));
|
||||
return List<Chain>.from(suites.whereType<Chain>());
|
||||
}
|
||||
|
||||
String toString() {
|
||||
@@ -68,7 +68,7 @@ class TestRoot {
|
||||
}
|
||||
|
||||
static Future<TestRoot> fromUri(Uri uri) async {
|
||||
String jsonText = await new File.fromUri(uri).readAsString();
|
||||
String jsonText = await File.fromUri(uri).readAsString();
|
||||
Map data = json.decode(jsonText);
|
||||
|
||||
addDefaults(data);
|
||||
@@ -76,14 +76,14 @@ class TestRoot {
|
||||
Uri packages = uri.resolve(data["packages"]);
|
||||
|
||||
List<Suite> suites = data["suites"]
|
||||
.map<Suite>((json) => new Suite.fromJsonMap(uri, json))
|
||||
.map<Suite>((json) => Suite.fromJsonMap(uri, json))
|
||||
.toList();
|
||||
|
||||
Analyze analyze = await Analyze.fromJsonMap(uri, data["analyze"], suites);
|
||||
|
||||
suites.add(analyze);
|
||||
|
||||
return new TestRoot(packages, suites);
|
||||
return TestRoot(packages, suites);
|
||||
}
|
||||
|
||||
static void addDefaults(Map data) {
|
||||
|
||||
@@ -16,14 +16,16 @@ import 'log.dart' show StdoutLogger;
|
||||
Future runGuarded(Future f(),
|
||||
{void Function(String)? printLineOnStdout,
|
||||
void Function(dynamic, StackTrace)? handleLateError}) {
|
||||
// ignore: prefer_typing_uninitialized_variables
|
||||
var printWrapper;
|
||||
if (printLineOnStdout != null) {
|
||||
// ignore: non_constant_identifier_names
|
||||
printWrapper = (_1, _2, _3, String line) {
|
||||
printLineOnStdout(line);
|
||||
};
|
||||
}
|
||||
|
||||
Completer completer = new Completer();
|
||||
Completer completer = Completer();
|
||||
|
||||
handleUncaughtError(error, StackTrace stackTrace) {
|
||||
StdoutLogger().logUncaughtError(error, stackTrace);
|
||||
@@ -46,9 +48,9 @@ Future runGuarded(Future f(),
|
||||
}
|
||||
}
|
||||
|
||||
ZoneSpecification specification = new ZoneSpecification(print: printWrapper);
|
||||
ZoneSpecification specification = ZoneSpecification(print: printWrapper);
|
||||
|
||||
ReceivePort errorPort = new ReceivePort();
|
||||
ReceivePort errorPort = ReceivePort();
|
||||
Future errorFuture = errorPort.listen((_errors) {
|
||||
List errors = _errors;
|
||||
Isolate.current.removeErrorListener(errorPort.sendPort);
|
||||
@@ -56,7 +58,7 @@ Future runGuarded(Future f(),
|
||||
var error = errors[0];
|
||||
var stackTrace = errors[1];
|
||||
if (stackTrace != null) {
|
||||
stackTrace = new StackTrace.fromString(stackTrace);
|
||||
stackTrace = StackTrace.fromString(stackTrace);
|
||||
}
|
||||
handleUncaughtError(error, stackTrace);
|
||||
}).asFuture();
|
||||
@@ -65,7 +67,7 @@ Future runGuarded(Future f(),
|
||||
Isolate.current.addErrorListener(errorPort.sendPort);
|
||||
return acknowledgeControlMessages(Isolate.current).then((_) {
|
||||
runZonedGuarded(
|
||||
() => new Future(f).then(completer.complete),
|
||||
() => Future(f).then(completer.complete),
|
||||
handleUncaughtError,
|
||||
zoneSpecification: specification,
|
||||
);
|
||||
@@ -83,7 +85,7 @@ Future runGuarded(Future f(),
|
||||
/// messages are things like [Isolate.addErrorListener] and
|
||||
/// [Isolate.addOnExitListener].
|
||||
Future acknowledgeControlMessages(Isolate isolate, {Capability? resume}) {
|
||||
ReceivePort ping = new ReceivePort();
|
||||
ReceivePort ping = ReceivePort();
|
||||
Isolate.current.ping(ping.sendPort);
|
||||
if (resume == null) {
|
||||
return ping.first;
|
||||
|
||||
@@ -3,5 +3,10 @@
|
||||
name: testing
|
||||
# This package is not intended for consumption on pub.dev. DO NOT publish.
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
|
||||
# Use 'any' constraints here; we get our versions from the DEPS file.
|
||||
dev_dependency:
|
||||
lints: any
|
||||
|
||||
Reference in New Issue
Block a user