Skip to content

Commit 40b596a

Browse files
authored
Support configuring the Node platform (flutter#703)
See flutter#391
1 parent 5670a12 commit 40b596a

File tree

3 files changed

+90
-24
lines changed

3 files changed

+90
-24
lines changed

doc/configuration.md

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ tags:
5454
* [`on_platform`](#on_platform)
5555
* [`override_platforms`](#override_platforms)
5656
* [`define_platforms`](#define_platforms)
57-
* [Browser Settings](#browser-settings)
57+
* [Browser/Node.js Settings](#browser-and-node-js-settings)
5858
* [`executable`](#executable)
5959
* [Configuration Presets](#configuration-presets)
6060
* [`presets`](#presets)
@@ -587,9 +587,9 @@ override_platforms:
587587
This tells the test runner to use the `chromium` executable for Chrome tests. It
588588
calls that executable with the same logic and flags it normally uses for Chrome.
589589

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.
590+
Each platform can define exactly which settings it supports. All browsers and
591+
Node.js support [the same settings](#browser-and-node-js-settings), but the VM
592+
doesn't support any settings and so can't be overridden.
593593

594594
### `define_platforms`
595595

@@ -620,24 +620,25 @@ might pass `testOn: "chromium"` to declare that a test is Chromium-specific.
620620
User-defined platforms also count as their parents, so Chromium will run tests
621621
that say `testOn: "chrome"` as well.
622622

623-
Each platform can define exactly which settings it supports. All browsers
624-
support [the same settings](#browser-settings), but the VM doesn't support any
625-
settings and so can't be extended.
623+
Each platform can define exactly which settings it supports. All browsers and
624+
Node.js support [the same settings](#browser-and-node-js-settings), but the VM
625+
doesn't support any settings and so can't be extended.
626626

627627
This field is not supported in the
628628
[global configuration file](#global-configuration).
629629

630-
### Browser Settings
630+
### Browser and Node.js Settings
631631

632-
All built-in browser platforms provide the same settings that can be set using
633-
[`define_platforms`](#define_platforms), which control how the browser
634-
executable is invoked.
632+
All built-in browser platforms, as well as the built-in Node.js platform,
633+
provide the same settings that can be set using
634+
[`define_platforms`](#define_platforms), which control how their executables are
635+
invoked.
635636

636637
#### `executable`
637638

638639
The `executable` field tells the test runner where to look for the executable to
639-
use to start the browser. It has three sub-keys, one for each supported operating
640-
system, which each take a path or an executable name:
640+
use to start the subprocess. It has three sub-keys, one for each supported
641+
operating system, which each take a path or an executable name:
641642

642643
```yaml
643644
define_platforms:

lib/src/runner/node/platform.dart

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,27 @@ import 'package:node_preamble/preamble.dart' as preamble;
1111
import 'package:package_resolver/package_resolver.dart';
1212
import 'package:path/path.dart' as p;
1313
import 'package:stream_channel/stream_channel.dart';
14+
import 'package:yaml/yaml.dart';
1415

1516
import '../../backend/test_platform.dart';
1617
import '../../util/io.dart';
1718
import '../../util/stack_trace_mapper.dart';
1819
import '../../utils.dart';
20+
import '../application_exception.dart';
1921
import '../compiler_pool.dart';
2022
import '../configuration.dart';
2123
import '../configuration/suite.dart';
24+
import '../executable_settings.dart';
2225
import '../load_exception.dart';
26+
import '../plugin/customizable_platform.dart';
2327
import '../plugin/environment.dart';
2428
import '../plugin/platform.dart';
2529
import '../plugin/platform_helpers.dart';
2630
import '../runner_suite.dart';
2731

2832
/// A platform that loads tests in Node.js processes.
29-
class NodePlatform extends PlatformPlugin {
33+
class NodePlatform extends PlatformPlugin
34+
implements CustomizablePlatform<ExecutableSettings> {
3035
/// The test runner configuration.
3136
final Configuration _config;
3237

@@ -39,22 +44,41 @@ class NodePlatform extends PlatformPlugin {
3944
/// The HTTP client to use when fetching JS files for `pub serve`.
4045
final HttpClient _http;
4146

42-
/// The Node executable to use.
43-
String get _executable => Platform.isWindows ? "node.exe" : "node";
47+
/// Executable settings for [TestPlatform.nodeJS] and platforms that extend
48+
/// it.
49+
final _settings = {
50+
TestPlatform.nodeJS: new ExecutableSettings(
51+
linuxExecutable: "node",
52+
macOSExecutable: "node",
53+
windowsExecutable: "node.exe")
54+
};
4455

4556
NodePlatform()
4657
: _config = Configuration.current,
4758
_http =
4859
Configuration.current.pubServeUrl == null ? null : new HttpClient();
4960

61+
ExecutableSettings parsePlatformSettings(YamlMap settings) =>
62+
new ExecutableSettings.parse(settings);
63+
64+
ExecutableSettings mergePlatformSettings(
65+
ExecutableSettings settings1, ExecutableSettings settings2) =>
66+
settings1.merge(settings2);
67+
68+
void customizePlatform(TestPlatform platform, ExecutableSettings settings) {
69+
var oldSettings = _settings[platform] ?? _settings[platform.root];
70+
if (oldSettings != null) settings = oldSettings.merge(settings);
71+
_settings[platform] = settings;
72+
}
73+
5074
StreamChannel loadChannel(String path, TestPlatform platform) =>
5175
throw new UnimplementedError();
5276

5377
Future<RunnerSuite> load(String path, TestPlatform platform,
5478
SuiteConfiguration suiteConfig, Object message) async {
5579
assert(platform == TestPlatform.nodeJS);
5680

57-
var pair = await _loadChannel(path, suiteConfig);
81+
var pair = await _loadChannel(path, platform, suiteConfig);
5882
var controller = await deserializeSuite(path, platform, suiteConfig,
5983
new PluginEnvironment(), pair.first, message,
6084
mapper: pair.last);
@@ -65,9 +89,9 @@ class NodePlatform extends PlatformPlugin {
6589
///
6690
/// Returns that channel along with a [StackTraceMapper] representing the
6791
/// source map for the compiled suite.
68-
Future<Pair<StreamChannel, StackTraceMapper>> _loadChannel(
69-
String path, SuiteConfiguration suiteConfig) async {
70-
var pair = await _spawnProcess(path, suiteConfig);
92+
Future<Pair<StreamChannel, StackTraceMapper>> _loadChannel(String path,
93+
TestPlatform platform, SuiteConfiguration suiteConfig) async {
94+
var pair = await _spawnProcess(path, platform, suiteConfig);
7195
var process = pair.first;
7296

7397
// Node normally doesn't emit any standard error, but if it does we forward
@@ -91,8 +115,8 @@ class NodePlatform extends PlatformPlugin {
91115
///
92116
/// Returns that channel along with a [StackTraceMapper] representing the
93117
/// source map for the compiled suite.
94-
Future<Pair<Process, StackTraceMapper>> _spawnProcess(
95-
String path, SuiteConfiguration suiteConfig) async {
118+
Future<Pair<Process, StackTraceMapper>> _spawnProcess(String path,
119+
TestPlatform platform, SuiteConfiguration suiteConfig) async {
96120
var dir = new Directory(_compiledDir).createTempSync('test_').path;
97121
var jsPath = p.join(dir, p.basename(path) + ".node_test.dart.js");
98122

@@ -122,7 +146,7 @@ class NodePlatform extends PlatformPlugin {
122146
sdkRoot: p.toUri(sdkDir));
123147
}
124148

125-
return new Pair(await Process.start(_executable, [jsPath]), mapper);
149+
return new Pair(await _startProcess(platform, jsPath), mapper);
126150
}
127151

128152
var url = _config.pubServeUrl.resolveUri(
@@ -141,7 +165,20 @@ class NodePlatform extends PlatformPlugin {
141165
sdkRoot: p.toUri('packages/\$sdk'));
142166
}
143167

144-
return new Pair(await Process.start(_executable, [jsPath]), mapper);
168+
return new Pair(await _startProcess(platform, jsPath), mapper);
169+
}
170+
171+
/// Starts the Node.js process for [platform] with [jsPath].
172+
Future<Process> _startProcess(TestPlatform platform, String jsPath) async {
173+
try {
174+
return await Process.start(_settings[platform].executable, [jsPath]);
175+
} catch (error, stackTrace) {
176+
await new Future.error(
177+
new ApplicationException(
178+
"Failed to run ${platform.name}: ${getErrorMessage(error)}"),
179+
stackTrace);
180+
return null;
181+
}
145182
}
146183

147184
/// Runs an HTTP GET on [url].

test/runner/configuration/custom_platform_test.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ void main() {
9898
}, tags: "chrome");
9999
});
100100

101+
test("can override Node.js without any changes", () async {
102+
await d.file("dart_test.yaml", """
103+
override_platforms:
104+
node:
105+
settings: {}
106+
""").create();
107+
108+
var test = await runTest(["-p", "node", "test.dart"]);
109+
expect(test.stdout, emitsThrough(contains("All tests passed!")));
110+
await test.shouldExit(0);
111+
}, tags: "node");
112+
101113
group("errors", () {
102114
test("rejects a non-map value", () async {
103115
await d.file("dart_test.yaml", "override_platforms: 12").create();
@@ -329,6 +341,22 @@ void main() {
329341
contains("Failed to run Chrome: $noSuchFileMessage")));
330342
await test.shouldExit(1);
331343
});
344+
345+
test("executable must exist for Node.js", () async {
346+
await d.file("dart_test.yaml", """
347+
override_platforms:
348+
node:
349+
settings:
350+
executable: _does_not_exist
351+
""").create();
352+
353+
var test = await runTest(["-p", "node", "test.dart"]);
354+
expect(
355+
test.stdout,
356+
emitsThrough(
357+
contains("Failed to run Node.js: $noSuchFileMessage")));
358+
await test.shouldExit(1);
359+
}, tags: "node");
332360
});
333361
});
334362
});

0 commit comments

Comments
 (0)