Skip to content

Commit fc438ef

Browse files
committed
Merge pull request #4774 from Microsoft/iocaptureFixes
Fixes for the the tsc instrumentation and adding tsconfig support to rwc runner
2 parents 4fedd77 + 5b5d876 commit fc438ef

File tree

4 files changed

+155
-94
lines changed

4 files changed

+155
-94
lines changed

src/harness/harness.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ module Harness {
428428
args(): string[];
429429
getExecutingFilePath(): string;
430430
exit(exitCode?: number): void;
431+
readDirectory(path: string, extension?: string, exclude?: string[]): string[];
431432
}
432433
export var IO: IO;
433434

@@ -464,6 +465,7 @@ module Harness {
464465
export const directoryExists: typeof IO.directoryExists = fso.FolderExists;
465466
export const fileExists: typeof IO.fileExists = fso.FileExists;
466467
export const log: typeof IO.log = global.WScript && global.WScript.StdOut.WriteLine;
468+
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude) => ts.sys.readDirectory(path, extension, exclude);
467469

468470
export function createDirectory(path: string) {
469471
if (directoryExists(path)) {
@@ -532,6 +534,8 @@ module Harness {
532534
export const fileExists: typeof IO.fileExists = fs.existsSync;
533535
export const log: typeof IO.log = s => console.log(s);
534536

537+
export const readDirectory: typeof IO.readDirectory = (path, extension, exclude) => ts.sys.readDirectory(path, extension, exclude);
538+
535539
export function createDirectory(path: string) {
536540
if (!directoryExists(path)) {
537541
fs.mkdirSync(path);
@@ -730,6 +734,10 @@ module Harness {
730734
export function writeFile(path: string, contents: string) {
731735
Http.writeToServerSync(serverRoot + path, "WRITE", contents);
732736
}
737+
738+
export function readDirectory(path: string, extension?: string, exclude?: string[]) {
739+
return listFiles(path).filter(f => !extension || ts.fileExtensionIs(f, extension));
740+
}
733741
}
734742
}
735743

src/harness/instrumenter.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ var fs: any = require('fs');
33
var path: any = require('path');
44

55
function instrumentForRecording(fn: string, tscPath: string) {
6-
instrument(tscPath, 'ts.sys = Playback.wrapSystem(ts.sys); ts.sys.startRecord("' + fn + '");', 'ts.sys.endRecord();');
6+
instrument(tscPath, `
7+
ts.sys = Playback.wrapSystem(ts.sys);
8+
ts.sys.startRecord("${ fn }");`, `ts.sys.endRecord();`);
79
}
810

911
function instrumentForReplay(logFilename: string, tscPath: string) {
10-
instrument(tscPath, 'ts.sys = Playback.wrapSystem(ts.sys); ts.sys.startReplay("' + logFilename + '");');
12+
instrument(tscPath, `
13+
ts.sys = Playback.wrapSystem(ts.sys);
14+
ts.sys.startReplay("${ logFilename }");`);
1115
}
1216

1317
function instrument(tscPath: string, prepareCode: string, cleanupCode: string = '') {

src/harness/loggedIO.ts

Lines changed: 119 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -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

6470
interface 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
}

src/harness/rwcRunner.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ module RWC {
1919
}
2020
}
2121

22+
function isTsConfigFile(file: { path: string }): boolean {
23+
const tsConfigFileName = "tsconfig.json";
24+
return file.path.substr(file.path.length - tsConfigFileName.length).toLowerCase() === tsConfigFileName;
25+
}
26+
2227
export function runRWCTest(jsonPath: string) {
2328
describe("Testing a RWC project: " + jsonPath, () => {
2429
let inputFiles: { unitName: string; content: string; }[] = [];
@@ -67,10 +72,21 @@ module RWC {
6772
runWithIOLog(ioLog, oldIO => {
6873
harnessCompiler.reset();
6974

75+
let fileNames = opts.fileNames;
76+
77+
let tsconfigFile = ts.forEach(ioLog.filesRead, f => isTsConfigFile(f) ? f : undefined);
78+
if (tsconfigFile) {
79+
let tsconfigFileContents = getHarnessCompilerInputUnit(tsconfigFile.path);
80+
let parsedTsconfigFileContents = ts.parseConfigFileText(tsconfigFile.path, tsconfigFileContents.content);
81+
let configParseResult = ts.parseConfigFile(parsedTsconfigFileContents.config, Harness.IO, ts.getDirectoryPath(tsconfigFile.path));
82+
fileNames = configParseResult.fileNames;
83+
opts.options = ts.extend(opts.options, configParseResult.options);
84+
}
85+
7086
// Load the files
71-
ts.forEach(opts.fileNames, fileName => {
87+
for (let fileName of fileNames) {
7288
inputFiles.push(getHarnessCompilerInputUnit(fileName));
73-
});
89+
}
7490

7591
// Add files to compilation
7692
let isInInputList = (resolvedPath: string) => (inputFile: { unitName: string; content: string; }) => inputFile.unitName === resolvedPath;
@@ -79,6 +95,10 @@ module RWC {
7995
const resolvedPath = ts.normalizeSlashes(Harness.IO.resolvePath(fileRead.path));
8096
let inInputList = ts.forEach(inputFiles, isInInputList(resolvedPath));
8197

98+
if (isTsConfigFile(fileRead)) {
99+
continue;
100+
}
101+
82102
if (!Harness.isLibraryFile(fileRead.path)) {
83103
if (inInputList) {
84104
continue;

0 commit comments

Comments
 (0)