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