Skip to content

Commit 3d7f546

Browse files
authored
Add Webdev installation test (#1935)
1 parent 9297c66 commit 3d7f546

File tree

1 file changed

+201
-0
lines changed

1 file changed

+201
-0
lines changed

webdev/test/installation_test.dart

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// Copyright (c) 2023, 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+
@Timeout(Duration(seconds: 90))
6+
import 'dart:async';
7+
import 'dart:convert';
8+
import 'dart:io';
9+
10+
import 'package:path/path.dart' as p;
11+
import 'package:test/test.dart';
12+
13+
// TODO(elliette): Move to a cron job. This tests that the currently published
14+
// Webdev can be activated and serve a web app. It is intended to catch any
15+
// regressions due to changes in the Dart SDK.
16+
17+
enum StreamType {
18+
stdout,
19+
stderr,
20+
}
21+
22+
const processTimeout = Duration(seconds: 30);
23+
24+
void main() {
25+
Process? _createProcess;
26+
Process? _activateProcess;
27+
Process? _serveProcess;
28+
Directory? _tempDir;
29+
30+
Future<void> _expectStdoutAndCleanExit(Process process,
31+
{required String expectedStdout}) async {
32+
final stdoutCompleter = _captureOutput(
33+
process,
34+
streamType: StreamType.stdout,
35+
stopCaptureFuture: process.exitCode,
36+
);
37+
final stderrCompleter = _captureOutput(
38+
process,
39+
streamType: StreamType.stderr,
40+
stopCaptureFuture: process.exitCode,
41+
);
42+
final exitCode = await _waitForExitOrTimeout(process);
43+
final stderrLogs = await stderrCompleter.future;
44+
final stdoutLogs = await stdoutCompleter.future;
45+
expect(
46+
exitCode,
47+
equals(0),
48+
// Include the stderr and stdout logs if the process does not terminate
49+
// cleanly:
50+
reason: 'stderr: $stderrLogs, stdout: $stdoutLogs',
51+
);
52+
expect(
53+
stderrLogs,
54+
isEmpty,
55+
);
56+
expect(
57+
stdoutLogs,
58+
contains(expectedStdout),
59+
);
60+
}
61+
62+
Future<void> _expectStdoutThenExit(Process process,
63+
{required String expectedStdout}) async {
64+
final expectedStdoutCompleter = _waitForStdoutOrTimeout(
65+
process,
66+
expectedStdout: expectedStdout,
67+
);
68+
final stderrCompleter = _captureOutput(
69+
process,
70+
streamType: StreamType.stderr,
71+
stopCaptureFuture: expectedStdoutCompleter.future,
72+
);
73+
final stdoutLogs = await expectedStdoutCompleter.future;
74+
final stderrLogs = await stderrCompleter.future;
75+
expect(
76+
stdoutLogs, contains(expectedStdout),
77+
// Also include the stderr if the stdout is not expected.
78+
reason: 'stderr: $stderrLogs',
79+
);
80+
}
81+
82+
setUp(() async {
83+
_tempDir = Directory.systemTemp.createTempSync('installation_test');
84+
85+
await Process.run(
86+
'dart',
87+
['pub', 'global', 'deactivate', 'webdev'],
88+
);
89+
});
90+
91+
tearDown(() async {
92+
// Kill any stale processes:
93+
if (_createProcess != null) {
94+
Process.killPid(_createProcess!.pid, ProcessSignal.sigint);
95+
_createProcess = null;
96+
}
97+
if (_activateProcess != null) {
98+
Process.killPid(_activateProcess!.pid, ProcessSignal.sigint);
99+
_activateProcess = null;
100+
}
101+
if (_serveProcess != null) {
102+
Process.killPid(_serveProcess!.pid, ProcessSignal.sigint);
103+
_serveProcess = null;
104+
}
105+
});
106+
107+
test('can activate and serve webdev', () async {
108+
final tempDir = _tempDir!;
109+
final tempPath = tempDir.path;
110+
111+
// Verify that we can create a new Dart app:
112+
_createProcess = await Process.start(
113+
'dart',
114+
['create', '--template', 'web', 'temp_app'],
115+
workingDirectory: tempPath,
116+
);
117+
await _expectStdoutAndCleanExit(
118+
_createProcess!,
119+
expectedStdout: 'Created project temp_app in temp_app!',
120+
);
121+
final appPath = p.join(tempPath, 'temp_app');
122+
expect(await Directory(appPath).exists(), isTrue);
123+
124+
// Verify that `dart pub global activate` works:
125+
_activateProcess = await Process.start(
126+
'dart',
127+
['pub', 'global', 'activate', 'webdev'],
128+
);
129+
await _expectStdoutAndCleanExit(
130+
_activateProcess!,
131+
expectedStdout: 'Activated webdev',
132+
);
133+
134+
// Verify that `webdev serve` works for our new app:
135+
_serveProcess = await Process.start(
136+
'dart', ['pub', 'global', 'run', 'webdev', 'serve'],
137+
workingDirectory: appPath);
138+
await _expectStdoutThenExit(_serveProcess!,
139+
expectedStdout: 'Serving `web` on');
140+
});
141+
}
142+
143+
Future<int> _waitForExitOrTimeout(Process process) {
144+
Timer(processTimeout, () {
145+
process.kill(ProcessSignal.sigint);
146+
});
147+
return process.exitCode;
148+
}
149+
150+
/// Returns the stdout for the [process] once the [expectedStdout] is found.
151+
///
152+
/// Otherwise returns all the stdout up to the [processTimeout].
153+
Completer<String> _waitForStdoutOrTimeout(Process process,
154+
{required String expectedStdout}) {
155+
var output = '';
156+
final completer = Completer<String>();
157+
158+
Timer(processTimeout, () {
159+
process.kill(ProcessSignal.sigint);
160+
if (!completer.isCompleted) {
161+
completer.complete(output);
162+
}
163+
});
164+
process.stdout.transform(utf8.decoder).listen((line) {
165+
output += line;
166+
if (line.contains(expectedStdout)) {
167+
process.kill(ProcessSignal.sigint);
168+
if (!completer.isCompleted) {
169+
completer.complete(output);
170+
}
171+
}
172+
});
173+
174+
return completer;
175+
}
176+
177+
Completer<String> _captureOutput(
178+
Process process, {
179+
required StreamType streamType,
180+
required Future stopCaptureFuture,
181+
}) {
182+
final stream =
183+
streamType == StreamType.stdout ? process.stdout : process.stderr;
184+
final completer = Completer<String>();
185+
var output = '';
186+
stream.transform(utf8.decoder).listen((line) {
187+
output += line;
188+
if (line.contains('[SEVERE]')) {
189+
process.kill(ProcessSignal.sigint);
190+
if (!completer.isCompleted) {
191+
completer.complete(output);
192+
}
193+
}
194+
});
195+
unawaited(stopCaptureFuture.then((_) {
196+
if (!completer.isCompleted) {
197+
completer.complete(output);
198+
}
199+
}));
200+
return completer;
201+
}

0 commit comments

Comments
 (0)