| OLD | NEW |
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import 'dart:io'; | 5 import 'dart:io'; |
| 6 | 6 |
| 7 import 'environment.dart'; | |
| 8 import 'expectation.dart'; | 7 import 'expectation.dart'; |
| 9 import 'path.dart'; | |
| 10 import 'status_expression.dart'; | 8 import 'status_expression.dart'; |
| 11 | 9 |
| 12 /// Splits out a trailing line comment | 10 /// Splits out a trailing line comment |
| 13 final _commentPattern = new RegExp("^([^#]*)(#.*)?\$"); | 11 final _commentPattern = new RegExp("^([^#]*)(#.*)?\$"); |
| 14 | 12 |
| 15 /// Matches the header that begins a new section, like: | 13 /// Matches the header that begins a new section, like: |
| 16 /// | 14 /// |
| 17 /// [ $compiler == dart2js && $minified ] | 15 /// [ $compiler == dart2js && $minified ] |
| 18 final _sectionPattern = new RegExp(r"^\[([^\]]+)\]"); | 16 final _sectionPattern = new RegExp(r"^\[([^\]]+)\]"); |
| 19 | 17 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 38 /// an environment. If it evaluates to true, then the entries after the header | 36 /// an environment. If it evaluates to true, then the entries after the header |
| 39 /// take effect. | 37 /// take effect. |
| 40 /// | 38 /// |
| 41 /// Each entry is a glob-like file path followed by a colon and then a | 39 /// Each entry is a glob-like file path followed by a colon and then a |
| 42 /// comma-separated list of [Expectation]s. The path may point to an individual | 40 /// comma-separated list of [Expectation]s. The path may point to an individual |
| 43 /// file, or a directory, in which case it applies to all files under that path. | 41 /// file, or a directory, in which case it applies to all files under that path. |
| 44 /// | 42 /// |
| 45 /// Entries may also appear before any section header, in which case they | 43 /// Entries may also appear before any section header, in which case they |
| 46 /// always apply. | 44 /// always apply. |
| 47 class StatusFile { | 45 class StatusFile { |
| 48 final String _path; | |
| 49 final List<StatusSection> sections = []; | 46 final List<StatusSection> sections = []; |
| 50 | 47 |
| 51 /// Parses the status file at [_path]. | 48 /// Parses the status file at [path]. |
| 52 StatusFile.read(this._path) { | 49 StatusFile.read(String path) { |
| 53 var lines = new File(_path).readAsLinesSync(); | 50 var lines = new File(path).readAsLinesSync(); |
| 54 | 51 |
| 55 // The current section whose rules are being parsed. | 52 // The current section whose rules are being parsed. |
| 56 StatusSection section; | 53 StatusSection section; |
| 57 | 54 |
| 58 var lineNumber = 0; | 55 var lineNumber = 0; |
| 59 | |
| 60 for (var line in lines) { | 56 for (var line in lines) { |
| 61 lineNumber++; | 57 lineNumber++; |
| 62 | 58 |
| 63 fail(String message, [List<String> errors]) { | |
| 64 print('$message in "$_shortPath" line $lineNumber:\n$line'); | |
| 65 | |
| 66 if (errors != null) { | |
| 67 for (var error in errors) { | |
| 68 print("- ${error.replaceAll('\n', '\n ')}"); | |
| 69 } | |
| 70 } | |
| 71 exit(1); | |
| 72 } | |
| 73 | |
| 74 // Strip off the comment and whitespace. | 59 // Strip off the comment and whitespace. |
| 75 var match = _commentPattern.firstMatch(line); | 60 var match = _commentPattern.firstMatch(line); |
| 76 var source = ""; | 61 var source = ""; |
| 77 var comment = ""; | 62 var comment = ""; |
| 78 if (match != null) { | 63 if (match != null) { |
| 79 source = match[1].trim(); | 64 source = match[1].trim(); |
| 80 comment = match[2] ?? ""; | 65 comment = match[2] ?? ""; |
| 81 } | 66 } |
| 82 | 67 |
| 83 // Ignore empty (or comment-only) lines. | 68 // Ignore empty (or comment-only) lines. |
| 84 if (source.isEmpty) continue; | 69 if (source.isEmpty) continue; |
| 85 | 70 |
| 86 // See if we are starting a new section. | 71 // See if we are starting a new section. |
| 87 match = _sectionPattern.firstMatch(source); | 72 match = _sectionPattern.firstMatch(source); |
| 88 if (match != null) { | 73 if (match != null) { |
| 89 try { | 74 var condition = Expression.parse(match[1].trim()); |
| 90 var condition = Expression.parse(match[1].trim()); | 75 section = new StatusSection(condition); |
| 91 | 76 sections.add(section); |
| 92 var errors = <String>[]; | |
| 93 condition.validate(errors); | |
| 94 | |
| 95 if (errors.isNotEmpty) { | |
| 96 var s = errors.length > 1 ? "s" : ""; | |
| 97 fail('Validation error$s', errors); | |
| 98 } | |
| 99 | |
| 100 section = new StatusSection(condition); | |
| 101 sections.add(section); | |
| 102 } on FormatException { | |
| 103 fail("Status expression syntax error"); | |
| 104 } | |
| 105 continue; | 77 continue; |
| 106 } | 78 } |
| 107 | 79 |
| 108 // Otherwise, it should be a new entry under the current section. | 80 // Otherwise, it should be a new entry under the current section. |
| 109 match = _entryPattern.firstMatch(source); | 81 match = _entryPattern.firstMatch(source); |
| 110 if (match != null) { | 82 if (match != null) { |
| 111 var path = match[1].trim(); | 83 var path = match[1].trim(); |
| 112 // TODO(whesse): Handle test names ending in a wildcard (*). | 84 // TODO(whesse): Handle test names ending in a wildcard (*). |
| 113 var expectations = <Expectation>[]; | 85 var expectations = _parseExpectations(match[2]); |
| 114 for (var name in match[2].split(",")) { | |
| 115 name = name.trim(); | |
| 116 try { | |
| 117 expectations.add(Expectation.find(name)); | |
| 118 } on ArgumentError { | |
| 119 fail('Unrecognized test expectation "$name"'); | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 var issue = _issueNumber(comment); | 86 var issue = _issueNumber(comment); |
| 124 | 87 |
| 125 // If we haven't found a section header yet, create an implicit section | 88 // If we haven't found a section header yet, create an implicit section |
| 126 // that matches everything. | 89 // that matches everything. |
| 127 if (section == null) { | 90 if (section == null) { |
| 128 section = new StatusSection(null); | 91 section = new StatusSection(null); |
| 129 sections.add(section); | 92 sections.add(section); |
| 130 } | 93 } |
| 131 | 94 |
| 132 section.entries.add(new StatusEntry(path, expectations, issue)); | 95 section.entries.add(new StatusEntry(path, expectations, issue)); |
| 133 continue; | 96 continue; |
| 134 } | 97 } |
| 135 | 98 |
| 136 fail("Unrecognized input"); | 99 throw new FormatException( |
| 100 "Could not parse line $lineNumber of status file '$path':\n$line"); |
| 137 } | 101 } |
| 138 } | 102 } |
| 139 | 103 |
| 140 /// Gets the path to this status file relative to the Dart repo root. | 104 /// Parses a comma-separated list of expectation names from [text]. |
| 141 String get _shortPath { | 105 List<Expectation> _parseExpectations(String text) { |
| 142 var repoRoot = new Path(Platform.script | 106 return text |
| 143 .toFilePath(windows: Platform.operatingSystem == "windows")) | 107 .split(",") |
| 144 .join(new Path("../../../../")); | 108 .map((name) => Expectation.find(name.trim())) |
| 145 return new Path(_path).relativeTo(repoRoot).toString(); | 109 .toList(); |
| 146 } | 110 } |
| 147 | 111 |
| 148 /// Returns the issue number embedded in [comment] or `null` if there is none. | 112 /// Returns the issue number embedded in [comment] or `null` if there is none. |
| 149 int _issueNumber(String comment) { | 113 int _issueNumber(String comment) { |
| 150 var match = _issuePattern.firstMatch(comment); | 114 var match = _issuePattern.firstMatch(comment); |
| 151 if (match == null) return null; | 115 if (match == null) return null; |
| 152 | 116 |
| 153 return int.parse(match[1], onError: (_) => null); | 117 return int.parse(match[1], onError: (_) => null); |
| 154 } | 118 } |
| 155 | 119 |
| (...skipping 21 matching lines...) Expand all Loading... |
| 177 /// the entries within the section. | 141 /// the entries within the section. |
| 178 class StatusSection { | 142 class StatusSection { |
| 179 /// The expression that determines when this section is applied. | 143 /// The expression that determines when this section is applied. |
| 180 /// | 144 /// |
| 181 /// May be `null` for paths that appear before any section header in the file. | 145 /// May be `null` for paths that appear before any section header in the file. |
| 182 /// In that case, the section always applies. | 146 /// In that case, the section always applies. |
| 183 final Expression _condition; | 147 final Expression _condition; |
| 184 final List<StatusEntry> entries = []; | 148 final List<StatusEntry> entries = []; |
| 185 | 149 |
| 186 /// Returns true if this section should apply in the given [environment]. | 150 /// Returns true if this section should apply in the given [environment]. |
| 187 bool isEnabled(Environment environment) => | 151 bool isEnabled(Map<String, dynamic> environment) => |
| 188 _condition == null || _condition.evaluate(environment); | 152 _condition == null || _condition.evaluate(environment); |
| 189 | 153 |
| 190 StatusSection(this._condition); | 154 StatusSection(this._condition); |
| 191 } | 155 } |
| 192 | 156 |
| 193 /// Describes the test status of the file or files at a given path. | 157 /// Describes the test status of the file or files at a given path. |
| 194 class StatusEntry { | 158 class StatusEntry { |
| 195 final String path; | 159 final String path; |
| 196 final List<Expectation> expectations; | 160 final List<Expectation> expectations; |
| 197 final int issue; | 161 final int issue; |
| 198 | 162 |
| 199 StatusEntry(this.path, this.expectations, this.issue); | 163 StatusEntry(this.path, this.expectations, this.issue); |
| 200 } | 164 } |
| OLD | NEW |