| // Copyright (c) 2015, 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. | |
| import 'dart:async'; | |
| import 'dart:io'; | |
| import 'package:boolean_selector/boolean_selector.dart'; | |
| import 'package:collection/collection.dart'; | |
| import 'package:glob/glob.dart'; | |
| import 'package:path/path.dart' as p; | |
| import 'package:source_span/source_span.dart'; | |
| import 'package:test_api/scaffolding.dart' show Timeout; | |
| import 'package:test_api/src/backend/platform_selector.dart'; // ignore: implementation_imports | |
| import 'package:test_api/src/backend/runtime.dart'; // ignore: implementation_imports | |
| import '../util/io.dart'; | |
| import 'compiler_selection.dart'; | |
| import 'configuration/args.dart' as args; | |
| import 'configuration/custom_runtime.dart'; | |
| import 'configuration/load.dart'; | |
| import 'configuration/reporters.dart'; | |
| import 'configuration/runtime_settings.dart'; | |
| import 'configuration/utils.dart'; | |
| import 'configuration/values.dart'; | |
| import 'runtime_selection.dart'; | |
| import 'suite.dart'; | |
| export 'suite.dart' show TestSelection; | |
| /// The key used to look up [Configuration.current] in a zone. | |
| final _currentKey = Object(); | |
| /// A class that encapsulates the command-line configuration of the test runner. | |
| class Configuration { | |
| /// An empty configuration with only default values. | |
| /// | |
| /// Using this is slightly more efficient than manually constructing a new | |
| /// configuration with no arguments. | |
| static final empty = Configuration._unsafe(); | |
| /// The usage string for the command-line arguments. | |
| static String get usage => args.usage; | |
| /// Whether `--help` was passed. | |
| bool get help => _help ?? false; | |
| final bool? _help; | |
| /// Custom HTML template file. | |
| final String? customHtmlTemplatePath; | |
| /// Whether `--version` was passed. | |
| bool get version => _version ?? false; | |
| final bool? _version; | |
| /// Whether to pause for debugging after loading each test suite. | |
| bool get pauseAfterLoad => _pauseAfterLoad ?? false; | |
| final bool? _pauseAfterLoad; | |
| /// Whether to run browsers in their respective debug modes | |
| bool get debug => | |
| pauseAfterLoad || | |
| (_debug ?? false) || | |
| coverage != null || | |
| coverageLcov != null; | |
| final bool? _debug; | |
| /// The output folder for coverage gathering. | |
| final String? coverage; | |
| /// The lcov file to output coverage to. | |
| final String? coverageLcov; | |
| /// Whether to collect branch coverage info. | |
| bool get branchCoverage => _branchCoverage ?? false; | |
| final bool? _branchCoverage; | |
| /// A list of regular expressions to match against the package names during | |
| /// coverage collection. | |
| /// | |
| /// Only packages that match at least one of the regexps will be included in | |
| /// the coverage report. | |
| final List<RegExp>? coveragePackages; | |
| /// The path to the file from which to load more configuration information. | |
| /// | |
| /// This is *not* resolved automatically. | |
| String get configurationPath => _configurationPath ?? 'dart_test.yaml'; | |
| final String? _configurationPath; | |
| /// The name of the reporter to use to display results. | |
| String get reporter => _reporter ?? defaultReporter; | |
| final String? _reporter; | |
| /// The map of file reporters where the key is the name of the reporter and | |
| /// the value is the filepath to which its output should be written. | |
| final Map<String, String> fileReporters; | |
| /// Whether to disable retries of tests. | |
| bool get noRetry => _noRetry ?? false; | |
| final bool? _noRetry; | |
| /// Whether to use command-line color escapes. | |
| bool get color => _color ?? canUseSpecialChars; | |
| final bool? _color; | |
| /// How many tests to run concurrently. | |
| int get concurrency => | |
| pauseAfterLoad ? 1 : (_concurrency ?? defaultConcurrency); | |
| final int? _concurrency; | |
| /// The index of the current shard, if sharding is in use, or `null` if it's | |
| /// not. | |
| /// | |
| /// Sharding is a technique that allows the Google internal test framework to | |
| /// easily split a test run across multiple workers without requiring the | |
| /// tests to be modified by the user. When sharding is in use, the runner gets | |
| /// a shard index (this field) and a total number of shards, and is expected | |
| /// to provide the following guarantees: | |
| /// | |
| /// * Running the same invocation of the runner, with the same shard index and | |
| /// total shards, will run the same set of tests. | |
| /// * Across all shards, each test must be run exactly once. | |
| /// | |
| /// In addition, tests should be balanced across shards as much as possible. | |
| final int? shardIndex; | |
| /// The total number of shards, if sharding is in use, or `null` if it's not. | |
| /// | |
| /// See [shardIndex] for details. | |
| final int? totalShards; | |
| /// The list of packages to fold when producing [StackTrace]s. | |
| Set<String> get foldTraceExcept => _foldTraceExcept ?? {}; | |
| final Set<String>? _foldTraceExcept; | |
| /// If non-empty, all packages not in this list will be folded when producing | |
| /// [StackTrace]s. | |
| Set<String> get foldTraceOnly => _foldTraceOnly ?? {}; | |
| final Set<String>? _foldTraceOnly; | |
| /// The paths from which to load tests, and the test cases to run. | |
| Map<String, Set<TestSelection>> get testSelections => | |
| _testSelections ?? | |
| const { | |
| 'test': {TestSelection()}, | |
| }; | |
| final Map<String, Set<TestSelection>>? _testSelections; | |
| /// Whether the load paths were passed explicitly or the default was used. | |
| bool get explicitPaths => _testSelections != null; | |
| /// The glob matching the basename of tests to run. | |
| /// | |
| /// This is used to find tests within a directory. | |
| Glob get filename => _filename ?? defaultFilename; | |
| final Glob? _filename; | |
| /// The set of presets to use. | |
| /// | |
| /// Any chosen presets for the parent configuration are added to the chosen | |
| /// preset sets for child configurations as well. | |
| /// | |
| /// Note that the order of this set matters. | |
| final Set<String> chosenPresets; | |
| /// The set of tags that have been declared in any way in this configuration. | |
| late final Set<String> knownTags = UnmodifiableSetView({ | |
| ...includeTags.variables, | |
| ...excludeTags.variables, | |
| ...suiteDefaults.knownTags, | |
| for (var configuration in presets.values) ...configuration.knownTags, | |
| }); | |
| /// Only run tests whose tags match this selector. | |
| /// | |
| /// When [merge]d, this is intersected with the other configuration's included | |
| /// tags. | |
| final BooleanSelector includeTags; | |
| /// Do not run tests whose tags match this selector. | |
| /// | |
| /// When [merge]d, this is unioned with the other configuration's | |
| /// excluded tags. | |
| final BooleanSelector excludeTags; | |
| /// Configuration presets. | |
| /// | |
| /// These are configurations that can be explicitly selected by the user via | |
| /// the command line. Preset configuration takes precedence over the base | |
| /// configuration. | |
| /// | |
| /// This is guaranteed not to have any keys that match [chosenPresets]; those | |
| /// are resolved when the configuration is constructed. | |
| final Map<String, Configuration> presets; | |
| /// All preset names that are known to be valid. | |
| /// | |
| /// This includes presets that have already been resolved. | |
| Set<String> get knownPresets => _knownPresets ??= UnmodifiableSetView({ | |
| ...presets.keys, | |
| for (var configuration in presets.values) ...configuration.knownPresets, | |
| }); | |
| Set<String>? _knownPresets; | |
| /// Built-in runtimes whose settings are overridden by the user. | |
| final Map<String, RuntimeSettings> overrideRuntimes; | |
| /// Runtimes defined by the user in terms of existing runtimes. | |
| final Map<String, CustomRuntime> defineRuntimes; | |
| /// The default suite-level configuration. | |
| final SuiteConfiguration suiteDefaults; | |
| /// The set of patterns to check test names against in all suites that run. | |
| final Set<Pattern> globalPatterns; | |
| /// The seed used to generate randomness for test case shuffling. | |
| /// | |
| /// If null or zero no shuffling will occur. | |
| /// The same seed will shuffle the tests in the same way every time. | |
| final int? testRandomizeOrderingSeed; | |
| final bool? _stopOnFirstFailure; | |
| /// Whether to stop running subsequent tests after a test fails. | |
| bool get stopOnFirstFailure => _stopOnFirstFailure ?? false; | |
| /// Returns the current configuration, or a default configuration if no | |
| /// current configuration is set. | |
| /// | |
| /// The current configuration is set using [asCurrent]. | |
| static Configuration get current => | |
| Zone.current[_currentKey] as Configuration? ?? Configuration._unsafe(); | |
| /// Parses the configuration from [args]. | |
| /// | |
| /// Throws a [FormatException] if [args] are invalid. | |
| factory Configuration.parse(List<String> arguments) => args.parse(arguments); | |
| /// Loads configuration from [path]. | |
| /// | |
| /// If [global] is `true`, this restricts the configuration to rules that are | |
| /// supported globally. | |
| /// | |
| /// Throws an [IOException] if [path] does not exist or cannot be read. Throws | |
| /// a [FormatException] if the file contents are invalid. | |
| factory Configuration.load(String path, {bool global = false}) { | |
| final content = File(path).readAsStringSync(); | |
| final sourceUrl = p.toUri(path); | |
| return parse(content, global: global, sourceUrl: sourceUrl); | |
| } | |
| /// Parses configuration from YAML formatted [content]. | |
| /// | |
| /// If [global] is `true`, this restricts the configuration to rules that are | |
| /// supported globally. | |
| /// | |
| /// If [sourceUrl] is provided it will be set as the source url for the yaml | |
| /// document. | |
| /// | |
| /// Throws a [FormatException] if the content is invalid. | |
| factory Configuration.loadFromString( | |
| String content, { | |
| bool global = false, | |
| Uri? sourceUrl, | |
| }) => parse(content, global: global, sourceUrl: sourceUrl); | |
| factory Configuration({ | |
| required bool? help, | |
| required String? customHtmlTemplatePath, | |
| required bool? version, | |
| required bool? pauseAfterLoad, | |
| required bool? debug, | |
| required bool? color, | |
| required String? configurationPath, | |
| required String? reporter, | |
| required Map<String, String>? fileReporters, | |
| required String? coverage, | |
| required String? coverageLcov, | |
| required bool? branchCoverage, | |
| required List<RegExp>? coveragePackages, | |
| required int? concurrency, | |
| required int? shardIndex, | |
| required int? totalShards, | |
| required Map<String, Set<TestSelection>>? testSelections, | |
| required Iterable<String>? foldTraceExcept, | |
| required Iterable<String>? foldTraceOnly, | |
| required Glob? filename, | |
| required Iterable<String>? chosenPresets, | |
| required Map<String, Configuration>? presets, | |
| required Map<String, RuntimeSettings>? overrideRuntimes, | |
| required Map<String, CustomRuntime>? defineRuntimes, | |
| required bool? noRetry, | |
| required int? testRandomizeOrderingSeed, | |
| required bool? stopOnFirstFailure, | |
| // Suite-level configuration | |
| required bool? allowDuplicateTestNames, | |
| required bool? allowTestRandomization, | |
| required bool? jsTrace, | |
| required bool? runSkipped, | |
| required Iterable<String>? dart2jsArgs, | |
| required String? precompiledPath, | |
| required Iterable<Pattern>? globalPatterns, | |
| required Iterable<CompilerSelection>? compilerSelections, | |
| required Iterable<RuntimeSelection>? runtimes, | |
| required BooleanSelector? includeTags, | |
| required BooleanSelector? excludeTags, | |
| required Map<BooleanSelector, SuiteConfiguration>? tags, | |
| required Map<PlatformSelector, SuiteConfiguration>? onPlatform, | |
| required bool? ignoreTimeouts, | |
| required Timeout? suiteLoadTimeout, | |
| // Test-level configuration | |
| required Timeout? timeout, | |
| required bool? verboseTrace, | |
| required bool? chainStackTraces, | |
| required bool? skip, | |
| required int? retry, | |
| required String? skipReason, | |
| required PlatformSelector? testOn, | |
| required Iterable<String>? addTags, | |
| }) { | |
| var chosenPresetSet = chosenPresets?.toSet(); | |
| var configuration = Configuration._( | |
| help: help, | |
| customHtmlTemplatePath: customHtmlTemplatePath, | |
| version: version, | |
| pauseAfterLoad: pauseAfterLoad, | |
| debug: debug, | |
| color: color, | |
| configurationPath: configurationPath, | |
| reporter: reporter, | |
| fileReporters: fileReporters, | |
| coverage: coverage, | |
| coverageLcov: coverageLcov, | |
| branchCoverage: branchCoverage, | |
| coveragePackages: coveragePackages, | |
| concurrency: concurrency, | |
| shardIndex: shardIndex, | |
| totalShards: totalShards, | |
| testSelections: testSelections, | |
| foldTraceExcept: foldTraceExcept, | |
| foldTraceOnly: foldTraceOnly, | |
| filename: filename, | |
| chosenPresets: chosenPresetSet, | |
| presets: _withChosenPresets(presets, chosenPresetSet), | |
| overrideRuntimes: overrideRuntimes, | |
| defineRuntimes: defineRuntimes, | |
| noRetry: noRetry, | |
| testRandomizeOrderingSeed: testRandomizeOrderingSeed, | |
| stopOnFirstFailure: stopOnFirstFailure, | |
| includeTags: includeTags, | |
| excludeTags: excludeTags, | |
| globalPatterns: globalPatterns, | |
| suiteDefaults: SuiteConfiguration( | |
| allowDuplicateTestNames: allowDuplicateTestNames, | |
| allowTestRandomization: allowTestRandomization, | |
| jsTrace: jsTrace, | |
| runSkipped: runSkipped, | |
| dart2jsArgs: dart2jsArgs, | |
| precompiledPath: precompiledPath, | |
| compilerSelections: compilerSelections, | |
| runtimes: runtimes, | |
| tags: tags, | |
| onPlatform: onPlatform, | |
| ignoreTimeouts: ignoreTimeouts, | |
| suiteLoadTimeout: suiteLoadTimeout, | |
| // Test-level configuration | |
| timeout: timeout, | |
| verboseTrace: verboseTrace, | |
| chainStackTraces: chainStackTraces, | |
| skip: skip, | |
| retry: retry, | |
| skipReason: skipReason, | |
| testOn: testOn, | |
| addTags: addTags, | |
| ), | |
| ); | |
| return configuration._resolvePresets(); | |
| } | |
| /// A constructor that doesn't require all of its options to be passed. | |
| /// | |
| /// This should only be used in situations where you really only want to | |
| /// configure a specific restricted set of options. | |
| factory Configuration._unsafe({ | |
| bool? help, | |
| String? customHtmlTemplatePath, | |
| bool? version, | |
| bool? pauseAfterLoad, | |
| bool? debug, | |
| bool? color, | |
| String? configurationPath, | |
| String? reporter, | |
| Map<String, String>? fileReporters, | |
| String? coverage, | |
| String? coverageLcov, | |
| bool? branchCoverage, | |
| List<RegExp>? coveragePackages, | |
| int? concurrency, | |
| int? shardIndex, | |
| int? totalShards, | |
| Map<String, Set<TestSelection>>? testSelections, | |
| Iterable<String>? foldTraceExcept, | |
| Iterable<String>? foldTraceOnly, | |
| Glob? filename, | |
| Iterable<String>? chosenPresets, | |
| Map<String, Configuration>? presets, | |
| Map<String, RuntimeSettings>? overrideRuntimes, | |
| Map<String, CustomRuntime>? defineRuntimes, | |
| bool? noRetry, | |
| int? testRandomizeOrderingSeed, | |
| bool? stopOnFirstFailure, | |
| // Suite-level configuration | |
| bool? allowDuplicateTestNames, | |
| bool? allowTestRandomization, | |
| bool? jsTrace, | |
| bool? runSkipped, | |
| Iterable<String>? dart2jsArgs, | |
| String? precompiledPath, | |
| Iterable<Pattern>? globalPatterns, | |
| Iterable<CompilerSelection>? compilerSelections, | |
| Iterable<RuntimeSelection>? runtimes, | |
| BooleanSelector? includeTags, | |
| BooleanSelector? excludeTags, | |
| Map<BooleanSelector, SuiteConfiguration>? tags, | |
| Map<PlatformSelector, SuiteConfiguration>? onPlatform, | |
| bool? ignoreTimeouts, | |
| Timeout? suiteLoadTimeout, | |
| // Test-level configuration | |
| Timeout? timeout, | |
| bool? verboseTrace, | |
| bool? chainStackTraces, | |
| bool? skip, | |
| int? retry, | |
| String? skipReason, | |
| PlatformSelector? testOn, | |
| Iterable<String>? addTags, | |
| }) => Configuration( | |
| help: help, | |
| customHtmlTemplatePath: customHtmlTemplatePath, | |
| version: version, | |
| pauseAfterLoad: pauseAfterLoad, | |
| debug: debug, | |
| color: color, | |
| configurationPath: configurationPath, | |
| reporter: reporter, | |
| fileReporters: fileReporters, | |
| coverage: coverage, | |
| coverageLcov: coverageLcov, | |
| branchCoverage: branchCoverage, | |
| coveragePackages: coveragePackages, | |
| concurrency: concurrency, | |
| shardIndex: shardIndex, | |
| totalShards: totalShards, | |
| testSelections: testSelections, | |
| foldTraceExcept: foldTraceExcept, | |
| foldTraceOnly: foldTraceOnly, | |
| filename: filename, | |
| chosenPresets: chosenPresets, | |
| presets: presets, | |
| overrideRuntimes: overrideRuntimes, | |
| defineRuntimes: defineRuntimes, | |
| noRetry: noRetry, | |
| testRandomizeOrderingSeed: testRandomizeOrderingSeed, | |
| stopOnFirstFailure: stopOnFirstFailure, | |
| allowDuplicateTestNames: allowDuplicateTestNames, | |
| allowTestRandomization: allowTestRandomization, | |
| jsTrace: jsTrace, | |
| runSkipped: runSkipped, | |
| dart2jsArgs: dart2jsArgs, | |
| precompiledPath: precompiledPath, | |
| globalPatterns: globalPatterns, | |
| compilerSelections: compilerSelections, | |
| runtimes: runtimes, | |
| includeTags: includeTags, | |
| excludeTags: excludeTags, | |
| tags: tags, | |
| onPlatform: onPlatform, | |
| ignoreTimeouts: ignoreTimeouts, | |
| suiteLoadTimeout: suiteLoadTimeout, | |
| timeout: timeout, | |
| verboseTrace: verboseTrace, | |
| chainStackTraces: chainStackTraces, | |
| skip: skip, | |
| retry: retry, | |
| skipReason: skipReason, | |
| testOn: testOn, | |
| addTags: addTags, | |
| ); | |
| /// Suite level configuration allowed in the global test config file. | |
| /// | |
| /// This is per-user configuration and should be limited as such, it should | |
| /// not contain options that would change the pass/fail result of any given | |
| /// test, or change which tests would run. | |
| factory Configuration.globalTest({ | |
| required bool? verboseTrace, | |
| required bool? jsTrace, | |
| required Timeout? timeout, | |
| required Timeout? suiteLoadTimeout, | |
| required Map<String, Configuration>? presets, | |
| required bool? chainStackTraces, | |
| required Iterable<String>? foldTraceExcept, | |
| required Iterable<String>? foldTraceOnly, | |
| }) => Configuration( | |
| foldTraceExcept: foldTraceExcept, | |
| foldTraceOnly: foldTraceOnly, | |
| jsTrace: jsTrace, | |
| timeout: timeout, | |
| verboseTrace: verboseTrace, | |
| chainStackTraces: chainStackTraces, | |
| help: null, | |
| customHtmlTemplatePath: null, | |
| version: null, | |
| pauseAfterLoad: null, | |
| debug: null, | |
| color: null, | |
| configurationPath: null, | |
| reporter: null, | |
| fileReporters: null, | |
| coverage: null, | |
| coverageLcov: null, | |
| branchCoverage: null, | |
| coveragePackages: null, | |
| concurrency: null, | |
| shardIndex: null, | |
| totalShards: null, | |
| testSelections: null, | |
| filename: null, | |
| chosenPresets: null, | |
| presets: presets, | |
| overrideRuntimes: null, | |
| defineRuntimes: null, | |
| noRetry: null, | |
| testRandomizeOrderingSeed: null, | |
| stopOnFirstFailure: null, | |
| ignoreTimeouts: null, | |
| suiteLoadTimeout: suiteLoadTimeout, | |
| allowDuplicateTestNames: null, | |
| allowTestRandomization: null, | |
| runSkipped: null, | |
| dart2jsArgs: null, | |
| precompiledPath: null, | |
| globalPatterns: null, | |
| compilerSelections: null, | |
| runtimes: null, | |
| includeTags: null, | |
| excludeTags: null, | |
| tags: null, | |
| onPlatform: null, | |
| skip: null, | |
| retry: null, | |
| skipReason: null, | |
| testOn: null, | |
| addTags: null, | |
| ); | |
| /// Suite level configuration that is not allowed in the global test | |
| /// config file. | |
| /// | |
| /// This configuration may alter the pass/fail result of a test run, and thus | |
| /// should only be configured per package and not at the global level (global | |
| /// config is user specific). | |
| factory Configuration.localTest({ | |
| required bool? skip, | |
| required int? retry, | |
| required String? skipReason, | |
| required PlatformSelector? testOn, | |
| required Iterable<String>? addTags, | |
| required bool? allowDuplicateTestNames, | |
| required bool? allowTestRandomization, | |
| }) => Configuration( | |
| allowDuplicateTestNames: allowDuplicateTestNames, | |
| allowTestRandomization: allowTestRandomization, | |
| skip: skip, | |
| retry: retry, | |
| skipReason: skipReason, | |
| testOn: testOn, | |
| addTags: addTags, | |
| help: null, | |
| customHtmlTemplatePath: null, | |
| version: null, | |
| pauseAfterLoad: null, | |
| debug: null, | |
| color: null, | |
| configurationPath: null, | |
| reporter: null, | |
| fileReporters: null, | |
| coverage: null, | |
| coverageLcov: null, | |
| branchCoverage: null, | |
| coveragePackages: null, | |
| concurrency: null, | |
| shardIndex: null, | |
| totalShards: null, | |
| testSelections: null, | |
| foldTraceExcept: null, | |
| foldTraceOnly: null, | |
| filename: null, | |
| chosenPresets: null, | |
| presets: null, | |
| overrideRuntimes: null, | |
| defineRuntimes: null, | |
| noRetry: null, | |
| testRandomizeOrderingSeed: null, | |
| stopOnFirstFailure: null, | |
| jsTrace: null, | |
| runSkipped: null, | |
| dart2jsArgs: null, | |
| precompiledPath: null, | |
| globalPatterns: null, | |
| compilerSelections: null, | |
| runtimes: null, | |
| includeTags: null, | |
| excludeTags: null, | |
| tags: null, | |
| onPlatform: null, | |
| ignoreTimeouts: null, | |
| suiteLoadTimeout: null, | |
| timeout: null, | |
| verboseTrace: null, | |
| chainStackTraces: null, | |
| ); | |
| /// Runner configuration that is allowed in the global test config file. | |
| /// | |
| /// This is per-user configuration and should be limited as such, it should | |
| /// not contain options that would change the pass/fail result of any given | |
| /// test, or change which tests would run. | |
| /// | |
| /// Note that [customHtmlTemplatePath] violates this rule, and really should | |
| /// not be configurable globally. | |
| factory Configuration.globalRunner({ | |
| required bool? pauseAfterLoad, | |
| required String? customHtmlTemplatePath, | |
| required bool? runSkipped, | |
| required String? reporter, | |
| required Map<String, String>? fileReporters, | |
| required int? concurrency, | |
| required Iterable<CompilerSelection>? compilerSelections, | |
| required Iterable<RuntimeSelection>? runtimes, | |
| required Iterable<String>? chosenPresets, | |
| required Map<String, RuntimeSettings>? overrideRuntimes, | |
| }) => Configuration( | |
| customHtmlTemplatePath: customHtmlTemplatePath, | |
| pauseAfterLoad: pauseAfterLoad, | |
| runSkipped: runSkipped, | |
| reporter: reporter, | |
| fileReporters: fileReporters, | |
| concurrency: concurrency, | |
| compilerSelections: compilerSelections, | |
| runtimes: runtimes, | |
| chosenPresets: chosenPresets, | |
| overrideRuntimes: overrideRuntimes, | |
| help: null, | |
| version: null, | |
| debug: null, | |
| color: null, | |
| configurationPath: null, | |
| coverage: null, | |
| coverageLcov: null, | |
| branchCoverage: null, | |
| coveragePackages: null, | |
| shardIndex: null, | |
| totalShards: null, | |
| testSelections: null, | |
| foldTraceExcept: null, | |
| foldTraceOnly: null, | |
| filename: null, | |
| presets: null, | |
| defineRuntimes: null, | |
| noRetry: null, | |
| testRandomizeOrderingSeed: null, | |
| stopOnFirstFailure: null, | |
| allowDuplicateTestNames: null, | |
| allowTestRandomization: null, | |
| jsTrace: null, | |
| dart2jsArgs: null, | |
| precompiledPath: null, | |
| globalPatterns: null, | |
| includeTags: null, | |
| excludeTags: null, | |
| tags: null, | |
| onPlatform: null, | |
| ignoreTimeouts: null, | |
| suiteLoadTimeout: null, | |
| timeout: null, | |
| verboseTrace: null, | |
| chainStackTraces: null, | |
| skip: null, | |
| retry: null, | |
| skipReason: null, | |
| testOn: null, | |
| addTags: null, | |
| ); | |
| /// Runner configuration that is not allowed in the global test config file. | |
| /// | |
| /// This configuration may alter the pass/fail result of a test run, and thus | |
| /// should only be configured per package and not at the global level (global | |
| /// config is user specific). | |
| factory Configuration.localRunner({ | |
| required Iterable<Pattern>? globalPatterns, | |
| required Map<String, Set<TestSelection>>? testSelections, | |
| required Glob? filename, | |
| required BooleanSelector? includeTags, | |
| required BooleanSelector? excludeTags, | |
| required Map<String, CustomRuntime>? defineRuntimes, | |
| }) => Configuration( | |
| globalPatterns: globalPatterns, | |
| testSelections: testSelections, | |
| filename: filename, | |
| includeTags: includeTags, | |
| excludeTags: excludeTags, | |
| defineRuntimes: defineRuntimes, | |
| help: null, | |
| customHtmlTemplatePath: null, | |
| version: null, | |
| pauseAfterLoad: null, | |
| debug: null, | |
| color: null, | |
| configurationPath: null, | |
| reporter: null, | |
| fileReporters: null, | |
| coverage: null, | |
| coverageLcov: null, | |
| branchCoverage: null, | |
| coveragePackages: null, | |
| concurrency: null, | |
| shardIndex: null, | |
| totalShards: null, | |
| foldTraceExcept: null, | |
| foldTraceOnly: null, | |
| chosenPresets: null, | |
| presets: null, | |
| overrideRuntimes: null, | |
| noRetry: null, | |
| testRandomizeOrderingSeed: null, | |
| stopOnFirstFailure: null, | |
| allowDuplicateTestNames: null, | |
| allowTestRandomization: null, | |
| jsTrace: null, | |
| runSkipped: null, | |
| dart2jsArgs: null, | |
| precompiledPath: null, | |
| compilerSelections: null, | |
| runtimes: null, | |
| tags: null, | |
| onPlatform: null, | |
| ignoreTimeouts: null, | |
| suiteLoadTimeout: null, | |
| timeout: null, | |
| verboseTrace: null, | |
| chainStackTraces: null, | |
| skip: null, | |
| retry: null, | |
| skipReason: null, | |
| testOn: null, | |
| addTags: null, | |
| ); | |
| /// A specialized constructor for configuring only `onPlatform`. | |
| factory Configuration.onPlatform( | |
| Map<PlatformSelector, SuiteConfiguration> onPlatform, | |
| ) => Configuration._unsafe(onPlatform: onPlatform); | |
| factory Configuration.tags(Map<BooleanSelector, SuiteConfiguration> tags) => | |
| Configuration._unsafe(tags: tags); | |
| static Map<String, Configuration>? _withChosenPresets( | |
| Map<String, Configuration>? map, | |
| Set<String>? chosenPresets, | |
| ) { | |
| if (map == null || chosenPresets == null) return map; | |
| return map.map( | |
| (key, config) => MapEntry( | |
| key, | |
| config.change(chosenPresets: config.chosenPresets.union(chosenPresets)), | |
| ), | |
| ); | |
| } | |
| /// Creates new Configuration. | |
| /// | |
| /// Unlike [Configuration.new], this assumes [presets] is already resolved. | |
| Configuration._({ | |
| required bool? help, | |
| required this.customHtmlTemplatePath, | |
| required bool? version, | |
| required bool? pauseAfterLoad, | |
| required bool? debug, | |
| required bool? color, | |
| required String? configurationPath, | |
| required String? reporter, | |
| required Map<String, String>? fileReporters, | |
| required this.coverage, | |
| required this.coverageLcov, | |
| required bool? branchCoverage, | |
| required this.coveragePackages, | |
| required int? concurrency, | |
| required this.shardIndex, | |
| required this.totalShards, | |
| required Map<String, Set<TestSelection>>? testSelections, | |
| required Iterable<String>? foldTraceExcept, | |
| required Iterable<String>? foldTraceOnly, | |
| required Glob? filename, | |
| required Iterable<String>? chosenPresets, | |
| required Map<String, Configuration>? presets, | |
| required Map<String, RuntimeSettings>? overrideRuntimes, | |
| required Map<String, CustomRuntime>? defineRuntimes, | |
| required bool? noRetry, | |
| required this.testRandomizeOrderingSeed, | |
| required bool? stopOnFirstFailure, | |
| required BooleanSelector? includeTags, | |
| required BooleanSelector? excludeTags, | |
| required Iterable<Pattern>? globalPatterns, | |
| required SuiteConfiguration? suiteDefaults, | |
| }) : _help = help, | |
| _version = version, | |
| _pauseAfterLoad = pauseAfterLoad, | |
| _debug = debug, | |
| _color = color, | |
| _configurationPath = configurationPath, | |
| _reporter = reporter, | |
| fileReporters = fileReporters ?? {}, | |
| _branchCoverage = branchCoverage, | |
| _concurrency = concurrency, | |
| _testSelections = testSelections == null || testSelections.isEmpty | |
| ? null | |
| : Map.unmodifiable(testSelections), | |
| _foldTraceExcept = _set(foldTraceExcept), | |
| _foldTraceOnly = _set(foldTraceOnly), | |
| _filename = filename, | |
| chosenPresets = UnmodifiableSetView(chosenPresets?.toSet() ?? {}), | |
| presets = _map(presets), | |
| overrideRuntimes = _map(overrideRuntimes), | |
| defineRuntimes = _map(defineRuntimes), | |
| _noRetry = noRetry, | |
| includeTags = includeTags ?? BooleanSelector.all, | |
| excludeTags = excludeTags ?? BooleanSelector.none, | |
| globalPatterns = globalPatterns == null | |
| ? const {} | |
| : UnmodifiableSetView(globalPatterns.toSet()), | |
| _stopOnFirstFailure = stopOnFirstFailure, | |
| suiteDefaults = (() { | |
| var config = suiteDefaults ?? SuiteConfiguration.empty; | |
| if (pauseAfterLoad == true) { | |
| return config.change(ignoreTimeouts: true); | |
| } | |
| return config; | |
| }()) { | |
| if (_filename != null && _filename.context.style != p.style) { | |
| throw ArgumentError( | |
| "filename's context must match the current operating system, was " | |
| '${_filename.context.style}.', | |
| ); | |
| } | |
| if ((shardIndex == null) != (totalShards == null)) { | |
| throw ArgumentError( | |
| 'shardIndex and totalShards may only be passed together.', | |
| ); | |
| } else if (shardIndex != null) { | |
| RangeError.checkValueInInterval( | |
| shardIndex!, | |
| 0, | |
| totalShards! - 1, | |
| 'shardIndex', | |
| ); | |
| } | |
| } | |
| /// Creates a new [Configuration] that takes its configuration from | |
| /// [SuiteConfiguration]. | |
| factory Configuration.fromSuiteConfiguration( | |
| SuiteConfiguration suiteConfig, | |
| ) => Configuration._( | |
| suiteDefaults: suiteConfig, | |
| globalPatterns: null, | |
| help: null, | |
| customHtmlTemplatePath: null, | |
| version: null, | |
| pauseAfterLoad: null, | |
| debug: null, | |
| color: null, | |
| configurationPath: null, | |
| reporter: null, | |
| fileReporters: null, | |
| coverage: null, | |
| coverageLcov: null, | |
| branchCoverage: null, | |
| coveragePackages: null, | |
| concurrency: null, | |
| shardIndex: null, | |
| totalShards: null, | |
| testSelections: null, | |
| foldTraceExcept: null, | |
| foldTraceOnly: null, | |
| filename: null, | |
| chosenPresets: null, | |
| presets: null, | |
| overrideRuntimes: null, | |
| defineRuntimes: null, | |
| noRetry: null, | |
| testRandomizeOrderingSeed: null, | |
| stopOnFirstFailure: null, | |
| includeTags: null, | |
| excludeTags: null, | |
| ); | |
| /// Returns a set from [input]. | |
| /// | |
| /// If [input] is `null` or empty, this returns `null`. | |
| static Set<T>? _set<T>(Iterable<T>? input) { | |
| if (input == null) return null; | |
| var set = Set<T>.from(input); | |
| if (set.isEmpty) return null; | |
| return set; | |
| } | |
| /// Returns an unmodifiable copy of [input] or an empty unmodifiable map. | |
| static Map<K, V> _map<K, V>(Map<K, V>? input) { | |
| input ??= {}; | |
| return Map.unmodifiable(input); | |
| } | |
| /// Runs [body] with this as [Configuration.current]. | |
| /// | |
| /// This is zone-scoped, so this will be the current configuration in any | |
| /// asynchronous callbacks transitively created by [body]. | |
| T asCurrent<T>(T Function() body) => | |
| runZoned(body, zoneValues: {_currentKey: this}); | |
| /// Throws a [FormatException] if this refers to any undefined runtimes. | |
| void validateRuntimes(List<Runtime> allRuntimes) { | |
| // We don't need to verify [customRuntimes] here because those runtimes | |
| // already need to be verified and resolved to create [allRuntimes]. | |
| for (var settings in overrideRuntimes.values) { | |
| if (!allRuntimes.any( | |
| (runtime) => runtime.identifier == settings.identifier, | |
| )) { | |
| throw SourceSpanFormatException( | |
| 'Unknown platform "${settings.identifier}".', | |
| settings.identifierSpan, | |
| ); | |
| } | |
| } | |
| suiteDefaults.validateRuntimes(allRuntimes); | |
| for (var config in presets.values) { | |
| config.validateRuntimes(allRuntimes); | |
| } | |
| } | |
| /// Merges this with [other]. | |
| /// | |
| /// For most fields, if both configurations have values set, [other]'s value | |
| /// takes precedence. However, certain fields are merged together instead. | |
| /// This is indicated in those fields' documentation. | |
| Configuration merge(Configuration other) { | |
| if (this == Configuration.empty) return other; | |
| if (other == Configuration.empty) return this; | |
| var foldTraceOnly = other._foldTraceOnly ?? _foldTraceOnly; | |
| var foldTraceExcept = other._foldTraceExcept ?? _foldTraceExcept; | |
| if (_foldTraceOnly != null) { | |
| if (other._foldTraceExcept != null) { | |
| foldTraceOnly = _foldTraceOnly.difference(other._foldTraceExcept); | |
| } else if (other._foldTraceOnly != null) { | |
| foldTraceOnly = other._foldTraceOnly.intersection(_foldTraceOnly); | |
| } | |
| } else if (_foldTraceExcept != null) { | |
| if (other._foldTraceOnly != null) { | |
| foldTraceOnly = other._foldTraceOnly.difference(_foldTraceExcept); | |
| } else if (other._foldTraceExcept != null) { | |
| foldTraceExcept = other._foldTraceExcept.union(_foldTraceExcept); | |
| } | |
| } | |
| var result = Configuration._( | |
| help: other._help ?? _help, | |
| customHtmlTemplatePath: | |
| other.customHtmlTemplatePath ?? customHtmlTemplatePath, | |
| version: other._version ?? _version, | |
| pauseAfterLoad: other._pauseAfterLoad ?? _pauseAfterLoad, | |
| debug: other._debug ?? _debug, | |
| color: other._color ?? _color, | |
| configurationPath: other._configurationPath ?? _configurationPath, | |
| reporter: other._reporter ?? _reporter, | |
| fileReporters: mergeMaps(fileReporters, other.fileReporters), | |
| coverage: other.coverage ?? coverage, | |
| coverageLcov: other.coverageLcov ?? coverageLcov, | |
| branchCoverage: other._branchCoverage ?? _branchCoverage, | |
| coveragePackages: other.coveragePackages ?? coveragePackages, | |
| concurrency: other._concurrency ?? _concurrency, | |
| shardIndex: other.shardIndex ?? shardIndex, | |
| totalShards: other.totalShards ?? totalShards, | |
| testSelections: other._testSelections ?? _testSelections, | |
| foldTraceExcept: foldTraceExcept, | |
| foldTraceOnly: foldTraceOnly, | |
| filename: other._filename ?? _filename, | |
| chosenPresets: chosenPresets.union(other.chosenPresets), | |
| presets: _mergeConfigMaps(presets, other.presets), | |
| overrideRuntimes: mergeUnmodifiableMaps( | |
| overrideRuntimes, | |
| other.overrideRuntimes, | |
| value: (settings1, settings2) => RuntimeSettings( | |
| settings1.identifier, | |
| settings1.identifierSpan, | |
| [...settings1.settings, ...settings2.settings], | |
| ), | |
| ), | |
| defineRuntimes: mergeUnmodifiableMaps( | |
| defineRuntimes, | |
| other.defineRuntimes, | |
| ), | |
| noRetry: other._noRetry ?? _noRetry, | |
| testRandomizeOrderingSeed: | |
| other.testRandomizeOrderingSeed ?? testRandomizeOrderingSeed, | |
| stopOnFirstFailure: other._stopOnFirstFailure ?? _stopOnFirstFailure, | |
| includeTags: includeTags.intersection(other.includeTags), | |
| excludeTags: excludeTags.union(other.excludeTags), | |
| globalPatterns: globalPatterns.union(other.globalPatterns), | |
| suiteDefaults: suiteDefaults.merge(other.suiteDefaults), | |
| ); | |
| result = result._resolvePresets(); | |
| // Make sure the merged config preserves any presets that were chosen and | |
| // discarded. | |
| result._knownPresets = knownPresets.union(other.knownPresets); | |
| return result; | |
| } | |
| /// Returns a copy of this configuration with the given fields updated. | |
| /// | |
| /// Note that unlike [merge], this has no merging behavior—the old value is | |
| /// always replaced by the new one. | |
| Configuration change({ | |
| bool? help, | |
| String? customHtmlTemplatePath, | |
| bool? version, | |
| bool? pauseAfterLoad, | |
| bool? debug, | |
| bool? color, | |
| String? configurationPath, | |
| String? reporter, | |
| Map<String, String>? fileReporters, | |
| String? coverage, | |
| String? coverageLcov, | |
| bool? branchCoverage, | |
| List<RegExp>? coveragePackages, | |
| int? concurrency, | |
| int? shardIndex, | |
| int? totalShards, | |
| Map<String, Set<TestSelection>>? testSelections, | |
| Iterable<String>? exceptPackages, | |
| Iterable<String>? onlyPackages, | |
| Glob? filename, | |
| Iterable<String>? chosenPresets, | |
| Map<String, Configuration>? presets, | |
| Map<String, RuntimeSettings>? overrideRuntimes, | |
| Map<String, CustomRuntime>? defineRuntimes, | |
| bool? noRetry, | |
| int? testRandomizeOrderingSeed, | |
| bool? ignoreTimeouts, | |
| // Suite-level configuration | |
| bool? allowDuplicateTestNames, | |
| bool? jsTrace, | |
| bool? runSkipped, | |
| Iterable<String>? dart2jsArgs, | |
| String? precompiledPath, | |
| Iterable<RuntimeSelection>? runtimes, | |
| BooleanSelector? includeTags, | |
| BooleanSelector? excludeTags, | |
| Map<BooleanSelector, SuiteConfiguration>? tags, | |
| Map<PlatformSelector, SuiteConfiguration>? onPlatform, | |
| // Test-level configuration | |
| Timeout? timeout, | |
| bool? verboseTrace, | |
| bool? chainStackTraces, | |
| bool? skip, | |
| String? skipReason, | |
| PlatformSelector? testOn, | |
| Iterable<String>? addTags, | |
| }) { | |
| var config = Configuration._( | |
| help: help ?? _help, | |
| customHtmlTemplatePath: | |
| customHtmlTemplatePath ?? this.customHtmlTemplatePath, | |
| version: version ?? _version, | |
| pauseAfterLoad: pauseAfterLoad ?? _pauseAfterLoad, | |
| debug: debug ?? _debug, | |
| color: color ?? _color, | |
| configurationPath: configurationPath ?? _configurationPath, | |
| reporter: reporter ?? _reporter, | |
| fileReporters: fileReporters ?? this.fileReporters, | |
| coverage: coverage ?? this.coverage, | |
| coverageLcov: coverageLcov ?? this.coverageLcov, | |
| branchCoverage: branchCoverage ?? _branchCoverage, | |
| coveragePackages: coveragePackages ?? this.coveragePackages, | |
| concurrency: concurrency ?? _concurrency, | |
| shardIndex: shardIndex ?? this.shardIndex, | |
| totalShards: totalShards ?? this.totalShards, | |
| testSelections: testSelections ?? _testSelections, | |
| foldTraceExcept: exceptPackages ?? _foldTraceExcept, | |
| foldTraceOnly: onlyPackages ?? _foldTraceOnly, | |
| filename: filename ?? _filename, | |
| chosenPresets: chosenPresets ?? this.chosenPresets, | |
| presets: presets ?? this.presets, | |
| overrideRuntimes: overrideRuntimes ?? this.overrideRuntimes, | |
| defineRuntimes: defineRuntimes ?? this.defineRuntimes, | |
| noRetry: noRetry ?? _noRetry, | |
| testRandomizeOrderingSeed: | |
| testRandomizeOrderingSeed ?? this.testRandomizeOrderingSeed, | |
| stopOnFirstFailure: _stopOnFirstFailure, | |
| includeTags: includeTags, | |
| excludeTags: excludeTags, | |
| globalPatterns: globalPatterns, | |
| suiteDefaults: suiteDefaults.change( | |
| allowDuplicateTestNames: allowDuplicateTestNames, | |
| jsTrace: jsTrace, | |
| runSkipped: runSkipped, | |
| dart2jsArgs: dart2jsArgs, | |
| precompiledPath: precompiledPath, | |
| runtimes: runtimes, | |
| tags: tags, | |
| onPlatform: onPlatform, | |
| timeout: timeout, | |
| verboseTrace: verboseTrace, | |
| chainStackTraces: chainStackTraces, | |
| skip: skip, | |
| skipReason: skipReason, | |
| testOn: testOn, | |
| addTags: addTags, | |
| ignoreTimeouts: ignoreTimeouts, | |
| ), | |
| ); | |
| return config._resolvePresets(); | |
| } | |
| /// Merges two maps whose values are [Configuration]s. | |
| /// | |
| /// Any overlapping keys in the maps have their configurations merged in the | |
| /// returned map. | |
| Map<String, Configuration> _mergeConfigMaps( | |
| Map<String, Configuration> map1, | |
| Map<String, Configuration> map2, | |
| ) => mergeMaps( | |
| map1, | |
| map2, | |
| value: (config1, config2) => config1.merge(config2), | |
| ); | |
| /// Returns a copy of this [Configuration] with all [chosenPresets] resolved | |
| /// against [presets]. | |
| Configuration _resolvePresets() { | |
| if (chosenPresets.isEmpty || presets.isEmpty) return this; | |
| var newPresets = Map<String, Configuration>.from(presets); | |
| var merged = chosenPresets.fold( | |
| empty, | |
| (Configuration merged, preset) => | |
| merged.merge(newPresets.remove(preset) ?? Configuration.empty), | |
| ); | |
| if (merged == empty) return this; | |
| var result = change(presets: newPresets).merge(merged); | |
| // Make sure the configuration knows about presets that were selected and | |
| // thus removed from [newPresets]. | |
| result._knownPresets = UnmodifiableSetView({ | |
| ...result.knownPresets, | |
| ...presets.keys, | |
| }); | |
| return result; | |
| } | |
| } |