Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(389)

Side by Side Diff: lib/src/runner/configuration/load.dart

Issue 1829483002: Add support for a global configuration file. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Code review changes Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/runner/configuration.dart ('k') | test/runner/configuration/global_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, 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 'package:boolean_selector/boolean_selector.dart'; 7 import 'package:boolean_selector/boolean_selector.dart';
8 import 'package:glob/glob.dart'; 8 import 'package:glob/glob.dart';
9 import 'package:path/path.dart' as p; 9 import 'package:path/path.dart' as p;
10 import 'package:source_span/source_span.dart'; 10 import 'package:source_span/source_span.dart';
11 import 'package:yaml/yaml.dart'; 11 import 'package:yaml/yaml.dart';
12 12
13 import '../../backend/operating_system.dart'; 13 import '../../backend/operating_system.dart';
14 import '../../backend/platform_selector.dart'; 14 import '../../backend/platform_selector.dart';
15 import '../../backend/test_platform.dart'; 15 import '../../backend/test_platform.dart';
16 import '../../frontend/timeout.dart'; 16 import '../../frontend/timeout.dart';
17 import '../../utils.dart'; 17 import '../../utils.dart';
18 import '../../util/io.dart'; 18 import '../../util/io.dart';
19 import '../configuration.dart'; 19 import '../configuration.dart';
20 import 'values.dart'; 20 import 'values.dart';
21 21
22 /// Loads configuration information from a YAML file at [path]. 22 /// Loads configuration information from a YAML file at [path].
23 /// 23 ///
24 /// If [global] is `true`, this restricts the configuration file to only rules
25 /// that are supported globally.
26 ///
24 /// Throws a [FormatException] if the configuration is invalid, and a 27 /// Throws a [FormatException] if the configuration is invalid, and a
25 /// [FileSystemException] if it can't be read. 28 /// [FileSystemException] if it can't be read.
26 Configuration load(String path) { 29 Configuration load(String path, {bool global: false}) {
27 var source = new File(path).readAsStringSync(); 30 var source = new File(path).readAsStringSync();
28 var document = loadYamlNode(source, sourceUrl: p.toUri(path)); 31 var document = loadYamlNode(source, sourceUrl: p.toUri(path));
29 32
30 if (document.value == null) return Configuration.empty; 33 if (document.value == null) return Configuration.empty;
31 34
32 if (document is! Map) { 35 if (document is! Map) {
33 throw new SourceSpanFormatException( 36 throw new SourceSpanFormatException(
34 "The configuration must be a YAML map.", document.span, source); 37 "The configuration must be a YAML map.", document.span, source);
35 } 38 }
36 39
37 var loader = new _ConfigurationLoader(document, source); 40 var loader = new _ConfigurationLoader(document, source, global: global);
38 return loader.load(); 41 return loader.load();
39 } 42 }
40 43
41 /// A helper for [load] that tracks the YAML document. 44 /// A helper for [load] that tracks the YAML document.
42 class _ConfigurationLoader { 45 class _ConfigurationLoader {
43 /// The parsed configuration document. 46 /// The parsed configuration document.
44 final YamlMap _document; 47 final YamlMap _document;
45 48
46 /// The source string for [_document]. 49 /// The source string for [_document].
47 /// 50 ///
48 /// Used for error reporting. 51 /// Used for error reporting.
49 final String _source; 52 final String _source;
50 53
54 /// Whether this is parsing the global configuration file.
55 final bool _global;
56
51 /// Whether runner configuration is allowed at this level. 57 /// Whether runner configuration is allowed at this level.
52 final bool _runnerConfig; 58 final bool _runnerConfig;
53 59
54 _ConfigurationLoader(this._document, this._source, {bool runnerConfig: true}) 60 _ConfigurationLoader(this._document, this._source, {bool global: false,
55 : _runnerConfig = runnerConfig; 61 bool runnerConfig: true})
62 : _global = global,
63 _runnerConfig = runnerConfig;
56 64
57 /// Loads the configuration in [_document]. 65 /// Loads the configuration in [_document].
58 Configuration load() => _loadTestConfig().merge(_loadRunnerConfig()); 66 Configuration load() => _loadGlobalTestConfig()
67 .merge(_loadLocalTestConfig())
68 .merge(_loadGlobalRunnerConfig())
69 .merge(_loadLocalRunnerConfig());
59 70
60 /// Loads test configuration (but not runner configuration). 71 /// Loads test configuration that's allowed in the global configuration file.
61 Configuration _loadTestConfig() { 72 Configuration _loadGlobalTestConfig() {
62 var verboseTrace = _getBool("verbose_trace"); 73 var verboseTrace = _getBool("verbose_trace");
63 var jsTrace = _getBool("js_trace"); 74 var jsTrace = _getBool("js_trace");
64 75
65 var skip = _getValue("skip", "boolean or string",
66 (value) => value is bool || value is String);
67 var skipReason;
68 if (skip is String) {
69 skipReason = skip;
70 skip = true;
71 }
72
73 var testOn = _parseValue("test_on",
74 (value) => new PlatformSelector.parse(value));
75
76 var timeout = _parseValue("timeout", (value) => new Timeout.parse(value)); 76 var timeout = _parseValue("timeout", (value) => new Timeout.parse(value));
77 77
78 var addTags = _getList("add_tags",
79 (tagNode) => _parseIdentifierLike(tagNode, "Tag name"));
80
81 var tags = _getMap("tags",
82 key: (keyNode) => _parseNode(keyNode, "tags key",
83 (value) => new BooleanSelector.parse(value)),
84 value: (valueNode) =>
85 _nestedConfig(valueNode, "tag value", runnerConfig: false));
86
87 var onPlatform = _getMap("on_platform", 78 var onPlatform = _getMap("on_platform",
88 key: (keyNode) => _parseNode(keyNode, "on_platform key", 79 key: (keyNode) => _parseNode(keyNode, "on_platform key",
89 (value) => new PlatformSelector.parse(value)), 80 (value) => new PlatformSelector.parse(value)),
90 value: (valueNode) => 81 value: (valueNode) =>
91 _nestedConfig(valueNode, "on_platform value", runnerConfig: false)); 82 _nestedConfig(valueNode, "on_platform value", runnerConfig: false));
92 83
93 var onOS = _getMap("on_os", key: (keyNode) { 84 var onOS = _getMap("on_os", key: (keyNode) {
94 _validate(keyNode, "on_os key must be a string.", 85 _validate(keyNode, "on_os key must be a string.",
95 (value) => value is String); 86 (value) => value is String);
96 87
97 var os = OperatingSystem.find(keyNode.value); 88 var os = OperatingSystem.find(keyNode.value);
98 if (os != null) return os; 89 if (os != null) return os;
99 90
100 throw new SourceSpanFormatException( 91 throw new SourceSpanFormatException(
101 'Invalid on_os key: No such operating system.', 92 'Invalid on_os key: No such operating system.',
102 keyNode.span, _source); 93 keyNode.span, _source);
103 }, value: (valueNode) => _nestedConfig(valueNode, "on_os value")); 94 }, value: (valueNode) => _nestedConfig(valueNode, "on_os value"));
104 95
105 var presets = _getMap("presets", 96 var presets = _getMap("presets",
106 key: (keyNode) => _parseIdentifierLike(keyNode, "presets key"), 97 key: (keyNode) => _parseIdentifierLike(keyNode, "presets key"),
107 value: (valueNode) => _nestedConfig(valueNode, "presets value")); 98 value: (valueNode) => _nestedConfig(valueNode, "presets value"));
108 99
109 var config = new Configuration( 100 var config = new Configuration(
110 verboseTrace: verboseTrace, 101 verboseTrace: verboseTrace,
111 jsTrace: jsTrace, 102 jsTrace: jsTrace,
112 skip: skip,
113 skipReason: skipReason,
114 testOn: testOn,
115 timeout: timeout, 103 timeout: timeout,
116 addTags: addTags,
117 tags: tags,
118 onPlatform: onPlatform, 104 onPlatform: onPlatform,
119 presets: presets); 105 presets: presets);
120 106
121 var osConfig = onOS[currentOS]; 107 var osConfig = onOS[currentOS];
122 return osConfig == null ? config : config.merge(osConfig); 108 return osConfig == null ? config : config.merge(osConfig);
123 } 109 }
124 110
125 /// Loads runner configuration (but not test configuration). 111 /// Loads test configuration that's not allowed in the global configuration
112 /// file.
113 ///
114 /// If [_global] is `true`, this will error if there are any local test-level
115 /// configuration fields.
116 Configuration _loadLocalTestConfig() {
117 if (_global) {
118 _disallow("skip");
119 _disallow("test_on");
120 _disallow("add_tags");
121 _disallow("tags");
122 return Configuration.empty;
123 }
124
125 var skip = _getValue("skip", "boolean or string",
126 (value) => value is bool || value is String);
127 var skipReason;
128 if (skip is String) {
129 skipReason = skip;
130 skip = true;
131 }
132
133 var testOn = _parseValue("test_on",
134 (value) => new PlatformSelector.parse(value));
135
136 var addTags = _getList("add_tags",
137 (tagNode) => _parseIdentifierLike(tagNode, "Tag name"));
138
139 var tags = _getMap("tags",
140 key: (keyNode) => _parseNode(keyNode, "tags key",
141 (value) => new BooleanSelector.parse(value)),
142 value: (valueNode) =>
143 _nestedConfig(valueNode, "tag value", runnerConfig: false));
144
145 return new Configuration(
146 skip: skip,
147 skipReason: skipReason,
148 testOn: testOn,
149 addTags: addTags,
150 tags: tags);
151 }
152
153 /// Loads runner configuration that's allowed in the global configuration
154 /// file.
126 /// 155 ///
127 /// If [_runnerConfig] is `false`, this will error if there are any 156 /// If [_runnerConfig] is `false`, this will error if there are any
128 /// runner-level configuration fields. 157 /// runner-level configuration fields.
129 Configuration _loadRunnerConfig() { 158 Configuration _loadGlobalRunnerConfig() {
130 if (!_runnerConfig) { 159 if (!_runnerConfig) {
131 _disallow("pause_after_load"); 160 _disallow("pause_after_load");
132 _disallow("reporter"); 161 _disallow("reporter");
133 _disallow("pub_serve");
134 _disallow("concurrency"); 162 _disallow("concurrency");
135 _disallow("names"); 163 _disallow("names");
136 _disallow("plain_names"); 164 _disallow("plain_names");
137 _disallow("platforms"); 165 _disallow("platforms");
138 _disallow("paths");
139 _disallow("filename");
140 _disallow("add_presets"); 166 _disallow("add_presets");
141 _disallow("include_tags");
142 _disallow("exclude_tags");
143 return Configuration.empty; 167 return Configuration.empty;
144 } 168 }
145 169
146 var pauseAfterLoad = _getBool("pause_after_load"); 170 var pauseAfterLoad = _getBool("pause_after_load");
147 171
148 var reporter = _getString("reporter"); 172 var reporter = _getString("reporter");
149 if (reporter != null && !allReporters.contains(reporter)) { 173 if (reporter != null && !allReporters.contains(reporter)) {
150 _error('Unknown reporter "$reporter".', "reporter"); 174 _error('Unknown reporter "$reporter".', "reporter");
151 } 175 }
152 176
153 var pubServePort = _getInt("pub_serve");
154 var concurrency = _getInt("concurrency"); 177 var concurrency = _getInt("concurrency");
155 178
156 var patterns = _getList("names", (nameNode) {
157 _validate(nameNode, "Names must be strings.", (value) => value is String);
158 return _parseNode(nameNode, "name", (value) => new RegExp(value));
159 })..addAll(_getList("plain_names", (nameNode) {
160 _validate(nameNode, "Names must be strings.", (value) => value is String);
161 return nameNode.value;
162 }));
163
164 var allPlatformIdentifiers = 179 var allPlatformIdentifiers =
165 TestPlatform.all.map((platform) => platform.identifier).toSet(); 180 TestPlatform.all.map((platform) => platform.identifier).toSet();
166 var platforms = _getList("platforms", (platformNode) { 181 var platforms = _getList("platforms", (platformNode) {
167 _validate(platformNode, "Platforms must be strings.", 182 _validate(platformNode, "Platforms must be strings.",
168 (value) => value is String); 183 (value) => value is String);
169 _validate(platformNode, 'Unknown platform "${platformNode.value}".', 184 _validate(platformNode, 'Unknown platform "${platformNode.value}".',
170 allPlatformIdentifiers.contains); 185 allPlatformIdentifiers.contains);
171 186
172 return TestPlatform.find(platformNode.value); 187 return TestPlatform.find(platformNode.value);
173 }); 188 });
174 189
190 var chosenPresets = _getList("add_presets",
191 (presetNode) => _parseIdentifierLike(presetNode, "Preset name"));
192
193 return new Configuration(
194 pauseAfterLoad: pauseAfterLoad,
195 reporter: reporter,
196 concurrency: concurrency,
197 platforms: platforms,
198 chosenPresets: chosenPresets);
199 }
200
201 /// Loads runner configuration that's not allowed in the global configuration
202 /// file.
203 ///
204 /// If [_runnerConfig] is `false` or if [_global] is `true`, this will error
205 /// if there are any local test-level configuration fields.
206 Configuration _loadLocalRunnerConfig() {
207 if (!_runnerConfig || _global) {
208 _disallow("pub_serve");
209 _disallow("names");
210 _disallow("plain_names");
211 _disallow("paths");
212 _disallow("filename");
213 _disallow("include_tags");
214 _disallow("exclude_tags");
215 return Configuration.empty;
216 }
217
218 var pubServePort = _getInt("pub_serve");
219
220 var patterns = _getList("names", (nameNode) {
221 _validate(nameNode, "Names must be strings.", (value) => value is String);
222 return _parseNode(nameNode, "name", (value) => new RegExp(value));
223 })..addAll(_getList("plain_names", (nameNode) {
224 _validate(nameNode, "Names must be strings.", (value) => value is String);
225 return nameNode.value;
226 }));
227
175 var paths = _getList("paths", (pathNode) { 228 var paths = _getList("paths", (pathNode) {
176 _validate(pathNode, "Paths must be strings.", (value) => value is String); 229 _validate(pathNode, "Paths must be strings.", (value) => value is String);
177 _validate(pathNode, "Paths must be relative.", p.url.isRelative); 230 _validate(pathNode, "Paths must be relative.", p.url.isRelative);
178 231
179 return _parseNode(pathNode, "path", p.fromUri); 232 return _parseNode(pathNode, "path", p.fromUri);
180 }); 233 });
181 234
182 var filename = _parseValue("filename", (value) => new Glob(value)); 235 var filename = _parseValue("filename", (value) => new Glob(value));
183 236
184 var chosenPresets = _getList("add_presets",
185 (presetNode) => _parseIdentifierLike(presetNode, "Preset name"));
186
187 var includeTags = _parseBooleanSelector("include_tags"); 237 var includeTags = _parseBooleanSelector("include_tags");
188 var excludeTags = _parseBooleanSelector("exclude_tags"); 238 var excludeTags = _parseBooleanSelector("exclude_tags");
189 239
190 return new Configuration( 240 return new Configuration(
191 pauseAfterLoad: pauseAfterLoad,
192 reporter: reporter,
193 pubServePort: pubServePort, 241 pubServePort: pubServePort,
194 concurrency: concurrency,
195 patterns: patterns, 242 patterns: patterns,
196 platforms: platforms,
197 paths: paths, 243 paths: paths,
198 filename: filename, 244 filename: filename,
199 chosenPresets: chosenPresets,
200 includeTags: includeTags, 245 includeTags: includeTags,
201 excludeTags: excludeTags); 246 excludeTags: excludeTags);
202 } 247 }
203 248
204 /// Throws an exception with [message] if [test] returns `false` when passed 249 /// Throws an exception with [message] if [test] returns `false` when passed
205 /// [node]'s value. 250 /// [node]'s value.
206 void _validate(YamlNode node, String message, bool test(value)) { 251 void _validate(YamlNode node, String message, bool test(value)) {
207 if (test(node.value)) return; 252 if (test(node.value)) return;
208 throw new SourceSpanFormatException(message, node.span, _source); 253 throw new SourceSpanFormatException(message, node.span, _source);
209 } 254 }
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 /// 370 ///
326 /// [name] is the name of the field, which is used for error-handling. 371 /// [name] is the name of the field, which is used for error-handling.
327 /// [runnerConfig] controls whether runner configuration is allowed in the 372 /// [runnerConfig] controls whether runner configuration is allowed in the
328 /// nested configuration. It defaults to [_runnerConfig]. 373 /// nested configuration. It defaults to [_runnerConfig].
329 Configuration _nestedConfig(YamlNode node, String name, 374 Configuration _nestedConfig(YamlNode node, String name,
330 {bool runnerConfig}) { 375 {bool runnerConfig}) {
331 if (node == null || node.value == null) return Configuration.empty; 376 if (node == null || node.value == null) return Configuration.empty;
332 377
333 _validate(node, "$name must be a map.", (value) => value is Map); 378 _validate(node, "$name must be a map.", (value) => value is Map);
334 var loader = new _ConfigurationLoader(node, _source, 379 var loader = new _ConfigurationLoader(node, _source,
380 global: _global,
335 runnerConfig: runnerConfig ?? _runnerConfig); 381 runnerConfig: runnerConfig ?? _runnerConfig);
336 return loader.load(); 382 return loader.load();
337 } 383 }
338 384
339 /// Throws an error if a field named [field] exists at this level. 385 /// Throws an error if a field named [field] exists at this level.
340 void _disallow(String field) { 386 void _disallow(String field) {
341 if (!_document.containsKey(field)) return; 387 if (!_document.containsKey(field)) return;
342 _error("$field isn't supported here.", field); 388
389 throw new SourceSpanFormatException(
390 "$field isn't supported here.",
391 // We need the key as a [YamlNode] to get its span.
392 _document.nodes.keys.firstWhere((key) => key.value == field).span,
393 _source);
343 } 394 }
344 395
345 /// Throws a [SourceSpanFormatException] with [message] about [field]. 396 /// Throws a [SourceSpanFormatException] with [message] about [field].
346 void _error(String message, String field) { 397 void _error(String message, String field) {
347 throw new SourceSpanFormatException( 398 throw new SourceSpanFormatException(
348 message, _document.nodes[field].span, _source); 399 message, _document.nodes[field].span, _source);
349 } 400 }
350 } 401 }
OLDNEW
« no previous file with comments | « lib/src/runner/configuration.dart ('k') | test/runner/configuration/global_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698