1
1
/// <reference path="harness.ts"/>
2
2
/// <reference path="runnerbase.ts" />
3
+ const fs = require ( "fs" ) ;
4
+ const path = require ( "path" ) ;
5
+
6
+ interface ExecResult {
7
+ stdout : Buffer ;
8
+ stderr : Buffer ;
9
+ status : number ;
10
+ }
11
+
3
12
abstract class ExternalCompileRunnerBase extends RunnerBase {
4
13
abstract testDir : string ;
5
- public enumerateTestFiles ( ) {
14
+ abstract report ( result : ExecResult , cwd : string ) : string ;
15
+ enumerateTestFiles ( ) {
6
16
return Harness . IO . getDirectories ( this . testDir ) ;
7
17
}
8
18
/** Setup the runner's tests so that they are ready to be executed by the harness
9
19
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
10
20
*/
11
- public initializeTests ( ) : void {
21
+ initializeTests ( ) : void {
12
22
// Read in and evaluate the test list
13
23
const testList = this . tests && this . tests . length ? this . tests : this . enumerateTestFiles ( ) ;
14
24
@@ -21,8 +31,6 @@ abstract class ExternalCompileRunnerBase extends RunnerBase {
21
31
private runTest ( directoryName : string ) {
22
32
describe ( directoryName , ( ) => {
23
33
const cp = require ( "child_process" ) ;
24
- const path = require ( "path" ) ;
25
- const fs = require ( "fs" ) ;
26
34
27
35
it ( "should build successfully" , ( ) => {
28
36
const cwd = path . join ( __dirname , "../../" , this . testDir , directoryName ) ;
@@ -36,32 +44,99 @@ abstract class ExternalCompileRunnerBase extends RunnerBase {
36
44
if ( install . status !== 0 ) throw new Error ( `NPM Install for ${ directoryName } failed!` ) ;
37
45
}
38
46
Harness . Baseline . runBaseline ( `${ this . kind ( ) } /${ directoryName } .log` , ( ) => {
39
- const result = cp . spawnSync ( `node` , [ path . join ( __dirname , "tsc.js" ) ] , { cwd, timeout, shell : true } ) ;
40
- // tslint:disable-next-line:no-null-keyword
41
- return result . status === 0 && ! result . stdout . length && ! result . stderr . length ? null : `Exit Code: ${ result . status }
42
- Standard output:
43
- ${ result . stdout . toString ( ) . replace ( / \r \n / g, "\n" ) }
44
-
45
-
46
- Standard error:
47
- ${ result . stderr . toString ( ) . replace ( / \r \n / g, "\n" ) } `;
47
+ return this . report ( cp . spawnSync ( `node` , [ path . join ( __dirname , "tsc.js" ) ] , { cwd, timeout, shell : true } ) , cwd ) ;
48
48
} ) ;
49
49
} ) ;
50
50
} ) ;
51
51
}
52
52
}
53
53
54
54
class UserCodeRunner extends ExternalCompileRunnerBase {
55
- public readonly testDir = "tests/cases/user/" ;
56
- public kind ( ) : TestRunnerKind {
55
+ readonly testDir = "tests/cases/user/" ;
56
+ kind ( ) : TestRunnerKind {
57
57
return "user" ;
58
58
}
59
+ report ( result : ExecResult ) {
60
+ // tslint:disable-next-line:no-null-keyword
61
+ return result . status === 0 && ! result . stdout . length && ! result . stderr . length ? null : `Exit Code: ${ result . status }
62
+ Standard output:
63
+ ${ result . stdout . toString ( ) . replace ( / \r \n / g, "\n" ) }
64
+
65
+
66
+ Standard error:
67
+ ${ result . stderr . toString ( ) . replace ( / \r \n / g, "\n" ) } `;
68
+ }
59
69
}
60
70
61
71
class DefinitelyTypedRunner extends ExternalCompileRunnerBase {
62
- public readonly testDir = "../DefinitelyTyped/types/" ;
63
- public workingDirectory = this . testDir ;
64
- public kind ( ) : TestRunnerKind {
72
+ readonly testDir = "../DefinitelyTyped/types/" ;
73
+ workingDirectory = this . testDir ;
74
+ kind ( ) : TestRunnerKind {
65
75
return "dt" ;
66
76
}
77
+ report ( result : ExecResult , cwd : string ) {
78
+ const stdout = removeExpectedErrors ( result . stdout . toString ( ) , cwd ) ;
79
+ const stderr = result . stderr . toString ( ) ;
80
+ // tslint:disable-next-line:no-null-keyword
81
+ return ! stdout . length && ! stderr . length ? null : `Exit Code: ${ result . status }
82
+ Standard output:
83
+ ${ stdout . replace ( / \r \n / g, "\n" ) }
84
+
85
+
86
+ Standard error:
87
+ ${ stderr . replace ( / \r \n / g, "\n" ) } `;
88
+ }
89
+ }
90
+
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" ) ;
93
+ }
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 ) {
101
+ return ( error : string [ ] ) => {
102
+ ts . Debug . assertGreaterThanOrEqual ( error . length , 1 ) ;
103
+ const match = error [ 0 ] . match ( / ( .+ \. t s ) \( ( \d + ) , \d + \) : e r r o r T S / ) ;
104
+ if ( ! match ) {
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 ! ts . stringContains ( lines [ lineNumber ] , "$ExpectError" ) && ! ts . stringContains ( previousLine , "$ExpectError" ) ;
114
+ } ;
115
+ }
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 [ ] [ ] {
125
+ const result = [ ] ;
126
+ let group : T [ ] = [ ] ;
127
+ for ( const x of xs ) {
128
+ if ( isStart ( x ) ) {
129
+ if ( group . length ) {
130
+ result . push ( group ) ;
131
+ }
132
+ group = [ x ] ;
133
+ }
134
+ else {
135
+ group . push ( x ) ;
136
+ }
137
+ }
138
+ if ( group . length ) {
139
+ result . push ( group ) ;
140
+ }
141
+ return result ;
67
142
}
0 commit comments