| OLD | NEW |
| 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:glob/glob.dart'; | 7 import 'package:glob/glob.dart'; |
| 8 import 'package:path/path.dart' as p; | 8 import 'package:path/path.dart' as p; |
| 9 import 'package:source_span/source_span.dart'; | 9 import 'package:source_span/source_span.dart'; |
| 10 import 'package:yaml/yaml.dart'; | 10 import 'package:yaml/yaml.dart'; |
| (...skipping 26 matching lines...) Expand all Loading... |
| 37 /// A helper for [load] that tracks the YAML document. | 37 /// A helper for [load] that tracks the YAML document. |
| 38 class _ConfigurationLoader { | 38 class _ConfigurationLoader { |
| 39 /// The parsed configuration document. | 39 /// The parsed configuration document. |
| 40 final YamlMap _document; | 40 final YamlMap _document; |
| 41 | 41 |
| 42 /// The source string for [_document]. | 42 /// The source string for [_document]. |
| 43 /// | 43 /// |
| 44 /// Used for error reporting. | 44 /// Used for error reporting. |
| 45 final String _source; | 45 final String _source; |
| 46 | 46 |
| 47 _ConfigurationLoader(this._document, this._source); | 47 /// Whether runner configuration is allowed at this level. |
| 48 final bool _runnerConfig; |
| 49 |
| 50 _ConfigurationLoader(this._document, this._source, {bool runnerConfig: true}) |
| 51 : _runnerConfig = runnerConfig; |
| 48 | 52 |
| 49 /// Loads the configuration in [_document]. | 53 /// Loads the configuration in [_document]. |
| 50 Configuration load() { | 54 Configuration load() => _loadTestConfig().merge(_loadRunnerConfig()); |
| 55 |
| 56 /// Loads test configuration (but not runner configuration). |
| 57 Configuration _loadTestConfig() { |
| 51 var verboseTrace = _getBool("verbose_trace"); | 58 var verboseTrace = _getBool("verbose_trace"); |
| 52 var jsTrace = _getBool("js_trace"); | 59 var jsTrace = _getBool("js_trace"); |
| 53 | 60 |
| 61 var timeout = _parseValue("timeout", (value) => new Timeout.parse(value)); |
| 62 |
| 63 var tags = _getMap("tags", key: (keyNode) { |
| 64 _validate(keyNode, "tags key must be a string.", |
| 65 (value) => value is String); |
| 66 _validate( |
| 67 keyNode, |
| 68 "Invalid tag. Tags must be (optionally hyphenated) Dart identifiers.", |
| 69 (value) => value.contains(anchoredHyphenatedIdentifier)); |
| 70 |
| 71 return keyNode.value; |
| 72 }, value: (valueNode) { |
| 73 return _nestedConfig(valueNode, "tag value", runnerConfig: false); |
| 74 }); |
| 75 |
| 76 return new Configuration( |
| 77 verboseTrace: verboseTrace, |
| 78 jsTrace: jsTrace, |
| 79 timeout: timeout, |
| 80 tags: tags); |
| 81 } |
| 82 |
| 83 /// Loads runner configuration (but not test configuration). |
| 84 /// |
| 85 /// If [_runnerConfig] is `false`, this will error if there are any |
| 86 /// runner-level configuration fields. |
| 87 Configuration _loadRunnerConfig() { |
| 88 if (!_runnerConfig) { |
| 89 _disallow("reporter"); |
| 90 _disallow("pub_serve"); |
| 91 _disallow("concurrency"); |
| 92 _disallow("platforms"); |
| 93 _disallow("paths"); |
| 94 _disallow("filename"); |
| 95 return new Configuration(); |
| 96 } |
| 97 |
| 54 var reporter = _getString("reporter"); | 98 var reporter = _getString("reporter"); |
| 55 if (reporter != null && !allReporters.contains(reporter)) { | 99 if (reporter != null && !allReporters.contains(reporter)) { |
| 56 _error('Unknown reporter "$reporter".', "reporter"); | 100 _error('Unknown reporter "$reporter".', "reporter"); |
| 57 } | 101 } |
| 58 | 102 |
| 59 var pubServePort = _getInt("pub_serve"); | 103 var pubServePort = _getInt("pub_serve"); |
| 60 var concurrency = _getInt("concurrency"); | 104 var concurrency = _getInt("concurrency"); |
| 61 var timeout = _parseValue("timeout", (value) => new Timeout.parse(value)); | |
| 62 | 105 |
| 63 var allPlatformIdentifiers = | 106 var allPlatformIdentifiers = |
| 64 TestPlatform.all.map((platform) => platform.identifier).toSet(); | 107 TestPlatform.all.map((platform) => platform.identifier).toSet(); |
| 65 var platforms = _getList("platforms", (platformNode) { | 108 var platforms = _getList("platforms", (platformNode) { |
| 66 _validate(platformNode, "Platforms must be strings.", | 109 _validate(platformNode, "Platforms must be strings.", |
| 67 (value) => value is String); | 110 (value) => value is String); |
| 68 _validate(platformNode, 'Unknown platform "${platformNode.value}".', | 111 _validate(platformNode, 'Unknown platform "${platformNode.value}".', |
| 69 allPlatformIdentifiers.contains); | 112 allPlatformIdentifiers.contains); |
| 70 | 113 |
| 71 return TestPlatform.find(platformNode.value); | 114 return TestPlatform.find(platformNode.value); |
| 72 }); | 115 }); |
| 73 | 116 |
| 74 var paths = _getList("paths", (pathNode) { | 117 var paths = _getList("paths", (pathNode) { |
| 75 _validate(pathNode, "Paths must be strings.", (value) => value is String); | 118 _validate(pathNode, "Paths must be strings.", (value) => value is String); |
| 76 _validate(pathNode, "Paths must be relative.", p.url.isRelative); | 119 _validate(pathNode, "Paths must be relative.", p.url.isRelative); |
| 77 | 120 |
| 78 return _parseNode(pathNode, "path", p.fromUri); | 121 return _parseNode(pathNode, "path", p.fromUri); |
| 79 }); | 122 }); |
| 80 | 123 |
| 81 var filename = _parseValue("filename", (value) => new Glob(value)); | 124 var filename = _parseValue("filename", (value) => new Glob(value)); |
| 82 | 125 |
| 83 return new Configuration( | 126 return new Configuration( |
| 84 verboseTrace: verboseTrace, | |
| 85 jsTrace: jsTrace, | |
| 86 reporter: reporter, | 127 reporter: reporter, |
| 87 pubServePort: pubServePort, | 128 pubServePort: pubServePort, |
| 88 concurrency: concurrency, | 129 concurrency: concurrency, |
| 89 timeout: timeout, | |
| 90 platforms: platforms, | 130 platforms: platforms, |
| 91 paths: paths, | 131 paths: paths, |
| 92 filename: filename); | 132 filename: filename); |
| 93 } | 133 } |
| 94 | 134 |
| 95 /// Throws an exception with [message] if [test] returns `false` when passed | 135 /// Throws an exception with [message] if [test] returns `false` when passed |
| 96 /// [node]'s value. | 136 /// [node]'s value. |
| 97 void _validate(YamlNode node, String message, bool test(value)) { | 137 void _validate(YamlNode node, String message, bool test(value)) { |
| 98 if (test(node.value)) return; | 138 if (test(node.value)) return; |
| 99 throw new SourceSpanFormatException(message, node.span, _source); | 139 throw new SourceSpanFormatException(message, node.span, _source); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 /// Asserts that [field] is a list and runs [forElement] for each element it | 175 /// Asserts that [field] is a list and runs [forElement] for each element it |
| 136 /// contains. | 176 /// contains. |
| 137 /// | 177 /// |
| 138 /// Returns a list of values returned by [forElement]. | 178 /// Returns a list of values returned by [forElement]. |
| 139 List _getList(String field, forElement(YamlNode elementNode)) { | 179 List _getList(String field, forElement(YamlNode elementNode)) { |
| 140 var node = _getNode(field, "list", (value) => value is List); | 180 var node = _getNode(field, "list", (value) => value is List); |
| 141 if (node == null) return []; | 181 if (node == null) return []; |
| 142 return node.nodes.map(forElement).toList(); | 182 return node.nodes.map(forElement).toList(); |
| 143 } | 183 } |
| 144 | 184 |
| 185 /// Asserts that [field] is a map and runs [key] and [value] for each pair. |
| 186 /// |
| 187 /// Returns a map with the keys and values returned by [key] and [value]. Each |
| 188 /// of these defaults to asserting that the value is a string. |
| 189 Map _getMap(String field, {key(YamlNode keyNode), |
| 190 value(YamlNode valueNode)}) { |
| 191 var node = _getNode(field, "map", (value) => value is Map); |
| 192 if (node == null) return {}; |
| 193 |
| 194 key ??= (keyNode) { |
| 195 _validate(keyNode, "$field keys must be strings.", |
| 196 (value) => value is String); |
| 197 |
| 198 return keyNode.value; |
| 199 }; |
| 200 |
| 201 value ??= (valueNode) { |
| 202 _validate(valueNode, "$field values must be strings.", |
| 203 (value) => value is String); |
| 204 |
| 205 return valueNode.value; |
| 206 }; |
| 207 |
| 208 return mapMap(node.nodes, |
| 209 key: (keyNode, _) => key(keyNode), |
| 210 value: (_, valueNode) => value(valueNode)); |
| 211 } |
| 212 |
| 145 /// Asserts that [node] is a string, passes its value to [parse], and returns | 213 /// Asserts that [node] is a string, passes its value to [parse], and returns |
| 146 /// the result. | 214 /// the result. |
| 147 /// | 215 /// |
| 148 /// If [parse] throws a [FormatException], it's wrapped to include [node]'s | 216 /// If [parse] throws a [FormatException], it's wrapped to include [node]'s |
| 149 /// span. | 217 /// span. |
| 150 _parseNode(YamlNode node, String name, parse(String value)) { | 218 _parseNode(YamlNode node, String name, parse(String value)) { |
| 151 _validate(node, "$name must be a string.", (value) => value is String); | 219 _validate(node, "$name must be a string.", (value) => value is String); |
| 152 | 220 |
| 153 try { | 221 try { |
| 154 return parse(node.value); | 222 return parse(node.value); |
| 155 } on FormatException catch (error) { | 223 } on FormatException catch (error) { |
| 156 throw new SourceSpanFormatException( | 224 throw new SourceSpanFormatException( |
| 157 'Invalid $name: ${error.message}', node.span, _source); | 225 'Invalid $name: ${error.message}', node.span, _source); |
| 158 } | 226 } |
| 159 } | 227 } |
| 160 | 228 |
| 161 /// Asserts that [field] is a string, passes it to [parse], and returns the | 229 /// Asserts that [field] is a string, passes it to [parse], and returns the |
| 162 /// result. | 230 /// result. |
| 163 /// | 231 /// |
| 164 /// If [parse] throws a [FormatException], it's wrapped to include [field]'s | 232 /// If [parse] throws a [FormatException], it's wrapped to include [field]'s |
| 165 /// span. | 233 /// span. |
| 166 _parseValue(String field, parse(String value)) { | 234 _parseValue(String field, parse(String value)) { |
| 167 var node = _document.nodes[field]; | 235 var node = _document.nodes[field]; |
| 168 if (node == null) return null; | 236 if (node == null) return null; |
| 169 return _parseNode(node, field, parse); | 237 return _parseNode(node, field, parse); |
| 170 } | 238 } |
| 171 | 239 |
| 240 /// Parses a nested configuration document. |
| 241 /// |
| 242 /// [name] is the name of the field, which is used for error-handling. |
| 243 /// [runnerConfig] controls whether runner configuration is allowed in the |
| 244 /// nested configuration. It defaults to [_runnerConfig]. |
| 245 Configuration _nestedConfig(YamlNode node, String name, |
| 246 {bool runnerConfig}) { |
| 247 if (node == null || node.value == null) return new Configuration(); |
| 248 |
| 249 _validate(node, "$name must be a map.", (value) => value is Map); |
| 250 var loader = new _ConfigurationLoader(node, _source, |
| 251 runnerConfig: runnerConfig ?? _runnerConfig); |
| 252 return loader.load(); |
| 253 } |
| 254 |
| 255 /// Throws an error if a field named [field] exists at this level. |
| 256 void _disallow(String field) { |
| 257 if (!_document.containsKey(field)) return; |
| 258 _error("$field isn't supported here.", field); |
| 259 } |
| 260 |
| 172 /// Throws a [SourceSpanFormatException] with [message] about [field]. | 261 /// Throws a [SourceSpanFormatException] with [message] about [field]. |
| 173 void _error(String message, String field) { | 262 void _error(String message, String field) { |
| 174 throw new SourceSpanFormatException( | 263 throw new SourceSpanFormatException( |
| 175 message, _document.nodes[field].span, _source); | 264 message, _document.nodes[field].span, _source); |
| 176 } | 265 } |
| 177 } | 266 } |
| OLD | NEW |