test(compiler-cli): improve testing harness for incremental compilation (#25275)

In tsc 3.0 the check that enables program structure reuse in tryReuseStructureFromOldProgram has changed
and now uses identity comparison on arrays within CompilerOptions. Since we recreate the options
on each incremental compilation, we now fail this check.

After this change the default set of options is reused in between incremental compilations, but we still
allow options to be overriden if needed.

PR Close #25275
This commit is contained in:
Igor Minar
2018-08-21 16:27:44 -07:00
committed by Matias Niemelä
parent ab32ac6bb7
commit 317d40d879
3 changed files with 87 additions and 79 deletions

View File

@ -59,7 +59,7 @@ describe('ng program', () => {
function compile(
oldProgram?: ng.Program, overrideOptions?: ng.CompilerOptions, rootNames?: string[],
host?: CompilerHost): {program: ng.Program, emitResult: ts.EmitResult, host: ng.CompilerHost} {
host?: CompilerHost): {program: ng.Program, emitResult: ts.EmitResult} {
const options = testSupport.createCompilerOptions(overrideOptions);
if (!rootNames) {
rootNames = [path.resolve(testSupport.basePath, 'src/index.ts')];
@ -75,7 +75,7 @@ describe('ng program', () => {
});
expectNoDiagnosticsInProgram(options, program);
const emitResult = program.emit();
return {emitResult, program, host};
return {emitResult, program};
}
function createWatchModeHost(): ng.CompilerHost {
@ -85,17 +85,16 @@ describe('ng program', () => {
const originalGetSourceFile = host.getSourceFile;
const cache = new Map<string, ts.SourceFile>();
host.getSourceFile = function(fileName: string): ts.SourceFile {
if (fileName.endsWith('@angular/core/src/di/injection_token.d.ts')) {
debugger;
}
const sf = originalGetSourceFile.call(host, fileName) as ts.SourceFile;
if (sf && cache.has(sf.fileName)) {
const oldSf = cache.get(sf.fileName)!;
if (oldSf.getFullText() === sf.getFullText()) {
return oldSf;
if (sf) {
if (cache.has(sf.fileName)) {
const oldSf = cache.get(sf.fileName) !;
if (oldSf.getFullText() === sf.getFullText()) {
return oldSf;
}
}
cache.set(sf.fileName, sf);
}
sf && cache.set(sf.fileName, sf);
return sf;
};
return host;
@ -290,62 +289,65 @@ describe('ng program', () => {
.toBe(false);
});
describe('reuse tests', () => {
it('should reuse the old ts program completely if nothing changed', () => {
testSupport.writeFiles({'src/index.ts': createModuleAndCompSource('main')});
const host = createWatchModeHost();
// Note: the second compile drops factories for library files,
// and therefore changes the structure again
const p1 = compile(undefined, undefined, undefined, host).program;
const p2 = compile(p1, undefined, undefined, host).program;
debugger;
compile(p2, undefined, undefined, host);
expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.Completely);
});
describe(
'verify that program structure is reused within tsc in order to speed up incremental compilation',
() => {
it('should reuse the old ts program completely if a template or a ts file changed', () => {
const host = createWatchModeHost();
testSupport.writeFiles({
'src/main.ts': createModuleAndCompSource('main', 'main.html'),
'src/main.html': `Some template`,
'src/util.ts': `export const x = 1`,
'src/index.ts': `
it('should reuse the old ts program completely if nothing changed', () => {
testSupport.writeFiles({'src/index.ts': createModuleAndCompSource('main')});
const host = createWatchModeHost();
// Note: the second compile drops factories for library files,
// and therefore changes the structure again
const p1 = compile(undefined, undefined, undefined, host).program;
const p2 = compile(p1, undefined, undefined, host).program;
compile(p2, undefined, undefined, host);
expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.Completely);
});
it('should reuse the old ts program completely if a template or a ts file changed',
() => {
const host = createWatchModeHost();
testSupport.writeFiles({
'src/main.ts': createModuleAndCompSource('main', 'main.html'),
'src/main.html': `Some template`,
'src/util.ts': `export const x = 1`,
'src/index.ts': `
export * from './main';
export * from './util';
`
});
// Note: the second compile drops factories for library files,
// and therefore changes the structure again
const p1 = compile(undefined, undefined, undefined, host).program;
const p2 = compile(p1, undefined, undefined, host).program;
testSupport.writeFiles({
'src/main.html': `Another template`,
'src/util.ts': `export const x = 2`,
});
compile(p2, undefined, undefined, host);
expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.Completely);
});
});
// Note: the second compile drops factories for library files,
// and therefore changes the structure again
const p1 = compile(undefined, undefined, undefined, host).program;
const p2 = compile(p1, undefined, undefined, host).program;
testSupport.writeFiles({
'src/main.html': `Another template`,
'src/util.ts': `export const x = 2`,
});
compile(p2, undefined, undefined, host);
expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.Completely);
});
it('should not reuse the old ts program if an import changed', () => {
const host = createWatchModeHost();
testSupport.writeFiles({
'src/main.ts': createModuleAndCompSource('main'),
'src/util.ts': `export const x = 1`,
'src/index.ts': `
it('should not reuse the old ts program if an import changed', () => {
const host = createWatchModeHost();
testSupport.writeFiles({
'src/main.ts': createModuleAndCompSource('main'),
'src/util.ts': `export const x = 1`,
'src/index.ts': `
export * from './main';
export * from './util';
`
});
// Note: the second compile drops factories for library files,
// and therefore changes the structure again
const p1 = compile(undefined, undefined, undefined, host).program;
const p2 = compile(p1, undefined, undefined, host).program;
testSupport.writeFiles(
{'src/util.ts': `import {Injectable} from '@angular/core'; export const x = 1;`});
compile(p2, undefined, undefined, host);
expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.SafeModules);
});
});
// Note: the second compile drops factories for library files,
// and therefore changes the structure again
const p1 = compile(undefined, undefined, undefined, host).program;
const p2 = compile(p1, undefined, undefined, host).program;
testSupport.writeFiles(
{'src/util.ts': `import {Injectable} from '@angular/core'; export const x = 1;`});
compile(p2, undefined, undefined, host);
expect(tsStructureIsReused(p2.getTsProgram())).toBe(StructureIsReused.SafeModules);
});
});
});