OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:io'; |
| 6 |
| 7 import 'package:path/path.dart' as p; |
| 8 import 'package:source_span/source_span.dart'; |
| 9 import 'package:yaml/yaml.dart'; |
| 10 |
| 11 import '../../utils.dart'; |
| 12 import '../../frontend/timeout.dart'; |
| 13 import '../../backend/test_platform.dart'; |
| 14 import '../configuration.dart'; |
| 15 import 'values.dart'; |
| 16 |
| 17 /// Loads configuration information from a YAML file at [path]. |
| 18 /// |
| 19 /// Throws a [FormatException] if the configuration is invalid, and a |
| 20 /// [FileSystemException] if it can't be read. |
| 21 Configuration load(String path) { |
| 22 var source = new File(path).readAsStringSync(); |
| 23 var document = loadYamlNode(source, sourceUrl: p.toUri(path)); |
| 24 |
| 25 if (document.value == null) return new Configuration(); |
| 26 |
| 27 if (document is! Map) { |
| 28 throw new SourceSpanFormatException( |
| 29 "The configuration must be a YAML map.", document.span, source); |
| 30 } |
| 31 |
| 32 var loader = new _ConfigurationLoader(document, source); |
| 33 return loader.load(); |
| 34 } |
| 35 |
| 36 /// A helper for [load] that tracks the YAML document. |
| 37 class _ConfigurationLoader { |
| 38 /// The parsed configuration document. |
| 39 final YamlMap _document; |
| 40 |
| 41 /// The source string for [_document]. |
| 42 /// |
| 43 /// Used for error reporting. |
| 44 final String _source; |
| 45 |
| 46 _ConfigurationLoader(this._document, this._source); |
| 47 |
| 48 /// Loads the configuration in [_document]. |
| 49 Configuration load() { |
| 50 var verboseTrace = _getBool("verbose_trace"); |
| 51 var jsTrace = _getBool("js_trace"); |
| 52 |
| 53 var reporter = _getString("reporter"); |
| 54 if (reporter != null && !allReporters.contains(reporter)) { |
| 55 _error('Unknown reporter "$reporter".', "reporter"); |
| 56 } |
| 57 |
| 58 var pubServePort = _getInt("pub_serve"); |
| 59 var concurrency = _getInt("concurrency"); |
| 60 var timeout = _parseValue("timeout", (value) => new Timeout.parse(value)); |
| 61 |
| 62 var allPlatformIdentifiers = |
| 63 TestPlatform.all.map((platform) => platform.identifier).toSet(); |
| 64 var platforms = _getList("platforms", (platformNode) { |
| 65 _validate(platformNode, "Platforms must be strings.", |
| 66 (value) => value is String); |
| 67 _validate(platformNode, 'Unknown platform "${platformNode.value}".', |
| 68 allPlatformIdentifiers.contains); |
| 69 |
| 70 return TestPlatform.find(platformNode.value); |
| 71 }); |
| 72 |
| 73 // TODO(nweiz): Add support for using globs to define defaults paths to run. |
| 74 |
| 75 return new Configuration( |
| 76 verboseTrace: verboseTrace, |
| 77 jsTrace: jsTrace, |
| 78 reporter: reporter, |
| 79 pubServePort: pubServePort, |
| 80 concurrency: concurrency, |
| 81 timeout: timeout, |
| 82 platforms: platforms); |
| 83 } |
| 84 |
| 85 /// Throws an exception with [message] if [test] returns `false` when passed |
| 86 /// [node]'s value. |
| 87 void _validate(YamlNode node, String message, bool test(value)) { |
| 88 if (test(node.value)) return; |
| 89 throw new SourceSpanFormatException(message, node.span, _source); |
| 90 } |
| 91 |
| 92 /// Returns the value of the node at [field]. |
| 93 /// |
| 94 /// If [typeTest] returns `false` for that value, instead throws an error |
| 95 /// complaining that the field is not a [typeName]. |
| 96 _getValue(String field, String typeName, bool typeTest(value)) { |
| 97 var value = _document[field]; |
| 98 if (value == null || typeTest(value)) return value; |
| 99 _error("$field must be ${a(typeName)}.", field); |
| 100 } |
| 101 |
| 102 /// Returns the YAML node at [field]. |
| 103 /// |
| 104 /// If [typeTest] returns `false` for that node's value, instead throws an |
| 105 /// error complaining that the field is not a [typeName]. |
| 106 YamlNode _getNode(String field, String typeName, bool typeTest(value)) { |
| 107 var node = _document.nodes[field]; |
| 108 if (node == null) return null; |
| 109 _validate(node, "$field must be ${a(typeName)}.", typeTest); |
| 110 return node; |
| 111 } |
| 112 |
| 113 /// Asserts that [field] is an int and returns its value. |
| 114 int _getInt(String field) => |
| 115 _getValue(field, "int", (value) => value is int); |
| 116 |
| 117 /// Asserts that [field] is a boolean and returns its value. |
| 118 bool _getBool(String field) => |
| 119 _getValue(field, "boolean", (value) => value is bool); |
| 120 |
| 121 /// Asserts that [field] is a string and returns its value. |
| 122 String _getString(String field) => |
| 123 _getValue(field, "string", (value) => value is String); |
| 124 |
| 125 /// Asserts that [field] is a list and runs [forElement] for each element it |
| 126 /// contains. |
| 127 /// |
| 128 /// Returns a list of values returned by [forElement]. |
| 129 List _getList(String field, forElement(YamlNode elementNode)) { |
| 130 var node = _getNode(field, "list", (value) => value is List); |
| 131 if (node == null) return []; |
| 132 return node.nodes.map(forElement).toList(); |
| 133 } |
| 134 |
| 135 /// Asserts that [field] is a string, passes it to [parse], and returns the |
| 136 /// result. |
| 137 /// |
| 138 /// If [parse] throws a [FormatException], it's wrapped to include [field]'s |
| 139 /// span. |
| 140 _parseValue(String field, parse(value)) { |
| 141 var value = _getString(field); |
| 142 if (value == null) return null; |
| 143 |
| 144 try { |
| 145 return parse(value); |
| 146 } on FormatException catch (error) { |
| 147 _error('Invalid $field: ${error.message}', field); |
| 148 } |
| 149 } |
| 150 |
| 151 /// Throws a [SourceSpanFormatException] with [message] about [field]. |
| 152 void _error(String message, String field) { |
| 153 throw new SourceSpanFormatException( |
| 154 message, _document.nodes[field].span, _source); |
| 155 } |
| 156 } |
OLD | NEW |