mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Move most harness globals into namespaces (#35530)
* Move most harness globals into namespaces * Remove forward declaration from `createMapShim` and move all `Map` declarations into one file * A small pile of more changes to get the harness transforming
This commit is contained in:
parent
2b567b2699
commit
a78342a160
@ -2,31 +2,8 @@
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
|
||||
/**
|
||||
* Returns the native Map implementation if it is available and compatible (i.e. supports iteration).
|
||||
*/
|
||||
export function tryGetNativeMap(): MapConstructor | undefined {
|
||||
// Internet Explorer's Map doesn't support iteration, so don't use it.
|
||||
// Natives
|
||||
// NOTE: TS doesn't strictly allow in-line declares, but if we suppress the error, the declaration
|
||||
// is still used for typechecking _and_ correctly elided, which is out goal, as this prevents us from
|
||||
// needing to pollute an outer scope with a declaration of `Map` just to satisfy the checks in this function
|
||||
//@ts-ignore
|
||||
declare const Map: (new <T>() => Map<T>) | undefined;
|
||||
// eslint-disable-next-line no-in-operator
|
||||
return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined;
|
||||
}
|
||||
|
||||
export const emptyArray: never[] = [] as never[];
|
||||
|
||||
export const Map: MapConstructor = tryGetNativeMap() || (() => {
|
||||
// NOTE: createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
|
||||
if (typeof createMapShim === "function") {
|
||||
return createMapShim();
|
||||
}
|
||||
throw new Error("TypeScript requires an environment that provides a compatible native Map implementation.");
|
||||
})();
|
||||
|
||||
/** Create a new map. */
|
||||
export function createMap<T>(): Map<T> {
|
||||
return new Map<T>();
|
||||
|
||||
@ -46,6 +46,31 @@ namespace ts {
|
||||
new <T>(): Map<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the native Map implementation if it is available and compatible (i.e. supports iteration).
|
||||
*/
|
||||
/* @internal */
|
||||
export function tryGetNativeMap(): MapConstructor | undefined {
|
||||
// Internet Explorer's Map doesn't support iteration, so don't use it.
|
||||
// Natives
|
||||
// NOTE: TS doesn't strictly allow in-line declares, but if we suppress the error, the declaration
|
||||
// is still used for typechecking _and_ correctly elided, which is out goal, as this prevents us from
|
||||
// needing to pollute an outer scope with a declaration of `Map` just to satisfy the checks in this function
|
||||
//@ts-ignore
|
||||
declare const Map: (new <T>() => Map<T>) | undefined;
|
||||
// eslint-disable-next-line no-in-operator
|
||||
return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export const Map: MapConstructor = tryGetNativeMap() || (() => {
|
||||
// NOTE: createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
|
||||
if (typeof createMapShim === "function") {
|
||||
return createMapShim();
|
||||
}
|
||||
throw new Error("TypeScript requires an environment that provides a compatible native Map implementation.");
|
||||
})();
|
||||
|
||||
/** ES6 Iterator type. */
|
||||
export interface Iterator<T> {
|
||||
next(): { value: T, done?: false } | { value: never, done: true };
|
||||
|
||||
@ -39,7 +39,7 @@ namespace fakes {
|
||||
public readFile(path: string) {
|
||||
try {
|
||||
const content = this.vfs.readFileSync(path, "utf8");
|
||||
return content === undefined ? undefined : utils.removeByteOrderMark(content);
|
||||
return content === undefined ? undefined : Utils.removeByteOrderMark(content);
|
||||
}
|
||||
catch {
|
||||
return undefined;
|
||||
@ -48,7 +48,7 @@ namespace fakes {
|
||||
|
||||
public writeFile(path: string, data: string, writeByteOrderMark?: boolean): void {
|
||||
this.vfs.mkdirpSync(vpath.dirname(path));
|
||||
this.vfs.writeFileSync(path, writeByteOrderMark ? utils.addUTF8ByteOrderMark(data) : data);
|
||||
this.vfs.writeFileSync(path, writeByteOrderMark ? Utils.addUTF8ByteOrderMark(data) : data);
|
||||
}
|
||||
|
||||
public deleteFile(path: string) {
|
||||
@ -289,7 +289,7 @@ namespace fakes {
|
||||
}
|
||||
|
||||
public writeFile(fileName: string, content: string, writeByteOrderMark: boolean) {
|
||||
if (writeByteOrderMark) content = utils.addUTF8ByteOrderMark(content);
|
||||
if (writeByteOrderMark) content = Utils.addUTF8ByteOrderMark(content);
|
||||
this.sys.writeFile(fileName, content);
|
||||
|
||||
const document = new documents.TextDocument(fileName, content);
|
||||
@ -1,6 +1,4 @@
|
||||
namespace FourSlash {
|
||||
ts.disableIncrementalParsing = false;
|
||||
|
||||
import ArrayOrSingle = FourSlashInterface.ArrayOrSingle;
|
||||
|
||||
export const enum FourSlashTestType {
|
||||
@ -2,8 +2,9 @@
|
||||
/* eslint-disable no-var */
|
||||
|
||||
// this will work in the browser via browserify
|
||||
var _chai: typeof chai = require("chai");
|
||||
var assert: typeof _chai.assert = _chai.assert;
|
||||
declare var assert: typeof _chai.assert;
|
||||
var _chai: typeof import("chai") = require("chai");
|
||||
globalThis.assert = _chai.assert;
|
||||
{
|
||||
// chai's builtin `assert.isFalse` is featureful but slow - we don't use those features,
|
||||
// so we'll just overwrite it as an alterative to migrating a bunch of code off of chai
|
||||
@ -27,4 +28,7 @@ var assert: typeof _chai.assert = _chai.assert;
|
||||
}
|
||||
};
|
||||
}
|
||||
/* eslint-enable no-var */
|
||||
/* eslint-enable no-var */
|
||||
// empty ts namespace so this file is included in the `ts.ts` namespace file generated by the module swapover
|
||||
// This way, everything that ends up importing `ts` downstream also imports this file and picks up its augmentation
|
||||
namespace ts {}
|
||||
@ -29,6 +29,9 @@ namespace Harness {
|
||||
}
|
||||
|
||||
export let IO: IO;
|
||||
export function setHarnessIO(io: IO) {
|
||||
IO = io;
|
||||
}
|
||||
|
||||
// harness always uses one kind of new line
|
||||
// But note that `parseTestData` in `fourslash.ts` uses "\n"
|
||||
@ -185,6 +188,9 @@ namespace Harness {
|
||||
export let userSpecifiedRoot = "";
|
||||
export let lightMode = false;
|
||||
/* eslint-enable prefer-const */
|
||||
export function setLightMode(flag: boolean) {
|
||||
lightMode = flag;
|
||||
}
|
||||
|
||||
/** Functionality for compiling TypeScript code */
|
||||
export namespace Compiler {
|
||||
@ -467,7 +473,7 @@ namespace Harness {
|
||||
else if (vpath.isTypeScript(file.unitName) || (vpath.isJavaScript(file.unitName) && options.allowJs)) {
|
||||
const declFile = findResultCodeFile(file.unitName);
|
||||
if (declFile && !findUnit(declFile.file, declInputFiles) && !findUnit(declFile.file, declOtherFiles)) {
|
||||
dtsFiles.push({ unitName: declFile.file, content: utils.removeByteOrderMark(declFile.text) });
|
||||
dtsFiles.push({ unitName: declFile.file, content: Utils.removeByteOrderMark(declFile.text) });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -557,7 +563,7 @@ namespace Harness {
|
||||
function outputErrorText(error: ts.Diagnostic) {
|
||||
const message = ts.flattenDiagnosticMessageText(error.messageText, IO.newLine());
|
||||
|
||||
const errLines = utils.removeTestPathPrefixes(message)
|
||||
const errLines = Utils.removeTestPathPrefixes(message)
|
||||
.split("\n")
|
||||
.map(s => s.length > 0 && s.charAt(s.length - 1) === "\r" ? s.substr(0, s.length - 1) : s)
|
||||
.filter(s => s.length > 0)
|
||||
@ -580,7 +586,7 @@ namespace Harness {
|
||||
}
|
||||
}
|
||||
|
||||
yield [diagnosticSummaryMarker, utils.removeTestPathPrefixes(minimalDiagnosticsToString(diagnostics, options && options.pretty)) + IO.newLine() + IO.newLine(), diagnostics.length];
|
||||
yield [diagnosticSummaryMarker, Utils.removeTestPathPrefixes(minimalDiagnosticsToString(diagnostics, options && options.pretty)) + IO.newLine() + IO.newLine(), diagnostics.length];
|
||||
|
||||
// Report global errors
|
||||
const globalErrors = diagnostics.filter(err => !err.file);
|
||||
@ -595,7 +601,7 @@ namespace Harness {
|
||||
// Filter down to the errors in the file
|
||||
const fileErrors = diagnostics.filter((e): e is ts.DiagnosticWithLocation => {
|
||||
const errFn = e.file;
|
||||
return !!errFn && ts.comparePaths(utils.removeTestPathPrefixes(errFn.fileName), utils.removeTestPathPrefixes(inputFile.unitName), options && options.currentDirectory || "", !(options && options.caseSensitive)) === ts.Comparison.EqualTo;
|
||||
return !!errFn && ts.comparePaths(Utils.removeTestPathPrefixes(errFn.fileName), Utils.removeTestPathPrefixes(inputFile.unitName), options && options.currentDirectory || "", !(options && options.caseSensitive)) === ts.Comparison.EqualTo;
|
||||
});
|
||||
|
||||
|
||||
@ -812,7 +818,7 @@ namespace Harness {
|
||||
}
|
||||
typeLines += "\r\n";
|
||||
}
|
||||
yield [checkDuplicatedFileName(unitName, dupeCase), utils.removeTestPathPrefixes(typeLines)];
|
||||
yield [checkDuplicatedFileName(unitName, dupeCase), Utils.removeTestPathPrefixes(typeLines)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -901,8 +907,8 @@ namespace Harness {
|
||||
}
|
||||
|
||||
function fileOutput(file: documents.TextDocument, harnessSettings: TestCaseParser.CompilerSettings): string {
|
||||
const fileName = harnessSettings.fullEmitPaths ? utils.removeTestPathPrefixes(file.file) : ts.getBaseFileName(file.file);
|
||||
return "//// [" + fileName + "]\r\n" + utils.removeTestPathPrefixes(file.text);
|
||||
const fileName = harnessSettings.fullEmitPaths ? Utils.removeTestPathPrefixes(file.file) : ts.getBaseFileName(file.file);
|
||||
return "//// [" + fileName + "]\r\n" + Utils.removeTestPathPrefixes(file.text);
|
||||
}
|
||||
|
||||
export function collateOutputs(outputFiles: readonly documents.TextDocument[]): string {
|
||||
@ -928,7 +934,7 @@ namespace Harness {
|
||||
const dupeCase = ts.createMap<number>();
|
||||
// Yield them
|
||||
for (const outputFile of files) {
|
||||
yield [checkDuplicatedFileName(outputFile.file, dupeCase), "/*====== " + outputFile.file + " ======*/\r\n" + utils.removeByteOrderMark(outputFile.text)];
|
||||
yield [checkDuplicatedFileName(outputFile.file, dupeCase), "/*====== " + outputFile.file + " ======*/\r\n" + Utils.removeByteOrderMark(outputFile.text)];
|
||||
}
|
||||
|
||||
function cleanName(fn: string) {
|
||||
|
||||
@ -1,86 +1,86 @@
|
||||
interface FileInformation {
|
||||
contents?: string;
|
||||
contentsPath?: string;
|
||||
codepage: number;
|
||||
bom?: string;
|
||||
}
|
||||
|
||||
interface FindFileResult {
|
||||
}
|
||||
|
||||
interface IoLogFile {
|
||||
path: string;
|
||||
codepage: number;
|
||||
result?: FileInformation;
|
||||
}
|
||||
|
||||
interface IoLog {
|
||||
timestamp: string;
|
||||
arguments: string[];
|
||||
executingPath: string;
|
||||
currentDirectory: string;
|
||||
useCustomLibraryFile?: boolean;
|
||||
filesRead: IoLogFile[];
|
||||
filesWritten: {
|
||||
path: string;
|
||||
contents?: string;
|
||||
contentsPath?: string;
|
||||
bom: boolean;
|
||||
}[];
|
||||
filesDeleted: string[];
|
||||
filesAppended: {
|
||||
path: string;
|
||||
contents?: string;
|
||||
contentsPath?: string;
|
||||
}[];
|
||||
fileExists: {
|
||||
path: string;
|
||||
result?: boolean;
|
||||
}[];
|
||||
filesFound: {
|
||||
path: string;
|
||||
pattern: string;
|
||||
result?: FindFileResult;
|
||||
}[];
|
||||
dirs: {
|
||||
path: string;
|
||||
re: string;
|
||||
re_m: boolean;
|
||||
re_g: boolean;
|
||||
re_i: boolean;
|
||||
opts: { recursive?: boolean; };
|
||||
result?: string[];
|
||||
}[];
|
||||
dirExists: {
|
||||
path: string;
|
||||
result?: boolean;
|
||||
}[];
|
||||
dirsCreated: string[];
|
||||
pathsResolved: {
|
||||
path: string;
|
||||
result?: string;
|
||||
}[];
|
||||
directoriesRead: {
|
||||
path: string,
|
||||
extensions: readonly string[] | undefined,
|
||||
exclude: readonly string[] | undefined,
|
||||
include: readonly string[] | undefined,
|
||||
depth: number | undefined,
|
||||
result: readonly string[],
|
||||
}[];
|
||||
useCaseSensitiveFileNames?: boolean;
|
||||
}
|
||||
|
||||
interface PlaybackControl {
|
||||
startReplayFromFile(logFileName: string): void;
|
||||
startReplayFromString(logContents: string): void;
|
||||
startReplayFromData(log: IoLog): void;
|
||||
endReplay(): void;
|
||||
startRecord(logFileName: string): void;
|
||||
endRecord(): void;
|
||||
}
|
||||
|
||||
namespace Playback {
|
||||
interface FileInformation {
|
||||
contents?: string;
|
||||
contentsPath?: string;
|
||||
codepage: number;
|
||||
bom?: string;
|
||||
}
|
||||
|
||||
interface FindFileResult {
|
||||
}
|
||||
|
||||
interface IoLogFile {
|
||||
path: string;
|
||||
codepage: number;
|
||||
result?: FileInformation;
|
||||
}
|
||||
|
||||
export interface IoLog {
|
||||
timestamp: string;
|
||||
arguments: string[];
|
||||
executingPath: string;
|
||||
currentDirectory: string;
|
||||
useCustomLibraryFile?: boolean;
|
||||
filesRead: IoLogFile[];
|
||||
filesWritten: {
|
||||
path: string;
|
||||
contents?: string;
|
||||
contentsPath?: string;
|
||||
bom: boolean;
|
||||
}[];
|
||||
filesDeleted: string[];
|
||||
filesAppended: {
|
||||
path: string;
|
||||
contents?: string;
|
||||
contentsPath?: string;
|
||||
}[];
|
||||
fileExists: {
|
||||
path: string;
|
||||
result?: boolean;
|
||||
}[];
|
||||
filesFound: {
|
||||
path: string;
|
||||
pattern: string;
|
||||
result?: FindFileResult;
|
||||
}[];
|
||||
dirs: {
|
||||
path: string;
|
||||
re: string;
|
||||
re_m: boolean;
|
||||
re_g: boolean;
|
||||
re_i: boolean;
|
||||
opts: { recursive?: boolean; };
|
||||
result?: string[];
|
||||
}[];
|
||||
dirExists: {
|
||||
path: string;
|
||||
result?: boolean;
|
||||
}[];
|
||||
dirsCreated: string[];
|
||||
pathsResolved: {
|
||||
path: string;
|
||||
result?: string;
|
||||
}[];
|
||||
directoriesRead: {
|
||||
path: string,
|
||||
extensions: readonly string[] | undefined,
|
||||
exclude: readonly string[] | undefined,
|
||||
include: readonly string[] | undefined,
|
||||
depth: number | undefined,
|
||||
result: readonly string[],
|
||||
}[];
|
||||
useCaseSensitiveFileNames?: boolean;
|
||||
}
|
||||
|
||||
interface PlaybackControl {
|
||||
startReplayFromFile(logFileName: string): void;
|
||||
startReplayFromString(logContents: string): void;
|
||||
startReplayFromData(log: IoLog): void;
|
||||
endReplay(): void;
|
||||
startRecord(logFileName: string): void;
|
||||
endRecord(): void;
|
||||
}
|
||||
|
||||
let recordLog: IoLog | undefined;
|
||||
let replayLog: IoLog | undefined;
|
||||
let replayFilesRead: ts.Map<IoLogFile> | undefined;
|
||||
|
||||
@ -1,53 +1,63 @@
|
||||
type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | "test262" | "user" | "dt" | "docker";
|
||||
type CompilerTestKind = "conformance" | "compiler";
|
||||
type FourslashTestKind = "fourslash" | "fourslash-shims" | "fourslash-shims-pp" | "fourslash-server";
|
||||
namespace Harness {
|
||||
export type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | "test262" | "user" | "dt" | "docker";
|
||||
export type CompilerTestKind = "conformance" | "compiler";
|
||||
export type FourslashTestKind = "fourslash" | "fourslash-shims" | "fourslash-shims-pp" | "fourslash-server";
|
||||
|
||||
/* eslint-disable prefer-const */
|
||||
let shards = 1;
|
||||
let shardId = 1;
|
||||
/* eslint-enable prefer-const */
|
||||
/* eslint-disable prefer-const */
|
||||
export let shards = 1;
|
||||
export let shardId = 1;
|
||||
/* eslint-enable prefer-const */
|
||||
|
||||
abstract class RunnerBase {
|
||||
// contains the tests to run
|
||||
public tests: (string | Harness.FileBasedTest)[] = [];
|
||||
|
||||
/** Add a source file to the runner's list of tests that need to be initialized with initializeTests */
|
||||
public addTest(fileName: string) {
|
||||
this.tests.push(fileName);
|
||||
// The following have setters as while they're read here in the harness, they're only set in the runner
|
||||
export function setShards(count: number) {
|
||||
shards = count;
|
||||
}
|
||||
export function setShardId(id: number) {
|
||||
shardId = id;
|
||||
}
|
||||
|
||||
public enumerateFiles(folder: string, regex?: RegExp, options?: { recursive: boolean }): string[] {
|
||||
return ts.map(Harness.IO.listFiles(Harness.userSpecifiedRoot + folder, regex, { recursive: (options ? options.recursive : false) }), ts.normalizeSlashes);
|
||||
}
|
||||
export abstract class RunnerBase {
|
||||
// contains the tests to run
|
||||
public tests: (string | FileBasedTest)[] = [];
|
||||
|
||||
abstract kind(): TestRunnerKind;
|
||||
|
||||
abstract enumerateTestFiles(): (string | Harness.FileBasedTest)[];
|
||||
|
||||
getTestFiles(): ReturnType<this["enumerateTestFiles"]> {
|
||||
const all = this.enumerateTestFiles();
|
||||
if (shards === 1) {
|
||||
return all as ReturnType<this["enumerateTestFiles"]>;
|
||||
/** Add a source file to the runner's list of tests that need to be initialized with initializeTests */
|
||||
public addTest(fileName: string) {
|
||||
this.tests.push(fileName);
|
||||
}
|
||||
|
||||
public enumerateFiles(folder: string, regex?: RegExp, options?: { recursive: boolean }): string[] {
|
||||
return ts.map(IO.listFiles(userSpecifiedRoot + folder, regex, { recursive: (options ? options.recursive : false) }), ts.normalizeSlashes);
|
||||
}
|
||||
|
||||
abstract kind(): TestRunnerKind;
|
||||
|
||||
abstract enumerateTestFiles(): (string | FileBasedTest)[];
|
||||
|
||||
getTestFiles(): ReturnType<this["enumerateTestFiles"]> {
|
||||
const all = this.enumerateTestFiles();
|
||||
if (shards === 1) {
|
||||
return all as ReturnType<this["enumerateTestFiles"]>;
|
||||
}
|
||||
return all.filter((_val, idx) => idx % shards === (shardId - 1)) as ReturnType<this["enumerateTestFiles"]>;
|
||||
}
|
||||
|
||||
/** The working directory where tests are found. Needed for batch testing where the input path will differ from the output path inside baselines */
|
||||
public workingDirectory = "";
|
||||
|
||||
/** Setup the runner's tests so that they are ready to be executed by the harness
|
||||
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
|
||||
*/
|
||||
public abstract initializeTests(): void;
|
||||
|
||||
/** Replaces instances of full paths with fileNames only */
|
||||
static removeFullPaths(path: string) {
|
||||
// If its a full path (starts with "C:" or "/") replace with just the filename
|
||||
let fixedPath = /^(\w:|\/)/.test(path) ? ts.getBaseFileName(path) : path;
|
||||
|
||||
// when running in the browser the 'full path' is the host name, shows up in error baselines
|
||||
const localHost = /http:\/localhost:\d+/g;
|
||||
fixedPath = fixedPath.replace(localHost, "");
|
||||
return fixedPath;
|
||||
}
|
||||
return all.filter((_val, idx) => idx % shards === (shardId - 1)) as ReturnType<this["enumerateTestFiles"]>;
|
||||
}
|
||||
|
||||
/** The working directory where tests are found. Needed for batch testing where the input path will differ from the output path inside baselines */
|
||||
public workingDirectory = "";
|
||||
|
||||
/** Setup the runner's tests so that they are ready to be executed by the harness
|
||||
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
|
||||
*/
|
||||
public abstract initializeTests(): void;
|
||||
|
||||
/** Replaces instances of full paths with fileNames only */
|
||||
static removeFullPaths(path: string) {
|
||||
// If its a full path (starts with "C:" or "/") replace with just the filename
|
||||
let fixedPath = /^(\w:|\/)/.test(path) ? ts.getBaseFileName(path) : path;
|
||||
|
||||
// when running in the browser the 'full path' is the host name, shows up in error baselines
|
||||
const localHost = /http:\/localhost:\d+/g;
|
||||
fixedPath = fixedPath.replace(localHost, "");
|
||||
return fixedPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,7 +157,7 @@ namespace Harness.SourceMapRecorder {
|
||||
const startPos = lineMap[line];
|
||||
const endPos = lineMap[line + 1];
|
||||
const text = code.substring(startPos, endPos);
|
||||
return line === 0 ? utils.removeByteOrderMark(text) : text;
|
||||
return line === 0 ? Utils.removeByteOrderMark(text) : text;
|
||||
}
|
||||
|
||||
function writeJsFileLines(endJsLine: number) {
|
||||
|
||||
@ -19,14 +19,14 @@
|
||||
],
|
||||
|
||||
"files": [
|
||||
"collections.ts",
|
||||
"utils.ts",
|
||||
"documents.ts",
|
||||
"vpath.ts",
|
||||
"vfs.ts",
|
||||
"compiler.ts",
|
||||
"evaluator.ts",
|
||||
"fakes.ts",
|
||||
"collectionsImpl.ts",
|
||||
"util.ts",
|
||||
"documentsUtil.ts",
|
||||
"vpathUtil.ts",
|
||||
"vfsUtil.ts",
|
||||
"compilerImpl.ts",
|
||||
"evaluatorImpl.ts",
|
||||
"fakesHosts.ts",
|
||||
"client.ts",
|
||||
|
||||
"runnerbase.ts",
|
||||
@ -36,8 +36,8 @@
|
||||
"harnessIO.ts",
|
||||
"harnessLanguageService.ts",
|
||||
"virtualFileSystemWithWatch.ts",
|
||||
"fourslash.ts",
|
||||
"fourslashInterface.ts",
|
||||
"fourslashImpl.ts",
|
||||
"fourslashInterfaceImpl.ts",
|
||||
"typeWriter.ts",
|
||||
"loggedIO.ts"
|
||||
]
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Common utilities
|
||||
*/
|
||||
namespace utils {
|
||||
namespace Utils {
|
||||
const testPathPrefixRegExp = /(?:(file:\/{3})|\/)\.(ts|lib|src)\//g;
|
||||
export function removeTestPathPrefixes(text: string, retainTrailingDirectorySeparator?: boolean): string {
|
||||
return text !== undefined ? text.replace(testPathPrefixRegExp, (_, scheme) => scheme || (retainTrailingDirectorySeparator ? "/" : "")) : undefined!; // TODO: GH#18217
|
||||
@ -5,7 +5,7 @@ declare namespace ts.server {
|
||||
data: any;
|
||||
}
|
||||
|
||||
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: { stack?: string, message?: string } };
|
||||
export type RequireResult = { module: {}, error: undefined } | { module: undefined, error: { stack?: string, message?: string } };
|
||||
export interface ServerHost extends System {
|
||||
watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher;
|
||||
watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher;
|
||||
|
||||
@ -1025,59 +1025,54 @@ namespace ts {
|
||||
return sourceFile;
|
||||
}
|
||||
|
||||
export let disableIncrementalParsing = false; // eslint-disable-line prefer-const
|
||||
|
||||
export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange | undefined, aggressiveChecks?: boolean): SourceFile {
|
||||
// If we were given a text change range, and our version or open-ness changed, then
|
||||
// incrementally parse this file.
|
||||
if (textChangeRange) {
|
||||
if (version !== sourceFile.version) {
|
||||
// Once incremental parsing is ready, then just call into this function.
|
||||
if (!disableIncrementalParsing) {
|
||||
let newText: string;
|
||||
let newText: string;
|
||||
|
||||
// grab the fragment from the beginning of the original text to the beginning of the span
|
||||
const prefix = textChangeRange.span.start !== 0
|
||||
? sourceFile.text.substr(0, textChangeRange.span.start)
|
||||
: "";
|
||||
// grab the fragment from the beginning of the original text to the beginning of the span
|
||||
const prefix = textChangeRange.span.start !== 0
|
||||
? sourceFile.text.substr(0, textChangeRange.span.start)
|
||||
: "";
|
||||
|
||||
// grab the fragment from the end of the span till the end of the original text
|
||||
const suffix = textSpanEnd(textChangeRange.span) !== sourceFile.text.length
|
||||
? sourceFile.text.substr(textSpanEnd(textChangeRange.span))
|
||||
: "";
|
||||
// grab the fragment from the end of the span till the end of the original text
|
||||
const suffix = textSpanEnd(textChangeRange.span) !== sourceFile.text.length
|
||||
? sourceFile.text.substr(textSpanEnd(textChangeRange.span))
|
||||
: "";
|
||||
|
||||
if (textChangeRange.newLength === 0) {
|
||||
// edit was a deletion - just combine prefix and suffix
|
||||
newText = prefix && suffix ? prefix + suffix : prefix || suffix;
|
||||
}
|
||||
else {
|
||||
// it was actual edit, fetch the fragment of new text that correspond to new span
|
||||
const changedText = scriptSnapshot.getText(textChangeRange.span.start, textChangeRange.span.start + textChangeRange.newLength);
|
||||
// combine prefix, changed text and suffix
|
||||
newText = prefix && suffix
|
||||
? prefix + changedText + suffix
|
||||
: prefix
|
||||
? (prefix + changedText)
|
||||
: (changedText + suffix);
|
||||
}
|
||||
|
||||
const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks);
|
||||
setSourceFileFields(newSourceFile, scriptSnapshot, version);
|
||||
// after incremental parsing nameTable might not be up-to-date
|
||||
// drop it so it can be lazily recreated later
|
||||
newSourceFile.nameTable = undefined;
|
||||
|
||||
// dispose all resources held by old script snapshot
|
||||
if (sourceFile !== newSourceFile && sourceFile.scriptSnapshot) {
|
||||
if (sourceFile.scriptSnapshot.dispose) {
|
||||
sourceFile.scriptSnapshot.dispose();
|
||||
}
|
||||
|
||||
sourceFile.scriptSnapshot = undefined;
|
||||
}
|
||||
|
||||
return newSourceFile;
|
||||
if (textChangeRange.newLength === 0) {
|
||||
// edit was a deletion - just combine prefix and suffix
|
||||
newText = prefix && suffix ? prefix + suffix : prefix || suffix;
|
||||
}
|
||||
else {
|
||||
// it was actual edit, fetch the fragment of new text that correspond to new span
|
||||
const changedText = scriptSnapshot.getText(textChangeRange.span.start, textChangeRange.span.start + textChangeRange.newLength);
|
||||
// combine prefix, changed text and suffix
|
||||
newText = prefix && suffix
|
||||
? prefix + changedText + suffix
|
||||
: prefix
|
||||
? (prefix + changedText)
|
||||
: (changedText + suffix);
|
||||
}
|
||||
|
||||
const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks);
|
||||
setSourceFileFields(newSourceFile, scriptSnapshot, version);
|
||||
// after incremental parsing nameTable might not be up-to-date
|
||||
// drop it so it can be lazily recreated later
|
||||
newSourceFile.nameTable = undefined;
|
||||
|
||||
// dispose all resources held by old script snapshot
|
||||
if (sourceFile !== newSourceFile && sourceFile.scriptSnapshot) {
|
||||
if (sourceFile.scriptSnapshot.dispose) {
|
||||
sourceFile.scriptSnapshot.dispose();
|
||||
}
|
||||
|
||||
sourceFile.scriptSnapshot = undefined;
|
||||
}
|
||||
|
||||
return newSourceFile;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ interface PromiseConstructor {
|
||||
all<T>(values: (T | PromiseLike<T>)[]): Promise<T[]>;
|
||||
}
|
||||
/* @internal */
|
||||
declare let Promise: PromiseConstructor;
|
||||
declare var Promise: PromiseConstructor; // eslint-disable-line no-var
|
||||
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
|
||||
@ -1,13 +1,21 @@
|
||||
/* @internal */
|
||||
namespace ts {
|
||||
// NOTE: Due to how the project-reference merging ends up working, `T` isn't considered referenced until `Map` merges with the definition
|
||||
// in src/compiler/core.ts
|
||||
// @ts-ignore
|
||||
export interface Map<T> {
|
||||
// full type defined in ~/src/compiler/core.ts
|
||||
interface IteratorShim<T> {
|
||||
next(): { value: T, done?: false } | { value: never, done: true };
|
||||
}
|
||||
|
||||
export function createMapShim(): new <T>() => Map<T> {
|
||||
interface MapShim<T> {
|
||||
readonly size: number;
|
||||
get(key: string): T | undefined;
|
||||
set(key: string, value: T): this;
|
||||
has(key: string): boolean;
|
||||
delete(key: string): boolean;
|
||||
clear(): void;
|
||||
keys(): IteratorShim<string>;
|
||||
values(): IteratorShim<T>;
|
||||
entries(): IteratorShim<[string, T]>;
|
||||
forEach(action: (value: T, key: string) => void): void;
|
||||
}
|
||||
export function createMapShim(): new <T>() => MapShim<T> {
|
||||
/** Create a MapLike with good performance. */
|
||||
function createDictionaryObject<T>(): Record<string, T> {
|
||||
const map = Object.create(/*prototype*/ null); // eslint-disable-line no-null/no-null
|
||||
@ -46,7 +54,7 @@ namespace ts {
|
||||
this.selector = selector;
|
||||
}
|
||||
|
||||
public next(): { value: U, done: false } | { value: never, done: true } {
|
||||
public next(): { value: U, done?: false } | { value: never, done: true } {
|
||||
// Navigate to the next entry.
|
||||
while (this.currentEntry) {
|
||||
const skipNext = !!this.currentEntry.skipNext;
|
||||
@ -66,7 +74,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
return class <T> implements Map<T> {
|
||||
return class <T> implements MapShim<T> {
|
||||
private data = createDictionaryObject<MapEntry<T>>();
|
||||
public size = 0;
|
||||
|
||||
@ -183,15 +191,15 @@ namespace ts {
|
||||
this.lastEntry = firstEntry;
|
||||
}
|
||||
|
||||
keys(): Iterator<string> {
|
||||
keys(): IteratorShim<string> {
|
||||
return new MapIterator(this.firstEntry, key => key);
|
||||
}
|
||||
|
||||
values(): Iterator<T> {
|
||||
values(): IteratorShim<T> {
|
||||
return new MapIterator(this.firstEntry, (_key, value) => value);
|
||||
}
|
||||
|
||||
entries(): Iterator<[string, T]> {
|
||||
entries(): IteratorShim<[string, T]> {
|
||||
return new MapIterator(this.firstEntry, (key, value) => [key, value] as [string, T]);
|
||||
}
|
||||
|
||||
|
||||
2
src/testRunner/compilerRef.ts
Normal file
2
src/testRunner/compilerRef.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// empty ref to compiler so it can be referenced by unittests
|
||||
namespace compiler {}
|
||||
@ -1,333 +1,335 @@
|
||||
const enum CompilerTestType {
|
||||
Conformance,
|
||||
Regressions,
|
||||
Test262
|
||||
}
|
||||
|
||||
interface CompilerFileBasedTest extends Harness.FileBasedTest {
|
||||
readonly content?: string;
|
||||
}
|
||||
|
||||
class CompilerBaselineRunner extends RunnerBase {
|
||||
private basePath = "tests/cases";
|
||||
private testSuiteName: TestRunnerKind;
|
||||
private emit: boolean;
|
||||
|
||||
public options: string | undefined;
|
||||
|
||||
constructor(public testType: CompilerTestType) {
|
||||
super();
|
||||
this.emit = true;
|
||||
if (testType === CompilerTestType.Conformance) {
|
||||
this.testSuiteName = "conformance";
|
||||
}
|
||||
else if (testType === CompilerTestType.Regressions) {
|
||||
this.testSuiteName = "compiler";
|
||||
}
|
||||
else if (testType === CompilerTestType.Test262) {
|
||||
this.testSuiteName = "test262";
|
||||
}
|
||||
else {
|
||||
this.testSuiteName = "compiler"; // default to this for historical reasons
|
||||
}
|
||||
this.basePath += "/" + this.testSuiteName;
|
||||
namespace Harness {
|
||||
export const enum CompilerTestType {
|
||||
Conformance,
|
||||
Regressions,
|
||||
Test262
|
||||
}
|
||||
|
||||
public kind() {
|
||||
return this.testSuiteName;
|
||||
interface CompilerFileBasedTest extends FileBasedTest {
|
||||
readonly content?: string;
|
||||
}
|
||||
|
||||
public enumerateTestFiles() {
|
||||
// see also: `enumerateTestFiles` in tests/webTestServer.ts
|
||||
return this.enumerateFiles(this.basePath, /\.tsx?$/, { recursive: true }).map(CompilerTest.getConfigurations);
|
||||
}
|
||||
export class CompilerBaselineRunner extends RunnerBase {
|
||||
private basePath = "tests/cases";
|
||||
private testSuiteName: TestRunnerKind;
|
||||
private emit: boolean;
|
||||
|
||||
public initializeTests() {
|
||||
describe(this.testSuiteName + " tests", () => {
|
||||
describe("Setup compiler for compiler baselines", () => {
|
||||
this.parseOptions();
|
||||
});
|
||||
public options: string | undefined;
|
||||
|
||||
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
|
||||
const files = this.tests.length > 0 ? this.tests : Harness.IO.enumerateTestFiles(this);
|
||||
files.forEach(test => {
|
||||
const file = typeof test === "string" ? test : test.file;
|
||||
this.checkTestCodeOutput(vpath.normalizeSeparators(file), typeof test === "string" ? CompilerTest.getConfigurations(test) : test);
|
||||
});
|
||||
});
|
||||
}
|
||||
constructor(public testType: CompilerTestType) {
|
||||
super();
|
||||
this.emit = true;
|
||||
if (testType === CompilerTestType.Conformance) {
|
||||
this.testSuiteName = "conformance";
|
||||
}
|
||||
else if (testType === CompilerTestType.Regressions) {
|
||||
this.testSuiteName = "compiler";
|
||||
}
|
||||
else if (testType === CompilerTestType.Test262) {
|
||||
this.testSuiteName = "test262";
|
||||
}
|
||||
else {
|
||||
this.testSuiteName = "compiler"; // default to this for historical reasons
|
||||
}
|
||||
this.basePath += "/" + this.testSuiteName;
|
||||
}
|
||||
|
||||
public checkTestCodeOutput(fileName: string, test?: CompilerFileBasedTest) {
|
||||
if (test && ts.some(test.configurations)) {
|
||||
test.configurations.forEach(configuration => {
|
||||
describe(`${this.testSuiteName} tests for ${fileName}${configuration ? ` (${Harness.getFileBasedTestConfigurationDescription(configuration)})` : ``}`, () => {
|
||||
this.runSuite(fileName, test, configuration);
|
||||
public kind() {
|
||||
return this.testSuiteName;
|
||||
}
|
||||
|
||||
public enumerateTestFiles() {
|
||||
// see also: `enumerateTestFiles` in tests/webTestServer.ts
|
||||
return this.enumerateFiles(this.basePath, /\.tsx?$/, { recursive: true }).map(CompilerTest.getConfigurations);
|
||||
}
|
||||
|
||||
public initializeTests() {
|
||||
describe(this.testSuiteName + " tests", () => {
|
||||
describe("Setup compiler for compiler baselines", () => {
|
||||
this.parseOptions();
|
||||
});
|
||||
|
||||
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
|
||||
const files = this.tests.length > 0 ? this.tests : IO.enumerateTestFiles(this);
|
||||
files.forEach(test => {
|
||||
const file = typeof test === "string" ? test : test.file;
|
||||
this.checkTestCodeOutput(vpath.normalizeSeparators(file), typeof test === "string" ? CompilerTest.getConfigurations(test) : test);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
describe(`${this.testSuiteName} tests for ${fileName}`, () => {
|
||||
this.runSuite(fileName, test);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private runSuite(fileName: string, test?: CompilerFileBasedTest, configuration?: Harness.FileBasedTestConfiguration) {
|
||||
// Mocha holds onto the closure environment of the describe callback even after the test is done.
|
||||
// Everything declared here should be cleared out in the "after" callback.
|
||||
let compilerTest!: CompilerTest;
|
||||
before(() => {
|
||||
let payload;
|
||||
if (test && test.content) {
|
||||
const rootDir = test.file.indexOf("conformance") === -1 ? "tests/cases/compiler/" : ts.getDirectoryPath(test.file) + "/";
|
||||
payload = Harness.TestCaseParser.makeUnitsFromTest(test.content, test.file, rootDir);
|
||||
public checkTestCodeOutput(fileName: string, test?: CompilerFileBasedTest) {
|
||||
if (test && ts.some(test.configurations)) {
|
||||
test.configurations.forEach(configuration => {
|
||||
describe(`${this.testSuiteName} tests for ${fileName}${configuration ? ` (${getFileBasedTestConfigurationDescription(configuration)})` : ``}`, () => {
|
||||
this.runSuite(fileName, test, configuration);
|
||||
});
|
||||
});
|
||||
}
|
||||
compilerTest = new CompilerTest(fileName, payload, configuration);
|
||||
});
|
||||
it(`Correct errors for ${fileName}`, () => { compilerTest.verifyDiagnostics(); });
|
||||
it(`Correct module resolution tracing for ${fileName}`, () => { compilerTest.verifyModuleResolution(); });
|
||||
it(`Correct sourcemap content for ${fileName}`, () => { compilerTest.verifySourceMapRecord(); });
|
||||
it(`Correct JS output for ${fileName}`, () => { if (this.emit) compilerTest.verifyJavaScriptOutput(); });
|
||||
it(`Correct Sourcemap output for ${fileName}`, () => { compilerTest.verifySourceMapOutput(); });
|
||||
it(`Correct type/symbol baselines for ${fileName}`, () => { compilerTest.verifyTypesAndSymbols(); });
|
||||
after(() => { compilerTest = undefined!; });
|
||||
}
|
||||
else {
|
||||
describe(`${this.testSuiteName} tests for ${fileName}`, () => {
|
||||
this.runSuite(fileName, test);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private parseOptions() {
|
||||
if (this.options && this.options.length > 0) {
|
||||
this.emit = false;
|
||||
private runSuite(fileName: string, test?: CompilerFileBasedTest, configuration?: FileBasedTestConfiguration) {
|
||||
// Mocha holds onto the closure environment of the describe callback even after the test is done.
|
||||
// Everything declared here should be cleared out in the "after" callback.
|
||||
let compilerTest!: CompilerTest;
|
||||
before(() => {
|
||||
let payload;
|
||||
if (test && test.content) {
|
||||
const rootDir = test.file.indexOf("conformance") === -1 ? "tests/cases/compiler/" : ts.getDirectoryPath(test.file) + "/";
|
||||
payload = TestCaseParser.makeUnitsFromTest(test.content, test.file, rootDir);
|
||||
}
|
||||
compilerTest = new CompilerTest(fileName, payload, configuration);
|
||||
});
|
||||
it(`Correct errors for ${fileName}`, () => { compilerTest.verifyDiagnostics(); });
|
||||
it(`Correct module resolution tracing for ${fileName}`, () => { compilerTest.verifyModuleResolution(); });
|
||||
it(`Correct sourcemap content for ${fileName}`, () => { compilerTest.verifySourceMapRecord(); });
|
||||
it(`Correct JS output for ${fileName}`, () => { if (this.emit) compilerTest.verifyJavaScriptOutput(); });
|
||||
it(`Correct Sourcemap output for ${fileName}`, () => { compilerTest.verifySourceMapOutput(); });
|
||||
it(`Correct type/symbol baselines for ${fileName}`, () => { compilerTest.verifyTypesAndSymbols(); });
|
||||
after(() => { compilerTest = undefined!; });
|
||||
}
|
||||
|
||||
const opts = this.options.split(",");
|
||||
for (const opt of opts) {
|
||||
switch (opt) {
|
||||
case "emit":
|
||||
this.emit = true;
|
||||
break;
|
||||
default:
|
||||
throw new Error("unsupported flag");
|
||||
private parseOptions() {
|
||||
if (this.options && this.options.length > 0) {
|
||||
this.emit = false;
|
||||
|
||||
const opts = this.options.split(",");
|
||||
for (const opt of opts) {
|
||||
switch (opt) {
|
||||
case "emit":
|
||||
this.emit = true;
|
||||
break;
|
||||
default:
|
||||
throw new Error("unsupported flag");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CompilerTest {
|
||||
private static varyBy: readonly string[] = [
|
||||
"module",
|
||||
"target",
|
||||
"jsx",
|
||||
"removeComments",
|
||||
"importHelpers",
|
||||
"importHelpers",
|
||||
"downlevelIteration",
|
||||
"isolatedModules",
|
||||
"strict",
|
||||
"noImplicitAny",
|
||||
"strictNullChecks",
|
||||
"strictFunctionTypes",
|
||||
"strictBindCallApply",
|
||||
"strictPropertyInitialization",
|
||||
"noImplicitThis",
|
||||
"alwaysStrict",
|
||||
"allowSyntheticDefaultImports",
|
||||
"esModuleInterop",
|
||||
"emitDecoratorMetadata",
|
||||
"skipDefaultLibCheck",
|
||||
"preserveConstEnums",
|
||||
"skipLibCheck",
|
||||
];
|
||||
private fileName: string;
|
||||
private justName: string;
|
||||
private configuredName: string;
|
||||
private lastUnit: Harness.TestCaseParser.TestUnitData;
|
||||
private harnessSettings: Harness.TestCaseParser.CompilerSettings;
|
||||
private hasNonDtsFiles: boolean;
|
||||
private result: compiler.CompilationResult;
|
||||
private options: ts.CompilerOptions;
|
||||
private tsConfigFiles: Harness.Compiler.TestFile[];
|
||||
// equivalent to the files that will be passed on the command line
|
||||
private toBeCompiled: Harness.Compiler.TestFile[];
|
||||
// equivalent to other files on the file system not directly passed to the compiler (ie things that are referenced by other files)
|
||||
private otherFiles: Harness.Compiler.TestFile[];
|
||||
class CompilerTest {
|
||||
private static varyBy: readonly string[] = [
|
||||
"module",
|
||||
"target",
|
||||
"jsx",
|
||||
"removeComments",
|
||||
"importHelpers",
|
||||
"importHelpers",
|
||||
"downlevelIteration",
|
||||
"isolatedModules",
|
||||
"strict",
|
||||
"noImplicitAny",
|
||||
"strictNullChecks",
|
||||
"strictFunctionTypes",
|
||||
"strictBindCallApply",
|
||||
"strictPropertyInitialization",
|
||||
"noImplicitThis",
|
||||
"alwaysStrict",
|
||||
"allowSyntheticDefaultImports",
|
||||
"esModuleInterop",
|
||||
"emitDecoratorMetadata",
|
||||
"skipDefaultLibCheck",
|
||||
"preserveConstEnums",
|
||||
"skipLibCheck",
|
||||
];
|
||||
private fileName: string;
|
||||
private justName: string;
|
||||
private configuredName: string;
|
||||
private lastUnit: TestCaseParser.TestUnitData;
|
||||
private harnessSettings: TestCaseParser.CompilerSettings;
|
||||
private hasNonDtsFiles: boolean;
|
||||
private result: compiler.CompilationResult;
|
||||
private options: ts.CompilerOptions;
|
||||
private tsConfigFiles: Compiler.TestFile[];
|
||||
// equivalent to the files that will be passed on the command line
|
||||
private toBeCompiled: Compiler.TestFile[];
|
||||
// equivalent to other files on the file system not directly passed to the compiler (ie things that are referenced by other files)
|
||||
private otherFiles: Compiler.TestFile[];
|
||||
|
||||
constructor(fileName: string, testCaseContent?: Harness.TestCaseParser.TestCaseContent, configurationOverrides?: Harness.TestCaseParser.CompilerSettings) {
|
||||
this.fileName = fileName;
|
||||
this.justName = vpath.basename(fileName);
|
||||
this.configuredName = this.justName;
|
||||
if (configurationOverrides) {
|
||||
let configuredName = "";
|
||||
const keys = Object
|
||||
.keys(configurationOverrides)
|
||||
.map(k => k.toLowerCase())
|
||||
.sort();
|
||||
for (const key of keys) {
|
||||
constructor(fileName: string, testCaseContent?: TestCaseParser.TestCaseContent, configurationOverrides?: TestCaseParser.CompilerSettings) {
|
||||
this.fileName = fileName;
|
||||
this.justName = vpath.basename(fileName);
|
||||
this.configuredName = this.justName;
|
||||
if (configurationOverrides) {
|
||||
let configuredName = "";
|
||||
const keys = Object
|
||||
.keys(configurationOverrides)
|
||||
.map(k => k.toLowerCase())
|
||||
.sort();
|
||||
for (const key of keys) {
|
||||
if (configuredName) {
|
||||
configuredName += ",";
|
||||
}
|
||||
configuredName += `${key}=${configurationOverrides[key].toLowerCase()}`;
|
||||
}
|
||||
if (configuredName) {
|
||||
configuredName += ",";
|
||||
const extname = vpath.extname(this.justName);
|
||||
const basename = vpath.basename(this.justName, extname, /*ignoreCase*/ true);
|
||||
this.configuredName = `${basename}(${configuredName})${extname}`;
|
||||
}
|
||||
configuredName += `${key}=${configurationOverrides[key].toLowerCase()}`;
|
||||
}
|
||||
if (configuredName) {
|
||||
const extname = vpath.extname(this.justName);
|
||||
const basename = vpath.basename(this.justName, extname, /*ignoreCase*/ true);
|
||||
this.configuredName = `${basename}(${configuredName})${extname}`;
|
||||
|
||||
const rootDir = fileName.indexOf("conformance") === -1 ? "tests/cases/compiler/" : ts.getDirectoryPath(fileName) + "/";
|
||||
|
||||
if (testCaseContent === undefined) {
|
||||
testCaseContent = TestCaseParser.makeUnitsFromTest(IO.readFile(fileName)!, fileName, rootDir);
|
||||
}
|
||||
}
|
||||
|
||||
const rootDir = fileName.indexOf("conformance") === -1 ? "tests/cases/compiler/" : ts.getDirectoryPath(fileName) + "/";
|
||||
|
||||
if (testCaseContent === undefined) {
|
||||
testCaseContent = Harness.TestCaseParser.makeUnitsFromTest(Harness.IO.readFile(fileName)!, fileName, rootDir);
|
||||
}
|
||||
|
||||
if (configurationOverrides) {
|
||||
testCaseContent = { ...testCaseContent, settings: { ...testCaseContent.settings, ...configurationOverrides } };
|
||||
}
|
||||
|
||||
const units = testCaseContent.testUnitData;
|
||||
this.harnessSettings = testCaseContent.settings;
|
||||
let tsConfigOptions: ts.CompilerOptions | undefined;
|
||||
this.tsConfigFiles = [];
|
||||
if (testCaseContent.tsConfig) {
|
||||
assert.equal(testCaseContent.tsConfig.fileNames.length, 0, `list of files in tsconfig is not currently supported`);
|
||||
assert.equal(testCaseContent.tsConfig.raw.exclude, undefined, `exclude in tsconfig is not currently supported`);
|
||||
|
||||
tsConfigOptions = ts.cloneCompilerOptions(testCaseContent.tsConfig.options);
|
||||
this.tsConfigFiles.push(this.createHarnessTestFile(testCaseContent.tsConfigFileUnitData!, rootDir, ts.combinePaths(rootDir, tsConfigOptions.configFilePath)));
|
||||
}
|
||||
else {
|
||||
const baseUrl = this.harnessSettings.baseUrl;
|
||||
if (baseUrl !== undefined && !ts.isRootedDiskPath(baseUrl)) {
|
||||
this.harnessSettings.baseUrl = ts.getNormalizedAbsolutePath(baseUrl, rootDir);
|
||||
if (configurationOverrides) {
|
||||
testCaseContent = { ...testCaseContent, settings: { ...testCaseContent.settings, ...configurationOverrides } };
|
||||
}
|
||||
}
|
||||
|
||||
this.lastUnit = units[units.length - 1];
|
||||
this.hasNonDtsFiles = units.some(unit => !ts.fileExtensionIs(unit.name, ts.Extension.Dts));
|
||||
// We need to assemble the list of input files for the compiler and other related files on the 'filesystem' (ie in a multi-file test)
|
||||
// If the last file in a test uses require or a triple slash reference we'll assume all other files will be brought in via references,
|
||||
// otherwise, assume all files are just meant to be in the same compilation session without explicit references to one another.
|
||||
this.toBeCompiled = [];
|
||||
this.otherFiles = [];
|
||||
const units = testCaseContent.testUnitData;
|
||||
this.harnessSettings = testCaseContent.settings;
|
||||
let tsConfigOptions: ts.CompilerOptions | undefined;
|
||||
this.tsConfigFiles = [];
|
||||
if (testCaseContent.tsConfig) {
|
||||
assert.equal(testCaseContent.tsConfig.fileNames.length, 0, `list of files in tsconfig is not currently supported`);
|
||||
assert.equal(testCaseContent.tsConfig.raw.exclude, undefined, `exclude in tsconfig is not currently supported`);
|
||||
|
||||
if (testCaseContent.settings.noImplicitReferences || /require\(/.test(this.lastUnit.content) || /reference\spath/.test(this.lastUnit.content)) {
|
||||
this.toBeCompiled.push(this.createHarnessTestFile(this.lastUnit, rootDir));
|
||||
units.forEach(unit => {
|
||||
if (unit.name !== this.lastUnit.name) {
|
||||
this.otherFiles.push(this.createHarnessTestFile(unit, rootDir));
|
||||
tsConfigOptions = ts.cloneCompilerOptions(testCaseContent.tsConfig.options);
|
||||
this.tsConfigFiles.push(this.createHarnessTestFile(testCaseContent.tsConfigFileUnitData!, rootDir, ts.combinePaths(rootDir, tsConfigOptions.configFilePath)));
|
||||
}
|
||||
else {
|
||||
const baseUrl = this.harnessSettings.baseUrl;
|
||||
if (baseUrl !== undefined && !ts.isRootedDiskPath(baseUrl)) {
|
||||
this.harnessSettings.baseUrl = ts.getNormalizedAbsolutePath(baseUrl, rootDir);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.toBeCompiled = units.map(unit => {
|
||||
return this.createHarnessTestFile(unit, rootDir);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (tsConfigOptions && tsConfigOptions.configFilePath !== undefined) {
|
||||
tsConfigOptions.configFilePath = ts.combinePaths(rootDir, tsConfigOptions.configFilePath);
|
||||
tsConfigOptions.configFile!.fileName = tsConfigOptions.configFilePath;
|
||||
}
|
||||
this.lastUnit = units[units.length - 1];
|
||||
this.hasNonDtsFiles = units.some(unit => !ts.fileExtensionIs(unit.name, ts.Extension.Dts));
|
||||
// We need to assemble the list of input files for the compiler and other related files on the 'filesystem' (ie in a multi-file test)
|
||||
// If the last file in a test uses require or a triple slash reference we'll assume all other files will be brought in via references,
|
||||
// otherwise, assume all files are just meant to be in the same compilation session without explicit references to one another.
|
||||
this.toBeCompiled = [];
|
||||
this.otherFiles = [];
|
||||
|
||||
this.result = Harness.Compiler.compileFiles(
|
||||
this.toBeCompiled,
|
||||
this.otherFiles,
|
||||
this.harnessSettings,
|
||||
/*options*/ tsConfigOptions,
|
||||
/*currentDirectory*/ this.harnessSettings.currentDirectory,
|
||||
testCaseContent.symlinks
|
||||
);
|
||||
if (testCaseContent.settings.noImplicitReferences || /require\(/.test(this.lastUnit.content) || /reference\spath/.test(this.lastUnit.content)) {
|
||||
this.toBeCompiled.push(this.createHarnessTestFile(this.lastUnit, rootDir));
|
||||
units.forEach(unit => {
|
||||
if (unit.name !== this.lastUnit.name) {
|
||||
this.otherFiles.push(this.createHarnessTestFile(unit, rootDir));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.toBeCompiled = units.map(unit => {
|
||||
return this.createHarnessTestFile(unit, rootDir);
|
||||
});
|
||||
}
|
||||
|
||||
this.options = this.result.options;
|
||||
}
|
||||
if (tsConfigOptions && tsConfigOptions.configFilePath !== undefined) {
|
||||
tsConfigOptions.configFilePath = ts.combinePaths(rootDir, tsConfigOptions.configFilePath);
|
||||
tsConfigOptions.configFile!.fileName = tsConfigOptions.configFilePath;
|
||||
}
|
||||
|
||||
public static getConfigurations(file: string): CompilerFileBasedTest {
|
||||
// also see `parseCompilerTestConfigurations` in tests/webTestServer.ts
|
||||
const content = Harness.IO.readFile(file)!;
|
||||
const settings = Harness.TestCaseParser.extractCompilerSettings(content);
|
||||
const configurations = Harness.getFileBasedTestConfigurations(settings, CompilerTest.varyBy);
|
||||
return { file, configurations, content };
|
||||
}
|
||||
|
||||
public verifyDiagnostics() {
|
||||
// check errors
|
||||
Harness.Compiler.doErrorBaseline(
|
||||
this.configuredName,
|
||||
this.tsConfigFiles.concat(this.toBeCompiled, this.otherFiles),
|
||||
this.result.diagnostics,
|
||||
!!this.options.pretty);
|
||||
}
|
||||
|
||||
public verifyModuleResolution() {
|
||||
if (this.options.traceResolution) {
|
||||
Harness.Baseline.runBaseline(this.configuredName.replace(/\.tsx?$/, ".trace.json"),
|
||||
JSON.stringify(this.result.traces.map(utils.sanitizeTraceResolutionLogEntry), undefined, 4));
|
||||
}
|
||||
}
|
||||
|
||||
public verifySourceMapRecord() {
|
||||
if (this.options.sourceMap || this.options.inlineSourceMap || this.options.declarationMap) {
|
||||
const record = utils.removeTestPathPrefixes(this.result.getSourceMapRecord()!);
|
||||
const baseline = (this.options.noEmitOnError && this.result.diagnostics.length !== 0) || record === undefined
|
||||
// Because of the noEmitOnError option no files are created. We need to return null because baselining isn't required.
|
||||
? null // eslint-disable-line no-null/no-null
|
||||
: record;
|
||||
Harness.Baseline.runBaseline(this.configuredName.replace(/\.tsx?$/, ".sourcemap.txt"), baseline);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyJavaScriptOutput() {
|
||||
if (this.hasNonDtsFiles) {
|
||||
Harness.Compiler.doJsEmitBaseline(
|
||||
this.configuredName,
|
||||
this.fileName,
|
||||
this.options,
|
||||
this.result,
|
||||
this.tsConfigFiles,
|
||||
this.result = Compiler.compileFiles(
|
||||
this.toBeCompiled,
|
||||
this.otherFiles,
|
||||
this.harnessSettings,
|
||||
/*options*/ tsConfigOptions,
|
||||
/*currentDirectory*/ this.harnessSettings.currentDirectory,
|
||||
testCaseContent.symlinks
|
||||
);
|
||||
|
||||
this.options = this.result.options;
|
||||
}
|
||||
|
||||
public static getConfigurations(file: string): CompilerFileBasedTest {
|
||||
// also see `parseCompilerTestConfigurations` in tests/webTestServer.ts
|
||||
const content = IO.readFile(file)!;
|
||||
const settings = TestCaseParser.extractCompilerSettings(content);
|
||||
const configurations = getFileBasedTestConfigurations(settings, CompilerTest.varyBy);
|
||||
return { file, configurations, content };
|
||||
}
|
||||
|
||||
public verifyDiagnostics() {
|
||||
// check errors
|
||||
Compiler.doErrorBaseline(
|
||||
this.configuredName,
|
||||
this.tsConfigFiles.concat(this.toBeCompiled, this.otherFiles),
|
||||
this.result.diagnostics,
|
||||
!!this.options.pretty);
|
||||
}
|
||||
|
||||
public verifyModuleResolution() {
|
||||
if (this.options.traceResolution) {
|
||||
Baseline.runBaseline(this.configuredName.replace(/\.tsx?$/, ".trace.json"),
|
||||
JSON.stringify(this.result.traces.map(Utils.sanitizeTraceResolutionLogEntry), undefined, 4));
|
||||
}
|
||||
}
|
||||
|
||||
public verifySourceMapRecord() {
|
||||
if (this.options.sourceMap || this.options.inlineSourceMap || this.options.declarationMap) {
|
||||
const record = Utils.removeTestPathPrefixes(this.result.getSourceMapRecord()!);
|
||||
const baseline = (this.options.noEmitOnError && this.result.diagnostics.length !== 0) || record === undefined
|
||||
// Because of the noEmitOnError option no files are created. We need to return null because baselining isn't required.
|
||||
? null // eslint-disable-line no-null/no-null
|
||||
: record;
|
||||
Baseline.runBaseline(this.configuredName.replace(/\.tsx?$/, ".sourcemap.txt"), baseline);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyJavaScriptOutput() {
|
||||
if (this.hasNonDtsFiles) {
|
||||
Compiler.doJsEmitBaseline(
|
||||
this.configuredName,
|
||||
this.fileName,
|
||||
this.options,
|
||||
this.result,
|
||||
this.tsConfigFiles,
|
||||
this.toBeCompiled,
|
||||
this.otherFiles,
|
||||
this.harnessSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public verifySourceMapOutput() {
|
||||
Compiler.doSourcemapBaseline(
|
||||
this.configuredName,
|
||||
this.options,
|
||||
this.result,
|
||||
this.harnessSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public verifySourceMapOutput() {
|
||||
Harness.Compiler.doSourcemapBaseline(
|
||||
this.configuredName,
|
||||
this.options,
|
||||
this.result,
|
||||
this.harnessSettings);
|
||||
}
|
||||
public verifyTypesAndSymbols() {
|
||||
if (this.fileName.indexOf("APISample") >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
public verifyTypesAndSymbols() {
|
||||
if (this.fileName.indexOf("APISample") >= 0) {
|
||||
return;
|
||||
const noTypesAndSymbols =
|
||||
this.harnessSettings.noTypesAndSymbols &&
|
||||
this.harnessSettings.noTypesAndSymbols.toLowerCase() === "true";
|
||||
if (noTypesAndSymbols) {
|
||||
return;
|
||||
}
|
||||
|
||||
Compiler.doTypeAndSymbolBaseline(
|
||||
this.configuredName,
|
||||
this.result.program!,
|
||||
this.toBeCompiled.concat(this.otherFiles).filter(file => !!this.result.program!.getSourceFile(file.unitName)),
|
||||
/*opts*/ undefined,
|
||||
/*multifile*/ undefined,
|
||||
/*skipTypeBaselines*/ undefined,
|
||||
/*skipSymbolBaselines*/ undefined,
|
||||
!!ts.length(this.result.diagnostics)
|
||||
);
|
||||
}
|
||||
|
||||
const noTypesAndSymbols =
|
||||
this.harnessSettings.noTypesAndSymbols &&
|
||||
this.harnessSettings.noTypesAndSymbols.toLowerCase() === "true";
|
||||
if (noTypesAndSymbols) {
|
||||
return;
|
||||
private makeUnitName(name: string, root: string) {
|
||||
const path = ts.toPath(name, root, ts.identity);
|
||||
const pathStart = ts.toPath(IO.getCurrentDirectory(), "", ts.identity);
|
||||
return pathStart ? path.replace(pathStart, "/") : path;
|
||||
}
|
||||
|
||||
Harness.Compiler.doTypeAndSymbolBaseline(
|
||||
this.configuredName,
|
||||
this.result.program!,
|
||||
this.toBeCompiled.concat(this.otherFiles).filter(file => !!this.result.program!.getSourceFile(file.unitName)),
|
||||
/*opts*/ undefined,
|
||||
/*multifile*/ undefined,
|
||||
/*skipTypeBaselines*/ undefined,
|
||||
/*skipSymbolBaselines*/ undefined,
|
||||
!!ts.length(this.result.diagnostics)
|
||||
);
|
||||
private createHarnessTestFile(lastUnit: TestCaseParser.TestUnitData, rootDir: string, unitName?: string): Compiler.TestFile {
|
||||
return { unitName: unitName || this.makeUnitName(lastUnit.name, rootDir), content: lastUnit.content, fileOptions: lastUnit.fileOptions };
|
||||
}
|
||||
}
|
||||
|
||||
private makeUnitName(name: string, root: string) {
|
||||
const path = ts.toPath(name, root, ts.identity);
|
||||
const pathStart = ts.toPath(Harness.IO.getCurrentDirectory(), "", ts.identity);
|
||||
return pathStart ? path.replace(pathStart, "/") : path;
|
||||
}
|
||||
|
||||
private createHarnessTestFile(lastUnit: Harness.TestCaseParser.TestUnitData, rootDir: string, unitName?: string): Harness.Compiler.TestFile {
|
||||
return { unitName: unitName || this.makeUnitName(lastUnit.name, rootDir), content: lastUnit.content, fileOptions: lastUnit.fileOptions };
|
||||
}
|
||||
}
|
||||
}
|
||||
2
src/testRunner/documentsRef.ts
Normal file
2
src/testRunner/documentsRef.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// empty ref to documents so it can be referenced by unittests
|
||||
namespace documents {}
|
||||
2
src/testRunner/evaluatorRef.ts
Normal file
2
src/testRunner/evaluatorRef.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// empty ref to evaluator so it can be referenced by unittests
|
||||
namespace evaluator {}
|
||||
@ -1,338 +1,340 @@
|
||||
const fs: typeof import("fs") = require("fs");
|
||||
const path: typeof import("path") = require("path");
|
||||
const del: typeof import("del") = require("del");
|
||||
namespace Harness {
|
||||
const fs: typeof import("fs") = require("fs");
|
||||
const path: typeof import("path") = require("path");
|
||||
const del: typeof import("del") = require("del");
|
||||
|
||||
interface ExecResult {
|
||||
stdout: Buffer;
|
||||
stderr: Buffer;
|
||||
status: number | null;
|
||||
}
|
||||
|
||||
interface UserConfig {
|
||||
types: string[];
|
||||
cloneUrl: string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
abstract class ExternalCompileRunnerBase extends RunnerBase {
|
||||
abstract testDir: string;
|
||||
abstract report(result: ExecResult, cwd: string): string | null;
|
||||
enumerateTestFiles() {
|
||||
return Harness.IO.getDirectories(this.testDir);
|
||||
interface ExecResult {
|
||||
stdout: Buffer;
|
||||
stderr: Buffer;
|
||||
status: number | null;
|
||||
}
|
||||
/** Setup the runner's tests so that they are ready to be executed by the harness
|
||||
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
|
||||
*/
|
||||
initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
const testList = this.tests && this.tests.length ? this.tests : this.getTestFiles();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const cls = this;
|
||||
describe(`${this.kind()} code samples`, function(this: Mocha.ISuiteCallbackContext) {
|
||||
this.timeout(600_000); // 10 minutes
|
||||
for (const test of testList) {
|
||||
cls.runTest(typeof test === "string" ? test : test.file);
|
||||
}
|
||||
});
|
||||
interface UserConfig {
|
||||
types: string[];
|
||||
cloneUrl: string;
|
||||
path?: string;
|
||||
}
|
||||
private runTest(directoryName: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const cls = this;
|
||||
const timeout = 600_000; // 10 minutes
|
||||
describe(directoryName, function(this: Mocha.ISuiteCallbackContext) {
|
||||
this.timeout(timeout);
|
||||
const cp: typeof import("child_process") = require("child_process");
|
||||
|
||||
it("should build successfully", () => {
|
||||
let cwd = path.join(Harness.IO.getWorkspaceRoot(), cls.testDir, directoryName);
|
||||
const originalCwd = cwd;
|
||||
const stdio = isWorker ? "pipe" : "inherit";
|
||||
let types: string[] | undefined;
|
||||
if (fs.existsSync(path.join(cwd, "test.json"))) {
|
||||
const config = JSON.parse(fs.readFileSync(path.join(cwd, "test.json"), { encoding: "utf8" })) as UserConfig;
|
||||
ts.Debug.assert(!!config.types, "Bad format from test.json: Types field must be present.");
|
||||
ts.Debug.assert(!!config.cloneUrl, "Bad format from test.json: cloneUrl field must be present.");
|
||||
const submoduleDir = path.join(cwd, directoryName);
|
||||
if (!fs.existsSync(submoduleDir)) {
|
||||
exec("git", ["clone", config.cloneUrl, directoryName], { cwd });
|
||||
}
|
||||
exec("git", ["reset", "HEAD", "--hard"], { cwd: submoduleDir });
|
||||
exec("git", ["clean", "-f"], { cwd: submoduleDir });
|
||||
exec("git", ["pull", "-f"], { cwd: submoduleDir });
|
||||
abstract class ExternalCompileRunnerBase extends RunnerBase {
|
||||
abstract testDir: string;
|
||||
abstract report(result: ExecResult, cwd: string): string | null;
|
||||
enumerateTestFiles() {
|
||||
return IO.getDirectories(this.testDir);
|
||||
}
|
||||
/** Setup the runner's tests so that they are ready to be executed by the harness
|
||||
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
|
||||
*/
|
||||
initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
const testList = this.tests && this.tests.length ? this.tests : this.getTestFiles();
|
||||
|
||||
types = config.types;
|
||||
|
||||
cwd = config.path ? path.join(cwd, config.path) : submoduleDir;
|
||||
}
|
||||
if (fs.existsSync(path.join(cwd, "package.json"))) {
|
||||
if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
|
||||
fs.unlinkSync(path.join(cwd, "package-lock.json"));
|
||||
}
|
||||
if (fs.existsSync(path.join(cwd, "node_modules"))) {
|
||||
del.sync(path.join(cwd, "node_modules"), { force: true });
|
||||
}
|
||||
exec("npm", ["i", "--ignore-scripts"], { cwd, timeout: timeout / 2 }); // NPM shouldn't take the entire timeout - if it takes a long time, it should be terminated and we should log the failure
|
||||
}
|
||||
const args = [path.join(Harness.IO.getWorkspaceRoot(), "built/local/tsc.js")];
|
||||
if (types) {
|
||||
args.push("--types", types.join(","));
|
||||
// Also actually install those types (for, eg, the js projects which need node)
|
||||
exec("npm", ["i", ...types.map(t => `@types/${t}`), "--no-save", "--ignore-scripts"], { cwd: originalCwd, timeout: timeout / 2 }); // NPM shouldn't take the entire timeout - if it takes a long time, it should be terminated and we should log the failure
|
||||
}
|
||||
args.push("--noEmit");
|
||||
Harness.Baseline.runBaseline(`${cls.kind()}/${directoryName}.log`, cls.report(cp.spawnSync(`node`, args, { cwd, timeout, shell: true }), cwd));
|
||||
|
||||
function exec(command: string, args: string[], options: { cwd: string, timeout?: number }): void {
|
||||
const res = cp.spawnSync(command, args, { timeout, shell: true, stdio, ...options });
|
||||
if (res.status !== 0) {
|
||||
throw new Error(`${command} ${args.join(" ")} for ${directoryName} failed: ${res.stderr && res.stderr.toString()}`);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const cls = this;
|
||||
describe(`${this.kind()} code samples`, function(this: Mocha.ISuiteCallbackContext) {
|
||||
this.timeout(600_000); // 10 minutes
|
||||
for (const test of testList) {
|
||||
cls.runTest(typeof test === "string" ? test : test.file);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
private runTest(directoryName: string) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const cls = this;
|
||||
const timeout = 600_000; // 10 minutes
|
||||
describe(directoryName, function(this: Mocha.ISuiteCallbackContext) {
|
||||
this.timeout(timeout);
|
||||
const cp: typeof import("child_process") = require("child_process");
|
||||
|
||||
class UserCodeRunner extends ExternalCompileRunnerBase {
|
||||
readonly testDir = "tests/cases/user/";
|
||||
kind(): TestRunnerKind {
|
||||
return "user";
|
||||
}
|
||||
report(result: ExecResult) {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
return result.status === 0 && !result.stdout.length && !result.stderr.length ? null : `Exit Code: ${result.status}
|
||||
Standard output:
|
||||
${sortErrors(stripAbsoluteImportPaths(result.stdout.toString().replace(/\r\n/g, "\n")))}
|
||||
it("should build successfully", () => {
|
||||
let cwd = path.join(IO.getWorkspaceRoot(), cls.testDir, directoryName);
|
||||
const originalCwd = cwd;
|
||||
const stdio = isWorker ? "pipe" : "inherit";
|
||||
let types: string[] | undefined;
|
||||
if (fs.existsSync(path.join(cwd, "test.json"))) {
|
||||
const config = JSON.parse(fs.readFileSync(path.join(cwd, "test.json"), { encoding: "utf8" })) as UserConfig;
|
||||
ts.Debug.assert(!!config.types, "Bad format from test.json: Types field must be present.");
|
||||
ts.Debug.assert(!!config.cloneUrl, "Bad format from test.json: cloneUrl field must be present.");
|
||||
const submoduleDir = path.join(cwd, directoryName);
|
||||
if (!fs.existsSync(submoduleDir)) {
|
||||
exec("git", ["clone", config.cloneUrl, directoryName], { cwd });
|
||||
}
|
||||
exec("git", ["reset", "HEAD", "--hard"], { cwd: submoduleDir });
|
||||
exec("git", ["clean", "-f"], { cwd: submoduleDir });
|
||||
exec("git", ["pull", "-f"], { cwd: submoduleDir });
|
||||
|
||||
types = config.types;
|
||||
|
||||
Standard error:
|
||||
${stripAbsoluteImportPaths(result.stderr.toString().replace(/\r\n/g, "\n"))}`;
|
||||
}
|
||||
}
|
||||
cwd = config.path ? path.join(cwd, config.path) : submoduleDir;
|
||||
}
|
||||
if (fs.existsSync(path.join(cwd, "package.json"))) {
|
||||
if (fs.existsSync(path.join(cwd, "package-lock.json"))) {
|
||||
fs.unlinkSync(path.join(cwd, "package-lock.json"));
|
||||
}
|
||||
if (fs.existsSync(path.join(cwd, "node_modules"))) {
|
||||
del.sync(path.join(cwd, "node_modules"), { force: true });
|
||||
}
|
||||
exec("npm", ["i", "--ignore-scripts"], { cwd, timeout: timeout / 2 }); // NPM shouldn't take the entire timeout - if it takes a long time, it should be terminated and we should log the failure
|
||||
}
|
||||
const args = [path.join(IO.getWorkspaceRoot(), "built/local/tsc.js")];
|
||||
if (types) {
|
||||
args.push("--types", types.join(","));
|
||||
// Also actually install those types (for, eg, the js projects which need node)
|
||||
exec("npm", ["i", ...types.map(t => `@types/${t}`), "--no-save", "--ignore-scripts"], { cwd: originalCwd, timeout: timeout / 2 }); // NPM shouldn't take the entire timeout - if it takes a long time, it should be terminated and we should log the failure
|
||||
}
|
||||
args.push("--noEmit");
|
||||
Baseline.runBaseline(`${cls.kind()}/${directoryName}.log`, cls.report(cp.spawnSync(`node`, args, { cwd, timeout, shell: true }), cwd));
|
||||
|
||||
class DockerfileRunner extends ExternalCompileRunnerBase {
|
||||
readonly testDir = "tests/cases/docker/";
|
||||
kind(): TestRunnerKind {
|
||||
return "docker";
|
||||
}
|
||||
initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
const testList = this.tests && this.tests.length ? this.tests : this.getTestFiles();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const cls = this;
|
||||
describe(`${this.kind()} code samples`, function(this: Mocha.ISuiteCallbackContext) {
|
||||
this.timeout(cls.timeout); // 20 minutes
|
||||
before(() => {
|
||||
cls.exec("docker", ["build", ".", "-t", "typescript/typescript"], { cwd: Harness.IO.getWorkspaceRoot() }); // cached because workspace is hashed to determine cacheability
|
||||
});
|
||||
for (const test of testList) {
|
||||
const directory = typeof test === "string" ? test : test.file;
|
||||
const cwd = path.join(Harness.IO.getWorkspaceRoot(), cls.testDir, directory);
|
||||
it(`should build ${directory} successfully`, () => {
|
||||
const imageName = `tstest/${directory}`;
|
||||
cls.exec("docker", ["build", "--no-cache", ".", "-t", imageName], { cwd }); // --no-cache so the latest version of the repos referenced is always fetched
|
||||
const cp: typeof import("child_process") = require("child_process");
|
||||
Harness.Baseline.runBaseline(`${cls.kind()}/${directory}.log`, cls.report(cp.spawnSync(`docker`, ["run", imageName], { cwd, timeout: cls.timeout, shell: true })));
|
||||
function exec(command: string, args: string[], options: { cwd: string, timeout?: number }): void {
|
||||
const res = cp.spawnSync(command, args, { timeout, shell: true, stdio, ...options });
|
||||
if (res.status !== 0) {
|
||||
throw new Error(`${command} ${args.join(" ")} for ${directoryName} failed: ${res.stderr && res.stderr.toString()}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class UserCodeRunner extends ExternalCompileRunnerBase {
|
||||
readonly testDir = "tests/cases/user/";
|
||||
kind(): TestRunnerKind {
|
||||
return "user";
|
||||
}
|
||||
report(result: ExecResult) {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
return result.status === 0 && !result.stdout.length && !result.stderr.length ? null : `Exit Code: ${result.status}
|
||||
Standard output:
|
||||
${sortErrors(stripAbsoluteImportPaths(result.stdout.toString().replace(/\r\n/g, "\n")))}
|
||||
|
||||
|
||||
Standard error:
|
||||
${stripAbsoluteImportPaths(result.stderr.toString().replace(/\r\n/g, "\n"))}`;
|
||||
}
|
||||
}
|
||||
|
||||
export class DockerfileRunner extends ExternalCompileRunnerBase {
|
||||
readonly testDir = "tests/cases/docker/";
|
||||
kind(): TestRunnerKind {
|
||||
return "docker";
|
||||
}
|
||||
initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
const testList = this.tests && this.tests.length ? this.tests : this.getTestFiles();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const cls = this;
|
||||
describe(`${this.kind()} code samples`, function(this: Mocha.ISuiteCallbackContext) {
|
||||
this.timeout(cls.timeout); // 20 minutes
|
||||
before(() => {
|
||||
cls.exec("docker", ["build", ".", "-t", "typescript/typescript"], { cwd: IO.getWorkspaceRoot() }); // cached because workspace is hashed to determine cacheability
|
||||
});
|
||||
for (const test of testList) {
|
||||
const directory = typeof test === "string" ? test : test.file;
|
||||
const cwd = path.join(IO.getWorkspaceRoot(), cls.testDir, directory);
|
||||
it(`should build ${directory} successfully`, () => {
|
||||
const imageName = `tstest/${directory}`;
|
||||
cls.exec("docker", ["build", "--no-cache", ".", "-t", imageName], { cwd }); // --no-cache so the latest version of the repos referenced is always fetched
|
||||
const cp: typeof import("child_process") = require("child_process");
|
||||
Baseline.runBaseline(`${cls.kind()}/${directory}.log`, cls.report(cp.spawnSync(`docker`, ["run", imageName], { cwd, timeout: cls.timeout, shell: true })));
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private timeout = 1_200_000; // 20 minutes;
|
||||
private exec(command: string, args: string[], options: { cwd: string, timeout?: number }): void {
|
||||
const cp: typeof import("child_process") = require("child_process");
|
||||
const stdio = isWorker ? "pipe" : "inherit";
|
||||
const res = cp.spawnSync(command, args, { timeout: this.timeout, shell: true, stdio, ...options });
|
||||
if (res.status !== 0) {
|
||||
throw new Error(`${command} ${args.join(" ")} for ${options.cwd} failed: ${res.stderr && res.stderr.toString()}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
report(result: ExecResult) {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
return result.status === 0 && !result.stdout.length && !result.stderr.length ? null : `Exit Code: ${result.status}
|
||||
Standard output:
|
||||
${sanitizeDockerfileOutput(result.stdout.toString())}
|
||||
|
||||
private timeout = 1_200_000; // 20 minutes;
|
||||
private exec(command: string, args: string[], options: { cwd: string, timeout?: number }): void {
|
||||
const cp: typeof import("child_process") = require("child_process");
|
||||
const stdio = isWorker ? "pipe" : "inherit";
|
||||
const res = cp.spawnSync(command, args, { timeout: this.timeout, shell: true, stdio, ...options });
|
||||
if (res.status !== 0) {
|
||||
throw new Error(`${command} ${args.join(" ")} for ${options.cwd} failed: ${res.stderr && res.stderr.toString()}`);
|
||||
|
||||
Standard error:
|
||||
${sanitizeDockerfileOutput(result.stderr.toString())}`;
|
||||
}
|
||||
}
|
||||
report(result: ExecResult) {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
return result.status === 0 && !result.stdout.length && !result.stderr.length ? null : `Exit Code: ${result.status}
|
||||
Standard output:
|
||||
${sanitizeDockerfileOutput(result.stdout.toString())}
|
||||
|
||||
|
||||
Standard error:
|
||||
${sanitizeDockerfileOutput(result.stderr.toString())}`;
|
||||
function sanitizeDockerfileOutput(result: string): string {
|
||||
return [
|
||||
normalizeNewlines,
|
||||
stripANSIEscapes,
|
||||
stripRushStageNumbers,
|
||||
stripWebpackHash,
|
||||
sanitizeVersionSpecifiers,
|
||||
sanitizeTimestamps,
|
||||
sanitizeSizes,
|
||||
sanitizeUnimportantGulpOutput,
|
||||
stripAbsoluteImportPaths,
|
||||
].reduce((result, f) => f(result), result);
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeDockerfileOutput(result: string): string {
|
||||
return [
|
||||
normalizeNewlines,
|
||||
stripANSIEscapes,
|
||||
stripRushStageNumbers,
|
||||
stripWebpackHash,
|
||||
sanitizeVersionSpecifiers,
|
||||
sanitizeTimestamps,
|
||||
sanitizeSizes,
|
||||
sanitizeUnimportantGulpOutput,
|
||||
stripAbsoluteImportPaths,
|
||||
].reduce((result, f) => f(result), result);
|
||||
}
|
||||
|
||||
function normalizeNewlines(result: string): string {
|
||||
return result.replace(/\r\n/g, "\n");
|
||||
}
|
||||
|
||||
function stripANSIEscapes(result: string): string {
|
||||
return result.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
||||
}
|
||||
|
||||
function stripRushStageNumbers(result: string): string {
|
||||
return result.replace(/\d+ of \d+:/g, "XX of XX:");
|
||||
}
|
||||
|
||||
function stripWebpackHash(result: string): string {
|
||||
return result.replace(/Hash: \w+/g, "Hash: [redacted]");
|
||||
}
|
||||
|
||||
function sanitizeSizes(result: string): string {
|
||||
return result.replace(/\d+(\.\d+)? ((Ki|M)B|bytes)/g, "X KiB");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gulp's output order within a `parallel` block is nondeterministic (and there's no way to configure it to execute in series),
|
||||
* so we purge as much of the gulp output as we can
|
||||
*/
|
||||
function sanitizeUnimportantGulpOutput(result: string): string {
|
||||
return result.replace(/^.*(\] (Starting)|(Finished)).*$/gm, "") // "gulp" task start/end messages (nondeterministic order)
|
||||
.replace(/^.*(\] . (finished)|(started)).*$/gm, "") // "just" task start/end messages (nondeterministic order)
|
||||
.replace(/^.*\] Respawned to PID: \d+.*$/gm, "") // PID of child is OS and system-load dependent (likely stableish in a container but still dangerous)
|
||||
.replace(/\n+/g, "\n");
|
||||
}
|
||||
|
||||
function sanitizeTimestamps(result: string): string {
|
||||
return result.replace(/\[\d?\d:\d\d:\d\d (A|P)M\]/g, "[XX:XX:XX XM]")
|
||||
.replace(/\[\d?\d:\d\d:\d\d\]/g, "[XX:XX:XX]")
|
||||
.replace(/\/\d+-\d+-[\d_TZ]+-debug.log/g, "\/XXXX-XX-XXXXXXXXX-debug.log")
|
||||
.replace(/\d+\/\d+\/\d+ \d+:\d+:\d+ (AM|PM)/g, "XX/XX/XX XX:XX:XX XM")
|
||||
.replace(/\d+(\.\d+)? sec(onds?)?/g, "? seconds")
|
||||
.replace(/\d+(\.\d+)? min(utes?)?/g, "")
|
||||
.replace(/\d+(\.\d+)? ?m?s/g, "?s")
|
||||
.replace(/ \(\?s\)/g, "");
|
||||
}
|
||||
|
||||
function sanitizeVersionSpecifiers(result: string): string {
|
||||
return result
|
||||
.replace(/\d+.\d+.\d+-insiders.\d\d\d\d\d\d\d\d/g, "X.X.X-insiders.xxxxxxxx")
|
||||
.replace(/Rush Multi-Project Build Tool (\d+)\.\d+\.\d+/g, "Rush Multi-Project Build Tool $1.X.X")
|
||||
.replace(/([@v\()])\d+\.\d+\.\d+/g, "$1X.X.X")
|
||||
.replace(/webpack (\d+)\.\d+\.\d+/g, "webpack $1.X.X")
|
||||
.replace(/Webpack version: (\d+)\.\d+\.\d+/g, "Webpack version: $1.X.X");
|
||||
}
|
||||
|
||||
/**
|
||||
* Import types and some other error messages use absolute paths in errors as they have no context to be written relative to;
|
||||
* This is problematic for error baselines, so we grep for them and strip them out.
|
||||
*/
|
||||
function stripAbsoluteImportPaths(result: string) {
|
||||
const workspaceRegexp = new RegExp(Harness.IO.getWorkspaceRoot().replace(/\\/g, "\\\\"), "g");
|
||||
return result
|
||||
.replace(/import\(".*?\/tests\/cases\/user\//g, `import("/`)
|
||||
.replace(/Module '".*?\/tests\/cases\/user\//g, `Module '"/`)
|
||||
.replace(workspaceRegexp, "../../..");
|
||||
}
|
||||
|
||||
function sortErrors(result: string) {
|
||||
return ts.flatten(splitBy(result.split("\n"), s => /^\S+/.test(s)).sort(compareErrorStrings)).join("\n");
|
||||
}
|
||||
|
||||
const errorRegexp = /^(.+\.[tj]sx?)\((\d+),(\d+)\)(: error TS.*)/;
|
||||
function compareErrorStrings(a: string[], b: string[]) {
|
||||
ts.Debug.assertGreaterThanOrEqual(a.length, 1);
|
||||
ts.Debug.assertGreaterThanOrEqual(b.length, 1);
|
||||
const matchA = a[0].match(errorRegexp);
|
||||
if (!matchA) {
|
||||
return -1;
|
||||
function normalizeNewlines(result: string): string {
|
||||
return result.replace(/\r\n/g, "\n");
|
||||
}
|
||||
const matchB = b[0].match(errorRegexp);
|
||||
if (!matchB) {
|
||||
return 1;
|
||||
|
||||
function stripANSIEscapes(result: string): string {
|
||||
return result.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "");
|
||||
}
|
||||
const [, errorFileA, lineNumberStringA, columnNumberStringA, remainderA] = matchA;
|
||||
const [, errorFileB, lineNumberStringB, columnNumberStringB, remainderB] = matchB;
|
||||
return ts.comparePathsCaseSensitive(errorFileA, errorFileB) ||
|
||||
ts.compareValues(parseInt(lineNumberStringA), parseInt(lineNumberStringB)) ||
|
||||
ts.compareValues(parseInt(columnNumberStringA), parseInt(columnNumberStringB)) ||
|
||||
ts.compareStringsCaseSensitive(remainderA, remainderB) ||
|
||||
ts.compareStringsCaseSensitive(a.slice(1).join("\n"), b.slice(1).join("\n"));
|
||||
}
|
||||
|
||||
class DefinitelyTypedRunner extends ExternalCompileRunnerBase {
|
||||
readonly testDir = "../DefinitelyTyped/types/";
|
||||
workingDirectory = this.testDir;
|
||||
kind(): TestRunnerKind {
|
||||
return "dt";
|
||||
function stripRushStageNumbers(result: string): string {
|
||||
return result.replace(/\d+ of \d+:/g, "XX of XX:");
|
||||
}
|
||||
report(result: ExecResult, cwd: string) {
|
||||
const stdout = removeExpectedErrors(result.stdout.toString(), cwd);
|
||||
const stderr = result.stderr.toString();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
return !stdout.length && !stderr.length ? null : `Exit Code: ${result.status}
|
||||
Standard output:
|
||||
${stdout.replace(/\r\n/g, "\n")}
|
||||
|
||||
|
||||
Standard error:
|
||||
${stderr.replace(/\r\n/g, "\n")}`;
|
||||
function stripWebpackHash(result: string): string {
|
||||
return result.replace(/Hash: \w+/g, "Hash: [redacted]");
|
||||
}
|
||||
}
|
||||
|
||||
function removeExpectedErrors(errors: string, cwd: string): string {
|
||||
return ts.flatten(splitBy(errors.split("\n"), s => /^\S+/.test(s)).filter(isUnexpectedError(cwd))).join("\n");
|
||||
}
|
||||
/**
|
||||
* Returns true if the line that caused the error contains '$ExpectError',
|
||||
* or if the line before that one contains '$ExpectError'.
|
||||
* '$ExpectError' is a marker used in Definitely Typed tests,
|
||||
* meaning that the error should not contribute toward our error baslines.
|
||||
*/
|
||||
function isUnexpectedError(cwd: string) {
|
||||
return (error: string[]) => {
|
||||
ts.Debug.assertGreaterThanOrEqual(error.length, 1);
|
||||
const match = error[0].match(/(.+\.tsx?)\((\d+),\d+\): error TS/);
|
||||
if (!match) {
|
||||
return true;
|
||||
function sanitizeSizes(result: string): string {
|
||||
return result.replace(/\d+(\.\d+)? ((Ki|M)B|bytes)/g, "X KiB");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gulp's output order within a `parallel` block is nondeterministic (and there's no way to configure it to execute in series),
|
||||
* so we purge as much of the gulp output as we can
|
||||
*/
|
||||
function sanitizeUnimportantGulpOutput(result: string): string {
|
||||
return result.replace(/^.*(\] (Starting)|(Finished)).*$/gm, "") // "gulp" task start/end messages (nondeterministic order)
|
||||
.replace(/^.*(\] . (finished)|(started)).*$/gm, "") // "just" task start/end messages (nondeterministic order)
|
||||
.replace(/^.*\] Respawned to PID: \d+.*$/gm, "") // PID of child is OS and system-load dependent (likely stableish in a container but still dangerous)
|
||||
.replace(/\n+/g, "\n");
|
||||
}
|
||||
|
||||
function sanitizeTimestamps(result: string): string {
|
||||
return result.replace(/\[\d?\d:\d\d:\d\d (A|P)M\]/g, "[XX:XX:XX XM]")
|
||||
.replace(/\[\d?\d:\d\d:\d\d\]/g, "[XX:XX:XX]")
|
||||
.replace(/\/\d+-\d+-[\d_TZ]+-debug.log/g, "\/XXXX-XX-XXXXXXXXX-debug.log")
|
||||
.replace(/\d+\/\d+\/\d+ \d+:\d+:\d+ (AM|PM)/g, "XX/XX/XX XX:XX:XX XM")
|
||||
.replace(/\d+(\.\d+)? sec(onds?)?/g, "? seconds")
|
||||
.replace(/\d+(\.\d+)? min(utes?)?/g, "")
|
||||
.replace(/\d+(\.\d+)? ?m?s/g, "?s")
|
||||
.replace(/ \(\?s\)/g, "");
|
||||
}
|
||||
|
||||
function sanitizeVersionSpecifiers(result: string): string {
|
||||
return result
|
||||
.replace(/\d+.\d+.\d+-insiders.\d\d\d\d\d\d\d\d/g, "X.X.X-insiders.xxxxxxxx")
|
||||
.replace(/Rush Multi-Project Build Tool (\d+)\.\d+\.\d+/g, "Rush Multi-Project Build Tool $1.X.X")
|
||||
.replace(/([@v\()])\d+\.\d+\.\d+/g, "$1X.X.X")
|
||||
.replace(/webpack (\d+)\.\d+\.\d+/g, "webpack $1.X.X")
|
||||
.replace(/Webpack version: (\d+)\.\d+\.\d+/g, "Webpack version: $1.X.X");
|
||||
}
|
||||
|
||||
/**
|
||||
* Import types and some other error messages use absolute paths in errors as they have no context to be written relative to;
|
||||
* This is problematic for error baselines, so we grep for them and strip them out.
|
||||
*/
|
||||
function stripAbsoluteImportPaths(result: string) {
|
||||
const workspaceRegexp = new RegExp(IO.getWorkspaceRoot().replace(/\\/g, "\\\\"), "g");
|
||||
return result
|
||||
.replace(/import\(".*?\/tests\/cases\/user\//g, `import("/`)
|
||||
.replace(/Module '".*?\/tests\/cases\/user\//g, `Module '"/`)
|
||||
.replace(workspaceRegexp, "../../..");
|
||||
}
|
||||
|
||||
function sortErrors(result: string) {
|
||||
return ts.flatten(splitBy(result.split("\n"), s => /^\S+/.test(s)).sort(compareErrorStrings)).join("\n");
|
||||
}
|
||||
|
||||
const errorRegexp = /^(.+\.[tj]sx?)\((\d+),(\d+)\)(: error TS.*)/;
|
||||
function compareErrorStrings(a: string[], b: string[]) {
|
||||
ts.Debug.assertGreaterThanOrEqual(a.length, 1);
|
||||
ts.Debug.assertGreaterThanOrEqual(b.length, 1);
|
||||
const matchA = a[0].match(errorRegexp);
|
||||
if (!matchA) {
|
||||
return -1;
|
||||
}
|
||||
const [, errorFile, lineNumberString] = match;
|
||||
const lines = fs.readFileSync(path.join(cwd, errorFile), { encoding: "utf8" }).split("\n");
|
||||
const lineNumber = parseInt(lineNumberString) - 1;
|
||||
ts.Debug.assertGreaterThanOrEqual(lineNumber, 0);
|
||||
ts.Debug.assertLessThan(lineNumber, lines.length);
|
||||
const previousLine = lineNumber - 1 > 0 ? lines[lineNumber - 1] : "";
|
||||
return !ts.stringContains(lines[lineNumber], "$ExpectError") && !ts.stringContains(previousLine, "$ExpectError");
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Split an array into multiple arrays whenever `isStart` returns true.
|
||||
* @example
|
||||
* splitBy([1,2,3,4,5,6], isOdd)
|
||||
* ==> [[1, 2], [3, 4], [5, 6]]
|
||||
* where
|
||||
* const isOdd = n => !!(n % 2)
|
||||
*/
|
||||
function splitBy<T>(xs: T[], isStart: (x: T) => boolean): T[][] {
|
||||
const result = [];
|
||||
let group: T[] = [];
|
||||
for (const x of xs) {
|
||||
if (isStart(x)) {
|
||||
if (group.length) {
|
||||
result.push(group);
|
||||
const matchB = b[0].match(errorRegexp);
|
||||
if (!matchB) {
|
||||
return 1;
|
||||
}
|
||||
const [, errorFileA, lineNumberStringA, columnNumberStringA, remainderA] = matchA;
|
||||
const [, errorFileB, lineNumberStringB, columnNumberStringB, remainderB] = matchB;
|
||||
return ts.comparePathsCaseSensitive(errorFileA, errorFileB) ||
|
||||
ts.compareValues(parseInt(lineNumberStringA), parseInt(lineNumberStringB)) ||
|
||||
ts.compareValues(parseInt(columnNumberStringA), parseInt(columnNumberStringB)) ||
|
||||
ts.compareStringsCaseSensitive(remainderA, remainderB) ||
|
||||
ts.compareStringsCaseSensitive(a.slice(1).join("\n"), b.slice(1).join("\n"));
|
||||
}
|
||||
|
||||
export class DefinitelyTypedRunner extends ExternalCompileRunnerBase {
|
||||
readonly testDir = "../DefinitelyTyped/types/";
|
||||
workingDirectory = this.testDir;
|
||||
kind(): TestRunnerKind {
|
||||
return "dt";
|
||||
}
|
||||
report(result: ExecResult, cwd: string) {
|
||||
const stdout = removeExpectedErrors(result.stdout.toString(), cwd);
|
||||
const stderr = result.stderr.toString();
|
||||
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
return !stdout.length && !stderr.length ? null : `Exit Code: ${result.status}
|
||||
Standard output:
|
||||
${stdout.replace(/\r\n/g, "\n")}
|
||||
|
||||
|
||||
Standard error:
|
||||
${stderr.replace(/\r\n/g, "\n")}`;
|
||||
}
|
||||
}
|
||||
|
||||
function removeExpectedErrors(errors: string, cwd: string): string {
|
||||
return ts.flatten(splitBy(errors.split("\n"), s => /^\S+/.test(s)).filter(isUnexpectedError(cwd))).join("\n");
|
||||
}
|
||||
/**
|
||||
* Returns true if the line that caused the error contains '$ExpectError',
|
||||
* or if the line before that one contains '$ExpectError'.
|
||||
* '$ExpectError' is a marker used in Definitely Typed tests,
|
||||
* meaning that the error should not contribute toward our error baslines.
|
||||
*/
|
||||
function isUnexpectedError(cwd: string) {
|
||||
return (error: string[]) => {
|
||||
ts.Debug.assertGreaterThanOrEqual(error.length, 1);
|
||||
const match = error[0].match(/(.+\.tsx?)\((\d+),\d+\): error TS/);
|
||||
if (!match) {
|
||||
return true;
|
||||
}
|
||||
group = [x];
|
||||
}
|
||||
else {
|
||||
group.push(x);
|
||||
}
|
||||
const [, errorFile, lineNumberString] = match;
|
||||
const lines = fs.readFileSync(path.join(cwd, errorFile), { encoding: "utf8" }).split("\n");
|
||||
const lineNumber = parseInt(lineNumberString) - 1;
|
||||
ts.Debug.assertGreaterThanOrEqual(lineNumber, 0);
|
||||
ts.Debug.assertLessThan(lineNumber, lines.length);
|
||||
const previousLine = lineNumber - 1 > 0 ? lines[lineNumber - 1] : "";
|
||||
return !ts.stringContains(lines[lineNumber], "$ExpectError") && !ts.stringContains(previousLine, "$ExpectError");
|
||||
};
|
||||
}
|
||||
if (group.length) {
|
||||
result.push(group);
|
||||
/**
|
||||
* Split an array into multiple arrays whenever `isStart` returns true.
|
||||
* @example
|
||||
* splitBy([1,2,3,4,5,6], isOdd)
|
||||
* ==> [[1, 2], [3, 4], [5, 6]]
|
||||
* where
|
||||
* const isOdd = n => !!(n % 2)
|
||||
*/
|
||||
function splitBy<T>(xs: T[], isStart: (x: T) => boolean): T[][] {
|
||||
const result = [];
|
||||
let group: T[] = [];
|
||||
for (const x of xs) {
|
||||
if (isStart(x)) {
|
||||
if (group.length) {
|
||||
result.push(group);
|
||||
}
|
||||
group = [x];
|
||||
}
|
||||
else {
|
||||
group.push(x);
|
||||
}
|
||||
}
|
||||
if (group.length) {
|
||||
result.push(group);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
2
src/testRunner/fakesRef.ts
Normal file
2
src/testRunner/fakesRef.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// empty ref to fakes so it can be referenced by unittests
|
||||
namespace fakes {}
|
||||
2
src/testRunner/fourslashRef.ts
Normal file
2
src/testRunner/fourslashRef.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// empty ref to FourSlash so it can be referenced by unittests
|
||||
namespace FourSlash {}
|
||||
@ -1,70 +1,72 @@
|
||||
class FourSlashRunner extends RunnerBase {
|
||||
protected basePath: string;
|
||||
protected testSuiteName: TestRunnerKind;
|
||||
namespace Harness {
|
||||
export class FourSlashRunner extends RunnerBase {
|
||||
protected basePath: string;
|
||||
protected testSuiteName: TestRunnerKind;
|
||||
|
||||
constructor(private testType: FourSlash.FourSlashTestType) {
|
||||
super();
|
||||
switch (testType) {
|
||||
case FourSlash.FourSlashTestType.Native:
|
||||
this.basePath = "tests/cases/fourslash";
|
||||
this.testSuiteName = "fourslash";
|
||||
break;
|
||||
case FourSlash.FourSlashTestType.Shims:
|
||||
this.basePath = "tests/cases/fourslash/shims";
|
||||
this.testSuiteName = "fourslash-shims";
|
||||
break;
|
||||
case FourSlash.FourSlashTestType.ShimsWithPreprocess:
|
||||
this.basePath = "tests/cases/fourslash/shims-pp";
|
||||
this.testSuiteName = "fourslash-shims-pp";
|
||||
break;
|
||||
case FourSlash.FourSlashTestType.Server:
|
||||
this.basePath = "tests/cases/fourslash/server";
|
||||
this.testSuiteName = "fourslash-server";
|
||||
break;
|
||||
default:
|
||||
throw ts.Debug.assertNever(testType);
|
||||
}
|
||||
}
|
||||
|
||||
public enumerateTestFiles() {
|
||||
// see also: `enumerateTestFiles` in tests/webTestServer.ts
|
||||
return this.enumerateFiles(this.basePath, /\.ts/i, { recursive: false });
|
||||
}
|
||||
|
||||
public kind() {
|
||||
return this.testSuiteName;
|
||||
}
|
||||
|
||||
public initializeTests() {
|
||||
if (this.tests.length === 0) {
|
||||
this.tests = Harness.IO.enumerateTestFiles(this);
|
||||
constructor(private testType: FourSlash.FourSlashTestType) {
|
||||
super();
|
||||
switch (testType) {
|
||||
case FourSlash.FourSlashTestType.Native:
|
||||
this.basePath = "tests/cases/fourslash";
|
||||
this.testSuiteName = "fourslash";
|
||||
break;
|
||||
case FourSlash.FourSlashTestType.Shims:
|
||||
this.basePath = "tests/cases/fourslash/shims";
|
||||
this.testSuiteName = "fourslash-shims";
|
||||
break;
|
||||
case FourSlash.FourSlashTestType.ShimsWithPreprocess:
|
||||
this.basePath = "tests/cases/fourslash/shims-pp";
|
||||
this.testSuiteName = "fourslash-shims-pp";
|
||||
break;
|
||||
case FourSlash.FourSlashTestType.Server:
|
||||
this.basePath = "tests/cases/fourslash/server";
|
||||
this.testSuiteName = "fourslash-server";
|
||||
break;
|
||||
default:
|
||||
throw ts.Debug.assertNever(testType);
|
||||
}
|
||||
}
|
||||
|
||||
describe(this.testSuiteName + " tests", () => {
|
||||
this.tests.forEach(test => {
|
||||
const file = typeof test === "string" ? test : test.file;
|
||||
describe(file, () => {
|
||||
let fn = ts.normalizeSlashes(file);
|
||||
const justName = fn.replace(/^.*[\\\/]/, "");
|
||||
public enumerateTestFiles() {
|
||||
// see also: `enumerateTestFiles` in tests/webTestServer.ts
|
||||
return this.enumerateFiles(this.basePath, /\.ts/i, { recursive: false });
|
||||
}
|
||||
|
||||
// Convert to relative path
|
||||
const testIndex = fn.indexOf("tests/");
|
||||
if (testIndex >= 0) fn = fn.substr(testIndex);
|
||||
public kind() {
|
||||
return this.testSuiteName;
|
||||
}
|
||||
|
||||
if (justName && !justName.match(/fourslash\.ts$/i) && !justName.match(/\.d\.ts$/i)) {
|
||||
it(this.testSuiteName + " test " + justName + " runs correctly", () => {
|
||||
FourSlash.runFourSlashTest(this.basePath, this.testType, fn);
|
||||
});
|
||||
}
|
||||
public initializeTests() {
|
||||
if (this.tests.length === 0) {
|
||||
this.tests = IO.enumerateTestFiles(this);
|
||||
}
|
||||
|
||||
describe(this.testSuiteName + " tests", () => {
|
||||
this.tests.forEach(test => {
|
||||
const file = typeof test === "string" ? test : test.file;
|
||||
describe(file, () => {
|
||||
let fn = ts.normalizeSlashes(file);
|
||||
const justName = fn.replace(/^.*[\\\/]/, "");
|
||||
|
||||
// Convert to relative path
|
||||
const testIndex = fn.indexOf("tests/");
|
||||
if (testIndex >= 0) fn = fn.substr(testIndex);
|
||||
|
||||
if (justName && !justName.match(/fourslash\.ts$/i) && !justName.match(/\.d\.ts$/i)) {
|
||||
it(this.testSuiteName + " test " + justName + " runs correctly", () => {
|
||||
FourSlash.runFourSlashTest(this.basePath, this.testType, fn);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GeneratedFourslashRunner extends FourSlashRunner {
|
||||
constructor(testType: FourSlash.FourSlashTestType) {
|
||||
super(testType);
|
||||
this.basePath += "/generated/";
|
||||
export class GeneratedFourslashRunner extends FourSlashRunner {
|
||||
constructor(testType: FourSlash.FourSlashTestType) {
|
||||
super(testType);
|
||||
this.basePath += "/generated/";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,7 +240,7 @@ namespace Harness.Parallel.Host {
|
||||
let totalPassing = 0;
|
||||
const startDate = new Date();
|
||||
|
||||
const progressBars = new ProgressBars({ noColors });
|
||||
const progressBars = new ProgressBars({ noColors: Harness.noColors }); // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
const progressUpdateInterval = 1 / progressBars._options.width;
|
||||
let nextProgress = progressUpdateInterval;
|
||||
|
||||
@ -250,7 +250,7 @@ namespace Harness.Parallel.Host {
|
||||
let closedWorkers = 0;
|
||||
for (let i = 0; i < workerCount; i++) {
|
||||
// TODO: Just send the config over the IPC channel or in the command line arguments
|
||||
const config: TestConfig = { light: lightMode, listenForWork: true, runUnitTests, stackTraceLimit, timeout: globalTimeout };
|
||||
const config: TestConfig = { light: lightMode, listenForWork: true, runUnitTests: Harness.runUnitTests, stackTraceLimit: Harness.stackTraceLimit, timeout: globalTimeout }; // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
const configPath = ts.combinePaths(taskConfigsFolder, `task-config${i}.json`);
|
||||
IO.writeFile(configPath, JSON.stringify(config));
|
||||
const worker: Worker = {
|
||||
@ -549,7 +549,7 @@ namespace Harness.Parallel.Host {
|
||||
failedTestReporter = new FailedTestReporter(replayRunner, {
|
||||
reporterOptions: {
|
||||
file: path.resolve(".failed-tests"),
|
||||
keepFailed
|
||||
keepFailed: Harness.keepFailed // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
2
src/testRunner/playbackRef.ts
Normal file
2
src/testRunner/playbackRef.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// empty ref to Playback so it can be referenced by unittests
|
||||
namespace Playback {}
|
||||
@ -30,16 +30,16 @@ namespace project {
|
||||
outputFiles?: readonly documents.TextDocument[];
|
||||
}
|
||||
|
||||
export class ProjectRunner extends RunnerBase {
|
||||
export class ProjectRunner extends Harness.RunnerBase {
|
||||
public enumerateTestFiles() {
|
||||
const all = this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true });
|
||||
if (shards === 1) {
|
||||
if (Harness.shards === 1) {
|
||||
return all;
|
||||
}
|
||||
return all.filter((_val, idx) => idx % shards === (shardId - 1));
|
||||
return all.filter((_val, idx) => idx % Harness.shards === (Harness.shardId - 1));
|
||||
}
|
||||
|
||||
public kind(): TestRunnerKind {
|
||||
public kind(): Harness.TestRunnerKind {
|
||||
return "project";
|
||||
}
|
||||
|
||||
@ -207,13 +207,13 @@ namespace project {
|
||||
const resolutionInfo: ProjectRunnerTestCaseResolutionInfo & ts.CompilerOptions = JSON.parse(JSON.stringify(this.testCase));
|
||||
resolutionInfo.resolvedInputFiles = this.compilerResult.program!.getSourceFiles()
|
||||
.map(({ fileName: input }) =>
|
||||
vpath.beneath(vfs.builtFolder, input, this.vfs.ignoreCase) || vpath.beneath(vfs.testLibFolder, input, this.vfs.ignoreCase) ? utils.removeTestPathPrefixes(input) :
|
||||
vpath.beneath(vfs.builtFolder, input, this.vfs.ignoreCase) || vpath.beneath(vfs.testLibFolder, input, this.vfs.ignoreCase) ? Utils.removeTestPathPrefixes(input) :
|
||||
vpath.isAbsolute(input) ? vpath.relative(cwd, input, ignoreCase) :
|
||||
input);
|
||||
|
||||
resolutionInfo.emittedFiles = this.compilerResult.outputFiles!
|
||||
.map(output => output.meta.get("fileName") || output.file)
|
||||
.map(output => utils.removeTestPathPrefixes(vpath.isAbsolute(output) ? vpath.relative(cwd, output, ignoreCase) : output));
|
||||
.map(output => Utils.removeTestPathPrefixes(vpath.isAbsolute(output) ? vpath.relative(cwd, output, ignoreCase) : output));
|
||||
|
||||
const content = JSON.stringify(resolutionInfo, undefined, " ");
|
||||
Harness.Baseline.runBaseline(this.getBaselineFolder(this.compilerResult.moduleKind) + this.testCaseJustName + ".json", content);
|
||||
@ -246,7 +246,7 @@ namespace project {
|
||||
nonSubfolderDiskFiles++;
|
||||
}
|
||||
|
||||
const content = utils.removeTestPathPrefixes(output.text, /*retainTrailingDirectorySeparator*/ true);
|
||||
const content = Utils.removeTestPathPrefixes(output.text, /*retainTrailingDirectorySeparator*/ true);
|
||||
Harness.Baseline.runBaseline(this.getBaselineFolder(this.compilerResult.moduleKind) + diskRelativeName, content as string | null); // TODO: GH#18217
|
||||
}
|
||||
catch (e) {
|
||||
@ -412,7 +412,7 @@ namespace project {
|
||||
|
||||
const inputFiles = inputSourceFiles.map<Harness.Compiler.TestFile>(sourceFile => ({
|
||||
unitName: ts.isRootedDiskPath(sourceFile.fileName) ?
|
||||
RunnerBase.removeFullPaths(sourceFile.fileName) :
|
||||
Harness.RunnerBase.removeFullPaths(sourceFile.fileName) :
|
||||
sourceFile.fileName,
|
||||
content: sourceFile.text
|
||||
}));
|
||||
|
||||
@ -1,257 +1,259 @@
|
||||
/* eslint-disable prefer-const */
|
||||
let runners: RunnerBase[] = [];
|
||||
let iterations = 1;
|
||||
/* eslint-enable prefer-const */
|
||||
namespace Harness {
|
||||
/* eslint-disable prefer-const */
|
||||
export let runners: RunnerBase[] = [];
|
||||
export let iterations = 1;
|
||||
/* eslint-enable prefer-const */
|
||||
|
||||
function runTests(runners: RunnerBase[]) {
|
||||
for (let i = iterations; i > 0; i--) {
|
||||
for (const runner of runners) {
|
||||
runner.initializeTests();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tryGetConfig(args: string[]) {
|
||||
const prefix = "--config=";
|
||||
const configPath = ts.forEach(args, arg => arg.lastIndexOf(prefix, 0) === 0 && arg.substr(prefix.length));
|
||||
// strip leading and trailing quotes from the path (necessary on Windows since shell does not do it automatically)
|
||||
return configPath && configPath.replace(/(^[\"'])|([\"']$)/g, "");
|
||||
}
|
||||
|
||||
function createRunner(kind: TestRunnerKind): RunnerBase {
|
||||
switch (kind) {
|
||||
case "conformance":
|
||||
return new CompilerBaselineRunner(CompilerTestType.Conformance);
|
||||
case "compiler":
|
||||
return new CompilerBaselineRunner(CompilerTestType.Regressions);
|
||||
case "fourslash":
|
||||
return new FourSlashRunner(FourSlash.FourSlashTestType.Native);
|
||||
case "fourslash-shims":
|
||||
return new FourSlashRunner(FourSlash.FourSlashTestType.Shims);
|
||||
case "fourslash-shims-pp":
|
||||
return new FourSlashRunner(FourSlash.FourSlashTestType.ShimsWithPreprocess);
|
||||
case "fourslash-server":
|
||||
return new FourSlashRunner(FourSlash.FourSlashTestType.Server);
|
||||
case "project":
|
||||
return new project.ProjectRunner();
|
||||
case "rwc":
|
||||
return new RWCRunner();
|
||||
case "test262":
|
||||
return new Test262BaselineRunner();
|
||||
case "user":
|
||||
return new UserCodeRunner();
|
||||
case "dt":
|
||||
return new DefinitelyTypedRunner();
|
||||
case "docker":
|
||||
return new DockerfileRunner();
|
||||
}
|
||||
return ts.Debug.fail(`Unknown runner kind ${kind}`);
|
||||
}
|
||||
|
||||
// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options
|
||||
|
||||
const mytestconfigFileName = "mytest.config";
|
||||
const testconfigFileName = "test.config";
|
||||
|
||||
const customConfig = tryGetConfig(Harness.IO.args());
|
||||
const testConfigContent =
|
||||
customConfig && Harness.IO.fileExists(customConfig)
|
||||
? Harness.IO.readFile(customConfig)!
|
||||
: Harness.IO.fileExists(mytestconfigFileName)
|
||||
? Harness.IO.readFile(mytestconfigFileName)!
|
||||
: Harness.IO.fileExists(testconfigFileName) ? Harness.IO.readFile(testconfigFileName)! : "";
|
||||
|
||||
let taskConfigsFolder: string;
|
||||
let workerCount: number;
|
||||
let runUnitTests: boolean | undefined;
|
||||
let stackTraceLimit: number | "full" | undefined;
|
||||
let noColors = false;
|
||||
let keepFailed = false;
|
||||
|
||||
interface TestConfig {
|
||||
light?: boolean;
|
||||
taskConfigsFolder?: string;
|
||||
listenForWork?: boolean;
|
||||
workerCount?: number;
|
||||
stackTraceLimit?: number | "full";
|
||||
test?: string[];
|
||||
runners?: string[];
|
||||
runUnitTests?: boolean;
|
||||
noColors?: boolean;
|
||||
timeout?: number;
|
||||
keepFailed?: boolean;
|
||||
shardId?: number;
|
||||
shards?: number;
|
||||
}
|
||||
|
||||
interface TaskSet {
|
||||
runner: TestRunnerKind;
|
||||
files: string[];
|
||||
}
|
||||
|
||||
let configOption: string;
|
||||
let globalTimeout: number;
|
||||
function handleTestConfig() {
|
||||
if (testConfigContent !== "") {
|
||||
const testConfig = <TestConfig>JSON.parse(testConfigContent);
|
||||
if (testConfig.light) {
|
||||
Harness.lightMode = true;
|
||||
}
|
||||
if (testConfig.timeout) {
|
||||
globalTimeout = testConfig.timeout;
|
||||
}
|
||||
runUnitTests = testConfig.runUnitTests;
|
||||
if (testConfig.workerCount) {
|
||||
workerCount = +testConfig.workerCount;
|
||||
}
|
||||
if (testConfig.taskConfigsFolder) {
|
||||
taskConfigsFolder = testConfig.taskConfigsFolder;
|
||||
}
|
||||
if (testConfig.noColors !== undefined) {
|
||||
noColors = testConfig.noColors;
|
||||
}
|
||||
if (testConfig.keepFailed) {
|
||||
keepFailed = true;
|
||||
}
|
||||
if (testConfig.shardId) {
|
||||
shardId = testConfig.shardId;
|
||||
}
|
||||
if (testConfig.shards) {
|
||||
shards = testConfig.shards;
|
||||
}
|
||||
|
||||
if (testConfig.stackTraceLimit === "full") {
|
||||
(<any>Error).stackTraceLimit = Infinity;
|
||||
stackTraceLimit = testConfig.stackTraceLimit;
|
||||
}
|
||||
else if ((+testConfig.stackTraceLimit! | 0) > 0) {
|
||||
(<any>Error).stackTraceLimit = +testConfig.stackTraceLimit! | 0;
|
||||
stackTraceLimit = +testConfig.stackTraceLimit! | 0;
|
||||
}
|
||||
if (testConfig.listenForWork) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const runnerConfig = testConfig.runners || testConfig.test;
|
||||
if (runnerConfig && runnerConfig.length > 0) {
|
||||
if (testConfig.runners) {
|
||||
runUnitTests = runnerConfig.indexOf("unittest") !== -1;
|
||||
}
|
||||
for (const option of runnerConfig) {
|
||||
if (!option) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!configOption) {
|
||||
configOption = option;
|
||||
}
|
||||
else {
|
||||
configOption += "+" + option;
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case "compiler":
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
|
||||
break;
|
||||
case "conformance":
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
break;
|
||||
case "project":
|
||||
runners.push(new project.ProjectRunner());
|
||||
break;
|
||||
case "fourslash":
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Native));
|
||||
break;
|
||||
case "fourslash-shims":
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Shims));
|
||||
break;
|
||||
case "fourslash-shims-pp":
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.ShimsWithPreprocess));
|
||||
break;
|
||||
case "fourslash-server":
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Server));
|
||||
break;
|
||||
case "fourslash-generated":
|
||||
runners.push(new GeneratedFourslashRunner(FourSlash.FourSlashTestType.Native));
|
||||
break;
|
||||
case "rwc":
|
||||
runners.push(new RWCRunner());
|
||||
break;
|
||||
case "test262":
|
||||
runners.push(new Test262BaselineRunner());
|
||||
break;
|
||||
case "user":
|
||||
runners.push(new UserCodeRunner());
|
||||
break;
|
||||
case "dt":
|
||||
runners.push(new DefinitelyTypedRunner());
|
||||
break;
|
||||
case "docker":
|
||||
runners.push(new DockerfileRunner());
|
||||
break;
|
||||
}
|
||||
function runTests(runners: RunnerBase[]) {
|
||||
for (let i = iterations; i > 0; i--) {
|
||||
for (const runner of runners) {
|
||||
runner.initializeTests();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (runners.length === 0) {
|
||||
// compiler
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
|
||||
function tryGetConfig(args: string[]) {
|
||||
const prefix = "--config=";
|
||||
const configPath = ts.forEach(args, arg => arg.lastIndexOf(prefix, 0) === 0 && arg.substr(prefix.length));
|
||||
// strip leading and trailing quotes from the path (necessary on Windows since shell does not do it automatically)
|
||||
return configPath && configPath.replace(/(^[\"'])|([\"']$)/g, "");
|
||||
}
|
||||
|
||||
runners.push(new project.ProjectRunner());
|
||||
export function createRunner(kind: TestRunnerKind): RunnerBase {
|
||||
switch (kind) {
|
||||
case "conformance":
|
||||
return new CompilerBaselineRunner(CompilerTestType.Conformance);
|
||||
case "compiler":
|
||||
return new CompilerBaselineRunner(CompilerTestType.Regressions);
|
||||
case "fourslash":
|
||||
return new FourSlashRunner(FourSlash.FourSlashTestType.Native);
|
||||
case "fourslash-shims":
|
||||
return new FourSlashRunner(FourSlash.FourSlashTestType.Shims);
|
||||
case "fourslash-shims-pp":
|
||||
return new FourSlashRunner(FourSlash.FourSlashTestType.ShimsWithPreprocess);
|
||||
case "fourslash-server":
|
||||
return new FourSlashRunner(FourSlash.FourSlashTestType.Server);
|
||||
case "project":
|
||||
return new project.ProjectRunner();
|
||||
case "rwc":
|
||||
return new RWC.RWCRunner();
|
||||
case "test262":
|
||||
return new Test262BaselineRunner();
|
||||
case "user":
|
||||
return new UserCodeRunner();
|
||||
case "dt":
|
||||
return new DefinitelyTypedRunner();
|
||||
case "docker":
|
||||
return new DockerfileRunner();
|
||||
}
|
||||
return ts.Debug.fail(`Unknown runner kind ${kind}`);
|
||||
}
|
||||
|
||||
// language services
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Native));
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Shims));
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.ShimsWithPreprocess));
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Server));
|
||||
// runners.push(new GeneratedFourslashRunner());
|
||||
// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options
|
||||
|
||||
// CRON-only tests
|
||||
if (process.env.TRAVIS_EVENT_TYPE === "cron") {
|
||||
runners.push(new UserCodeRunner());
|
||||
runners.push(new DockerfileRunner());
|
||||
const mytestconfigFileName = "mytest.config";
|
||||
const testconfigFileName = "test.config";
|
||||
|
||||
const customConfig = tryGetConfig(IO.args());
|
||||
const testConfigContent =
|
||||
customConfig && IO.fileExists(customConfig)
|
||||
? IO.readFile(customConfig)!
|
||||
: IO.fileExists(mytestconfigFileName)
|
||||
? IO.readFile(mytestconfigFileName)!
|
||||
: IO.fileExists(testconfigFileName) ? IO.readFile(testconfigFileName)! : "";
|
||||
|
||||
export let taskConfigsFolder: string;
|
||||
export let workerCount: number;
|
||||
export let runUnitTests: boolean | undefined;
|
||||
export let stackTraceLimit: number | "full" | undefined;
|
||||
export let noColors = false;
|
||||
export let keepFailed = false;
|
||||
|
||||
export interface TestConfig {
|
||||
light?: boolean;
|
||||
taskConfigsFolder?: string;
|
||||
listenForWork?: boolean;
|
||||
workerCount?: number;
|
||||
stackTraceLimit?: number | "full";
|
||||
test?: string[];
|
||||
runners?: string[];
|
||||
runUnitTests?: boolean;
|
||||
noColors?: boolean;
|
||||
timeout?: number;
|
||||
keepFailed?: boolean;
|
||||
shardId?: number;
|
||||
shards?: number;
|
||||
}
|
||||
|
||||
export interface TaskSet {
|
||||
runner: TestRunnerKind;
|
||||
files: string[];
|
||||
}
|
||||
|
||||
export let configOption: string;
|
||||
export let globalTimeout: number;
|
||||
function handleTestConfig() {
|
||||
if (testConfigContent !== "") {
|
||||
const testConfig = <TestConfig>JSON.parse(testConfigContent);
|
||||
if (testConfig.light) {
|
||||
setLightMode(true);
|
||||
}
|
||||
if (testConfig.timeout) {
|
||||
globalTimeout = testConfig.timeout;
|
||||
}
|
||||
runUnitTests = testConfig.runUnitTests;
|
||||
if (testConfig.workerCount) {
|
||||
workerCount = +testConfig.workerCount;
|
||||
}
|
||||
if (testConfig.taskConfigsFolder) {
|
||||
taskConfigsFolder = testConfig.taskConfigsFolder;
|
||||
}
|
||||
if (testConfig.noColors !== undefined) {
|
||||
noColors = testConfig.noColors;
|
||||
}
|
||||
if (testConfig.keepFailed) {
|
||||
keepFailed = true;
|
||||
}
|
||||
if (testConfig.shardId) {
|
||||
setShardId(testConfig.shardId);
|
||||
}
|
||||
if (testConfig.shards) {
|
||||
setShards(testConfig.shards);
|
||||
}
|
||||
|
||||
if (testConfig.stackTraceLimit === "full") {
|
||||
(<any>Error).stackTraceLimit = Infinity;
|
||||
stackTraceLimit = testConfig.stackTraceLimit;
|
||||
}
|
||||
else if ((+testConfig.stackTraceLimit! | 0) > 0) {
|
||||
(<any>Error).stackTraceLimit = +testConfig.stackTraceLimit! | 0;
|
||||
stackTraceLimit = +testConfig.stackTraceLimit! | 0;
|
||||
}
|
||||
if (testConfig.listenForWork) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const runnerConfig = testConfig.runners || testConfig.test;
|
||||
if (runnerConfig && runnerConfig.length > 0) {
|
||||
if (testConfig.runners) {
|
||||
runUnitTests = runnerConfig.indexOf("unittest") !== -1;
|
||||
}
|
||||
for (const option of runnerConfig) {
|
||||
if (!option) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!configOption) {
|
||||
configOption = option;
|
||||
}
|
||||
else {
|
||||
configOption += "+" + option;
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case "compiler":
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
|
||||
break;
|
||||
case "conformance":
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
break;
|
||||
case "project":
|
||||
runners.push(new project.ProjectRunner());
|
||||
break;
|
||||
case "fourslash":
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Native));
|
||||
break;
|
||||
case "fourslash-shims":
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Shims));
|
||||
break;
|
||||
case "fourslash-shims-pp":
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.ShimsWithPreprocess));
|
||||
break;
|
||||
case "fourslash-server":
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Server));
|
||||
break;
|
||||
case "fourslash-generated":
|
||||
runners.push(new GeneratedFourslashRunner(FourSlash.FourSlashTestType.Native));
|
||||
break;
|
||||
case "rwc":
|
||||
runners.push(new RWC.RWCRunner());
|
||||
break;
|
||||
case "test262":
|
||||
runners.push(new Test262BaselineRunner());
|
||||
break;
|
||||
case "user":
|
||||
runners.push(new UserCodeRunner());
|
||||
break;
|
||||
case "dt":
|
||||
runners.push(new DefinitelyTypedRunner());
|
||||
break;
|
||||
case "docker":
|
||||
runners.push(new DockerfileRunner());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (runners.length === 0) {
|
||||
// compiler
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Conformance));
|
||||
runners.push(new CompilerBaselineRunner(CompilerTestType.Regressions));
|
||||
|
||||
runners.push(new project.ProjectRunner());
|
||||
|
||||
// language services
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Native));
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Shims));
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.ShimsWithPreprocess));
|
||||
runners.push(new FourSlashRunner(FourSlash.FourSlashTestType.Server));
|
||||
// runners.push(new GeneratedFourslashRunner());
|
||||
|
||||
// CRON-only tests
|
||||
if (process.env.TRAVIS_EVENT_TYPE === "cron") {
|
||||
runners.push(new UserCodeRunner());
|
||||
runners.push(new DockerfileRunner());
|
||||
}
|
||||
}
|
||||
if (runUnitTests === undefined) {
|
||||
runUnitTests = runners.length !== 1; // Don't run unit tests when running only one runner if unit tests were not explicitly asked for
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function beginTests() {
|
||||
if (ts.Debug.isDebugging) {
|
||||
ts.Debug.enableDebugInfo();
|
||||
}
|
||||
|
||||
// run tests in en-US by default.
|
||||
let savedUILocale: string | undefined;
|
||||
beforeEach(() => {
|
||||
savedUILocale = ts.getUILocale();
|
||||
ts.setUILocale("en-US");
|
||||
});
|
||||
afterEach(() => ts.setUILocale(savedUILocale));
|
||||
|
||||
runTests(runners);
|
||||
|
||||
if (!runUnitTests) {
|
||||
// patch `describe` to skip unit tests
|
||||
(global as any).describe = ts.noop;
|
||||
}
|
||||
}
|
||||
if (runUnitTests === undefined) {
|
||||
runUnitTests = runners.length !== 1; // Don't run unit tests when running only one runner if unit tests were not explicitly asked for
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function beginTests() {
|
||||
if (ts.Debug.isDebugging) {
|
||||
ts.Debug.enableDebugInfo();
|
||||
export let isWorker: boolean;
|
||||
function startTestEnvironment() {
|
||||
isWorker = handleTestConfig();
|
||||
if (isWorker) {
|
||||
return Parallel.Worker.start();
|
||||
}
|
||||
else if (taskConfigsFolder && workerCount && workerCount > 1) {
|
||||
return Parallel.Host.start();
|
||||
}
|
||||
beginTests();
|
||||
}
|
||||
|
||||
// run tests in en-US by default.
|
||||
let savedUILocale: string | undefined;
|
||||
beforeEach(() => {
|
||||
savedUILocale = ts.getUILocale();
|
||||
ts.setUILocale("en-US");
|
||||
});
|
||||
afterEach(() => ts.setUILocale(savedUILocale));
|
||||
|
||||
runTests(runners);
|
||||
|
||||
if (!runUnitTests) {
|
||||
// patch `describe` to skip unit tests
|
||||
(global as any).describe = ts.noop;
|
||||
}
|
||||
}
|
||||
|
||||
let isWorker: boolean;
|
||||
function startTestEnvironment() {
|
||||
isWorker = handleTestConfig();
|
||||
if (isWorker) {
|
||||
return Harness.Parallel.Worker.start();
|
||||
}
|
||||
else if (taskConfigsFolder && workerCount && workerCount > 1) {
|
||||
return Harness.Parallel.Host.start();
|
||||
}
|
||||
beginTests();
|
||||
}
|
||||
|
||||
startTestEnvironment();
|
||||
startTestEnvironment();
|
||||
}
|
||||
@ -1,18 +1,18 @@
|
||||
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
|
||||
namespace RWC {
|
||||
function runWithIOLog(ioLog: IoLog, fn: (oldIO: Harness.IO) => void) {
|
||||
function runWithIOLog(ioLog: Playback.IoLog, fn: (oldIO: Harness.IO) => void) {
|
||||
const oldIO = Harness.IO;
|
||||
|
||||
const wrappedIO = Playback.wrapIO(oldIO);
|
||||
wrappedIO.startReplayFromData(ioLog);
|
||||
Harness.IO = wrappedIO;
|
||||
Harness.setHarnessIO(wrappedIO);
|
||||
|
||||
try {
|
||||
fn(oldIO);
|
||||
}
|
||||
finally {
|
||||
wrappedIO.endReplay();
|
||||
Harness.IO = oldIO;
|
||||
Harness.setHarnessIO(oldIO);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ namespace RWC {
|
||||
this.timeout(800_000); // Allow long timeouts for RWC compilations
|
||||
let opts!: ts.ParsedCommandLine;
|
||||
|
||||
const ioLog: IoLog = Playback.newStyleLogIntoOldStyleLog(JSON.parse(Harness.IO.readFile(`internal/cases/rwc/${jsonPath}/test.json`)!), Harness.IO, `internal/cases/rwc/${baseName}`);
|
||||
const ioLog: Playback.IoLog = Playback.newStyleLogIntoOldStyleLog(JSON.parse(Harness.IO.readFile(`internal/cases/rwc/${jsonPath}/test.json`)!), Harness.IO, `internal/cases/rwc/${baseName}`);
|
||||
currentDirectory = ioLog.currentDirectory;
|
||||
useCustomLibraryFile = !!ioLog.useCustomLibraryFile;
|
||||
runWithIOLog(ioLog, () => {
|
||||
@ -207,29 +207,29 @@ namespace RWC {
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class RWCRunner extends RunnerBase {
|
||||
public enumerateTestFiles() {
|
||||
// see also: `enumerateTestFiles` in tests/webTestServer.ts
|
||||
return Harness.IO.getDirectories("internal/cases/rwc/");
|
||||
}
|
||||
export class RWCRunner extends Harness.RunnerBase {
|
||||
public enumerateTestFiles() {
|
||||
// see also: `enumerateTestFiles` in tests/webTestServer.ts
|
||||
return Harness.IO.getDirectories("internal/cases/rwc/");
|
||||
}
|
||||
|
||||
public kind(): TestRunnerKind {
|
||||
return "rwc";
|
||||
}
|
||||
public kind(): Harness.TestRunnerKind {
|
||||
return "rwc";
|
||||
}
|
||||
|
||||
/** Setup the runner's tests so that they are ready to be executed by the harness
|
||||
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
|
||||
*/
|
||||
public initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
for (const test of this.tests && this.tests.length ? this.tests : this.getTestFiles()) {
|
||||
this.runTest(typeof test === "string" ? test : test.file);
|
||||
/** Setup the runner's tests so that they are ready to be executed by the harness
|
||||
* The first test should be a describe/it block that sets up the harness's compiler instance appropriately
|
||||
*/
|
||||
public initializeTests(): void {
|
||||
// Read in and evaluate the test list
|
||||
for (const test of this.tests && this.tests.length ? this.tests : this.getTestFiles()) {
|
||||
this.runTest(typeof test === "string" ? test : test.file);
|
||||
}
|
||||
}
|
||||
|
||||
private runTest(jsonFileName: string) {
|
||||
runRWCTest(jsonFileName);
|
||||
}
|
||||
}
|
||||
|
||||
private runTest(jsonFileName: string) {
|
||||
RWC.runRWCTest(jsonFileName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,108 +1,110 @@
|
||||
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
|
||||
class Test262BaselineRunner extends RunnerBase {
|
||||
private static readonly basePath = "internal/cases/test262";
|
||||
private static readonly helpersFilePath = "tests/cases/test262-harness/helpers.d.ts";
|
||||
private static readonly helperFile: Harness.Compiler.TestFile = {
|
||||
unitName: Test262BaselineRunner.helpersFilePath,
|
||||
content: Harness.IO.readFile(Test262BaselineRunner.helpersFilePath)!,
|
||||
};
|
||||
private static readonly testFileExtensionRegex = /\.js$/;
|
||||
private static readonly options: ts.CompilerOptions = {
|
||||
allowNonTsExtensions: true,
|
||||
target: ts.ScriptTarget.Latest,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
};
|
||||
private static readonly baselineOptions: Harness.Baseline.BaselineOptions = {
|
||||
Subfolder: "test262",
|
||||
Baselinefolder: "internal/baselines"
|
||||
};
|
||||
namespace Harness {
|
||||
// In harness baselines, null is different than undefined. See `generateActual` in `harness.ts`.
|
||||
export class Test262BaselineRunner extends RunnerBase {
|
||||
private static readonly basePath = "internal/cases/test262";
|
||||
private static readonly helpersFilePath = "tests/cases/test262-harness/helpers.d.ts";
|
||||
private static readonly helperFile: Compiler.TestFile = {
|
||||
unitName: Test262BaselineRunner.helpersFilePath,
|
||||
content: IO.readFile(Test262BaselineRunner.helpersFilePath)!,
|
||||
};
|
||||
private static readonly testFileExtensionRegex = /\.js$/;
|
||||
private static readonly options: ts.CompilerOptions = {
|
||||
allowNonTsExtensions: true,
|
||||
target: ts.ScriptTarget.Latest,
|
||||
module: ts.ModuleKind.CommonJS
|
||||
};
|
||||
private static readonly baselineOptions: Baseline.BaselineOptions = {
|
||||
Subfolder: "test262",
|
||||
Baselinefolder: "internal/baselines"
|
||||
};
|
||||
|
||||
private static getTestFilePath(filename: string): string {
|
||||
return Test262BaselineRunner.basePath + "/" + filename;
|
||||
}
|
||||
private static getTestFilePath(filename: string): string {
|
||||
return Test262BaselineRunner.basePath + "/" + filename;
|
||||
}
|
||||
|
||||
private runTest(filePath: string) {
|
||||
describe("test262 test for " + filePath, () => {
|
||||
// Mocha holds onto the closure environment of the describe callback even after the test is done.
|
||||
// Everything declared here should be cleared out in the "after" callback.
|
||||
let testState: {
|
||||
filename: string;
|
||||
compilerResult: compiler.CompilationResult;
|
||||
inputFiles: Harness.Compiler.TestFile[];
|
||||
};
|
||||
|
||||
before(() => {
|
||||
const content = Harness.IO.readFile(filePath)!;
|
||||
const testFilename = ts.removeFileExtension(filePath).replace(/\//g, "_") + ".test";
|
||||
const testCaseContent = Harness.TestCaseParser.makeUnitsFromTest(content, testFilename);
|
||||
|
||||
const inputFiles: Harness.Compiler.TestFile[] = testCaseContent.testUnitData.map(unit => {
|
||||
const unitName = Test262BaselineRunner.getTestFilePath(unit.name);
|
||||
return { unitName, content: unit.content };
|
||||
});
|
||||
|
||||
// Emit the results
|
||||
testState = {
|
||||
filename: testFilename,
|
||||
inputFiles,
|
||||
compilerResult: undefined!, // TODO: GH#18217
|
||||
private runTest(filePath: string) {
|
||||
describe("test262 test for " + filePath, () => {
|
||||
// Mocha holds onto the closure environment of the describe callback even after the test is done.
|
||||
// Everything declared here should be cleared out in the "after" callback.
|
||||
let testState: {
|
||||
filename: string;
|
||||
compilerResult: compiler.CompilationResult;
|
||||
inputFiles: Compiler.TestFile[];
|
||||
};
|
||||
|
||||
testState.compilerResult = Harness.Compiler.compileFiles(
|
||||
[Test262BaselineRunner.helperFile].concat(inputFiles),
|
||||
/*otherFiles*/ [],
|
||||
/* harnessOptions */ undefined,
|
||||
Test262BaselineRunner.options,
|
||||
/* currentDirectory */ undefined);
|
||||
});
|
||||
before(() => {
|
||||
const content = IO.readFile(filePath)!;
|
||||
const testFilename = ts.removeFileExtension(filePath).replace(/\//g, "_") + ".test";
|
||||
const testCaseContent = TestCaseParser.makeUnitsFromTest(content, testFilename);
|
||||
|
||||
after(() => {
|
||||
testState = undefined!;
|
||||
});
|
||||
const inputFiles: Compiler.TestFile[] = testCaseContent.testUnitData.map(unit => {
|
||||
const unitName = Test262BaselineRunner.getTestFilePath(unit.name);
|
||||
return { unitName, content: unit.content };
|
||||
});
|
||||
|
||||
it("has the expected emitted code", () => {
|
||||
const files = Array.from(testState.compilerResult.js.values()).filter(f => f.file !== Test262BaselineRunner.helpersFilePath);
|
||||
Harness.Baseline.runBaseline(testState.filename + ".output.js", Harness.Compiler.collateOutputs(files), Test262BaselineRunner.baselineOptions);
|
||||
});
|
||||
// Emit the results
|
||||
testState = {
|
||||
filename: testFilename,
|
||||
inputFiles,
|
||||
compilerResult: undefined!, // TODO: GH#18217
|
||||
};
|
||||
|
||||
it("has the expected errors", () => {
|
||||
const errors = testState.compilerResult.diagnostics;
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const baseline = errors.length === 0 ? null : Harness.Compiler.getErrorBaseline(testState.inputFiles, errors);
|
||||
Harness.Baseline.runBaseline(testState.filename + ".errors.txt", baseline, Test262BaselineRunner.baselineOptions);
|
||||
});
|
||||
testState.compilerResult = Compiler.compileFiles(
|
||||
[Test262BaselineRunner.helperFile].concat(inputFiles),
|
||||
/*otherFiles*/ [],
|
||||
/* harnessOptions */ undefined,
|
||||
Test262BaselineRunner.options,
|
||||
/* currentDirectory */ undefined);
|
||||
});
|
||||
|
||||
it("satisfies invariants", () => {
|
||||
const sourceFile = testState.compilerResult.program!.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
|
||||
Utils.assertInvariants(sourceFile, /*parent:*/ undefined);
|
||||
});
|
||||
after(() => {
|
||||
testState = undefined!;
|
||||
});
|
||||
|
||||
it("has the expected AST", () => {
|
||||
const sourceFile = testState.compilerResult.program!.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename))!;
|
||||
Harness.Baseline.runBaseline(testState.filename + ".AST.txt", Utils.sourceFileToJSON(sourceFile), Test262BaselineRunner.baselineOptions);
|
||||
});
|
||||
});
|
||||
}
|
||||
it("has the expected emitted code", () => {
|
||||
const files = Array.from(testState.compilerResult.js.values()).filter(f => f.file !== Test262BaselineRunner.helpersFilePath);
|
||||
Baseline.runBaseline(testState.filename + ".output.js", Compiler.collateOutputs(files), Test262BaselineRunner.baselineOptions);
|
||||
});
|
||||
|
||||
public kind(): TestRunnerKind {
|
||||
return "test262";
|
||||
}
|
||||
it("has the expected errors", () => {
|
||||
const errors = testState.compilerResult.diagnostics;
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const baseline = errors.length === 0 ? null : Compiler.getErrorBaseline(testState.inputFiles, errors);
|
||||
Baseline.runBaseline(testState.filename + ".errors.txt", baseline, Test262BaselineRunner.baselineOptions);
|
||||
});
|
||||
|
||||
public enumerateTestFiles() {
|
||||
// see also: `enumerateTestFiles` in tests/webTestServer.ts
|
||||
return ts.map(this.enumerateFiles(Test262BaselineRunner.basePath, Test262BaselineRunner.testFileExtensionRegex, { recursive: true }), ts.normalizePath);
|
||||
}
|
||||
it("satisfies invariants", () => {
|
||||
const sourceFile = testState.compilerResult.program!.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename));
|
||||
Utils.assertInvariants(sourceFile, /*parent:*/ undefined);
|
||||
});
|
||||
|
||||
public initializeTests() {
|
||||
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
|
||||
if (this.tests.length === 0) {
|
||||
const testFiles = this.getTestFiles();
|
||||
testFiles.forEach(fn => {
|
||||
this.runTest(fn);
|
||||
it("has the expected AST", () => {
|
||||
const sourceFile = testState.compilerResult.program!.getSourceFile(Test262BaselineRunner.getTestFilePath(testState.filename))!;
|
||||
Baseline.runBaseline(testState.filename + ".AST.txt", Utils.sourceFileToJSON(sourceFile), Test262BaselineRunner.baselineOptions);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.tests.forEach(test => this.runTest(typeof test === "string" ? test : test.file));
|
||||
|
||||
public kind(): TestRunnerKind {
|
||||
return "test262";
|
||||
}
|
||||
|
||||
public enumerateTestFiles() {
|
||||
// see also: `enumerateTestFiles` in tests/webTestServer.ts
|
||||
return ts.map(this.enumerateFiles(Test262BaselineRunner.basePath, Test262BaselineRunner.testFileExtensionRegex, { recursive: true }), ts.normalizePath);
|
||||
}
|
||||
|
||||
public initializeTests() {
|
||||
// this will set up a series of describe/it blocks to run between the setup and cleanup phases
|
||||
if (this.tests.length === 0) {
|
||||
const testFiles = this.getTestFiles();
|
||||
testFiles.forEach(fn => {
|
||||
this.runTest(fn);
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.tests.forEach(test => this.runTest(typeof test === "string" ? test : test.file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -24,6 +24,16 @@
|
||||
],
|
||||
|
||||
"files": [
|
||||
"compilerRef.ts",
|
||||
"evaluatorRef.ts",
|
||||
"fakesRef.ts",
|
||||
"vpathRef.ts",
|
||||
"vfsRef.ts",
|
||||
"fourslashRef.ts",
|
||||
"playbackRef.ts",
|
||||
"utilsRef.ts",
|
||||
"documentsRef.ts",
|
||||
|
||||
"fourslashRunner.ts",
|
||||
"compilerRunner.ts",
|
||||
"projectsRunner.ts",
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
namespace ts {
|
||||
// make clear this is a global mutation!
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-qualifier
|
||||
ts.disableIncrementalParsing = false;
|
||||
|
||||
function withChange(text: IScriptSnapshot, start: number, length: number, newText: string): { text: IScriptSnapshot; textChangeRange: TextChangeRange; } {
|
||||
const contents = getSnapshotText(text);
|
||||
const newContents = contents.substr(0, start) + newText + contents.substring(start + length);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
namespace ts {
|
||||
import theory = utils.theory;
|
||||
import theory = Utils.theory;
|
||||
describe("unittests:: semver", () => {
|
||||
describe("Version", () => {
|
||||
function assertVersion(version: Version, [major, minor, patch, prerelease, build]: [number, number, number, string[]?, string[]?]) {
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
namespace ts {
|
||||
const _chai: typeof import("chai") = require("chai");
|
||||
const expect: typeof _chai.expect = _chai.expect;
|
||||
describe("unittests:: services:: languageService", () => {
|
||||
const files: {[index: string]: string} = {
|
||||
"foo.ts": `import Vue from "./vue";
|
||||
|
||||
@ -4,14 +4,14 @@ namespace ts {
|
||||
scenario: "javascriptProjectEmit",
|
||||
subScenario: `loads js-based projects and emits them correctly`,
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/common/nominal.js": utils.dedent`
|
||||
"/src/common/nominal.js": Utils.dedent`
|
||||
/**
|
||||
* @template T, Name
|
||||
* @typedef {T & {[Symbol.species]: Name}} Nominal
|
||||
*/
|
||||
module.exports = {};
|
||||
`,
|
||||
"/src/common/tsconfig.json": utils.dedent`
|
||||
"/src/common/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -19,14 +19,14 @@ namespace ts {
|
||||
},
|
||||
"include": ["nominal.js"]
|
||||
}`,
|
||||
"/src/sub-project/index.js": utils.dedent`
|
||||
"/src/sub-project/index.js": Utils.dedent`
|
||||
import { Nominal } from '../common/nominal';
|
||||
|
||||
/**
|
||||
* @typedef {Nominal<string, 'MyNominal'>} MyNominal
|
||||
*/
|
||||
`,
|
||||
"/src/sub-project/tsconfig.json": utils.dedent`
|
||||
"/src/sub-project/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -37,7 +37,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.js"]
|
||||
}`,
|
||||
"/src/sub-project-2/index.js": utils.dedent`
|
||||
"/src/sub-project-2/index.js": Utils.dedent`
|
||||
import { MyNominal } from '../sub-project/index';
|
||||
|
||||
const variable = {
|
||||
@ -51,7 +51,7 @@ namespace ts {
|
||||
return 'key';
|
||||
}
|
||||
`,
|
||||
"/src/sub-project-2/tsconfig.json": utils.dedent`
|
||||
"/src/sub-project-2/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -62,7 +62,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.js"]
|
||||
}`,
|
||||
"/src/tsconfig.json": utils.dedent`
|
||||
"/src/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
@ -73,7 +73,7 @@ namespace ts {
|
||||
],
|
||||
"include": []
|
||||
}`,
|
||||
"/src/tsconfig.base.json": utils.dedent`
|
||||
"/src/tsconfig.base.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
@ -93,13 +93,13 @@ namespace ts {
|
||||
let projFs: vfs.FileSystem;
|
||||
before(() => {
|
||||
projFs = loadProjectFromFiles({
|
||||
"/src/common/nominal.js": utils.dedent`
|
||||
"/src/common/nominal.js": Utils.dedent`
|
||||
/**
|
||||
* @template T, Name
|
||||
* @typedef {T & {[Symbol.species]: Name}} Nominal
|
||||
*/
|
||||
`,
|
||||
"/src/common/tsconfig.json": utils.dedent`
|
||||
"/src/common/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -108,13 +108,13 @@ namespace ts {
|
||||
},
|
||||
"include": ["nominal.js"]
|
||||
}`,
|
||||
"/src/sub-project/index.js": utils.dedent`
|
||||
"/src/sub-project/index.js": Utils.dedent`
|
||||
/**
|
||||
* @typedef {Nominal<string, 'MyNominal'>} MyNominal
|
||||
*/
|
||||
const c = /** @type {*} */(null);
|
||||
`,
|
||||
"/src/sub-project/tsconfig.json": utils.dedent`
|
||||
"/src/sub-project/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -126,7 +126,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.js"]
|
||||
}`,
|
||||
"/src/sub-project-2/index.js": utils.dedent`
|
||||
"/src/sub-project-2/index.js": Utils.dedent`
|
||||
const variable = {
|
||||
key: /** @type {MyNominal} */('value'),
|
||||
};
|
||||
@ -138,7 +138,7 @@ namespace ts {
|
||||
return 'key';
|
||||
}
|
||||
`,
|
||||
"/src/sub-project-2/tsconfig.json": utils.dedent`
|
||||
"/src/sub-project-2/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -150,7 +150,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.js"]
|
||||
}`,
|
||||
"/src/tsconfig.json": utils.dedent`
|
||||
"/src/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
@ -162,7 +162,7 @@ namespace ts {
|
||||
],
|
||||
"include": []
|
||||
}`,
|
||||
"/src/tsconfig.base.json": utils.dedent`
|
||||
"/src/tsconfig.base.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
@ -200,15 +200,15 @@ namespace ts {
|
||||
scenario: "javascriptProjectEmit",
|
||||
subScenario: `loads js-based projects with non-moved json files and emits them correctly`,
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/common/obj.json": utils.dedent`
|
||||
"/src/common/obj.json": Utils.dedent`
|
||||
{
|
||||
"val": 42
|
||||
}`,
|
||||
"/src/common/index.ts": utils.dedent`
|
||||
"/src/common/index.ts": Utils.dedent`
|
||||
import x = require("./obj.json");
|
||||
export = x;
|
||||
`,
|
||||
"/src/common/tsconfig.json": utils.dedent`
|
||||
"/src/common/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -217,12 +217,12 @@ namespace ts {
|
||||
},
|
||||
"include": ["index.ts", "obj.json"]
|
||||
}`,
|
||||
"/src/sub-project/index.js": utils.dedent`
|
||||
"/src/sub-project/index.js": Utils.dedent`
|
||||
import mod from '../common';
|
||||
|
||||
export const m = mod;
|
||||
`,
|
||||
"/src/sub-project/tsconfig.json": utils.dedent`
|
||||
"/src/sub-project/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -233,7 +233,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.js"]
|
||||
}`,
|
||||
"/src/sub-project-2/index.js": utils.dedent`
|
||||
"/src/sub-project-2/index.js": Utils.dedent`
|
||||
import { m } from '../sub-project/index';
|
||||
|
||||
const variable = {
|
||||
@ -244,7 +244,7 @@ namespace ts {
|
||||
return variable;
|
||||
}
|
||||
`,
|
||||
"/src/sub-project-2/tsconfig.json": utils.dedent`
|
||||
"/src/sub-project-2/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -255,7 +255,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.js"]
|
||||
}`,
|
||||
"/src/tsconfig.json": utils.dedent`
|
||||
"/src/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
@ -266,7 +266,7 @@ namespace ts {
|
||||
],
|
||||
"include": []
|
||||
}`,
|
||||
"/src/tsconfig.base.json": utils.dedent`
|
||||
"/src/tsconfig.base.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
|
||||
@ -5,12 +5,12 @@ namespace ts {
|
||||
scenario: "moduleSpecifiers",
|
||||
subScenario: `synthesized module specifiers resolve correctly`,
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/solution/common/nominal.ts": utils.dedent`
|
||||
"/src/solution/common/nominal.ts": Utils.dedent`
|
||||
export declare type Nominal<T, Name extends string> = T & {
|
||||
[Symbol.species]: Name;
|
||||
};
|
||||
`,
|
||||
"/src/solution/common/tsconfig.json": utils.dedent`
|
||||
"/src/solution/common/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -18,12 +18,12 @@ namespace ts {
|
||||
},
|
||||
"include": ["nominal.ts"]
|
||||
}`,
|
||||
"/src/solution/sub-project/index.ts": utils.dedent`
|
||||
"/src/solution/sub-project/index.ts": Utils.dedent`
|
||||
import { Nominal } from '../common/nominal';
|
||||
|
||||
export type MyNominal = Nominal<string, 'MyNominal'>;
|
||||
`,
|
||||
"/src/solution/sub-project/tsconfig.json": utils.dedent`
|
||||
"/src/solution/sub-project/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -34,7 +34,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.ts"]
|
||||
}`,
|
||||
"/src/solution/sub-project-2/index.ts": utils.dedent`
|
||||
"/src/solution/sub-project-2/index.ts": Utils.dedent`
|
||||
import { MyNominal } from '../sub-project/index';
|
||||
|
||||
const variable = {
|
||||
@ -45,7 +45,7 @@ namespace ts {
|
||||
return 'key';
|
||||
}
|
||||
`,
|
||||
"/src/solution/sub-project-2/tsconfig.json": utils.dedent`
|
||||
"/src/solution/sub-project-2/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -56,7 +56,7 @@ namespace ts {
|
||||
],
|
||||
"include": ["./index.ts"]
|
||||
}`,
|
||||
"/src/solution/tsconfig.json": utils.dedent`
|
||||
"/src/solution/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
@ -67,7 +67,7 @@ namespace ts {
|
||||
],
|
||||
"include": []
|
||||
}`,
|
||||
"/src/tsconfig.base.json": utils.dedent`
|
||||
"/src/tsconfig.base.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
@ -75,7 +75,7 @@ namespace ts {
|
||||
"outDir": "lib",
|
||||
}
|
||||
}`,
|
||||
"/src/tsconfig.json": utils.dedent`{
|
||||
"/src/tsconfig.json": Utils.dedent`{
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
|
||||
@ -4,12 +4,12 @@ namespace ts {
|
||||
scenario: "declarationEmit",
|
||||
subScenario: "when same version is referenced through source and another symlinked package",
|
||||
fs: () => {
|
||||
const fsaPackageJson = utils.dedent`
|
||||
const fsaPackageJson = Utils.dedent`
|
||||
{
|
||||
"name": "typescript-fsa",
|
||||
"version": "3.0.0-beta-2"
|
||||
}`;
|
||||
const fsaIndex = utils.dedent`
|
||||
const fsaIndex = Utils.dedent`
|
||||
export interface Action<Payload> {
|
||||
type: string;
|
||||
payload: Payload;
|
||||
@ -24,7 +24,7 @@ namespace ts {
|
||||
export declare function actionCreatorFactory(prefix?: string | null): ActionCreatorFactory;
|
||||
export default actionCreatorFactory;`;
|
||||
return loadProjectFromFiles({
|
||||
"/src/plugin-two/index.d.ts": utils.dedent`
|
||||
"/src/plugin-two/index.d.ts": Utils.dedent`
|
||||
declare const _default: {
|
||||
features: {
|
||||
featureOne: {
|
||||
@ -48,16 +48,16 @@ namespace ts {
|
||||
export default _default;`,
|
||||
"/src/plugin-two/node_modules/typescript-fsa/package.json": fsaPackageJson,
|
||||
"/src/plugin-two/node_modules/typescript-fsa/index.d.ts": fsaIndex,
|
||||
"/src/plugin-one/tsconfig.json": utils.dedent`
|
||||
"/src/plugin-one/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"declaration": true,
|
||||
},
|
||||
}`,
|
||||
"/src/plugin-one/index.ts": utils.dedent`
|
||||
"/src/plugin-one/index.ts": Utils.dedent`
|
||||
import pluginTwo from "plugin-two"; // include this to add reference to symlink`,
|
||||
"/src/plugin-one/action.ts": utils.dedent`
|
||||
"/src/plugin-one/action.ts": Utils.dedent`
|
||||
import { actionCreatorFactory } from "typescript-fsa"; // Include version of shared lib
|
||||
const action = actionCreatorFactory("somekey");
|
||||
const featureOne = action<{ route: string }>("feature-one");
|
||||
@ -74,9 +74,9 @@ namespace ts {
|
||||
scenario: "declarationEmit",
|
||||
subScenario: "when pkg references sibling package through indirect symlink",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/pkg1/dist/index.d.ts": utils.dedent`
|
||||
"/src/pkg1/dist/index.d.ts": Utils.dedent`
|
||||
export * from './types';`,
|
||||
"/src/pkg1/dist/types.d.ts": utils.dedent`
|
||||
"/src/pkg1/dist/types.d.ts": Utils.dedent`
|
||||
export declare type A = {
|
||||
id: string;
|
||||
};
|
||||
@ -90,7 +90,7 @@ namespace ts {
|
||||
toString(): string;
|
||||
static create<T, D extends IdType = IdType>(key: string): MetadataAccessor<T, D>;
|
||||
}`,
|
||||
"/src/pkg1/package.json": utils.dedent`
|
||||
"/src/pkg1/package.json": Utils.dedent`
|
||||
{
|
||||
"name": "@raymondfeng/pkg1",
|
||||
"version": "1.0.0",
|
||||
@ -98,11 +98,11 @@ namespace ts {
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts"
|
||||
}`,
|
||||
"/src/pkg2/dist/index.d.ts": utils.dedent`
|
||||
"/src/pkg2/dist/index.d.ts": Utils.dedent`
|
||||
export * from './types';`,
|
||||
"/src/pkg2/dist/types.d.ts": utils.dedent`
|
||||
"/src/pkg2/dist/types.d.ts": Utils.dedent`
|
||||
export {MetadataAccessor} from '@raymondfeng/pkg1';`,
|
||||
"/src/pkg2/package.json": utils.dedent`
|
||||
"/src/pkg2/package.json": Utils.dedent`
|
||||
{
|
||||
"name": "@raymondfeng/pkg2",
|
||||
"version": "1.0.0",
|
||||
@ -110,12 +110,12 @@ namespace ts {
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts"
|
||||
}`,
|
||||
"/src/pkg3/src/index.ts": utils.dedent`
|
||||
"/src/pkg3/src/index.ts": Utils.dedent`
|
||||
export * from './keys';`,
|
||||
"/src/pkg3/src/keys.ts": utils.dedent`
|
||||
"/src/pkg3/src/keys.ts": Utils.dedent`
|
||||
import {MetadataAccessor} from "@raymondfeng/pkg2";
|
||||
export const ADMIN = MetadataAccessor.create<boolean>('1');`,
|
||||
"/src/pkg3/tsconfig.json": utils.dedent`
|
||||
"/src/pkg3/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
|
||||
@ -5,7 +5,7 @@ namespace ts {
|
||||
subScenario: "when passing filename for buildinfo on commandline",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/project/src/main.ts": "export const x = 10;",
|
||||
"/src/project/tsconfig.json": utils.dedent`
|
||||
"/src/project/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
@ -25,7 +25,7 @@ namespace ts {
|
||||
subScenario: "when passing rootDir from commandline",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/project/src/main.ts": "export const x = 10;",
|
||||
"/src/project/tsconfig.json": utils.dedent`
|
||||
"/src/project/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
@ -60,7 +60,7 @@ namespace ts {
|
||||
subScenario: "when passing rootDir is in the tsconfig",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/project/src/main.ts": "export const x = 10;",
|
||||
"/src/project/tsconfig.json": utils.dedent`
|
||||
"/src/project/tsconfig.json": Utils.dedent`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
|
||||
@ -4,7 +4,7 @@ namespace ts {
|
||||
scenario: "listFilesOnly",
|
||||
subScenario: "combined with watch",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/test.ts": utils.dedent`
|
||||
"/src/test.ts": Utils.dedent`
|
||||
export const x = 1;`,
|
||||
}),
|
||||
commandLineArgs: ["/src/test.ts", "--watch", "--listFilesOnly"]
|
||||
@ -14,7 +14,7 @@ namespace ts {
|
||||
scenario: "listFilesOnly",
|
||||
subScenario: "loose file",
|
||||
fs: () => loadProjectFromFiles({
|
||||
"/src/test.ts": utils.dedent`
|
||||
"/src/test.ts": Utils.dedent`
|
||||
export const x = 1;`,
|
||||
}),
|
||||
commandLineArgs: ["/src/test.ts", "--listFilesOnly"]
|
||||
|
||||
@ -194,7 +194,7 @@ namespace ts.tscWatch {
|
||||
exportedModulesMap: {},
|
||||
semanticDiagnosticsPerFile: [libFilePath, file1Path, file2Path]
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -221,7 +221,7 @@ namespace ts.tscWatch {
|
||||
exportedModulesMap: {},
|
||||
semanticDiagnosticsPerFile: [libFilePath, file1Path, file2Path]
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -285,7 +285,7 @@ namespace ts.tscWatch {
|
||||
file2ReuasableError
|
||||
]
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -315,7 +315,7 @@ namespace ts.tscWatch {
|
||||
file2ReuasableError
|
||||
]
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -348,7 +348,7 @@ namespace ts.tscWatch {
|
||||
]
|
||||
},
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -420,7 +420,7 @@ namespace ts.tscWatch {
|
||||
exportedModulesMap: {},
|
||||
semanticDiagnosticsPerFile: [libFilePath, file1Path, file2Path]
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -446,7 +446,7 @@ namespace ts.tscWatch {
|
||||
exportedModulesMap: {},
|
||||
semanticDiagnosticsPerFile: [libFilePath, file1Path, file2Path]
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -506,7 +506,7 @@ namespace ts.tscWatch {
|
||||
file2ReuasableError
|
||||
]
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -536,7 +536,7 @@ namespace ts.tscWatch {
|
||||
file1Path
|
||||
]
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -620,7 +620,7 @@ namespace ts.tscWatch {
|
||||
]
|
||||
},
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -729,7 +729,7 @@ export { C } from "./c";
|
||||
path: `${project}/tsconfig.tsbuildinfo`,
|
||||
content: getBuildInfoText({
|
||||
program: initialProgram,
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
@ -753,7 +753,7 @@ export { C } from "./c";
|
||||
},
|
||||
...rest
|
||||
},
|
||||
version
|
||||
version: ts.version // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
})
|
||||
}
|
||||
],
|
||||
|
||||
@ -276,7 +276,7 @@ namespace ts.projectSystem {
|
||||
configFileName: "tsconfig.json",
|
||||
projectType: "configured",
|
||||
languageServiceEnabled: true,
|
||||
version,
|
||||
version: ts.version, // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
...partial,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
const expect: typeof _chai.expect = _chai.expect;
|
||||
|
||||
namespace ts.server {
|
||||
const _chai: typeof import("chai") = require("chai");
|
||||
const expect: typeof _chai.expect = _chai.expect;
|
||||
let lastWrittenToHost: string;
|
||||
const noopFileWatcher: FileWatcher = { close: noop };
|
||||
const mockHost: ServerHost = {
|
||||
@ -179,7 +179,9 @@ namespace ts.server {
|
||||
type: "request"
|
||||
};
|
||||
|
||||
const expected: protocol.StatusResponseBody = { version };
|
||||
const expected: protocol.StatusResponseBody = {
|
||||
version: ts.version, // eslint-disable-line @typescript-eslint/no-unnecessary-qualifier
|
||||
};
|
||||
assert.deepEqual(session.executeCommand(req).response, expected);
|
||||
});
|
||||
});
|
||||
|
||||
2
src/testRunner/utilsRef.ts
Normal file
2
src/testRunner/utilsRef.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// empty ref to Utils so it can be referenced by unittests
|
||||
namespace Utils {}
|
||||
2
src/testRunner/vfsRef.ts
Normal file
2
src/testRunner/vfsRef.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// empty ref to vfs so it can be referenced by unittests
|
||||
namespace vfs {}
|
||||
2
src/testRunner/vpathRef.ts
Normal file
2
src/testRunner/vpathRef.ts
Normal file
@ -0,0 +1,2 @@
|
||||
// empty ref to vpath so it can be referenced by unittests
|
||||
namespace vpath {}
|
||||
@ -5876,7 +5876,6 @@ declare namespace ts {
|
||||
function getDefaultCompilerOptions(): CompilerOptions;
|
||||
function getSupportedCodeFixes(): string[];
|
||||
function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile;
|
||||
let disableIncrementalParsing: boolean;
|
||||
function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange | undefined, aggressiveChecks?: boolean): SourceFile;
|
||||
function createLanguageService(host: LanguageServiceHost, documentRegistry?: DocumentRegistry, syntaxOnly?: boolean): LanguageService;
|
||||
/**
|
||||
|
||||
@ -5876,7 +5876,6 @@ declare namespace ts {
|
||||
function getDefaultCompilerOptions(): CompilerOptions;
|
||||
function getSupportedCodeFixes(): string[];
|
||||
function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile;
|
||||
let disableIncrementalParsing: boolean;
|
||||
function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange | undefined, aggressiveChecks?: boolean): SourceFile;
|
||||
function createLanguageService(host: LanguageServiceHost, documentRegistry?: DocumentRegistry, syntaxOnly?: boolean): LanguageService;
|
||||
/**
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user