mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +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
|
# 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.
|
# BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
include: package:lints/recommended.yaml
|
||||||
|
|
||||||
analyzer:
|
analyzer:
|
||||||
errors:
|
errors:
|
||||||
# Allow having TODOs in the code
|
# Allow having TODOs in the code
|
||||||
|
|
|
@ -15,10 +15,10 @@ void main(List<String> arguments) {
|
||||||
|
|
||||||
var path = arguments[0];
|
var path = arguments[0];
|
||||||
|
|
||||||
if (new File(path).existsSync()) {
|
if (File(path).existsSync()) {
|
||||||
formatFile(path);
|
formatFile(path);
|
||||||
} else if (new Directory(path).existsSync()) {
|
} else if (Directory(path).existsSync()) {
|
||||||
for (var entry in new Directory(path).listSync(recursive: true)) {
|
for (var entry in Directory(path).listSync(recursive: true)) {
|
||||||
if (!entry.path.endsWith(".status")) continue;
|
if (!entry.path.endsWith(".status")) continue;
|
||||||
|
|
||||||
formatFile(entry.path);
|
formatFile(entry.path);
|
||||||
|
@ -28,8 +28,8 @@ void main(List<String> arguments) {
|
||||||
|
|
||||||
void formatFile(String path) {
|
void formatFile(String path) {
|
||||||
try {
|
try {
|
||||||
var statusFile = new StatusFile.read(path);
|
var statusFile = StatusFile.read(path);
|
||||||
new File(path).writeAsStringSync(statusFile.serialize());
|
File(path).writeAsStringSync(statusFile.serialize());
|
||||||
print("Formatted $path");
|
print("Formatted $path");
|
||||||
} on SyntaxError catch (error) {
|
} on SyntaxError catch (error) {
|
||||||
stderr.writeln("Could not parse $path:\n$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';
|
import 'package:status_file/utils.dart';
|
||||||
|
|
||||||
ArgParser buildParser() {
|
ArgParser buildParser() {
|
||||||
var parser = new ArgParser();
|
var parser = ArgParser();
|
||||||
parser.addFlag("check-for-disjunctions",
|
parser.addFlag("check-for-disjunctions",
|
||||||
negatable: false,
|
negatable: false,
|
||||||
defaultsTo: false,
|
defaultsTo: false,
|
||||||
|
@ -78,7 +78,7 @@ void lintPath(path, {bool checkForDisjunctions = false}) {
|
||||||
filesWithErrors.add(path);
|
filesWithErrors.add(path);
|
||||||
}
|
}
|
||||||
} else if (FileSystemEntity.isDirectorySync(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)) {
|
if (!canLint(entry.path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ void lintPath(path, {bool checkForDisjunctions = false}) {
|
||||||
|
|
||||||
bool lintText(List<String> text, {bool checkForDisjunctions = false}) {
|
bool lintText(List<String> text, {bool checkForDisjunctions = false}) {
|
||||||
try {
|
try {
|
||||||
var statusFile = new StatusFile.parse("stdin", text);
|
var statusFile = StatusFile.parse("stdin", text);
|
||||||
return lintStatusFile(statusFile,
|
return lintStatusFile(statusFile,
|
||||||
checkForDisjunctions: checkForDisjunctions);
|
checkForDisjunctions: checkForDisjunctions);
|
||||||
} on status_file.SyntaxError {
|
} on status_file.SyntaxError {
|
||||||
|
@ -109,7 +109,7 @@ bool lintText(List<String> text, {bool checkForDisjunctions = false}) {
|
||||||
|
|
||||||
bool lintFile(String path, {bool checkForDisjunctions = false}) {
|
bool lintFile(String path, {bool checkForDisjunctions = false}) {
|
||||||
try {
|
try {
|
||||||
var statusFile = new StatusFile.read(path);
|
var statusFile = StatusFile.read(path);
|
||||||
return lintStatusFile(statusFile,
|
return lintStatusFile(statusFile,
|
||||||
checkForDisjunctions: checkForDisjunctions);
|
checkForDisjunctions: checkForDisjunctions);
|
||||||
} on status_file.SyntaxError catch (error) {
|
} on status_file.SyntaxError catch (error) {
|
||||||
|
@ -128,7 +128,7 @@ bool lintStatusFile(StatusFile statusFile,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (statusFile.path.isNotEmpty) {
|
if (statusFile.path.isNotEmpty) {
|
||||||
print("${statusFile.path}");
|
print(statusFile.path);
|
||||||
}
|
}
|
||||||
var errors = lintingErrors.toList();
|
var errors = lintingErrors.toList();
|
||||||
errors.sort((a, b) => a.lineNumber.compareTo((b.lineNumber)));
|
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';
|
import 'package:status_file/utils.dart';
|
||||||
|
|
||||||
ArgParser buildParser() {
|
ArgParser buildParser() {
|
||||||
var parser = new ArgParser();
|
var parser = ArgParser();
|
||||||
parser.addFlag("overwrite",
|
parser.addFlag("overwrite",
|
||||||
abbr: 'w',
|
abbr: 'w',
|
||||||
negatable: false,
|
negatable: false,
|
||||||
|
@ -48,7 +48,7 @@ void main(List<String> arguments) {
|
||||||
if (FileSystemEntity.isFileSync(path)) {
|
if (FileSystemEntity.isFileSync(path)) {
|
||||||
normalizeFile(path, overwrite);
|
normalizeFile(path, overwrite);
|
||||||
} else if (FileSystemEntity.isDirectorySync(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)) {
|
if (!canLint(entry.path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -60,10 +60,10 @@ void main(List<String> arguments) {
|
||||||
|
|
||||||
bool normalizeFile(String path, bool writeFile) {
|
bool normalizeFile(String path, bool writeFile) {
|
||||||
try {
|
try {
|
||||||
var statusFile = new StatusFile.read(path);
|
var statusFile = StatusFile.read(path);
|
||||||
var normalizedStatusFile = normalizeStatusFile(statusFile);
|
var normalizedStatusFile = normalizeStatusFile(statusFile);
|
||||||
if (writeFile) {
|
if (writeFile) {
|
||||||
new File(path).writeAsStringSync(normalizedStatusFile.toString());
|
File(path).writeAsStringSync(normalizedStatusFile.toString());
|
||||||
print("Normalized $path");
|
print("Normalized $path");
|
||||||
} else {
|
} else {
|
||||||
print(normalizedStatusFile);
|
print(normalizedStatusFile);
|
||||||
|
|
|
@ -159,7 +159,7 @@ Future<StatusFile> removeNonEssentialEntries(
|
||||||
String conditionPrefix = section.condition != Expression.always
|
String conditionPrefix = section.condition != Expression.always
|
||||||
? "${section.condition}"
|
? "${section.condition}"
|
||||||
: "";
|
: "";
|
||||||
String issueText = await getIssueText(comment, resolveIssueState);
|
String issueText = getIssueText(comment, resolveIssueState);
|
||||||
String statusLine = "$conditionPrefix\t$testName\t$expectations"
|
String statusLine = "$conditionPrefix\t$testName\t$expectations"
|
||||||
"\t$comment\t$issueText";
|
"\t$comment\t$issueText";
|
||||||
comments.add(statusLine);
|
comments.add(statusLine);
|
||||||
|
@ -217,7 +217,7 @@ void printHelp(ArgParser parser) {
|
||||||
}
|
}
|
||||||
|
|
||||||
String formatComments(List<String> comments) {
|
String formatComments(List<String> comments) {
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = StringBuffer();
|
||||||
for (String statusLine in comments) {
|
for (String statusLine in comments) {
|
||||||
sb.writeln(statusLine);
|
sb.writeln(statusLine);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,24 +14,24 @@ import 'src/expression.dart';
|
||||||
/// Matches the header that begins a new section, like:
|
/// Matches the header that begins a new section, like:
|
||||||
///
|
///
|
||||||
/// [ $compiler == dart2js && $minified ]
|
/// [ $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,
|
/// Matches an entry that defines the status for a path in the current section,
|
||||||
/// like:
|
/// like:
|
||||||
///
|
///
|
||||||
/// some/path/to/some_test: Pass || Fail
|
/// 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:
|
/// Matches an issue number in a comment, like:
|
||||||
///
|
///
|
||||||
/// blah_test: Fail # Issue 1234
|
/// 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:
|
/// Matches a comment and indented comment, like:
|
||||||
///
|
///
|
||||||
/// < white space > #
|
/// < 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
|
/// A parsed status file, which describes how a collection of tests are
|
||||||
/// expected to behave under various configurations and conditions.
|
/// expected to behave under various configurations and conditions.
|
||||||
|
@ -80,7 +80,7 @@ class StatusFile {
|
||||||
///
|
///
|
||||||
/// Throws a [SyntaxError] if the file could not be parsed.
|
/// Throws a [SyntaxError] if the file could not be parsed.
|
||||||
StatusFile.read(this.path) {
|
StatusFile.read(this.path) {
|
||||||
_parse(new File(path).readAsLinesSync());
|
_parse(File(path).readAsLinesSync());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses lines of strings coming from a status file at [path].
|
/// Parses lines of strings coming from a status file at [path].
|
||||||
|
@ -152,11 +152,10 @@ class StatusFile {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (line.isEmpty) {
|
if (line.isEmpty) {
|
||||||
sectionHeaderComments.add(new EmptyEntry(_lineCount));
|
sectionHeaderComments.add(EmptyEntry(_lineCount));
|
||||||
lastEmptyLine = _lineCount;
|
lastEmptyLine = _lineCount;
|
||||||
} else {
|
} else {
|
||||||
sectionHeaderComments
|
sectionHeaderComments.add(CommentEntry(_lineCount, Comment(line)));
|
||||||
.add(new CommentEntry(_lineCount, new Comment(line)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,7 +177,7 @@ class StatusFile {
|
||||||
// The current section whose rules are being parsed. Initialized to an
|
// The current section whose rules are being parsed. Initialized to an
|
||||||
// implicit section that matches everything.
|
// implicit section that matches everything.
|
||||||
StatusSection section =
|
StatusSection section =
|
||||||
new StatusSection(Expression.always, -1, implicitSectionHeaderComments);
|
StatusSection(Expression.always, -1, implicitSectionHeaderComments);
|
||||||
section.entries.addAll(entries);
|
section.entries.addAll(entries);
|
||||||
sections.add(section);
|
sections.add(section);
|
||||||
|
|
||||||
|
@ -186,12 +185,12 @@ class StatusFile {
|
||||||
var line = lines[_lineCount - 1];
|
var line = lines[_lineCount - 1];
|
||||||
|
|
||||||
fail(String message, [List<String>? errors]) {
|
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 it is an empty line
|
||||||
if (line.isEmpty) {
|
if (line.isEmpty) {
|
||||||
section.entries.add(new EmptyEntry(_lineCount));
|
section.entries.add(EmptyEntry(_lineCount));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,8 +199,7 @@ class StatusFile {
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
try {
|
try {
|
||||||
var condition = Expression.parse(match[1]!.trim());
|
var condition = Expression.parse(match[1]!.trim());
|
||||||
section =
|
section = StatusSection(condition, _lineCount, sectionHeaderComments);
|
||||||
new StatusSection(condition, _lineCount, sectionHeaderComments);
|
|
||||||
sections.add(section);
|
sections.add(section);
|
||||||
// Reset section header comments.
|
// Reset section header comments.
|
||||||
sectionHeaderComments = [];
|
sectionHeaderComments = [];
|
||||||
|
@ -226,10 +224,10 @@ class StatusFile {
|
||||||
});
|
});
|
||||||
if (match[3] == null) {
|
if (match[3] == null) {
|
||||||
section.entries
|
section.entries
|
||||||
.add(new StatusEntry(path, _lineCount, expectations, null));
|
.add(StatusEntry(path, _lineCount, expectations, null));
|
||||||
} else {
|
} else {
|
||||||
section.entries.add(new StatusEntry(
|
section.entries.add(
|
||||||
path, _lineCount, expectations, new Comment(match[3]!)));
|
StatusEntry(path, _lineCount, expectations, Comment(match[3]!)));
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -238,7 +236,7 @@ class StatusFile {
|
||||||
// section or the next section
|
// section or the next section
|
||||||
match = _commentPattern.firstMatch(line);
|
match = _commentPattern.firstMatch(line);
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
var commentEntry = new CommentEntry(_lineCount, new Comment(line));
|
var commentEntry = CommentEntry(_lineCount, Comment(line));
|
||||||
if (hasBreakFromPreviousSection(_lineCount) &&
|
if (hasBreakFromPreviousSection(_lineCount) &&
|
||||||
commentBelongsToNextSectionHeader(_lineCount)) {
|
commentBelongsToNextSectionHeader(_lineCount)) {
|
||||||
sectionHeaderComments.add(commentEntry);
|
sectionHeaderComments.add(commentEntry);
|
||||||
|
@ -253,7 +251,7 @@ class StatusFile {
|
||||||
|
|
||||||
// There are no comment entries in [sectionHeaderComments], because of the
|
// There are no comment entries in [sectionHeaderComments], because of the
|
||||||
// check for [commentBelongsToSectionHeader].
|
// check for [commentBelongsToSectionHeader].
|
||||||
assert(sectionHeaderComments.length == 0);
|
assert(sectionHeaderComments.isEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isEmpty => sections.length == 1 && sections[0].isEmpty();
|
bool get isEmpty => sections.length == 1 && sections[0].isEmpty();
|
||||||
|
@ -269,7 +267,7 @@ class StatusFile {
|
||||||
|
|
||||||
if (errors.isNotEmpty) {
|
if (errors.isNotEmpty) {
|
||||||
var s = errors.length > 1 ? "s" : "";
|
var s = errors.length > 1 ? "s" : "";
|
||||||
throw new SyntaxError(_shortPath, section.lineNumber,
|
throw SyntaxError(_shortPath, section.lineNumber,
|
||||||
"[ ${section.condition} ]", 'Validation error$s', errors);
|
"[ ${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
|
/// 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.
|
/// "canonical" rendering of the status file that can be saved back to disc.
|
||||||
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var buffer = new StringBuffer();
|
var buffer = StringBuffer();
|
||||||
sections.forEach(buffer.write);
|
sections.forEach(buffer.write);
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
@ -317,7 +316,7 @@ class StatusSection {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var buffer = new StringBuffer();
|
var buffer = StringBuffer();
|
||||||
sectionHeaderComments.forEach(buffer.writeln);
|
sectionHeaderComments.forEach(buffer.writeln);
|
||||||
if (condition != Expression.always) {
|
if (condition != Expression.always) {
|
||||||
buffer.writeln("[ $condition ]");
|
buffer.writeln("[ $condition ]");
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
/// The possible outcomes from running a test.
|
/// The possible outcomes from running a test.
|
||||||
class Expectation {
|
class Expectation {
|
||||||
/// The test completed normally and did what it intended to do.
|
/// 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
|
/// 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
|
/// from the test itself. This is not considered a failure. It means an
|
||||||
|
@ -20,22 +20,22 @@ class Expectation {
|
||||||
/// internal exception in dart2js itself.
|
/// internal exception in dart2js itself.
|
||||||
///
|
///
|
||||||
/// * The browser process crashes.
|
/// * 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
|
/// The test did not complete (either successfully or unsuccessfully) in the
|
||||||
/// amount of time that the test runner gave it.
|
/// 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.
|
/// The test completed but did not produce the intended output.
|
||||||
///
|
///
|
||||||
/// This status is rarely used directly. Instead, most of the expectations
|
/// This status is rarely used directly. Instead, most of the expectations
|
||||||
/// below refine this into more specific reasons *why* it failed.
|
/// 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
|
/// The test compiled and began executing but then threw an uncaught
|
||||||
/// exception or produced the wrong output.
|
/// exception or produced the wrong output.
|
||||||
static final Expectation runtimeError =
|
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
|
/// The test failed with an error at compile time and did not execute any
|
||||||
/// code.
|
/// code.
|
||||||
|
@ -44,70 +44,70 @@ class Expectation {
|
||||||
/// * For an analyzer test, means the analyzer reported a static error.
|
/// * For an analyzer test, means the analyzer reported a static error.
|
||||||
/// * For a dart2js test, means dart2js reported a compile error.
|
/// * For a dart2js test, means dart2js reported a compile error.
|
||||||
static final Expectation compileTimeError =
|
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.
|
/// The test was parsed by the spec_parser, and there was a syntax error.
|
||||||
static final Expectation syntaxError =
|
static final Expectation syntaxError =
|
||||||
new Expectation._('SyntaxError', group: fail);
|
Expectation._('SyntaxError', group: fail);
|
||||||
|
|
||||||
/// The test itself contains a comment with `@runtime-error` in it,
|
/// The test itself contains a comment with `@runtime-error` in it,
|
||||||
/// indicating it should have produced a runtime error when run. But when it
|
/// indicating it should have produced a runtime error when run. But when it
|
||||||
/// was run, the test completed without error.
|
/// was run, the test completed without error.
|
||||||
static final Expectation missingRuntimeError =
|
static final Expectation missingRuntimeError =
|
||||||
new Expectation._('MissingRuntimeError', group: fail);
|
Expectation._('MissingRuntimeError', group: fail);
|
||||||
|
|
||||||
/// The test itself contains a comment with `@syntax-error` in it,
|
/// The test itself contains a comment with `@syntax-error` in it,
|
||||||
/// indicating it should have produced a syntax error when compiled. But when
|
/// indicating it should have produced a syntax error when compiled. But when
|
||||||
/// it was compiled, no error was reported.
|
/// it was compiled, no error was reported.
|
||||||
static final Expectation missingSyntaxError =
|
static final Expectation missingSyntaxError =
|
||||||
new Expectation._('MissingSyntaxError', group: fail);
|
Expectation._('MissingSyntaxError', group: fail);
|
||||||
|
|
||||||
/// The test itself contains a comment with `@compile-error` in it,
|
/// The test itself contains a comment with `@compile-error` in it,
|
||||||
/// indicating it should have produced an error when compiled. But when it
|
/// indicating it should have produced an error when compiled. But when it
|
||||||
/// was compiled, no error was reported.
|
/// was compiled, no error was reported.
|
||||||
static final Expectation missingCompileTimeError =
|
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
|
/// When the test is processed by analyzer, a static warning should be
|
||||||
/// reported.
|
/// reported.
|
||||||
static final Expectation staticWarning =
|
static final Expectation staticWarning =
|
||||||
new Expectation._('StaticWarning', group: fail);
|
Expectation._('StaticWarning', group: fail);
|
||||||
|
|
||||||
/// The test itself contains a comment with `@static-warning` in it,
|
/// The test itself contains a comment with `@static-warning` in it,
|
||||||
/// indicating analyzer should report a static warning when analyzing it, but
|
/// indicating analyzer should report a static warning when analyzing it, but
|
||||||
/// analysis did not produce any warnings.
|
/// analysis did not produce any warnings.
|
||||||
static final Expectation missingStaticWarning =
|
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.
|
/// An invocation of "pub get" exited with a non-zero exit code.
|
||||||
// TODO(rnystrom): Is this still used? If not, remove.
|
// TODO(rnystrom): Is this still used? If not, remove.
|
||||||
static final Expectation pubGetError =
|
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
|
/// The stdout or stderr produced by the test was not valid UTF-8 and could
|
||||||
/// not be decoded.
|
/// not be decoded.
|
||||||
// TODO(rnystrom): The only test that uses this expectation is the one that
|
// TODO(rnystrom): The only test that uses this expectation is the one that
|
||||||
// tests that the test runner handles this expectation. Remove it?
|
// tests that the test runner handles this expectation. Remove it?
|
||||||
static final Expectation nonUtf8Error =
|
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
|
/// The stdout or stderr produced by the test was too long and had to be
|
||||||
/// truncated by the test runner.
|
/// truncated by the test runner.
|
||||||
static final Expectation truncatedOutput =
|
static final Expectation truncatedOutput =
|
||||||
new Expectation._('TruncatedOutput', group: fail);
|
Expectation._('TruncatedOutput', group: fail);
|
||||||
|
|
||||||
/// The VM exited with the special exit code 252.
|
/// The VM exited with the special exit code 252.
|
||||||
static final Expectation dartkCrash =
|
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.
|
/// A timeout occurred in a test using the Kernel-based front end.
|
||||||
static final Expectation dartkTimeout =
|
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
|
/// A compile error was reported on a test compiled using the Kernel-based
|
||||||
/// front end.
|
/// front end.
|
||||||
static final Expectation dartkCompileTimeError =
|
static final Expectation dartkCompileTimeError =
|
||||||
new Expectation._('DartkCompileTimeError', group: compileTimeError);
|
Expectation._('DartkCompileTimeError', group: compileTimeError);
|
||||||
|
|
||||||
// "meta expectations"
|
// "meta expectations"
|
||||||
/// A marker applied to a test to indicate that the other non-pass
|
/// 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
|
// 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
|
// 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.
|
// 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.
|
/// A marker that indicates the test takes longer to complete than most tests.
|
||||||
/// Tells the test runner to increase the timeout when running it.
|
/// 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
|
/// A marker that indicates the test takes a lot longer to complete than most
|
||||||
/// tests.
|
/// tests.
|
||||||
/// Tells the test runner to increase the timeout when running it.
|
/// Tells the test runner to increase the timeout when running it.
|
||||||
static final Expectation extraSlow =
|
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.
|
/// 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
|
/// 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
|
/// doesn't indicate *why* the test is being skipped and means we won't
|
||||||
/// notice if the actual behavior of the test changes.
|
/// 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
|
/// Tells the test runner to skip the test because it takes too long to
|
||||||
/// complete.
|
/// complete.
|
||||||
|
@ -147,7 +147,7 @@ class Expectation {
|
||||||
/// Prefer this over timeout since this avoids wasting CPU resources running
|
/// Prefer this over timeout since this avoids wasting CPU resources running
|
||||||
/// a test we know won't complete.
|
/// a test we know won't complete.
|
||||||
static final Expectation skipSlow =
|
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
|
/// Skips this test because it is not intended to be meaningful for a certain
|
||||||
/// reason or on some configuration.
|
/// reason or on some configuration.
|
||||||
|
@ -155,21 +155,21 @@ class Expectation {
|
||||||
/// For example, tests that use dart:io are SkipByDesign on the browser since
|
/// For example, tests that use dart:io are SkipByDesign on the browser since
|
||||||
/// dart:io isn't supported there.
|
/// dart:io isn't supported there.
|
||||||
static final Expectation skipByDesign =
|
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,
|
/// 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.
|
/// and assumed to meet the expectations, due to an infrastructure failure.
|
||||||
///
|
///
|
||||||
/// This should not appear in status files.
|
/// 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.
|
/// 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.
|
/// Included here so that we can parse .status files that contain it.
|
||||||
static final Expectation verificationError =
|
static final Expectation verificationError =
|
||||||
new Expectation._('VerificationError');
|
Expectation._('VerificationError');
|
||||||
|
|
||||||
/// Maps case-insensitive names to expectations.
|
/// 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,
|
pass,
|
||||||
crash,
|
crash,
|
||||||
timeout,
|
timeout,
|
||||||
|
@ -199,7 +199,7 @@ class Expectation {
|
||||||
static Expectation find(String name) {
|
static Expectation find(String name) {
|
||||||
var expectation = _all[name.toLowerCase()];
|
var expectation = _all[name.toLowerCase()];
|
||||||
if (expectation == null) {
|
if (expectation == null) {
|
||||||
throw new ArgumentError("Could not find an expectation named '$name'.");
|
throw ArgumentError("Could not find an expectation named '$name'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return expectation;
|
return expectation;
|
||||||
|
@ -211,7 +211,7 @@ class Expectation {
|
||||||
/// Whether this expectation is a test outcome. If not, it's a "meta marker".
|
/// Whether this expectation is a test outcome. If not, it's a "meta marker".
|
||||||
final bool isOutcome;
|
final bool isOutcome;
|
||||||
|
|
||||||
Expectation._(this._name, {Expectation? group, bool isMeta: false})
|
Expectation._(this._name, {Expectation? group, bool isMeta = false})
|
||||||
: _group = group,
|
: _group = group,
|
||||||
isOutcome = !isMeta;
|
isOutcome = !isMeta;
|
||||||
|
|
||||||
|
@ -229,5 +229,6 @@ class Expectation {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
String toString() => _name;
|
String toString() => _name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
import 'expression.dart';
|
import 'expression.dart';
|
||||||
import '../environment.dart';
|
import '../environment.dart';
|
||||||
|
|
||||||
final Expression T = new LogicExpression.and([]);
|
final Expression T = LogicExpression.and([]);
|
||||||
final Expression F = new LogicExpression.or([]);
|
final Expression F = LogicExpression.or([]);
|
||||||
|
|
||||||
// Token to combine left and right value of a comparison expression in a
|
// Token to combine left and right value of a comparison expression in a
|
||||||
// variable expression.
|
// variable expression.
|
||||||
|
@ -32,7 +32,7 @@ Expression toDisjunctiveNormalForm(Expression expression) {
|
||||||
} else if (minTerms.isEmpty) {
|
} else if (minTerms.isEmpty) {
|
||||||
return F;
|
return F;
|
||||||
}
|
}
|
||||||
var disjunctiveNormalForm = new LogicExpression.or(minTerms);
|
var disjunctiveNormalForm = LogicExpression.or(minTerms);
|
||||||
disjunctiveNormalForm = _minimizeByComplementation(disjunctiveNormalForm);
|
disjunctiveNormalForm = _minimizeByComplementation(disjunctiveNormalForm);
|
||||||
return _recoverComparisonExpressions(disjunctiveNormalForm);
|
return _recoverComparisonExpressions(disjunctiveNormalForm);
|
||||||
}
|
}
|
||||||
|
@ -101,10 +101,10 @@ LogicExpression _minimizeByComplementation(LogicExpression expression) {
|
||||||
return onesInA - onesInB;
|
return onesInA - onesInB;
|
||||||
});
|
});
|
||||||
var combinedMinSets = _combineMinSets(
|
var combinedMinSets = _combineMinSets(
|
||||||
clauses.map((e) => [new LogicExpression.and(e)]).toList(), []);
|
clauses.map((e) => [LogicExpression.and(e)]).toList(), []);
|
||||||
List<List<LogicExpression>> minCover = _findMinCover(combinedMinSets, []);
|
List<List<LogicExpression>> minCover = _findMinCover(combinedMinSets, []);
|
||||||
var finalOperands = minCover.map((minSet) => _reduceMinSet(minSet)).toList();
|
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
|
/// Computes all assignments of literals that make the [expression] evaluate to
|
||||||
|
@ -113,7 +113,7 @@ List<Expression>? _satisfiableMinTerms(Expression expression) {
|
||||||
var variables = _getVariables(expression);
|
var variables = _getVariables(expression);
|
||||||
bool hasNotSatisfiableAssignment = false;
|
bool hasNotSatisfiableAssignment = false;
|
||||||
List<Expression> satisfiableTerms = <Expression>[];
|
List<Expression> satisfiableTerms = <Expression>[];
|
||||||
var environment = new TruthTableEnvironment(variables);
|
var environment = TruthTableEnvironment(variables);
|
||||||
for (int i = 0; i < 1 << variables.length; i++) {
|
for (int i = 0; i < 1 << variables.length; i++) {
|
||||||
environment.setConfiguration(i);
|
environment.setConfiguration(i);
|
||||||
if (expression.evaluate(environment)) {
|
if (expression.evaluate(environment)) {
|
||||||
|
@ -125,7 +125,7 @@ List<Expression>? _satisfiableMinTerms(Expression expression) {
|
||||||
operands.add(variables[j]);
|
operands.add(variables[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
satisfiableTerms.add(new LogicExpression.and(operands));
|
satisfiableTerms.add(LogicExpression.and(operands));
|
||||||
} else {
|
} else {
|
||||||
hasNotSatisfiableAssignment = true;
|
hasNotSatisfiableAssignment = true;
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ class TruthTableEnvironment extends Environment {
|
||||||
List<List<LogicExpression>> _combineMinSets(List<List<LogicExpression>> minSets,
|
List<List<LogicExpression>> _combineMinSets(List<List<LogicExpression>> minSets,
|
||||||
List<List<LogicExpression>> primeImplicants) {
|
List<List<LogicExpression>> primeImplicants) {
|
||||||
List<List<LogicExpression>> combined = <List<LogicExpression>>[];
|
List<List<LogicExpression>> combined = <List<LogicExpression>>[];
|
||||||
var addedInThisIteration = new Set<List<LogicExpression>>();
|
var addedInThisIteration = <List<LogicExpression>>{};
|
||||||
for (var i = 0; i < minSets.length; i++) {
|
for (var i = 0; i < minSets.length; i++) {
|
||||||
var minSet = minSets[i];
|
var minSet = minSets[i];
|
||||||
var combinedMinSet = false;
|
var combinedMinSet = false;
|
||||||
|
@ -255,7 +255,7 @@ LogicExpression _reduceMinSet(List<LogicExpression> minSet) {
|
||||||
negative.remove(neg);
|
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
|
/// [_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
|
// Computes the difference between two sets of expressions in disjunctive normal
|
||||||
// form. if the difference is a negation, the difference is only counted once.
|
// form. If the difference is a negation, the difference is only counted once.
|
||||||
List<Expression> _difference(List<Expression> As, List<Expression> Bs) {
|
List<Expression> _difference(List<Expression> aList, List<Expression> bList) {
|
||||||
var difference = <Expression>[]
|
var difference = <Expression>[
|
||||||
..addAll(As.where((a) => _findFirst(a, Bs) == null))
|
...aList.where((a) => _findFirst(a, bList) == null),
|
||||||
..addAll(Bs.where((b) => _findFirst(b, As) == null));
|
...bList.where((b) => _findFirst(b, aList) == null),
|
||||||
|
];
|
||||||
for (var expression in difference.toList()) {
|
for (var expression in difference.toList()) {
|
||||||
if (_isNegatedExpression(expression)) {
|
if (_isNegatedExpression(expression)) {
|
||||||
if (_findFirst(negate(expression), difference) != null) {
|
if (_findFirst(negate(expression), difference) != null) {
|
||||||
|
@ -390,37 +391,37 @@ bool _isNegatedExpression(Expression expression) {
|
||||||
List<Expression> _getVariables(Expression expression) {
|
List<Expression> _getVariables(Expression expression) {
|
||||||
if (expression is LogicExpression) {
|
if (expression is LogicExpression) {
|
||||||
var variables = <Expression>[];
|
var variables = <Expression>[];
|
||||||
expression.operands.forEach(
|
for (var e in expression.operands) {
|
||||||
(e) => _getVariables(e).forEach((v) => _addIfNotPresent(v, variables)));
|
_getVariables(e).forEach((v) => _addIfNotPresent(v, variables));
|
||||||
|
}
|
||||||
return variables;
|
return variables;
|
||||||
}
|
}
|
||||||
if (expression is VariableExpression) {
|
if (expression is VariableExpression) {
|
||||||
return [new VariableExpression(expression.variable)];
|
return [VariableExpression(expression.variable)];
|
||||||
}
|
}
|
||||||
if (expression is ComparisonExpression) {
|
if (expression is ComparisonExpression) {
|
||||||
throw new Exception("Cannot use ComparisonExpression for variables");
|
throw Exception("Cannot use ComparisonExpression for variables");
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression negate(Expression expression, {bool positive: false}) {
|
Expression negate(Expression expression, {bool positive = false}) {
|
||||||
if (expression is LogicExpression && expression.isOr) {
|
if (expression is LogicExpression && expression.isOr) {
|
||||||
return new LogicExpression.and(expression.operands
|
return LogicExpression.and(expression.operands
|
||||||
.map((e) => negate(e, positive: !positive))
|
.map((e) => negate(e, positive: !positive))
|
||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
if (expression is LogicExpression && expression.isAnd) {
|
if (expression is LogicExpression && expression.isAnd) {
|
||||||
return new LogicExpression.or(expression.operands
|
return LogicExpression.or(expression.operands
|
||||||
.map((e) => negate(e, positive: !positive))
|
.map((e) => negate(e, positive: !positive))
|
||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
if (expression is ComparisonExpression) {
|
if (expression is ComparisonExpression) {
|
||||||
return new ComparisonExpression(
|
return ComparisonExpression(
|
||||||
expression.left, expression.right, !expression.negate);
|
expression.left, expression.right, !expression.negate);
|
||||||
}
|
}
|
||||||
if (expression is VariableExpression) {
|
if (expression is VariableExpression) {
|
||||||
return new VariableExpression(expression.variable,
|
return VariableExpression(expression.variable, negate: !expression.negate);
|
||||||
negate: !expression.negate);
|
|
||||||
}
|
}
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
@ -429,16 +430,15 @@ Expression negate(Expression expression, {bool positive: false}) {
|
||||||
// we can se individual variables truthiness in the [TruthTableEnvironment].
|
// we can se individual variables truthiness in the [TruthTableEnvironment].
|
||||||
Expression _comparisonExpressionsToVariableExpressions(Expression expression) {
|
Expression _comparisonExpressionsToVariableExpressions(Expression expression) {
|
||||||
if (expression is LogicExpression) {
|
if (expression is LogicExpression) {
|
||||||
return new LogicExpression(
|
return LogicExpression(
|
||||||
expression.op,
|
expression.op,
|
||||||
expression.operands
|
expression.operands
|
||||||
.map((exp) => _comparisonExpressionsToVariableExpressions(exp))
|
.map((exp) => _comparisonExpressionsToVariableExpressions(exp))
|
||||||
.toList());
|
.toList());
|
||||||
}
|
}
|
||||||
if (expression is ComparisonExpression) {
|
if (expression is ComparisonExpression) {
|
||||||
return new VariableExpression(
|
return VariableExpression(
|
||||||
new Variable(
|
Variable(expression.left.name + _comparisonToken + expression.right),
|
||||||
expression.left.name + _comparisonToken + expression.right),
|
|
||||||
negate: expression.negate);
|
negate: expression.negate);
|
||||||
}
|
}
|
||||||
return expression;
|
return expression;
|
||||||
|
@ -446,7 +446,7 @@ Expression _comparisonExpressionsToVariableExpressions(Expression expression) {
|
||||||
|
|
||||||
Expression _recoverComparisonExpressions(Expression expression) {
|
Expression _recoverComparisonExpressions(Expression expression) {
|
||||||
if (expression is LogicExpression) {
|
if (expression is LogicExpression) {
|
||||||
return new LogicExpression(
|
return LogicExpression(
|
||||||
expression.op,
|
expression.op,
|
||||||
expression.operands
|
expression.operands
|
||||||
.map((exp) => _recoverComparisonExpressions(exp))
|
.map((exp) => _recoverComparisonExpressions(exp))
|
||||||
|
@ -455,8 +455,8 @@ Expression _recoverComparisonExpressions(Expression expression) {
|
||||||
if (expression is VariableExpression &&
|
if (expression is VariableExpression &&
|
||||||
expression.variable.name.contains(_comparisonToken)) {
|
expression.variable.name.contains(_comparisonToken)) {
|
||||||
int tokenIndex = expression.variable.name.indexOf(_comparisonToken);
|
int tokenIndex = expression.variable.name.indexOf(_comparisonToken);
|
||||||
return new ComparisonExpression(
|
return ComparisonExpression(
|
||||||
new Variable(expression.variable.name.substring(0, tokenIndex)),
|
Variable(expression.variable.name.substring(0, tokenIndex)),
|
||||||
expression.variable.name
|
expression.variable.name
|
||||||
.substring(tokenIndex + _comparisonToken.length),
|
.substring(tokenIndex + _comparisonToken.length),
|
||||||
expression.negate);
|
expression.negate);
|
||||||
|
|
|
@ -29,7 +29,7 @@ abstract class Expression implements Comparable<Expression> {
|
||||||
/// Expressions evaluate as expected, with values of variables found in an
|
/// Expressions evaluate as expected, with values of variables found in an
|
||||||
/// environment passed to the evaluator.
|
/// environment passed to the evaluator.
|
||||||
static Expression parse(String expression) =>
|
static Expression parse(String expression) =>
|
||||||
new _ExpressionParser(expression).parse();
|
_ExpressionParser(expression).parse();
|
||||||
|
|
||||||
const Expression();
|
const Expression();
|
||||||
|
|
||||||
|
@ -68,6 +68,7 @@ abstract class Expression implements Comparable<Expression> {
|
||||||
/// This is useful for things like sorting lists of expressions or
|
/// This is useful for things like sorting lists of expressions or
|
||||||
/// normalizing a list of subexpressions. The rough logic is that higher
|
/// normalizing a list of subexpressions. The rough logic is that higher
|
||||||
/// precedence and alphabetically lower expressions come first.
|
/// precedence and alphabetically lower expressions come first.
|
||||||
|
@override
|
||||||
int compareTo(Expression other) {
|
int compareTo(Expression other) {
|
||||||
var comparison = _typeComparison.compareTo(other._typeComparison);
|
var comparison = _typeComparison.compareTo(other._typeComparison);
|
||||||
if (comparison != 0) return comparison;
|
if (comparison != 0) return comparison;
|
||||||
|
@ -104,7 +105,7 @@ class Variable {
|
||||||
String lookup(Environment environment) {
|
String lookup(Environment environment) {
|
||||||
var value = environment.lookUp(name);
|
var value = environment.lookUp(name);
|
||||||
if (value == null) {
|
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.");
|
"while evaluating status file expression.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,25 +154,29 @@ class ComparisonExpression extends Expression {
|
||||||
|
|
||||||
ComparisonExpression(this.left, this.right, this.negate);
|
ComparisonExpression(this.left, this.right, this.negate);
|
||||||
|
|
||||||
|
@override
|
||||||
void validate(Environment environment, List<String> errors) {
|
void validate(Environment environment, List<String> errors) {
|
||||||
environment.validate(left.name, right, errors);
|
environment.validate(left.name, right, errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
bool evaluate(Environment environment) {
|
bool evaluate(Environment environment) {
|
||||||
return negate != (left.lookup(environment) == right);
|
return negate != (left.lookup(environment) == right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
Expression normalize() {
|
Expression normalize() {
|
||||||
// Replace Boolean comparisons with a straight variable expression.
|
// Replace Boolean comparisons with a straight variable expression.
|
||||||
if (right == "true") {
|
if (right == "true") {
|
||||||
return new VariableExpression(left, negate: negate);
|
return VariableExpression(left, negate: negate);
|
||||||
} else if (right == "false") {
|
} else if (right == "false") {
|
||||||
return new VariableExpression(left, negate: !negate);
|
return VariableExpression(left, negate: !negate);
|
||||||
} else {
|
} else {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
int _compareToMyType(ComparisonExpression other) {
|
int _compareToMyType(ComparisonExpression other) {
|
||||||
if (left.name != other.left.name) {
|
if (left.name != other.left.name) {
|
||||||
return left.name.compareTo(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
|
// Comparisons come before variables so that "$compiler == ..." and
|
||||||
// "$runtime == ..." appear on the left in status expressions.
|
// "$runtime == ..." appear on the left in status expressions.
|
||||||
|
@override
|
||||||
int get _typeComparison => 1;
|
int get _typeComparison => 1;
|
||||||
|
|
||||||
|
@override
|
||||||
String toString() => "\$${left.name} ${negate ? '!=' : '=='} $right";
|
String toString() => "\$${left.name} ${negate ? '!=' : '=='} $right";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,17 +221,21 @@ class VariableExpression extends Expression {
|
||||||
|
|
||||||
VariableExpression(this.variable, {this.negate = false});
|
VariableExpression(this.variable, {this.negate = false});
|
||||||
|
|
||||||
|
@override
|
||||||
void validate(Environment environment, List<String> errors) {
|
void validate(Environment environment, List<String> errors) {
|
||||||
// It must be a Boolean, so it should allow either Boolean value.
|
// It must be a Boolean, so it should allow either Boolean value.
|
||||||
environment.validate(variable.name, "true", errors);
|
environment.validate(variable.name, "true", errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
bool evaluate(Environment environment) =>
|
bool evaluate(Environment environment) =>
|
||||||
negate != (variable.lookup(environment) == "true");
|
negate != (variable.lookup(environment) == "true");
|
||||||
|
|
||||||
/// Variable expressions are fine as they are.
|
/// Variable expressions are fine as they are.
|
||||||
|
@override
|
||||||
Expression normalize() => this;
|
Expression normalize() => this;
|
||||||
|
|
||||||
|
@override
|
||||||
int _compareToMyType(VariableExpression other) {
|
int _compareToMyType(VariableExpression other) {
|
||||||
if (variable.name != other.variable.name) {
|
if (variable.name != other.variable.name) {
|
||||||
return variable.name.compareTo(other.variable.name);
|
return variable.name.compareTo(other.variable.name);
|
||||||
|
@ -233,8 +244,10 @@ class VariableExpression extends Expression {
|
||||||
return _compareBool(negate, other.negate);
|
return _compareBool(negate, other.negate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
int get _typeComparison => 2;
|
int get _typeComparison => 2;
|
||||||
|
|
||||||
|
@override
|
||||||
String toString() => "${negate ? "!" : ""}\$${variable.name}";
|
String toString() => "${negate ? "!" : ""}\$${variable.name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,12 +266,14 @@ class LogicExpression extends Expression {
|
||||||
bool get isAnd => op == _Token.and;
|
bool get isAnd => op == _Token.and;
|
||||||
bool get isOr => op == _Token.or;
|
bool get isOr => op == _Token.or;
|
||||||
|
|
||||||
|
@override
|
||||||
void validate(Environment environment, List<String> errors) {
|
void validate(Environment environment, List<String> errors) {
|
||||||
for (var operand in operands) {
|
for (var operand in operands) {
|
||||||
operand.validate(environment, errors);
|
operand.validate(environment, errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
bool evaluate(Environment environment) {
|
bool evaluate(Environment environment) {
|
||||||
if (op == _Token.and) {
|
if (op == _Token.and) {
|
||||||
return operands.every((operand) => operand.evaluate(environment));
|
return operands.every((operand) => operand.evaluate(environment));
|
||||||
|
@ -267,6 +282,7 @@ class LogicExpression extends Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
LogicExpression normalize() {
|
LogicExpression normalize() {
|
||||||
// Normalize the order of the clauses. Since there is no short-circuiting,
|
// 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
|
// a || b means the same as b || a. Picking a standard order lets us
|
||||||
|
@ -274,12 +290,12 @@ class LogicExpression extends Expression {
|
||||||
// order.
|
// order.
|
||||||
|
|
||||||
// Recurse into the operands, sort them, and remove duplicates.
|
// Recurse into the operands, sort them, and remove duplicates.
|
||||||
var normalized = new LogicExpression(
|
var normalized = LogicExpression(
|
||||||
op, operands.map((operand) => operand.normalize()).toList())
|
op, operands.map((operand) => operand.normalize()).toList())
|
||||||
.operands;
|
.operands;
|
||||||
normalized = flatten(normalized);
|
normalized = flatten(normalized);
|
||||||
var ordered = new SplayTreeSet<Expression>.from(normalized).toList();
|
var ordered = SplayTreeSet<Expression>.from(normalized).toList();
|
||||||
return new LogicExpression(op, ordered);
|
return LogicExpression(op, ordered);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Expression> flatten(List<Expression> operands) {
|
List<Expression> flatten(List<Expression> operands) {
|
||||||
|
@ -294,6 +310,7 @@ class LogicExpression extends Expression {
|
||||||
return newOperands;
|
return newOperands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
int _compareToMyType(LogicExpression other) {
|
int _compareToMyType(LogicExpression other) {
|
||||||
// Put "&&" before "||".
|
// Put "&&" before "||".
|
||||||
if (op != other.op) return op == _Token.and ? -1 : 1;
|
if (op != other.op) return op == _Token.and ? -1 : 1;
|
||||||
|
@ -311,8 +328,10 @@ class LogicExpression extends Expression {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
int get _typeComparison => 3;
|
int get _typeComparison => 3;
|
||||||
|
|
||||||
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
String parenthesize(Expression operand) {
|
String parenthesize(Expression operand) {
|
||||||
var result = operand.toString();
|
var result = operand.toString();
|
||||||
|
@ -331,14 +350,14 @@ class LogicExpression extends Expression {
|
||||||
class _ExpressionParser {
|
class _ExpressionParser {
|
||||||
final _Scanner _scanner;
|
final _Scanner _scanner;
|
||||||
|
|
||||||
_ExpressionParser(String expression) : _scanner = new _Scanner(expression);
|
_ExpressionParser(String expression) : _scanner = _Scanner(expression);
|
||||||
|
|
||||||
Expression parse() {
|
Expression parse() {
|
||||||
var expression = _parseOr();
|
var expression = _parseOr();
|
||||||
|
|
||||||
// Should consume entire string.
|
// Should consume entire string.
|
||||||
if (_scanner.hasMore) {
|
if (_scanner.hasMore) {
|
||||||
throw new FormatException("Unexpected input after expression");
|
throw FormatException("Unexpected input after expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
return expression;
|
return expression;
|
||||||
|
@ -352,7 +371,7 @@ class _ExpressionParser {
|
||||||
|
|
||||||
if (operands.length == 1) return operands.single;
|
if (operands.length == 1) return operands.single;
|
||||||
|
|
||||||
return new LogicExpression(_Token.or, operands);
|
return LogicExpression(_Token.or, operands);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression _parseAnd() {
|
Expression _parseAnd() {
|
||||||
|
@ -363,7 +382,7 @@ class _ExpressionParser {
|
||||||
|
|
||||||
if (operands.length == 1) return operands.single;
|
if (operands.length == 1) return operands.single;
|
||||||
|
|
||||||
return new LogicExpression(_Token.and, operands);
|
return LogicExpression(_Token.and, operands);
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression _parsePrimary() {
|
Expression _parsePrimary() {
|
||||||
|
@ -372,7 +391,7 @@ class _ExpressionParser {
|
||||||
if (_scanner.match(_Token.leftParen)) {
|
if (_scanner.match(_Token.leftParen)) {
|
||||||
var value = _parseOr();
|
var value = _parseOr();
|
||||||
if (!_scanner.match(_Token.rightParen)) {
|
if (!_scanner.match(_Token.rightParen)) {
|
||||||
throw new FormatException("Missing right parenthesis in expression");
|
throw FormatException("Missing right parenthesis in expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
@ -386,16 +405,16 @@ class _ExpressionParser {
|
||||||
// The only atomic booleans are of the form $variable == value or
|
// The only atomic booleans are of the form $variable == value or
|
||||||
// of the form $variable.
|
// of the form $variable.
|
||||||
if (!_scanner.match(_Token.dollar)) {
|
if (!_scanner.match(_Token.dollar)) {
|
||||||
throw new FormatException(
|
throw FormatException(
|
||||||
"Expected \$ in expression, got ${_scanner.current}");
|
"Expected \$ in expression, got ${_scanner.current}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_scanner.isIdentifier) {
|
if (!_scanner.isIdentifier) {
|
||||||
throw new FormatException(
|
throw FormatException(
|
||||||
"Expected identifier in expression, got ${_scanner.current}");
|
"Expected identifier in expression, got ${_scanner.current}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var left = new Variable(_scanner.current!);
|
var left = Variable(_scanner.current!);
|
||||||
_scanner.advance();
|
_scanner.advance();
|
||||||
|
|
||||||
if (!negate &&
|
if (!negate &&
|
||||||
|
@ -404,14 +423,14 @@ class _ExpressionParser {
|
||||||
var isNotEquals = _scanner.advance() == _Token.notEqual;
|
var isNotEquals = _scanner.advance() == _Token.notEqual;
|
||||||
|
|
||||||
if (!_scanner.isIdentifier) {
|
if (!_scanner.isIdentifier) {
|
||||||
throw new FormatException(
|
throw FormatException(
|
||||||
"Expected value in expression, got ${_scanner.current}");
|
"Expected value in expression, got ${_scanner.current}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var right = _scanner.advance()!;
|
var right = _scanner.advance()!;
|
||||||
return new ComparisonExpression(left, right, isNotEquals);
|
return ComparisonExpression(left, right, isNotEquals);
|
||||||
} else {
|
} 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.
|
/// An iterator that allows peeking at the current token.
|
||||||
class _Scanner {
|
class _Scanner {
|
||||||
/// Tokens are "(", ")", "$", "&&", "||", "!", ==", "!=", and (maximal) \w+.
|
/// Tokens are "(", ")", "$", "&&", "||", "!", ==", "!=", and (maximal) \w+.
|
||||||
static final _testPattern = new RegExp(r"^(?:[()$\w\s]|&&|\|\||==|!=?)+$");
|
static final _testPattern = RegExp(r"^(?:[()$\w\s]|&&|\|\||==|!=?)+$");
|
||||||
static final _tokenPattern = new RegExp(r"[()$]|&&|\|\||==|!=?|\w+");
|
static final _tokenPattern = RegExp(r"[()$]|&&|\|\||==|!=?|\w+");
|
||||||
|
|
||||||
/// Pattern that recognizes identifier tokens.
|
/// Pattern that recognizes identifier tokens.
|
||||||
///
|
///
|
||||||
/// Only checks the first character, since no non-identifier token can start
|
/// Only checks the first character, since no non-identifier token can start
|
||||||
/// with a word character.
|
/// with a word character.
|
||||||
static final _identifierPattern = new RegExp(r"^\w");
|
static final _identifierPattern = RegExp(r"^\w");
|
||||||
|
|
||||||
/// The token strings being iterated.
|
/// The token strings being iterated.
|
||||||
final Iterator<String> tokenIterator;
|
final Iterator<String> tokenIterator;
|
||||||
|
@ -439,7 +458,7 @@ class _Scanner {
|
||||||
|
|
||||||
static List<String> tokenize(String expression) {
|
static List<String> tokenize(String expression) {
|
||||||
if (!_testPattern.hasMatch(expression)) {
|
if (!_testPattern.hasMatch(expression)) {
|
||||||
throw new FormatException("Syntax error in '$expression'");
|
throw FormatException("Syntax error in '$expression'");
|
||||||
}
|
}
|
||||||
|
|
||||||
return _tokenPattern
|
return _tokenPattern
|
||||||
|
|
|
@ -13,19 +13,19 @@ import 'src/expression.dart';
|
||||||
/// Matches the header that begins a new section, like:
|
/// Matches the header that begins a new section, like:
|
||||||
///
|
///
|
||||||
/// [ $compiler == dart2js && $minified ]
|
/// [ $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,
|
/// Matches an entry that defines the status for a path in the current section,
|
||||||
/// like:
|
/// like:
|
||||||
///
|
///
|
||||||
/// some/path/to/some_test: Pass || Fail
|
/// some/path/to/some_test: Pass || Fail
|
||||||
final _entryPattern = new RegExp(r"^([^:#]+):(.*)");
|
final _entryPattern = RegExp(r"^([^:#]+):(.*)");
|
||||||
|
|
||||||
/// Matches an issue number in a comment, like:
|
/// Matches an issue number in a comment, like:
|
||||||
///
|
///
|
||||||
/// blah_test: Fail # Issue 1234
|
/// 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
|
/// A parsed status file, which describes how a collection of tests are
|
||||||
/// expected to behave under various configurations and conditions.
|
/// expected to behave under various configurations and conditions.
|
||||||
|
@ -55,14 +55,14 @@ class StatusFile {
|
||||||
///
|
///
|
||||||
/// Throws a [SyntaxError] if the file could not be parsed.
|
/// Throws a [SyntaxError] if the file could not be parsed.
|
||||||
StatusFile.read(this.path) {
|
StatusFile.read(this.path) {
|
||||||
var lines = new File(path).readAsLinesSync();
|
var lines = File(path).readAsLinesSync();
|
||||||
_comments.length = lines.length + 1;
|
_comments.length = lines.length + 1;
|
||||||
|
|
||||||
for (var line in lines) {
|
for (var line in lines) {
|
||||||
_lineCount++;
|
_lineCount++;
|
||||||
|
|
||||||
fail(String message, [List<String>? errors]) {
|
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.
|
// Strip off the comment and whitespace.
|
||||||
|
@ -84,7 +84,7 @@ class StatusFile {
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
try {
|
try {
|
||||||
var condition = Expression.parse(match[1]!.trim());
|
var condition = Expression.parse(match[1]!.trim());
|
||||||
sections.add(new StatusSection(condition, _lineCount));
|
sections.add(StatusSection(condition, _lineCount));
|
||||||
} on FormatException {
|
} on FormatException {
|
||||||
fail("Status expression syntax error");
|
fail("Status expression syntax error");
|
||||||
}
|
}
|
||||||
|
@ -111,11 +111,11 @@ class StatusFile {
|
||||||
// If we haven't found a section header yet, create an implicit section
|
// If we haven't found a section header yet, create an implicit section
|
||||||
// that matches everything.
|
// that matches everything.
|
||||||
if (sections.isEmpty) {
|
if (sections.isEmpty) {
|
||||||
sections.add(new StatusSection(Expression.always, -1));
|
sections.add(StatusSection(Expression.always, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
sections.last.entries
|
sections.last.entries
|
||||||
.add(new StatusEntry(path, _lineCount, expectations, issue));
|
.add(StatusEntry(path, _lineCount, expectations, issue));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ class StatusFile {
|
||||||
|
|
||||||
if (errors.isNotEmpty) {
|
if (errors.isNotEmpty) {
|
||||||
var s = errors.length > 1 ? "s" : "";
|
var s = errors.length > 1 ? "s" : "";
|
||||||
throw new SyntaxError(_shortPath, section.lineNumber,
|
throw SyntaxError(_shortPath, section.lineNumber,
|
||||||
"[ ${section.condition} ]", 'Validation error$s', errors);
|
"[ ${section.condition} ]", 'Validation error$s', errors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,8 +158,9 @@ class StatusFile {
|
||||||
return int.parse(match[1]!);
|
return int.parse(match[1]!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var buffer = new StringBuffer();
|
var buffer = StringBuffer();
|
||||||
for (var section in sections) {
|
for (var section in sections) {
|
||||||
buffer.writeln("[ ${section.condition} ]");
|
buffer.writeln("[ ${section.condition} ]");
|
||||||
|
|
||||||
|
@ -180,7 +181,7 @@ class StatusFile {
|
||||||
/// Unlike [toString()], this preserves comments and gives a "canonical"
|
/// Unlike [toString()], this preserves comments and gives a "canonical"
|
||||||
/// rendering of the status file that can be saved back to disc.
|
/// rendering of the status file that can be saved back to disc.
|
||||||
String serialize() {
|
String serialize() {
|
||||||
var buffer = new StringBuffer();
|
var buffer = StringBuffer();
|
||||||
|
|
||||||
var lastLine = 0;
|
var lastLine = 0;
|
||||||
var needBlankLine = false;
|
var needBlankLine = false;
|
||||||
|
@ -283,8 +284,9 @@ class SyntaxError implements Exception {
|
||||||
|
|
||||||
SyntaxError(this.file, this.lineNumber, this.line, this.message, this.errors);
|
SyntaxError(this.file, this.lineNumber, this.line, this.message, this.errors);
|
||||||
|
|
||||||
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var buffer = new StringBuffer();
|
var buffer = StringBuffer();
|
||||||
buffer.writeln('$message in "$file" line $lineNumber:');
|
buffer.writeln('$message in "$file" line $lineNumber:');
|
||||||
buffer.writeln(line);
|
buffer.writeln(line);
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ class LintingError {
|
||||||
final String message;
|
final String message;
|
||||||
LintingError(this.lineNumber, this.message);
|
LintingError(this.lineNumber, this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "Error at line $lineNumber: $message";
|
return "Error at line $lineNumber: $message";
|
||||||
}
|
}
|
||||||
|
@ -55,14 +56,14 @@ Iterable<LintingError> lintCommentLinesInSection(StatusSection section) {
|
||||||
for (var entry in section.entries) {
|
for (var entry in section.entries) {
|
||||||
seenStatusEntry = seenStatusEntry || entry is StatusEntry;
|
seenStatusEntry = seenStatusEntry || entry is StatusEntry;
|
||||||
if (seenStatusEntry && entry is CommentEntry) {
|
if (seenStatusEntry && entry is CommentEntry) {
|
||||||
lintingErrors.add(new LintingError(
|
lintingErrors.add(
|
||||||
entry.lineNumber, "Comment is on a line by itself."));
|
LintingError(entry.lineNumber, "Comment is on a line by itself."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lintingErrors;
|
return lintingErrors;
|
||||||
}
|
}
|
||||||
return section.entries.whereType<CommentEntry>().map((entry) =>
|
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.
|
/// Checks for disjunctions in headers. Disjunctions should be separated out.
|
||||||
|
@ -84,7 +85,7 @@ Iterable<LintingError> lintCommentLinesInSection(StatusSection section) {
|
||||||
Iterable<LintingError> lintDisjunctionsInHeader(StatusSection section) {
|
Iterable<LintingError> lintDisjunctionsInHeader(StatusSection section) {
|
||||||
if (section.condition.toString().contains("||")) {
|
if (section.condition.toString().contains("||")) {
|
||||||
return [
|
return [
|
||||||
new LintingError(
|
LintingError(
|
||||||
section.lineNumber,
|
section.lineNumber,
|
||||||
"Expression contains '||'. Please split the expression into multiple "
|
"Expression contains '||'. Please split the expression into multiple "
|
||||||
"separate sections.")
|
"separate sections.")
|
||||||
|
@ -104,7 +105,7 @@ Iterable<LintingError> lintAlphabeticalOrderingOfPaths(StatusSection section) {
|
||||||
var witness = _findNotEqualWitness<String>(sortedList, entries);
|
var witness = _findNotEqualWitness<String>(sortedList, entries);
|
||||||
if (witness != null) {
|
if (witness != null) {
|
||||||
return [
|
return [
|
||||||
new LintingError(
|
LintingError(
|
||||||
section.lineNumber,
|
section.lineNumber,
|
||||||
"Test paths are not alphabetically ordered in section. "
|
"Test paths are not alphabetically ordered in section. "
|
||||||
"${witness.first} should come before ${witness.second}.")
|
"${witness.first} should come before ${witness.second}.")
|
||||||
|
@ -119,7 +120,7 @@ Iterable<LintingError> lintNormalizedSection(StatusSection section) {
|
||||||
var normalized = section.condition.normalize().toString();
|
var normalized = section.condition.normalize().toString();
|
||||||
if (nonNormalized != normalized) {
|
if (nonNormalized != normalized) {
|
||||||
return [
|
return [
|
||||||
new LintingError(
|
LintingError(
|
||||||
section.lineNumber,
|
section.lineNumber,
|
||||||
"Condition expression should be '$normalized' "
|
"Condition expression should be '$normalized' "
|
||||||
"but was '$nonNormalized'.")
|
"but was '$nonNormalized'.")
|
||||||
|
@ -140,10 +141,10 @@ Iterable<LintingError> lintSectionEntryDuplicates(StatusSection section) {
|
||||||
if (entry.path == otherEntry.path &&
|
if (entry.path == otherEntry.path &&
|
||||||
_findNotEqualWitness(entry.expectations, otherEntry.expectations) ==
|
_findNotEqualWitness(entry.expectations, otherEntry.expectations) ==
|
||||||
null) {
|
null) {
|
||||||
errors.add(new LintingError(
|
errors.add(LintingError(
|
||||||
section.lineNumber,
|
section.lineNumber,
|
||||||
"The status entry "
|
"The status entry "
|
||||||
"'${entry}' is duplicated on lines "
|
"'$entry' is duplicated on lines "
|
||||||
"${entry.lineNumber} and ${otherEntry.lineNumber}."));
|
"${entry.lineNumber} and ${otherEntry.lineNumber}."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,7 +183,7 @@ Iterable<LintingError> lintSectionHeaderOrdering(List<StatusSection> sections) {
|
||||||
var witness = _findNotEqualWitness<StatusSection>(sorted, unsorted);
|
var witness = _findNotEqualWitness<StatusSection>(sorted, unsorted);
|
||||||
if (witness != null) {
|
if (witness != null) {
|
||||||
return [
|
return [
|
||||||
new LintingError(
|
LintingError(
|
||||||
witness.second!.lineNumber,
|
witness.second!.lineNumber,
|
||||||
"Section expressions are not correctly ordered in file. "
|
"Section expressions are not correctly ordered in file. "
|
||||||
"'${witness.first!.condition}' on line ${witness.first!.lineNumber} "
|
"'${witness.first!.condition}' on line ${witness.first!.lineNumber} "
|
||||||
|
@ -203,7 +204,7 @@ Iterable<LintingError> lintSectionHeaderDuplicates(
|
||||||
var section = sorted[i];
|
var section = sorted[i];
|
||||||
var previousSection = sorted[i - 1];
|
var previousSection = sorted[i - 1];
|
||||||
if (section.condition.compareTo(previousSection.condition) == 0) {
|
if (section.condition.compareTo(previousSection.condition) == 0) {
|
||||||
errors.add(new LintingError(
|
errors.add(LintingError(
|
||||||
section.lineNumber,
|
section.lineNumber,
|
||||||
"The condition "
|
"The condition "
|
||||||
"'${section.condition}' is duplicated on lines "
|
"'${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++) {
|
for (var i = 0; i < math.max(first.length, second.length); i++) {
|
||||||
if (i >= second.length) {
|
if (i >= second.length) {
|
||||||
return new ListNotEqualWitness(first[i], null);
|
return ListNotEqualWitness(first[i], null);
|
||||||
} else if (i >= first.length) {
|
} else if (i >= first.length) {
|
||||||
return new ListNotEqualWitness(null, second[i]);
|
return ListNotEqualWitness(null, second[i]);
|
||||||
} else if (first[i] != second[i]) {
|
} else if (first[i] != second[i]) {
|
||||||
return new ListNotEqualWitness(first[i], second[i]);
|
return ListNotEqualWitness(first[i], second[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -9,10 +9,10 @@ import 'dart:convert';
|
||||||
|
|
||||||
StatusFile normalizeStatusFile(StatusFile statusFile) {
|
StatusFile normalizeStatusFile(StatusFile statusFile) {
|
||||||
StatusFile newStatusFile = _sortSectionsAndCombine(statusFile);
|
StatusFile newStatusFile = _sortSectionsAndCombine(statusFile);
|
||||||
newStatusFile.sections.forEach((section) {
|
for (var section in newStatusFile.sections) {
|
||||||
_sortEntriesInSection(section);
|
_sortEntriesInSection(section);
|
||||||
_oneLineBetweenSections(section);
|
_oneLineBetweenSections(section);
|
||||||
});
|
}
|
||||||
// Remove empty line at the end of the file
|
// Remove empty line at the end of the file
|
||||||
newStatusFile.sections.last.entries.removeLast();
|
newStatusFile.sections.last.entries.removeLast();
|
||||||
return newStatusFile;
|
return newStatusFile;
|
||||||
|
@ -44,32 +44,32 @@ void _sortEntriesInSection(StatusSection section) {
|
||||||
void _oneLineBetweenSections(StatusSection section) {
|
void _oneLineBetweenSections(StatusSection section) {
|
||||||
section.entries.removeWhere((entry) => entry is EmptyEntry);
|
section.entries.removeWhere((entry) => entry is EmptyEntry);
|
||||||
section.entries
|
section.entries
|
||||||
.add(new EmptyEntry(section.lineNumber + section.entries.length + 1));
|
.add(EmptyEntry(section.lineNumber + section.entries.length + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusFile _sortSectionsAndCombine(StatusFile statusFile) {
|
StatusFile _sortSectionsAndCombine(StatusFile statusFile) {
|
||||||
// Create the new status file to be returned.
|
// Create the new status file to be returned.
|
||||||
StatusFile oldStatusFile = new StatusFile.parse(
|
StatusFile oldStatusFile = StatusFile.parse(
|
||||||
statusFile.path, LineSplitter.split(statusFile.toString()).toList());
|
statusFile.path, LineSplitter.split(statusFile.toString()).toList());
|
||||||
List<StatusSection> newSections = [];
|
List<StatusSection> newSections = [];
|
||||||
// Copy over all sections and normalize all the expressions.
|
// 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.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)
|
section.lineNumber, section.sectionHeaderComments)
|
||||||
..entries.addAll(section.entries));
|
..entries.addAll(section.entries));
|
||||||
} else {
|
} else {
|
||||||
newSections.add(section);
|
newSections.add(section);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Sort the headers
|
// Sort the headers
|
||||||
newSections.sort((a, b) => a.condition.compareTo(b.condition));
|
newSections.sort((a, b) => a.condition.compareTo(b.condition));
|
||||||
|
|
||||||
// See if we can combine section headers by simple comparison.
|
// 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]);
|
newStatusFile.sections.add(newSections[0]);
|
||||||
for (var i = 1; i < newSections.length; i++) {
|
for (var i = 1; i < newSections.length; i++) {
|
||||||
var previousSection = newSections[i - 1];
|
var previousSection = newSections[i - 1];
|
||||||
|
|
|
@ -13,3 +13,4 @@ dependencies:
|
||||||
# Use 'any' constraints here; we get our versions from the DEPS file.
|
# Use 'any' constraints here; we get our versions from the DEPS file.
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
expect: any
|
expect: any
|
||||||
|
lints: any
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// 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.
|
// 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:expect/expect.dart';
|
||||||
import 'package:status_file/canonical_status_file.dart';
|
import 'package:status_file/canonical_status_file.dart';
|
||||||
import 'package:status_file/status_file_linter.dart';
|
import 'package:status_file/status_file_linter.dart';
|
||||||
|
@ -36,7 +38,7 @@ void main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusFile createFromString(String text) {
|
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}) {
|
expectError(String text, String expectedError, {bool disjunctions = false}) {
|
||||||
|
|
|
@ -24,33 +24,33 @@ main() {
|
||||||
void normalizeCheck() {
|
void normalizeCheck() {
|
||||||
var files = getStatusFiles();
|
var files = getStatusFiles();
|
||||||
for (var file in files) {
|
for (var file in files) {
|
||||||
print("------- " + file.path + " -------");
|
print("------- ${file.path} -------");
|
||||||
var statusFile = new StatusFile.read(file.path);
|
var statusFile = StatusFile.read(file.path);
|
||||||
var statusFileOther = normalizeStatusFile(new StatusFile.read(file.path));
|
var statusFileOther = normalizeStatusFile(StatusFile.read(file.path));
|
||||||
checkSemanticallyEqual(statusFile, statusFileOther,
|
checkSemanticallyEqual(statusFile, statusFileOther,
|
||||||
warnOnDuplicateHeader: true);
|
warnOnDuplicateHeader: true);
|
||||||
checkFileHeaderIntact(statusFile, statusFileOther);
|
checkFileHeaderIntact(statusFile, statusFileOther);
|
||||||
print("------- " + file.path + " -------");
|
print("------- ${file.path} -------");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sanityCheck() {
|
void sanityCheck() {
|
||||||
var files = getStatusFiles();
|
var files = getStatusFiles();
|
||||||
for (var file in files) {
|
for (var file in files) {
|
||||||
print("------- " + file.path + " -------");
|
print("------- ${file.path} -------");
|
||||||
var statusFile = new StatusFile.read(file.path);
|
var statusFile = StatusFile.read(file.path);
|
||||||
var statusFileOther = new StatusFile.read(file.path);
|
var statusFileOther = StatusFile.read(file.path);
|
||||||
checkSemanticallyEqual(statusFile, statusFileOther,
|
checkSemanticallyEqual(statusFile, statusFileOther,
|
||||||
warnOnDuplicateHeader: true);
|
warnOnDuplicateHeader: true);
|
||||||
checkFileHeaderIntact(statusFile, statusFileOther);
|
checkFileHeaderIntact(statusFile, statusFileOther);
|
||||||
print("------- " + file.path + " -------");
|
print("------- ${file.path} -------");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<FileSystemEntity> getStatusFiles() {
|
List<FileSystemEntity> getStatusFiles() {
|
||||||
var statusFiles = <FileSystemEntity>[];
|
var statusFiles = <FileSystemEntity>[];
|
||||||
for (var entry
|
for (var entry
|
||||||
in new Directory.fromUri(statusFilePath).listSync(recursive: true)) {
|
in Directory.fromUri(statusFilePath).listSync(recursive: true)) {
|
||||||
statusFiles.add(entry);
|
statusFiles.add(entry);
|
||||||
}
|
}
|
||||||
return statusFiles;
|
return statusFiles;
|
||||||
|
@ -64,7 +64,7 @@ void checkSemanticallyEqual(StatusFile original, StatusFile normalized,
|
||||||
print(original);
|
print(original);
|
||||||
print("==================");
|
print("==================");
|
||||||
print(normalized);
|
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 "
|
"$entriesInOriginal and the count of entries in normalized is "
|
||||||
"$entriesInNormalized. Those two numbers are not the same.");
|
"$entriesInNormalized. Those two numbers are not the same.");
|
||||||
}
|
}
|
||||||
|
@ -77,8 +77,7 @@ void checkSemanticallyEqual(StatusFile original, StatusFile normalized,
|
||||||
|
|
||||||
int countEntries(StatusFile statusFile) {
|
int countEntries(StatusFile statusFile) {
|
||||||
return statusFile.sections
|
return statusFile.sections
|
||||||
.map((section) =>
|
.map((section) => section.entries.whereType<StatusEntry>().length)
|
||||||
section.entries.where((entry) => entry is StatusEntry).length)
|
|
||||||
.fold(0, (count, sum) => count + sum);
|
.fold(0, (count, sum) => count + sum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,29 +95,29 @@ void findInStatusFile(
|
||||||
entry.path.compareTo(entryToFind.path) == 0 &&
|
entry.path.compareTo(entryToFind.path) == 0 &&
|
||||||
listEqual(entry.expectations, entryToFind.expectations))
|
listEqual(entry.expectations, entryToFind.expectations))
|
||||||
.toList();
|
.toList();
|
||||||
if (matchingEntries.length == 0) {
|
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 "
|
"header matched on line number ${section.lineNumber}. Sections "
|
||||||
"should be unique.";
|
"should be unique.";
|
||||||
if (warnOnDuplicateHeader) {
|
if (warnOnDuplicateHeader) {
|
||||||
print(message);
|
print(message);
|
||||||
} else {
|
} else {
|
||||||
throw new Exception(message);
|
throw Exception(message);
|
||||||
}
|
}
|
||||||
} else if (matchingEntries.length == 1 && foundEntryPosition >= 0) {
|
} 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 "
|
"${entryToFind.lineNumber} in section ${section.condition} was "
|
||||||
"already found in a previous section on line $foundEntryPosition.");
|
"already found in a previous section on line $foundEntryPosition.");
|
||||||
} else if (matchingEntries.length == 1) {
|
} else if (matchingEntries.length == 1) {
|
||||||
foundEntryPosition = matchingEntries[0].lineNumber;
|
foundEntryPosition = matchingEntries[0].lineNumber;
|
||||||
} else {
|
} else {
|
||||||
throw new Exception("The entry '$entryToFind' on line "
|
throw Exception("The entry '$entryToFind' on line "
|
||||||
"${entryToFind.lineNumber} in section ${section.condition} on line "
|
"${entryToFind.lineNumber} in section ${section.condition} on line "
|
||||||
"${section.lineNumber} had multiple matches in section.");
|
"${section.lineNumber} had multiple matches in section.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (foundEntryPosition < 0) {
|
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.");
|
"condition $condition in the status file.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +127,7 @@ void checkFileHeaderIntact(StatusFile original, StatusFile normalized) {
|
||||||
var normalizedHeader =
|
var normalizedHeader =
|
||||||
normalized.sections.first.sectionHeaderComments.toString();
|
normalized.sections.first.sectionHeaderComments.toString();
|
||||||
if (originalHeader != normalizedHeader) {
|
if (originalHeader != normalizedHeader) {
|
||||||
throw new Exception(
|
throw Exception(
|
||||||
"File headers changed.\nExpected:\n$originalHeader\n\nActual:\n$normalizedHeader");
|
"File headers changed.\nExpected:\n$originalHeader\n\nActual:\n$normalizedHeader");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,16 +12,16 @@ final Uri repoRoot = Platform.script.resolve("../../../");
|
||||||
void main() {
|
void main() {
|
||||||
// Parse every status file in the repository.
|
// Parse every status file in the repository.
|
||||||
for (var directory in ["tests", "runtime/tests"]) {
|
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)) {
|
.listSync(recursive: true)) {
|
||||||
if (!entry.path.endsWith(".status")) continue;
|
if (!entry.path.endsWith(".status")) continue;
|
||||||
try {
|
try {
|
||||||
var statusFile = new StatusFile.read(entry.path);
|
var statusFile = StatusFile.read(entry.path);
|
||||||
statusFile.toString();
|
statusFile.toString();
|
||||||
} catch (err, st) {
|
} catch (err, st) {
|
||||||
print(err);
|
print(err);
|
||||||
print(st);
|
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() {
|
void main() {
|
||||||
// Parse every status file in the repository.
|
// Parse every status file in the repository.
|
||||||
for (var directory in ["tests", "runtime/tests"]) {
|
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)) {
|
.listSync(recursive: true)) {
|
||||||
if (!entry.path.endsWith(".status")) continue;
|
if (!entry.path.endsWith(".status")) continue;
|
||||||
try {
|
try {
|
||||||
new StatusFile.read(entry.path);
|
StatusFile.read(entry.path);
|
||||||
} catch (err, stack) {
|
} catch (err, stack) {
|
||||||
Expect.fail("Could not parse '${entry.path}'.\n$err\n$stack");
|
Expect.fail("Could not parse '${entry.path}'.\n$err\n$stack");
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,13 @@ class TestEnvironment implements Environment {
|
||||||
|
|
||||||
TestEnvironment(this._values);
|
TestEnvironment(this._values);
|
||||||
|
|
||||||
|
@override
|
||||||
void validate(String name, String value, List<String> errors) {
|
void validate(String name, String value, List<String> errors) {
|
||||||
throw new UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Looks up the value of the variable with [name].
|
/// Looks up the value of the variable with [name].
|
||||||
|
@override
|
||||||
String? lookUp(String name) => _values[name];
|
String? lookUp(String name) => _values[name];
|
||||||
|
|
||||||
operator []=(String key, String value) => _values[key] = value;
|
operator []=(String key, String value) => _values[key] = value;
|
||||||
|
@ -39,7 +41,7 @@ void testExpression() {
|
||||||
expression.toString());
|
expression.toString());
|
||||||
|
|
||||||
// Test BooleanExpression.evaluate().
|
// Test BooleanExpression.evaluate().
|
||||||
var environment = new TestEnvironment({"arch": "dartc", "mode": "debug"});
|
var environment = TestEnvironment({"arch": "dartc", "mode": "debug"});
|
||||||
|
|
||||||
Expect.isTrue(expression.evaluate(environment));
|
Expect.isTrue(expression.evaluate(environment));
|
||||||
environment["mode"] = "release";
|
environment["mode"] = "release";
|
||||||
|
@ -67,7 +69,7 @@ void testBoolean() {
|
||||||
|
|
||||||
// Test BooleanExpression.evaluate().
|
// Test BooleanExpression.evaluate().
|
||||||
var environment =
|
var environment =
|
||||||
new TestEnvironment({"arch": "ia32", "checked": "true", "mode": "debug"});
|
TestEnvironment({"arch": "ia32", "checked": "true", "mode": "debug"});
|
||||||
|
|
||||||
Expect.isTrue(expression.evaluate(environment));
|
Expect.isTrue(expression.evaluate(environment));
|
||||||
environment["mode"] = "release";
|
environment["mode"] = "release";
|
||||||
|
@ -88,8 +90,8 @@ void testNotBoolean() {
|
||||||
Expect.equals(
|
Expect.equals(
|
||||||
r"$arch == ia32 && !$checked || $mode == release", expression.toString());
|
r"$arch == ia32 && !$checked || $mode == release", expression.toString());
|
||||||
|
|
||||||
var environment = new TestEnvironment(
|
var environment =
|
||||||
{"arch": "ia32", "checked": "false", "mode": "debug"});
|
TestEnvironment({"arch": "ia32", "checked": "false", "mode": "debug"});
|
||||||
|
|
||||||
Expect.isTrue(expression.evaluate(environment));
|
Expect.isTrue(expression.evaluate(environment));
|
||||||
environment["mode"] = "release";
|
environment["mode"] = "release";
|
||||||
|
@ -111,7 +113,7 @@ void testNotEqual() {
|
||||||
r"$compiler == dart2js && $runtime != ie9", expression.toString());
|
r"$compiler == dart2js && $runtime != ie9", expression.toString());
|
||||||
|
|
||||||
// Test BooleanExpression.evaluate().
|
// Test BooleanExpression.evaluate().
|
||||||
var environment = new TestEnvironment({
|
var environment = TestEnvironment({
|
||||||
"compiler": "none",
|
"compiler": "none",
|
||||||
"runtime": "ie9",
|
"runtime": "ie9",
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue