Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(75)

Side by Side Diff: pkg/front_end/test/src/incremental/hot_reload_e2e_test.dart

Issue 2925953002: Add integration test: hot reload + incremental compiler (Closed)
Patch Set: address CL comments Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 /// Integration test that runs the incremental compiler, runs the compiled
6 /// program, incrementally rebuild portions of the app, and triggers a hot
7 /// reload on the running program.
8 library front_end.incremental.hot_reload_e2e_test;
9
10 import 'dart:async';
11 import 'dart:convert';
12 import 'dart:io';
13
14 import 'package:front_end/compiler_options.dart';
15 import 'package:front_end/file_system.dart';
16 import 'package:front_end/incremental_kernel_generator.dart';
17 import 'package:front_end/memory_file_system.dart';
18 import 'package:front_end/src/incremental/byte_store.dart';
19 import 'package:front_end/src/testing/hybrid_file_system.dart';
20 import 'package:front_end/src/vm/reload.dart';
21 import 'package:kernel/ast.dart';
22 import 'package:kernel/binary/limited_ast_to_binary.dart';
23 import 'package:test/test.dart';
24
25 main() {
26 IncrementalKernelGenerator compiler;
27 MemoryFileSystem fs;
28 Directory outDir;
29 Uri outputUri;
30 List<Future<String>> lines;
31 Future programIsDone;
32
33 setUp(() async {
34 outDir = Directory.systemTemp.createTempSync('hotreload_test');
35 outputUri = outDir.uri.resolve('test.dill');
36 fs = new MemoryFileSystem(Uri.parse('file:///'));
37 writeFile(fs, 'a.dart', sourceA);
38 writeFile(fs, 'b.dart', sourceB);
39 writeFile(fs, '.packages', '');
40 compiler = await createIncrementalCompiler(
41 'file:///a.dart', new HybridFileSystem(fs));
42 await rebuild(compiler, outputUri); // this is a full compile.
43 });
44
45 tearDown(() async {
46 outDir.deleteSync(recursive: true);
47 lines = null;
48 });
49
50 /// Start the VM with the first version of the program compiled by the
51 /// incremental compiler.
52 startProgram(int reloadCount) async {
53 var vmArgs = [
54 '--enable-vm-service=0', // Note: use 0 to avoid port collisions.
55 '--platform=${platformFile.toFilePath()}',
56 outputUri.toFilePath()
57 ];
58 vmArgs.add('$reloadCount');
59 var vm = await Process.start(Platform.executable, vmArgs);
60 var splitter = new LineSplitter();
61
62 /// The program prints at most 2 + reloadCount lines:
63 /// - a line displaying the observatory port
64 /// - a line before waiting for a reload
65 /// - a line after each hot-reload
66 int i = 0;
67 int expectedLines = 2 + reloadCount;
68 var completers =
69 new List.generate(expectedLines, (_) => new Completer<String>());
70 lines = completers.map((c) => c.future).toList();
71 vm.stdout.transform(UTF8.decoder).transform(splitter).listen((line) {
72 expect(i, lessThan(expectedLines));
73 completers[i++].complete(line);
74 }, onDone: () {
75 expect(i, expectedLines);
76 });
77
78 vm.stderr.transform(UTF8.decoder).transform(splitter).toList().then((err) {
79 expect(err, isEmpty, reason: err.join('\n'));
80 });
81
82 programIsDone = vm.exitCode;
83 }
84
85 /// Request a hot reload on the running program.
86 Future hotReload() async {
87 var portLine = await lines[0];
88 expect(observatoryPortRegExp.hasMatch(portLine), isTrue);
89 var match = observatoryPortRegExp.firstMatch(portLine);
90 var port = int.parse(match.group(1));
91 var reloader = new VmReloader(port);
92 var reloadResult = await reloader.reload(outputUri);
93 expect(reloadResult['success'], isTrue);
94 await reloader.disconnect();
95 }
96
97 test('initial program is valid', () async {
98 await startProgram(0);
99 await programIsDone;
100 expect(await lines.skip(1).first, "part1 part2");
101 });
102
103 test('reload after leaf library modification', () async {
104 await startProgram(1);
105 expect(await lines[1], "part1 part2");
106
107 writeFile(fs, 'b.dart', sourceB.replaceAll("part1", "part3"));
108 await rebuild(compiler, outputUri);
109 await hotReload();
110 await programIsDone;
111 expect(await lines[2], "part3 part2");
112 });
113
114 test('reload after non-leaf library modification', () async {
115 await startProgram(1);
116 expect(await lines[1], "part1 part2");
117
118 writeFile(fs, 'a.dart', sourceA.replaceAll("part2", "part4"));
119 await rebuild(compiler, outputUri);
120 await hotReload();
121 await programIsDone;
122 expect(await lines[2], "part1 part4");
123 }, skip: true /* VM crashes on reload */);
124
125 test('reload after whole program modification', () async {
126 await startProgram(1);
127 expect(await lines[1], "part1 part2");
128
129 writeFile(fs, 'b.dart', sourceB.replaceAll("part1", "part5"));
130 writeFile(fs, 'a.dart', sourceA.replaceAll("part2", "part6"));
131 await rebuild(compiler, outputUri);
132 await hotReload();
133 await programIsDone;
134 expect(await lines[2], "part5 part6");
135 });
136
137 test('reload twice', () async {
138 await startProgram(2);
139 expect(await lines[1], "part1 part2");
140
141 writeFile(fs, 'b.dart', sourceB.replaceAll("part1", "part5"));
142 writeFile(fs, 'a.dart', sourceA.replaceAll("part2", "part6"));
143 await rebuild(compiler, outputUri);
144 await hotReload();
145 expect(await lines[2], "part5 part6");
146
147 writeFile(fs, 'b.dart', sourceB.replaceAll("part1", "part7"));
148 writeFile(fs, 'a.dart', sourceA.replaceAll("part2", "part8"));
149 await rebuild(compiler, outputUri);
150 await hotReload();
151 await programIsDone;
152 expect(await lines[3], "part7 part8");
153 });
154 }
155
156 var dartVm = Uri.base.resolve(Platform.resolvedExecutable);
157 var sdkRoot = dartVm.resolve("patched_sdk/");
158 var platformFile = sdkRoot.resolve('platform.dill');
159
160 Future<IncrementalKernelGenerator> createIncrementalCompiler(
161 String entry, FileSystem fs) {
162 var entryUri = Uri.base.resolve(entry);
163 var options = new CompilerOptions()
164 ..sdkRoot = sdkRoot
165 ..sdkSummary = sdkRoot.resolve('outline.dill')
166 ..packagesFileUri = Uri.parse('file:///.packages')
167 ..strongMode = false
168 ..dartLibraries = loadDartLibraries()
169 ..fileSystem = fs
170 ..byteStore = new MemoryByteStore();
171 return IncrementalKernelGenerator.newInstance(options, entryUri);
172 }
173
174 Map<String, Uri> loadDartLibraries() {
175 var libraries = sdkRoot.resolve('lib/libraries.json');
176 var map =
177 JSON.decode(new File.fromUri(libraries).readAsStringSync())['libraries'];
178 var dartLibraries = <String, Uri>{};
179 map.forEach((k, v) => dartLibraries[k] = libraries.resolve(v));
180 return dartLibraries;
181 }
182
183 Future<bool> rebuild(IncrementalKernelGenerator compiler, Uri outputUri) async {
184 compiler.invalidate(Uri.parse("file:///a.dart"));
185 compiler.invalidate(Uri.parse("file:///b.dart"));
186 var program = (await compiler.computeDelta()).newProgram;
187 if (program != null && !program.libraries.isEmpty) {
188 await writeProgram(program, outputUri);
189 return true;
190 }
191 return false;
192 }
193
194 Future<Null> writeProgram(Program program, Uri outputUri) async {
195 var sink = new File.fromUri(outputUri).openWrite();
196 // TODO(sigmund): the incremental generator should always filter these
197 // libraries instead.
198 new LimitedBinaryPrinter(
199 sink, (library) => library.importUri.scheme != 'dart')
200 .writeProgramFile(program);
201 await sink.close();
202 }
203
204 void writeFile(MemoryFileSystem fs, String fileName, String contents) {
205 fs.entityForUri(Uri.parse('file:///$fileName')).writeAsStringSync(contents);
206 }
207
208 /// This program calls a function periodically and tracks when the function
209 /// returns a different value than before (which only happens after a
210 /// hot-reload). The program exits after certain number of reloads, specified as
211 /// an argument to main.
212 const sourceA = r'''
213 import 'dart:async';
214 import 'b.dart';
215
216 void main(List<String> args) {
217 var last = f();
218 print(last);
219
220 // The argument indicates how many "visible" hot-reloads to run
221 int reloadCount = 0;
222 if (args.length > 0) {
223 reloadCount = int.parse(args[0]);
224 }
225 if (reloadCount == 0) return;
226
227 new Timer.periodic(new Duration(milliseconds: 100), (timer) {
228 var result = f();
229 if (last != result) {
230 print(result);
231 last = result;
232 if (--reloadCount == 0) timer.cancel();
233 }
234 });
235 }
236
237 f() => "$line part2";
238 ''';
239
240 const sourceB = r'''
241 get line => "part1";
242 ''';
243
244 RegExp observatoryPortRegExp =
245 new RegExp("Observatory listening on http://127.0.0.1:\([0-9]*\)/");
OLDNEW
« no previous file with comments | « pkg/front_end/lib/src/vm/reload.dart ('k') | pkg/front_end/test/subpackage_relationships_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698