Testing WIP

This commit is contained in:
Ryan Cavanaugh 2018-05-21 19:40:09 -07:00
parent 7e0825a3e7
commit 6d04378e90
15 changed files with 184 additions and 29 deletions

View file

@ -1073,6 +1073,7 @@ namespace ts {
_fs.utimesSync(path, time, time);
}
catch (e) {
return;
}
}
@ -1081,6 +1082,7 @@ namespace ts {
return _fs.unlinkSync(path);
}
catch (e) {
return;
}
}

View file

@ -7,7 +7,7 @@ namespace ts {
* The primary thing we track here is which files were written to,
* but unchanged, because this enables fast downstream updates
*/
interface BuildContext {
export interface BuildContext {
options: BuildOptions;
/**
* Map from output file name to its pre-build timestamp
@ -299,6 +299,9 @@ namespace ts {
function parseConfigFile(configFilePath: string) {
const sourceFile = host.getSourceFile(configFilePath, ScriptTarget.JSON) as JsonSourceFile;
if (sourceFile === undefined) {
return undefined;
}
const parsed = parseJsonSourceFileConfigFileContent(sourceFile, configParseHost, getDirectoryPath(configFilePath));
parsed.options.configFilePath = configFilePath;
cache.setValue(configFilePath, parsed);
@ -322,7 +325,7 @@ namespace ts {
return fileExtensionIs(fileName, ".d.ts");
}
function createBuildContext(options: BuildOptions): BuildContext {
export function createBuildContext(options: BuildOptions): BuildContext {
const verboseDiag = options.verbose && createDiagnosticReporter(sys, /*pretty*/ false);
return {
options,
@ -334,18 +337,15 @@ namespace ts {
};
}
export function performBuild(args: string[]) {
const diagReporter = createDiagnosticReporter(sys, /*pretty*/true);
const host = createCompilerHost({});
export function performBuild(host: CompilerHost, reportDiagnostic: DiagnosticReporter, args: string[]) {
let verbose = false;
let dry = false;
let force = false;
let clean = false;
const projects: string[] = [];
for (let i = 0; i < args.length; i++) {
switch (args[i].toLowerCase()) {
for (const arg of args) {
switch (arg.toLowerCase()) {
case "-v":
case "--verbose":
verbose = true;
@ -363,7 +363,7 @@ namespace ts {
continue;
}
// Not a flag, parse as filename
addProject(args[i]);
addProject(arg);
}
if (projects.length === 0) {
@ -372,7 +372,7 @@ namespace ts {
}
const context = createBuildContext({ verbose, dry, force });
const builder = createSolutionBuilder(host, context);
const builder = createSolutionBuilder(host, reportDiagnostic, context);
if (clean) {
builder.cleanProjects(projects);
}
@ -384,15 +384,14 @@ namespace ts {
const fileName = resolvePath(host.getCurrentDirectory(), projectSpecification);
const refPath = resolveProjectReferencePath(host, { path: fileName });
if (!host.fileExists(refPath)) {
diagReporter(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, fileName));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, fileName));
}
projects.push(refPath);
}
}
export function createSolutionBuilder(host: CompilerHost, context: BuildContext) {
const diagReporter = createDiagnosticReporter(sys, /*pretty*/true);
export function createSolutionBuilder(host: CompilerHost, reportDiagnostic: DiagnosticReporter, context: BuildContext) {
const configFileCache = createConfigFileCache(host);
return {
@ -418,7 +417,7 @@ namespace ts {
else {
const outputs: string[] = [];
for (const inputFile of project.fileNames) {
(outputs as string[]).push(...getOutputFileNames(inputFile, project));
outputs.push(...getOutputFileNames(inputFile, project));
}
return outputs;
}
@ -553,7 +552,8 @@ namespace ts {
for (const root of roots) {
const config = configFileCache.parseConfigFile(root);
if (config === undefined) {
throw new Error(`Could not parse ${root}`);
reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_does_not_exist, root));
continue;
}
enumerateReferences(normalizePath(root), config);
}
@ -564,7 +564,7 @@ namespace ts {
dependencyMap
};
function enumerateReferences(fileName: string, root: ts.ParsedCommandLine): void {
function enumerateReferences(fileName: string, root: ParsedCommandLine): void {
const myBuildLevel = buildQueue[buildQueuePosition] = buildQueue[buildQueuePosition] || [];
if (myBuildLevel.indexOf(fileName) < 0) {
myBuildLevel.push(fileName);
@ -605,7 +605,7 @@ namespace ts {
// TODO Accept parsedCommandLine
function buildSingleProject(proj: string) {
if (context.options.dry) {
diagReporter(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj));
}
context.verbose(Diagnostics.Building_project_0, proj);
@ -627,18 +627,18 @@ namespace ts {
const programOptions: CreateProgramOptions = {
projectReferences: configFile.projectReferences,
host: host,
host,
rootNames: configFile.fileNames,
options: configFile.options
};
const program = ts.createProgram(programOptions);
const program = createProgram(programOptions);
// Don't emit anything in the presence of syntactic errors or options diagnostics
const syntaxDiagnostics = [...program.getOptionsDiagnostics(), ...program.getSyntacticDiagnostics()];
if (syntaxDiagnostics.length) {
resultFlags |= BuildResultFlags.SyntaxErrors;
for (const diag of syntaxDiagnostics) {
diagReporter(diag);
reportDiagnostic(diag);
}
return resultFlags;
}
@ -649,7 +649,7 @@ namespace ts {
if (declDiagnostics.length) {
resultFlags |= BuildResultFlags.DeclarationEmitErrors;
for (const diag of declDiagnostics) {
diagReporter(diag);
reportDiagnostic(diag);
}
return resultFlags;
}
@ -659,13 +659,13 @@ namespace ts {
if (semanticDiagnostics.length) {
resultFlags |= BuildResultFlags.TypeErrors;
for (const diag of semanticDiagnostics) {
diagReporter(diag);
reportDiagnostic(diag);
}
return resultFlags;
}
let newestDeclarationFileContentChangedTime = minimumDate;
program.emit(undefined, (fileName, content, writeBom, onError) => {
program.emit(/*targetSourceFile*/ undefined, (fileName, content, writeBom, onError) => {
let priorChangeTime: Date | undefined;
if (isDeclarationFile(fileName) && host.fileExists(fileName)) {
@ -690,7 +690,7 @@ namespace ts {
function updateOutputTimestamps(proj: ParsedCommandLine) {
if (context.options.dry) {
diagReporter(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj.options.configFilePath));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Would_build_project_0, proj.options.configFilePath));
return;
}
@ -731,13 +731,29 @@ namespace ts {
}
if (context.options.dry) {
diagReporter(createCompilerDiagnostic(Diagnostics.Would_delete_the_following_files_Colon_0, fileReport.map(f => `\r\n * ${f}`).join("")));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Would_delete_the_following_files_Colon_0, fileReport.map(f => `\r\n * ${f}`).join("")));
}
}
function buildProjects(configFileNames: string[]) {
const resolvedNames: string[] = [];
for (const name of configFileNames) {
let fullPath = resolvePath(host.getCurrentDirectory(), name);
if (host.fileExists(fullPath)) {
resolvedNames.push(fullPath);
continue;
}
fullPath = combinePaths(fullPath, "tsconfig.json");
if (host.fileExists(fullPath)) {
resolvedNames.push(fullPath);
continue;
}
reportDiagnostic(createCompilerDiagnostic(Diagnostics.File_0_not_found, fullPath));
return;
}
// Establish what needs to be built
const graph = createDependencyGraph(configFileNames);
const graph = createDependencyGraph(resolvedNames);
const queue = graph.buildQueue;
reportBuildQueue(graph);

View file

@ -48,9 +48,9 @@ namespace ts {
export function executeCommandLine(args: string[]): void {
if ((args[0].toLowerCase() === "--build") || (args[0].toLowerCase() === "-b")) {
return performBuild(args.slice(1));
return performBuild(createCompilerHost({}), createDiagnosticReporter(sys), args.slice(1));
}
const commandLine = parseCommandLine(args);
// Configuration file name (if any)

View file

@ -131,6 +131,10 @@ namespace fakes {
return stats ? stats.mtime : undefined;
}
public setModifiedTime(path: string, time: Date) {
this.vfs.utimesSync(path, time, time);
}
public createHash(data: string): string {
return data;
}
@ -252,6 +256,14 @@ namespace fakes {
return this.sys.directoryExists(directoryName);
}
public getModifiedTime(fileName: string) {
return this.sys.getModifiedTime(fileName);
}
public setModifiedTime(fileName: string, time: Date) {
return this.sys.setModifiedTime(fileName, time);
}
public getDirectories(path: string): string[] {
return this.sys.getDirectories(path);
}

View file

@ -52,6 +52,7 @@
"../compiler/builder.ts",
"../compiler/resolutionCache.ts",
"../compiler/watch.ts",
"../compiler/tsbuild.ts",
"../compiler/commandLineParser.ts",
"../services/types.ts",

View file

@ -0,0 +1,71 @@
/// <reference path="../vfs.ts" />
namespace ts {
let currentTime = 100;
const bfs = new vfs.FileSystem(/*ignoreCase*/ false, { time });
const lastDiagnostics: Diagnostic[] = [];
const reportDiagnostic: DiagnosticReporter = diagnostic => lastDiagnostics.push(diagnostic);
const sampleRoot = resolvePath(__dirname, "../../tests/projects/sample1");
loadFsMirror(bfs, sampleRoot, "/src");
bfs.mkdirpSync("/lib");
bfs.writeFileSync("/lib/lib.d.ts", Harness.IO.readFile(combinePaths(Harness.libFolder, "lib.d.ts")));
bfs.meta.set("defaultLibLocation", "/lib");
bfs.makeReadonly();
describe("tsbuild tests", () => {
it("builds the referenced project", () => {
const fs = bfs.shadow();
const host = new fakes.CompilerHost(fs);
const builder = createSolutionBuilder(host, reportDiagnostic, createBuildContext({ dry: false, force: false, verbose: false }));
fs.chdir("/src/tests");
fs.debugPrint();
builder.buildProjects(["."]);
printDiagnostics();
fs.debugPrint();
assertDiagnosticMessages(Diagnostics.File_0_does_not_exist);
tick();
});
});
function assertDiagnosticMessages(...expected: DiagnosticMessage[]) {
const actual = lastDiagnostics.slice();
actual.sort((a, b) => b.code - a.code);
expected.sort((a, b) => b.code - a.code);
if (actual.length !== expected.length) {
assert.fail<any>(actual, expected, `Diagnostic arrays did not match - expected ${actual.join(",")}, got ${expected.join(",")}`);
}
for (let i = 0; i < actual.length; i++) {
if (actual[i].code !== expected[i].code) {
assert.fail(actual[i].messageText, expected[i].message, "Mismatched error code");
}
}
}
export function printDiagnostics() {
const out = createDiagnosticReporter(sys);
for (const d of lastDiagnostics) {
out(d);
}
}
function tick() {
currentTime += 10;
}
function time() {
return currentTime;
}
function loadFsMirror(vfs: vfs.FileSystem, localRoot: string, virtualRoot: string) {
vfs.mkdirpSync(virtualRoot);
for (const path of Harness.IO.readDirectory(localRoot)) {
const file = getBaseFileName(path);
vfs.writeFileSync(virtualRoot + "/" + file, Harness.IO.readFile(localRoot + "/" + file));
}
for (const dir of Harness.IO.getDirectories(localRoot)){
loadFsMirror(vfs, localRoot + "/" + dir, virtualRoot + "/" + dir);
}
}
}

View file

@ -5,6 +5,11 @@ namespace vfs {
*/
export const builtFolder = "/.ts";
/**
* Posix-style path to additional mountable folders (./tests/projects in this repo)
*/
export const projectsFolder = "/.projects";
/**
* Posix-style path to additional test libraries
*/
@ -404,7 +409,18 @@ namespace vfs {
}
/**
* Get file status.
* Change file access times
*
* NOTE: do not rename this method as it is intended to align with the same named export of the "fs" module.
*/
public utimesSync(path: string, atime: Date, mtime: Date) {
const entry = this._walk(this._resolve(path));
entry.node.atimeMs = +atime;
entry.node.mtimeMs = +mtime;
}
/**
* Get file status. If `path` is a symbolic link, it is dereferenced.
*
* @link http://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html
*
@ -414,6 +430,7 @@ namespace vfs {
return this._stat(this._walk(this._resolve(path), /*noFollow*/ true));
}
private _stat(entry: WalkResult) {
const node = entry.node;
if (!node) throw createIOError("ENOENT");
@ -1282,6 +1299,7 @@ namespace vfs {
files: {
[builtFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "built/local"), resolver),
[testLibFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "tests/lib"), resolver),
[projectsFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "tests/projects"), resolver),
[srcFolder]: {}
},
cwd: srcFolder,

View file

@ -0,0 +1,2 @@
export function leftPad(s: string, n: number) { return s + n; }
export function multiply(a: number, b: number) { return a * b; }

View file

@ -0,0 +1,3 @@
{
}

View file

@ -0,0 +1,4 @@
import * as c from '../core';
export function getSecondsInDay() {
return c.multiply(10, 15);
}

View file

@ -0,0 +1,5 @@
{
"references": [
{ "path": "../core" }
]
}

View file

@ -0,0 +1,5 @@
import * as c from '../core';
import * as logic from '../logic';
c.leftPad("", 10);
logic.getSecondsInDay();

View file

@ -0,0 +1,6 @@
{
"references": [
{ "path": "../core" },
{ "path": "../logic" }
]
}

View file

@ -0,0 +1,5 @@
import * as logic from '../logic';
export function run() {
console.log(logic.getSecondsInDay());
}

View file

@ -0,0 +1,5 @@
{
"references": [
{ "path": "../logic" }
]
}