Reformat pkg/status_file.

I was starting to migrate it to use primary constructors but realized
the formatting was out of date, so I figured I may as well fix that
first so that the migration CL is easier to read.

There are no changes in this CL, I only ran `dart format .`.

Change-Id: I25f772ce0e0a00d83f1f8b561fc8bb9fe9486859
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/506741
Auto-Submit: Bob Nystrom <rnystrom@google.com>
Commit-Queue: Bob Nystrom <rnystrom@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
This commit is contained in:
Robert Nystrom
2026-05-27 14:43:43 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 8b555128be
commit 1e5aebc601
17 changed files with 635 additions and 361 deletions
+87 -49
View File
@@ -12,30 +12,40 @@ import 'package:status_file/utils.dart';
ArgParser buildParser() {
var parser = ArgParser();
parser.addFlag("check-for-disjunctions",
negatable: false,
defaultsTo: false,
help: "Warn if a status header expression contains '||'.");
parser.addFlag("check-for-non-existing",
negatable: true,
defaultsTo: true,
help: "Check for and error on non-existing test entries.");
parser.addFlag("text",
abbr: "t",
negatable: false,
defaultsTo: false,
help: "Lint text passed in stdin.");
parser.addFlag("help",
abbr: "h",
negatable: false,
defaultsTo: false,
help: "Show help and commands for this tool.");
parser.addFlag(
"check-for-disjunctions",
negatable: false,
defaultsTo: false,
help: "Warn if a status header expression contains '||'.",
);
parser.addFlag(
"check-for-non-existing",
negatable: true,
defaultsTo: true,
help: "Check for and error on non-existing test entries.",
);
parser.addFlag(
"text",
abbr: "t",
negatable: false,
defaultsTo: false,
help: "Lint text passed in stdin.",
);
parser.addFlag(
"help",
abbr: "h",
negatable: false,
defaultsTo: false,
help: "Show help and commands for this tool.",
);
return parser;
}
void printHelp(ArgParser parser) {
print("Usage: 'dart status_file/bin/lint.dart <path>' or 'dart "
"status_file/bin/lint.dart -t <input>' for text input.");
print(
"Usage: 'dart status_file/bin/lint.dart <path>' or 'dart "
"status_file/bin/lint.dart -t <input>' for text input.",
);
print(parser.usage);
}
@@ -51,20 +61,26 @@ void main(List<String> arguments) {
bool usePipe = results["text"];
if (usePipe) {
lintStdIn(
checkForDisjunctions: checkForDisjunctions, checkForNonExisting: false);
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: false,
);
} else {
if (results.rest.length != 1) {
printHelp(parser);
exit(1);
}
lintPath(results.rest.first,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting);
lintPath(
results.rest.first,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting,
);
}
}
void lintStdIn(
{bool checkForDisjunctions = false, required bool checkForNonExisting}) {
void lintStdIn({
bool checkForDisjunctions = false,
required bool checkForNonExisting,
}) {
var strings = <String>[];
try {
while (true) {
@@ -80,13 +96,18 @@ void lintStdIn(
}
}
void lintPath(String path,
{bool checkForDisjunctions = false, required bool checkForNonExisting}) {
void lintPath(
String path, {
bool checkForDisjunctions = false,
required bool checkForNonExisting,
}) {
var filesWithErrors = <String>[];
if (FileSystemEntity.isFileSync(path)) {
if (!lintFile(path,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting)) {
if (!lintFile(
path,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting,
)) {
filesWithErrors.add(path);
}
} else if (FileSystemEntity.isDirectorySync(path)) {
@@ -94,9 +115,11 @@ void lintPath(String path,
if (!canLint(entry.path)) {
return;
}
if (!lintFile(entry.path,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting)) {
if (!lintFile(
entry.path,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting,
)) {
filesWithErrors.add(entry.path);
}
});
@@ -110,37 +133,52 @@ void lintPath(String path,
}
}
bool lintText(List<String> text,
{bool checkForDisjunctions = false, required bool checkForNonExisting}) {
bool lintText(
List<String> text, {
bool checkForDisjunctions = false,
required bool checkForNonExisting,
}) {
try {
var statusFile = StatusFile.parse("stdin", text);
return lintStatusFile(statusFile,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting);
return lintStatusFile(
statusFile,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting,
);
} on status_file.SyntaxError {
stderr.writeln("Could not parse stdin.");
}
return false;
}
bool lintFile(String path,
{bool checkForDisjunctions = false, required bool checkForNonExisting}) {
bool lintFile(
String path, {
bool checkForDisjunctions = false,
required bool checkForNonExisting,
}) {
try {
var statusFile = StatusFile.read(path);
return lintStatusFile(statusFile,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting);
return lintStatusFile(
statusFile,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting,
);
} on status_file.SyntaxError catch (error) {
stderr.writeln("Could not parse $path:\n$error");
}
return false;
}
bool lintStatusFile(StatusFile statusFile,
{bool checkForDisjunctions = false, required bool checkForNonExisting}) {
var lintingErrors = lint(statusFile,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting);
bool lintStatusFile(
StatusFile statusFile, {
bool checkForDisjunctions = false,
required bool checkForNonExisting,
}) {
var lintingErrors = lint(
statusFile,
checkForDisjunctions: checkForDisjunctions,
checkForNonExisting: checkForNonExisting,
);
if (lintingErrors.isEmpty) {
print("${statusFile.path}\n Status file passed all tests");
print("");
+43 -25
View File
@@ -13,21 +13,27 @@ import 'package:status_file/utils.dart';
ArgParser buildParser() {
var parser = ArgParser();
parser.addFlag("overwrite",
abbr: 'w',
negatable: false,
defaultsTo: false,
help: "Overwrite input files with formatted output.");
parser.addFlag("delete-non-existing",
abbr: 'd',
negatable: true,
defaultsTo: true,
help: "Remove non-existing test entries.");
parser.addFlag("help",
abbr: "h",
negatable: false,
defaultsTo: false,
help: "Show help and commands for this tool.");
parser.addFlag(
"overwrite",
abbr: 'w',
negatable: false,
defaultsTo: false,
help: "Overwrite input files with formatted output.",
);
parser.addFlag(
"delete-non-existing",
abbr: 'd',
negatable: true,
defaultsTo: true,
help: "Remove non-existing test entries.",
);
parser.addFlag(
"help",
abbr: "h",
negatable: false,
defaultsTo: false,
help: "Show help and commands for this tool.",
);
return parser;
}
@@ -58,19 +64,27 @@ void main(List<String> arguments) {
if (!canLint(entry.path)) {
return;
}
normalizeFile(entry.path, overwrite,
deleteNonExisting: deleteNonExisting);
normalizeFile(
entry.path,
overwrite,
deleteNonExisting: deleteNonExisting,
);
});
}
}
}
bool normalizeFile(String path, bool writeFile,
{required bool deleteNonExisting}) {
bool normalizeFile(
String path,
bool writeFile, {
required bool deleteNonExisting,
}) {
try {
var statusFile = StatusFile.read(path);
var normalizedStatusFile =
normalizeStatusFile(statusFile, deleteNonExisting: deleteNonExisting);
var normalizedStatusFile = normalizeStatusFile(
statusFile,
deleteNonExisting: deleteNonExisting,
);
if (writeFile) {
File(path).writeAsStringSync(normalizedStatusFile.toString());
print("Normalized $path");
@@ -79,11 +93,15 @@ bool normalizeFile(String path, bool writeFile,
}
// Check if there are linting errors remaining, such as line comments,
// that needs to be handled manually.
var lintErrors =
lint(normalizedStatusFile, checkForNonExisting: deleteNonExisting);
var lintErrors = lint(
normalizedStatusFile,
checkForNonExisting: deleteNonExisting,
);
if (lintErrors.isNotEmpty) {
print("The normalizer could not remove all linting errors. The following "
"has to be removed manually:");
print(
"The normalizer could not remove all linting errors. The following "
"has to be removed manually:",
);
lintErrors.forEach(print);
}
} on status_file.SyntaxError catch (error) {
@@ -36,10 +36,13 @@ import 'package:status_file/expectation.dart';
import 'package:status_file/src/expression.dart';
StatusEntry? filterExpectations(
StatusEntry entry, List<Expectation> expectationsToKeep) {
StatusEntry entry,
List<Expectation> expectationsToKeep,
) {
List<Expectation> remaining = entry.expectations
.where(
(Expectation expectation) => expectationsToKeep.contains(expectation))
(Expectation expectation) => expectationsToKeep.contains(expectation),
)
.toList();
return remaining.isEmpty
? null
@@ -128,11 +131,12 @@ String getIssueText(String comment, bool resolveState) {
}
Future<StatusFile> removeNonEssentialEntries(
StatusFile statusFile,
List<Expectation> expectationsToKeep,
bool removeComments,
List<String> comments,
bool resolveIssueState) async {
StatusFile statusFile,
List<Expectation> expectationsToKeep,
bool removeComments,
List<String> comments,
bool resolveIssueState,
) async {
List<StatusSection> sections = <StatusSection>[];
for (StatusSection section in statusFile.sections) {
bool hasStatusEntries = false;
@@ -160,7 +164,8 @@ Future<StatusFile> removeNonEssentialEntries(
? "${section.condition}"
: "";
String issueText = getIssueText(comment, resolveIssueState);
String statusLine = "$conditionPrefix\t$testName\t$expectations"
String statusLine =
"$conditionPrefix\t$testName\t$expectations"
"\t$comment\t$issueText";
comments.add(statusLine);
}
@@ -177,8 +182,11 @@ Future<StatusFile> removeNonEssentialEntries(
var isDefaultSection = section.condition == Expression.always;
if (hasStatusEntries ||
(isDefaultSection && section.sectionHeaderComments.isNotEmpty)) {
var newSection =
StatusSection(section.condition, -1, section.sectionHeaderComments);
var newSection = StatusSection(
section.condition,
-1,
section.sectionHeaderComments,
);
newSection.entries.addAll(entries);
sections.add(newSection);
}
@@ -191,28 +199,46 @@ Future<StatusFile> removeNonEssentialEntries(
ArgParser buildParser() {
var parser = ArgParser();
parser.addFlag("overwrite",
abbr: 'w',
negatable: false,
defaultsTo: false,
help: "Overwrite input file with output.");
parser.addFlag("keep-crashes",
abbr: 'c', negatable: false, defaultsTo: false);
parser.addFlag("remove-comments",
abbr: 'r', negatable: false, defaultsTo: false);
parser.addFlag("resolve-issue-states",
abbr: 'i', negatable: false, defaultsTo: false);
parser.addFlag("help",
abbr: "h",
negatable: false,
defaultsTo: false,
help: "Show help and commands for this tool.");
parser.addFlag(
"overwrite",
abbr: 'w',
negatable: false,
defaultsTo: false,
help: "Overwrite input file with output.",
);
parser.addFlag(
"keep-crashes",
abbr: 'c',
negatable: false,
defaultsTo: false,
);
parser.addFlag(
"remove-comments",
abbr: 'r',
negatable: false,
defaultsTo: false,
);
parser.addFlag(
"resolve-issue-states",
abbr: 'i',
negatable: false,
defaultsTo: false,
);
parser.addFlag(
"help",
abbr: "h",
negatable: false,
defaultsTo: false,
help: "Show help and commands for this tool.",
);
return parser;
}
void printHelp(ArgParser parser) {
print("Usage: dart pkg/status_file/bin/remove_non_essential_entries.dart"
" <path>");
print(
"Usage: dart pkg/status_file/bin/remove_non_essential_entries.dart"
" <path>",
);
print(parser.usage);
}
@@ -237,7 +263,7 @@ void main(List<String> arguments) async {
Expectation.skipByDesign,
Expectation.skipSlow,
Expectation.slow,
Expectation.extraSlow
Expectation.extraSlow,
];
if (results["keep-crashes"]) {
@@ -255,8 +281,13 @@ void main(List<String> arguments) async {
if (resolveGithubIssueState) {
await parseIssueFile();
}
statusFile = await removeNonEssentialEntries(statusFile, expectationsToKeep,
removeComments, comments, resolveGithubIssueState);
statusFile = await removeNonEssentialEntries(
statusFile,
expectationsToKeep,
removeComments,
comments,
resolveGithubIssueState,
);
if (writeFile) {
await File(path).writeAsString(statusFile.toString());
print("Wrote $path.");
+21 -9
View File
@@ -165,8 +165,10 @@ class StatusFile {
// Comments after the last empty line belong to the next section's header.
// The empty line is not added to the section header, because it will be
// added to the section's entries.
implicitSectionHeaderComments =
implicitSectionHeaderComments.sublist(0, lastEmptyLine - 1);
implicitSectionHeaderComments = implicitSectionHeaderComments.sublist(
0,
lastEmptyLine - 1,
);
entries.add(sectionHeaderComments[lastEmptyLine - 1]);
sectionHeaderComments = sectionHeaderComments.sublist(lastEmptyLine);
} else {
@@ -176,8 +178,11 @@ class StatusFile {
// The current section whose rules are being parsed. Initialized to an
// implicit section that matches everything.
StatusSection section =
StatusSection(Expression.always, -1, implicitSectionHeaderComments);
StatusSection section = StatusSection(
Expression.always,
-1,
implicitSectionHeaderComments,
);
section.entries.addAll(entries);
sections.add(section);
@@ -223,11 +228,13 @@ class StatusFile {
}
});
if (match[3] == null) {
section.entries
.add(StatusEntry(path, _lineCount, expectations, null));
section.entries.add(
StatusEntry(path, _lineCount, expectations, null),
);
} else {
section.entries.add(
StatusEntry(path, _lineCount, expectations, Comment(match[3]!)));
StatusEntry(path, _lineCount, expectations, Comment(match[3]!)),
);
}
continue;
}
@@ -267,8 +274,13 @@ class StatusFile {
if (errors.isNotEmpty) {
var s = errors.length > 1 ? "s" : "";
throw SyntaxError(_shortPath, section.lineNumber,
"[ ${section.condition} ]", 'Validation error$s', errors);
throw SyntaxError(
_shortPath,
section.lineNumber,
"[ ${section.condition} ]",
'Validation error$s',
errors,
);
}
}
}
+70 -35
View File
@@ -34,8 +34,10 @@ class Expectation {
/// The test compiled and began executing but then threw an uncaught
/// exception or produced the wrong output.
static final Expectation runtimeError =
Expectation._('RuntimeError', group: fail);
static final Expectation runtimeError = Expectation._(
'RuntimeError',
group: fail,
);
/// The test failed with an error at compile time and did not execute any
/// code.
@@ -43,66 +45,90 @@ class Expectation {
/// * For a VM test, means the VM exited with a "compile error" exit code 254.
/// * 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 =
Expectation._('CompileTimeError', group: fail);
static final Expectation compileTimeError = Expectation._(
'CompileTimeError',
group: fail,
);
/// The test was parsed by the spec_parser, and there was a syntax error.
static final Expectation syntaxError =
Expectation._('SyntaxError', group: fail);
static final Expectation syntaxError = 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 =
Expectation._('MissingRuntimeError', group: fail);
static final Expectation missingRuntimeError = 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 =
Expectation._('MissingSyntaxError', group: fail);
static final Expectation missingSyntaxError = 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 =
Expectation._('MissingCompileTimeError', group: fail);
static final Expectation missingCompileTimeError = Expectation._(
'MissingCompileTimeError',
group: fail,
);
/// When the test is processed by analyzer, a static warning should be
/// reported.
static final Expectation staticWarning =
Expectation._('StaticWarning', group: fail);
static final Expectation staticWarning = 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 =
Expectation._('MissingStaticWarning', group: fail);
static final Expectation missingStaticWarning = Expectation._(
'MissingStaticWarning',
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 =
Expectation._('NonUtf8Output', group: fail);
static final Expectation nonUtf8Error = 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 =
Expectation._('TruncatedOutput', group: fail);
static final Expectation truncatedOutput = Expectation._(
'TruncatedOutput',
group: fail,
);
/// The VM exited with the special exit code 252.
static final Expectation dartkCrash =
Expectation._('DartkCrash', group: crash);
static final Expectation dartkCrash = Expectation._(
'DartkCrash',
group: crash,
);
/// A timeout occurred in a test using the Kernel-based front end.
static final Expectation dartkTimeout =
Expectation._('DartkTimeout', group: timeout);
static final Expectation dartkTimeout = Expectation._(
'DartkTimeout',
group: timeout,
);
/// A compile error was reported on a test compiled using the Kernel-based
/// front end.
static final Expectation dartkCompileTimeError =
Expectation._('DartkCompileTimeError', group: compileTimeError);
static final Expectation dartkCompileTimeError = Expectation._(
'DartkCompileTimeError',
group: compileTimeError,
);
// "meta expectations"
/// A marker applied to a test to indicate that the other non-pass
@@ -125,8 +151,11 @@ class Expectation {
/// 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 =
Expectation._('ExtraSlow', isMeta: true, group: skip);
static final Expectation extraSlow = Expectation._(
'ExtraSlow',
isMeta: true,
group: skip,
);
/// Tells the test runner to not attempt to run the test.
///
@@ -141,16 +170,21 @@ class Expectation {
///
/// Prefer this over timeout since this avoids wasting CPU resources running
/// a test we know won't complete.
static final Expectation skipSlow =
Expectation._('SkipSlow', isMeta: true, group: skip);
static final Expectation skipSlow = 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.
///
/// For example, tests that use dart:io are SkipByDesign on the browser since
/// dart:io isn't supported there.
static final Expectation skipByDesign =
Expectation._('SkipByDesign', isMeta: true);
static final Expectation skipByDesign = 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.
@@ -160,8 +194,9 @@ class Expectation {
/// 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 =
Expectation._('VerificationError');
static final Expectation verificationError = Expectation._(
'VerificationError',
);
/// Maps case-insensitive names to expectations.
static final Map<String, Expectation> _all = Map.fromIterable(<Expectation>[
@@ -206,7 +241,7 @@ class Expectation {
final bool isOutcome;
Expectation._(this._name, {this._group, bool isMeta = false})
: isOutcome = !isMeta;
: isOutcome = !isMeta;
bool canBeOutcomeOf(Expectation expectation) {
Expectation? outcome = this;
+55 -35
View File
@@ -24,8 +24,9 @@ final String _comparisonToken = "__";
/// The procedure is exponential so [expression] should not be too big.
Expression toDisjunctiveNormalForm(Expression expression) {
var normalizedExpression = expression.normalize();
var variableExpression =
_comparisonExpressionsToVariableExpressions(normalizedExpression);
var variableExpression = _comparisonExpressionsToVariableExpressions(
normalizedExpression,
);
var minTerms = _satisfiableMinTerms(variableExpression);
if (minTerms == null) {
return T;
@@ -101,7 +102,9 @@ LogicExpression _minimizeByComplementation(LogicExpression expression) {
return onesInA - onesInB;
});
var combinedMinSets = _combineMinSets(
clauses.map((e) => [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 LogicExpression.or(finalOperands).normalize();
@@ -172,8 +175,10 @@ class TruthTableEnvironment extends Environment {
/// Combines [minSets] recursively as long as possible. Prime implicants (those
/// that cannot be reduced further) are kept track of in [primeImplicants]. When
/// finished the function returns all combined min sets.
List<List<LogicExpression>> _combineMinSets(List<List<LogicExpression>> minSets,
List<List<LogicExpression>> primeImplicants) {
List<List<LogicExpression>> _combineMinSets(
List<List<LogicExpression>> minSets,
List<List<LogicExpression>> primeImplicants,
) {
List<List<LogicExpression>> combined = <List<LogicExpression>>[];
var addedInThisIteration = <List<LogicExpression>>{};
for (var i = 0; i < minSets.length; i++) {
@@ -205,8 +210,10 @@ List<List<LogicExpression>> _combineMinSets(List<List<LogicExpression>> minSets,
/// Two min sets can be combined if they only differ by one. We reduce min sets
/// and find their difference based on variables.
bool _canCombine(List<LogicExpression> a, List<LogicExpression> b) {
return _difference(_reduceMinSet(a).operands, _reduceMinSet(b).operands)
.length ==
return _difference(
_reduceMinSet(a).operands,
_reduceMinSet(b).operands,
).length ==
1;
}
@@ -263,8 +270,9 @@ LogicExpression _reduceMinSet(List<LogicExpression> minSet) {
/// here. The implicants that cover only a single truth assignment can be
/// directly added to [cover].
List<List<LogicExpression>> _findMinCover(
List<List<LogicExpression>> primaryImplicants,
List<List<LogicExpression>> cover) {
List<List<LogicExpression>> primaryImplicants,
List<List<LogicExpression>> cover,
) {
var minCover = primaryImplicants.toList()..addAll(cover);
if (cover.isEmpty) {
var allImplicants = primaryImplicants.toList();
@@ -291,8 +299,9 @@ List<List<LogicExpression>> _findMinCover(
for (var implicant in primaryImplicants) {
var newCover = cover.toList()..add(implicant);
if (!_isCover(newCover, primaryImplicants)) {
var newPrimaryList =
primaryImplicants.where((i) => i != implicant).toList();
var newPrimaryList = primaryImplicants
.where((i) => i != implicant)
.toList();
newCover = _findMinCover(newPrimaryList, newCover);
}
if (newCover.length < minCover.length) {
@@ -345,7 +354,9 @@ T? _findFirst<T extends Expression>(T expressionToFind, List<T> expressions) {
/// Adds [expressionToAdd] to [expressions] if is not present.
void _addIfNotPresent(
Expression expressionToAdd, List<Expression> expressions) {
Expression expressionToAdd,
List<Expression> expressions,
) {
if (_findFirst(expressionToAdd, expressions) == null) {
expressions.add(expressionToAdd);
}
@@ -354,7 +365,8 @@ void _addIfNotPresent(
/// Computes all unique min sets, thereby disregarding the order for which they
/// were combined.
List<List<LogicExpression>> _uniqueMinSets(
List<List<LogicExpression>> minSets) {
List<List<LogicExpression>> minSets,
) {
var uniqueMinSets = <List<LogicExpression>>[];
for (int i = 0; i < minSets.length; i++) {
bool foundEqual = false;
@@ -374,7 +386,9 @@ List<List<LogicExpression>> _uniqueMinSets(
/// Measures if two min sets are equal by checking that [minSet1] c [minSet2]
/// and minSet1.length == minSet2.length.
bool _areMinSetsEqual(
List<LogicExpression> minSet1, List<LogicExpression> minSet2) {
List<LogicExpression> minSet1,
List<LogicExpression> minSet2,
) {
int found = 0;
for (var expression in minSet1) {
if (_findFirst(expression, minSet2) != null) {
@@ -409,18 +423,21 @@ List<Expression> _getVariables(Expression expression) {
Expression negate(Expression expression, {bool positive = false}) {
if (expression is LogicExpression && expression.isOr) {
return LogicExpression.and(expression.operands
.map((e) => negate(e, positive: !positive))
.toList());
return LogicExpression.and(
expression.operands.map((e) => negate(e, positive: !positive)).toList(),
);
}
if (expression is LogicExpression && expression.isAnd) {
return LogicExpression.or(expression.operands
.map((e) => negate(e, positive: !positive))
.toList());
return LogicExpression.or(
expression.operands.map((e) => negate(e, positive: !positive)).toList(),
);
}
if (expression is ComparisonExpression) {
return ComparisonExpression(
expression.left, expression.right, !expression.negate);
expression.left,
expression.right,
!expression.negate,
);
}
if (expression is VariableExpression) {
return VariableExpression(expression.variable, negate: !expression.negate);
@@ -433,15 +450,17 @@ Expression negate(Expression expression, {bool positive = false}) {
Expression _comparisonExpressionsToVariableExpressions(Expression expression) {
if (expression is LogicExpression) {
return LogicExpression(
expression.op,
expression.operands
.map((exp) => _comparisonExpressionsToVariableExpressions(exp))
.toList());
expression.op,
expression.operands
.map((exp) => _comparisonExpressionsToVariableExpressions(exp))
.toList(),
);
}
if (expression is ComparisonExpression) {
return VariableExpression(
Variable(expression.left.name + _comparisonToken + expression.right),
negate: expression.negate);
Variable(expression.left.name + _comparisonToken + expression.right),
negate: expression.negate,
);
}
return expression;
}
@@ -449,19 +468,20 @@ Expression _comparisonExpressionsToVariableExpressions(Expression expression) {
Expression _recoverComparisonExpressions(Expression expression) {
if (expression is LogicExpression) {
return LogicExpression(
expression.op,
expression.operands
.map((exp) => _recoverComparisonExpressions(exp))
.toList());
expression.op,
expression.operands
.map((exp) => _recoverComparisonExpressions(exp))
.toList(),
);
}
if (expression is VariableExpression &&
expression.variable.name.contains(_comparisonToken)) {
int tokenIndex = expression.variable.name.indexOf(_comparisonToken);
return ComparisonExpression(
Variable(expression.variable.name.substring(0, tokenIndex)),
expression.variable.name
.substring(tokenIndex + _comparisonToken.length),
expression.negate);
Variable(expression.variable.name.substring(0, tokenIndex)),
expression.variable.name.substring(tokenIndex + _comparisonToken.length),
expression.negate,
);
}
return expression;
}
+13 -7
View File
@@ -105,8 +105,10 @@ class Variable {
String lookup(Environment environment) {
var value = environment.lookUp(name);
if (value == null) {
throw Exception("Could not find '$name' in environment "
"while evaluating status file expression.");
throw Exception(
"Could not find '$name' in environment "
"while evaluating status file expression.",
);
}
// Explicitly stringify all values so that things like:
@@ -291,8 +293,9 @@ class LogicExpression extends Expression {
// Recurse into the operands, sort them, and remove duplicates.
var normalized = LogicExpression(
op, operands.map((operand) => operand.normalize()).toList())
.operands;
op,
operands.map((operand) => operand.normalize()).toList(),
).operands;
normalized = flatten(normalized);
var ordered = SplayTreeSet<Expression>.from(normalized).toList();
return LogicExpression(op, ordered);
@@ -406,12 +409,14 @@ class _ExpressionParser {
// of the form $variable.
if (!_scanner.match(_Token.dollar)) {
throw FormatException(
"Expected \$ in expression, got ${_scanner.current}");
"Expected \$ in expression, got ${_scanner.current}",
);
}
if (!_scanner.isIdentifier) {
throw FormatException(
"Expected identifier in expression, got ${_scanner.current}");
"Expected identifier in expression, got ${_scanner.current}",
);
}
var left = Variable(_scanner.current!);
@@ -424,7 +429,8 @@ class _ExpressionParser {
if (!_scanner.isIdentifier) {
throw FormatException(
"Expected value in expression, got ${_scanner.current}");
"Expected value in expression, got ${_scanner.current}",
);
}
var right = _scanner.advance()!;
+14 -6
View File
@@ -114,8 +114,9 @@ class StatusFile {
sections.add(StatusSection(Expression.always, -1));
}
sections.last.entries
.add(StatusEntry(path, _lineCount, expectations, issue));
sections.last.entries.add(
StatusEntry(path, _lineCount, expectations, issue),
);
continue;
}
@@ -138,8 +139,13 @@ class StatusFile {
if (errors.isNotEmpty) {
var s = errors.length > 1 ? "s" : "";
throw SyntaxError(_shortPath, section.lineNumber,
"[ ${section.condition} ]", 'Validation error$s', errors);
throw SyntaxError(
_shortPath,
section.lineNumber,
"[ ${section.condition} ]",
'Validation error$s',
errors,
);
}
}
}
@@ -223,8 +229,10 @@ class StatusFile {
}
for (var entry in section.entries) {
writeText("${entry.path}: ${entry.expectations.join(', ')}",
entry.lineNumber);
writeText(
"${entry.path}: ${entry.expectations.join(', ')}",
entry.lineNumber,
);
}
needBlankLine = true;
@@ -25,7 +25,7 @@ bool isNonExistingEntry(Uri statusFileUri, StatusEntry entry) {
for (var regexp in [
_underscoreTestEnd,
_underscoreTestDotDart,
_underscoreTestSlash
_underscoreTestSlash,
]) {
var matches = regexp.allMatches(entry.path);
if (matches.length == 1) {
+46 -27
View File
@@ -19,8 +19,11 @@ class LintingError {
}
/// Main function to check a status file for linting errors.
List<LintingError> lint(StatusFile file,
{bool checkForDisjunctions = false, required bool checkForNonExisting}) {
List<LintingError> lint(
StatusFile file, {
bool checkForDisjunctions = false,
required bool checkForNonExisting,
}) {
var errors = <LintingError>[];
for (var section in file.sections) {
errors
@@ -62,13 +65,16 @@ Iterable<LintingError> lintCommentLinesInSection(StatusSection section) {
seenStatusEntry = seenStatusEntry || entry is StatusEntry;
if (seenStatusEntry && entry is CommentEntry) {
lintingErrors.add(
LintingError(entry.lineNumber, "Comment is on a line by itself."));
LintingError(entry.lineNumber, "Comment is on a line by itself."),
);
}
}
return lintingErrors;
}
return section.entries.whereType<CommentEntry>().map((entry) =>
LintingError(entry.lineNumber, "Comment is on a line by itself."));
return section.entries.whereType<CommentEntry>().map(
(entry) =>
LintingError(entry.lineNumber, "Comment is on a line by itself."),
);
}
/// Checks for disjunctions in headers. Disjunctions should be separated out.
@@ -91,9 +97,10 @@ Iterable<LintingError> lintDisjunctionsInHeader(StatusSection section) {
if (section.condition.toString().contains("||")) {
return [
LintingError(
section.lineNumber,
"Expression contains '||'. Please split the expression into multiple "
"separate sections.")
section.lineNumber,
"Expression contains '||'. Please split the expression into multiple "
"separate sections.",
),
];
}
return [];
@@ -111,9 +118,10 @@ Iterable<LintingError> lintAlphabeticalOrderingOfPaths(StatusSection section) {
if (witness != null) {
return [
LintingError(
section.lineNumber,
"Test paths are not alphabetically ordered in section. "
"${witness.first} should come before ${witness.second}.")
section.lineNumber,
"Test paths are not alphabetically ordered in section. "
"${witness.first} should come before ${witness.second}.",
),
];
}
return [];
@@ -126,7 +134,8 @@ Iterable<LintingError> lintEntryExists(StatusFile file, StatusSection section) {
for (var entry in section.entries.whereType<StatusEntry>()) {
if (isNonExistingEntry(statusFileUri, entry)) {
errors.add(
LintingError(entry.lineNumber, "This path doesn't seem to exist."));
LintingError(entry.lineNumber, "This path doesn't seem to exist."),
);
}
}
@@ -140,9 +149,10 @@ Iterable<LintingError> lintNormalizedSection(StatusSection section) {
if (nonNormalized != normalized) {
return [
LintingError(
section.lineNumber,
"Condition expression should be '$normalized' "
"but was '$nonNormalized'.")
section.lineNumber,
"Condition expression should be '$normalized' "
"but was '$nonNormalized'.",
),
];
}
return const [];
@@ -151,8 +161,9 @@ Iterable<LintingError> lintNormalizedSection(StatusSection section) {
/// Checks for duplicate section entries in the body of a section.
Iterable<LintingError> lintSectionEntryDuplicates(StatusSection section) {
var errors = <LintingError>[];
List<StatusEntry> statusEntries =
section.entries.whereType<StatusEntry>().toList();
List<StatusEntry> statusEntries = section.entries
.whereType<StatusEntry>()
.toList();
for (var i = 0; i < statusEntries.length; i++) {
var entry = statusEntries[i];
for (var j = i + 1; j < statusEntries.length; j++) {
@@ -160,11 +171,14 @@ Iterable<LintingError> lintSectionEntryDuplicates(StatusSection section) {
if (entry.path == otherEntry.path &&
_findNotEqualWitness(entry.expectations, otherEntry.expectations) ==
null) {
errors.add(LintingError(
errors.add(
LintingError(
section.lineNumber,
"The status entry "
"'$entry' is duplicated on lines "
"${entry.lineNumber} and ${otherEntry.lineNumber}."));
"${entry.lineNumber} and ${otherEntry.lineNumber}.",
),
);
}
}
}
@@ -203,11 +217,12 @@ Iterable<LintingError> lintSectionHeaderOrdering(List<StatusSection> sections) {
if (witness != null) {
return [
LintingError(
witness.second!.lineNumber,
"Section expressions are not correctly ordered in file. "
"'${witness.first!.condition}' on line ${witness.first!.lineNumber} "
"should come before '${witness.second!.condition}' at line "
"${witness.second!.lineNumber}.")
witness.second!.lineNumber,
"Section expressions are not correctly ordered in file. "
"'${witness.first!.condition}' on line ${witness.first!.lineNumber} "
"should come before '${witness.second!.condition}' at line "
"${witness.second!.lineNumber}.",
),
];
}
return [];
@@ -215,7 +230,8 @@ Iterable<LintingError> lintSectionHeaderOrdering(List<StatusSection> sections) {
/// Checks for duplicate section headers.
Iterable<LintingError> lintSectionHeaderDuplicates(
List<StatusSection> sections) {
List<StatusSection> sections,
) {
var errors = <LintingError>[];
var sorted = sections.toList()
..sort((a, b) => a.condition.compareTo(b.condition));
@@ -223,11 +239,14 @@ Iterable<LintingError> lintSectionHeaderDuplicates(
var section = sorted[i];
var previousSection = sorted[i - 1];
if (section.condition.compareTo(previousSection.condition) == 0) {
errors.add(LintingError(
errors.add(
LintingError(
section.lineNumber,
"The condition "
"'${section.condition}' is duplicated on lines "
"${previousSection.lineNumber} and ${section.lineNumber}."));
"${previousSection.lineNumber} and ${section.lineNumber}.",
),
);
}
}
return errors;
+24 -13
View File
@@ -8,8 +8,10 @@ import 'package:status_file/status_file_entries_file_checker.dart';
import 'canonical_status_file.dart';
import 'dart:convert';
StatusFile normalizeStatusFile(StatusFile statusFile,
{required bool deleteNonExisting}) {
StatusFile normalizeStatusFile(
StatusFile statusFile, {
required bool deleteNonExisting,
}) {
StatusFile newStatusFile = _sortSectionsAndCombine(statusFile);
for (var section in newStatusFile.sections) {
if (deleteNonExisting) {
@@ -20,11 +22,13 @@ StatusFile normalizeStatusFile(StatusFile statusFile,
}
// Remove any empty sections.
newStatusFile.sections.removeWhere((section) =>
section.sectionHeaderComments.isEmpty &&
(section.entries.isEmpty ||
(section.entries.length == 1 &&
section.entries.single is EmptyEntry)));
newStatusFile.sections.removeWhere(
(section) =>
section.sectionHeaderComments.isEmpty &&
(section.entries.isEmpty ||
(section.entries.length == 1 &&
section.entries.single is EmptyEntry)),
);
// Remove empty line at the end of the file
newStatusFile.sections.last.entries.removeLast();
@@ -70,23 +74,30 @@ void _sortEntriesInSection(StatusSection section) {
/// Ensure that there is only one empty line to end a section.
void _oneLineBetweenSections(StatusSection section) {
section.entries.removeWhere((entry) => entry is EmptyEntry);
section.entries
.add(EmptyEntry(section.lineNumber + section.entries.length + 1));
section.entries.add(
EmptyEntry(section.lineNumber + section.entries.length + 1),
);
}
StatusFile _sortSectionsAndCombine(StatusFile statusFile) {
// Create the new status file to be returned.
StatusFile oldStatusFile = StatusFile.parse(
statusFile.path, LineSplitter.split(statusFile.toString()).toList());
statusFile.path,
LineSplitter.split(statusFile.toString()).toList(),
);
List<StatusSection> newSections = [];
// Copy over all sections and normalize all the expressions.
for (var section in oldStatusFile.sections) {
if (section.condition != Expression.always) {
if (section.isEmpty()) continue;
newSections.add(StatusSection(section.condition.normalize(),
section.lineNumber, section.sectionHeaderComments)
..entries.addAll(section.entries));
newSections.add(
StatusSection(
section.condition.normalize(),
section.lineNumber,
section.sectionHeaderComments,
)..entries.addAll(section.entries),
);
} else {
newSections.add(section);
}
+72 -52
View File
@@ -41,19 +41,26 @@ StatusFile createFromString(String text) {
return StatusFile.parse("test", text.split('\n'));
}
void expectError(String text, String expectedError,
{bool disjunctions = false}) {
void expectError(
String text,
String expectedError, {
bool disjunctions = false,
}) {
var statusFile = createFromString(text);
var errors = lint(statusFile,
checkForDisjunctions: disjunctions, checkForNonExisting: false)
.toList();
var errors = lint(
statusFile,
checkForDisjunctions: disjunctions,
checkForNonExisting: false,
).toList();
Expect.equals(expectedError, errors.first.toString());
}
void expectNoError(String text, {bool disjunctions = true}) {
var errors = lint(createFromString(text),
checkForDisjunctions: disjunctions, checkForNonExisting: false)
.toList();
var errors = lint(
createFromString(text),
checkForDisjunctions: disjunctions,
checkForNonExisting: false,
).toList();
Expect.listEquals([], errors);
}
@@ -102,12 +109,13 @@ vm/tests2: Timeout # this comment is also valid
void testCheckForDisjunctions_notAllowedDisjunction() {
expectError(
r"""[ $mode == debug || $mode == release ]
r"""[ $mode == debug || $mode == release ]
vm/tests: Skip # this comment is valid
""",
"Error at line 1: Expression contains '||'. Please split the expression "
"into multiple separate sections.",
disjunctions: true);
"Error at line 1: Expression contains '||'. Please split the expression "
"into multiple separate sections.",
disjunctions: true,
);
}
void testCheckForDisjunctions_shouldBeAllowedInComments() {
@@ -119,12 +127,13 @@ vm/tests: Skip # this comment is valid
void testCheckForAlphabeticalOrderingOfPaths_invalidOrdering() {
expectError(
r"""[ $mode == debug ]
r"""[ $mode == debug ]
vm/tests: Skip # this should come after a_test
a_test: Pass
""",
"Error at line 1: Test paths are not alphabetically ordered in "
"section. a_test should come before vm/tests.");
"Error at line 1: Test paths are not alphabetically ordered in "
"section. a_test should come before vm/tests.",
);
}
void testCheckForAlphabeticalOrderingOfPaths_okOrdering() {
@@ -138,60 +147,66 @@ xyz_test: Skip
void testCheckForDuplicateEntries_hasDuplicates() {
expectError(
r"""[ $mode == debug ]
r"""[ $mode == debug ]
a_test: Pass
a_test: Pass
bc_test: Pass
xyz_test: Skip
""",
"Error at line 1: The status entry 'a_test: Pass' is duplicated on lines "
"2 and 3.");
"Error at line 1: The status entry 'a_test: Pass' is duplicated on lines "
"2 and 3.",
);
}
void testCheckForCorrectOrderingInSections_invalidRuntimeBeforeCompiler() {
expectError(
r"""[ $runtime == ff && $compiler == dart2js]
r"""[ $runtime == ff && $compiler == dart2js]
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$compiler == dart2js "
r"&& $runtime == ff' but was '$runtime == ff && $compiler == dart2js'.");
r"Error at line 1: Condition expression should be '$compiler == dart2js "
r"&& $runtime == ff' but was '$runtime == ff && $compiler == dart2js'.",
);
}
void testCheckForCorrectOrderingInSections_invalidRuntimeBeforeMode() {
expectError(
r"""[ $runtime == ff && $mode == debug ]
r"""[ $runtime == ff && $mode == debug ]
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$mode == debug && "
r"$runtime == ff' but was '$runtime == ff && $mode == debug'.");
r"Error at line 1: Condition expression should be '$mode == debug && "
r"$runtime == ff' but was '$runtime == ff && $mode == debug'.",
);
}
void testCheckForCorrectOrderingInSections_invalidSystemBeforeMode() {
expectError(
r"""[ $system == win && $mode == debug ]
r"""[ $system == win && $mode == debug ]
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$mode == debug && "
r"$system == win' but was '$system == win && $mode == debug'.");
r"Error at line 1: Condition expression should be '$mode == debug && "
r"$system == win' but was '$system == win && $mode == debug'.",
);
}
void testCheckForCorrectOrderingInSections_invalidStrongBeforeKernel() {
expectError(
r"""[ !$strong && !$kernel ]
r"""[ !$strong && !$kernel ]
a_test: Pass
""",
r"Error at line 1: Condition expression should be '!$kernel && !$strong' "
r"but was '!$strong && !$kernel'.");
r"Error at line 1: Condition expression should be '!$kernel && !$strong' "
r"but was '!$strong && !$kernel'.",
);
}
void testCheckForCorrectOrderingInSections_invalidOrdering() {
expectError(
r"""[ $compiler == dart2js && $builder_tag == strong && !$browser ]
r"""[ $compiler == dart2js && $builder_tag == strong && !$browser ]
a_test: Pass
""",
r"Error at line 1: Condition expression should be '$builder_tag == "
r"strong && $compiler == dart2js && !$browser' but was "
r"'$compiler == dart2js && $builder_tag == strong && !$browser'.");
r"Error at line 1: Condition expression should be '$builder_tag == "
r"strong && $compiler == dart2js && !$browser' but was "
r"'$compiler == dart2js && $builder_tag == strong && !$browser'.",
);
}
void testCheckForCorrectOrderingInSections_okOrdering() {
@@ -202,33 +217,35 @@ a_test: Pass
void checkLintNormalizedSection_invalidAlphabeticalOrderingVariables() {
expectError(
r"""[ $runtime == ff ]
r"""[ $runtime == ff ]
a_test: Pass
[ $compiler == dart2js ]
a_test: Pass
""",
r"Error at line 1: Section expressions are not correctly ordered in file."
r" '$compiler == dart2js' on line 4 should come before '$runtime == ff' "
r"at line 1.");
r"Error at line 1: Section expressions are not correctly ordered in file."
r" '$compiler == dart2js' on line 4 should come before '$runtime == ff' "
r"at line 1.",
);
}
void checkLintNormalizedSection_invalidAlphabeticalOrderingVariableArguments() {
expectError(
r"""[ $runtime == ff ]
r"""[ $runtime == ff ]
a_test: Pass
[ $runtime == chrome ]
a_test: Pass
""",
r"Error at line 1: Section expressions are not correctly ordered in file."
r" '$runtime == chrome' on line 4 should come before '$runtime == ff' at "
r"line 1.");
r"Error at line 1: Section expressions are not correctly ordered in file."
r" '$runtime == chrome' on line 4 should come before '$runtime == ff' at "
r"line 1.",
);
}
void checkLintNormalizedSection_invalidOrderingWithNotEqual() {
expectError(
r"""
r"""
[ $ runtime == chrome ]
a_test: Pass
@@ -238,14 +255,15 @@ a_test: Pass
[ $runtime == ff ]
a_test: Pass
""",
r"Error at line 4: Section expressions are not correctly ordered in file."
r" '$runtime == ff' on line 7 should come before '$runtime != ff' at "
r"line 4.");
r"Error at line 4: Section expressions are not correctly ordered in file."
r" '$runtime == ff' on line 7 should come before '$runtime != ff' at "
r"line 4.",
);
}
void checkLintNormalizedSection_invalidOrderingWithNegation() {
expectError(
r"""
r"""
[ ! $browser ]
a_test: Pass
@@ -256,8 +274,9 @@ a_test: Pass
a_test: Pass
""",
r"Error at line 4: Section expressions are not correctly ordered in file."
r" '$checked' on line 7 should come before '!$checked' at line 4.");
r"Error at line 4: Section expressions are not correctly ordered in file."
r" '$checked' on line 7 should come before '!$checked' at line 4.",
);
}
void checkLintNormalizedSection_correctOrdering() {
@@ -283,13 +302,14 @@ a_test: Pass
void checkLintSectionHeaderDuplicates_invalidDuplicateSections() {
expectError(
r"""
r"""
[ ! $browser ]
a_test: Pass
[ ! $browser ]
a_test: Pass
""",
r"Error at line 4: The condition '!$browser' is duplicated on lines 1 "
r"and 4.");
r"Error at line 4: The condition '!$browser' is duplicated on lines 1 "
r"and 4.",
);
}
+66 -34
View File
@@ -26,10 +26,15 @@ void normalizeCheck() {
for (var file in files) {
print("------- ${file.path} -------");
var statusFile = StatusFile.read(file.path);
var statusFileOther = normalizeStatusFile(StatusFile.read(file.path),
deleteNonExisting: false);
checkSemanticallyEqual(statusFile, statusFileOther,
warnOnDuplicateHeader: true);
var statusFileOther = normalizeStatusFile(
StatusFile.read(file.path),
deleteNonExisting: false,
);
checkSemanticallyEqual(
statusFile,
statusFileOther,
warnOnDuplicateHeader: true,
);
checkFileHeaderIntact(statusFile, statusFileOther);
print("------- ${file.path} -------");
}
@@ -41,8 +46,11 @@ void sanityCheck() {
print("------- ${file.path} -------");
var statusFile = StatusFile.read(file.path);
var statusFileOther = StatusFile.read(file.path);
checkSemanticallyEqual(statusFile, statusFileOther,
warnOnDuplicateHeader: true);
checkSemanticallyEqual(
statusFile,
statusFileOther,
warnOnDuplicateHeader: true,
);
checkFileHeaderIntact(statusFile, statusFileOther);
print("------- ${file.path} -------");
}
@@ -50,29 +58,40 @@ void sanityCheck() {
List<FileSystemEntity> getStatusFiles() {
var statusFiles = <FileSystemEntity>[];
for (var entry
in Directory.fromUri(statusFilePath).listSync(recursive: true)) {
for (var entry in Directory.fromUri(
statusFilePath,
).listSync(recursive: true)) {
statusFiles.add(entry);
}
return statusFiles;
}
void checkSemanticallyEqual(StatusFile original, StatusFile normalized,
{bool warnOnDuplicateHeader = false}) {
void checkSemanticallyEqual(
StatusFile original,
StatusFile normalized, {
bool warnOnDuplicateHeader = false,
}) {
var entriesInOriginal = countEntries(original);
var entriesInNormalized = countEntries(normalized);
if (entriesInOriginal != entriesInNormalized) {
print(original);
print("==================");
print(normalized);
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.");
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.",
);
}
for (var section in original.sections) {
section.entries.whereType<StatusEntry>().forEach((entry) =>
findInStatusFile(normalized, entry, section.condition.normalize(),
warnOnDuplicateHeader: warnOnDuplicateHeader));
section.entries.whereType<StatusEntry>().forEach(
(entry) => findInStatusFile(
normalized,
entry,
section.condition.normalize(),
warnOnDuplicateHeader: warnOnDuplicateHeader,
),
);
}
}
@@ -83,21 +102,27 @@ int countEntries(StatusFile statusFile) {
}
void findInStatusFile(
StatusFile statusFile, StatusEntry entryToFind, Expression condition,
{bool warnOnDuplicateHeader = false}) {
StatusFile statusFile,
StatusEntry entryToFind,
Expression condition, {
bool warnOnDuplicateHeader = false,
}) {
int foundEntryPosition = -1;
for (var section in statusFile.sections) {
if (section.condition.normalize().compareTo(condition) != 0) {
continue;
}
var matchingEntries = section.entries
.where((entry) =>
entry is StatusEntry &&
entry.path.compareTo(entryToFind.path) == 0 &&
listEqual(entry.expectations, entryToFind.expectations))
.where(
(entry) =>
entry is StatusEntry &&
entry.path.compareTo(entryToFind.path) == 0 &&
listEqual(entry.expectations, entryToFind.expectations),
)
.toList();
if (matchingEntries.isEmpty) {
var message = "Could not find the entry even though the section "
var message =
"Could not find the entry even though the section "
"header matched on line number ${section.lineNumber}. Sections "
"should be unique.";
if (warnOnDuplicateHeader) {
@@ -106,30 +131,37 @@ void findInStatusFile(
throw Exception(message);
}
} else if (matchingEntries.length == 1 && foundEntryPosition >= 0) {
throw Exception("The entry '$entryToFind' on line "
"${entryToFind.lineNumber} in section ${section.condition} was "
"already found in a previous section on line $foundEntryPosition.");
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 Exception("The entry '$entryToFind' on line "
"${entryToFind.lineNumber} in section ${section.condition} on line "
"${section.lineNumber} had multiple matches in section.");
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 Exception("Could not find entry '$entryToFind' under the "
"condition $condition in the status file.");
throw Exception(
"Could not find entry '$entryToFind' under the "
"condition $condition in the status file.",
);
}
}
void checkFileHeaderIntact(StatusFile original, StatusFile normalized) {
var originalHeader = original.sections.first.sectionHeaderComments.toString();
var normalizedHeader =
normalized.sections.first.sectionHeaderComments.toString();
var normalizedHeader = normalized.sections.first.sectionHeaderComments
.toString();
if (originalHeader != normalizedHeader) {
throw Exception(
"File headers changed.\nExpected:\n$originalHeader\n\nActual:\n$normalizedHeader");
"File headers changed.\nExpected:\n$originalHeader\n\nActual:\n$normalizedHeader",
);
}
}
@@ -14,8 +14,9 @@ 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 Directory.fromUri(repoRoot.resolve(directory))
.listSync(recursive: true)) {
for (var entry in Directory.fromUri(
repoRoot.resolve(directory),
).listSync(recursive: true)) {
if (!entry.path.endsWith(".status")) continue;
try {
var statusFile = StatusFile.read(entry.path);
@@ -16,8 +16,9 @@ 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 Directory.fromUri(repoRoot.resolve(directory))
.listSync(recursive: true)) {
for (var entry in Directory.fromUri(
repoRoot.resolve(directory),
).listSync(recursive: true)) {
if (!entry.path.endsWith(".status")) continue;
try {
StatusFile.read(entry.path);
@@ -47,26 +47,32 @@ void testDnf() {
// Testing dnf and simple minimization (duplicates).
shouldDnfTo(r'$a && ($b || $c)', r'$a && $b || $a && $c');
shouldDnfTo(r'($a || $b) && ($c || $d)',
r'$a && $c || $a && $d || $b && $c || $b && $d');
shouldDnfTo(
r'($a || $b) && ($c || $d)',
r'$a && $c || $a && $d || $b && $c || $b && $d',
);
// Testing minimizing by complementation
// The following two examples can be found here:
// https://en.wikipedia.org/wiki/Quine%E2%80%93McCluskey_algorithm
shouldDnfTo(
r"$a && !$b && !$c && !$d || $a && !$b && !$c && $d || "
r"$a && !$b && $c && !$d || $a && !$b && $c && $d",
r"$a && !$b");
r"$a && !$b && !$c && !$d || $a && !$b && !$c && $d || "
r"$a && !$b && $c && !$d || $a && !$b && $c && $d",
r"$a && !$b",
);
shouldDnfTo(
r"!$a && $b && !$c && !$d || $a && !$b && !$c && !$d || "
r"$a && !$b && $c && !$d || $a && !$b && $c && $d || $a && $b && !$c && !$d ||"
r" $a && $b && $c && $d || $a && !$b && !$c && $d || $a && $b && $c && !$d",
r"$a && !$b || $a && $c || $b && !$c && !$d");
r"!$a && $b && !$c && !$d || $a && !$b && !$c && !$d || "
r"$a && !$b && $c && !$d || $a && !$b && $c && $d || $a && $b && !$c && !$d ||"
r" $a && $b && $c && $d || $a && !$b && !$c && $d || $a && $b && $c && !$d",
r"$a && !$b || $a && $c || $b && !$c && !$d",
);
// Test that an expression is converted to dnf and minified correctly.
shouldDnfTo(r'($a || $b) && ($a || $c)', r'$a || $b && $c');
shouldDnfTo(r'(!$a || $b) && ($a || $b)', r'$b');
shouldDnfTo(r'($a || $b || $c) && (!$a || !$b)',
r'$a && !$b || !$a && $b || !$b && $c');
shouldDnfTo(
r'($a || $b || $c) && (!$a || !$b)',
r'$a && !$b || !$a && $b || !$b && $c',
);
}
@@ -36,9 +36,12 @@ void main() {
void testExpression() {
var expression = Expression.parse(
r" $mode == debug && ($arch == chromium || $arch == dartc) ");
Expect.equals(r"$mode == debug && ($arch == chromium || $arch == dartc)",
expression.toString());
r" $mode == debug && ($arch == chromium || $arch == dartc) ",
);
Expect.equals(
r"$mode == debug && ($arch == chromium || $arch == dartc)",
expression.toString(),
);
// Test BooleanExpression.evaluate().
var environment = TestEnvironment({"arch": "dartc", "mode": "debug"});
@@ -62,14 +65,20 @@ void testSyntaxError() {
}
void testBoolean() {
var expression =
Expression.parse(r" $arch == ia32 && $checked || $mode == release ");
var expression = Expression.parse(
r" $arch == ia32 && $checked || $mode == release ",
);
Expect.equals(
r"$arch == ia32 && $checked || $mode == release", expression.toString());
r"$arch == ia32 && $checked || $mode == release",
expression.toString(),
);
// Test BooleanExpression.evaluate().
var environment =
TestEnvironment({"arch": "ia32", "checked": "true", "mode": "debug"});
var environment = TestEnvironment({
"arch": "ia32",
"checked": "true",
"mode": "debug",
});
Expect.isTrue(expression.evaluate(environment));
environment["mode"] = "release";
@@ -85,13 +94,19 @@ void testBoolean() {
}
void testNotBoolean() {
var expression =
Expression.parse(r" $arch == ia32 && ! $checked || $mode == release ");
var expression = Expression.parse(
r" $arch == ia32 && ! $checked || $mode == release ",
);
Expect.equals(
r"$arch == ia32 && !$checked || $mode == release", expression.toString());
r"$arch == ia32 && !$checked || $mode == release",
expression.toString(),
);
var environment =
TestEnvironment({"arch": "ia32", "checked": "false", "mode": "debug"});
var environment = TestEnvironment({
"arch": "ia32",
"checked": "false",
"mode": "debug",
});
Expect.isTrue(expression.evaluate(environment));
environment["mode"] = "release";
@@ -108,16 +123,16 @@ void testNotBoolean() {
void testNotEqual() {
// Test the != operator.
var expression =
Expression.parse(r"$compiler == dart2js && $runtime != safari");
var expression = Expression.parse(
r"$compiler == dart2js && $runtime != safari",
);
Expect.equals(
r"$compiler == dart2js && $runtime != safari", expression.toString());
r"$compiler == dart2js && $runtime != safari",
expression.toString(),
);
// Test BooleanExpression.evaluate().
var environment = TestEnvironment({
"compiler": "none",
"runtime": "safari",
});
var environment = TestEnvironment({"compiler": "none", "runtime": "safari"});
Expect.isFalse(expression.evaluate(environment));
environment["runtime"] = "chrome";
@@ -163,8 +178,9 @@ void testNormalize() {
// Order logic clauses.
shouldNormalizeTo(
r"$b || ! $b || $b == b || $b && $d || $a || ! $a || $a == a || $a && $c",
r"$a == a || $b == b || $a || !$a || $b || !$b || $a && $c || $b && $d");
r"$b || ! $b || $b == b || $b && $d || $a || ! $a || $a == a || $a && $c",
r"$a == a || $b == b || $a || !$a || $b || !$b || $a && $c || $b && $d",
);
// Recursively normalize.
shouldNormalizeTo(r"$c == true || $b && $a", r"$c || $a && $b");