2
2
/// <reference path="runnerbase.ts" />
3
3
const fs = require ( "fs" ) ;
4
4
const path = require ( "path" ) ;
5
+
6
+ interface ExecResult {
7
+ stdout : Buffer ;
8
+ stderr : Buffer ;
9
+ status : number ;
10
+ }
11
+
5
12
abstract class ExternalCompileRunnerBase extends RunnerBase {
6
13
abstract testDir : string ;
7
- abstract report ( result : any , cwd : string ) : string ;
14
+ abstract report ( result : ExecResult , cwd : string ) : string ;
8
15
enumerateTestFiles ( ) {
9
16
return Harness . IO . getDirectories ( this . testDir ) ;
10
17
}
@@ -24,8 +31,6 @@ abstract class ExternalCompileRunnerBase extends RunnerBase {
24
31
private runTest ( directoryName : string ) {
25
32
describe ( directoryName , ( ) => {
26
33
const cp = require ( "child_process" ) ;
27
- const path = require ( "path" ) ;
28
- const fs = require ( "fs" ) ;
29
34
30
35
it ( "should build successfully" , ( ) => {
31
36
const cwd = path . join ( __dirname , "../../" , this . testDir , directoryName ) ;
@@ -51,7 +56,7 @@ class UserCodeRunner extends ExternalCompileRunnerBase {
51
56
kind ( ) : TestRunnerKind {
52
57
return "user" ;
53
58
}
54
- report ( result : any ) {
59
+ report ( result : ExecResult ) {
55
60
// tslint:disable-next-line:no-null-keyword
56
61
return result . status === 0 && ! result . stdout . length && ! result . stderr . length ? null : `Exit Code: ${ result . status }
57
62
Standard output:
@@ -69,9 +74,9 @@ class DefinitelyTypedRunner extends ExternalCompileRunnerBase {
69
74
kind ( ) : TestRunnerKind {
70
75
return "dt" ;
71
76
}
72
- report ( result : any , cwd : string ) {
73
- const stdout = filterExpectedErrors ( result . stdout . toString ( ) , cwd )
74
- const stderr = result . stderr . toString ( )
77
+ report ( result : ExecResult , cwd : string ) {
78
+ const stdout = removeExpectedErrors ( result . stdout . toString ( ) , cwd ) ;
79
+ const stderr = result . stderr . toString ( ) ;
75
80
// tslint:disable-next-line:no-null-keyword
76
81
return ! stdout . length && ! stderr . length ? null : `Exit Code: ${ result . status }
77
82
Standard output:
@@ -83,29 +88,40 @@ ${stderr.replace(/\r\n/g, "\n")}`;
83
88
}
84
89
}
85
90
86
- function filterExpectedErrors ( errors : string , cwd : string ) : string {
87
- return breaks ( errors . split ( "\n" ) , s => / ^ \w + / . test ( s ) ) . filter ( isExpectedError ( cwd ) ) . map ( lines => lines . join ( "\n" ) ) . join ( "\n" ) ;
91
+ function removeExpectedErrors ( errors : string , cwd : string ) : string {
92
+ return ts . flatten ( splitBy ( errors . split ( "\n" ) , s => / ^ \S + / . test ( s ) ) . filter ( isUnexpectedError ( cwd ) ) ) . join ( "\n" ) ;
88
93
}
89
- function isExpectedError ( cwd : string ) {
94
+ /**
95
+ * Returns true if the line that caused the error contains '$ExpectError',
96
+ * or if the line before that one contains '$ExpectError'.
97
+ * '$ExpectError' is a marker used in Definitely Typed tests,
98
+ * meaning that the error should not contribute toward our error baslines.
99
+ */
100
+ function isUnexpectedError ( cwd : string ) {
90
101
return ( error : string [ ] ) => {
91
- if ( error . length === 0 ) {
92
- return true ;
93
- }
102
+ ts . Debug . assertGreaterThanOrEqual ( error . length , 1 ) ;
94
103
const match = error [ 0 ] . match ( / ( .+ \. t s ) \( ( \d + ) , \d + \) : e r r o r T S / ) ;
95
104
if ( ! match ) {
96
105
return true ;
97
106
}
98
- const errlines = fs . readFileSync ( path . join ( cwd , match [ 1 ] ) , { encoding : "utf8" } ) . split ( "\n" ) ;
99
- const index = parseInt ( match [ 2 ] ) ;
100
- const errline = index < errlines . length ? errlines [ index ] : "" ;
101
- const prevline = index - 1 < errlines . length && index > 0 ? errlines [ index - 1 ] : "" ;
102
- if ( errline . indexOf ( "$ExpectError" ) > - 1 || prevline . indexOf ( "$ExpectError" ) > - 1 ) {
103
- return false ;
104
- }
105
- return true ;
106
- }
107
+ const [ , errorFile , lineNumberString ] = match ;
108
+ const lines = fs . readFileSync ( path . join ( cwd , errorFile ) , { encoding : "utf8" } ) . split ( "\n" ) ;
109
+ const lineNumber = parseInt ( lineNumberString ) ;
110
+ ts . Debug . assertGreaterThanOrEqual ( lineNumber , 0 ) ;
111
+ ts . Debug . assertLessThan ( lineNumber , lines . length ) ;
112
+ const previousLine = lineNumber - 1 > 0 ? lines [ lineNumber - 1 ] : "" ;
113
+ return lines [ lineNumber ] . indexOf ( "$ExpectError" ) === - 1 && previousLine . indexOf ( "$ExpectError" ) === - 1 ;
114
+ } ;
107
115
}
108
- function breaks < T > ( xs : T [ ] , isStart : ( T : any ) => boolean ) : T [ ] [ ] {
116
+ /**
117
+ * Split an array into multiple arrays whenever `isStart` returns true.
118
+ * @example
119
+ * splitBy([1,2,3,4,5,6], isOdd)
120
+ * ==> [[1, 2], [3, 4], [5, 6]]
121
+ * where
122
+ * const isOdd = n => !!(n % 2)
123
+ */
124
+ function splitBy < T > ( xs : T [ ] , isStart : ( x : T ) => boolean ) : T [ ] [ ] {
109
125
const result = [ ] ;
110
126
let group : T [ ] = [ ] ;
111
127
for ( const x of xs ) {
0 commit comments