Skip to content

Commit 0d96eda

Browse files
committed
Add an override_platforms config field
See flutter#391
1 parent 465927c commit 0d96eda

File tree

11 files changed

+478
-27
lines changed

11 files changed

+478
-27
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## 0.12.25
2+
3+
* Add a `override_platforms` configuration field which allows test platforms'
4+
settings (such as browsers' executables) to be overridden by the user.
5+
6+
* Add a `define_platforms` configuration field which makes it possible to define
7+
new platforms that use the same logic as existing ones but have different
8+
settings.
9+
110
## 0.12.24+8
211

312
* `spawnHybridUri()` now interprets relative URIs correctly in browser tests.

doc/configuration.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ tags:
5252
* [Configuring Platforms](#configuring-platforms)
5353
* [`on_os`](#on_os)
5454
* [`on_platform`](#on_platform)
55-
* [`define_platform`](#define_platform)
55+
* [`override_platforms`](#override_platforms)
56+
* [`define_platforms`](#define_platforms)
5657
* [Browser Settings](#browser-settings)
5758
* [`executable`](#executable)
5859
* [Configuration Presets](#configuration-presets)
@@ -570,6 +571,26 @@ when running on a particular operating system, use [`on_os`](#on_os) instead.
570571

571572
This field counts as [test configuration](#test-configuration).
572573

574+
### `override_platforms`
575+
576+
This field allows you to customize the settings for built-in test platforms. It
577+
takes a map from platform identifiers to settings for those platforms. For example:
578+
579+
```yaml
580+
override_platforms:
581+
chrome:
582+
# The settings to override for this platform.
583+
settings:
584+
executable: chromium
585+
```
586+
587+
This tells the test runner to use the `chromium` executable for Chrome tests. It
588+
calls that executable with the same logic and flags it normally uses for Chrome.
589+
590+
Each platform can define exactly which settings it supports. All browsers
591+
support [the same settings](#browser-settings), but the VM doesn't support any
592+
settings and so can't be overridden.
593+
573594
### `define_platforms`
574595

575596
You can define new platforms in terms of old ones using the `define_platforms`

lib/src/runner/browser/platform.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ class BrowserPlatform extends PlatformPlugin
202202
ExecutableSettings parsePlatformSettings(YamlMap settings) =>
203203
new ExecutableSettings.parse(settings);
204204

205+
ExecutableSettings mergePlatformSettings(
206+
ExecutableSettings settings1, ExecutableSettings settings2) =>
207+
settings1.merge(settings2);
208+
205209
void customizePlatform(TestPlatform platform, ExecutableSettings settings) {
206210
var oldSettings =
207211
_browserSettings[platform] ?? _browserSettings[platform.root];

lib/src/runner/configuration.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:boolean_selector/boolean_selector.dart';
88
import 'package:collection/collection.dart';
99
import 'package:glob/glob.dart';
1010
import 'package:path/path.dart' as p;
11+
import 'package:source_span/source_span.dart';
1112

1213
import '../backend/platform_selector.dart';
1314
import '../backend/test_platform.dart';
@@ -18,6 +19,7 @@ import 'configuration/args.dart' as args;
1819
import 'configuration/custom_platform.dart';
1920
import 'configuration/load.dart';
2021
import 'configuration/platform_selection.dart';
22+
import 'configuration/platform_settings.dart';
2123
import 'configuration/reporters.dart';
2224
import 'configuration/suite.dart';
2325
import 'configuration/values.dart';
@@ -172,6 +174,9 @@ class Configuration {
172174

173175
Set<String> _knownPresets;
174176

177+
/// Built-in platforms whose settings are overridden by the user.
178+
final Map<String, PlatformSettings> overridePlatforms;
179+
175180
/// Platforms defined by the user in terms of existing platforms.
176181
final Map<String, CustomPlatform> definePlatforms;
177182

@@ -218,6 +223,7 @@ class Configuration {
218223
Glob filename,
219224
Iterable<String> chosenPresets,
220225
Map<String, Configuration> presets,
226+
Map<String, PlatformSettings> overridePlatforms,
221227
Map<String, CustomPlatform> definePlatforms,
222228
bool noRetry,
223229

@@ -261,6 +267,7 @@ class Configuration {
261267
filename: filename,
262268
chosenPresets: chosenPresetSet,
263269
presets: _withChosenPresets(presets, chosenPresetSet),
270+
overridePlatforms: overridePlatforms,
264271
definePlatforms: definePlatforms,
265272
noRetry: noRetry,
266273
suiteDefaults: new SuiteConfiguration(
@@ -316,6 +323,7 @@ class Configuration {
316323
Glob filename,
317324
Iterable<String> chosenPresets,
318325
Map<String, Configuration> presets,
326+
Map<String, PlatformSettings> overridePlatforms,
319327
Map<String, CustomPlatform> definePlatforms,
320328
bool noRetry,
321329
SuiteConfiguration suiteDefaults})
@@ -337,6 +345,7 @@ class Configuration {
337345
chosenPresets =
338346
new UnmodifiableSetView(chosenPresets?.toSet() ?? new Set()),
339347
presets = _map(presets),
348+
overridePlatforms = _map(overridePlatforms),
340349
definePlatforms = _map(definePlatforms),
341350
_noRetry = noRetry,
342351
suiteDefaults = pauseAfterLoad == true
@@ -399,6 +408,15 @@ class Configuration {
399408
// We don't need to verify [customPlatforms] here because those platforms
400409
// already need to be verified and resolved to create [allPlatforms].
401410

411+
for (var settings in overridePlatforms.values) {
412+
if (!allPlatforms
413+
.any((platform) => platform.identifier == settings.identifier)) {
414+
throw new SourceSpanFormatException(
415+
'Unknown platform "${settings.identifier}".',
416+
settings.identifierSpan);
417+
}
418+
}
419+
402420
suiteDefaults.validatePlatforms(allPlatforms);
403421
for (var config in presets.values) {
404422
config.validatePlatforms(allPlatforms);
@@ -448,6 +466,12 @@ class Configuration {
448466
filename: other._filename ?? _filename,
449467
chosenPresets: chosenPresets.union(other.chosenPresets),
450468
presets: _mergeConfigMaps(presets, other.presets),
469+
overridePlatforms: mergeUnmodifiableMaps(
470+
overridePlatforms, other.overridePlatforms,
471+
value: (settings1, settings2) => new PlatformSettings(
472+
settings1.identifier,
473+
settings1.identifierSpan,
474+
settings1.settings.toList()..addAll(settings2.settings))),
451475
definePlatforms:
452476
mergeUnmodifiableMaps(definePlatforms, other.definePlatforms),
453477
noRetry: other._noRetry ?? _noRetry,
@@ -482,6 +506,7 @@ class Configuration {
482506
Glob filename,
483507
Iterable<String> chosenPresets,
484508
Map<String, Configuration> presets,
509+
Map<String, PlatformSettings> overridePlatforms,
485510
Map<String, CustomPlatform> definePlatforms,
486511
bool noRetry,
487512

@@ -523,6 +548,7 @@ class Configuration {
523548
filename: filename ?? _filename,
524549
chosenPresets: chosenPresets ?? this.chosenPresets,
525550
presets: presets ?? this.presets,
551+
overridePlatforms: overridePlatforms ?? this.overridePlatforms,
526552
definePlatforms: definePlatforms ?? this.definePlatforms,
527553
noRetry: noRetry ?? _noRetry,
528554
suiteDefaults: suiteDefaults.change(

lib/src/runner/configuration/load.dart

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import '../configuration.dart';
2020
import '../configuration/suite.dart';
2121
import 'custom_platform.dart';
2222
import 'platform_selection.dart';
23+
import 'platform_settings.dart';
2324
import 'reporters.dart';
2425

2526
/// A regular expression matching a Dart identifier.
@@ -193,6 +194,7 @@ class _ConfigurationLoader {
193194
_disallow("plain_names");
194195
_disallow("platforms");
195196
_disallow("add_presets");
197+
_disallow("override_platforms");
196198
return Configuration.empty;
197199
}
198200

@@ -215,13 +217,41 @@ class _ConfigurationLoader {
215217
var chosenPresets = _getList("add_presets",
216218
(presetNode) => _parseIdentifierLike(presetNode, "Preset name"));
217219

220+
var overridePlatforms = _loadOverridePlatforms();
221+
218222
return new Configuration(
219223
pauseAfterLoad: pauseAfterLoad,
220224
runSkipped: runSkipped,
221225
reporter: reporter,
222226
concurrency: concurrency,
223227
platforms: platforms,
224-
chosenPresets: chosenPresets);
228+
chosenPresets: chosenPresets,
229+
overridePlatforms: overridePlatforms);
230+
}
231+
232+
/// Loads the `override_platforms` field.
233+
Map<String, PlatformSettings> _loadOverridePlatforms() {
234+
var platformsNode =
235+
_getNode("override_platforms", "map", (value) => value is Map)
236+
as YamlMap;
237+
if (platformsNode == null) return const {};
238+
239+
var platforms = <String, PlatformSettings>{};
240+
platformsNode.nodes.forEach((identifierNode, valueNode) {
241+
var identifier =
242+
_parseIdentifierLike(identifierNode, "Platform identifier");
243+
244+
_validate(valueNode, "Platform definition must be a map.",
245+
(value) => value is Map);
246+
var map = valueNode as YamlMap;
247+
248+
var settings = _expect(map, "settings");
249+
_validate(settings, "Must be a map.", (value) => value is Map);
250+
251+
platforms[identifier] = new PlatformSettings(
252+
identifier, identifierNode.span, [settings as YamlMap]);
253+
});
254+
return platforms;
225255
}
226256

227257
/// Loads runner configuration that's not allowed in the global configuration
@@ -316,6 +346,7 @@ class _ConfigurationLoader {
316346
});
317347
}
318348

349+
/// Loads the `define_platforms` field.
319350
Map<String, CustomPlatform> _loadDefinePlatforms() {
320351
var platformsNode =
321352
_getNode("define_platforms", "map", (value) => value is Map) as YamlMap;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright (c) 2017, 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 'package:source_span/source_span.dart';
6+
import 'package:yaml/yaml.dart';
7+
8+
import '../plugin/customizable_platform.dart';
9+
10+
/// User-defined settings for a built-in test platform.
11+
class PlatformSettings {
12+
/// The identifier used to look up the platform being overridden.
13+
final String identifier;
14+
15+
/// The location that [identifier] was defined in the configuration file.
16+
final SourceSpan identifierSpan;
17+
18+
/// The user's settings for this platform.
19+
///
20+
/// This is a list of settings, from most global to most specific, that will
21+
/// eventually be merged using [CustomizablePlatform.mergePlatformSettings].
22+
final List<YamlMap> settings;
23+
24+
PlatformSettings(this.identifier, this.identifierSpan, List<YamlMap> settings)
25+
: settings = new List.unmodifiable(settings);
26+
}

lib/src/runner/loader.dart

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ class Loader {
5353
/// their string identifiers.
5454
final _platformsByIdentifier = <String, TestPlatform>{};
5555

56-
/// The user-provided settings for platforms.
57-
final _platformSettings = <TestPlatform, YamlMap>{};
56+
/// The user-provided settings for platforms, as a list of settings that will
57+
/// be merged together using [CustomizablePlatform.mergePlatformSettings].
58+
final _platformSettings = <TestPlatform, List<YamlMap>>{};
5859

5960
/// The user-provided settings for platforms.
6061
final _parsedPlatformSettings = <TestPlatform, Object>{};
@@ -104,6 +105,8 @@ class Loader {
104105
_registerCustomPlatforms();
105106

106107
_config.validatePlatforms(allPlatforms);
108+
109+
_registerPlatformOverrides();
107110
}
108111

109112
/// Registers a [PlatformPlugin] for [platforms].
@@ -140,7 +143,21 @@ class Loader {
140143
_platformCallbacks[platform] = _platformCallbacks[parent];
141144
_platformsByIdentifier[platform.identifier] = platform;
142145

143-
_platformSettings[platform] = customPlatform.settings;
146+
_platformSettings[platform] = [customPlatform.settings];
147+
}
148+
}
149+
150+
/// Registers users' platform settings from [Configuration.overridePlatforms].
151+
void _registerPlatformOverrides() {
152+
for (var settings in _config.overridePlatforms.values) {
153+
var platform = _platformsByIdentifier[settings.identifier];
154+
155+
// This is officially validated in [Configuration.validatePlatforms].
156+
assert(platform != null);
157+
158+
_platformSettings
159+
.putIfAbsent(platform, () => [])
160+
.addAll(settings.settings);
144161
}
145162
}
146163

@@ -258,7 +275,9 @@ class Loader {
258275
if (settings == null) return;
259276

260277
if (plugin is CustomizablePlatform) {
261-
parsed = plugin.parsePlatformSettings(settings);
278+
parsed = settings
279+
.map(plugin.parsePlatformSettings)
280+
.reduce(plugin.mergePlatformSettings);
262281
plugin.customizePlatform(platform, parsed);
263282
_parsedPlatformSettings[platform] = parsed;
264283
} else {
@@ -268,9 +287,8 @@ class Loader {
268287
identifier = platform.parent.identifier;
269288
span = _config.definePlatforms[platform.identifier].parentSpan;
270289
} else {
271-
// TODO(nweiz): Get information from [_config.overridePlatforms] when
272-
// that exists.
273-
throw new UnimplementedError();
290+
identifier = platform.identifier;
291+
span = _config.overridePlatforms[platform.identifier].identifierSpan;
274292
}
275293

276294
throw new SourceSpanFormatException(

lib/src/runner/plugin/customizable_platform.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ abstract class CustomizablePlatform<T> extends PlatformPlugin {
3434
/// possible.
3535
T parsePlatformSettings(YamlMap settings);
3636

37+
/// Merges [settings1] with [settings2] and returns a new settings object that
38+
/// includes the configuration of both.
39+
///
40+
/// When the settings conflict, [settings2] should take priority.
41+
///
42+
/// This is used to merge global settings with local settings, or a custom
43+
/// platform's settings with its parent's.
44+
T mergePlatformSettings(T settings1, T settings2);
45+
3746
/// Defines user-provided [settings] for [platform].
3847
///
3948
/// The [platform] is a platform this plugin was declared to accept when

lib/src/utils.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,11 @@ List flatten(Iterable nested) {
199199
/// creating a new map unnecessarily.
200200
///
201201
/// The return value *may or may not* be unmodifiable.
202-
Map<K, V> mergeUnmodifiableMaps<K, V>(Map<K, V> map1, Map<K, V> map2) {
202+
Map<K, V> mergeUnmodifiableMaps<K, V>(Map<K, V> map1, Map<K, V> map2,
203+
{V value(V value1, V value2)}) {
203204
if (map1.isEmpty) return map2;
204205
if (map2.isEmpty) return map1;
205-
return mergeMaps(map1, map2);
206+
return mergeMaps(map1, map2, value: value);
206207
}
207208

208209
/// Truncates [text] to fit within [maxLength].

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: test
2-
version: 0.12.24+8
2+
version: 0.12.25-dev
33
author: Dart Team <[email protected]>
44
description: A library for writing dart unit tests.
55
homepage: https://github.com/dart-lang/test

0 commit comments

Comments
 (0)