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

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: 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(args) async {
scheglov 2017/06/07 00:27:23 Please type "args".
53 var vmArgs = [
54 '--enable-vm-service=0', // Note: use 0 to avoid port collsions.
scheglov 2017/06/07 00:27:23 collisions
55 '--platform=${platformFile.toFilePath()}',
56 outputUri.toFilePath()
57 ];
58 vmArgs.addAll(args);
59 var vm = await Process.start(Platform.executable, vmArgs);
60 var splitter = new LineSplitter();
61
62 /// All tests below expect at most 3 events: reading the observatory port,
63 /// the output before waiting for a reload, and the output after the reload.
64 /// Each is represented by a single line in the output.
65 int i = 0;
66 var completers = new List.generate(3, (_) => new Completer<String>());
67 lines = completers.map((c) => c.future).toList();
68 vm.stdout.transform(UTF8.decoder).transform(splitter).listen((line) {
69 expect(i, lessThan(3));
70 completers[i++].complete(line);
71 });
72
73 vm.stderr.transform(UTF8.decoder).transform(splitter).toList().then((err) {
74 expect(err, isEmpty, reason: err.join('\n'));
75 });
76
77 programIsDone = vm.exitCode;
78 }
79
80 /// Request a hot reload on the running program.
81 hotReload() async {
82 var portLine = await lines[0];
83 expect(observatoryPortRegExp.hasMatch(portLine), isTrue);
84 var match = observatoryPortRegExp.firstMatch(portLine);
85 var port = int.parse(match.group(1));
86 var reloader = new VmReloader(port);
87 var reloadResult = await reloader.reload(outputUri);
88 expect(reloadResult['success'], isTrue);
89 await reloader?.disconnect();
90 }
91
92 test('initial program is valid', () async {
93 await startProgram(["2"]);
scheglov 2017/06/07 00:27:23 What means ["2"] arguments here? Just a signal to
94 await programIsDone;
95 expect(await lines.skip(1).first, "part1 part2");
96 });
97
98 test('reload after leaf library modification', () async {
99 await startProgram([]);
100 expect(await lines[1], "part1 part2");
101
102 writeFile(fs, 'b.dart', sourceB.replaceAll("part1", "part3"));
103 await rebuild(compiler, outputUri);
104 await hotReload();
105 await programIsDone;
106 expect(await lines[2], "part3 part2");
107 });
108
109 test('reload after non-leaf library modification', () async {
110 await startProgram([]);
111 expect(await lines[1], "part1 part2");
112
113 writeFile(fs, 'a.dart', sourceA.replaceAll("part2", "part4"));
114 await rebuild(compiler, outputUri);
115 await hotReload();
116 await programIsDone;
117 expect(await lines[2], "part1 part4");
118 }, skip: true /* VM crashes on reload */);
Siggi Cherem (dart-lang) 2017/06/06 22:44:07 Siva: I forgot to mention, to reproduce the failur
119
120 test('reload after whole program modification', () async {
121 await startProgram([]);
122 expect(await lines[1], "part1 part2");
123
124 await hotReload();
125
126 writeFile(fs, 'b.dart', sourceB.replaceAll("part1", "part5"));
127 writeFile(fs, 'a.dart', sourceA.replaceAll("part2", "part6"));
128 await rebuild(compiler, outputUri);
129 await hotReload();
130 await programIsDone;
131 expect(await lines[2], "part5 part6");
132 });
scheglov 2017/06/07 00:27:23 It might be interesting to have a test with more t
133 }
134
135 var dartVm = Uri.base.resolve(Platform.resolvedExecutable);
136 var sdkRoot = dartVm.resolve("patched_sdk/");
137 var platformFile = sdkRoot.resolve('platform.dill');
138
139 Future<IncrementalKernelGenerator> createIncrementalCompiler(
140 String entry, FileSystem fs) {
141 var entryUri = Uri.base.resolve(entry);
142 var options = new CompilerOptions()
143 ..sdkRoot = sdkRoot
144 ..sdkSummary = sdkRoot.resolve('outline.dill')
145 ..packagesFileUri = Uri.parse('file:///.packages')
146 ..strongMode = false
147 ..dartLibraries = loadDartLibraries()
148 ..fileSystem = fs
149 ..byteStore = new MemoryByteStore();
150 return IncrementalKernelGenerator.newInstance(options, entryUri);
151 }
152
153 Map<String, Uri> loadDartLibraries() {
154 var libraries = sdkRoot.resolve('lib/libraries.json');
155 var map =
156 JSON.decode(new File.fromUri(libraries).readAsStringSync())['libraries'];
157 var dartLibraries = <String, Uri>{};
158 map.forEach((k, v) => dartLibraries[k] = libraries.resolve(v));
159 return dartLibraries;
160 }
161
162 Future<bool> rebuild(IncrementalKernelGenerator compiler, Uri outputUri) async {
163 compiler.invalidate(Uri.parse("file:///a.dart"));
164 compiler.invalidate(Uri.parse("file:///b.dart"));
165 var program = (await compiler.computeDelta()).newProgram;
166 if (program != null && !program.libraries.isEmpty) {
167 await writeProgram(program, outputUri);
168 return true;
169 }
170 return false;
171 }
172
173 writeProgram(Program program, Uri outputUri) async {
174 // TODO(sigmund): the incremental generator should set the main method
175 // directly.
176 var mainLib = program.libraries.last;
177 program.mainMethod =
178 mainLib.procedures.firstWhere((p) => p.name.name == 'main');
179 var sink = new File.fromUri(outputUri).openWrite();
scheglov 2017/06/07 00:27:23 I can do this.
180
181 // TODO(sigmund): the incremental generator should always filter these
182 // libraries instead.
scheglov 2017/06/07 00:27:23 Is it a requirement for any kernel generation or f
183 new LimitedBinaryPrinter(
184 sink, (library) => library.importUri.scheme != 'dart')
185 .writeProgramFile(program);
186 await sink.close();
187 }
188
189 writeFile(MemoryFileSystem fs, String fileName, String contents) {
scheglov 2017/06/07 00:27:23 void?
190 fs.entityForUri(Uri.parse('file:///$fileName')).writeAsStringSync(contents);
191 }
192
193 /// This program calls a function periodically and exits when the function
194 /// returns a different value than before (which only happens after a
195 /// hot-reload).
196 const sourceA = r'''
197 import 'dart:async';
198 import 'b.dart';
199
200 main(args) {
201 var last = f();
202 print(last);
203
204 // Use any arguments to indicate that we don't want to wait for a
205 // hot-reload request.
206 if (args.length > 0) return;
207
208 new Timer.periodic(new Duration(milliseconds: 100), (timer) {
209 var result = f();
210 if (last != result) {
211 print(result);
212 timer.cancel();
213 }
214 });
215 }
216
217 f() => "$line part2";
218 ''';
219
220 const sourceB = r'''
221 get line => "part1";
222 ''';
223
224 RegExp observatoryPortRegExp =
225 new RegExp("Observatory listening on http://127.0.0.1:\([0-9]*\)/");
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698