// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. /// This library defines how to read a test specification from a Yaml /// file. We expect specifications written in this format: /// /// dependencies: /// b: a /// main: [b, expect] /// flags: /// - constant-update-2018 /// /// Where the dependencies section describe how modules depend on one another, /// and the flags section show what flags are needed to run that specific test. /// /// When defining dependencies: /// - Each name corresponds to a module. /// - Module names correlate to either a file, a folder, or a package. /// - A map entry contains all the dependencies of a module, if any. /// - If a module has a single dependency, it can be written as a single /// value. /// /// The logic in this library mostly treats these names as strings, separately /// `loader.dart` is responsible for validating and attaching this dependency /// information to a set of module definitions. /// /// The framework is agnostic of what the flags are, but at this time we only /// use the name of experimental language features. These are then used to /// decide what options to pass to the tools that compile and run the tests. import 'package:yaml/yaml.dart'; /// Parses [contents] containing a module dependencies specification written in /// yaml, and returns a [TestSpecification]. TestSpecification parseTestSpecification(String contents) { var spec = loadYaml(contents); if (spec is! YamlMap) { return _invalidSpecification("spec is not a map"); } var dependencies = spec['dependencies']; if (dependencies == null) { return _invalidSpecification("no dependencies section"); } if (dependencies is! YamlMap) { return _invalidSpecification("dependencies is not a map"); } Map> normalizedMap = {}; dependencies.forEach((key, value) { if (key is! String) { _invalidSpecification("key: '$key' is not a string"); } normalizedMap[key] = []; if (value is String) { normalizedMap[key].add(value); } else if (value is List) { value.forEach((entry) { if (entry is! String) { _invalidSpecification("entry: '$entry' is not a string"); } normalizedMap[key].add(entry); }); } else { _invalidSpecification( "entry: '$value' is not a string or a list of strings"); } }); List normalizedFlags = []; dynamic flags = spec['flags']; if (flags is String) { normalizedFlags.add(flags); } else if (flags is List) { normalizedFlags.addAll(flags.cast()); } else if (flags != null) { _invalidSpecification( "flags: '$flags' expected to be string or list of strings"); } return new TestSpecification(normalizedFlags, normalizedMap); } /// Data specifying details about a modular test including dependencies and /// flags that are necessary in order to properly run a test. /// class TestSpecification { /// Set of flags necessary to properly run a test. /// /// Usually this contains flags enabling language experiments. final List flags; /// Dependencies of the modules that are expected to exist on the test. /// /// Note: some values in the map may not have a corresponding key. That may be /// the case for modules that have no dependencies and modules that are not /// specified explicitly because they are added automatically by the framework /// (for instance, the module of `package:expect` or the sdk itself). final Map> dependencies; TestSpecification(this.flags, this.dependencies); } _invalidSpecification(String message) { throw new InvalidSpecificationError(message); } class InvalidSpecificationError extends Error { final String message; InvalidSpecificationError(this.message); String toString() => "Invalid specification: $message"; }