Index: lib/src/runner/configuration/load.dart |
diff --git a/lib/src/runner/configuration/load.dart b/lib/src/runner/configuration/load.dart |
index 287ed07852b742d320c963ab6f3ee86a6a788b77..e93c80b51f25f00c7d6e0db511191fbad6b1bdf0 100644 |
--- a/lib/src/runner/configuration/load.dart |
+++ b/lib/src/runner/configuration/load.dart |
@@ -21,9 +21,12 @@ import 'values.dart'; |
/// Loads configuration information from a YAML file at [path]. |
/// |
+/// If [global] is `true`, this restricts the configuration file to only rules |
+/// that are supported globally. |
+/// |
/// Throws a [FormatException] if the configuration is invalid, and a |
/// [FileSystemException] if it can't be read. |
-Configuration load(String path) { |
+Configuration load(String path, {bool global: false}) { |
var source = new File(path).readAsStringSync(); |
var document = loadYamlNode(source, sourceUrl: p.toUri(path)); |
@@ -34,7 +37,7 @@ Configuration load(String path) { |
"The configuration must be a YAML map.", document.span, source); |
} |
- var loader = new _ConfigurationLoader(document, source); |
+ var loader = new _ConfigurationLoader(document, source, global: global); |
return loader.load(); |
} |
@@ -48,42 +51,30 @@ class _ConfigurationLoader { |
/// Used for error reporting. |
final String _source; |
+ /// Whether this is parsing the global configuration file. |
+ final bool _global; |
+ |
/// Whether runner configuration is allowed at this level. |
final bool _runnerConfig; |
- _ConfigurationLoader(this._document, this._source, {bool runnerConfig: true}) |
- : _runnerConfig = runnerConfig; |
+ _ConfigurationLoader(this._document, this._source, {bool global: false, |
+ bool runnerConfig: true}) |
+ : _global = global, |
+ _runnerConfig = runnerConfig; |
/// Loads the configuration in [_document]. |
- Configuration load() => _loadTestConfig().merge(_loadRunnerConfig()); |
+ Configuration load() => _loadGlobalTestConfig() |
+ .merge(_loadLocalTestConfig()) |
+ .merge(_loadGlobalRunnerConfig()) |
+ .merge(_loadLocalRunnerConfig()); |
- /// Loads test configuration (but not runner configuration). |
- Configuration _loadTestConfig() { |
+ /// Loads test configuration that's allowed in the global configuration file. |
+ Configuration _loadGlobalTestConfig() { |
var verboseTrace = _getBool("verbose_trace"); |
var jsTrace = _getBool("js_trace"); |
- var skip = _getValue("skip", "boolean or string", |
- (value) => value is bool || value is String); |
- var skipReason; |
- if (skip is String) { |
- skipReason = skip; |
- skip = true; |
- } |
- |
- var testOn = _parseValue("test_on", |
- (value) => new PlatformSelector.parse(value)); |
- |
var timeout = _parseValue("timeout", (value) => new Timeout.parse(value)); |
- var addTags = _getList("add_tags", |
- (tagNode) => _parseIdentifierLike(tagNode, "Tag name")); |
- |
- var tags = _getMap("tags", |
- key: (keyNode) => _parseNode(keyNode, "tags key", |
- (value) => new BooleanSelector.parse(value)), |
- value: (valueNode) => |
- _nestedConfig(valueNode, "tag value", runnerConfig: false)); |
- |
var onPlatform = _getMap("on_platform", |
key: (keyNode) => _parseNode(keyNode, "on_platform key", |
(value) => new PlatformSelector.parse(value)), |
@@ -109,12 +100,7 @@ class _ConfigurationLoader { |
var config = new Configuration( |
verboseTrace: verboseTrace, |
jsTrace: jsTrace, |
- skip: skip, |
- skipReason: skipReason, |
- testOn: testOn, |
timeout: timeout, |
- addTags: addTags, |
- tags: tags, |
onPlatform: onPlatform, |
presets: presets); |
@@ -122,24 +108,62 @@ class _ConfigurationLoader { |
return osConfig == null ? config : config.merge(osConfig); |
} |
- /// Loads runner configuration (but not test configuration). |
+ /// Loads test configuration that's not allowed in the global configuration |
+ /// file. |
+ /// |
+ /// If [_global] is `true`, this will error if there are any local test-level |
+ /// configuration fields. |
+ Configuration _loadLocalTestConfig() { |
+ if (_global) { |
+ _disallow("skip"); |
+ _disallow("test_on"); |
+ _disallow("add_tags"); |
+ _disallow("tags"); |
+ return Configuration.empty; |
+ } |
+ |
+ var skip = _getValue("skip", "boolean or string", |
+ (value) => value is bool || value is String); |
+ var skipReason; |
+ if (skip is String) { |
+ skipReason = skip; |
+ skip = true; |
+ } |
+ |
+ var testOn = _parseValue("test_on", |
+ (value) => new PlatformSelector.parse(value)); |
+ |
+ var addTags = _getList("add_tags", |
+ (tagNode) => _parseIdentifierLike(tagNode, "Tag name")); |
+ |
+ var tags = _getMap("tags", |
+ key: (keyNode) => _parseNode(keyNode, "tags key", |
+ (value) => new BooleanSelector.parse(value)), |
+ value: (valueNode) => |
+ _nestedConfig(valueNode, "tag value", runnerConfig: false)); |
+ |
+ return new Configuration( |
+ skip: skip, |
+ skipReason: skipReason, |
+ testOn: testOn, |
+ addTags: addTags, |
+ tags: tags); |
+ } |
+ |
+ /// Loads runner configuration that's allowed in the global configuration |
+ /// file. |
/// |
/// If [_runnerConfig] is `false`, this will error if there are any |
/// runner-level configuration fields. |
- Configuration _loadRunnerConfig() { |
+ Configuration _loadGlobalRunnerConfig() { |
if (!_runnerConfig) { |
_disallow("pause_after_load"); |
_disallow("reporter"); |
- _disallow("pub_serve"); |
_disallow("concurrency"); |
_disallow("names"); |
_disallow("plain_names"); |
_disallow("platforms"); |
- _disallow("paths"); |
- _disallow("filename"); |
_disallow("add_presets"); |
- _disallow("include_tags"); |
- _disallow("exclude_tags"); |
return Configuration.empty; |
} |
@@ -150,17 +174,8 @@ class _ConfigurationLoader { |
_error('Unknown reporter "$reporter".', "reporter"); |
} |
- var pubServePort = _getInt("pub_serve"); |
var concurrency = _getInt("concurrency"); |
- var patterns = _getList("names", (nameNode) { |
- _validate(nameNode, "Names must be strings.", (value) => value is String); |
- return _parseNode(nameNode, "name", (value) => new RegExp(value)); |
- })..addAll(_getList("plain_names", (nameNode) { |
- _validate(nameNode, "Names must be strings.", (value) => value is String); |
- return nameNode.value; |
- })); |
- |
var allPlatformIdentifiers = |
TestPlatform.all.map((platform) => platform.identifier).toSet(); |
var platforms = _getList("platforms", (platformNode) { |
@@ -172,6 +187,44 @@ class _ConfigurationLoader { |
return TestPlatform.find(platformNode.value); |
}); |
+ var chosenPresets = _getList("add_presets", |
+ (presetNode) => _parseIdentifierLike(presetNode, "Preset name")); |
+ |
+ return new Configuration( |
+ pauseAfterLoad: pauseAfterLoad, |
+ reporter: reporter, |
+ concurrency: concurrency, |
+ platforms: platforms, |
+ chosenPresets: chosenPresets); |
+ } |
+ |
+ /// Loads runner configuration that's not allowed in the global configuration |
+ /// file. |
+ /// |
+ /// If [_runnerConfig] is `false` or if [_global] is `true`, this will error |
+ /// if there are any local test-level configuration fields. |
+ Configuration _loadLocalRunnerConfig() { |
+ if (!_runnerConfig || _global) { |
+ _disallow("pub_serve"); |
+ _disallow("names"); |
+ _disallow("plain_names"); |
+ _disallow("paths"); |
+ _disallow("filename"); |
+ _disallow("include_tags"); |
+ _disallow("exclude_tags"); |
+ return Configuration.empty; |
+ } |
+ |
+ var pubServePort = _getInt("pub_serve"); |
+ |
+ var patterns = _getList("names", (nameNode) { |
+ _validate(nameNode, "Names must be strings.", (value) => value is String); |
+ return _parseNode(nameNode, "name", (value) => new RegExp(value)); |
+ })..addAll(_getList("plain_names", (nameNode) { |
+ _validate(nameNode, "Names must be strings.", (value) => value is String); |
+ return nameNode.value; |
+ })); |
+ |
var paths = _getList("paths", (pathNode) { |
_validate(pathNode, "Paths must be strings.", (value) => value is String); |
_validate(pathNode, "Paths must be relative.", p.url.isRelative); |
@@ -181,22 +234,14 @@ class _ConfigurationLoader { |
var filename = _parseValue("filename", (value) => new Glob(value)); |
- var chosenPresets = _getList("add_presets", |
- (presetNode) => _parseIdentifierLike(presetNode, "Preset name")); |
- |
var includeTags = _parseBooleanSelector("include_tags"); |
var excludeTags = _parseBooleanSelector("exclude_tags"); |
return new Configuration( |
- pauseAfterLoad: pauseAfterLoad, |
- reporter: reporter, |
pubServePort: pubServePort, |
- concurrency: concurrency, |
patterns: patterns, |
- platforms: platforms, |
paths: paths, |
filename: filename, |
- chosenPresets: chosenPresets, |
includeTags: includeTags, |
excludeTags: excludeTags); |
} |
@@ -332,6 +377,7 @@ class _ConfigurationLoader { |
_validate(node, "$name must be a map.", (value) => value is Map); |
var loader = new _ConfigurationLoader(node, _source, |
+ global: _global, |
runnerConfig: runnerConfig ?? _runnerConfig); |
return loader.load(); |
} |
@@ -339,7 +385,12 @@ class _ConfigurationLoader { |
/// Throws an error if a field named [field] exists at this level. |
void _disallow(String field) { |
if (!_document.containsKey(field)) return; |
- _error("$field isn't supported here.", field); |
+ |
+ throw new SourceSpanFormatException( |
+ "$field isn't supported here.", |
+ // We need the key as a [YamlNode] to get its span. |
+ _document.nodes.keys.firstWhere((key) => key.value == field).span, |
+ _source); |
} |
/// Throws a [SourceSpanFormatException] with [message] about [field]. |