Chromium Code Reviews| Index: tools/migration/lib/src/status_file.dart |
| diff --git a/tools/migration/lib/src/status_file.dart b/tools/migration/lib/src/status_file.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e082b4e3015b472047c8b3ced32d8295f53ef649 |
| --- /dev/null |
| +++ b/tools/migration/lib/src/status_file.dart |
| @@ -0,0 +1,154 @@ |
| +// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| +// 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. |
| + |
| +import 'dart:io'; |
| + |
| +import 'path.dart'; |
| + |
| +/// Matches the header that begins a new section, like: |
|
jcollins
2017/07/24 18:24:51
There should probably be a TODO here to merge this
bkonyi
2017/07/24 19:16:04
Do you mean with tools/testing/dart/status_file.da
|
| +/// |
| +/// [ $compiler == dart2js && $minified ] |
| +final _sectionPattern = new 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"^([^:#]+):(.*)"); |
| + |
| +/// Matches an issue number in a comment, like: |
| +/// |
| +/// blah_test: Fail # Issue 1234 |
| +/// ^^^^ |
| +final _issuePattern = new 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. |
| +/// |
| +/// Each status file is made of a series of sections. Each section begins with |
| +/// a header, followed by a series of entries. A header is enclosed in square |
| +/// brackets and contains a Boolean expression. That expression is evaluated in |
| +/// an environment. If it evaluates to true, then the entries after the header |
| +/// take effect. |
| +/// |
| +/// Each entry is a glob-like file path followed by a colon and then a |
| +/// comma-separated list of expectations. The path may point to an individual |
| +/// file, or a directory, in which case it applies to all files under that path. |
| +/// |
| +/// Entries may also appear before any section header, in which case they |
| +/// always apply. |
| +class StatusFile { |
| + final String path; |
| + final List<StatusSection> sections = []; |
| + |
| + bool get isEmpty => sections.isEmpty; |
| + |
| + StatusFile(this.path); |
| + |
| + /// Parses the status file at [_path]. |
| + StatusFile.read(this.path) { |
| + var lines = new File(path).readAsLinesSync(); |
| + |
| + // The current section whose rules are being parsed. |
| + StatusSection section; |
| + |
| + var lineNumber = 0; |
| + |
| + for (var line in lines) { |
| + lineNumber++; |
| + |
| + fail(String message, [List<String> errors]) { |
| + print('$message in "$_shortPath" line $lineNumber:\n$line'); |
| + |
| + if (errors != null) { |
| + for (var error in errors) { |
| + print("- ${error.replaceAll('\n', '\n ')}"); |
| + } |
| + } |
| + exit(1); |
| + } |
| + |
| + // Strip off the comment and whitespace. |
| + var source = line; |
| + var comment = ""; |
| + var hashIndex = line.indexOf('#'); |
| + if (hashIndex >= 0) { |
| + source = line.substring(0, hashIndex); |
| + comment = line.substring(hashIndex); |
| + } |
| + source = source.trim(); |
| + // Ignore empty (or comment-only) lines. |
| + if (source.isEmpty) continue; |
| + |
| + // See if we are starting a new section. |
| + var match = _sectionPattern.firstMatch(source); |
| + if (match != null) { |
| + var condition = match[1].trim(); |
| + section = new StatusSection(condition); |
| + sections.add(section); |
| + continue; |
| + } |
| + |
| + // Otherwise, it should be a new entry under the current section. |
| + match = _entryPattern.firstMatch(source); |
| + if (match != null) { |
| + // If we haven't found a section header yet, create an implicit section |
| + // that matches everything. |
| + if (section == null) { |
| + section = new StatusSection(null); |
| + sections.add(section); |
| + } |
| + if (!comment.isEmpty) { |
| + source += " $comment"; |
| + } |
| + section.entries.add(source); |
| + continue; |
| + } |
| + |
| + fail("Unrecognized input"); |
| + } |
| + } |
| + |
| + /// Gets the path to this status file relative to the Dart repo root. |
| + String get _shortPath { |
| + var repoRoot = new Path(Platform.script |
| + .toFilePath(windows: Platform.operatingSystem == "windows")) |
| + .join(new Path("../../../../")); |
| + return new Path(_path).relativeTo(repoRoot).toString(); |
| + } |
| + |
| + String toString() { |
| + var buffer = new StringBuffer(); |
| + for (var section in sections) { |
| + buffer.writeln("[${section.condition}]"); |
| + for (var entry in section.entries) { |
| + buffer.writeln(entry.replaceAll("\n", " ")); |
| + } |
| + buffer.writeln(); |
| + } |
| + return buffer.toString(); |
| + } |
| +} |
| + |
| +/// One section in a status file. |
| +/// |
| +/// Contains the condition from the header that begins the section, then all of |
| +/// the entries within the section. |
| +class StatusSection { |
| + /// In that case, the section always applies. |
| + final String condition; |
| + final List<String> entries = []; |
| + |
| + StatusSection(this.condition); |
| + |
| + String toString() { |
| + var buffer = new StringBuffer(); |
| + buffer.writeln("[$condition]"); |
| + for (var entry in entries) { |
| + buffer.writeln(entry); |
| + } |
| + buffer.writeln(); |
| + return buffer.toString(); |
| + } |
| +} |