test(ivy): support chdir() on the compiler's filesystem abstraction (#33828)
This commit adds the ability to change directories using the compiler's internal filesystem abstraction. This is a prerequisite for writing tests which are sensitive to the current working directory. In addition to supporting the `chdir()` operation, this commit also fixes `getDefaultLibLocation()` for mock filesystems to not assume `node_modules` is in the current directory, but to resolve it similarly to how Node does by progressively looking higher in the directory tree. PR Close #33828
This commit is contained in:
parent
6bf2531b19
commit
51720745dd
@ -99,6 +99,7 @@ export class CachedFileSystem implements FileSystem {
|
|||||||
// The following methods simply call through to the delegate.
|
// The following methods simply call through to the delegate.
|
||||||
readdir(path: AbsoluteFsPath): PathSegment[] { return this.delegate.readdir(path); }
|
readdir(path: AbsoluteFsPath): PathSegment[] { return this.delegate.readdir(path); }
|
||||||
pwd(): AbsoluteFsPath { return this.delegate.pwd(); }
|
pwd(): AbsoluteFsPath { return this.delegate.pwd(); }
|
||||||
|
chdir(path: AbsoluteFsPath): void { this.delegate.chdir(path); }
|
||||||
extname(path: AbsoluteFsPath|PathSegment): string { return this.delegate.extname(path); }
|
extname(path: AbsoluteFsPath|PathSegment): string { return this.delegate.extname(path); }
|
||||||
isCaseSensitive(): boolean { return this.delegate.isCaseSensitive(); }
|
isCaseSensitive(): boolean { return this.delegate.isCaseSensitive(); }
|
||||||
isRoot(path: AbsoluteFsPath): boolean { return this.delegate.isRoot(path); }
|
isRoot(path: AbsoluteFsPath): boolean { return this.delegate.isRoot(path); }
|
||||||
|
@ -24,6 +24,7 @@ export class InvalidFileSystem implements FileSystem {
|
|||||||
lstat(path: AbsoluteFsPath): FileStats { throw makeError(); }
|
lstat(path: AbsoluteFsPath): FileStats { throw makeError(); }
|
||||||
stat(path: AbsoluteFsPath): FileStats { throw makeError(); }
|
stat(path: AbsoluteFsPath): FileStats { throw makeError(); }
|
||||||
pwd(): AbsoluteFsPath { throw makeError(); }
|
pwd(): AbsoluteFsPath { throw makeError(); }
|
||||||
|
chdir(path: AbsoluteFsPath): void { throw makeError(); }
|
||||||
extname(path: AbsoluteFsPath|PathSegment): string { throw makeError(); }
|
extname(path: AbsoluteFsPath|PathSegment): string { throw makeError(); }
|
||||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); }
|
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); }
|
||||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); }
|
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); }
|
||||||
|
@ -26,6 +26,7 @@ export class NodeJSFileSystem implements FileSystem {
|
|||||||
lstat(path: AbsoluteFsPath): FileStats { return fs.lstatSync(path); }
|
lstat(path: AbsoluteFsPath): FileStats { return fs.lstatSync(path); }
|
||||||
stat(path: AbsoluteFsPath): FileStats { return fs.statSync(path); }
|
stat(path: AbsoluteFsPath): FileStats { return fs.statSync(path); }
|
||||||
pwd(): AbsoluteFsPath { return this.normalize(process.cwd()) as AbsoluteFsPath; }
|
pwd(): AbsoluteFsPath { return this.normalize(process.cwd()) as AbsoluteFsPath; }
|
||||||
|
chdir(dir: AbsoluteFsPath): void { process.chdir(dir); }
|
||||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.copyFileSync(from, to); }
|
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.copyFileSync(from, to); }
|
||||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.renameSync(from, to); }
|
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.renameSync(from, to); }
|
||||||
ensureDir(path: AbsoluteFsPath): void {
|
ensureDir(path: AbsoluteFsPath): void {
|
||||||
|
@ -43,6 +43,7 @@ export interface FileSystem {
|
|||||||
lstat(path: AbsoluteFsPath): FileStats;
|
lstat(path: AbsoluteFsPath): FileStats;
|
||||||
stat(path: AbsoluteFsPath): FileStats;
|
stat(path: AbsoluteFsPath): FileStats;
|
||||||
pwd(): AbsoluteFsPath;
|
pwd(): AbsoluteFsPath;
|
||||||
|
chdir(path: AbsoluteFsPath): void;
|
||||||
extname(path: AbsoluteFsPath|PathSegment): string;
|
extname(path: AbsoluteFsPath|PathSegment): string;
|
||||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
|
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
|
||||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
|
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
|
||||||
|
@ -132,7 +132,36 @@ export abstract class MockFileSystem implements FileSystem {
|
|||||||
|
|
||||||
pwd(): AbsoluteFsPath { return this._cwd; }
|
pwd(): AbsoluteFsPath { return this._cwd; }
|
||||||
|
|
||||||
getDefaultLibLocation(): AbsoluteFsPath { return this.resolve('node_modules/typescript/lib'); }
|
chdir(path: AbsoluteFsPath): void { this._cwd = this.normalize(path); }
|
||||||
|
|
||||||
|
getDefaultLibLocation(): AbsoluteFsPath {
|
||||||
|
// Mimic the node module resolution algorithm and start in the current directory, then look
|
||||||
|
// progressively further up the tree until reaching the FS root.
|
||||||
|
// E.g. if the current directory is /foo/bar, look in /foo/bar/node_modules, then
|
||||||
|
// /foo/node_modules, then /node_modules.
|
||||||
|
|
||||||
|
let path = 'node_modules/typescript/lib';
|
||||||
|
let resolvedPath = this.resolve(path);
|
||||||
|
|
||||||
|
// Construct a path for the top-level node_modules to identify the stopping point.
|
||||||
|
const topLevelNodeModules = this.resolve('/' + path);
|
||||||
|
|
||||||
|
while (resolvedPath !== topLevelNodeModules) {
|
||||||
|
if (this.exists(resolvedPath)) {
|
||||||
|
return resolvedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not here, look one level higher.
|
||||||
|
path = '../' + path;
|
||||||
|
resolvedPath = this.resolve(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The loop exits before checking the existence of /node_modules/typescript at the top level.
|
||||||
|
// This is intentional - if no /node_modules/typescript exists anywhere in the tree, there's
|
||||||
|
// nothing this function can do about it, and TS may error later if it looks for a lib.d.ts file
|
||||||
|
// within this directory. It might be okay, though, if TS never checks for one.
|
||||||
|
return topLevelNodeModules;
|
||||||
|
}
|
||||||
|
|
||||||
abstract resolve(...paths: string[]): AbsoluteFsPath;
|
abstract resolve(...paths: string[]): AbsoluteFsPath;
|
||||||
abstract dirname<T extends string>(file: T): T;
|
abstract dirname<T extends string>(file: T): T;
|
||||||
|
@ -35,7 +35,8 @@ export class NgtscTestEnvironment {
|
|||||||
/**
|
/**
|
||||||
* Set up a new testing environment.
|
* Set up a new testing environment.
|
||||||
*/
|
*/
|
||||||
static setup(files?: Folder): NgtscTestEnvironment {
|
static setup(files?: Folder, workingDir: AbsoluteFsPath = absoluteFrom('/')):
|
||||||
|
NgtscTestEnvironment {
|
||||||
const fs = getFileSystem();
|
const fs = getFileSystem();
|
||||||
if (files !== undefined && fs instanceof MockFileSystem) {
|
if (files !== undefined && fs instanceof MockFileSystem) {
|
||||||
fs.init(files);
|
fs.init(files);
|
||||||
@ -44,7 +45,8 @@ export class NgtscTestEnvironment {
|
|||||||
const host = new AugmentedCompilerHost(fs);
|
const host = new AugmentedCompilerHost(fs);
|
||||||
setWrapHostForTest(makeWrapHost(host));
|
setWrapHostForTest(makeWrapHost(host));
|
||||||
|
|
||||||
const env = new NgtscTestEnvironment(fs, fs.resolve('/built'), absoluteFrom('/'));
|
const env = new NgtscTestEnvironment(fs, fs.resolve('/built'), workingDir);
|
||||||
|
fs.chdir(workingDir);
|
||||||
|
|
||||||
env.write(absoluteFrom('/tsconfig-base.json'), `{
|
env.write(absoluteFrom('/tsconfig-base.json'), `{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
@ -54,7 +56,6 @@ export class NgtscTestEnvironment {
|
|||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"outDir": "built",
|
"outDir": "built",
|
||||||
"rootDir": ".",
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user