@@ -59,6 +59,12 @@ interface IOLog {
5959 path : string ;
6060 result ?: string ;
6161 } [ ] ;
62+ directoriesRead : {
63+ path : string ,
64+ extension : string ,
65+ exclude : string [ ] ,
66+ result : string [ ]
67+ } [ ] ;
6268}
6369
6470interface PlaybackControl {
@@ -95,12 +101,15 @@ module Playback {
95101
96102 export interface PlaybackIO extends Harness . IO , PlaybackControl { }
97103
104+ export interface PlaybackSystem extends ts . System , PlaybackControl { }
105+
98106 function createEmptyLog ( ) : IOLog {
99107 return {
100108 timestamp : ( new Date ( ) ) . toString ( ) ,
101109 arguments : [ ] ,
102110 currentDirectory : "" ,
103111 filesRead : [ ] ,
112+ directoriesRead : [ ] ,
104113 filesWritten : [ ] ,
105114 filesDeleted : [ ] ,
106115 filesAppended : [ ] ,
@@ -114,8 +123,10 @@ module Playback {
114123 } ;
115124 }
116125
117- function initWrapper < T > ( wrapper : PlaybackControl , underlying : T ) {
118- Object . keys ( underlying ) . forEach ( prop => {
126+ function initWrapper ( wrapper : PlaybackSystem , underlying : ts . System ) : void ;
127+ function initWrapper ( wrapper : PlaybackIO , underlying : Harness . IO ) : void ;
128+ function initWrapper ( wrapper : PlaybackSystem | PlaybackIO , underlying : ts . System | Harness . IO ) : void {
129+ ts . forEach ( Object . keys ( underlying ) , prop => {
119130 ( < any > wrapper ) [ prop ] = ( < any > underlying ) [ prop ] ;
120131 } ) ;
121132
@@ -135,6 +146,93 @@ module Playback {
135146 wrapper . startRecord = ( fileNameBase ) => {
136147 recordLogFileNameBase = fileNameBase ;
137148 recordLog = createEmptyLog ( ) ;
149+
150+ if ( typeof underlying . args !== "function" ) {
151+ recordLog . arguments = < string [ ] > underlying . args ;
152+ }
153+ } ;
154+
155+ wrapper . startReplayFromFile = logFn => {
156+ wrapper . startReplayFromString ( underlying . readFile ( logFn ) ) ;
157+ } ;
158+ wrapper . endRecord = ( ) => {
159+ if ( recordLog !== undefined ) {
160+ let i = 0 ;
161+ let fn = ( ) => recordLogFileNameBase + i + ".json" ;
162+ while ( underlying . fileExists ( fn ( ) ) ) i ++ ;
163+ underlying . writeFile ( fn ( ) , JSON . stringify ( recordLog ) ) ;
164+ recordLog = undefined ;
165+ }
166+ } ;
167+
168+ wrapper . fileExists = recordReplay ( wrapper . fileExists , underlying ) (
169+ path => callAndRecord ( underlying . fileExists ( path ) , recordLog . fileExists , { path } ) ,
170+ memoize ( path => {
171+ // If we read from the file, it must exist
172+ if ( findResultByPath ( wrapper , replayLog . filesRead , path , null ) !== null ) {
173+ return true ;
174+ }
175+ else {
176+ return findResultByFields ( replayLog . fileExists , { path } , false ) ;
177+ }
178+ } )
179+ ) ;
180+
181+ wrapper . getExecutingFilePath = ( ) => {
182+ if ( replayLog !== undefined ) {
183+ return replayLog . executingPath ;
184+ }
185+ else if ( recordLog !== undefined ) {
186+ return recordLog . executingPath = underlying . getExecutingFilePath ( ) ;
187+ }
188+ else {
189+ return underlying . getExecutingFilePath ( ) ;
190+ }
191+ } ;
192+
193+ wrapper . getCurrentDirectory = ( ) => {
194+ if ( replayLog !== undefined ) {
195+ return replayLog . currentDirectory || "" ;
196+ }
197+ else if ( recordLog !== undefined ) {
198+ return recordLog . currentDirectory = underlying . getCurrentDirectory ( ) ;
199+ }
200+ else {
201+ return underlying . getCurrentDirectory ( ) ;
202+ }
203+ } ;
204+
205+ wrapper . resolvePath = recordReplay ( wrapper . resolvePath , underlying ) (
206+ path => callAndRecord ( underlying . resolvePath ( path ) , recordLog . pathsResolved , { path } ) ,
207+ memoize ( path => findResultByFields ( replayLog . pathsResolved , { path } , ! ts . isRootedDiskPath ( ts . normalizeSlashes ( path ) ) && replayLog . currentDirectory ? replayLog . currentDirectory + "/" + path : ts . normalizeSlashes ( path ) ) ) ) ;
208+
209+ wrapper . readFile = recordReplay ( wrapper . readFile , underlying ) (
210+ path => {
211+ let result = underlying . readFile ( path ) ;
212+ let logEntry = { path, codepage : 0 , result : { contents : result , codepage : 0 } } ;
213+ recordLog . filesRead . push ( logEntry ) ;
214+ return result ;
215+ } ,
216+ memoize ( path => findResultByPath ( wrapper , replayLog . filesRead , path ) . contents ) ) ;
217+
218+ wrapper . readDirectory = recordReplay ( wrapper . readDirectory , underlying ) (
219+ ( path , extension , exclude ) => {
220+ let result = ( < ts . System > underlying ) . readDirectory ( path , extension , exclude ) ;
221+ let logEntry = { path, extension, exclude, result } ;
222+ recordLog . directoriesRead . push ( logEntry ) ;
223+ return result ;
224+ } ,
225+ ( path , extension , exclude ) => findResultByPath ( wrapper , replayLog . directoriesRead . filter ( d => d . extension === extension && ts . arrayIsEqualTo ( d . exclude , exclude ) ) , path ) ) ;
226+
227+ wrapper . writeFile = recordReplay ( wrapper . writeFile , underlying ) (
228+ ( path , contents ) => callAndRecord ( underlying . writeFile ( path , contents ) , recordLog . filesWritten , { path, contents, bom : false } ) ,
229+ ( path , contents ) => noOpReplay ( "writeFile" ) ) ;
230+
231+ wrapper . exit = ( exitCode ) => {
232+ if ( recordLog !== undefined ) {
233+ wrapper . endRecord ( ) ;
234+ }
235+ underlying . exit ( exitCode ) ;
138236 } ;
139237 }
140238
@@ -143,9 +241,11 @@ module Playback {
143241 return < any > ( function ( ) {
144242 if ( replayLog !== undefined ) {
145243 return replay . apply ( undefined , arguments ) ;
146- } else if ( recordLog !== undefined ) {
244+ }
245+ else if ( recordLog !== undefined ) {
147246 return record . apply ( undefined , arguments ) ;
148- } else {
247+ }
248+ else {
149249 return original . apply ( underlying , arguments ) ;
150250 }
151251 } ) ;
@@ -169,15 +269,16 @@ module Playback {
169269 if ( results . length === 0 ) {
170270 if ( defaultValue !== undefined ) {
171271 return defaultValue ;
172- } else {
272+ }
273+ else {
173274 throw new Error ( "No matching result in log array for: " + JSON . stringify ( expectedFields ) ) ;
174275 }
175276 }
176277 return results [ 0 ] . result ;
177278 }
178279
179280 function findResultByPath < T > ( wrapper : { resolvePath ( s : string ) : string } , logArray : { path : string ; result ?: T } [ ] , expectedPath : string , defaultValue ?: T ) : T {
180- let normalizedName = ts . normalizeSlashes ( expectedPath ) . toLowerCase ( ) ;
281+ let normalizedName = ts . normalizePath ( expectedPath ) . toLowerCase ( ) ;
181282 // Try to find the result through normal fileName
182283 for ( let i = 0 ; i < logArray . length ; i ++ ) {
183284 if ( ts . normalizeSlashes ( logArray [ i ] . path ) . toLowerCase ( ) === normalizedName ) {
@@ -193,10 +294,12 @@ module Playback {
193294 }
194295 }
195296 }
297+
196298 // If we got here, we didn't find a match
197299 if ( defaultValue === undefined ) {
198300 throw new Error ( "No matching result in log array for path: " + expectedPath ) ;
199- } else {
301+ }
302+ else {
200303 return defaultValue ;
201304 }
202305 }
@@ -214,7 +317,8 @@ module Playback {
214317 }
215318 if ( pathEquivCache . hasOwnProperty ( key ) ) {
216319 return pathEquivCache [ key ] ;
217- } else {
320+ }
321+ else {
218322 return pathEquivCache [ key ] = check ( ) ;
219323 }
220324 }
@@ -227,93 +331,18 @@ module Playback {
227331 let wrapper : PlaybackIO = < any > { } ;
228332 initWrapper ( wrapper , underlying ) ;
229333
230- wrapper . startReplayFromFile = logFn => {
231- wrapper . startReplayFromString ( underlying . readFile ( logFn ) ) ;
232- } ;
233- wrapper . endRecord = ( ) => {
234- if ( recordLog !== undefined ) {
235- let i = 0 ;
236- let fn = ( ) => recordLogFileNameBase + i + ".json" ;
237- while ( underlying . fileExists ( fn ( ) ) ) i ++ ;
238- underlying . writeFile ( fn ( ) , JSON . stringify ( recordLog ) ) ;
239- recordLog = undefined ;
240- }
241- } ;
242-
243- wrapper . args = ( ) => {
244- if ( replayLog !== undefined ) {
245- return replayLog . arguments ;
246- } else if ( recordLog !== undefined ) {
247- recordLog . arguments = underlying . args ( ) ;
248- }
249- return underlying . args ( ) ;
250- }
251-
252- wrapper . newLine = ( ) => underlying . newLine ( ) ;
253- wrapper . useCaseSensitiveFileNames = ( ) => underlying . useCaseSensitiveFileNames ( ) ;
254334 wrapper . directoryName = ( path ) : string => { throw new Error ( "NotSupported" ) ; } ;
255- wrapper . createDirectory = path => { throw new Error ( "NotSupported" ) ; } ;
335+ wrapper . createDirectory = ( path ) : void => { throw new Error ( "NotSupported" ) ; } ;
256336 wrapper . directoryExists = ( path ) : boolean => { throw new Error ( "NotSupported" ) ; } ;
257- wrapper . deleteFile = path => { throw new Error ( "NotSupported" ) ; } ;
337+ wrapper . deleteFile = ( path ) : void => { throw new Error ( "NotSupported" ) ; } ;
258338 wrapper . listFiles = ( path , filter , options ) : string [ ] => { throw new Error ( "NotSupported" ) ; } ;
259- wrapper . log = text => underlying . log ( text ) ;
260-
261- wrapper . fileExists = recordReplay ( wrapper . fileExists , underlying ) (
262- ( path ) => callAndRecord ( underlying . fileExists ( path ) , recordLog . fileExists , { path : path } ) ,
263- memoize ( ( path ) => {
264- // If we read from the file, it must exist
265- if ( findResultByPath ( wrapper , replayLog . filesRead , path , null ) !== null ) {
266- return true ;
267- } else {
268- return findResultByFields ( replayLog . fileExists , { path : path } , false ) ;
269- }
270- } )
271- ) ;
272-
273- wrapper . getExecutingFilePath = ( ) => {
274- if ( replayLog !== undefined ) {
275- return replayLog . executingPath ;
276- } else if ( recordLog !== undefined ) {
277- return recordLog . executingPath = underlying . getExecutingFilePath ( ) ;
278- } else {
279- return underlying . getExecutingFilePath ( ) ;
280- }
281- } ;
282339
283- wrapper . getCurrentDirectory = ( ) => {
284- if ( replayLog !== undefined ) {
285- return replayLog . currentDirectory || "" ;
286- } else if ( recordLog !== undefined ) {
287- return recordLog . currentDirectory = underlying . getCurrentDirectory ( ) ;
288- } else {
289- return underlying . getCurrentDirectory ( ) ;
290- }
291- } ;
292-
293- wrapper . resolvePath = recordReplay ( wrapper . resolvePath , underlying ) (
294- ( path ) => callAndRecord ( underlying . resolvePath ( path ) , recordLog . pathsResolved , { path : path } ) ,
295- memoize ( ( path ) => findResultByFields ( replayLog . pathsResolved , { path : path } , ! ts . isRootedDiskPath ( ts . normalizeSlashes ( path ) ) && replayLog . currentDirectory ? replayLog . currentDirectory + "/" + path : ts . normalizeSlashes ( path ) ) ) ) ;
296-
297- wrapper . readFile = recordReplay ( wrapper . readFile , underlying ) (
298- ( path ) => {
299- let result = underlying . readFile ( path ) ;
300- let logEntry = { path : path , codepage : 0 , result : { contents : result , codepage : 0 } } ;
301- recordLog . filesRead . push ( logEntry ) ;
302- return result ;
303- } ,
304- memoize ( ( path ) => findResultByPath ( wrapper , replayLog . filesRead , path ) . contents ) ) ;
305-
306- wrapper . writeFile = recordReplay ( wrapper . writeFile , underlying ) (
307- ( path , contents ) => callAndRecord ( underlying . writeFile ( path , contents ) , recordLog . filesWritten , { path : path , contents : contents , bom : false } ) ,
308- ( path , contents ) => noOpReplay ( "writeFile" ) ) ;
309-
310- wrapper . exit = ( exitCode ) => {
311- if ( recordLog !== undefined ) {
312- wrapper . endRecord ( ) ;
313- }
314- underlying . exit ( exitCode ) ;
315- } ;
340+ return wrapper ;
341+ }
316342
343+ export function wrapSystem ( underlying : ts . System ) : PlaybackSystem {
344+ let wrapper : PlaybackSystem = < any > { } ;
345+ initWrapper ( wrapper , underlying ) ;
317346 return wrapper ;
318347 }
319348}
0 commit comments