[pkg/status_file] analyze using package:lints

Change-Id: Iac4401d92f21fc1edd49e3abd9fc6c5474337cae
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/250769
Reviewed-by: Nate Bosch <nbosch@google.com>
Commit-Queue: Devon Carew <devoncarew@google.com>
This commit is contained in:
Devon Carew 2022-07-06 22:48:35 +00:00 committed by Commit Bot
parent aa2e19827f
commit 58392c017a
18 changed files with 211 additions and 183 deletions

View file

@ -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/recommended.yaml
analyzer:
errors:
# Allow having TODOs in the code

View file

@ -15,10 +15,10 @@ void main(List<String> arguments) {
var path = arguments[0];
if (new File(path).existsSync()) {
if (File(path).existsSync()) {
formatFile(path);
} else if (new Directory(path).existsSync()) {
for (var entry in new Directory(path).listSync(recursive: true)) {
} else if (Directory(path).existsSync()) {
for (var entry in Directory(path).listSync(recursive: true)) {
if (!entry.path.endsWith(".status")) continue;
formatFile(entry.path);
@ -28,8 +28,8 @@ void main(List<String> arguments) {
void formatFile(String path) {
try {
var statusFile = new StatusFile.read(path);
new File(path).writeAsStringSync(statusFile.serialize());
var statusFile = StatusFile.read(path);
File(path).writeAsStringSync(statusFile.serialize());
print("Formatted $path");
} on SyntaxError catch (error) {
stderr.writeln("Could not parse $path:\n$error");

View file

@ -11,7 +11,7 @@ import 'package:status_file/status_file_linter.dart';
import 'package:status_file/utils.dart';
ArgParser buildParser() {
var parser = new ArgParser();
var parser = ArgParser();
parser.addFlag("check-for-disjunctions",
negatable: false,
defaultsTo: false,
@ -78,7 +78,7 @@ void lintPath(path, {bool checkForDisjunctions = false}) {
filesWithErrors.add(path);
}
} else if (FileSystemEntity.isDirectorySync(path)) {
new Directory(path).listSync(recursive: true).forEach((entry) {
Directory(path).listSync(recursive: true).forEach((entry) {
if (!canLint(entry.path)) {
return;
}
@ -98,7 +98,7 @@ void lintPath(path, {bool checkForDisjunctions = false}) {
bool lintText(List<String> text, {bool checkForDisjunctions = false}) {
try {
var statusFile = new StatusFile.parse("stdin", text);
var statusFile = StatusFile.parse("stdin", text);
return lintStatusFile(statusFile,
checkForDisjunctions: checkForDisjunctions);
} on status_file.SyntaxError {
@ -109,7 +109,7 @@ bool lintText(List<String> text, {bool checkForDisjunctions = false}) {
bool lintFile(String path, {bool checkForDisjunctions = false}) {
try {
var statusFile = new StatusFile.read(path);
var statusFile = StatusFile.read(path);
return lintStatusFile(statusFile,
checkForDisjunctions: checkForDisjunctions);
} on status_file.SyntaxError catch (error) {
@ -128,7 +128,7 @@ bool lintStatusFile(StatusFile statusFile,
return true;
}
if (statusFile.path.isNotEmpty) {
print("${statusFile.path}");
print(statusFile.path);
}
var errors = lintingErrors.toList();
errors.sort((a, b) => a.lineNumber.compareTo((b.lineNumber)));

View file

@ -12,7 +12,7 @@ import 'package:status_file/status_file_normalizer.dart';
import 'package:status_file/utils.dart';
ArgParser buildParser() {
var parser = new ArgParser();
var parser = ArgParser();
parser.addFlag("overwrite",
abbr: 'w',
negatable: false,
@ -48,7 +48,7 @@ void main(List<String> arguments) {
if (FileSystemEntity.isFileSync(path)) {
normalizeFile(path, overwrite);
} else if (FileSystemEntity.isDirectorySync(path)) {
new Directory(path).listSync(recursive: true).forEach((entry) {
Directory(path).listSync(recursive: true).forEach((entry) {
if (!canLint(entry.path)) {
return;
}
@ -60,10 +60,10 @@ void main(List<String> arguments) {
bool normalizeFile(String path, bool writeFile) {
try {
var statusFile = new StatusFile.read(path);
var statusFile = StatusFile.read(path);
var normalizedStatusFile = normalizeStatusFile(statusFile);
if (writeFile) {
new File(path).writeAsStringSync(normalizedStatusFile.toString());
File(path).writeAsStringSync(normalizedStatusFile.toString());
print("Normalized $path");
} else {
print(normalizedStatusFile);

View file

@ -159,7 +159,7 @@ Future<StatusFile> removeNonEssentialEntries(
String conditionPrefix = section.condition != Expression.always
? "${section.condition}"
: "";
String issueText = await getIssueText(comment, resolveIssueState);
String issueText = getIssueText(comment, resolveIssueState);
String statusLine = "$conditionPrefix\t$testName\t$expectations"
"\t$comment\t$issueText";
comments.add(statusLine);
@ -217,7 +217,7 @@ void printHelp(ArgParser parser) {
}
String formatComments(List<String> comments) {
StringBuffer sb = new StringBuffer();
StringBuffer sb = StringBuffer();
for (String statusLine in comments) {
sb.writeln(statusLine);
}

View file

@ -14,24 +14,24 @@ import 'src/expression.dart';
/// Matches the header that begins a new section, like:
///
/// [ $compiler == dart2js && $minified ]
final RegExp _sectionPattern = new RegExp(r"^\[(.+?)\]");
final RegExp _sectionPattern = RegExp(r"^\[(.+?)\]");
/// Matches an entry that defines the status for a path in the current section,
/// like:
///
/// some/path/to/some_test: Pass || Fail
final RegExp _entryPattern = new RegExp(r"^([^:#]+):([^#]+)(#.*)?");
final RegExp _entryPattern = RegExp(r"^([^:#]+):([^#]+)(#.*)?");
/// Matches an issue number in a comment, like:
///
/// blah_test: Fail # Issue 1234
/// ^^^^
final RegExp _issuePattern = new RegExp(r"[Ii]ssue (\d+)");
final RegExp _issuePattern = RegExp(r"[Ii]ssue (\d+)");
/// Matches a comment and indented comment, like:
///
/// < white space > #
final RegExp _commentPattern = new RegExp(r"^(\s*)#");
final RegExp _commentPattern = RegExp(r"^(\s*)#");
/// A parsed status file, which describes how a collection of tests are
/// expected to behave under various configurations and conditions.
@ -80,7 +80,7 @@ class StatusFile {
///
/// Throws a [SyntaxError] if the file could not be parsed.
StatusFile.read(this.path) {
_parse(new File(path).readAsLinesSync());
_parse(File(path).readAsLinesSync());
}
/// Parses lines of strings coming from a status file at [path].
@ -152,11 +152,10 @@ class StatusFile {
break;
}
if (line.isEmpty) {
sectionHeaderComments.add(new EmptyEntry(_lineCount));
sectionHeaderComments.add(EmptyEntry(_lineCount));
lastEmptyLine = _lineCount;
} else {
sectionHeaderComments
.add(new CommentEntry(_lineCount, new Comment(line)));
sectionHeaderComments.add(CommentEntry(_lineCount, Comment(line)));
}
}
@ -178,7 +177,7 @@ class StatusFile {
// The current section whose rules are being parsed. Initialized to an
// implicit section that matches everything.
StatusSection section =
new StatusSection(Expression.always, -1, implicitSectionHeaderComments);
StatusSection(Expression.always, -1, implicitSectionHeaderComments);
section.entries.addAll(entries);
sections.add(section);
@ -186,12 +185,12 @@ class StatusFile {
var line = lines[_lineCount - 1];
fail(String message, [List<String>? errors]) {
throw new SyntaxError(_shortPath, _lineCount, line, message, errors);
throw SyntaxError(_shortPath, _lineCount, line, message, errors);
}
// If it is an empty line
if (line.isEmpty) {
section.entries.add(new EmptyEntry(_lineCount));
section.entries.add(EmptyEntry(_lineCount));
continue;
}
@ -200,8 +199,7 @@ class StatusFile {
if (match != null) {
try {
var condition = Expression.parse(match[1]!.trim());
section =
new StatusSection(condition, _lineCount, sectionHeaderComments);
section = StatusSection(condition, _lineCount, sectionHeaderComments);
sections.add(section);
// Reset section header comments.
sectionHeaderComments = [];
@ -226,10 +224,10 @@ class StatusFile {
});
if (match[3] == null) {
section.entries
.add(new StatusEntry(path, _lineCount, expectations, null));
.add(StatusEntry(path, _lineCount, expectations, null));
} else {
section.entries.add(new StatusEntry(
path, _lineCount, expectations, new Comment(match[3]!)));
section.entries.add(
StatusEntry(path, _lineCount, expectations, Comment(match[3]!)));
}
continue;
}
@ -238,7 +236,7 @@ class StatusFile {
// section or the next section
match = _commentPattern.firstMatch(line);
if (match != null) {
var commentEntry = new CommentEntry(_lineCount, new Comment(line));
var commentEntry = CommentEntry(_lineCount, Comment(line));
if (hasBreakFromPreviousSection(_lineCount) &&
commentBelongsToNextSectionHeader(_lineCount)) {
sectionHeaderComments.add(commentEntry);
@ -253,7 +251,7 @@ class StatusFile {
// There are no comment entries in [sectionHeaderComments], because of the
// check for [commentBelongsToSectionHeader].
assert(sectionHeaderComments.length == 0);
assert(sectionHeaderComments.isEmpty);
}
bool get isEmpty => sections.length == 1 && sections[0].isEmpty();
@ -269,7 +267,7 @@ class StatusFile {
if (errors.isNotEmpty) {
var s = errors.length > 1 ? "s" : "";
throw new SyntaxError(_shortPath, section.lineNumber,
throw SyntaxError(_shortPath, section.lineNumber,
"[ ${section.condition} ]", 'Validation error$s', errors);
}
}
@ -283,8 +281,9 @@ class StatusFile {
/// Returns the status file as a string. This preserves comments and gives a
/// "canonical" rendering of the status file that can be saved back to disc.
@override
String toString() {
var buffer = new StringBuffer();
var buffer = StringBuffer();
sections.forEach(buffer.write);
return buffer.toString();
}
@ -317,7 +316,7 @@ class StatusSection {
@override
String toString() {
var buffer = new StringBuffer();
var buffer = StringBuffer();
sectionHeaderComments.forEach(buffer.writeln);
if (condition != Expression.always) {
buffer.writeln("[ $condition ]");

View file

@ -5,7 +5,7 @@
/// The possible outcomes from running a test.
class Expectation {
/// The test completed normally and did what it intended to do.
static final Expectation pass = new Expectation._('Pass');
static final Expectation pass = Expectation._('Pass');
/// The process aborted in a way that is not a potential runtime error coming
/// from the test itself. This is not considered a failure. It means an
@ -20,22 +20,22 @@ class Expectation {
/// internal exception in dart2js itself.
///
/// * The browser process crashes.
static final Expectation crash = new Expectation._('Crash');
static final Expectation crash = Expectation._('Crash');
/// The test did not complete (either successfully or unsuccessfully) in the
/// amount of time that the test runner gave it.
static final Expectation timeout = new Expectation._('Timeout');
static final Expectation timeout = Expectation._('Timeout');
/// The test completed but did not produce the intended output.
///
/// This status is rarely used directly. Instead, most of the expectations
/// below refine this into more specific reasons *why* it failed.
static final Expectation fail = new Expectation._('Fail');
static final Expectation fail = Expectation._('Fail');
/// The test compiled and began executing but then threw an uncaught
/// exception or produced the wrong output.
static final Expectation runtimeError =
new Expectation._('RuntimeError', group: fail);
Expectation._('RuntimeError', group: fail);
/// The test failed with an error at compile time and did not execute any
/// code.
@ -44,70 +44,70 @@ class Expectation {
/// * For an analyzer test, means the analyzer reported a static error.
/// * For a dart2js test, means dart2js reported a compile error.
static final Expectation compileTimeError =
new Expectation._('CompileTimeError', group: fail);
Expectation._('CompileTimeError', group: fail);
/// The test was parsed by the spec_parser, and there was a syntax error.
static final Expectation syntaxError =
new Expectation._('SyntaxError', group: fail);
Expectation._('SyntaxError', group: fail);
/// The test itself contains a comment with `@runtime-error` in it,
/// indicating it should have produced a runtime error when run. But when it
/// was run, the test completed without error.
static final Expectation missingRuntimeError =
new Expectation._('MissingRuntimeError', group: fail);
Expectation._('MissingRuntimeError', group: fail);
/// The test itself contains a comment with `@syntax-error` in it,
/// indicating it should have produced a syntax error when compiled. But when
/// it was compiled, no error was reported.
static final Expectation missingSyntaxError =
new Expectation._('MissingSyntaxError', group: fail);
Expectation._('MissingSyntaxError', group: fail);
/// The test itself contains a comment with `@compile-error` in it,
/// indicating it should have produced an error when compiled. But when it
/// was compiled, no error was reported.
static final Expectation missingCompileTimeError =
new Expectation._('MissingCompileTimeError', group: fail);
Expectation._('MissingCompileTimeError', group: fail);
/// When the test is processed by analyzer, a static warning should be
/// reported.
static final Expectation staticWarning =
new Expectation._('StaticWarning', group: fail);
Expectation._('StaticWarning', group: fail);
/// The test itself contains a comment with `@static-warning` in it,
/// indicating analyzer should report a static warning when analyzing it, but
/// analysis did not produce any warnings.
static final Expectation missingStaticWarning =
new Expectation._('MissingStaticWarning', group: fail);
Expectation._('MissingStaticWarning', group: fail);
/// An invocation of "pub get" exited with a non-zero exit code.
// TODO(rnystrom): Is this still used? If not, remove.
static final Expectation pubGetError =
new Expectation._('PubGetError', group: fail);
Expectation._('PubGetError', group: fail);
/// The stdout or stderr produced by the test was not valid UTF-8 and could
/// not be decoded.
// TODO(rnystrom): The only test that uses this expectation is the one that
// tests that the test runner handles this expectation. Remove it?
static final Expectation nonUtf8Error =
new Expectation._('NonUtf8Output', group: fail);
Expectation._('NonUtf8Output', group: fail);
/// The stdout or stderr produced by the test was too long and had to be
/// truncated by the test runner.
static final Expectation truncatedOutput =
new Expectation._('TruncatedOutput', group: fail);
Expectation._('TruncatedOutput', group: fail);
/// The VM exited with the special exit code 252.
static final Expectation dartkCrash =
new Expectation._('DartkCrash', group: crash);
Expectation._('DartkCrash', group: crash);
/// A timeout occurred in a test using the Kernel-based front end.
static final Expectation dartkTimeout =
new Expectation._('DartkTimeout', group: timeout);
Expectation._('DartkTimeout', group: timeout);
/// A compile error was reported on a test compiled using the Kernel-based
/// front end.
static final Expectation dartkCompileTimeError =
new Expectation._('DartkCompileTimeError', group: compileTimeError);
Expectation._('DartkCompileTimeError', group: compileTimeError);
// "meta expectations"
/// A marker applied to a test to indicate that the other non-pass
@ -121,17 +121,17 @@ class Expectation {
// static error should be reported. It leads to perpetually larger status
// files and means a reader of a test can't tell what the intended behavior
// actually is without knowing which status files mention it. Remove.
static final Expectation ok = new Expectation._('OK', isMeta: true);
static final Expectation ok = Expectation._('OK', isMeta: true);
/// A marker that indicates the test takes longer to complete than most tests.
/// Tells the test runner to increase the timeout when running it.
static final Expectation slow = new Expectation._('Slow', isMeta: true);
static final Expectation slow = Expectation._('Slow', isMeta: true);
/// A marker that indicates the test takes a lot longer to complete than most
/// tests.
/// Tells the test runner to increase the timeout when running it.
static final Expectation extraSlow =
new Expectation._('ExtraSlow', isMeta: true, group: skip);
Expectation._('ExtraSlow', isMeta: true, group: skip);
/// Tells the test runner to not attempt to run the test.
///
@ -139,7 +139,7 @@ class Expectation {
/// the expected results at all. This expectation should be avoided since it's
/// doesn't indicate *why* the test is being skipped and means we won't
/// notice if the actual behavior of the test changes.
static final Expectation skip = new Expectation._('Skip', isMeta: true);
static final Expectation skip = Expectation._('Skip', isMeta: true);
/// Tells the test runner to skip the test because it takes too long to
/// complete.
@ -147,7 +147,7 @@ class Expectation {
/// Prefer this over timeout since this avoids wasting CPU resources running
/// a test we know won't complete.
static final Expectation skipSlow =
new Expectation._('SkipSlow', isMeta: true, group: skip);
Expectation._('SkipSlow', isMeta: true, group: skip);
/// Skips this test because it is not intended to be meaningful for a certain
/// reason or on some configuration.
@ -155,21 +155,21 @@ class Expectation {
/// For example, tests that use dart:io are SkipByDesign on the browser since
/// dart:io isn't supported there.
static final Expectation skipByDesign =
new Expectation._('SkipByDesign', isMeta: true);
Expectation._('SkipByDesign', isMeta: true);
/// Can be returned by the test runner to say the result should be ignored,
/// and assumed to meet the expectations, due to an infrastructure failure.
///
/// This should not appear in status files.
static final Expectation ignore = new Expectation._('Ignore');
static final Expectation ignore = Expectation._('Ignore');
/// Used by pkg/front_end/lib/src/fasta/testing, but not used by test.dart.
/// Included here so that we can parse .status files that contain it.
static final Expectation verificationError =
new Expectation._('VerificationError');
Expectation._('VerificationError');
/// Maps case-insensitive names to expectations.
static Map<String, Expectation> _all = new Map.fromIterable(<Expectation>[
static final Map<String, Expectation> _all = Map.fromIterable(<Expectation>[
pass,
crash,
timeout,
@ -199,7 +199,7 @@ class Expectation {
static Expectation find(String name) {
var expectation = _all[name.toLowerCase()];
if (expectation == null) {
throw new ArgumentError("Could not find an expectation named '$name'.");
throw ArgumentError("Could not find an expectation named '$name'.");
}
return expectation;
@ -211,7 +211,7 @@ class Expectation {
/// Whether this expectation is a test outcome. If not, it's a "meta marker".
final bool isOutcome;
Expectation._(this._name, {Expectation? group, bool isMeta: false})
Expectation._(this._name, {Expectation? group, bool isMeta = false})
: _group = group,
isOutcome = !isMeta;
@ -229,5 +229,6 @@ class Expectation {
return false;
}
@override
String toString() => _name;
}

View file

@ -5,8 +5,8 @@
import 'expression.dart';
import '../environment.dart';
final Expression T = new LogicExpression.and([]);
final Expression F = new LogicExpression.or([]);
final Expression T = LogicExpression.and([]);
final Expression F = LogicExpression.or([]);
// Token to combine left and right value of a comparison expression in a
// variable expression.
@ -32,7 +32,7 @@ Expression toDisjunctiveNormalForm(Expression expression) {
} else if (minTerms.isEmpty) {
return F;
}
var disjunctiveNormalForm = new LogicExpression.or(minTerms);
var disjunctiveNormalForm = LogicExpression.or(minTerms);
disjunctiveNormalForm = _minimizeByComplementation(disjunctiveNormalForm);
return _recoverComparisonExpressions(disjunctiveNormalForm);
}
@ -101,10 +101,10 @@ LogicExpression _minimizeByComplementation(LogicExpression expression) {
return onesInA - onesInB;
});
var combinedMinSets = _combineMinSets(
clauses.map((e) => [new LogicExpression.and(e)]).toList(), []);
clauses.map((e) => [LogicExpression.and(e)]).toList(), []);
List<List<LogicExpression>> minCover = _findMinCover(combinedMinSets, []);
var finalOperands = minCover.map((minSet) => _reduceMinSet(minSet)).toList();
return new LogicExpression.or(finalOperands).normalize();
return LogicExpression.or(finalOperands).normalize();
}
/// Computes all assignments of literals that make the [expression] evaluate to
@ -113,7 +113,7 @@ List<Expression>? _satisfiableMinTerms(Expression expression) {
var variables = _getVariables(expression);
bool hasNotSatisfiableAssignment = false;
List<Expression> satisfiableTerms = <Expression>[];
var environment = new TruthTableEnvironment(variables);
var environment = TruthTableEnvironment(variables);
for (int i = 0; i < 1 << variables.length; i++) {
environment.setConfiguration(i);
if (expression.evaluate(environment)) {
@ -125,7 +125,7 @@ List<Expression>? _satisfiableMinTerms(Expression expression) {
operands.add(variables[j]);
}
}
satisfiableTerms.add(new LogicExpression.and(operands));
satisfiableTerms.add(LogicExpression.and(operands));
} else {
hasNotSatisfiableAssignment = true;
}
@ -175,7 +175,7 @@ class TruthTableEnvironment extends Environment {
List<List<LogicExpression>> _combineMinSets(List<List<LogicExpression>> minSets,
List<List<LogicExpression>> primeImplicants) {
List<List<LogicExpression>> combined = <List<LogicExpression>>[];
var addedInThisIteration = new Set<List<LogicExpression>>();
var addedInThisIteration = <List<LogicExpression>>{};
for (var i = 0; i < minSets.length; i++) {
var minSet = minSets[i];
var combinedMinSet = false;
@ -255,7 +255,7 @@ LogicExpression _reduceMinSet(List<LogicExpression> minSet) {
negative.remove(neg);
}
}
return new LogicExpression.and(positive..addAll(negative));
return LogicExpression.and(positive..addAll(negative));
}
/// [_findMinCover] finds the minimum cover of [primaryImplicants]. Finding a
@ -316,11 +316,12 @@ bool _isCover(List<List<Expression>> cover, List<List<Expression>> implicants) {
}
// Computes the difference between two sets of expressions in disjunctive normal
// form. if the difference is a negation, the difference is only counted once.
List<Expression> _difference(List<Expression> As, List<Expression> Bs) {
var difference = <Expression>[]
..addAll(As.where((a) => _findFirst(a, Bs) == null))
..addAll(Bs.where((b) => _findFirst(b, As) == null));
// form. If the difference is a negation, the difference is only counted once.
List<Expression> _difference(List<Expression> aList, List<Expression> bList) {
var difference = <Expression>[
...aList.where((a) => _findFirst(a, bList) == null),
...bList.where((b) => _findFirst(b, aList) == null),
];
for (var expression in difference.toList()) {
if (_isNegatedExpression(expression)) {
if (_findFirst(negate(expression), difference) != null) {
@ -390,37 +391,37 @@ bool _isNegatedExpression(Expression expression) {
List<Expression> _getVariables(Expression expression) {
if (expression is LogicExpression) {
var variables = <Expression>[];
expression.operands.forEach(
(e) => _getVariables(e).forEach((v) => _addIfNotPresent(v, variables)));
for (var e in expression.operands) {
_getVariables(e).forEach((v) => _addIfNotPresent(v, variables));
}
return variables;
}
if (expression is VariableExpression) {
return [new VariableExpression(expression.variable)];
return [VariableExpression(expression.variable)];
}
if (expression is ComparisonExpression) {
throw new Exception("Cannot use ComparisonExpression for variables");
throw Exception("Cannot use ComparisonExpression for variables");
}
return [];
}
Expression negate(Expression expression, {bool positive: false}) {
Expression negate(Expression expression, {bool positive = false}) {
if (expression is LogicExpression && expression.isOr) {
return new LogicExpression.and(expression.operands
return LogicExpression.and(expression.operands
.map((e) => negate(e, positive: !positive))
.toList());
}
if (expression is LogicExpression && expression.isAnd) {
return new LogicExpression.or(expression.operands
return LogicExpression.or(expression.operands
.map((e) => negate(e, positive: !positive))
.toList());
}
if (expression is ComparisonExpression) {
return new ComparisonExpression(
return ComparisonExpression(
expression.left, expression.right, !expression.negate);
}
if (expression is VariableExpression) {
return new VariableExpression(expression.variable,
negate: !expression.negate);
return VariableExpression(expression.variable, negate: !expression.negate);
}
return expression;
}
@ -429,16 +430,15 @@ Expression negate(Expression expression, {bool positive: false}) {
// we can se individual variables truthiness in the [TruthTableEnvironment].
Expression _comparisonExpressionsToVariableExpressions(Expression expression) {
if (expression is LogicExpression) {
return new LogicExpression(
return LogicExpression(
expression.op,
expression.operands
.map((exp) => _comparisonExpressionsToVariableExpressions(exp))
.toList());
}
if (expression is ComparisonExpression) {
return new VariableExpression(
new Variable(
expression.left.name + _comparisonToken + expression.right),
return VariableExpression(
Variable(expression.left.name + _comparisonToken + expression.right),
negate: expression.negate);
}
return expression;
@ -446,7 +446,7 @@ Expression _comparisonExpressionsToVariableExpressions(Expression expression) {
Expression _recoverComparisonExpressions(Expression expression) {
if (expression is LogicExpression) {
return new LogicExpression(
return LogicExpression(
expression.op,
expression.operands
.map((exp) => _recoverComparisonExpressions(exp))
@ -455,8 +455,8 @@ Expression _recoverComparisonExpressions(Expression expression) {
if (expression is VariableExpression &&
expression.variable.name.contains(_comparisonToken)) {
int tokenIndex = expression.variable.name.indexOf(_comparisonToken);
return new ComparisonExpression(
new Variable(expression.variable.name.substring(0, tokenIndex)),
return ComparisonExpression(
Variable(expression.variable.name.substring(0, tokenIndex)),
expression.variable.name
.substring(tokenIndex + _comparisonToken.length),
expression.negate);

View file

@ -29,7 +29,7 @@ abstract class Expression implements Comparable<Expression> {
/// Expressions evaluate as expected, with values of variables found in an
/// environment passed to the evaluator.
static Expression parse(String expression) =>
new _ExpressionParser(expression).parse();
_ExpressionParser(expression).parse();
const Expression();
@ -68,6 +68,7 @@ abstract class Expression implements Comparable<Expression> {
/// This is useful for things like sorting lists of expressions or
/// normalizing a list of subexpressions. The rough logic is that higher
/// precedence and alphabetically lower expressions come first.
@override
int compareTo(Expression other) {
var comparison = _typeComparison.compareTo(other._typeComparison);
if (comparison != 0) return comparison;
@ -104,7 +105,7 @@ class Variable {
String lookup(Environment environment) {
var value = environment.lookUp(name);
if (value == null) {
throw new Exception("Could not find '$name' in environment "
throw Exception("Could not find '$name' in environment "
"while evaluating status file expression.");
}
@ -153,25 +154,29 @@ class ComparisonExpression extends Expression {
ComparisonExpression(this.left, this.right, this.negate);
@override
void validate(Environment environment, List<String> errors) {
environment.validate(left.name, right, errors);
}
@override
bool evaluate(Environment environment) {
return negate != (left.lookup(environment) == right);
}
@override
Expression normalize() {
// Replace Boolean comparisons with a straight variable expression.
if (right == "true") {
return new VariableExpression(left, negate: negate);
return VariableExpression(left, negate: negate);
} else if (right == "false") {
return new VariableExpression(left, negate: !negate);
return VariableExpression(left, negate: !negate);
} else {
return this;
}
}
@override
int _compareToMyType(ComparisonExpression other) {
if (left.name != other.left.name) {
return left.name.compareTo(other.left.name);
@ -186,8 +191,10 @@ class ComparisonExpression extends Expression {
// Comparisons come before variables so that "$compiler == ..." and
// "$runtime == ..." appear on the left in status expressions.
@override
int get _typeComparison => 1;
@override
String toString() => "\$${left.name} ${negate ? '!=' : '=='} $right";
}
@ -214,17 +221,21 @@ class VariableExpression extends Expression {
VariableExpression(this.variable, {this.negate = false});
@override
void validate(Environment environment, List<String> errors) {
// It must be a Boolean, so it should allow either Boolean value.
environment.validate(variable.name, "true", errors);
}
@override
bool evaluate(Environment environment) =>
negate != (variable.lookup(environment) == "true");
/// Variable expressions are fine as they are.
@override
Expression normalize() => this;
@override
int _compareToMyType(VariableExpression other) {
if (variable.name != other.variable.name) {
return variable.name.compareTo(other.variable.name);
@ -233,8 +244,10 @@ class VariableExpression extends Expression {
return _compareBool(negate, other.negate);
}
@override
int get _typeComparison => 2;
@override
String toString() => "${negate ? "!" : ""}\$${variable.name}";
}
@ -253,12 +266,14 @@ class LogicExpression extends Expression {
bool get isAnd => op == _Token.and;
bool get isOr => op == _Token.or;
@override
void validate(Environment environment, List<String> errors) {
for (var operand in operands) {
operand.validate(environment, errors);
}
}
@override
bool evaluate(Environment environment) {
if (op == _Token.and) {
return operands.every((operand) => operand.evaluate(environment));
@ -267,6 +282,7 @@ class LogicExpression extends Expression {
}
}
@override
LogicExpression normalize() {
// Normalize the order of the clauses. Since there is no short-circuiting,
// a || b means the same as b || a. Picking a standard order lets us
@ -274,12 +290,12 @@ class LogicExpression extends Expression {
// order.
// Recurse into the operands, sort them, and remove duplicates.
var normalized = new LogicExpression(
var normalized = LogicExpression(
op, operands.map((operand) => operand.normalize()).toList())
.operands;
normalized = flatten(normalized);
var ordered = new SplayTreeSet<Expression>.from(normalized).toList();
return new LogicExpression(op, ordered);
var ordered = SplayTreeSet<Expression>.from(normalized).toList();
return LogicExpression(op, ordered);
}
List<Expression> flatten(List<Expression> operands) {
@ -294,6 +310,7 @@ class LogicExpression extends Expression {
return newOperands;
}
@override
int _compareToMyType(LogicExpression other) {
// Put "&&" before "||".
if (op != other.op) return op == _Token.and ? -1 : 1;
@ -311,8 +328,10 @@ class LogicExpression extends Expression {
return 0;
}
@override
int get _typeComparison => 3;
@override
String toString() {
String parenthesize(Expression operand) {
var result = operand.toString();
@ -331,14 +350,14 @@ class LogicExpression extends Expression {
class _ExpressionParser {
final _Scanner _scanner;
_ExpressionParser(String expression) : _scanner = new _Scanner(expression);
_ExpressionParser(String expression) : _scanner = _Scanner(expression);
Expression parse() {
var expression = _parseOr();
// Should consume entire string.
if (_scanner.hasMore) {
throw new FormatException("Unexpected input after expression");
throw FormatException("Unexpected input after expression");
}
return expression;
@ -352,7 +371,7 @@ class _ExpressionParser {
if (operands.length == 1) return operands.single;
return new LogicExpression(_Token.or, operands);
return LogicExpression(_Token.or, operands);
}
Expression _parseAnd() {
@ -363,7 +382,7 @@ class _ExpressionParser {
if (operands.length == 1) return operands.single;
return new LogicExpression(_Token.and, operands);
return LogicExpression(_Token.and, operands);
}
Expression _parsePrimary() {
@ -372,7 +391,7 @@ class _ExpressionParser {
if (_scanner.match(_Token.leftParen)) {
var value = _parseOr();
if (!_scanner.match(_Token.rightParen)) {
throw new FormatException("Missing right parenthesis in expression");
throw FormatException("Missing right parenthesis in expression");
}
return value;
@ -386,16 +405,16 @@ class _ExpressionParser {
// The only atomic booleans are of the form $variable == value or
// of the form $variable.
if (!_scanner.match(_Token.dollar)) {
throw new FormatException(
throw FormatException(
"Expected \$ in expression, got ${_scanner.current}");
}
if (!_scanner.isIdentifier) {
throw new FormatException(
throw FormatException(
"Expected identifier in expression, got ${_scanner.current}");
}
var left = new Variable(_scanner.current!);
var left = Variable(_scanner.current!);
_scanner.advance();
if (!negate &&
@ -404,14 +423,14 @@ class _ExpressionParser {
var isNotEquals = _scanner.advance() == _Token.notEqual;
if (!_scanner.isIdentifier) {
throw new FormatException(
throw FormatException(
"Expected value in expression, got ${_scanner.current}");
}
var right = _scanner.advance()!;
return new ComparisonExpression(left, right, isNotEquals);
return ComparisonExpression(left, right, isNotEquals);
} else {
return new VariableExpression(left, negate: negate);
return VariableExpression(left, negate: negate);
}
}
}
@ -419,14 +438,14 @@ class _ExpressionParser {
/// An iterator that allows peeking at the current token.
class _Scanner {
/// Tokens are "(", ")", "$", "&&", "||", "!", ==", "!=", and (maximal) \w+.
static final _testPattern = new RegExp(r"^(?:[()$\w\s]|&&|\|\||==|!=?)+$");
static final _tokenPattern = new RegExp(r"[()$]|&&|\|\||==|!=?|\w+");
static final _testPattern = RegExp(r"^(?:[()$\w\s]|&&|\|\||==|!=?)+$");
static final _tokenPattern = RegExp(r"[()$]|&&|\|\||==|!=?|\w+");
/// Pattern that recognizes identifier tokens.
///
/// Only checks the first character, since no non-identifier token can start
/// with a word character.
static final _identifierPattern = new RegExp(r"^\w");
static final _identifierPattern = RegExp(r"^\w");
/// The token strings being iterated.
final Iterator<String> tokenIterator;
@ -439,7 +458,7 @@ class _Scanner {
static List<String> tokenize(String expression) {
if (!_testPattern.hasMatch(expression)) {
throw new FormatException("Syntax error in '$expression'");
throw FormatException("Syntax error in '$expression'");
}
return _tokenPattern

View file

@ -13,19 +13,19 @@ import 'src/expression.dart';
/// Matches the header that begins a new section, like:
///
/// [ $compiler == dart2js && $minified ]
final _sectionPattern = new RegExp(r"^\[(.+?)\]");
final _sectionPattern = RegExp(r"^\[(.+?)\]");
/// Matches an entry that defines the status for a path in the current section,
/// like:
///
/// some/path/to/some_test: Pass || Fail
final _entryPattern = new RegExp(r"^([^:#]+):(.*)");
final _entryPattern = RegExp(r"^([^:#]+):(.*)");
/// Matches an issue number in a comment, like:
///
/// blah_test: Fail # Issue 1234
/// ^^^^
final _issuePattern = new RegExp(r"[Ii]ssue (\d+)");
final _issuePattern = RegExp(r"[Ii]ssue (\d+)");
/// A parsed status file, which describes how a collection of tests are
/// expected to behave under various configurations and conditions.
@ -55,14 +55,14 @@ class StatusFile {
///
/// Throws a [SyntaxError] if the file could not be parsed.
StatusFile.read(this.path) {
var lines = new File(path).readAsLinesSync();
var lines = File(path).readAsLinesSync();
_comments.length = lines.length + 1;
for (var line in lines) {
_lineCount++;
fail(String message, [List<String>? errors]) {
throw new SyntaxError(_shortPath, _lineCount, line, message, errors);
throw SyntaxError(_shortPath, _lineCount, line, message, errors);
}
// Strip off the comment and whitespace.
@ -84,7 +84,7 @@ class StatusFile {
if (match != null) {
try {
var condition = Expression.parse(match[1]!.trim());
sections.add(new StatusSection(condition, _lineCount));
sections.add(StatusSection(condition, _lineCount));
} on FormatException {
fail("Status expression syntax error");
}
@ -111,11 +111,11 @@ class StatusFile {
// If we haven't found a section header yet, create an implicit section
// that matches everything.
if (sections.isEmpty) {
sections.add(new StatusSection(Expression.always, -1));
sections.add(StatusSection(Expression.always, -1));
}
sections.last.entries
.add(new StatusEntry(path, _lineCount, expectations, issue));
.add(StatusEntry(path, _lineCount, expectations, issue));
continue;
}
@ -138,7 +138,7 @@ class StatusFile {
if (errors.isNotEmpty) {
var s = errors.length > 1 ? "s" : "";
throw new SyntaxError(_shortPath, section.lineNumber,
throw SyntaxError(_shortPath, section.lineNumber,
"[ ${section.condition} ]", 'Validation error$s', errors);
}
}
@ -158,8 +158,9 @@ class StatusFile {
return int.parse(match[1]!);
}
@override
String toString() {
var buffer = new StringBuffer();
var buffer = StringBuffer();
for (var section in sections) {
buffer.writeln("[ ${section.condition} ]");
@ -180,7 +181,7 @@ class StatusFile {
/// Unlike [toString()], this preserves comments and gives a "canonical"
/// rendering of the status file that can be saved back to disc.
String serialize() {
var buffer = new StringBuffer();
var buffer = StringBuffer();
var lastLine = 0;
var needBlankLine = false;
@ -283,8 +284,9 @@ class SyntaxError implements Exception {
SyntaxError(this.file, this.lineNumber, this.line, this.message, this.errors);
@override
String toString() {
var buffer = new StringBuffer();
var buffer = StringBuffer();
buffer.writeln('$message in "$file" line $lineNumber:');
buffer.writeln(line);

View file

@ -11,6 +11,7 @@ class LintingError {
final String message;
LintingError(this.lineNumber, this.message);
@override
String toString() {
return "Error at line $lineNumber: $message";
}
@ -55,14 +56,14 @@ Iterable<LintingError> lintCommentLinesInSection(StatusSection section) {
for (var entry in section.entries) {
seenStatusEntry = seenStatusEntry || entry is StatusEntry;
if (seenStatusEntry && entry is CommentEntry) {
lintingErrors.add(new LintingError(
entry.lineNumber, "Comment is on a line by itself."));
lintingErrors.add(
LintingError(entry.lineNumber, "Comment is on a line by itself."));
}
}
return lintingErrors;
}
return section.entries.whereType<CommentEntry>().map((entry) =>
new LintingError(entry.lineNumber, "Comment is on a line by itself."));
LintingError(entry.lineNumber, "Comment is on a line by itself."));
}
/// Checks for disjunctions in headers. Disjunctions should be separated out.
@ -84,7 +85,7 @@ Iterable<LintingError> lintCommentLinesInSection(StatusSection section) {
Iterable<LintingError> lintDisjunctionsInHeader(StatusSection section) {
if (section.condition.toString().contains("||")) {
return [
new LintingError(
LintingError(
section.lineNumber,
"Expression contains '||'. Please split the expression into multiple "
"separate sections.")
@ -104,7 +105,7 @@ Iterable<LintingError> lintAlphabeticalOrderingOfPaths(StatusSection section) {
var witness = _findNotEqualWitness<String>(sortedList, entries);
if (witness != null) {
return [
new LintingError(
LintingError(
section.lineNumber,
"Test paths are not alphabetically ordered in section. "
"${witness.first} should come before ${witness.second}.")
@ -119,7 +120,7 @@ Iterable<LintingError> lintNormalizedSection(StatusSection section) {
var normalized = section.condition.normalize().toString();
if (nonNormalized != normalized) {
return [
new LintingError(
LintingError(
section.lineNumber,
"Condition expression should be '$normalized' "
"but was '$nonNormalized'.")
@ -140,10 +141,10 @@ Iterable<LintingError> lintSectionEntryDuplicates(StatusSection section) {
if (entry.path == otherEntry.path &&
_findNotEqualWitness(entry.expectations, otherEntry.expectations) ==
null) {
errors.add(new LintingError(
errors.add(LintingError(
section.lineNumber,
"The status entry "
"'${entry}' is duplicated on lines "
"'$entry' is duplicated on lines "
"${entry.lineNumber} and ${otherEntry.lineNumber}."));
}
}
@ -182,7 +183,7 @@ Iterable<LintingError> lintSectionHeaderOrdering(List<StatusSection> sections) {
var witness = _findNotEqualWitness<StatusSection>(sorted, unsorted);
if (witness != null) {
return [
new LintingError(
LintingError(
witness.second!.lineNumber,
"Section expressions are not correctly ordered in file. "
"'${witness.first!.condition}' on line ${witness.first!.lineNumber} "
@ -203,7 +204,7 @@ Iterable<LintingError> lintSectionHeaderDuplicates(
var section = sorted[i];
var previousSection = sorted[i - 1];
if (section.condition.compareTo(previousSection.condition) == 0) {
errors.add(new LintingError(
errors.add(LintingError(
section.lineNumber,
"The condition "
"'${section.condition}' is duplicated on lines "
@ -219,11 +220,11 @@ ListNotEqualWitness<T>? _findNotEqualWitness<T>(List<T> first, List<T> second) {
}
for (var i = 0; i < math.max(first.length, second.length); i++) {
if (i >= second.length) {
return new ListNotEqualWitness(first[i], null);
return ListNotEqualWitness(first[i], null);
} else if (i >= first.length) {
return new ListNotEqualWitness(null, second[i]);
return ListNotEqualWitness(null, second[i]);
} else if (first[i] != second[i]) {
return new ListNotEqualWitness(first[i], second[i]);
return ListNotEqualWitness(first[i], second[i]);
}
}
return null;

View file

@ -9,10 +9,10 @@ import 'dart:convert';
StatusFile normalizeStatusFile(StatusFile statusFile) {
StatusFile newStatusFile = _sortSectionsAndCombine(statusFile);
newStatusFile.sections.forEach((section) {
for (var section in newStatusFile.sections) {
_sortEntriesInSection(section);
_oneLineBetweenSections(section);
});
}
// Remove empty line at the end of the file
newStatusFile.sections.last.entries.removeLast();
return newStatusFile;
@ -44,32 +44,32 @@ void _sortEntriesInSection(StatusSection section) {
void _oneLineBetweenSections(StatusSection section) {
section.entries.removeWhere((entry) => entry is EmptyEntry);
section.entries
.add(new EmptyEntry(section.lineNumber + section.entries.length + 1));
.add(EmptyEntry(section.lineNumber + section.entries.length + 1));
}
StatusFile _sortSectionsAndCombine(StatusFile statusFile) {
// Create the new status file to be returned.
StatusFile oldStatusFile = new StatusFile.parse(
StatusFile oldStatusFile = StatusFile.parse(
statusFile.path, LineSplitter.split(statusFile.toString()).toList());
List<StatusSection> newSections = [];
// Copy over all sections and normalize all the expressions.
oldStatusFile.sections.forEach((section) {
for (var section in oldStatusFile.sections) {
if (section.condition != Expression.always) {
if (section.isEmpty()) return;
if (section.isEmpty()) continue;
newSections.add(new StatusSection(section.condition.normalize(),
newSections.add(StatusSection(section.condition.normalize(),
section.lineNumber, section.sectionHeaderComments)
..entries.addAll(section.entries));
} else {
newSections.add(section);
}
});
}
// Sort the headers
newSections.sort((a, b) => a.condition.compareTo(b.condition));
// See if we can combine section headers by simple comparison.
var newStatusFile = new StatusFile(statusFile.path);
var newStatusFile = StatusFile(statusFile.path);
newStatusFile.sections.add(newSections[0]);
for (var i = 1; i < newSections.length; i++) {
var previousSection = newSections[i - 1];

View file

@ -13,3 +13,4 @@ dependencies:
# Use 'any' constraints here; we get our versions from the DEPS file.
dev_dependencies:
expect: any
lints: any

View file

@ -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.
// ignore_for_file: non_constant_identifier_names
import 'package:expect/expect.dart';
import 'package:status_file/canonical_status_file.dart';
import 'package:status_file/status_file_linter.dart';
@ -36,7 +38,7 @@ void main() {
}
StatusFile createFromString(String text) {
return new StatusFile.parse("test", text.split('\n'));
return StatusFile.parse("test", text.split('\n'));
}
expectError(String text, String expectedError, {bool disjunctions = false}) {

View file

@ -24,33 +24,33 @@ main() {
void normalizeCheck() {
var files = getStatusFiles();
for (var file in files) {
print("------- " + file.path + " -------");
var statusFile = new StatusFile.read(file.path);
var statusFileOther = normalizeStatusFile(new StatusFile.read(file.path));
print("------- ${file.path} -------");
var statusFile = StatusFile.read(file.path);
var statusFileOther = normalizeStatusFile(StatusFile.read(file.path));
checkSemanticallyEqual(statusFile, statusFileOther,
warnOnDuplicateHeader: true);
checkFileHeaderIntact(statusFile, statusFileOther);
print("------- " + file.path + " -------");
print("------- ${file.path} -------");
}
}
void sanityCheck() {
var files = getStatusFiles();
for (var file in files) {
print("------- " + file.path + " -------");
var statusFile = new StatusFile.read(file.path);
var statusFileOther = new StatusFile.read(file.path);
print("------- ${file.path} -------");
var statusFile = StatusFile.read(file.path);
var statusFileOther = StatusFile.read(file.path);
checkSemanticallyEqual(statusFile, statusFileOther,
warnOnDuplicateHeader: true);
checkFileHeaderIntact(statusFile, statusFileOther);
print("------- " + file.path + " -------");
print("------- ${file.path} -------");
}
}
List<FileSystemEntity> getStatusFiles() {
var statusFiles = <FileSystemEntity>[];
for (var entry
in new Directory.fromUri(statusFilePath).listSync(recursive: true)) {
in Directory.fromUri(statusFilePath).listSync(recursive: true)) {
statusFiles.add(entry);
}
return statusFiles;
@ -64,7 +64,7 @@ void checkSemanticallyEqual(StatusFile original, StatusFile normalized,
print(original);
print("==================");
print(normalized);
throw new Exception("The count of entries in original is "
throw Exception("The count of entries in original is "
"$entriesInOriginal and the count of entries in normalized is "
"$entriesInNormalized. Those two numbers are not the same.");
}
@ -77,8 +77,7 @@ void checkSemanticallyEqual(StatusFile original, StatusFile normalized,
int countEntries(StatusFile statusFile) {
return statusFile.sections
.map((section) =>
section.entries.where((entry) => entry is StatusEntry).length)
.map((section) => section.entries.whereType<StatusEntry>().length)
.fold(0, (count, sum) => count + sum);
}
@ -96,29 +95,29 @@ void findInStatusFile(
entry.path.compareTo(entryToFind.path) == 0 &&
listEqual(entry.expectations, entryToFind.expectations))
.toList();
if (matchingEntries.length == 0) {
if (matchingEntries.isEmpty) {
var message = "Could not find the entry even though the section "
"header matched on line number ${section.lineNumber}. Sections "
"should be unique.";
if (warnOnDuplicateHeader) {
print(message);
} else {
throw new Exception(message);
throw Exception(message);
}
} else if (matchingEntries.length == 1 && foundEntryPosition >= 0) {
throw new Exception("The entry '$entryToFind' on line "
throw Exception("The entry '$entryToFind' on line "
"${entryToFind.lineNumber} in section ${section.condition} was "
"already found in a previous section on line $foundEntryPosition.");
} else if (matchingEntries.length == 1) {
foundEntryPosition = matchingEntries[0].lineNumber;
} else {
throw new Exception("The entry '$entryToFind' on line "
throw Exception("The entry '$entryToFind' on line "
"${entryToFind.lineNumber} in section ${section.condition} on line "
"${section.lineNumber} had multiple matches in section.");
}
}
if (foundEntryPosition < 0) {
throw new Exception("Could not find entry '$entryToFind' under the "
throw Exception("Could not find entry '$entryToFind' under the "
"condition $condition in the status file.");
}
}
@ -128,7 +127,7 @@ void checkFileHeaderIntact(StatusFile original, StatusFile normalized) {
var normalizedHeader =
normalized.sections.first.sectionHeaderComments.toString();
if (originalHeader != normalizedHeader) {
throw new Exception(
throw Exception(
"File headers changed.\nExpected:\n$originalHeader\n\nActual:\n$normalizedHeader");
}
}

View file

@ -12,16 +12,16 @@ final Uri repoRoot = Platform.script.resolve("../../../");
void main() {
// Parse every status file in the repository.
for (var directory in ["tests", "runtime/tests"]) {
for (var entry in new Directory.fromUri(repoRoot.resolve(directory))
for (var entry in Directory.fromUri(repoRoot.resolve(directory))
.listSync(recursive: true)) {
if (!entry.path.endsWith(".status")) continue;
try {
var statusFile = new StatusFile.read(entry.path);
var statusFile = StatusFile.read(entry.path);
statusFile.toString();
} catch (err, st) {
print(err);
print(st);
throw new Exception("Could not parse '${entry.path}'.\n$err");
throw Exception("Could not parse '${entry.path}'.\n$err");
}
}
}

View file

@ -14,11 +14,11 @@ final Uri repoRoot = Platform.script.resolve("../../../");
void main() {
// Parse every status file in the repository.
for (var directory in ["tests", "runtime/tests"]) {
for (var entry in new Directory.fromUri(repoRoot.resolve(directory))
for (var entry in Directory.fromUri(repoRoot.resolve(directory))
.listSync(recursive: true)) {
if (!entry.path.endsWith(".status")) continue;
try {
new StatusFile.read(entry.path);
StatusFile.read(entry.path);
} catch (err, stack) {
Expect.fail("Could not parse '${entry.path}'.\n$err\n$stack");
}

View file

@ -12,11 +12,13 @@ class TestEnvironment implements Environment {
TestEnvironment(this._values);
@override
void validate(String name, String value, List<String> errors) {
throw new UnimplementedError();
throw UnimplementedError();
}
/// Looks up the value of the variable with [name].
@override
String? lookUp(String name) => _values[name];
operator []=(String key, String value) => _values[key] = value;
@ -39,7 +41,7 @@ void testExpression() {
expression.toString());
// Test BooleanExpression.evaluate().
var environment = new TestEnvironment({"arch": "dartc", "mode": "debug"});
var environment = TestEnvironment({"arch": "dartc", "mode": "debug"});
Expect.isTrue(expression.evaluate(environment));
environment["mode"] = "release";
@ -67,7 +69,7 @@ void testBoolean() {
// Test BooleanExpression.evaluate().
var environment =
new TestEnvironment({"arch": "ia32", "checked": "true", "mode": "debug"});
TestEnvironment({"arch": "ia32", "checked": "true", "mode": "debug"});
Expect.isTrue(expression.evaluate(environment));
environment["mode"] = "release";
@ -88,8 +90,8 @@ void testNotBoolean() {
Expect.equals(
r"$arch == ia32 && !$checked || $mode == release", expression.toString());
var environment = new TestEnvironment(
{"arch": "ia32", "checked": "false", "mode": "debug"});
var environment =
TestEnvironment({"arch": "ia32", "checked": "false", "mode": "debug"});
Expect.isTrue(expression.evaluate(environment));
environment["mode"] = "release";
@ -111,7 +113,7 @@ void testNotEqual() {
r"$compiler == dart2js && $runtime != ie9", expression.toString());
// Test BooleanExpression.evaluate().
var environment = new TestEnvironment({
var environment = TestEnvironment({
"compiler": "none",
"runtime": "ie9",
});