angular/packages/compiler-cli/test/helpers/src/mock_file_loading.ts
Pete Bacon Darwin 7186f9c016 refactor(ivy): implement a virtual file-system layer in ngtsc + ngcc (#30921)
To improve cross platform support, all file access (and path manipulation)
is now done through a well known interface (`FileSystem`).

For testing a number of `MockFileSystem` implementations are provided.
These provide an in-memory file-system which emulates operating systems
like OS/X, Unix and Windows.

The current file system is always available via the static method,
`FileSystem.getFileSystem()`. This is also used by a number of static
methods on `AbsoluteFsPath` and `PathSegment`, to avoid having to pass
`FileSystem` objects around all the time. The result of this is that one
must be careful to ensure that the file-system has been initialized before
using any of these static methods. To prevent this happening accidentally
the current file system always starts out as an instance of `InvalidFileSystem`,
which will throw an error if any of its methods are called.

You can set the current file-system by calling `FileSystem.setFileSystem()`.
During testing you can call the helper function `initMockFileSystem(os)`
which takes a string name of the OS to emulate, and will also monkey-patch
aspects of the TypeScript library to ensure that TS is also using the
current file-system.

Finally there is the `NgtscCompilerHost` to be used for any TypeScript
compilation, which uses a given file-system.

All tests that interact with the file-system should be tested against each
of the mock file-systems. A series of helpers have been provided to support
such tests:

* `runInEachFileSystem()` - wrap your tests in this helper to run all the
wrapped tests in each of the mock file-systems.
* `addTestFilesToFileSystem()` - use this to add files and their contents
to the mock file system for testing.
* `loadTestFilesFromDisk()` - use this to load a mirror image of files on
disk into the in-memory mock file-system.
* `loadFakeCore()` - use this to load a fake version of `@angular/core`
into the mock file-system.

All ngcc and ngtsc source and tests now use this virtual file-system setup.

PR Close #30921
2019-06-25 16:25:24 -07:00

83 lines
2.8 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/// <reference types="node" />
import {readFileSync, readdirSync, statSync} from 'fs';
import {resolve} from 'path';
import {getAngularPackagesFromRunfiles, resolveNpmTreeArtifact} from '..';
import {AbsoluteFsPath, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system';
import {Folder, MockFileSystemPosix, TestFile} from '../../../src/ngtsc/file_system/testing';
export function loadTestFiles(files: TestFile[]) {
const fs = getFileSystem();
files.forEach(file => {
fs.ensureDir(fs.dirname(file.name));
fs.writeFile(file.name, file.contents);
});
}
export function loadStandardTestFiles(
{fakeCore = true, rxjs = false}: {fakeCore?: boolean, rxjs?: boolean} = {}): Folder {
const tmpFs = new MockFileSystemPosix(true);
const basePath = '/' as AbsoluteFsPath;
loadTestDirectory(
tmpFs, resolveNpmTreeArtifact('typescript'),
tmpFs.resolve(basePath, 'node_modules/typescript'));
loadTestDirectory(
tmpFs, resolveNpmTreeArtifact('tslib'), tmpFs.resolve(basePath, 'node_modules/tslib'));
if (fakeCore) {
loadFakeCore(tmpFs, basePath);
} else {
getAngularPackagesFromRunfiles().forEach(({name, pkgPath}) => {
loadTestDirectory(tmpFs, pkgPath, tmpFs.resolve('/node_modules/@angular', name));
});
}
if (rxjs) {
loadTestDirectory(
tmpFs, resolveNpmTreeArtifact('rxjs'), tmpFs.resolve(basePath, 'node_modules/rxjs'));
}
return tmpFs.dump();
}
export function loadFakeCore(fs: FileSystem, basePath: string = '/') {
loadTestDirectory(
fs, resolveNpmTreeArtifact('angular/packages/compiler-cli/test/ngtsc/fake_core/npm_package'),
fs.resolve(basePath, 'node_modules/@angular/core'));
}
/**
* Load real files from the real file-system into a mock file-system.
* @param fs the file-system where the directory is to be loaded.
* @param directoryPath the path to the directory we want to load.
* @param mockPath the path within the mock file-system where the directory is to be loaded.
*/
function loadTestDirectory(fs: FileSystem, directoryPath: string, mockPath: AbsoluteFsPath): void {
readdirSync(directoryPath).forEach(item => {
const srcPath = resolve(directoryPath, item);
const targetPath = fs.resolve(mockPath, item);
try {
if (statSync(srcPath).isDirectory()) {
fs.ensureDir(targetPath);
loadTestDirectory(fs, srcPath, targetPath);
} else {
fs.ensureDir(fs.dirname(targetPath));
fs.writeFile(targetPath, readFileSync(srcPath, 'utf-8'));
}
} catch (e) {
console.warn(`Failed to add ${srcPath} to the mock file-system: ${e.message}`);
}
});
}