@@ -8,6 +8,7 @@ import 'dart:convert';
8
8
import 'dart:io' as io;
9
9
10
10
import 'package:status_file/expectation.dart' ;
11
+ import 'package:test_runner/src/test_file.dart' ;
11
12
12
13
import 'browser_controller.dart' ;
13
14
import 'command.dart' ;
@@ -128,7 +129,7 @@ class CommandOutput extends UniqueObject {
128
129
}
129
130
130
131
/// Called when producing output for a test failure to describe this output.
131
- void describe (Progress progress, OutputWriter output) {
132
+ void describe (TestCase testCase, Progress progress, OutputWriter output) {
132
133
output.subsection ("exit code" );
133
134
output.write (exitCode.toString ());
134
135
@@ -351,7 +352,7 @@ class BrowserCommandOutput extends CommandOutput
351
352
return _rawOutcome;
352
353
}
353
354
354
- void describe (Progress progress, OutputWriter output) {
355
+ void describe (TestCase testCase, Progress progress, OutputWriter output) {
355
356
if (_jsonResult != null ) {
356
357
_describeEvents (progress, output);
357
358
} else {
@@ -360,7 +361,7 @@ class BrowserCommandOutput extends CommandOutput
360
361
output.write (_result.lastKnownMessage);
361
362
}
362
363
363
- super .describe (progress, output);
364
+ super .describe (testCase, progress, output);
364
365
365
366
if (_result.browserOutput.stdout.isNotEmpty) {
366
367
output.subsection ("Browser stdout" );
@@ -434,10 +435,11 @@ class BrowserCommandOutput extends CommandOutput
434
435
}
435
436
436
437
class AnalysisCommandOutput extends CommandOutput {
437
- // An error line has 8 fields that look like:
438
- // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source.
439
- static const int _errorLevel = 0 ;
440
- static const int _formattedError = 7 ;
438
+ /// Reported static errors, parsed from [stderr] .
439
+ final List <StaticError > _errors = [];
440
+
441
+ /// Reported static warnings, parsed from [stderr] .
442
+ final List <StaticError > _warnings = [];
441
443
442
444
AnalysisCommandOutput (
443
445
Command command,
@@ -448,7 +450,20 @@ class AnalysisCommandOutput extends CommandOutput {
448
450
Duration time,
449
451
bool compilationSkipped)
450
452
: super (command, exitCode, timedOut, stdout, stderr, time,
451
- compilationSkipped, 0 );
453
+ compilationSkipped, 0 ) {
454
+ _parseOutput ();
455
+ }
456
+
457
+ @override
458
+ void describe (TestCase testCase, Progress progress, OutputWriter output) {
459
+ if (testCase.testFile.isStaticErrorTest) {
460
+ _validateExpectedErrors (testCase, output);
461
+ }
462
+
463
+ if (! testCase.testFile.isStaticErrorTest || progress == Progress .verbose) {
464
+ super .describe (testCase, progress, output);
465
+ }
466
+ }
452
467
453
468
Expectation result (TestCase testCase) {
454
469
// TODO(kustermann): If we run the analyzer not in batch mode, make sure
@@ -460,41 +475,41 @@ class AnalysisCommandOutput extends CommandOutput {
460
475
if (hasTimedOut) return Expectation .timeout;
461
476
if (hasNonUtf8) return Expectation .nonUtf8Error;
462
477
463
- // Get the errors/warnings from the analyzer
464
- var errors = < String > [];
465
- var warnings = < String > [] ;
466
- parseAnalyzerOutput (errors, warnings);
478
+ // If it's a static error test, validate the exact errors.
479
+ if (testCase.testFile.isStaticErrorTest) {
480
+ return _validateExpectedErrors (testCase) ;
481
+ }
467
482
468
483
// Handle negative
469
484
if (testCase.isNegative) {
470
- return errors .isNotEmpty
485
+ return _errors .isNotEmpty
471
486
? Expectation .pass
472
487
: Expectation .missingCompileTimeError;
473
488
}
474
489
475
490
// Handle errors / missing errors
476
491
if (testCase.hasCompileError) {
477
- if (errors .isNotEmpty) {
492
+ if (_errors .isNotEmpty) {
478
493
return Expectation .pass;
479
494
}
480
495
return Expectation .missingCompileTimeError;
481
496
}
482
- if (errors .isNotEmpty) {
497
+ if (_errors .isNotEmpty) {
483
498
return Expectation .compileTimeError;
484
499
}
485
500
486
501
// Handle static warnings / missing static warnings
487
502
if (testCase.hasStaticWarning) {
488
- if (warnings .isNotEmpty) {
503
+ if (_warnings .isNotEmpty) {
489
504
return Expectation .pass;
490
505
}
491
506
return Expectation .missingStaticWarning;
492
507
}
493
- if (warnings .isNotEmpty) {
508
+ if (_warnings .isNotEmpty) {
494
509
return Expectation .staticWarning;
495
510
}
496
511
497
- assert (errors .isEmpty && warnings .isEmpty);
512
+ assert (_errors .isEmpty && _warnings .isEmpty);
498
513
assert (! testCase.hasCompileError && ! testCase.hasStaticWarning);
499
514
return Expectation .pass;
500
515
}
@@ -511,23 +526,32 @@ class AnalysisCommandOutput extends CommandOutput {
511
526
if (hasTimedOut) return Expectation .timeout;
512
527
if (hasNonUtf8) return Expectation .nonUtf8Error;
513
528
514
- // Get the errors/warnings from the analyzer
515
- var errors = < String > [];
516
- var warnings = < String > [] ;
517
- parseAnalyzerOutput (errors, warnings);
529
+ // If it's a static error test, validate the exact errors.
530
+ if (testCase.testFile.isStaticErrorTest) {
531
+ return _validateExpectedErrors (testCase) ;
532
+ }
518
533
519
- if (errors .isNotEmpty) {
534
+ if (_errors .isNotEmpty) {
520
535
return Expectation .compileTimeError;
521
536
}
522
- if (warnings .isNotEmpty) {
537
+ if (_warnings .isNotEmpty) {
523
538
return Expectation .staticWarning;
524
539
}
525
540
return Expectation .pass;
526
541
}
527
542
528
- void parseAnalyzerOutput (List <String > outErrors, List <String > outWarnings) {
529
- // Parse a line delimited by the | character using \ as an escape character
530
- // like: FOO|BAR|FOO\|BAR|FOO\\BAZ as 4 fields: FOO BAR FOO|BAR FOO\BAZ
543
+ /// Parses the machine-readable output of analyzer, which looks like:
544
+ ///
545
+ /// ERROR|STATIC_TYPE_WARNING|SOME_ERROR_CODE|/path/to/some_test.dart|9|26|1|Error message.
546
+ ///
547
+ /// Pipes can be escaped with backslashes:
548
+ ///
549
+ /// FOO|BAR|FOO\|BAR|FOO\\BAZ
550
+ ///
551
+ /// Is parsed as:
552
+ ///
553
+ /// FOO BAR FOO|BAR FOO\BAZ
554
+ void _parseOutput () {
531
555
List <String > splitMachineError (String line) {
532
556
var field = StringBuffer ();
533
557
var result = < String > [];
@@ -550,20 +574,82 @@ class AnalysisCommandOutput extends CommandOutput {
550
574
return result;
551
575
}
552
576
553
- for (String line in decodeUtf8 (super . stderr).split ("\n " )) {
577
+ for (var line in decodeUtf8 (stderr).split ("\n " )) {
554
578
if (line.isEmpty) continue ;
555
579
556
- List <String > fields = splitMachineError (line);
557
- // We only consider errors/warnings for files of interest.
558
- if (fields.length > _formattedError) {
559
- if (fields[_errorLevel] == 'ERROR' ) {
560
- outErrors.add (fields[_formattedError]);
561
- } else if (fields[_errorLevel] == 'WARNING' ) {
562
- outWarnings.add (fields[_formattedError]);
580
+ var fields = splitMachineError (line);
581
+
582
+ // Lines without enough fields are other output we don't care about.
583
+ if (fields.length >= 8 ) {
584
+ var severity = fields[0 ];
585
+ var errorCode = "${fields [1 ]}.${fields [2 ]}" ;
586
+ var line = int .parse (fields[4 ]);
587
+ var column = int .parse (fields[5 ]);
588
+ var length = int .parse (fields[6 ]);
589
+
590
+ var error = StaticError (
591
+ line: line, column: column, length: length, code: errorCode);
592
+
593
+ if (severity == 'ERROR' ) {
594
+ _errors.add (error);
595
+ } else if (severity == 'WARNING' ) {
596
+ _warnings.add (error);
597
+ }
598
+ }
599
+ }
600
+ }
601
+
602
+ // TODO(rnystrom): This will probably move up to CommandOutput once other
603
+ // front ends support static error tests.
604
+ /// Compare the actual errors produced to the expected static errors parsed
605
+ /// from the test file.
606
+ ///
607
+ /// Returns [Expectation.pass] if all expected errors were correctly
608
+ /// reported.
609
+ ///
610
+ /// If [writer] is given, outputs a description of any error mismatches.
611
+ Expectation _validateExpectedErrors (TestCase testCase,
612
+ [OutputWriter writer]) {
613
+ // Don't require the test or analyzer to output in any specific order.
614
+ var expected = testCase.testFile.expectedErrors.toList ();
615
+ var actual = _errors.toList ();
616
+ expected.sort ();
617
+ actual.sort ();
618
+
619
+ if (writer != null ) writer.subsection ("incorrect static errors" );
620
+
621
+ var success = expected.length == actual.length;
622
+ for (var i = 0 ; i < expected.length && i < actual.length; i++ ) {
623
+ var differences = expected[i].describeDifferences (actual[i]);
624
+ if (differences == null ) continue ;
625
+
626
+ if (writer != null ) {
627
+ writer.write (actual[i].location);
628
+ for (var difference in differences) {
629
+ writer.write ("- $difference " );
563
630
}
564
- // OK to Skip error output that doesn't match the machine format.
631
+ writer.separator ();
632
+ }
633
+
634
+ success = false ;
635
+ }
636
+
637
+ if (writer != null ) {
638
+ writer.subsection ("missing expected static errors" );
639
+ for (var i = actual.length; i < expected.length; i++ ) {
640
+ writer.write (expected[i].toString ());
641
+ writer.separator ();
642
+ }
643
+
644
+ writer.subsection ("reported unexpected static errors" );
645
+ for (var i = expected.length; i < actual.length; i++ ) {
646
+ writer.write (actual[i].toString ());
647
+ writer.separator ();
565
648
}
566
649
}
650
+
651
+ // TODO(rnystrom): Is there a better expectation we can use?
652
+ return success ? Expectation .pass : Expectation .missingCompileTimeError;
567
653
}
568
654
}
569
655
0 commit comments