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

Side by Side Diff: tools/testing/dart/command_output.dart

Issue 2933973002: Simplify Command classes. (Closed)
Patch Set: Rename class. 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
« no previous file with comments | « tools/testing/dart/command.dart ('k') | tools/testing/dart/compiler_configuration.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 import 'dart:convert';
6 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow
7 // CommandOutput.exitCode in subclasses of CommandOutput.
8 import 'dart:io' as io;
9
10 import 'browser_controller.dart';
11 import 'command.dart';
12 import 'configuration.dart';
13 import 'expectation.dart';
14 import 'test_runner.dart';
15 import 'utils.dart';
16
17 /**
18 * CommandOutput records the output of a completed command: the process's exit
19 * code, the standard output and standard error, whether the process timed out,
20 * and the time the process took to run. It does not contain a pointer to the
21 * [TestCase] this is the output of, so some functions require the test case
22 * to be passed as an argument.
23 */
24 abstract class CommandOutput {
25 Command get command;
26
27 Expectation result(TestCase testCase);
28
29 bool get hasCrashed;
30
31 bool get hasTimedOut;
32
33 bool didFail(TestCase testCase);
34
35 bool hasFailed(TestCase testCase);
36
37 bool get canRunDependendCommands;
38
39 bool get successful; // otherwise we might to retry running
40
41 Duration get time;
42
43 int get exitCode;
44
45 int get pid;
46
47 List<int> get stdout;
48
49 List<int> get stderr;
50
51 List<String> get diagnostics;
52
53 bool get compilationSkipped;
54 }
55
56 class CommandOutputImpl extends UniqueObject implements CommandOutput {
57 Command command;
58 int exitCode;
59
60 bool timedOut;
61 List<int> stdout;
62 List<int> stderr;
63 Duration time;
64 List<String> diagnostics;
65 bool compilationSkipped;
66 int pid;
67
68 /**
69 * A flag to indicate we have already printed a warning about ignoring the VM
70 * crash, to limit the amount of output produced per test.
71 */
72 bool alreadyPrintedWarning = false;
73
74 CommandOutputImpl(
75 Command this.command,
76 int this.exitCode,
77 bool this.timedOut,
78 List<int> this.stdout,
79 List<int> this.stderr,
80 Duration this.time,
81 bool this.compilationSkipped,
82 int this.pid) {
83 diagnostics = [];
84 }
85
86 Expectation result(TestCase testCase) {
87 if (hasCrashed) return Expectation.crash;
88 if (hasTimedOut) return Expectation.timeout;
89 if (hasFailed(testCase)) return Expectation.fail;
90 if (hasNonUtf8) return Expectation.nonUtf8Error;
91 return Expectation.pass;
92 }
93
94 bool get hasCrashed {
95 // dart2js exits with code 253 in case of unhandled exceptions.
96 // The dart binary exits with code 253 in case of an API error such
97 // as an invalid snapshot file.
98 // In either case an exit code of 253 is considered a crash.
99 if (exitCode == 253) return true;
100 if (io.Platform.operatingSystem == 'windows') {
101 // The VM uses std::abort to terminate on asserts.
102 // std::abort terminates with exit code 3 on Windows.
103 if (exitCode == 3 || exitCode == CRASHING_BROWSER_EXITCODE) {
104 return !timedOut;
105 }
106 // If a program receives an uncaught system exception, the program
107 // terminates with the exception code as exit code.
108 // The 0x3FFFFF00 mask here tries to determine if an exception indicates
109 // a crash of the program.
110 // System exception codes can be found in 'winnt.h', for example
111 // "#define STATUS_ACCESS_VIOLATION ((DWORD) 0xC0000005)"
112 return (!timedOut && (exitCode < 0) && ((0x3FFFFF00 & exitCode) == 0));
113 }
114 return !timedOut && ((exitCode < 0));
115 }
116
117 bool get hasTimedOut => timedOut;
118
119 bool didFail(TestCase testCase) {
120 return (exitCode != 0 && !hasCrashed);
121 }
122
123 bool get canRunDependendCommands {
124 // FIXME(kustermann): We may need to change this
125 return !hasTimedOut && exitCode == 0;
126 }
127
128 bool get successful {
129 // FIXME(kustermann): We may need to change this
130 return !hasTimedOut && exitCode == 0;
131 }
132
133 // Reverse result of a negative test.
134 bool hasFailed(TestCase testCase) {
135 return testCase.isNegative ? !didFail(testCase) : didFail(testCase);
136 }
137
138 bool get hasNonUtf8 => exitCode == NON_UTF_FAKE_EXITCODE;
139
140 Expectation _negateOutcomeIfNegativeTest(
141 Expectation outcome, bool isNegative) {
142 if (!isNegative) return outcome;
143 if (outcome == Expectation.ignore) return outcome;
144 if (outcome.canBeOutcomeOf(Expectation.fail)) {
145 return Expectation.pass;
146 }
147 return Expectation.fail;
148 }
149 }
150
151 class ContentShellCommandOutputImpl extends CommandOutputImpl {
152 // Although tests are reported as passing, content shell sometimes exits with
153 // a nonzero exitcode which makes our dartium builders extremely falky.
154 // See: http://dartbug.com/15139.
155 // TODO(rnystrom): Is this still needed? The underlying bug is closed.
156 static int WHITELISTED_CONTENTSHELL_EXITCODE = -1073740022;
157 static bool isWindows = io.Platform.operatingSystem == 'windows';
158 static bool _failedBecauseOfFlakyInfrastructure(
159 Command command, bool timedOut, List<int> stderrBytes) {
160 // If the browser test failed, it may have been because content shell
161 // and the virtual framebuffer X server didn't hook up, or it crashed with
162 // a core dump. Sometimes content shell crashes after it has set the stdout
163 // to PASS, so we have to do this check first.
164 // Content shell also fails with a broken pipe message: Issue 26739
165 var zygoteCrash =
166 new RegExp(r"ERROR:zygote_linux\.cc\(\d+\)] write: Broken pipe");
167 var stderr = decodeUtf8(stderrBytes);
168 // TODO(7564): See http://dartbug.com/7564
169 // This may not be happening anymore. Test by removing this suppression.
170 if (stderr.contains(MESSAGE_CANNOT_OPEN_DISPLAY) ||
171 stderr.contains(MESSAGE_FAILED_TO_RUN_COMMAND)) {
172 DebugLogger.warning(
173 "Warning: Failure because of missing XDisplay. Test ignored");
174 return true;
175 }
176 // TODO(26739): See http://dartbug.com/26739
177 if (zygoteCrash.hasMatch(stderr)) {
178 DebugLogger.warning("Warning: Failure because of content_shell "
179 "zygote crash. Test ignored");
180 return true;
181 }
182 return false;
183 }
184
185 bool _infraFailure;
186
187 ContentShellCommandOutputImpl(
188 Command command,
189 int exitCode,
190 bool timedOut,
191 List<int> stdout,
192 List<int> stderr,
193 Duration time,
194 bool compilationSkipped)
195 : _infraFailure =
196 _failedBecauseOfFlakyInfrastructure(command, timedOut, stderr),
197 super(command, exitCode, timedOut, stdout, stderr, time,
198 compilationSkipped, 0);
199
200 Expectation result(TestCase testCase) {
201 if (_infraFailure) {
202 return Expectation.ignore;
203 }
204
205 // Handle crashes and timeouts first
206 if (hasCrashed) return Expectation.crash;
207 if (hasTimedOut) return Expectation.timeout;
208 if (hasNonUtf8) return Expectation.nonUtf8Error;
209
210 var outcome = _getOutcome();
211
212 if (testCase.hasRuntimeError) {
213 if (!outcome.canBeOutcomeOf(Expectation.runtimeError)) {
214 return Expectation.missingRuntimeError;
215 }
216 }
217 if (testCase.isNegative) {
218 if (outcome.canBeOutcomeOf(Expectation.fail)) return Expectation.pass;
219 return Expectation.fail;
220 }
221 return outcome;
222 }
223
224 bool get successful => canRunDependendCommands;
225
226 bool get canRunDependendCommands {
227 // We cannot rely on the exit code of content_shell as a method to
228 // determine if we were successful or not.
229 return super.canRunDependendCommands && !didFail(null);
230 }
231
232 bool get hasCrashed {
233 return super.hasCrashed || _rendererCrashed;
234 }
235
236 Expectation _getOutcome() {
237 if (_browserTestFailure) {
238 return Expectation.runtimeError;
239 }
240 return Expectation.pass;
241 }
242
243 bool get _rendererCrashed =>
244 decodeUtf8(super.stdout).contains("#CRASHED - rendere");
245
246 bool get _browserTestFailure {
247 // Browser tests fail unless stdout contains
248 // 'Content-Type: text/plain' followed by 'PASS'.
249 bool hasContentType = false;
250 var stdoutLines = decodeUtf8(super.stdout).split("\n");
251 var containsFail = false;
252 var containsPass = false;
253 for (String line in stdoutLines) {
254 switch (line) {
255 case 'Content-Type: text/plain':
256 hasContentType = true;
257 break;
258 case 'FAIL':
259 if (hasContentType) {
260 containsFail = true;
261 }
262 break;
263 case 'PASS':
264 if (hasContentType) {
265 containsPass = true;
266 }
267 break;
268 }
269 }
270 if (hasContentType) {
271 if (containsFail && containsPass) {
272 DebugLogger.warning("Test had 'FAIL' and 'PASS' in stdout. ($command)");
273 }
274 if (!containsFail && !containsPass) {
275 DebugLogger.warning("Test had neither 'FAIL' nor 'PASS' in stdout. "
276 "($command)");
277 return true;
278 }
279 if (containsFail) {
280 return true;
281 }
282 assert(containsPass);
283 if (exitCode != 0) {
284 var message = "All tests passed, but exitCode != 0. "
285 "Actual exitcode: $exitCode. "
286 "($command)";
287 DebugLogger.warning(message);
288 diagnostics.add(message);
289 }
290 return (!hasCrashed &&
291 exitCode != 0 &&
292 (!isWindows || exitCode != WHITELISTED_CONTENTSHELL_EXITCODE));
293 }
294 DebugLogger.warning("Couldn't find 'Content-Type: text/plain' in output. "
295 "($command).");
296 return true;
297 }
298 }
299
300 class HTMLBrowserCommandOutputImpl extends ContentShellCommandOutputImpl {
301 HTMLBrowserCommandOutputImpl(
302 Command command,
303 int exitCode,
304 bool timedOut,
305 List<int> stdout,
306 List<int> stderr,
307 Duration time,
308 bool compilationSkipped)
309 : super(command, exitCode, timedOut, stdout, stderr, time,
310 compilationSkipped);
311
312 bool didFail(TestCase testCase) {
313 return _getOutcome() != Expectation.pass;
314 }
315
316 bool get _browserTestFailure {
317 // We should not need to convert back and forward.
318 var output = decodeUtf8(super.stdout);
319 if (output.contains("FAIL")) return true;
320 return !output.contains("PASS");
321 }
322 }
323
324 class BrowserTestJsonResult {
325 static const ALLOWED_TYPES = const [
326 'sync_exception',
327 'window_onerror',
328 'script_onerror',
329 'window_compilationerror',
330 'print',
331 'message_received',
332 'dom',
333 'debug'
334 ];
335
336 final Expectation outcome;
337 final String htmlDom;
338 final List<dynamic> events;
339
340 BrowserTestJsonResult(this.outcome, this.htmlDom, this.events);
341
342 static BrowserTestJsonResult parseFromString(String content) {
343 void validate(String assertion, bool value) {
344 if (!value) {
345 throw "InvalidFormat sent from browser driving page: $assertion:\n\n"
346 "$content";
347 }
348 }
349
350 try {
351 var events = JSON.decode(content);
352 if (events != null) {
353 validate("Message must be a List", events is List);
354
355 var messagesByType = <String, List<String>>{};
356 ALLOWED_TYPES.forEach((type) => messagesByType[type] = <String>[]);
357
358 for (var entry in events) {
359 validate("An entry must be a Map", entry is Map);
360
361 var type = entry['type'];
362 var value = entry['value'] as String;
363 var timestamp = entry['timestamp'];
364
365 validate("'type' of an entry must be a String", type is String);
366 validate("'type' has to be in $ALLOWED_TYPES.",
367 ALLOWED_TYPES.contains(type));
368 validate(
369 "'timestamp' of an entry must be a number", timestamp is num);
370
371 messagesByType[type].add(value);
372 }
373 validate("The message must have exactly one 'dom' entry.",
374 messagesByType['dom'].length == 1);
375
376 var dom = messagesByType['dom'][0];
377 if (dom.endsWith('\n')) {
378 dom = '$dom\n';
379 }
380
381 return new BrowserTestJsonResult(
382 _getOutcome(messagesByType), dom, events as List<dynamic>);
383 }
384 } catch (error) {
385 // If something goes wrong, we know the content was not in the correct
386 // JSON format. So we can't parse it.
387 // The caller is responsible for falling back to the old way of
388 // determining if a test failed.
389 }
390
391 return null;
392 }
393
394 static Expectation _getOutcome(Map<String, List<String>> messagesByType) {
395 occured(String type) => messagesByType[type].length > 0;
396 searchForMsg(List<String> types, String message) {
397 return types.any((type) => messagesByType[type].contains(message));
398 }
399
400 // FIXME(kustermann,ricow): I think this functionality doesn't work in
401 // test_controller.js: So far I haven't seen anything being reported on
402 // "window.compilationerror"
403 if (occured('window_compilationerror')) {
404 return Expectation.compileTimeError;
405 }
406
407 if (occured('sync_exception') ||
408 occured('window_onerror') ||
409 occured('script_onerror')) {
410 return Expectation.runtimeError;
411 }
412
413 if (messagesByType['dom'][0].contains('FAIL')) {
414 return Expectation.runtimeError;
415 }
416
417 // We search for these messages in 'print' and 'message_received' because
418 // the unittest implementation posts these messages using
419 // "window.postMessage()" instead of the normal "print()" them.
420
421 var isAsyncTest = searchForMsg(
422 ['print', 'message_received'], 'unittest-suite-wait-for-done');
423 var isAsyncSuccess =
424 searchForMsg(['print', 'message_received'], 'unittest-suite-success') ||
425 searchForMsg(['print', 'message_received'], 'unittest-suite-done');
426
427 if (isAsyncTest) {
428 if (isAsyncSuccess) {
429 return Expectation.pass;
430 }
431 return Expectation.runtimeError;
432 }
433
434 var mainStarted =
435 searchForMsg(['print', 'message_received'], 'dart-calling-main');
436 var mainDone =
437 searchForMsg(['print', 'message_received'], 'dart-main-done');
438
439 if (mainStarted && mainDone) {
440 return Expectation.pass;
441 }
442 return Expectation.fail;
443 }
444 }
445
446 class BrowserCommandOutputImpl extends CommandOutputImpl
447 with UnittestSuiteMessagesMixin {
448 BrowserTestOutput _result;
449 Expectation _rawOutcome;
450
451 factory BrowserCommandOutputImpl(Command command, BrowserTestOutput result) {
452 String indent(String string, int numSpaces) {
453 var spaces = new List.filled(numSpaces, ' ').join('');
454 return string
455 .replaceAll('\r\n', '\n')
456 .split('\n')
457 .map((line) => "$spaces$line")
458 .join('\n');
459 }
460
461 String stdout = "";
462 String stderr = "";
463 Expectation outcome;
464
465 var parsedResult =
466 BrowserTestJsonResult.parseFromString(result.lastKnownMessage);
467 if (parsedResult != null) {
468 outcome = parsedResult.outcome;
469 } else {
470 // Old way of determining whether a test failed or passed.
471 if (result.lastKnownMessage.contains("FAIL")) {
472 outcome = Expectation.runtimeError;
473 } else if (result.lastKnownMessage.contains("PASS")) {
474 outcome = Expectation.pass;
475 } else {
476 outcome = Expectation.runtimeError;
477 }
478 }
479
480 if (result.didTimeout) {
481 if (result.delayUntilTestStarted != null) {
482 stderr = "This test timed out. The delay until the test actually "
483 "started was: ${result.delayUntilTestStarted}.";
484 } else {
485 stderr = "This test has not notified test.py that it started running.";
486 }
487 }
488
489 if (parsedResult != null) {
490 stdout = "events:\n${indent(prettifyJson(parsedResult.events), 2)}\n\n";
491 } else {
492 stdout = "message:\n${indent(result.lastKnownMessage, 2)}\n\n";
493 }
494
495 stderr = '$stderr\n\n'
496 'BrowserOutput while running the test (* EXPERIMENTAL *):\n'
497 'BrowserOutput.stdout:\n'
498 '${indent(result.browserOutput.stdout.toString(), 2)}\n'
499 'BrowserOutput.stderr:\n'
500 '${indent(result.browserOutput.stderr.toString(), 2)}\n'
501 '\n';
502 return new BrowserCommandOutputImpl._internal(
503 command, result, outcome, encodeUtf8(stdout), encodeUtf8(stderr));
504 }
505
506 BrowserCommandOutputImpl._internal(Command command, BrowserTestOutput result,
507 this._rawOutcome, List<int> stdout, List<int> stderr)
508 : super(command, 0, result.didTimeout, stdout, stderr, result.duration,
509 false, 0) {
510 _result = result;
511 }
512
513 Expectation result(TestCase testCase) {
514 // Handle timeouts first
515 if (_result.didTimeout) return Expectation.timeout;
516 if (_result.didTimeout) {
517 if (testCase.configuration.runtime == Runtime.ie11) {
518 // TODO(28955): See http://dartbug.com/28955
519 DebugLogger.warning("Timeout of ie11 on test ${testCase.displayName}");
520 return Expectation.ignore;
521 }
522 return Expectation.timeout;
523 }
524
525 if (hasNonUtf8) return Expectation.nonUtf8Error;
526
527 // Multitests are handled specially
528 if (testCase.hasRuntimeError) {
529 if (_rawOutcome == Expectation.runtimeError) return Expectation.pass;
530 return Expectation.missingRuntimeError;
531 }
532
533 return _negateOutcomeIfNegativeTest(_rawOutcome, testCase.isNegative);
534 }
535 }
536
537 class AnalysisCommandOutputImpl extends CommandOutputImpl {
538 // An error line has 8 fields that look like:
539 // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source.
540 final int ERROR_LEVEL = 0;
541 final int ERROR_TYPE = 1;
542 final int FILENAME = 3;
543 final int FORMATTED_ERROR = 7;
544
545 AnalysisCommandOutputImpl(
546 Command command,
547 int exitCode,
548 bool timedOut,
549 List<int> stdout,
550 List<int> stderr,
551 Duration time,
552 bool compilationSkipped)
553 : super(command, exitCode, timedOut, stdout, stderr, time,
554 compilationSkipped, 0);
555
556 Expectation result(TestCase testCase) {
557 // TODO(kustermann): If we run the analyzer not in batch mode, make sure
558 // that command.exitCodes matches 2 (errors), 1 (warnings), 0 (no warnings,
559 // no errors)
560
561 // Handle crashes and timeouts first
562 if (hasCrashed) return Expectation.crash;
563 if (hasTimedOut) return Expectation.timeout;
564 if (hasNonUtf8) return Expectation.nonUtf8Error;
565
566 // Get the errors/warnings from the analyzer
567 List<String> errors = [];
568 List<String> warnings = [];
569 parseAnalyzerOutput(errors, warnings);
570
571 // Handle errors / missing errors
572 if (testCase.expectCompileError) {
573 if (errors.length > 0) {
574 return Expectation.pass;
575 }
576 return Expectation.missingCompileTimeError;
577 }
578 if (errors.length > 0) {
579 return Expectation.compileTimeError;
580 }
581
582 // Handle static warnings / missing static warnings
583 if (testCase.hasStaticWarning) {
584 if (warnings.length > 0) {
585 return Expectation.pass;
586 }
587 return Expectation.missingStaticWarning;
588 }
589 if (warnings.length > 0) {
590 return Expectation.staticWarning;
591 }
592
593 assert(errors.length == 0 && warnings.length == 0);
594 assert(!testCase.hasCompileError && !testCase.hasStaticWarning);
595 return Expectation.pass;
596 }
597
598 void parseAnalyzerOutput(List<String> outErrors, List<String> outWarnings) {
599 // Parse a line delimited by the | character using \ as an escape character
600 // like: FOO|BAR|FOO\|BAR|FOO\\BAZ as 4 fields: FOO BAR FOO|BAR FOO\BAZ
601 List<String> splitMachineError(String line) {
602 StringBuffer field = new StringBuffer();
603 List<String> result = [];
604 bool escaped = false;
605 for (var i = 0; i < line.length; i++) {
606 var c = line[i];
607 if (!escaped && c == '\\') {
608 escaped = true;
609 continue;
610 }
611 escaped = false;
612 if (c == '|') {
613 result.add(field.toString());
614 field = new StringBuffer();
615 continue;
616 }
617 field.write(c);
618 }
619 result.add(field.toString());
620 return result;
621 }
622
623 for (String line in decodeUtf8(super.stderr).split("\n")) {
624 if (line.length == 0) continue;
625 List<String> fields = splitMachineError(line);
626 // We only consider errors/warnings for files of interest.
627 if (fields.length > FORMATTED_ERROR) {
628 if (fields[ERROR_LEVEL] == 'ERROR') {
629 outErrors.add(fields[FORMATTED_ERROR]);
630 } else if (fields[ERROR_LEVEL] == 'WARNING') {
631 outWarnings.add(fields[FORMATTED_ERROR]);
632 }
633 // OK to Skip error output that doesn't match the machine format
634 }
635 }
636 }
637 }
638
639 class VmCommandOutputImpl extends CommandOutputImpl
640 with UnittestSuiteMessagesMixin {
641 static const DART_VM_EXITCODE_DFE_ERROR = 252;
642 static const DART_VM_EXITCODE_COMPILE_TIME_ERROR = 254;
643 static const DART_VM_EXITCODE_UNCAUGHT_EXCEPTION = 255;
644
645 VmCommandOutputImpl(Command command, int exitCode, bool timedOut,
646 List<int> stdout, List<int> stderr, Duration time, int pid)
647 : super(command, exitCode, timedOut, stdout, stderr, time, false, pid);
648
649 Expectation result(TestCase testCase) {
650 // Handle crashes and timeouts first
651 if (exitCode == DART_VM_EXITCODE_DFE_ERROR) return Expectation.dartkCrash;
652 if (hasCrashed) return Expectation.crash;
653 if (hasTimedOut) return Expectation.timeout;
654 if (hasNonUtf8) return Expectation.nonUtf8Error;
655
656 // Multitests are handled specially
657 if (testCase.expectCompileError) {
658 if (exitCode == DART_VM_EXITCODE_COMPILE_TIME_ERROR) {
659 return Expectation.pass;
660 }
661 return Expectation.missingCompileTimeError;
662 }
663 if (testCase.hasRuntimeError) {
664 // TODO(kustermann): Do we consider a "runtimeError" only an uncaught
665 // exception or does any nonzero exit code fullfil this requirement?
666 if (exitCode != 0) {
667 return Expectation.pass;
668 }
669 return Expectation.missingRuntimeError;
670 }
671
672 // The actual outcome depends on the exitCode
673 Expectation outcome;
674 if (exitCode == DART_VM_EXITCODE_COMPILE_TIME_ERROR) {
675 outcome = Expectation.compileTimeError;
676 } else if (exitCode == DART_VM_EXITCODE_UNCAUGHT_EXCEPTION) {
677 outcome = Expectation.runtimeError;
678 } else if (exitCode != 0) {
679 // This is a general fail, in case we get an unknown nonzero exitcode.
680 outcome = Expectation.fail;
681 } else {
682 outcome = Expectation.pass;
683 }
684 outcome = _negateOutcomeIfIncompleteAsyncTest(outcome, decodeUtf8(stdout));
685 return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative);
686 }
687 }
688
689 class CompilationCommandOutputImpl extends CommandOutputImpl {
690 static const DART2JS_EXITCODE_CRASH = 253;
691
692 CompilationCommandOutputImpl(
693 Command command,
694 int exitCode,
695 bool timedOut,
696 List<int> stdout,
697 List<int> stderr,
698 Duration time,
699 bool compilationSkipped)
700 : super(command, exitCode, timedOut, stdout, stderr, time,
701 compilationSkipped, 0);
702
703 Expectation result(TestCase testCase) {
704 // Handle general crash/timeout detection.
705 if (hasCrashed) return Expectation.crash;
706 if (hasTimedOut) {
707 bool isWindows = io.Platform.operatingSystem == 'windows';
708 bool isBrowserTestCase =
709 testCase.commands.any((command) => command is BrowserTestCommand);
710 // TODO(26060) Dart2js batch mode hangs on Windows under heavy load.
711 return (isWindows && isBrowserTestCase)
712 ? Expectation.ignore
713 : Expectation.timeout;
714 }
715 if (hasNonUtf8) return Expectation.nonUtf8Error;
716
717 // Handle dart2js specific crash detection
718 if (exitCode == DART2JS_EXITCODE_CRASH ||
719 exitCode == VmCommandOutputImpl.DART_VM_EXITCODE_COMPILE_TIME_ERROR ||
720 exitCode == VmCommandOutputImpl.DART_VM_EXITCODE_UNCAUGHT_EXCEPTION) {
721 return Expectation.crash;
722 }
723
724 // Multitests are handled specially
725 if (testCase.expectCompileError) {
726 // Nonzero exit code of the compiler means compilation failed
727 // TODO(kustermann): Do we have a special exit code in that case???
728 if (exitCode != 0) {
729 return Expectation.pass;
730 }
731 return Expectation.missingCompileTimeError;
732 }
733
734 // TODO(kustermann): This is a hack, remove it
735 if (testCase.hasRuntimeError && testCase.commands.length > 1) {
736 // We expected to run the test, but we got an compile time error.
737 // If the compilation succeeded, we wouldn't be in here!
738 assert(exitCode != 0);
739 return Expectation.compileTimeError;
740 }
741
742 Expectation outcome =
743 exitCode == 0 ? Expectation.pass : Expectation.compileTimeError;
744 return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative);
745 }
746 }
747
748 class KernelCompilationCommandOutputImpl extends CompilationCommandOutputImpl {
749 KernelCompilationCommandOutputImpl(
750 Command command,
751 int exitCode,
752 bool timedOut,
753 List<int> stdout,
754 List<int> stderr,
755 Duration time,
756 bool compilationSkipped)
757 : super(command, exitCode, timedOut, stdout, stderr, time,
758 compilationSkipped);
759
760 bool get canRunDependendCommands {
761 // See [BatchRunnerProcess]: 0 means success, 1 means compile-time error.
762 // TODO(asgerf): When the frontend supports it, continue running even if
763 // there were compile-time errors. See kernel_sdk issue #18.
764 return !hasCrashed && !timedOut && exitCode == 0;
765 }
766
767 Expectation result(TestCase testCase) {
768 Expectation result = super.result(testCase);
769 if (result.canBeOutcomeOf(Expectation.crash)) {
770 return Expectation.dartkCrash;
771 } else if (result.canBeOutcomeOf(Expectation.timeout)) {
772 return Expectation.dartkTimeout;
773 } else if (result.canBeOutcomeOf(Expectation.compileTimeError)) {
774 return Expectation.dartkCompileTimeError;
775 }
776 return result;
777 }
778
779 // If the compiler was able to produce a Kernel IR file we want to run the
780 // result on the Dart VM. We therefore mark the [KernelCompilationCommand] as
781 // successful.
782 // => This ensures we test that the DartVM produces correct CompileTime errors
783 // as it is supposed to for our test suites.
784 bool get successful => canRunDependendCommands;
785 }
786
787 class JsCommandlineOutputImpl extends CommandOutputImpl
788 with UnittestSuiteMessagesMixin {
789 JsCommandlineOutputImpl(Command command, int exitCode, bool timedOut,
790 List<int> stdout, List<int> stderr, Duration time)
791 : super(command, exitCode, timedOut, stdout, stderr, time, false, 0);
792
793 Expectation result(TestCase testCase) {
794 // Handle crashes and timeouts first
795 if (hasCrashed) return Expectation.crash;
796 if (hasTimedOut) return Expectation.timeout;
797 if (hasNonUtf8) return Expectation.nonUtf8Error;
798
799 if (testCase.hasRuntimeError) {
800 if (exitCode != 0) return Expectation.pass;
801 return Expectation.missingRuntimeError;
802 }
803
804 var outcome = exitCode == 0 ? Expectation.pass : Expectation.runtimeError;
805 outcome = _negateOutcomeIfIncompleteAsyncTest(outcome, decodeUtf8(stdout));
806 return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative);
807 }
808 }
809
810 class PubCommandOutputImpl extends CommandOutputImpl {
811 PubCommandOutputImpl(PubCommand command, int exitCode, bool timedOut,
812 List<int> stdout, List<int> stderr, Duration time)
813 : super(command, exitCode, timedOut, stdout, stderr, time, false, 0);
814
815 Expectation result(TestCase testCase) {
816 // Handle crashes and timeouts first
817 if (hasCrashed) return Expectation.crash;
818 if (hasTimedOut) return Expectation.timeout;
819 if (hasNonUtf8) return Expectation.nonUtf8Error;
820
821 if (exitCode == 0) {
822 return Expectation.pass;
823 } else if ((command as PubCommand).command == 'get') {
824 return Expectation.pubGetError;
825 } else {
826 return Expectation.fail;
827 }
828 }
829 }
830
831 class ScriptCommandOutputImpl extends CommandOutputImpl {
832 final Expectation _result;
833
834 ScriptCommandOutputImpl(ScriptCommand command, this._result,
835 String scriptExecutionInformation, Duration time)
836 : super(command, 0, false, [], [], time, false, 0) {
837 var lines = scriptExecutionInformation.split("\n");
838 diagnostics.addAll(lines);
839 }
840
841 Expectation result(TestCase testCase) => _result;
842
843 bool get canRunDependendCommands => _result == Expectation.pass;
844
845 bool get successful => _result == Expectation.pass;
846 }
847
848 CommandOutput createCommandOutput(Command command, int exitCode, bool timedOut,
849 List<int> stdout, List<int> stderr, Duration time, bool compilationSkipped,
850 [int pid = 0]) {
851 if (command is ContentShellCommand) {
852 return new ContentShellCommandOutputImpl(
853 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
854 } else if (command is BrowserTestCommand) {
855 return new HTMLBrowserCommandOutputImpl(
856 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
857 } else if (command is AnalysisCommand) {
858 return new AnalysisCommandOutputImpl(
859 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
860 } else if (command is VmCommand) {
861 return new VmCommandOutputImpl(
862 command, exitCode, timedOut, stdout, stderr, time, pid);
863 } else if (command is KernelCompilationCommand) {
864 return new KernelCompilationCommandOutputImpl(
865 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
866 } else if (command is AdbPrecompilationCommand) {
867 return new VmCommandOutputImpl(
868 command, exitCode, timedOut, stdout, stderr, time, pid);
869 } else if (command is CompilationCommand) {
870 if (command.displayName == 'precompiler' ||
871 command.displayName == 'app_jit') {
872 return new VmCommandOutputImpl(
873 command, exitCode, timedOut, stdout, stderr, time, pid);
874 }
875 return new CompilationCommandOutputImpl(
876 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
877 } else if (command is JSCommandlineCommand) {
878 return new JsCommandlineOutputImpl(
879 command, exitCode, timedOut, stdout, stderr, time);
880 } else if (command is PubCommand) {
881 return new PubCommandOutputImpl(
882 command, exitCode, timedOut, stdout, stderr, time);
883 }
884
885 return new CommandOutputImpl(command, exitCode, timedOut, stdout, stderr,
886 time, compilationSkipped, pid);
887 }
888
889 class UnittestSuiteMessagesMixin {
890 bool _isAsyncTest(String testOutput) {
891 return testOutput.contains("unittest-suite-wait-for-done");
892 }
893
894 bool _isAsyncTestSuccessful(String testOutput) {
895 return testOutput.contains("unittest-suite-success");
896 }
897
898 Expectation _negateOutcomeIfIncompleteAsyncTest(
899 Expectation outcome, String testOutput) {
900 // If this is an asynchronous test and the asynchronous operation didn't
901 // complete successfully, it's outcome is Expectation.FAIL.
902 // TODO: maybe we should introduce a AsyncIncomplete marker or so
903 if (outcome == Expectation.pass) {
904 if (_isAsyncTest(testOutput) && !_isAsyncTestSuccessful(testOutput)) {
905 return Expectation.fail;
906 }
907 }
908 return outcome;
909 }
910 }
OLDNEW
« no previous file with comments | « tools/testing/dart/command.dart ('k') | tools/testing/dart/compiler_configuration.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698