1+ /* --------------------------------------------------------------------------------------------
2+ * Copyright (c) Microsoft Corporation. All rights reserved.
3+ * Licensed under the MIT License. See License.txt in the project root for license information.
4+ * ------------------------------------------------------------------------------------------ */
5+
6+ import * as fs from 'fs' ;
7+ import * as os from 'os' ;
8+ import * as path from 'path' ;
9+ import * as cp from 'child_process' ;
10+ import * as util from 'util' ;
11+
12+ import * as shelljs from 'shelljs' ;
13+
14+ const exists = util . promisify ( fs . exists ) ;
15+ const readFile = util . promisify ( fs . readFile ) ;
16+ const stat = util . promisify ( fs . stat ) ;
17+ const readdir = util . promisify ( fs . readdir ) ;
18+
19+ const ROOT = path . join ( __dirname , '..' , '..' , '..' , '..' ) ;
20+
21+ interface DataFormat {
22+ name : string ;
23+ repository : string ;
24+ branch ?: string ;
25+ init ?: { command : string , args ?: string [ ] } [ ] ;
26+ tests : { tsconfig : string , projectRoot ?: string , cwd ?: string , } [ ] ;
27+ }
28+
29+ interface TestStatistics {
30+ passed : string [ ] ;
31+ failed : string [ ] ;
32+ }
33+
34+ async function runCommand ( command : string , args ?: ReadonlyArray < string > , cwd ?: string ) : Promise < number | undefined > {
35+ return new Promise ( ( resolve , reject ) => {
36+ const process = cp . spawn ( command , args , {
37+ cwd,
38+ stdio : 'inherit' ,
39+ shell : true
40+ } ) ;
41+ let resolved : boolean = false ;
42+ const handleEnd = ( code : number | null , signal : string | null ) => {
43+ if ( resolved ) {
44+ return ;
45+ }
46+ resolved = true ;
47+ if ( signal ) {
48+ reject ( 1 ) ;
49+ }
50+ if ( code === null || code === 0 ) {
51+ resolve ( ) ;
52+ } else {
53+ reject ( code ) ;
54+ }
55+ }
56+ process . on ( 'close' , handleEnd ) ;
57+ process . on ( 'exit' , handleEnd ) ;
58+ process . on ( 'error' , ( error ) => {
59+ console . error ( error ) ;
60+ reject ( 1 ) ;
61+ } ) ;
62+ } )
63+ }
64+
65+ async function runTest ( filename : string ) : Promise < number | undefined > {
66+ process . stdout . write ( `Running tests for: ${ filename } \n` ) ;
67+ if ( ! filename ) {
68+ process . stderr . write ( `No repository description provided.\n` ) ;
69+ return 1 ;
70+ }
71+ if ( ! await exists ( filename ) ) {
72+ process . stderr . write ( `Repository description ${ filename } not found.` ) ;
73+ return 1 ;
74+ }
75+ const data : DataFormat = JSON . parse ( await readFile ( filename , { encoding : 'utf8' } ) ) ;
76+ const tmpdir = os . tmpdir ( ) ;
77+ let directory = path . join ( tmpdir , data . name ) ;
78+
79+ if ( await exists ( directory ) ) {
80+ shelljs . rm ( '-rf' , directory ) ;
81+ }
82+
83+ await runCommand ( 'git' , [ 'clone' , data . repository , directory ] ) ;
84+ if ( data . branch ) {
85+ await runCommand ( 'git' , [ 'checkout' , data . branch ] , directory ) ;
86+ }
87+ if ( data . init ) {
88+ for ( let init of data . init ) {
89+ await runCommand ( init . command , init . args , directory ) ;
90+ }
91+ }
92+ if ( data . tests ) {
93+ for ( let test of data . tests ) {
94+ let cwd = test . cwd ? path . join ( directory , test . cwd ) : directory ;
95+ process . stdout . write ( `Running LSIF exporter for ${ path . join ( cwd , test . tsconfig ) } \n` ) ;
96+ let args : string [ ] = [ path . join ( ROOT , 'tsc' , 'lib' , 'main.js' ) ,
97+ '-p' , test . tsconfig ,
98+ '--outputFormat' , 'line'
99+ ] ;
100+ if ( test . projectRoot ) {
101+ args . push ( '--projectRoot' , test . projectRoot ) ;
102+ }
103+ args . push ( '--out' , path . join ( cwd , `${ data . name } .lsif` ) ) ;
104+ await runCommand ( 'node' , args , cwd ) ;
105+ process . stdout . write ( `\n` ) ;
106+ }
107+ }
108+ if ( await exists ( directory ) ) {
109+ shelljs . rm ( '-rf' , directory ) ;
110+ }
111+ return undefined ;
112+ }
113+
114+
115+ async function main ( pathname : string | undefined ) : Promise < number | undefined > {
116+ if ( pathname === undefined ) {
117+ console . error ( `No test file or test directory provided` ) ;
118+ return 1 ;
119+ }
120+
121+ let testStats : TestStatistics = { passed : [ ] , failed : [ ] } ;
122+ let stats = await stat ( pathname ) ;
123+ if ( stats . isFile ( ) && path . extname ( pathname ) === '.json' ) {
124+ try {
125+ await runTest ( pathname ) ;
126+ testStats . passed . push ( pathname ) ;
127+ } catch ( error ) {
128+ testStats . failed . push ( pathname ) ;
129+ console . log ( error ) ;
130+ }
131+ } else if ( stats . isDirectory ( ) ) {
132+ let entries = await readdir ( pathname ) ;
133+ for ( let entry of entries ) {
134+ if ( entry === '.' || entry === '..' ) {
135+ continue ;
136+ }
137+ let candidate = path . join ( pathname , entry ) ;
138+ let stats = await stat ( candidate ) ;
139+ if ( stats . isFile ( ) && path . extname ( candidate ) === '.json' ) {
140+ try {
141+ await runTest ( candidate ) ;
142+ testStats . passed . push ( candidate ) ;
143+ } catch ( error ) {
144+ testStats . failed . push ( candidate ) ;
145+ console . log ( error ) ;
146+ }
147+ }
148+ }
149+ } else {
150+ console . error ( 'No tests to run found' ) ;
151+ return 1 ;
152+ }
153+ process . stdout . write ( `\n\nTest summary:\n` ) ;
154+ process . stdout . write ( `\tPassed tests: ${ testStats . passed . length } \n` ) ;
155+ process . stdout . write ( `\tFailed tests: ${ testStats . failed . length } \n` ) ;
156+ if ( testStats . failed . length > 0 ) {
157+ for ( let failed of testStats . failed ) {
158+ process . stdout . write ( `\t\t${ failed } \n` ) ;
159+ }
160+ }
161+ return testStats . failed . length > 0 ? 1 : undefined ;
162+ }
163+
164+ main ( process . argv [ 2 ] ) . then ( ( error ) => {
165+ if ( error !== undefined ) {
166+ process . exitCode = error ;
167+ }
168+ } , ( error ) => {
169+ process . exitCode = 1 ;
170+ } ) ;
0 commit comments