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

Side by Side Diff: tests/fletchc/incremental/feature_test.dart

Issue 1659163007: Rename fletch -> dartino (Closed) Base URL: https://github.com/dartino/sdk.git@master
Patch Set: address comments Created 4 years, 10 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) 2014, 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 library fletchc.test.feature_test;
6
7 import 'dart:io' hide
8 exitCode,
9 stderr,
10 stdin,
11 stdout;
12
13 import 'dart:io' as io;
14
15 import 'dart:async' show
16 Completer,
17 Future,
18 Stream,
19 StreamController,
20 StreamIterator;
21
22 import 'dart:convert' show
23 LineSplitter,
24 UTF8,
25 Utf8Decoder;
26
27 import 'package:expect/expect.dart' show
28 Expect;
29
30 import 'package:fletchc/incremental/scope_information_visitor.dart' show
31 ScopeInformationVisitor;
32
33 import 'compiler_test_case.dart' show
34 CompilerTestCase;
35
36 import 'package:compiler/src/elements/elements.dart' show
37 AbstractFieldElement,
38 Element,
39 FieldElement,
40 FunctionElement,
41 LibraryElement;
42
43 import 'package:compiler/src/compiler.dart' show
44 Compiler;
45
46 import 'package:compiler/src/source_file_provider.dart' show
47 FormattingDiagnosticHandler,
48 SourceFileProvider;
49
50 import 'package:compiler/src/io/source_file.dart' show
51 StringSourceFile;
52
53 import 'package:fletchc/incremental/fletchc_incremental.dart' show
54 IncrementalCompilationFailed,
55 IncrementalCompiler,
56 IncrementalMode;
57
58 import 'package:fletchc/vm_commands.dart' show
59 VmCommand,
60 CommitChanges,
61 CommitChangesResult,
62 HandShakeResult,
63 MapId;
64
65 import 'package:fletchc/fletch_compiler.dart' show
66 FletchCompiler;
67
68 import 'package:fletchc/src/fletch_compiler_implementation.dart' show
69 OutputProvider;
70
71 import 'package:fletchc/src/guess_configuration.dart' show
72 fletchVersion,
73 guessFletchVm;
74
75 import 'package:fletchc/fletch_system.dart';
76
77 import 'package:fletchc/vm_commands.dart' as commands_lib;
78
79 import 'package:fletchc/vm_session.dart' show
80 Session;
81
82 import 'package:fletchc/src/fletch_backend.dart' show
83 FletchBackend;
84
85 import 'package:fletchc/fletch_vm.dart' show
86 FletchVm;
87
88 import 'package:fletchc/debug_state.dart' as debug show
89 BackTraceFrame,
90 BackTrace;
91
92 import 'program_result.dart';
93
94 import 'package:fletchc/src/hub/exit_codes.dart' show
95 DART_VM_EXITCODE_COMPILE_TIME_ERROR;
96
97 const String PACKAGE_SCHEME = 'org.dartlang.fletch.packages';
98
99 const String CUSTOM_SCHEME = 'org.dartlang.fletch.test-case';
100
101 final Uri customUriBase = new Uri(scheme: CUSTOM_SCHEME, path: '/');
102
103 typedef Future NoArgFuture();
104
105 compileAndRun(
106 String testName,
107 EncodedResult encodedResult,
108 {IncrementalMode incrementalMode}) async {
109 print("Test '$testName'");
110 IncrementalTestHelper helper = new IncrementalTestHelper(incrementalMode);
111 TestSession session =
112 await TestSession.spawnVm(helper.compiler, testName: testName);
113
114 bool hasCompileTimeError = false;
115
116 await new Future(() async {
117
118 int version = 0;
119
120 for (ProgramResult program in encodedResult.decode()) {
121 bool isFirstProgram = version == 0;
122 version++;
123
124 if (!program.compileUpdatesShouldThrow) {
125 // We should only update the status of hasCompileTimeError when we run
126 // something on the VM.
127 hasCompileTimeError = program.hasCompileTimeError;
128 }
129
130 print("Program version $version #$testName:");
131 print(numberedLines(program.code));
132
133 bool compileUpdatesThrew = true;
134 FletchDelta fletchDelta;
135 if (isFirstProgram) {
136 // The first program is compiled "fully".
137 fletchDelta = await helper.fullCompile(program);
138 compileUpdatesThrew = false;
139 } else {
140 // An update to the first program, all updates are compiled as
141 // incremental updates to the first program.
142 try {
143 fletchDelta = await helper.incrementalCompile(program, version);
144 compileUpdatesThrew = false;
145 } on IncrementalCompilationFailed catch (error) {
146 if (program.compileUpdatesShouldThrow) {
147 print("Expected error in compileUpdates: $error");
148 } else {
149 print("Unexpected error in compileUpdates.");
150 rethrow;
151 }
152 }
153 }
154
155 FletchBackend backend = helper.compiler.compiler.context.backend;
156
157 if (program.compileUpdatesShouldThrow) {
158 Expect.isFalse(isFirstProgram);
159 Expect.isTrue(
160 compileUpdatesThrew,
161 "Expected an exception in compileUpdates");
162 Expect.isNull(fletchDelta, "Expected update == null");
163 break;
164 }
165
166 if (!isFirstProgram ||
167 const bool.fromEnvironment("feature_test.print_initial_commands")) {
168 for (VmCommand command in fletchDelta.commands) {
169 print(command);
170 }
171 }
172
173 if (isFirstProgram) {
174 // Perform handshake with VM.
175 HandShakeResult handShakeResult =
176 await session.handShake(fletchVersion);
177 Expect.isTrue(handShakeResult.success, "Fletch VM version mismatch");
178 }
179
180 CommitChangesResult result = await session.applyDelta(fletchDelta);
181
182 if (!result.successful) {
183 print("The CommitChanges() command was not successful: "
184 "${result.message}");
185 }
186
187 Expect.equals(result.successful, !program.commitChangesShouldFail,
188 result.message);
189
190 if (isFirstProgram) {
191 // Turn on debugging.
192 await session.enableDebugger();
193 // Spawn the process to run.
194 await session.spawnProcess();
195 // Allow operations on internal frames.
196 await session.toggleInternal();
197 }
198
199 if (result.successful) {
200 // Set breakpoint in main in case main was replaced.
201 var breakpoints =
202 await session.setBreakpoint(methodName: "main", bytecodeIndex: 0);
203 for (var breakpoint in breakpoints) {
204 print("Added breakpoint: $breakpoint");
205 }
206 if (!helper.compiler.compiler.mainFunction.isMalformed) {
207 // If there's a syntax error in main, we cannot find it to set a
208 // breakpoint.
209 // TODO(ahe): Consider if this is a problem?
210 Expect.equals(1, breakpoints.length);
211 }
212 if (isFirstProgram) {
213 // Run the program to hit the breakpoint in main.
214 await session.debugRun();
215 } else {
216 // Restart the current frame to rerun main.
217 await session.restart();
218 }
219 if (session.running) {
220 // Step out of main to finish execution of main.
221 await session.stepOut();
222 }
223
224 // Select the stack frame of callMain.
225 debug.BackTrace trace = await session.backTrace();
226 FunctionElement callMainElement =
227 backend.fletchSystemLibrary.findLocal("callMain");
228 FletchFunction callMain =
229 helper.system.lookupFunctionByElement(callMainElement);
230 debug.BackTraceFrame mainFrame =
231 trace.frames.firstWhere(
232 (debug.BackTraceFrame frame) => frame.function == callMain);
233 int frame = trace.frames.indexOf(mainFrame);
234 Expect.notEquals(1, frame);
235 session.selectFrame(frame);
236 print(trace.format());
237
238 List<String> actualMessages = session.stdoutSink.takeLines();
239
240 List<String> messages = new List<String>.from(program.messages);
241 if (program.hasCompileTimeError) {
242 print("Compile-time error expected");
243 // TODO(ahe): The compile-time error message shouldn't be printed by
244 // the Fletch VM.
245
246 // Find the compile-time error message in the actual output, and
247 // remove all lines after it.
248 int compileTimeErrorIndex = -1;
249 for (int i = 0; i < actualMessages.length; i++) {
250 if (actualMessages[i].startsWith("Compile error:")) {
251 compileTimeErrorIndex = i;
252 break;
253 }
254 }
255 Expect.isTrue(compileTimeErrorIndex != -1);
256 actualMessages.removeRange(compileTimeErrorIndex,
257 actualMessages.length);
258 }
259
260 Expect.listEquals(messages, actualMessages,
261 "Expected $messages, got $actualMessages");
262
263 // TODO(ahe): Enable SerializeScopeTestCase for multiple parts.
264 if (!isFirstProgram && program.code is String) {
265 await new SerializeScopeTestCase(
266 program.code, helper.compiler.mainApp,
267 helper.compiler.compiler).run();
268 }
269 }
270 }
271
272 // If everything went fine, we will try finishing the execution and do a
273 // graceful shutdown.
274 if (session.running) {
275 // The session is still alive. Run to completion.
276 var continueCommand = const commands_lib.ProcessContinue();
277 print(continueCommand);
278
279 // Wait for process termination.
280 VmCommand response = await session.runCommand(continueCommand);
281 if (response is! commands_lib.ProcessTerminated) {
282 // TODO(ahe): It's probably an instance of
283 // commands_lib.UncaughtException, and if so, we should try to print
284 // the stack trace.
285 throw new StateError(
286 "Expected ProcessTerminated, but got: $response");
287 }
288 }
289 await session.runCommand(const commands_lib.SessionEnd());
290 await session.shutdown();
291 }).catchError(session.handleError);
292
293 // TODO(ahe/kustermann/ager): We really need to distinguish VM crashes from
294 // normal test failures. This information is based on exitCode and we need
295 // to propagate the exitCode back to test.dart, so we can have Fail/Crash
296 // outcomes of these tests.
297 await session.waitForCompletion();
298
299 int actualExitCode = await session.exitCode;
300 // TODO(ahe): We should expect exit code 0, and instead be able to detect
301 // compile-time errors directly via the session.
302 int expectedExitCode = hasCompileTimeError
303 ? DART_VM_EXITCODE_COMPILE_TIME_ERROR : 0;
304 Expect.equals(
305 expectedExitCode, actualExitCode, "Unexpected exit code from fletch VM");
306 }
307
308 class SerializeScopeTestCase extends CompilerTestCase {
309 final String source;
310
311 final String scopeInfo;
312
313 final Compiler compiler = null; // TODO(ahe): Provide a compiler.
314
315 SerializeScopeTestCase(
316 this.source,
317 LibraryElement library,
318 Compiler compiler)
319 : scopeInfo = computeScopeInfo(compiler, library),
320 super(library.canonicalUri);
321
322 Future run() {
323 if (true) {
324 // TODO(ahe): Remove this. We're temporarily bypassing scope validation.
325 return new Future.value(null);
326 }
327 return loadMainApp().then(checkScopes);
328 }
329
330 void checkScopes(LibraryElement library) {
331 var compiler = null;
332 Expect.stringEquals(computeScopeInfo(compiler, library), scopeInfo);
333 }
334
335 Future<LibraryElement> loadMainApp() async {
336 LibraryElement library =
337 await compiler.libraryLoader.loadLibrary(scriptUri);
338 if (compiler.mainApp == null) {
339 compiler.mainApp = library;
340 } else if (compiler.mainApp != library) {
341 throw "Inconsistent use of compiler (${compiler.mainApp} != $library).";
342 }
343 return library;
344 }
345
346 static String computeScopeInfo(Compiler compiler, LibraryElement library) {
347 ScopeInformationVisitor visitor =
348 new ScopeInformationVisitor(compiler, library, 0);
349
350 visitor.ignoreImports = true;
351 visitor.sortMembers = true;
352 visitor.indented.write('[\n');
353 visitor.indentationLevel++;
354 visitor.indented;
355 library.accept(visitor, null);
356 library.forEachLocalMember((Element member) {
357 if (member.isClass) {
358 visitor.buffer.write(',\n');
359 visitor.indented;
360 member.accept(visitor, null);
361 }
362 });
363 visitor.buffer.write('\n');
364 visitor.indentationLevel--;
365 visitor.indented.write(']');
366 return '${visitor.buffer}';
367 }
368 }
369
370 void logger(x) {
371 print(x);
372 }
373
374 String numberedLines(code) {
375 if (code is! Map) {
376 code = {'main.dart': code};
377 }
378 StringBuffer result = new StringBuffer();
379 code.forEach((String fileName, String code) {
380 result.writeln("==> $fileName <==");
381 int lineNumber = 1;
382 for (String text in splitLines(code)) {
383 result.write("$lineNumber: $text");
384 lineNumber++;
385 }
386 });
387 return '$result';
388 }
389
390 List<String> splitLines(String text) {
391 return text.split(new RegExp('^', multiLine: true));
392 }
393
394 class TestSession extends Session {
395 final Process process;
396 final StreamIterator stdoutIterator;
397 final Stream<String> stderr;
398
399 final List<Future> futures;
400
401 final Future<int> exitCode;
402
403 bool isWaitingForCompletion = false;
404
405 TestSession(
406 Socket vmSocket,
407 IncrementalCompiler compiler,
408 this.process,
409 this.stdoutIterator,
410 this.stderr,
411 this.futures,
412 this.exitCode)
413 : super(vmSocket, compiler, new BytesSink(), null);
414
415 // Refines type of [stdoutSink].
416 BytesSink get stdoutSink => super.stdoutSink;
417
418 void writeStdout(String s) {
419 // Unfortunately, print will always add a newline, and the alternative is
420 // to use stdout.write. However, to make it easier to debug problems in
421 // this and other fletch_tests, everything that is printed to stdout ends
422 // up on the console of test.dart. This is good enough for testing, but DO
423 // NOT COPY TO PRODUCTION CODE.
424 print(s);
425 }
426
427 void writeStdoutLine(String s) {
428 print(s);
429 }
430
431 /// Add [future] to this session. All futures that can fail after calling
432 /// [waitForCompletion] must be added to the session.
433 void recordFuture(Future future) {
434 futures.add(convertErrorToString(future));
435 }
436
437 void addError(error, StackTrace stackTrace) {
438 recordFuture(new Future.error(error, stackTrace));
439 }
440
441 /// Waits for the VM to shutdown and any futures added with [add] to
442 /// complete, and report all errors that occurred.
443 Future waitForCompletion() async {
444 if (isWaitingForCompletion) {
445 throw "waitForCompletion called more than once.";
446 }
447 isWaitingForCompletion = true;
448 // [stderr] and [iterator] (stdout) must have active listeners before
449 // waiting for [futures] below to avoid a deadlock.
450 Future<List<String>> stderrFuture = stderr.toList();
451 Future<List<String>> stdoutFuture = (() async {
452 List<String> result = <String>[];
453 while (await stdoutIterator.moveNext()) {
454 result.add(stdoutIterator.current);
455 }
456 return result;
457 })();
458
459 StringBuffer sb = new StringBuffer();
460 int problemCount = 0;
461 for (var error in await Future.wait(futures)) {
462 if (error != null) {
463 sb.writeln("Problem #${++problemCount}:");
464 sb.writeln(error);
465 sb.writeln("");
466 }
467 }
468 await stdoutFuture;
469 List<String> stdoutLines = stdoutSink.takeLines();
470 List<String> stderrLines = await stderrFuture;
471 if (stdoutLines.isNotEmpty) {
472 sb.writeln("Problem #${++problemCount}:");
473 sb.writeln("Unexpected stdout from fletch-vm:");
474 for (String line in stdoutLines) {
475 sb.writeln(line);
476 }
477 sb.writeln("");
478 }
479 if (stderrLines.isNotEmpty) {
480 sb.writeln("Problem #${++problemCount}:");
481 sb.writeln("Unexpected stderr from fletch-vm:");
482 for (String line in stderrLines) {
483 sb.writeln(line);
484 }
485 sb.writeln("");
486 }
487 if (problemCount > 0) {
488 throw new StateError('Test has $problemCount problem(s). Details:\n$sb');
489 }
490 }
491
492 static Future<String> convertErrorToString(Future future) {
493 return future.then((_) => null).catchError((error, stackTrace) {
494 return "$error\n$stackTrace";
495 });
496 }
497
498 static Future<TestSession> spawnVm(
499 IncrementalCompiler compiler,
500 {String testName}) async {
501 String vmPath = guessFletchVm(null).toFilePath();
502
503 List<Future> futures = <Future>[];
504 void recordFuture(String name, Future future) {
505 if (future != null) {
506 futures.add(convertErrorToString(future));
507 }
508 }
509
510 List<String> vmOptions = <String>[
511 '-Xvalidate-heaps',
512 ];
513
514 print("Running '$vmPath ${vmOptions.join(" ")}'");
515 var environment = getProcessEnvironment(testName);
516 FletchVm fletchVm = await FletchVm.start(
517 vmPath, arguments: vmOptions, environment: environment);
518
519 // Unlike [fletchvm.stdoutLines] and [fletchvm.stderrLines], their
520 // corresponding controller cannot produce an error.
521 StreamController<String> stdoutController = new StreamController<String>();
522 StreamController<String> stderrController = new StreamController<String>();
523 recordFuture("stdout", fletchVm.stdoutLines.listen((String line) {
524 print('fletch_vm_stdout: $line');
525 stdoutController.add(line);
526 }).asFuture().whenComplete(stdoutController.close));
527 recordFuture("stderr", fletchVm.stderrLines.listen((String line) {
528 print('fletch_vm_stderr: $line');
529 stderrController.add(line);
530 }).asFuture().whenComplete(stderrController.close));
531
532 Completer<int> exitCodeCompleter = new Completer<int>();
533
534 // TODO(ahe): If the VM crashes on startup, this will never complete. This
535 // makes this program hang forever. But the exitCode completer might
536 // actually be ready to give us a crashed exit code. Exiting early with a
537 // failure in case exitCode is ready before server.first or having a
538 // timeout on server.first would be possible solutions.
539 var vmSocket = await fletchVm.connect();
540 recordFuture("vmSocket", vmSocket.done);
541
542 TestSession session = new TestSession(
543 vmSocket, compiler, fletchVm.process,
544 new StreamIterator(stdoutController.stream),
545 stderrController.stream,
546 futures, exitCodeCompleter.future);
547
548 recordFuture("exitCode", fletchVm.exitCode.then((int exitCode) {
549 print("VM exited with exit code: $exitCode.");
550 exitCodeCompleter.complete(exitCode);
551 }));
552
553 return session;
554 }
555
556 static Map<String, String> getProcessEnvironment(String testName) {
557 if (testName == null) return null;
558
559 var environment = new Map.from(Platform.environment);
560 environment['FEATURE_TEST_TESTNAME'] = testName;
561 return environment;
562 }
563
564 Future handleError(error, StackTrace stackTrace) {
565 addError(error, stackTrace);
566
567 // We either failed before we got to start a process or there was an
568 // uncaught exception in the program. If there was an uncaught exception
569 // the VM is intentionally hanging to give the debugger a chance to inspect
570 // the state at the point of the throw. Therefore, we explicitly have to
571 // kill the VM process. Notice, it is important that we kill the VM before
572 // we close the socket to it. Otherwise, the VM may write a message on
573 // stderr claiming that the compiler died (due to the socket getting
574 // closed).
575 process.kill();
576
577 // After the process has been killed, we need to close the socket and
578 // discard any commands that may have arrived.
579 recordFuture(process.exitCode.then((_) => kill()));
580
581 return waitForCompletion();
582 }
583 }
584
585 class BytesSink implements Sink<List<int>> {
586 final BytesBuilder builder = new BytesBuilder();
587
588 void add(List<int> data) => builder.add(data);
589
590 void close() {
591 }
592
593 List<String> takeLines() {
594 return new LineSplitter().convert(UTF8.decode(builder.takeBytes()));
595 }
596 }
597
598 class IncrementalTestHelper {
599 final Uri packageConfig;
600
601 final IoInputProvider inputProvider;
602
603 final IncrementalCompiler compiler;
604
605 FletchSystem system;
606
607 IncrementalTestHelper.internal(
608 this.packageConfig,
609 this.inputProvider,
610 this.compiler);
611
612 factory IncrementalTestHelper(IncrementalMode incrementalMode) {
613 Uri packageConfig = Uri.base.resolve('.packages');
614 IoInputProvider inputProvider = new IoInputProvider(packageConfig);
615 FormattingDiagnosticHandler diagnosticHandler =
616 new FormattingDiagnosticHandler(inputProvider);
617 IncrementalCompiler compiler = new IncrementalCompiler(
618 packageConfig: packageConfig,
619 inputProvider: inputProvider,
620 diagnosticHandler: diagnosticHandler,
621 outputProvider: new OutputProvider(),
622 support: incrementalMode,
623 platform: "fletch_mobile.platform");
624 return new IncrementalTestHelper.internal(
625 packageConfig,
626 inputProvider,
627 compiler);
628 }
629
630 Future<FletchDelta> fullCompile(ProgramResult program) async {
631 Map<String, String> code = computeCode(program);
632 inputProvider.sources.clear();
633 code.forEach((String name, String code) {
634 inputProvider.sources[customUriBase.resolve(name)] = code;
635 });
636
637 await compiler.compile(customUriBase.resolve('main.dart'), customUriBase);
638 FletchDelta delta = compiler.compiler.context.backend.computeDelta();
639 system = delta.system;
640 return delta;
641 }
642
643 Future<FletchDelta> incrementalCompile(
644 ProgramResult program,
645 int version) async {
646 Map<String, String> code = computeCode(program);
647 Map<Uri, Uri> uriMap = <Uri, Uri>{};
648 for (String name in code.keys) {
649 Uri uri = customUriBase.resolve('$name?v$version');
650 inputProvider.cachedSources[uri] = new Future.value(code[name]);
651 uriMap[customUriBase.resolve(name)] = uri;
652 }
653 FletchDelta delta = await compiler.compileUpdates(
654 system, uriMap, logVerbose: logger, logTime: logger);
655 system = delta.system;
656 return delta;
657 }
658
659 Map<String, String> computeCode(ProgramResult program) {
660 return program.code is String
661 ? <String,String>{ 'main.dart': program.code }
662 : program.code;
663 }
664 }
665
666 /// An input provider which provides input via the class [File]. Includes
667 /// in-memory compilation units [sources] which are returned when a matching
668 /// key requested.
669 class IoInputProvider extends SourceFileProvider {
670 final Map<Uri, String> sources = <Uri, String>{};
671
672 final Uri packageConfig;
673
674 final Map<Uri, Future> cachedSources = new Map<Uri, Future>();
675
676 static final Map<Uri, String> cachedFiles = new Map<Uri, String>();
677
678 IoInputProvider(this.packageConfig);
679
680 Future readFromUri(Uri uri) {
681 return cachedSources.putIfAbsent(uri, () {
682 String text;
683 String name;
684 if (sources.containsKey(uri)) {
685 name = '$uri';
686 text = sources[uri];
687 } else {
688 if (uri.scheme == PACKAGE_SCHEME) {
689 throw "packages not supported $uri";
690 }
691 text = readCachedFile(uri);
692 name = new File.fromUri(uri).path;
693 }
694 sourceFiles[uri] = new StringSourceFile(uri, name, text);
695 return new Future<String>.value(text);
696 });
697 }
698
699 Future call(Uri uri) => readStringFromUri(uri);
700
701 Future<String> readStringFromUri(Uri uri) {
702 return readFromUri(uri);
703 }
704
705 Future<List<int>> readUtf8BytesFromUri(Uri uri) {
706 throw "not supported";
707 }
708
709 static String readCachedFile(Uri uri) {
710 return cachedFiles.putIfAbsent(
711 uri, () => new File.fromUri(uri).readAsStringSync());
712 }
713 }
OLDNEW
« no previous file with comments | « tests/fletchc/incremental/experimental_mode.dart ('k') | tests/fletchc/incremental/production_mode.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698