mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
[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:
parent
aa2e19827f
commit
58392c017a
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 ]");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -13,3 +13,4 @@ dependencies:
|
|||
# Use 'any' constraints here; we get our versions from the DEPS file.
|
||||
dev_dependencies:
|
||||
expect: any
|
||||
lints: any
|
||||
|
|
|
@ -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}) {
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue