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

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

Issue 2933973002: Simplify Command classes. (Closed)
Patch Set: Move Command and CommandOutput classes to separate files. 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 BrowserCommandOutputImpl extends CommandOutputImpl {
Bill Hesse 2017/06/15 12:08:40 I just renamed BrowserCommandOutputImpl to Content
Bob Nystrom 2017/06/15 21:19:29 Yup, I saw that when I merged. Renamed it here too
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 // TODO(28955): See http://dartbug.com/28955
183 if (timedOut &&
184 command is BrowserTestCommand &&
185 command.browser == Runtime.ie11) {
186 DebugLogger.warning("Timeout of ie11 on test page ${command.url}");
187 return true;
188 }
189 return false;
190 }
191
192 bool _infraFailure;
193
194 BrowserCommandOutputImpl(
195 Command command,
196 int exitCode,
197 bool timedOut,
198 List<int> stdout,
199 List<int> stderr,
200 Duration time,
201 bool compilationSkipped)
202 : _infraFailure =
203 _failedBecauseOfFlakyInfrastructure(command, timedOut, stderr),
204 super(command, exitCode, timedOut, stdout, stderr, time,
205 compilationSkipped, 0);
206
207 Expectation result(TestCase testCase) {
208 if (_infraFailure) {
209 return Expectation.ignore;
210 }
211
212 // Handle crashes and timeouts first
213 if (hasCrashed) return Expectation.crash;
214 if (hasTimedOut) return Expectation.timeout;
215 if (hasNonUtf8) return Expectation.nonUtf8Error;
216
217 var outcome = _getOutcome();
218
219 if (testCase.hasRuntimeError) {
220 if (!outcome.canBeOutcomeOf(Expectation.runtimeError)) {
221 return Expectation.missingRuntimeError;
222 }
223 }
224 if (testCase.isNegative) {
225 if (outcome.canBeOutcomeOf(Expectation.fail)) return Expectation.pass;
226 return Expectation.fail;
227 }
228 return outcome;
229 }
230
231 bool get successful => canRunDependendCommands;
232
233 bool get canRunDependendCommands {
234 // We cannot rely on the exit code of content_shell as a method to
235 // determine if we were successful or not.
236 return super.canRunDependendCommands && !didFail(null);
237 }
238
239 bool get hasCrashed {
240 return super.hasCrashed || _rendererCrashed;
241 }
242
243 Expectation _getOutcome() {
244 if (_browserTestFailure) {
245 return Expectation.runtimeError;
246 }
247 return Expectation.pass;
248 }
249
250 bool get _rendererCrashed =>
251 decodeUtf8(super.stdout).contains("#CRASHED - rendere");
252
253 bool get _browserTestFailure {
254 // Browser tests fail unless stdout contains
255 // 'Content-Type: text/plain' followed by 'PASS'.
256 bool hasContentType = false;
257 var stdoutLines = decodeUtf8(super.stdout).split("\n");
258 var containsFail = false;
259 var containsPass = false;
260 for (String line in stdoutLines) {
261 switch (line) {
262 case 'Content-Type: text/plain':
263 hasContentType = true;
264 break;
265 case 'FAIL':
266 if (hasContentType) {
267 containsFail = true;
268 }
269 break;
270 case 'PASS':
271 if (hasContentType) {
272 containsPass = true;
273 }
274 break;
275 }
276 }
277 if (hasContentType) {
278 if (containsFail && containsPass) {
279 DebugLogger.warning("Test had 'FAIL' and 'PASS' in stdout. ($command)");
280 }
281 if (!containsFail && !containsPass) {
282 DebugLogger.warning("Test had neither 'FAIL' nor 'PASS' in stdout. "
283 "($command)");
284 return true;
285 }
286 if (containsFail) {
287 return true;
288 }
289 assert(containsPass);
290 if (exitCode != 0) {
291 var message = "All tests passed, but exitCode != 0. "
292 "Actual exitcode: $exitCode. "
293 "($command)";
294 DebugLogger.warning(message);
295 diagnostics.add(message);
296 }
297 return (!hasCrashed &&
298 exitCode != 0 &&
299 (!isWindows || exitCode != WHITELISTED_CONTENTSHELL_EXITCODE));
300 }
301 DebugLogger.warning("Couldn't find 'Content-Type: text/plain' in output. "
302 "($command).");
303 return true;
304 }
305 }
306
307 class HTMLBrowserCommandOutputImpl extends BrowserCommandOutputImpl {
Bill Hesse 2017/06/15 12:08:40 I think this class may not be used any more. Can
Bob Nystrom 2017/06/15 21:19:29 It doesn't seem to be used, but the corresponding
308 HTMLBrowserCommandOutputImpl(
309 Command command,
310 int exitCode,
311 bool timedOut,
312 List<int> stdout,
313 List<int> stderr,
314 Duration time,
315 bool compilationSkipped)
316 : super(command, exitCode, timedOut, stdout, stderr, time,
317 compilationSkipped);
318
319 bool didFail(TestCase testCase) {
320 return _getOutcome() != Expectation.pass;
321 }
322
323 bool get _browserTestFailure {
324 // We should not need to convert back and forward.
325 var output = decodeUtf8(super.stdout);
326 if (output.contains("FAIL")) return true;
327 return !output.contains("PASS");
328 }
329 }
330
331 class BrowserTestJsonResult {
332 static const ALLOWED_TYPES = const [
333 'sync_exception',
334 'window_onerror',
335 'script_onerror',
336 'window_compilationerror',
337 'print',
338 'message_received',
339 'dom',
340 'debug'
341 ];
342
343 final Expectation outcome;
344 final String htmlDom;
345 final List<dynamic> events;
346
347 BrowserTestJsonResult(this.outcome, this.htmlDom, this.events);
348
349 static BrowserTestJsonResult parseFromString(String content) {
350 void validate(String assertion, bool value) {
351 if (!value) {
352 throw "InvalidFormat sent from browser driving page: $assertion:\n\n"
353 "$content";
354 }
355 }
356
357 try {
358 var events = JSON.decode(content);
359 if (events != null) {
360 validate("Message must be a List", events is List);
361
362 var messagesByType = <String, List<String>>{};
363 ALLOWED_TYPES.forEach((type) => messagesByType[type] = <String>[]);
364
365 for (var entry in events) {
366 validate("An entry must be a Map", entry is Map);
367
368 var type = entry['type'];
369 var value = entry['value'] as String;
370 var timestamp = entry['timestamp'];
371
372 validate("'type' of an entry must be a String", type is String);
373 validate("'type' has to be in $ALLOWED_TYPES.",
374 ALLOWED_TYPES.contains(type));
375 validate(
376 "'timestamp' of an entry must be a number", timestamp is num);
377
378 messagesByType[type].add(value);
379 }
380 validate("The message must have exactly one 'dom' entry.",
381 messagesByType['dom'].length == 1);
382
383 var dom = messagesByType['dom'][0];
384 if (dom.endsWith('\n')) {
385 dom = '$dom\n';
386 }
387
388 return new BrowserTestJsonResult(
389 _getOutcome(messagesByType), dom, events as List<dynamic>);
390 }
391 } catch (error) {
392 // If something goes wrong, we know the content was not in the correct
393 // JSON format. So we can't parse it.
394 // The caller is responsible for falling back to the old way of
395 // determining if a test failed.
396 }
397
398 return null;
399 }
400
401 static Expectation _getOutcome(Map<String, List<String>> messagesByType) {
402 occured(String type) => messagesByType[type].length > 0;
403 searchForMsg(List<String> types, String message) {
404 return types.any((type) => messagesByType[type].contains(message));
405 }
406
407 // FIXME(kustermann,ricow): I think this functionality doesn't work in
408 // test_controller.js: So far I haven't seen anything being reported on
409 // "window.compilationerror"
410 if (occured('window_compilationerror')) {
411 return Expectation.compileTimeError;
412 }
413
414 if (occured('sync_exception') ||
415 occured('window_onerror') ||
416 occured('script_onerror')) {
417 return Expectation.runtimeError;
418 }
419
420 if (messagesByType['dom'][0].contains('FAIL')) {
421 return Expectation.runtimeError;
422 }
423
424 // We search for these messages in 'print' and 'message_received' because
425 // the unittest implementation posts these messages using
426 // "window.postMessage()" instead of the normal "print()" them.
427
428 var isAsyncTest = searchForMsg(
429 ['print', 'message_received'], 'unittest-suite-wait-for-done');
430 var isAsyncSuccess =
431 searchForMsg(['print', 'message_received'], 'unittest-suite-success') ||
432 searchForMsg(['print', 'message_received'], 'unittest-suite-done');
433
434 if (isAsyncTest) {
435 if (isAsyncSuccess) {
436 return Expectation.pass;
437 }
438 return Expectation.runtimeError;
439 }
440
441 var mainStarted =
442 searchForMsg(['print', 'message_received'], 'dart-calling-main');
443 var mainDone =
444 searchForMsg(['print', 'message_received'], 'dart-main-done');
445
446 if (mainStarted && mainDone) {
447 return Expectation.pass;
448 }
449 return Expectation.fail;
450 }
451 }
452
453 class BrowserControllerTestOutcome extends CommandOutputImpl
Bill Hesse 2017/06/15 12:08:40 This really should be named like the others, as Br
Bob Nystrom 2017/06/15 21:19:29 Done.
454 with UnittestSuiteMessagesMixin {
455 BrowserTestOutput _result;
456 Expectation _rawOutcome;
457
458 factory BrowserControllerTestOutcome(
459 Command command, BrowserTestOutput result) {
460 String indent(String string, int numSpaces) {
461 var spaces = new List.filled(numSpaces, ' ').join('');
462 return string
463 .replaceAll('\r\n', '\n')
464 .split('\n')
465 .map((line) => "$spaces$line")
466 .join('\n');
467 }
468
469 String stdout = "";
470 String stderr = "";
471 Expectation outcome;
472
473 var parsedResult =
474 BrowserTestJsonResult.parseFromString(result.lastKnownMessage);
475 if (parsedResult != null) {
476 outcome = parsedResult.outcome;
477 } else {
478 // Old way of determining whether a test failed or passed.
479 if (result.lastKnownMessage.contains("FAIL")) {
480 outcome = Expectation.runtimeError;
481 } else if (result.lastKnownMessage.contains("PASS")) {
482 outcome = Expectation.pass;
483 } else {
484 outcome = Expectation.runtimeError;
485 }
486 }
487
488 if (result.didTimeout) {
489 if (result.delayUntilTestStarted != null) {
490 stderr = "This test timed out. The delay until the test actually "
491 "started was: ${result.delayUntilTestStarted}.";
492 } else {
493 stderr = "This test has not notified test.py that it started running.";
494 }
495 }
496
497 if (parsedResult != null) {
498 stdout = "events:\n${indent(prettifyJson(parsedResult.events), 2)}\n\n";
499 } else {
500 stdout = "message:\n${indent(result.lastKnownMessage, 2)}\n\n";
501 }
502
503 stderr = '$stderr\n\n'
504 'BrowserOutput while running the test (* EXPERIMENTAL *):\n'
505 'BrowserOutput.stdout:\n'
506 '${indent(result.browserOutput.stdout.toString(), 2)}\n'
507 'BrowserOutput.stderr:\n'
508 '${indent(result.browserOutput.stderr.toString(), 2)}\n'
509 '\n';
510 return new BrowserControllerTestOutcome._internal(
511 command, result, outcome, encodeUtf8(stdout), encodeUtf8(stderr));
512 }
513
514 BrowserControllerTestOutcome._internal(
515 Command command,
516 BrowserTestOutput result,
517 this._rawOutcome,
518 List<int> stdout,
519 List<int> stderr)
520 : super(command, 0, result.didTimeout, stdout, stderr, result.duration,
521 false, 0) {
522 _result = result;
523 }
524
525 Expectation result(TestCase testCase) {
526 // Handle timeouts first
527 if (_result.didTimeout) return Expectation.timeout;
528 if (hasNonUtf8) return Expectation.nonUtf8Error;
529
530 // Multitests are handled specially
531 if (testCase.hasRuntimeError) {
532 if (_rawOutcome == Expectation.runtimeError) return Expectation.pass;
533 return Expectation.missingRuntimeError;
534 }
535
536 return _negateOutcomeIfNegativeTest(_rawOutcome, testCase.isNegative);
537 }
538 }
539
540 class AnalysisCommandOutputImpl extends CommandOutputImpl {
541 // An error line has 8 fields that look like:
542 // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source.
543 final int ERROR_LEVEL = 0;
544 final int ERROR_TYPE = 1;
545 final int FILENAME = 3;
546 final int FORMATTED_ERROR = 7;
547
548 AnalysisCommandOutputImpl(
549 Command command,
550 int exitCode,
551 bool timedOut,
552 List<int> stdout,
553 List<int> stderr,
554 Duration time,
555 bool compilationSkipped)
556 : super(command, exitCode, timedOut, stdout, stderr, time,
557 compilationSkipped, 0);
558
559 Expectation result(TestCase testCase) {
560 // TODO(kustermann): If we run the analyzer not in batch mode, make sure
561 // that command.exitCodes matches 2 (errors), 1 (warnings), 0 (no warnings,
562 // no errors)
563
564 // Handle crashes and timeouts first
565 if (hasCrashed) return Expectation.crash;
566 if (hasTimedOut) return Expectation.timeout;
567 if (hasNonUtf8) return Expectation.nonUtf8Error;
568
569 // Get the errors/warnings from the analyzer
570 List<String> errors = [];
571 List<String> warnings = [];
572 parseAnalyzerOutput(errors, warnings);
573
574 // Handle errors / missing errors
575 if (testCase.expectCompileError) {
576 if (errors.length > 0) {
577 return Expectation.pass;
578 }
579 return Expectation.missingCompileTimeError;
580 }
581 if (errors.length > 0) {
582 return Expectation.compileTimeError;
583 }
584
585 // Handle static warnings / missing static warnings
586 if (testCase.hasStaticWarning) {
587 if (warnings.length > 0) {
588 return Expectation.pass;
589 }
590 return Expectation.missingStaticWarning;
591 }
592 if (warnings.length > 0) {
593 return Expectation.staticWarning;
594 }
595
596 assert(errors.length == 0 && warnings.length == 0);
597 assert(!testCase.hasCompileError && !testCase.hasStaticWarning);
598 return Expectation.pass;
599 }
600
601 void parseAnalyzerOutput(List<String> outErrors, List<String> outWarnings) {
602 // Parse a line delimited by the | character using \ as an escape character
603 // like: FOO|BAR|FOO\|BAR|FOO\\BAZ as 4 fields: FOO BAR FOO|BAR FOO\BAZ
604 List<String> splitMachineError(String line) {
605 StringBuffer field = new StringBuffer();
606 List<String> result = [];
607 bool escaped = false;
608 for (var i = 0; i < line.length; i++) {
609 var c = line[i];
610 if (!escaped && c == '\\') {
611 escaped = true;
612 continue;
613 }
614 escaped = false;
615 if (c == '|') {
616 result.add(field.toString());
617 field = new StringBuffer();
618 continue;
619 }
620 field.write(c);
621 }
622 result.add(field.toString());
623 return result;
624 }
625
626 for (String line in decodeUtf8(super.stderr).split("\n")) {
627 if (line.length == 0) continue;
628 List<String> fields = splitMachineError(line);
629 // We only consider errors/warnings for files of interest.
630 if (fields.length > FORMATTED_ERROR) {
631 if (fields[ERROR_LEVEL] == 'ERROR') {
632 outErrors.add(fields[FORMATTED_ERROR]);
633 } else if (fields[ERROR_LEVEL] == 'WARNING') {
634 outWarnings.add(fields[FORMATTED_ERROR]);
635 }
636 // OK to Skip error output that doesn't match the machine format
637 }
638 }
639 }
640 }
641
642 class VmCommandOutputImpl extends CommandOutputImpl
643 with UnittestSuiteMessagesMixin {
644 static const DART_VM_EXITCODE_DFE_ERROR = 252;
645 static const DART_VM_EXITCODE_COMPILE_TIME_ERROR = 254;
646 static const DART_VM_EXITCODE_UNCAUGHT_EXCEPTION = 255;
647
648 VmCommandOutputImpl(Command command, int exitCode, bool timedOut,
649 List<int> stdout, List<int> stderr, Duration time, int pid)
650 : super(command, exitCode, timedOut, stdout, stderr, time, false, pid);
651
652 Expectation result(TestCase testCase) {
653 // Handle crashes and timeouts first
654 if (exitCode == DART_VM_EXITCODE_DFE_ERROR) return Expectation.dartkCrash;
655 if (hasCrashed) return Expectation.crash;
656 if (hasTimedOut) return Expectation.timeout;
657 if (hasNonUtf8) return Expectation.nonUtf8Error;
658
659 // Multitests are handled specially
660 if (testCase.expectCompileError) {
661 if (exitCode == DART_VM_EXITCODE_COMPILE_TIME_ERROR) {
662 return Expectation.pass;
663 }
664 return Expectation.missingCompileTimeError;
665 }
666 if (testCase.hasRuntimeError) {
667 // TODO(kustermann): Do we consider a "runtimeError" only an uncaught
668 // exception or does any nonzero exit code fullfil this requirement?
669 if (exitCode != 0) {
670 return Expectation.pass;
671 }
672 return Expectation.missingRuntimeError;
673 }
674
675 // The actual outcome depends on the exitCode
676 Expectation outcome;
677 if (exitCode == DART_VM_EXITCODE_COMPILE_TIME_ERROR) {
678 outcome = Expectation.compileTimeError;
679 } else if (exitCode == DART_VM_EXITCODE_UNCAUGHT_EXCEPTION) {
680 outcome = Expectation.runtimeError;
681 } else if (exitCode != 0) {
682 // This is a general fail, in case we get an unknown nonzero exitcode.
683 outcome = Expectation.fail;
684 } else {
685 outcome = Expectation.pass;
686 }
687 outcome = _negateOutcomeIfIncompleteAsyncTest(outcome, decodeUtf8(stdout));
688 return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative);
689 }
690 }
691
692 class CompilationCommandOutputImpl extends CommandOutputImpl {
693 static const DART2JS_EXITCODE_CRASH = 253;
694
695 CompilationCommandOutputImpl(
696 Command command,
697 int exitCode,
698 bool timedOut,
699 List<int> stdout,
700 List<int> stderr,
701 Duration time,
702 bool compilationSkipped)
703 : super(command, exitCode, timedOut, stdout, stderr, time,
704 compilationSkipped, 0);
705
706 Expectation result(TestCase testCase) {
707 // Handle general crash/timeout detection.
708 if (hasCrashed) return Expectation.crash;
709 if (hasTimedOut) {
710 bool isWindows = io.Platform.operatingSystem == 'windows';
711 bool isBrowserTestCase =
712 testCase.commands.any((command) => command is BrowserTestCommand);
713 // TODO(26060) Dart2js batch mode hangs on Windows under heavy load.
714 return (isWindows && isBrowserTestCase)
715 ? Expectation.ignore
716 : Expectation.timeout;
717 }
718 if (hasNonUtf8) return Expectation.nonUtf8Error;
719
720 // Handle dart2js specific crash detection
721 if (exitCode == DART2JS_EXITCODE_CRASH ||
722 exitCode == VmCommandOutputImpl.DART_VM_EXITCODE_COMPILE_TIME_ERROR ||
723 exitCode == VmCommandOutputImpl.DART_VM_EXITCODE_UNCAUGHT_EXCEPTION) {
724 return Expectation.crash;
725 }
726
727 // Multitests are handled specially
728 if (testCase.expectCompileError) {
729 // Nonzero exit code of the compiler means compilation failed
730 // TODO(kustermann): Do we have a special exit code in that case???
731 if (exitCode != 0) {
732 return Expectation.pass;
733 }
734 return Expectation.missingCompileTimeError;
735 }
736
737 // TODO(kustermann): This is a hack, remove it
738 if (testCase.hasRuntimeError && testCase.commands.length > 1) {
739 // We expected to run the test, but we got an compile time error.
740 // If the compilation succeeded, we wouldn't be in here!
741 assert(exitCode != 0);
742 return Expectation.compileTimeError;
743 }
744
745 Expectation outcome =
746 exitCode == 0 ? Expectation.pass : Expectation.compileTimeError;
747 return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative);
748 }
749 }
750
751 class KernelCompilationCommandOutputImpl extends CompilationCommandOutputImpl {
752 KernelCompilationCommandOutputImpl(
753 Command command,
754 int exitCode,
755 bool timedOut,
756 List<int> stdout,
757 List<int> stderr,
758 Duration time,
759 bool compilationSkipped)
760 : super(command, exitCode, timedOut, stdout, stderr, time,
761 compilationSkipped);
762
763 bool get canRunDependendCommands {
764 // See [BatchRunnerProcess]: 0 means success, 1 means compile-time error.
765 // TODO(asgerf): When the frontend supports it, continue running even if
766 // there were compile-time errors. See kernel_sdk issue #18.
767 return !hasCrashed && !timedOut && exitCode == 0;
768 }
769
770 Expectation result(TestCase testCase) {
771 Expectation result = super.result(testCase);
772 if (result.canBeOutcomeOf(Expectation.crash)) {
773 return Expectation.dartkCrash;
774 } else if (result.canBeOutcomeOf(Expectation.timeout)) {
775 return Expectation.dartkTimeout;
776 } else if (result.canBeOutcomeOf(Expectation.compileTimeError)) {
777 return Expectation.dartkCompileTimeError;
778 }
779 return result;
780 }
781
782 // If the compiler was able to produce a Kernel IR file we want to run the
783 // result on the Dart VM. We therefore mark the [KernelCompilationCommand] as
784 // successful.
785 // => This ensures we test that the DartVM produces correct CompileTime errors
786 // as it is supposed to for our test suites.
787 bool get successful => canRunDependendCommands;
788 }
789
790 class JsCommandlineOutputImpl extends CommandOutputImpl
791 with UnittestSuiteMessagesMixin {
792 JsCommandlineOutputImpl(Command command, int exitCode, bool timedOut,
793 List<int> stdout, List<int> stderr, Duration time)
794 : super(command, exitCode, timedOut, stdout, stderr, time, false, 0);
795
796 Expectation result(TestCase testCase) {
797 // Handle crashes and timeouts first
798 if (hasCrashed) return Expectation.crash;
799 if (hasTimedOut) return Expectation.timeout;
800 if (hasNonUtf8) return Expectation.nonUtf8Error;
801
802 if (testCase.hasRuntimeError) {
803 if (exitCode != 0) return Expectation.pass;
804 return Expectation.missingRuntimeError;
805 }
806
807 var outcome = exitCode == 0 ? Expectation.pass : Expectation.runtimeError;
808 outcome = _negateOutcomeIfIncompleteAsyncTest(outcome, decodeUtf8(stdout));
809 return _negateOutcomeIfNegativeTest(outcome, testCase.isNegative);
810 }
811 }
812
813 class PubCommandOutputImpl extends CommandOutputImpl {
814 PubCommandOutputImpl(PubCommand command, int exitCode, bool timedOut,
815 List<int> stdout, List<int> stderr, Duration time)
816 : super(command, exitCode, timedOut, stdout, stderr, time, false, 0);
817
818 Expectation result(TestCase testCase) {
819 // Handle crashes and timeouts first
820 if (hasCrashed) return Expectation.crash;
821 if (hasTimedOut) return Expectation.timeout;
822 if (hasNonUtf8) return Expectation.nonUtf8Error;
823
824 if (exitCode == 0) {
825 return Expectation.pass;
826 } else if ((command as PubCommand).command == 'get') {
827 return Expectation.pubGetError;
828 } else {
829 return Expectation.fail;
830 }
831 }
832 }
833
834 class ScriptCommandOutputImpl extends CommandOutputImpl {
835 final Expectation _result;
836
837 ScriptCommandOutputImpl(ScriptCommand command, this._result,
838 String scriptExecutionInformation, Duration time)
839 : super(command, 0, false, [], [], time, false, 0) {
840 var lines = scriptExecutionInformation.split("\n");
841 diagnostics.addAll(lines);
842 }
843
844 Expectation result(TestCase testCase) => _result;
845
846 bool get canRunDependendCommands => _result == Expectation.pass;
847
848 bool get successful => _result == Expectation.pass;
849 }
850
851 CommandOutput createCommandOutput(Command command, int exitCode, bool timedOut,
852 List<int> stdout, List<int> stderr, Duration time, bool compilationSkipped,
853 [int pid = 0]) {
854 if (command is ContentShellCommand) {
855 return new BrowserCommandOutputImpl(
856 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
857 } else if (command is BrowserTestCommand) {
858 return new HTMLBrowserCommandOutputImpl(
859 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
860 } else if (command is AnalysisCommand) {
861 return new AnalysisCommandOutputImpl(
862 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
863 } else if (command is VmCommand) {
864 return new VmCommandOutputImpl(
865 command, exitCode, timedOut, stdout, stderr, time, pid);
866 } else if (command is KernelCompilationCommand) {
867 return new KernelCompilationCommandOutputImpl(
868 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
869 } else if (command is AdbPrecompilationCommand) {
870 return new VmCommandOutputImpl(
871 command, exitCode, timedOut, stdout, stderr, time, pid);
872 } else if (command is CompilationCommand) {
873 if (command.displayName == 'precompiler' ||
874 command.displayName == 'app_jit') {
875 return new VmCommandOutputImpl(
876 command, exitCode, timedOut, stdout, stderr, time, pid);
877 }
878 return new CompilationCommandOutputImpl(
879 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped);
880 } else if (command is JSCommandlineCommand) {
881 return new JsCommandlineOutputImpl(
882 command, exitCode, timedOut, stdout, stderr, time);
883 } else if (command is PubCommand) {
884 return new PubCommandOutputImpl(
885 command, exitCode, timedOut, stdout, stderr, time);
886 }
887
888 return new CommandOutputImpl(command, exitCode, timedOut, stdout, stderr,
889 time, compilationSkipped, pid);
890 }
891
892 class UnittestSuiteMessagesMixin {
893 bool _isAsyncTest(String testOutput) {
894 return testOutput.contains("unittest-suite-wait-for-done");
895 }
896
897 bool _isAsyncTestSuccessful(String testOutput) {
898 return testOutput.contains("unittest-suite-success");
899 }
900
901 Expectation _negateOutcomeIfIncompleteAsyncTest(
902 Expectation outcome, String testOutput) {
903 // If this is an asynchronous test and the asynchronous operation didn't
904 // complete successfully, it's outcome is Expectation.FAIL.
905 // TODO: maybe we should introduce a AsyncIncomplete marker or so
906 if (outcome == Expectation.pass) {
907 if (_isAsyncTest(testOutput) && !_isAsyncTestSuccessful(testOutput)) {
908 return Expectation.fail;
909 }
910 }
911 return outcome;
912 }
913 }
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