test(compiler-cli): re-enable dynamic value diagnostic tests on Windows CI (#37782)

This commit re-enables some tests that were temporarily disabled on Windows,
as they failed on native Windows CI. The Windows filesystem emulation has
been corrected in an earlier commit, such that the original failure would
now also occur during emulation on Linux CI.

PR Close #37782
This commit is contained in:
JoostK 2020-06-26 23:06:36 +02:00 committed by Misko Hevery
parent 1a62f74496
commit 49f27e31ed
2 changed files with 116 additions and 128 deletions

View File

@ -6,99 +6,92 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {platform} from 'os';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {FatalDiagnosticError} from '../../diagnostics'; import {absoluteFrom as _, getSourceFileOrError} from '../../file_system';
import {absoluteFrom as _} from '../../file_system';
import {runInEachFileSystem, TestFile} from '../../file_system/testing'; import {runInEachFileSystem, TestFile} from '../../file_system/testing';
import {PartialEvaluator} from '../../partial_evaluator'; import {PartialEvaluator} from '../../partial_evaluator';
import {TypeScriptReflectionHost} from '../../reflection'; import {TypeScriptReflectionHost} from '../../reflection';
import {getDeclaration, makeProgram} from '../../testing'; import {getDeclaration, makeProgram} from '../../testing';
import {createValueHasWrongTypeError} from '../src/diagnostics'; import {createValueHasWrongTypeError} from '../src/diagnostics';
runInEachFileSystem(os => { runInEachFileSystem(() => {
describe('ngtsc annotation diagnostics', () => { describe('ngtsc annotation diagnostics', () => {
// These tests are currently disabled when running in Windows mode as the assertions involving describe('createValueError()', () => {
// the filename attached to the diagnostic are suffering from a case-sensitivity issue. it('should include a trace for dynamic values', () => {
// const {error, program} = createError('', 'nonexistent', 'Error message');
// TODO(JoostK): re-enable on Windows once the case issue has been solved. const entrySf = getSourceFileOrError(program, _('/entry.ts'));
if (os !== 'Windows' && platform() !== 'win32') {
describe('createValueError()', () => {
it('should include a trace for dynamic values', () => {
const error = createError('', 'nonexistent', 'Error message');
if (typeof error.message === 'string') { if (typeof error.message === 'string') {
return fail('Created error must have a message chain'); return fail('Created error must have a message chain');
} }
expect(error.message.messageText).toBe('Error message'); expect(error.message.messageText).toBe('Error message');
expect(error.message.next!.length).toBe(1); expect(error.message.next!.length).toBe(1);
expect(error.message.next![0].messageText) expect(error.message.next![0].messageText)
.toBe(`Value could not be determined statically.`); .toBe(`Value could not be determined statically.`);
expect(error.relatedInformation).toBeDefined(); expect(error.relatedInformation).toBeDefined();
expect(error.relatedInformation!.length).toBe(1); expect(error.relatedInformation!.length).toBe(1);
expect(error.relatedInformation![0].messageText).toBe('Unknown reference.'); expect(error.relatedInformation![0].messageText).toBe('Unknown reference.');
expect(error.relatedInformation![0].file!.fileName).toBe(_('/entry.ts')); expect(error.relatedInformation![0].file!.fileName).toBe(entrySf.fileName);
expect(getSourceCode(error.relatedInformation![0])).toBe('nonexistent'); expect(getSourceCode(error.relatedInformation![0])).toBe('nonexistent');
});
it('should include a pointer for a reference to a named declaration', () => {
const error = createError(
`import {Foo} from './foo';`, 'Foo', 'Error message',
[{name: _('/foo.ts'), contents: 'export class Foo {}'}]);
if (typeof error.message === 'string') {
return fail('Created error must have a message chain');
}
expect(error.message.messageText).toBe('Error message');
expect(error.message.next!.length).toBe(1);
expect(error.message.next![0].messageText).toBe(`Value is a reference to 'Foo'.`);
expect(error.relatedInformation).toBeDefined();
expect(error.relatedInformation!.length).toBe(1);
expect(error.relatedInformation![0].messageText).toBe('Reference is declared here.');
expect(error.relatedInformation![0].file!.fileName).toBe(_('/foo.ts'));
expect(getSourceCode(error.relatedInformation![0])).toBe('Foo');
});
it('should include a pointer for a reference to an anonymous declaration', () => {
const error = createError(
`import Foo from './foo';`, 'Foo', 'Error message',
[{name: _('/foo.ts'), contents: 'export default class {}'}]);
if (typeof error.message === 'string') {
return fail('Created error must have a message chain');
}
expect(error.message.messageText).toBe('Error message');
expect(error.message.next!.length).toBe(1);
expect(error.message.next![0].messageText)
.toBe(`Value is a reference to an anonymous declaration.`);
expect(error.relatedInformation).toBeDefined();
expect(error.relatedInformation!.length).toBe(1);
expect(error.relatedInformation![0].messageText).toBe('Reference is declared here.');
expect(error.relatedInformation![0].file!.fileName).toBe(_('/foo.ts'));
expect(getSourceCode(error.relatedInformation![0])).toBe('export default class {}');
});
it('should include a representation of the value\'s type', () => {
const error = createError('', '{a: 2}', 'Error message');
if (typeof error.message === 'string') {
return fail('Created error must have a message chain');
}
expect(error.message.messageText).toBe('Error message');
expect(error.message.next!.length).toBe(1);
expect(error.message.next![0].messageText).toBe(`Value is of type '{ a: number }'.`);
expect(error.relatedInformation).not.toBeDefined();
});
}); });
}
it('should not be empty', () => {}); it('should include a pointer for a reference to a named declaration', () => {
const {error, program} = createError(
`import {Foo} from './foo';`, 'Foo', 'Error message',
[{name: _('/foo.ts'), contents: 'export class Foo {}'}]);
const fooSf = getSourceFileOrError(program, _('/foo.ts'));
if (typeof error.message === 'string') {
return fail('Created error must have a message chain');
}
expect(error.message.messageText).toBe('Error message');
expect(error.message.next!.length).toBe(1);
expect(error.message.next![0].messageText).toBe(`Value is a reference to 'Foo'.`);
expect(error.relatedInformation).toBeDefined();
expect(error.relatedInformation!.length).toBe(1);
expect(error.relatedInformation![0].messageText).toBe('Reference is declared here.');
expect(error.relatedInformation![0].file!.fileName).toBe(fooSf.fileName);
expect(getSourceCode(error.relatedInformation![0])).toBe('Foo');
});
it('should include a pointer for a reference to an anonymous declaration', () => {
const {error, program} = createError(
`import Foo from './foo';`, 'Foo', 'Error message',
[{name: _('/foo.ts'), contents: 'export default class {}'}]);
const fooSf = getSourceFileOrError(program, _('/foo.ts'));
if (typeof error.message === 'string') {
return fail('Created error must have a message chain');
}
expect(error.message.messageText).toBe('Error message');
expect(error.message.next!.length).toBe(1);
expect(error.message.next![0].messageText)
.toBe(`Value is a reference to an anonymous declaration.`);
expect(error.relatedInformation).toBeDefined();
expect(error.relatedInformation!.length).toBe(1);
expect(error.relatedInformation![0].messageText).toBe('Reference is declared here.');
expect(error.relatedInformation![0].file!.fileName).toBe(fooSf.fileName);
expect(getSourceCode(error.relatedInformation![0])).toBe('export default class {}');
});
it('should include a representation of the value\'s type', () => {
const {error} = createError('', '{a: 2}', 'Error message');
if (typeof error.message === 'string') {
return fail('Created error must have a message chain');
}
expect(error.message.messageText).toBe('Error message');
expect(error.message.next!.length).toBe(1);
expect(error.message.next![0].messageText).toBe(`Value is of type '{ a: number }'.`);
expect(error.relatedInformation).not.toBeDefined();
});
});
}); });
}); });
@ -108,8 +101,7 @@ function getSourceCode(diag: ts.DiagnosticRelatedInformation): string {
} }
function createError( function createError(
code: string, expr: string, messageText: string, code: string, expr: string, messageText: string, supportingFiles: TestFile[] = []) {
supportingFiles: TestFile[] = []): FatalDiagnosticError {
const {program} = makeProgram( const {program} = makeProgram(
[{name: _('/entry.ts'), contents: `${code}; const target$ = ${expr}`}, ...supportingFiles], [{name: _('/entry.ts'), contents: `${code}; const target$ = ${expr}`}, ...supportingFiles],
/* options */ undefined, /* host */ undefined, /* checkForErrors */ false); /* options */ undefined, /* host */ undefined, /* checkForErrors */ false);
@ -121,5 +113,6 @@ function createError(
const evaluator = new PartialEvaluator(reflectionHost, checker, /* dependencyTracker */ null); const evaluator = new PartialEvaluator(reflectionHost, checker, /* dependencyTracker */ null);
const value = evaluator.evaluate(valueExpr); const value = evaluator.evaluate(valueExpr);
return createValueHasWrongTypeError(valueExpr, value, messageText); const error = createValueHasWrongTypeError(valueExpr, value, messageText);
return {error, program};
} }

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {platform} from 'os';
import {ErrorCode, ngErrorCode} from '../../diagnostics'; import {ErrorCode, ngErrorCode} from '../../diagnostics';
import {absoluteFrom, absoluteFromSourceFile, getSourceFileOrError} from '../../file_system'; import {absoluteFrom, absoluteFromSourceFile, getSourceFileOrError} from '../../file_system';
import {runInEachFileSystem} from '../../file_system/testing'; import {runInEachFileSystem} from '../../file_system/testing';
@ -15,7 +13,7 @@ import {OptimizeFor} from '../api';
import {getClass, setup, TestDeclaration} from './test_utils'; import {getClass, setup, TestDeclaration} from './test_utils';
runInEachFileSystem(os => { runInEachFileSystem(() => {
describe('TemplateTypeChecker', () => { describe('TemplateTypeChecker', () => {
it('should batch diagnostic operations when requested in WholeProgram mode', () => { it('should batch diagnostic operations when requested in WholeProgram mode', () => {
const file1 = absoluteFrom('/file1.ts'); const file1 = absoluteFrom('/file1.ts');
@ -172,50 +170,47 @@ runInEachFileSystem(os => {
expect(diags[0].code).toBe(ngErrorCode(ErrorCode.INLINE_TCB_REQUIRED)); expect(diags[0].code).toBe(ngErrorCode(ErrorCode.INLINE_TCB_REQUIRED));
}); });
// These tests are currently disabled when running in Windows mode as the assertions involving it('should produce errors for components that require type constructor inlining', () => {
// the filename attached to the diagnostic are suffering from a case-sensitivity issue. const fileName = absoluteFrom('/main.ts');
if (os !== 'Windows' && platform() !== 'win32') { const dirFile = absoluteFrom('/dir.ts');
it('should produce errors for components that require type constructor inlining', () => { const {program, templateTypeChecker} = setup(
const fileName = absoluteFrom('/main.ts'); [
const dirFile = absoluteFrom('/dir.ts'); {
const {program, templateTypeChecker} = setup( fileName,
[ source: `export class Cmp {}`,
{ templates: {'Cmp': '<div dir></div>'},
fileName, declarations: [{
source: `export class Cmp {}`, name: 'TestDir',
templates: {'Cmp': '<div dir></div>'}, selector: '[dir]',
declarations: [{ file: dirFile,
name: 'TestDir', type: 'directive',
selector: '[dir]', isGeneric: true,
file: dirFile, }]
type: 'directive', },
isGeneric: true, {
}] fileName: dirFile,
}, source: `
{ // A non-exported interface used as a type bound for a generic directive causes
fileName: dirFile, // an inline type constructor to be required.
source: ` interface NotExported {}
// A non-exported interface used as a type bound for a generic directive causes export class TestDir<T extends NotExported> {}`,
// an inline type constructor to be required. templates: {},
interface NotExported {} }
export abstract class TestDir<T extends NotExported> {}`, ],
templates: {}, {inlining: false});
} const sf = getSourceFileOrError(program, fileName);
], const diags = templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram);
{inlining: false}); expect(diags.length).toBe(1);
const sf = getSourceFileOrError(program, fileName); expect(diags[0].code).toBe(ngErrorCode(ErrorCode.INLINE_TYPE_CTOR_REQUIRED));
const diags = templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram);
expect(diags.length).toBe(1);
expect(diags[0].code).toBe(ngErrorCode(ErrorCode.INLINE_TYPE_CTOR_REQUIRED));
// The relatedInformation of the diagnostic should point to the directive which required // The relatedInformation of the diagnostic should point to the directive which required
// the inline type constructor. // the inline type constructor.
expect(diags[0].relatedInformation).not.toBeUndefined(); const dirSf = getSourceFileOrError(program, dirFile);
expect(diags[0].relatedInformation!.length).toBe(1); expect(diags[0].relatedInformation).not.toBeUndefined();
expect(diags[0].relatedInformation![0].file).not.toBeUndefined(); expect(diags[0].relatedInformation!.length).toBe(1);
expect(absoluteFromSourceFile(diags[0].relatedInformation![0].file!)).toBe(dirFile); expect(diags[0].relatedInformation![0].file).not.toBeUndefined();
}); expect(absoluteFromSourceFile(diags[0].relatedInformation![0].file!)).toBe(dirSf.fileName);
} });
}); });
describe('template overrides', () => { describe('template overrides', () => {