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 |