@@ -113,180 +113,6 @@ abstract class ExternalCompileRunnerBase extends RunnerBase {
113113 }
114114}
115115
116- export class UserCodeRunner extends ExternalCompileRunnerBase {
117- readonly testDir = "tests/cases/user/" ;
118- kind ( ) : TestRunnerKind {
119- return "user" ;
120- }
121- report ( result : ExecResult ) {
122- // eslint-disable-next-line no-null/no-null
123- return result . status === 0 && ! result . stdout . length && ! result . stderr . length ? null : `Exit Code: ${ result . status }
124- Standard output:
125- ${ sortErrors ( stripAbsoluteImportPaths ( result . stdout . toString ( ) . replace ( / \r \n / g, "\n" ) ) ) }
126-
127-
128- Standard error:
129- ${ stripAbsoluteImportPaths ( result . stderr . toString ( ) . replace ( / \r \n / g, "\n" ) ) } `;
130- }
131- }
132-
133- export class DockerfileRunner extends ExternalCompileRunnerBase {
134- readonly testDir = "tests/cases/docker/" ;
135- kind ( ) : TestRunnerKind {
136- return "docker" ;
137- }
138- override initializeTests ( ) : void {
139- // Read in and evaluate the test list
140- const testList = this . tests && this . tests . length ? this . tests : this . getTestFiles ( ) ;
141-
142- // eslint-disable-next-line @typescript-eslint/no-this-alias
143- const cls = this ;
144- describe ( `${ this . kind ( ) } code samples` , function ( this : Mocha . Suite ) {
145- this . timeout ( cls . timeout ) ; // 20 minutes
146- before ( ( ) => {
147- // cached because workspace is hashed to determine cacheability
148- cls . exec ( "docker" , [ "build" , "." , "-t" , "typescript/typescript" , "-f" , cls . testDir + "Dockerfile" ] , {
149- cwd : IO . getWorkspaceRoot ( ) ,
150- env : { ...process . env , DOCKER_BUILDKIT : "1" } , // We need buildkit to allow Dockerfile.dockerignore to work.
151- } ) ;
152- } ) ;
153- for ( const test of testList ) {
154- const directory = typeof test === "string" ? test : test . file ;
155- const cwd = path . join ( IO . getWorkspaceRoot ( ) , cls . testDir , directory ) ;
156- it ( `should build ${ directory } successfully` , ( ) => {
157- const imageName = `tstest/${ directory } ` ;
158- cls . exec ( "docker" , [ "build" , "--no-cache" , "." , "-t" , imageName ] , { cwd } ) ; // --no-cache so the latest version of the repos referenced is always fetched
159- const cp : typeof import ( "child_process" ) = require ( "child_process" ) ;
160- Baseline . runBaseline ( `${ cls . kind ( ) } /${ directory } .log` , cls . report ( cp . spawnSync ( `docker` , [ "run" , imageName ] , { cwd, timeout : cls . timeout , shell : true } ) ) ) ;
161- } ) ;
162- }
163- } ) ;
164- }
165-
166- private timeout = 1_200_000 ; // 20 minutes;
167- private exec ( command : string , args : string [ ] , options : { cwd : string ; env ?: NodeJS . ProcessEnv } ) : void {
168- const cp : typeof import ( "child_process" ) = require ( "child_process" ) ;
169- const stdio = isWorker ? "pipe" : "inherit" ;
170- const res = cp . spawnSync ( isWorker ? `${ command } 2>&1` : command , args , { timeout : this . timeout , shell : true , stdio, ...options } ) ;
171- if ( res . status !== 0 ) {
172- throw new Error ( `${ command } ${ args . join ( " " ) } for ${ options . cwd } failed: ${ res . stdout && res . stdout . toString ( ) } ` ) ;
173- }
174- }
175- report ( result : ExecResult ) {
176- // eslint-disable-next-line no-null/no-null
177- return result . status === 0 && ! result . stdout . length && ! result . stderr . length ? null : `Exit Code: ${ result . status }
178- Standard output:
179- ${ sanitizeDockerfileOutput ( result . stdout . toString ( ) ) }
180-
181-
182- Standard error:
183- ${ sanitizeDockerfileOutput ( result . stderr . toString ( ) ) } `;
184- }
185- }
186-
187- function sanitizeDockerfileOutput ( result : string ) : string {
188- return [
189- normalizeNewlines ,
190- stripANSIEscapes ,
191- stripRushStageNumbers ,
192- stripWebpackHash ,
193- sanitizeVersionSpecifiers ,
194- sanitizeTimestamps ,
195- sanitizeSizes ,
196- sanitizeUnimportantGulpOutput ,
197- stripAbsoluteImportPaths ,
198- ] . reduce ( ( result , f ) => f ( result ) , result ) ;
199- }
200-
201- function normalizeNewlines ( result : string ) : string {
202- return result . replace ( / \r \n / g, "\n" ) ;
203- }
204-
205- function stripANSIEscapes ( result : string ) : string {
206- return result . replace ( / \x1b \[ [ 0 - 9 ; ] * [ a - z A - Z ] / g, "" ) ;
207- }
208-
209- function stripRushStageNumbers ( result : string ) : string {
210- return result . replace ( / \d + o f \d + : / g, "XX of XX:" ) ;
211- }
212-
213- function stripWebpackHash ( result : string ) : string {
214- return result . replace ( / H a s h : \w + / g, "Hash: [redacted]" ) ;
215- }
216-
217- function sanitizeSizes ( result : string ) : string {
218- return result . replace ( / \d + ( \. \d + ) ? ( ( K i | M ) B | b y t e s ) / g, "X KiB" ) ;
219- }
220-
221- /**
222- * Gulp's output order within a `parallel` block is nondeterministic (and there's no way to configure it to execute in series),
223- * so we purge as much of the gulp output as we can
224- */
225- function sanitizeUnimportantGulpOutput ( result : string ) : string {
226- return result . replace ( / ^ .* ( \] ( S t a r t i n g ) | ( F i n i s h e d ) ) .* $ / gm, "" ) // "gulp" task start/end messages (nondeterministic order)
227- . replace ( / ^ .* ( \] . ( f i n i s h e d ) | ( s t a r t e d ) ) .* $ / gm, "" ) // "just" task start/end messages (nondeterministic order)
228- . replace ( / ^ .* \] R e s p a w n e d t o P I D : \d + .* $ / gm, "" ) // PID of child is OS and system-load dependent (likely stableish in a container but still dangerous)
229- . replace ( / \n + / g, "\n" )
230- . replace ( / \/ t m p \/ y a r n - - .* ?\/ n o d e / g, "" ) ;
231- }
232-
233- function sanitizeTimestamps ( result : string ) : string {
234- return result . replace ( / \[ \d ? \d : \d \d : \d \d ( A | P ) M \] / g, "[XX:XX:XX XM]" )
235- . replace ( / \[ \d ? \d : \d \d : \d \d \] / g, "[XX:XX:XX]" )
236- . replace ( / \/ \d + - \d + - [ \d _ T Z ] + - d e b u g .l o g / g, "\/XXXX-XX-XXXXXXXXX-debug.log" )
237- . replace ( / \d + \/ \d + \/ \d + \d + : \d + : \d + ( A M | P M ) / g, "XX/XX/XX XX:XX:XX XM" )
238- . replace ( / \d + ( \. \d + ) ? s e c ( o n d s ? ) ? / g, "? seconds" )
239- . replace ( / \d + ( \. \d + ) ? m i n ( u t e s ? ) ? / g, "" )
240- . replace ( / \d + ( \. \d + ) ? ? m ? s / g, "?s" )
241- . replace ( / \( \? s \) / g, "" ) ;
242- }
243-
244- function sanitizeVersionSpecifiers ( result : string ) : string {
245- return result
246- . replace ( / \d + .\d + .\d + - i n s i d e r s .\d \d \d \d \d \d \d \d / g, "X.X.X-insiders.xxxxxxxx" )
247- . replace ( / R u s h M u l t i - P r o j e c t B u i l d T o o l ( \d + ) \. \d + \. \d + / g, "Rush Multi-Project Build Tool $1.X.X" )
248- . replace ( / ( [ @ v \( ) ] ) \d + \. \d + \. \d + / g, "$1X.X.X" )
249- . replace ( / w e b p a c k ( \d + ) \. \d + \. \d + / g, "webpack $1.X.X" )
250- . replace ( / W e b p a c k v e r s i o n : ( \d + ) \. \d + \. \d + / g, "Webpack version: $1.X.X" ) ;
251- }
252-
253- /**
254- * Import types and some other error messages use absolute paths in errors as they have no context to be written relative to;
255- * This is problematic for error baselines, so we grep for them and strip them out.
256- */
257- function stripAbsoluteImportPaths ( result : string ) {
258- const workspaceRegexp = new RegExp ( IO . getWorkspaceRoot ( ) . replace ( / \\ / g, "\\\\" ) , "g" ) ;
259- return result
260- . replace ( / i m p o r t \( " .* ?\/ t e s t s \/ c a s e s \/ u s e r \/ / g, `import("/` )
261- . replace ( / M o d u l e ' " .* ?\/ t e s t s \/ c a s e s \/ u s e r \/ / g, `Module '"/` )
262- . replace ( workspaceRegexp , "../../.." ) ;
263- }
264-
265- function sortErrors ( result : string ) {
266- return ts . flatten ( splitBy ( result . split ( "\n" ) , s => / ^ \S + / . test ( s ) ) . sort ( compareErrorStrings ) ) . join ( "\n" ) ;
267- }
268-
269- const errorRegexp = / ^ ( .+ \. [ t j ] s x ? ) \( ( \d + ) , ( \d + ) \) ( : e r r o r T S .* ) / ;
270- function compareErrorStrings ( a : string [ ] , b : string [ ] ) {
271- ts . Debug . assertGreaterThanOrEqual ( a . length , 1 ) ;
272- ts . Debug . assertGreaterThanOrEqual ( b . length , 1 ) ;
273- const matchA = a [ 0 ] . match ( errorRegexp ) ;
274- if ( ! matchA ) {
275- return - 1 ;
276- }
277- const matchB = b [ 0 ] . match ( errorRegexp ) ;
278- if ( ! matchB ) {
279- return 1 ;
280- }
281- const [ , errorFileA , lineNumberStringA , columnNumberStringA , remainderA ] = matchA ;
282- const [ , errorFileB , lineNumberStringB , columnNumberStringB , remainderB ] = matchB ;
283- return ts . comparePathsCaseSensitive ( errorFileA , errorFileB ) ||
284- ts . compareValues ( parseInt ( lineNumberStringA ) , parseInt ( lineNumberStringB ) ) ||
285- ts . compareValues ( parseInt ( columnNumberStringA ) , parseInt ( columnNumberStringB ) ) ||
286- ts . compareStringsCaseSensitive ( remainderA , remainderB ) ||
287- ts . compareStringsCaseSensitive ( a . slice ( 1 ) . join ( "\n" ) , b . slice ( 1 ) . join ( "\n" ) ) ;
288- }
289-
290116export class DefinitelyTypedRunner extends ExternalCompileRunnerBase {
291117 readonly testDir = "../DefinitelyTyped/types/" ;
292118 override workingDirectory = this . testDir ;
@@ -304,31 +130,3 @@ Standard error:
304130${ result . stderr . toString ( ) . replace ( / \r \n / g, "\n" ) } `;
305131 }
306132}
307-
308- /**
309- * Split an array into multiple arrays whenever `isStart` returns true.
310- * @example
311- * splitBy([1,2,3,4,5,6], isOdd)
312- * ==> [[1, 2], [3, 4], [5, 6]]
313- * where
314- * const isOdd = n => !!(n % 2)
315- */
316- function splitBy < T > ( xs : T [ ] , isStart : ( x : T ) => boolean ) : T [ ] [ ] {
317- const result = [ ] ;
318- let group : T [ ] = [ ] ;
319- for ( const x of xs ) {
320- if ( isStart ( x ) ) {
321- if ( group . length ) {
322- result . push ( group ) ;
323- }
324- group = [ x ] ;
325- }
326- else {
327- group . push ( x ) ;
328- }
329- }
330- if ( group . length ) {
331- result . push ( group ) ;
332- }
333- return result ;
334- }
0 commit comments