text:
import * as ts from 'typescript';

import {ErrorCode, ngErrorCode} from '../../src/ngtsc/diagnostics';
import {absoluteFrom as _, getFileSystem, getSourceFileOrError} from '../../src/ngtsc/file_system';
import {runInEachFileSystem} from '../../src/ngtsc/file_system/testing';
import {expectCompleteReuse, loadStandardTestFiles} from '../../src/ngtsc/testing';

import {NgtscTestEnvironment} from './env';

const testFiles = loadStandardTestFiles();

runInEachFileSystem(() => {
  describe('ngtsc type checking', () => {
    let env!: NgtscTestEnvironment;

    beforeEach(() => {
      env = NgtscTestEnvironment.setup(testFiles);
      env.tsconfig({fullTemplateTypeCheck: true});
      env.write('node_modules/@angular/common/index.d.ts', `
import * as i0 from '@angular/core';

export declare class NgForOfContext<T, U extends i0.NgIterable<T> = i0.NgIterable<T>> {
  $implicit: T;
  count: number;
  readonly even: boolean;
  readonly first: boolean;
  index: number;
  readonly last: boolean;
  ngForOf: U;
  readonly odd: boolean;
  constructor($implicit: T, ngForOf: U, index: number, count: number);
}

export declare class IndexPipe {
  transform<T>(value: T[], index: number): T;

  static ɵpipe: i0.ɵPipeDefWithMeta<IndexPipe, 'index'>;
}

export declare class SlicePipe {
  transform<T>(value: ReadonlyArray<T>, start: number, end?: number): Array<T>;
  transform(value: string, start: number, end?: number): string;
  transform(value: null, start: number, end?: number): null;
  transform(value: undefined, start: number, end?: number): undefined;
  transform(value: any, start: number, end?: number): any;

  static ɵpipe: i0.ɵPipeDefWithMeta<SlicePipe, 'slice'>;
}

export declare class NgForOf<T, U extends i0.NgIterable<T> = i0.NgIterable<T>> implements DoCheck {
  ngForOf: (U & i0.NgIterable<T>) | undefined | null;
  ngForTemplate: TemplateRef<NgForOfContext<T, U>>;
  ngForTrackBy: TrackByFunction<T>;
  constructor(_viewContainer: ViewContainerRef, _template: TemplateRef<NgForOfContext<T, U>>, _differs: IterableDiffers);
  ngDoCheck(): void;
  static ngTemplateContextGuard<T, U extends i0.NgIterable<T>>(dir: NgForOf<T, U>, ctx: any): ctx is NgForOfContext<T, U>;
  static ɵdir: i0.ɵɵDirectiveDefWithMeta<NgForOf<any>, '[ngFor][ngForOf]', never, {'ngForOf': 'ngForOf'}, {}, never>;
}

export declare class NgIf<T = unknown> {
  ngIf: T;
  ngIfElse: TemplateRef<NgIfContext<T>> | null;
  ngIfThen: TemplateRef<NgIfContext<T>> | null;
  constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext<T>>);
  static ngTemplateGuard_ngIf: 'binding';
  static ngTemplateContextGuard<T>(dir: NgIf<T>, ctx: any): ctx is NgIfContext<Exclude<T, false | 0 | "" | null | undefined>>;
  static ɵdir: i0.ɵɵDirectiveDefWithMeta<NgIf<any>, '[ngIf]', never, {'ngIf': 'ngIf'}, {}, never>;
}

export declare class NgIfContext<T = unknown> {
  $implicit: T;
  ngIf: T;
}

export declare class CommonModule {
  static ɵmod: i0.ɵɵNgModuleDefWithMeta<CommonModule, [typeof NgIf, typeof NgForOf, typeof IndexPipe, typeof SlicePipe], never, [typeof NgIf, typeof NgForOf, typeof IndexPipe, typeof SlicePipe]>;
}
`);
      env.write('node_modules/@angular/animations/index.d.ts', `
export declare class AnimationEvent {
  element: any;
}
`);
    });

    it('should check a simple component', () => {
      env.write('test.ts', `
    import {Component, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: 'I am a simple template with no type info',
    })
    class TestCmp {}

    @NgModule({
      declarations: [TestCmp],
    })
    class Module {}
    `);

      env.driveMain();
    });

    it('should check regular attributes that are directive inputs', () => {
      env.tsconfig(
          {fullTemplateTypeCheck: true, strictInputTypes: true, strictAttributeTypes: true});
      env.write('test.ts', `
        import {Component, Directive, NgModule, Input} from '@angular/core';

        @Component({
          selector: 'test',
          template: '

',
        })
        class TestCmp {}

        @Directive({selector: '[dir]'})
        class TestDir {
          @Input() foo: number;
        }

        @NgModule({
          declarations: [TestCmp, TestDir],
        })
        class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(1);
      expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
      // The reported error code should be in the TS error space, not a -99 "NG" code.
      expect(diags[0].code).toBeGreaterThan(0);
    });

    it('should produce diagnostics when mapping to multiple fields and bound types are incorrect',
       () => {
         env.tsconfig(
             {fullTemplateTypeCheck: true, strictInputTypes: true, strictAttributeTypes: true});
         env.write('test.ts', `
        import {Component, Directive, NgModule, Input} from '@angular/core';

        @Component({
          selector: 'test',
          template: '

',
        })
        class TestCmp {}

        @Directive({selector: '[dir]'})
        class TestDir {
          @Input('foo') foo1: number;
          @Input('foo') foo2: number;
        }

        @NgModule({
          declarations: [TestCmp, TestDir],
        })
        class Module {}
      `);

         const diags = env.driveDiagnostics();
         expect(diags.length).toBe(2);
         expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
         expect(diags[1].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
       });

    it('should support inputs and outputs with names that are not JavaScript identifiers', () => {
      env.tsconfig(
          {fullTemplateTypeCheck: true, strictInputTypes: true, strictOutputEventTypes: true});
      env.write('test.ts', `
        import {Component, Directive, NgModule, EventEmitter} from '@angular/core';

        @Component({
          selector: 'test',
          template: '

',
        })
        class TestCmp {
          handleEvent(event: number): void {}
        }

        @Directive({
          selector: '[dir]',
          inputs: ['some-input.xs'],
          outputs: ['some-output'],
        })
        class TestDir {
          'some-input.xs': string;
          'some-output': EventEmitter<string>;
        }

        @NgModule({
          declarations: [TestCmp, TestDir],
        })
        class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(2);
      expect(diags[0].messageText).toEqual(`Type 'number' is not assignable to type 'string'.`);
      expect(diags[1].messageText)
          .toEqual(`Argument of type 'string' is not assignable to parameter of type 'number'.`);
    });

    it('should support one input property mapping to multiple fields', () => {
      env.write('test.ts', `
        import {Component, Directive, Input, NgModule} from '@angular/core';

        @Directive({
          selector: '[dir]',
        })
        export class Dir {

          @Input('propertyName') fieldA!: string;
          @Input('propertyName') fieldB!: string;
        }

        @Component({
          selector: 'test-cmp',
          template: '

',
        })
        export class Cmp {}

        @NgModule({declarations: [Dir, Cmp]})
        export class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(0);
    });

    it('should check event bindings', () => {
      env.tsconfig({fullTemplateTypeCheck: true, strictOutputEventTypes: true});
      env.write('test.ts', `
        import {Component, Directive, EventEmitter, NgModule, Output} from '@angular/core';

        @Component({
          selector: 'test',
          template: '

',
        })
        class TestCmp {
          update(data: string) {}
        }

        @Directive({selector: '[dir]'})
        class TestDir {
          @Output() update = new EventEmitter<number>();
        }

        @NgModule({
          declarations: [TestCmp, TestDir],
        })
        class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(3);
      expect(diags[0].messageText)
          .toEqual(`Argument of type 'number' is not assignable to parameter of type 'string'.`);
      expect(diags[1].messageText)
          .toEqual(`Property 'updated' does not exist on type 'TestCmp'. Did you mean 'update'?`);
      // Disabled because `checkTypeOfDomEvents` is disabled by default
      // expect(diags[2].messageText)
      //     .toEqual(
      //         `Argument of type 'FocusEvent' is not assignable to parameter of type 'string'.`);
      expect(diags[2].messageText).toEqual(`Property 'focused' does not exist on type 'TestCmp'.`);
    });

    // https://github.com/angular/angular/issues/35073
    it('ngIf should narrow on output types', () => {
      env.tsconfig({strictTemplates: true});
      env.write('test.ts', `
        import {CommonModule} from '@angular/common';
        import {Component, NgModule} from '@angular/core';

        @Component({
          selector: 'test',
          template: '

',
        })
        class TestCmp {
          person?: { name: string; };
          handleEvent(name: string) {}
        }

        @NgModule({
          imports: [CommonModule],
          declarations: [TestCmp],
        })
        class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(0);
    });

    it('ngIf should narrow on output types across multiple guards', () => {
      env.tsconfig({strictTemplates: true});
      env.write('test.ts', `
        import {CommonModule} from '@angular/common';
        import {Component, NgModule} from '@angular/core';

        @Component({
          selector: 'test',
          template: '

',
        })
        class TestCmp {
          person?: { name?: string; };
          handleEvent(name: string) {}
        }

        @NgModule({
          imports: [CommonModule],
          declarations: [TestCmp],
        })
        class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(0);
    });

    it('should support a directive being used in its own input expression', () => {
      env.tsconfig({strictTemplates: true});
      env.write('test.ts', `
        import {Component, Directive, NgModule, Input} from '@angular/core';

        @Component({
          selector: 'test',
          template: '<target-cmp #ref [foo]="ref.bar"></target-cmp>',
        })
        export class TestCmp {}

        @Component({template: '', selector: 'target-cmp'})
        export class TargetCmp {
          readonly bar = 'test';
          @Input() foo: string;
        }

        @NgModule({
          declarations: [TestCmp, TargetCmp],
        })
        export class Module {}
      `);
      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(0);
    });

    describe('strictInputTypes', () => {
      beforeEach(() => {
        env.write('test.ts', `
          import {Component, Directive, NgModule, Input} from '@angular/core';

          @Component({
            selector: 'test',
            template: '

',
          })
          class TestCmp {}

          @Directive({selector: '[dir]'})
          class TestDir {
            @Input() foo: string;
          }

          @NgModule({
            declarations: [TestCmp, TestDir],
          })
          class Module {}
        `);
      });

      it('should check expressions and their type when enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect(diags[0].messageText).toEqual(`Type 'boolean' is not assignable to type 'string'.`);
        expect(diags[1].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });

      it('should check expressions and their type when overall strictness is enabled', () => {
        env.tsconfig({strictTemplates: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect(diags[0].messageText).toEqual(`Type 'boolean' is not assignable to type 'string'.`);
        expect(diags[1].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });

      it('should check expressions but not their type when not enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });
    });

    describe('strictNullInputTypes', () => {
      beforeEach(() => {
        env.write('test.ts', `
          import {Component, Directive, NgModule, Input} from '@angular/core';

          @Component({
            selector: 'test',
            template: '

',
          })
          class TestCmp {
            nullable: boolean | null | undefined;
          }

          @Directive({selector: '[dir]'})
          class TestDir {
            @Input() foo: boolean;
          }

          @NgModule({
            declarations: [TestCmp, TestDir],
          })
          class Module {}
        `);
      });

      it('should check expressions and their nullability when enabled', () => {
        env.tsconfig(
            {fullTemplateTypeCheck: true, strictInputTypes: true, strictNullInputTypes: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect((diags[0].messageText as ts.DiagnosticMessageChain).messageText)
            .toEqual(`Type 'boolean | null | undefined' is not assignable to type 'boolean'.`);
        expect(diags[1].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });

      it('should check expressions and their nullability when overall strictness is enabled',
         () => {
           env.tsconfig({strictTemplates: true});

           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(2);
           expect((diags[0].messageText as ts.DiagnosticMessageChain).messageText)
               .toEqual(`Type 'boolean | null | undefined' is not assignable to type 'boolean'.`);
           expect(diags[1].messageText)
               .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
         });

      it('should check expressions but not their nullability when not enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });
    });

    describe('strictSafeNavigationTypes', () => {
      beforeEach(() => {
        env.write('test.ts', `
          import {Component, Directive, NgModule, Input} from '@angular/core';

          @Component({
            selector: 'test',
            template: '

',
          })
          class TestCmp {
            user?: {isMember: boolean};
          }

          @Directive({selector: '[dir]'})
          class TestDir {
            @Input() foo: boolean;
          }

          @NgModule({
            declarations: [TestCmp, TestDir],
          })
          class Module {}
        `);
      });

      it('should infer result type for safe navigation expressions when enabled', () => {
        env.tsconfig({
          fullTemplateTypeCheck: true,
          strictInputTypes: true,
          strictNullInputTypes: true,
          strictSafeNavigationTypes: true
        });

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect((diags[0].messageText as ts.DiagnosticMessageChain).messageText)
            .toEqual(`Type 'boolean | undefined' is not assignable to type 'boolean'.`);
        expect(diags[1].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });

      it('should infer result type for safe navigation expressions when overall strictness is enabled',
         () => {
           env.tsconfig({strictTemplates: true});

           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(2);
           expect((diags[0].messageText as ts.DiagnosticMessageChain).messageText)
               .toEqual(`Type 'boolean | undefined' is not assignable to type 'boolean'.`);
           expect(diags[1].messageText)
               .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
         });

      it('should not infer result type for safe navigation expressions when not enabled', () => {
        env.tsconfig({
          fullTemplateTypeCheck: true,
          strictInputTypes: true,
        });

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });
    });

    describe('strictOutputEventTypes', () => {
      beforeEach(() => {
        env.write('test.ts', `
          import {Component, Directive, EventEmitter, NgModule, Output} from '@angular/core';

          @Component({
            selector: 'test',
            template: '

',
          })
          class TestCmp {
            update(data: string) {}
          }

          @Directive({selector: '[dir]'})
          class TestDir {
            @Output() update = new EventEmitter<number>();
          }

          @NgModule({
            declarations: [TestCmp, TestDir],
          })
          class Module {}
        `);
      });

      it('should expressions and infer type of $event when enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true, strictOutputEventTypes: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
        expect(diags[1].messageText)
            .toEqual(`Argument of type 'number' is not assignable to parameter of type 'string'.`);
      });

      it('should expressions and infer type of $event when overall strictness is enabled', () => {
        env.tsconfig({strictTemplates: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
        expect(diags[1].messageText)
            .toEqual(`Argument of type 'number' is not assignable to parameter of type 'string'.`);
      });

      it('should check expressions but not infer type of $event when not enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });
    });

    describe('strictOutputEventTypes and animation event bindings', () => {
      beforeEach(() => {
        env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';

          @Component({
            selector: 'test',
            template: '

',
          })
          class TestCmp {
            update(data: string) {}
          }

          @NgModule({
            declarations: [TestCmp],
          })
          class Module {}
        `);
      });

      it('should check expressions and let $event be of type AnimationEvent when enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true, strictOutputEventTypes: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
        expect(diags[1].messageText)
            .toEqual(
                `Argument of type 'AnimationEvent' is not assignable to parameter of type 'string'.`);
      });

      it('should check expressions and let $event be of type AnimationEvent when overall strictness is enabled',
         () => {
           env.tsconfig({strictTemplates: true});

           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(2);
           expect(diags[0].messageText)
               .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
           expect(diags[1].messageText)
               .toEqual(
                   `Argument of type 'AnimationEvent' is not assignable to parameter of type 'string'.`);
         });

      it('should check expressions and let $event be of type any when not enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });
    });

    describe('strictDomLocalRefTypes', () => {
      beforeEach(() => {
        env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';

          @Component({
            selector: 'test',
            template: '<input #ref>{{ref.does_not_exist}}',
          })
          class TestCmp {}

          @NgModule({
            declarations: [TestCmp],
          })
          class Module {}
        `);
      });

      it('should infer the type of DOM references when enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true, strictDomLocalRefTypes: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toEqual(`Property 'does_not_exist' does not exist on type 'HTMLInputElement'.`);
      });

      it('should infer the type of DOM references when overall strictness is enabled', () => {
        env.tsconfig({strictTemplates: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toEqual(`Property 'does_not_exist' does not exist on type 'HTMLInputElement'.`);
      });

      it('should let the type of DOM references be any when not enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(0);
      });
    });

    describe('strictAttributeTypes', () => {
      beforeEach(() => {
        env.write('test.ts', `
          import {Component, Directive, NgModule, Input} from '@angular/core';

          @Component({
            selector: 'test',
            template: '<textarea dir disabled cols="3"></textarea>',
          })
          class TestCmp {}

          @Directive({selector: '[dir]'})
          class TestDir {
            @Input() disabled: boolean;
            @Input() cols: number;
          }

          @NgModule({
            declarations: [TestCmp, TestDir],
          })
          class Module {}
        `);
      });

      it('should produce an error for text attributes when enabled', () => {
        env.tsconfig(
            {fullTemplateTypeCheck: true, strictInputTypes: true, strictAttributeTypes: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'boolean'.`);
        expect(diags[1].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
      });

      it('should produce an error for text attributes when overall strictness is enabled', () => {
        env.tsconfig({strictTemplates: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'boolean'.`);
        expect(diags[1].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
      });

      it('should not produce an error for text attributes when not enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(0);
      });
    });

    describe('strictDomEventTypes', () => {
      beforeEach(() => {
        env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';

          @Component({
            selector: 'test',
            template: '

',
          })
          class TestCmp {
            update(data: string) {}
          }

          @NgModule({
            declarations: [TestCmp],
          })
          class Module {}
        `);
      });

      it('should check expressions and infer type of $event when enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true, strictDomEventTypes: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(2);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
        expect(diags[1].messageText)
            .toEqual(
                `Argument of type 'FocusEvent' is not assignable to parameter of type 'string'.`);
      });

      it('should check expressions and infer type of $event when overall strictness is enabled',
         () => {
           env.tsconfig({strictTemplates: true});

           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(2);
           expect(diags[0].messageText)
               .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
           expect(diags[1].messageText)
               .toEqual(
                   `Argument of type 'FocusEvent' is not assignable to parameter of type 'string'.`);
         });

      it('should check expressions but not infer type of $event when not enabled', () => {
        env.tsconfig({fullTemplateTypeCheck: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toEqual(`Property 'invalid' does not exist on type 'TestCmp'.`);
      });
    });

    it('should check basic usage of NgIf', () => {
      env.write('test.ts', `
    import {CommonModule} from '@angular/common';
    import {Component, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: '
{{user.name}}
',
    })
    class TestCmp {
      user: {name: string}|null;
    }

    @NgModule({
      declarations: [TestCmp],
      imports: [CommonModule],
    })
    class Module {}
    `);

      env.driveMain();
    });

    it('should check usage of NgIf with explicit non-null guard', () => {
      env.write('test.ts', `
    import {CommonModule} from '@angular/common';
    import {Component, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: '
{{user.name}}
',
    })
    class TestCmp {
      user: {name: string}|null;
    }

    @NgModule({
      declarations: [TestCmp],
      imports: [CommonModule],
    })
    class Module {}
    `);

      env.driveMain();
    });

    it('should check usage of NgIf when using "let" to capture $implicit context variable', () => {
      env.tsconfig({strictTemplates: true});
      env.write('test.ts', `
    import {CommonModule} from '@angular/common';
    import {Component, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: '
{{u.name}}
',
    })
    class TestCmp {
      user: {name: string}|null|false;
    }

    @NgModule({
      declarations: [TestCmp],
      imports: [CommonModule],
    })
    class Module {}
    `);

      env.driveMain();
    });

    it('should check usage of NgIf when using "as" to capture `ngIf` context variable', () => {
      env.tsconfig({strictTemplates: true});
      env.write('test.ts', `
    import {CommonModule} from '@angular/common';
    import {Component, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: '
{{u.name}}
',
    })
    class TestCmp {
      user: {name: string}|null|false;
    }

    @NgModule({
      declarations: [TestCmp],
      imports: [CommonModule],
    })
    class Module {}
    `);

      env.driveMain();
    });

    it('should check basic usage of NgFor', () => {
      env.write('test.ts', `
    import {CommonModule} from '@angular/common';
    import {Component, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: '
{{user.name}}
',
    })
    class TestCmp {
      users: {name: string}[];
    }

    @NgModule({
      declarations: [TestCmp],
      imports: [CommonModule],
    })
    class Module {}
    `);

      env.driveMain();
    });

    it('should report an error inside the NgFor template', () => {
      env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
      env.write('test.ts', `
    import {CommonModule} from '@angular/common';
    import {Component, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: '
{{user.does_not_exist}}
',
    })
    export class TestCmp {
      users: {name: string}[];
    }

    @NgModule({
      declarations: [TestCmp],
      imports: [CommonModule],
    })
    export class Module {}
    `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(1);
      expect(diags[0].messageText)
          .toEqual(`Property 'does_not_exist' does not exist on type '{ name: string; }'.`);
      expect(getSourceCodeForDiagnostic(diags[0])).toBe('does_not_exist');
    });

    it('should accept an NgFor iteration over an any-typed value', () => {
      env.write('test.ts', `
    import {CommonModule} from '@angular/common';
    import {Component, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: '
{{user.name}}
',
    })
    export class TestCmp {
      users: any;
    }

    @NgModule({
      declarations: [TestCmp],
      imports: [CommonModule],
    })
    export class Module {}
    `);

      env.driveMain();
    });

    it('should accept NgFor iteration over a QueryList', () => {
      env.tsconfig({strictTemplates: true});
      env.write('test.ts', `
        import {CommonModule} from '@angular/common';
        import {Component, NgModule, QueryList} from '@angular/core';

        @Component({
          selector: 'test',
          template: '
{{user.name}}
',
        })
        class TestCmp {
          users!: QueryList<{name: string}>;
        }

        @NgModule({
          declarations: [TestCmp],
          imports: [CommonModule],
        })
        class Module {}
    `);

      env.driveMain();
    });

    it('should infer the context of NgFor', () => {
      env.tsconfig({strictTemplates: true});
      env.write('test.ts', `
        import {CommonModule} from '@angular/common';
        import {Component, NgModule} from '@angular/core';

        @Component({
          selector: 'test',
          template: '
{{all.length}}
',
        })
        class TestCmp {
          users: {name: string}[];
        }

        @NgModule({
          declarations: [TestCmp],
          imports: [CommonModule],
        })
        class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(0);
    });

    it('should allow the implicit value of an NgFor to be invoked', () => {
      env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
      env.write('test.ts', `
        import {CommonModule} from '@angular/common';
        import {Component, NgModule} from '@angular/core';

        @Component({
          selector: 'test',
          template: '
{{fn()}}
',
        })
        class TestCmp {
          functions = [() => 1, () => 2];
        }

        @NgModule({
          declarations: [TestCmp],
          imports: [CommonModule],
        })
        class Module {}
    `);

      env.driveMain();
    });

    it('should infer the context of NgIf', () => {
      env.tsconfig({strictTemplates: true});
      env.write('test.ts', `
        import {CommonModule} from '@angular/common';
        import {Component, NgModule} from '@angular/core';
        @Component({
          selector: 'test',
          template: '
{{user.nonExistingProp}}
',
        })
        class TestCmp {
          getUser(): {name: string} {
            return {name: 'frodo'};
          }
        }
        @NgModule({
          declarations: [TestCmp],
          imports: [CommonModule],
        })
        class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(1);
      expect(diags[0].messageText)
          .toBe(`Property 'nonExistingProp' does not exist on type '{ name: string; }'.`);
    });

    it('should report an error with an unknown local ref target', () => {
      env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';

        @Component({
          selector: 'test',
          template: '

',
        })
        class TestCmp {}

        @NgModule({
          declarations: [TestCmp],
        })
        class Module {}
      `);
      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(1);
      expect(diags[0].messageText).toBe(`No directive found with exportAs 'unknownTarget'.`);
      expect(getSourceCodeForDiagnostic(diags[0])).toBe('unknownTarget');
    });

    it('should treat an unknown local ref target as type any', () => {
      env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';

        @Component({
          selector: 'test',
          template: '
{{ use(ref) }}
',
        })
        class TestCmp {
          use(ref: string): string { return ref; }
        }

        @NgModule({
          declarations: [TestCmp],
        })
        class Module {}
      `);
      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(1);
      expect(diags[0].messageText).toBe(`No directive found with exportAs 'unknownTarget'.`);
      expect(getSourceCodeForDiagnostic(diags[0])).toBe('unknownTarget');
    });

    it('should report an error with an unknown pipe', () => {
      env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';

        @Component({
          selector: 'test',
          template: '{{expr | unknown}}',
        })
        class TestCmp {
          expr = 3;
        }

        @NgModule({
          declarations: [TestCmp],
        })
        class Module {}
      `);
      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(1);
      expect(diags[0].messageText).toBe(`No pipe found with name 'unknown'.`);
      expect(getSourceCodeForDiagnostic(diags[0])).toBe('unknown');
    });

    it('should report an error with an unknown pipe even if `fullTemplateTypeCheck` is disabled',
       () => {
         env.tsconfig({fullTemplateTypeCheck: false});
         env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';

          @Component({
            selector: 'test',
            template: '{{expr | unknown}}',
          })
          class TestCmp {
            expr = 3;
          }

          @NgModule({
            declarations: [TestCmp],
          })
          class Module {}
        `);
         const diags = env.driveDiagnostics();
         expect(diags.length).toBe(1);
         expect(diags[0].messageText).toBe(`No pipe found with name 'unknown'.`);
         expect(getSourceCodeForDiagnostic(diags[0])).toBe('unknown');
       });

    it('should report an error with pipe bindings', () => {
      env.write('test.ts', `
    import {CommonModule} from '@angular/common';
    import {Component, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: \`
        checking the input type to the pipe:
        {{user | index: 1}}

        checking the return type of the pipe:
        {{(users | index: 1).does_not_exist}}

        checking the argument type:
        {{users | index: 'test'}}

        checking the argument count:
        {{users | index: 1:2}}
      \`
    })
    class TestCmp {
      user: {name: string};
      users: {name: string}[];
    }

    @NgModule({
      declarations: [TestCmp],
      imports: [CommonModule],
    })
    class Module {}
    `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(4);

      const allErrors = [
        `'does_not_exist' does not exist on type '{ name: string; }'`,
        `Expected 2 arguments, but got 3.`,
        `Argument of type 'string' is not assignable to parameter of type 'number'`,
        `Argument of type '{ name: string; }' is not assignable to parameter of type 'unknown[]'`,
      ];

      for (const error of allErrors) {
        if (!diags.some(
                diag =>
                    ts.flattenDiagnosticMessageText(diag.messageText, '').indexOf(error) > -1)) {
          fail(`Expected a diagnostic message with text: ${error}`);
        }
      }
    });

    it('should constrain types using type parameter bounds', () => {
      env.tsconfig(
          {fullTemplateTypeCheck: true, strictInputTypes: true, strictContextGenerics: true});
      env.write('test.ts', `
    import {CommonModule} from '@angular/common';
    import {Component, Input, NgModule} from '@angular/core';

    @Component({
      selector: 'test',
      template: '
{{user.does_not_exist}}
',
    })
    class TestCmp<T extends {name: string}> {
      @Input() users: T[];
    }

    @NgModule({
      declarations: [TestCmp],
      imports: [CommonModule],
    })
    class Module {}
    `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(1);
      expect(diags[0].messageText).toEqual(`Property 'does_not_exist' does not exist on type 'T'.`);
      expect(getSourceCodeForDiagnostic(diags[0])).toBe('does_not_exist');
    });

    describe('microsyntax variables', () => {
      beforeEach(() => {
        // Use the same template for both tests
        env.write('test.ts', `
          import {CommonModule} from '@angular/common';
          import {Component, NgModule} from '@angular/core';

          @Component({
            selector: 'test',
            template: \`

              {{foo.name}} of {{foos.nonExistingProp}}

            \`,
          })
          export class TestCmp {
            foos: {name: string}[];
          }

          @NgModule({
            declarations: [TestCmp],
            imports: [CommonModule],
          })
          export class Module {}
        `);
      });

      it('should be treated as \'any\' without strictTemplates', () => {
        env.tsconfig({fullTemplateTypeCheck: true, strictTemplates: false});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(0);
      });

      it('should be correctly inferred under strictTemplates', () => {
        env.tsconfig({strictTemplates: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toBe(`Property 'nonExistingProp' does not exist on type '{ name: string; }[]'.`);
      });
    });

    it('should properly type-check inherited directives', () => {
      env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
      env.write('test.ts', `
    import {Component, Directive, Input, NgModule} from '@angular/core';

    @Directive()
    class AbstractDir {
      @Input() fromAbstract!: number;
    }

    @Directive({
      selector: '[base]',
    })
    class BaseDir extends AbstractDir {
      @Input() fromBase!: string;
    }

    @Directive({
      selector: '[child]',
    })
    class ChildDir extends BaseDir {
      @Input() fromChild!: boolean;
    }

    @Component({
      selector: 'test',
      template: '

',
    })
    class TestCmp {}

    @NgModule({
      declarations: [TestCmp, ChildDir],
    })
    class Module {}
    `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(3);
      expect(diags[0].messageText).toBe(`Type 'boolean' is not assignable to type 'number'.`);
      expect(getSourceCodeForDiagnostic(diags[0])).toEqual('fromAbstract');
      expect(diags[1].messageText).toBe(`Type 'number' is not assignable to type 'string'.`);
      expect(getSourceCodeForDiagnostic(diags[1])).toEqual('fromBase');
      expect(diags[2].messageText).toBe(`Type 'number' is not assignable to type 'boolean'.`);
      expect(getSourceCodeForDiagnostic(diags[2])).toEqual('fromChild');
    });

    it('should properly type-check inherited directives from external libraries', () => {
      env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});

      env.write('node_modules/external/index.d.ts', `
        import * as i0 from '@angular/core';

        export declare class AbstractDir {
          fromAbstract: number;

          static ɵdir: i0.ɵɵDirectiveDefWithMeta<AbstractDir, never, never, {'fromAbstract': 'fromAbstract'}, never, never>;
        }

        export declare class BaseDir extends AbstractDir {
          fromBase: string;

          static ɵdir: i0.ɵɵDirectiveDefWithMeta<BaseDir, '[base]', never, {'fromBase': 'fromBase'}, never, never>;
        }

        export declare class ExternalModule {
          static ɵmod: i0.ɵɵNgModuleDefWithMeta<ExternalModule, [typeof BaseDir], never, [typeof BaseDir]>;
        }
      `);

      env.write('test.ts', `
        import {Component, Directive, Input, NgModule} from '@angular/core';
        import {BaseDir, ExternalModule} from 'external';

        @Directive({
          selector: '[child]',
        })
        class ChildDir extends BaseDir {
          @Input() fromChild!: boolean;
        }

        @Component({
          selector: 'test',
          template: '

',
        })
        class TestCmp {}

        @NgModule({
          declarations: [TestCmp, ChildDir],
          imports: [ExternalModule],
        })
        class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(3);
      expect(diags[0].messageText).toBe(`Type 'boolean' is not assignable to type 'number'.`);
      expect(getSourceCodeForDiagnostic(diags[0])).toEqual('fromAbstract');
      expect(diags[1].messageText).toBe(`Type 'number' is not assignable to type 'string'.`);
      expect(getSourceCodeForDiagnostic(diags[1])).toEqual('fromBase');
      expect(diags[2].messageText).toBe(`Type 'number' is not assignable to type 'boolean'.`);
      expect(getSourceCodeForDiagnostic(diags[2])).toEqual('fromChild');
    });

    it('should detect an illegal write to a template variable', () => {
      env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        import {CommonModule} from '@angular/common';

        @Component({
          selector: 'test',
          template: \`

              <button (click)="y = !y">Toggle</button>

          \`,
        })
        export class TestCmp {
          x!: boolean;
        }

        @NgModule({
          declarations: [TestCmp],
          imports: [CommonModule],
        })
        export class Module {}
      `);
      const diags = env.driveDiagnostics();
      expect(diags.length).toEqual(1);
      expect(getSourceCodeForDiagnostic(diags[0])).toEqual('y = !y');
    });

    it('should detect a duplicate variable declaration', () => {
      env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        import {CommonModule} from '@angular/common';

        @Component({
          selector: 'test',
          template: \`

              {{i}}

          \`,
        })
        export class TestCmp {
          items!: string[];
        }

        @NgModule({
          declarations: [TestCmp],
          imports: [CommonModule],
        })
        export class Module {}
      `);

      const diags = env.driveDiagnostics();
      expect(diags.length).toEqual(1);
      expect(diags[0].code).toEqual(ngErrorCode(ErrorCode.DUPLICATE_VARIABLE_DECLARATION));
      expect(getSourceCodeForDiagnostic(diags[0])).toContain('let i = index');
    });

    it('should still type-check when fileToModuleName aliasing is enabled, but alias exports are not in the .d.ts file',
       () => {
         // The template type-checking file imports directives/pipes in order to type-check their
         // usage. When `UnifiedModulesHost` aliasing is enabled, these imports would ordinarily use
         // aliased values. However, such aliases are not guaranteed to exist in the .d.ts files,
         // and so feeding such imports back into TypeScript does not work.
         //
         // Instead, direct imports should be used within template type-checking code. This test
         // verifies that template type-checking is able to cope with such a scenario where
         // aliasing is enabled and alias re-exports don't exist in .d.ts files.
         env.tsconfig({
           // Setting this private flag turns on aliasing.
           '_useHostForImportGeneration': true,
           // Because the tsconfig is overridden, template type-checking needs to be turned back on
           // explicitly as well.
           'fullTemplateTypeCheck': true,
         });

         // 'alpha' declares the directive which will ultimately be imported.
         env.write('alpha.d.ts', `
          import {ɵɵDirectiveDefWithMeta, ɵɵNgModuleDefWithMeta} from '@angular/core';

          export declare class ExternalDir {
            input: string;
            static ɵdir: ɵɵDirectiveDefWithMeta<ExternalDir, '[test]', never, { 'input': "input" }, never, never>;
          }

          export declare class AlphaModule {
            static ɵmod: ɵɵNgModuleDefWithMeta<AlphaModule, [typeof ExternalDir], never, [typeof ExternalDir]>;
          }
         `);

         // 'beta' re-exports AlphaModule from alpha.
         env.write('beta.d.ts', `
          import {ɵɵNgModuleDefWithMeta} from '@angular/core';
          import {AlphaModule} from './alpha';

          export declare class BetaModule {
            static ɵmod: ɵɵNgModuleDefWithMeta<BetaModule, never, never, [typeof AlphaModule]>;
          }
         `);

         // The application imports BetaModule from beta, gaining visibility of ExternalDir from
         // alpha.
         env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';
          import {BetaModule} from './beta';

          @Component({
            selector: 'cmp',
            template: '

',
          })
          export class Cmp {}

          @NgModule({
            declarations: [Cmp],
            imports: [BetaModule],
          })
          export class Module {}
         `);

         const diags = env.driveDiagnostics();
         expect(diags.length).toBe(0);
       });

    describe('input coercion', () => {
      beforeEach(() => {
        env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
        env.write('node_modules/@angular/material/index.d.ts', `
        import * as i0 from '@angular/core';

        export declare class MatInput {
          value: string;
          static ɵdir: i0.ɵɵDirectiveDefWithMeta<MatInput, '[matInput]', never, {'value': 'value'}, {}, never>;
          static ngAcceptInputType_value: string|number;
        }

        export declare class MatInputModule {
          static ɵmod: i0.ɵɵNgModuleDefWithMeta<MatInputModule, [typeof MatInput], never, [typeof MatInput]>;
        }
        `);
      });

      it('should coerce an input using a coercion function if provided', () => {
        env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';
          import {MatInputModule} from '@angular/material';

          @Component({
            selector: 'blah',
            template: '<input matInput [value]="someNumber">',
          })
          export class FooCmp {
            someNumber = 3;
          }

          @NgModule({
            declarations: [FooCmp],
            imports: [MatInputModule],
          })
          export class FooModule {}
        `);
        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(0);
      });

      it('should apply coercion members of base classes', () => {
        env.write('test.ts', `
          import {Component, Directive, Input, NgModule} from '@angular/core';

          @Directive()
          export class BaseDir {
            @Input()
            value: string;

            static ngAcceptInputType_value: string|number;
          }

          @Directive({
            selector: '[dir]',
          })
          export class MyDir extends BaseDir {}

          @Component({
            selector: 'blah',
            template: '<input dir [value]="someNumber">',
          })
          export class FooCmp {
            someNumber = 3;
          }

          @NgModule({
            declarations: [MyDir, FooCmp],
          })
          export class FooModule {}
        `);
        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(0);
      });

      it('should give an error if the binding expression type is not accepted by the coercion function',
         () => {
           env.write('test.ts', `
            import {Component, NgModule, Input, Directive} from '@angular/core';
            import {MatInputModule} from '@angular/material';

            @Component({
              selector: 'blah',
              template: '<input matInput [value]="invalidType">',
            })
            export class FooCmp {
              invalidType = true;
            }

            @NgModule({
              declarations: [FooCmp],
              imports: [MatInputModule],
            })
            export class FooModule {}
        `);
           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(1);
           expect(diags[0].messageText)
               .toBe(`Type 'boolean' is not assignable to type 'string | number'.`);
         });

      it('should give an error for undefined bindings into regular inputs when coercion members are present',
         () => {
           env.tsconfig({strictTemplates: true});
           env.write('test.ts', `
            import {Component, Directive, NgModule, Input} from '@angular/core';

            @Component({
              selector: 'blah',
              template: '<input dir [regular]="undefined" [coerced]="1">',
            })
            export class FooCmp {
              invalidType = true;
            }

            @Directive({selector: '[dir]'})
            export class CoercionDir {
              @Input() regular: string;
              @Input() coerced: boolean;

              static ngAcceptInputType_coerced: boolean|number;
            }

            @NgModule({
              declarations: [FooCmp, CoercionDir],
            })
            export class FooModule {}
        `);
           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(1);
           expect(diags[0].messageText)
               .toBe(`Type 'undefined' is not assignable to type 'string'.`);
         });
    });

    describe('restricted inputs', () => {
      const directiveDeclaration = `
            @Directive({selector: '[dir]'})
            export class TestDir {
              @Input()
              protected protectedField!: string;
              @Input()
              private privateField!: string;
              @Input()
              readonly readonlyField!: string;
            }
      `;

      const correctTypeInputsToRestrictedFields = `
            import {Component, NgModule, Input, Directive} from '@angular/core';

            @Component({
              selector: 'blah',
              template: '

',
            })
            export class FooCmp {
              value = "value";
            }

            ${directiveDeclaration}

            @NgModule({
              declarations: [FooCmp, TestDir],
            })
            export class FooModule {}
        `;

      const correctInputsToRestrictedFieldsFromBaseClass = `
            import {Component, NgModule, Input, Directive} from '@angular/core';

            @Component({
              selector: 'blah',
              template: '

',
            })
            export class FooCmp {
              value = "value";
            }

            ${directiveDeclaration}

            @Directive({selector: '[child-dir]'})
            export class ChildDir extends TestDir {
            }

            @NgModule({
              declarations: [FooCmp, ChildDir],
            })
            export class FooModule {}
        `;
      describe('with strictInputAccessModifiers', () => {
        beforeEach(() => {
          env.tsconfig({
            fullTemplateTypeCheck: true,
            strictInputTypes: true,
            strictInputAccessModifiers: true
          });
        });

        it('should produce diagnostics for inputs which assign to readonly, private, and protected fields',
           () => {
             env.write('test.ts', correctTypeInputsToRestrictedFields);
             expectIllegalAssignmentErrors(env.driveDiagnostics());
           });

        it('should produce diagnostics for inputs which assign to readonly, private, and protected fields inherited from a base class',
           () => {
             env.write('test.ts', correctInputsToRestrictedFieldsFromBaseClass);
             expectIllegalAssignmentErrors(env.driveDiagnostics());
           });

        function expectIllegalAssignmentErrors(diags: ReadonlyArray<ts.Diagnostic>) {
          expect(diags.length).toBe(3);
          const actualMessages = diags.map(d => d.messageText).sort();
          const expectedMessages = [
            `Property 'protectedField' is protected and only accessible within class 'TestDir' and its subclasses.`,
            `Property 'privateField' is private and only accessible within class 'TestDir'.`,
            `Cannot assign to 'readonlyField' because it is a read-only property.`,
          ].sort();
          expect(actualMessages).toEqual(expectedMessages);
        }

        it('should report invalid type assignment when field name is not a valid JS identifier',
           () => {
             env.write('test.ts', `
            import {Component, NgModule, Input, Directive} from '@angular/core';

            @Component({
              selector: 'blah',
              template: '

',
            })
            export class FooCmp {
              value = 5;
            }

            @Directive({selector: '[dir]'})
            export class TestDir {
              @Input()
              private 'private-input.xs'!: string;
            }

            @NgModule({
              declarations: [FooCmp, TestDir],
            })
            export class FooModule {}
          `);
             const diags = env.driveDiagnostics();
             expect(diags.length).toBe(1);
             expect(diags[0].messageText)
                 .toEqual(`Type 'number' is not assignable to type 'string'.`);
           });
      });

      describe('with strict inputs', () => {
        beforeEach(() => {
          env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
        });

        it('should not produce diagnostics for correct inputs which assign to readonly, private, or protected fields',
           () => {
             env.write('test.ts', correctTypeInputsToRestrictedFields);
             const diags = env.driveDiagnostics();
             expect(diags.length).toBe(0);
           });

        it('should not produce diagnostics for correct inputs which assign to readonly, private, or protected fields inherited from a base class',
           () => {
             env.write('test.ts', correctInputsToRestrictedFieldsFromBaseClass);
             const diags = env.driveDiagnostics();
             expect(diags.length).toBe(0);
           });

        it('should produce diagnostics when assigning incorrect type to readonly, private, or protected fields',
           () => {
             env.write('test.ts', `
            import {Component, NgModule, Input, Directive} from '@angular/core';

            @Component({
              selector: 'blah',
              template: '

',
            })
            export class FooCmp {
              value = 1;
            }

            ${directiveDeclaration}

            @NgModule({
              declarations: [FooCmp, TestDir],
            })
            export class FooModule {}
        `);
             const diags = env.driveDiagnostics();
             expect(diags.length).toBe(3);
             expect(diags[0].messageText)
                 .toEqual(`Type 'number' is not assignable to type 'string'.`);
             expect(diags[1].messageText)
                 .toEqual(`Type 'number' is not assignable to type 'string'.`);
             expect(diags[2].messageText)
                 .toEqual(`Type 'number' is not assignable to type 'string'.`);
           });
      });
    });

    it('should not produce diagnostics for undeclared inputs', () => {
      env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
      env.write('test.ts', `
            import {Component, NgModule, Input, Directive} from '@angular/core';

            @Component({
              selector: 'blah',
              template: '

',
            })
            export class FooCmp {
              value = "value";
            }

            @Directive({
              selector: '[dir]',
              inputs: ['undeclared'],
            })
            export class TestDir {
            }

            @NgModule({
              declarations: [FooCmp, TestDir],
            })
            export class FooModule {}
        `);
      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(0);
    });

    it('should produce diagnostics for invalid expressions when assigned into an undeclared input',
       () => {
         env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
         env.write('test.ts', `
            import {Component, NgModule, Input, Directive} from '@angular/core';

            @Component({
              selector: 'blah',
              template: '

',
            })
            export class FooCmp {
            }

            @Directive({
              selector: '[dir]',
              inputs: ['undeclared'],
            })
            export class TestDir {
            }

            @NgModule({
              declarations: [FooCmp, TestDir],
            })
            export class FooModule {}
        `);
         const diags = env.driveDiagnostics();
         expect(diags.length).toBe(1);
         expect(diags[0].messageText).toBe(`Property 'value' does not exist on type 'FooCmp'.`);
       });

    it('should not produce diagnostics for undeclared inputs inherited from a base class', () => {
      env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
      env.write('test.ts', `
            import {Component, NgModule, Input, Directive} from '@angular/core';

            @Component({
              selector: 'blah',
              template: '

',
            })
            export class FooCmp {
              value = "value";
            }

            @Directive({
              inputs: ['undeclaredBase'],
            })
            export class BaseDir {
            }

            @Directive({selector: '[dir]'})
            export class TestDir extends BaseDir {
            }

            @NgModule({
              declarations: [FooCmp, TestDir],
            })
            export class FooModule {}
        `);
      const diags = env.driveDiagnostics();
      expect(diags.length).toBe(0);
    });

    describe('legacy schema checking with the DOM schema', () => {
      beforeEach(() => {
        env.tsconfig({fullTemplateTypeCheck: false});
      });

      it('should check for unknown elements', () => {
        env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        @Component({
          selector: 'blah',
          template: '<foo>test</foo>',
        })
        export class FooCmp {}
        @NgModule({
          declarations: [FooCmp],
        })
        export class FooModule {}
      `);
        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText).toBe(`'foo' is not a known element:
1. If 'foo' is an Angular component, then verify that it is part of this module.
2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`);
      });

      it('should have a descriptive error for unknown elements that contain a dash', () => {
        env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        @Component({
          selector: 'blah',
          template: '<my-foo>test</my-foo>',
        })
        export class FooCmp {}
        @NgModule({
          declarations: [FooCmp],
        })
        export class FooModule {}
      `);
        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText).toBe(`'my-foo' is not a known element:
1. If 'my-foo' is an Angular component, then verify that it is part of this module.
2. If 'my-foo' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`);
      });

      it('should check for unknown properties', () => {
        env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        @Component({
          selector: 'blah',
          template: '
test
',
        })
        export class FooCmp {}
        @NgModule({
          declarations: [FooCmp],
        })
        export class FooModule {}
      `);
        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toBe(`Can't bind to 'foo' since it isn't a known property of 'div'.`);
      });

      it('should have a descriptive error for unknown properties with an "ng-" prefix', () => {
        env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        @Component({
          selector: 'blah',
          template: '
test
',
        })
        export class FooCmp {}
        @NgModule({
          declarations: [FooCmp],
        })
        export class FooModule {}
      `);
        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toBe(`Can't bind to 'foo' since it isn't a known property of 'div'.`);
      });

      it('should convert property names when binding special properties', () => {
        env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        @Component({
          selector: 'blah',
          template: '',
        })
        export class FooCmp {
          test: string = 'test';
        }
        @NgModule({
          declarations: [FooCmp],
        })
        export class FooModule {}
      `);
        const diags = env.driveDiagnostics();
        // Should not be an error to bind [for] of , even though the actual property in the
        // DOM schema.
        expect(diags.length).toBe(0);
      });

      it('should produce diagnostics for custom-elements-style elements when not using the CUSTOM_ELEMENTS_SCHEMA',
         () => {
           env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';
          @Component({
            selector: 'blah',
            template: '<custom-element [foo]="1">test</custom-element>',
          })
          export class FooCmp {}
          @NgModule({
            declarations: [FooCmp],
          })
          export class FooModule {}
      `);
           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(2);
           expect(diags[0].messageText).toBe(`'custom-element' is not a known element:
1. If 'custom-element' is an Angular component, then verify that it is part of this module.
2. If 'custom-element' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`);
           expect(diags[1].messageText)
               .toBe(`Can't bind to 'foo' since it isn't a known property of 'custom-element'.
1. If 'custom-element' is an Angular component and it has 'foo' input, then verify that it is part of this module.
2. If 'custom-element' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`);
         });

      it('should not produce diagnostics for custom-elements-style elements when using the CUSTOM_ELEMENTS_SCHEMA',
         () => {
           env.write('test.ts', `
            import {Component, NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';

            @Component({
              selector: 'blah',
              template: '<custom-element [foo]="1">test</custom-element>',
            })
            export class FooCmp {}

            @NgModule({
              declarations: [FooCmp],
              schemas: [CUSTOM_ELEMENTS_SCHEMA],
            })
            export class FooModule {}
          `);
           const diags = env.driveDiagnostics();
           expect(diags).toEqual([]);
         });

      it('should not produce diagnostics when using the NO_ERRORS_SCHEMA', () => {
        env.write('test.ts', `
        import {Component, NgModule, NO_ERRORS_SCHEMA} from '@angular/core';

        @Component({
          selector: 'blah',
          template: '<foo [bar]="1"></foo>',
        })
        export class FooCmp {}

        @NgModule({
          declarations: [FooCmp],
          schemas: [NO_ERRORS_SCHEMA],
        })
        export class FooModule {}
      `);
        const diags = env.driveDiagnostics();
        expect(diags).toEqual([]);
      });

      it('should allow HTML elements inside SVG foreignObject', () => {
        env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        @Component({
          selector: 'blah',
          template: \`
            <svg>
              <svg:foreignObject>
                <xhtml:div>Hello</xhtml:div>
              </svg:foreignObject>
            </svg>
          \`,
        })
        export class FooCmp {}
        @NgModule({
          declarations: [FooCmp],
        })
        export class FooModule {}
      `);
        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(0);
      });

      it('should allow HTML elements without explicit namespace inside SVG foreignObject', () => {
        env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        @Component({
          template: \`
            <svg>
              <foreignObject>

Hello

              </foreignObject>
            </svg>
          \`,
        })
        export class FooCmp {}
        @NgModule({
          declarations: [FooCmp],
        })
        export class FooModule {}
      `);
        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(0);
      });

      it('should check for unknown elements inside an SVG foreignObject', () => {
        env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        @Component({
          selector: 'blah',
          template: \`
            <svg>
              <svg:foreignObject>
                <xhtml:foo>Hello</xhtml:foo>
              </svg:foreignObject>
            </svg>
          \`,
        })
        export class FooCmp {}
        @NgModule({
          declarations: [FooCmp],
        })
        export class FooModule {}
      `);
        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText).toBe(`'foo' is not a known element:
1. If 'foo' is an Angular component, then verify that it is part of this module.
2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`);
      });

      it('should check for unknown elements without explicit namespace inside an SVG foreignObject',
         () => {
           env.write('test.ts', `
        import {Component, NgModule} from '@angular/core';
        @Component({
          selector: 'blah',
          template: \`
            <svg>
              <foreignObject>
                <foo>Hello</foo>
              </foreignObject>
            </svg>
          \`,
        })
        export class FooCmp {}
        @NgModule({
          declarations: [FooCmp],
        })
        export class FooModule {}
      `);
           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(1);
           expect(diags[0].messageText).toBe(`'foo' is not a known element:
1. If 'foo' is an Angular component, then verify that it is part of this module.
2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`);
         });
    });

    // Test both sync and async compilations, see https://github.com/angular/angular/issues/32538
    ['sync', 'async'].forEach(mode => {
      describe(`error locations [${mode}]`, () => {
        let driveDiagnostics: () => Promise<ReadonlyArray<ts.Diagnostic>>;
        beforeEach(() => {
          if (mode === 'async') {
            env.enablePreloading();
            driveDiagnostics = () => env.driveDiagnosticsAsync();
          } else {
            driveDiagnostics = () => Promise.resolve(env.driveDiagnostics());
          }
        });

        it('should be correct for direct templates', async () => {
          env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';

          @Component({
            selector: 'test',
            template: \`

              {{user.does_not_exist}}

\`,
          })
          export class TestCmp {
            user: {name: string}[];
          }`);

          const diags = await driveDiagnostics();
          expect(diags.length).toBe(1);
          expect(diags[0].file!.fileName).toBe(_('/test.ts'));
          expect(getSourceCodeForDiagnostic(diags[0])).toBe('does_not_exist');
        });

        it('should be correct for indirect templates', async () => {
          env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';

          const TEMPLATE = \`

            {{user.does_not_exist}}

\`;

          @Component({
            selector: 'test',
            template: TEMPLATE,
          })
          export class TestCmp {
            user: {name: string}[];
          }`);

          const diags = await driveDiagnostics();
          expect(diags.length).toBe(1);
          expect(diags[0].file!.fileName).toBe(_('/test.ts') + ' (TestCmp template)');
          expect(getSourceCodeForDiagnostic(diags[0])).toBe('does_not_exist');
          expect(getSourceCodeForDiagnostic(diags[0].relatedInformation![0])).toBe('TEMPLATE');
        });

        it('should be correct for external templates', async () => {
          env.write('template.html', `

          {{user.does_not_exist}}

`);
          env.write('test.ts', `
          import {Component, NgModule} from '@angular/core';

          @Component({
            selector: 'test',
            templateUrl: './template.html',
          })
          export class TestCmp {
            user: {name: string}[];
          }`);

          const diags = await driveDiagnostics();
          expect(diags.length).toBe(1);
          expect(diags[0].file!.fileName).toBe(_('/template.html'));
          expect(getSourceCodeForDiagnostic(diags[0])).toBe('does_not_exist');
          expect(getSourceCodeForDiagnostic(diags[0].relatedInformation![0]))
              .toBe(`'./template.html'`);
        });
      });
    });

    describe('option compatibility verification', () => {
      beforeEach(() => env.write('index.ts', `export const a = 1;`));

      it('should error if "fullTemplateTypeCheck" is false when "strictTemplates" is true', () => {
        env.tsconfig({fullTemplateTypeCheck: false, strictTemplates: true});

        const diags = env.driveDiagnostics();
        expect(diags.length).toBe(1);
        expect(diags[0].messageText)
            .toContain(
                'Angular compiler option "strictTemplates" is enabled, however "fullTemplateTypeCheck" is disabled.');
      });
      it('should not error if "fullTemplateTypeCheck" is false when "strictTemplates" is false',
         () => {
           env.tsconfig({fullTemplateTypeCheck: false, strictTemplates: false});

           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(0);
         });
      it('should not error if "fullTemplateTypeCheck" is not set when "strictTemplates" is true',
         () => {
           env.tsconfig({strictTemplates: true});

           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(0);
         });
      it('should not error if "fullTemplateTypeCheck" is true set when "strictTemplates" is true',
         () => {
           env.tsconfig({strictTemplates: true});

           const diags = env.driveDiagnostics();
           expect(diags.length).toBe(0);
         });
    });

    describe('stability', () => {
      beforeEach(() => {
        env.write('test.ts', `
          import {Component} from '@angular/core';

          @Component({
            selector: 'test-cmp',
            template: '{{expr}}'
          })
          export class TestCmp {
            expr = 'string';
          }
        `);
      });

      // This section tests various scenarios which have more complex ts.Program setups and thus
      // exercise edge cases of the template type-checker.
      it('should accept a program with a flat index', () => {
        // This test asserts that flat indices don't have any negative interactions with the
        // generation of template type-checking code in the program.
        env.tsconfig({fullTemplateTypeCheck: true, flatModuleOutFile: 'flat.js'});

        expect(env.driveDiagnostics()).toEqual([]);
      });

      it('should not leave referencedFiles in a tagged state', () => {
        env.enableMultipleCompilations();

        env.driveMain();
        const sf = getSourceFileOrError(env.getTsProgram(), _('/test.ts'));
        expect(sf.referencedFiles.map(ref => ref.fileName)).toEqual([]);
      });

      it('should allow for complete program reuse during incremental compilations', () => {
        env.enableMultipleCompilations();

        env.write('other.ts', `export const VERSION = 1;`);

        env.driveMain();
        const firstProgram = env.getReuseTsProgram();

        env.write('other.ts', `export const VERSION = 2;`);
        env.driveMain();

        expectCompleteReuse(env.getTsProgram());
        expectCompleteReuse(env.getReuseTsProgram());
      });
    });
  });
});

function getSourceCodeForDiagnostic(diag: ts.Diagnostic): string {
  const text = diag.file!.text;
  return text.substr(diag.start!, diag.length!);
}


text:
package dk.statsbiblioteket.dpaviser.report;

import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import dk.statsbiblioteket.dpaviser.report.helpers.JSoupHelpers;
import dk.statsbiblioteket.dpaviser.report.jhove.JHoveHelpers;
import dk.statsbiblioteket.dpaviser.report.sax.ExceptionThrowingErrorHandler;
import dk.statsbiblioteket.util.xml.XSLT;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.xpath.XPathExpressionException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import static java.util.Arrays.asList;
import static javax.xml.xpath.XPathConstants.STRING;

public class MetadataExtractorPerPage {
    protected static DocumentBuilderFactory documentBuilderFactory;

    /**
     * Only return a single row - the multirow unpacking was too tricky for now
     */

    @SuppressWarnings("deprecation")
	protected static Map<String, List<String>> getZeroOrOneRowMappedByTypeForPath(Path path, File tmpDir) {

        Map<String, List<String>> result = new HashMap<>();

        String pathString = path.toString();

        if (pathString.endsWith(".pdf")) { // PDF: run jhove and extract information
            InputStream configStream = Main.class.getResourceAsStream("/jhove.config.xml");
            Function<Path, InputStream> jHoveFunction = JHoveHelpers.getJHoveFunction(configStream, tmpDir);

            InputStream is = jHoveFunction.apply(path);

            try {
                URL pdfXSLT = Thread.currentThread().getContextClassLoader().getResource("pdf-process-jhove-output.xsl");

                String tableXML = XSLT.transform(pdfXSLT, is, null).toString(); // sbutil caches compiled stylesheet

                List<List<String>> firstTableFromHTML = JSoupHelpers.getFirstTableFromHTML(tableXML);

                List<String> firstRow = firstTableFromHTML.get(0);
                String firstCell = firstRow.get(0);
                firstRow.set(0, URLDecoder.decode(firstCell)); // URLDecode of paths. Hard in XSLT 1.0.

                result.put("PDF", firstRow);
            } catch (TransformerException e) {
                throw new RuntimeException("could not transform jhove output", e);
            }
        } else if (pathString.endsWith(".xml")) { // XML:  Validate.
            try {
                if (documentBuilderFactory == null) {
                    documentBuilderFactory = DocumentBuilderFactory.newInstance();
                    documentBuilderFactory.setNamespaceAware(true);
                    SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
                    schemaFactory.setErrorHandler(new ExceptionThrowingErrorHandler());
                    Schema schema = schemaFactory.newSchema(Main.class.getResource("/NewsML_1.2-infomedia.xsd"));

                    documentBuilderFactory.setSchema(schema);
                }
                DocumentBuilder db = documentBuilderFactory.newDocumentBuilder();
                db.setErrorHandler(new ExceptionThrowingErrorHandler());
                Document document = db.parse(path.toUri().toString());

                List<String> row = new ArrayList<>(asList(pathString, "XML", "XML valid"));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='KKOD']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PUBX']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='OFIL']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PUGE']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='STATUS']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PMOD']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='FOED']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PMOD']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PIND']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PSID']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PSEK']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PSIN']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PSNA']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='CCRE']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='UDGA']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='PDF']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='CMOD']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='READERSHIP']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='CIRCULATION']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='MEDIATYPE']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='COUNTRY']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='REGION']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='LIX']/@Value)", document, STRING));
                row.add((String) MetadataExtractorPerDelivery.noNamespacesXPath.evaluate("string(//Metadata[MetadataType/@FormalName='InfomediaMetadata']/Property[@FormalName='LANGUAGE']/@Value)", document, STRING));
                result.put("XML", row);
            } catch (SAXException e) {
                result.put("XML", asList(pathString, "XML", "XML not valid", e.getMessage()));
            } catch (ParserConfigurationException e) {
                throw new RuntimeException("cannot configure parser - bad Java?", e);
            } catch (XPathExpressionException e) {
                result.put("XML", asList(pathString, "XML", "XPath expression not valid", e.getMessage()));
                System.err.println(e.toString());
            } catch (IOException e) {
                result.put("XML", asList(pathString, "XML", "Cannot read input", e.getMessage()));
                e.printStackTrace();
            }
        } else if (pathString.endsWith(".md5")) {
            // compare value in this file with value calculated.
            try {
                String md5FileLine = Files.readAllLines(path).get(0);
                if (md5FileLine.length() != 32) {
                    result.put("MD5", asList(pathString, "MD5", "MD5 first line not 32 characters."));
                } else {
                    String originalFileName = pathString.substring(0, pathString.length() - ".md5".length());
                    HashCode md5calculated = com.google.common.io.Files.hash(new File(originalFileName), Hashing.md5());
                    if (md5FileLine.equalsIgnoreCase(md5calculated.toString())) {
                        // all well, do not report anything
                    } else {
                        result.put("MD5", asList(pathString, "MD5", "MD5 " + md5FileLine + " stated in file, calculated " + md5calculated));
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException("could not process " + path, e);
            }
        } else {
            // Ignore other files.
        }
        return result;
    }
}

redpajama_set_name: RedPajamaGithub


text:
class FilterOperation : public MultiplePixelOperation
{
public:
    FilterOperation(const FilterMat &filter);

    virtual cv::Vec3b applyFilter(cv::Mat_<cv::Vec3b> const &input) const;

    virtual FilterSize getFilterSize() const;

private:
    uchar countColor(cv::Mat_<cv::Vec3b> const &input, int index) const;

    bool checkDimensionEquals(cv::Mat_<cv::Vec3b> const &input) const;

    cv::Vec3b filterImage(cv::Mat_<cv::Vec3b> const &input) const;

    const FilterMat &filter_;
};

#endif //FILTER_OPERATION_HPP


text:
package su.iota.backend.frontend;

import co.paralleluniverse.actors.ActorRef;
import co.paralleluniverse.actors.behaviors.Server;
import co.paralleluniverse.fibers.SuspendExecution;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Contract;
import su.iota.backend.messages.IncomingMessage;
import su.iota.backend.messages.OutgoingMessage;
import su.iota.backend.messages.game.AbstractPlayerActionMessage;
import su.iota.backend.models.UserProfile;

@Contract
public interface FrontendService {

    boolean signUp(@Nullable UserProfile userProfile) throws SuspendExecution;

    boolean signIn(@Nullable UserProfile userProfile) throws SuspendExecution;

    void signOut() throws SuspendExecution;

    @Nullable
    UserProfile getSignedInUser() throws SuspendExecution;

    @Nullable
    UserProfile getUserById(long userId) throws SuspendExecution;

    @NotNull
    AbstractPlayerActionMessage.AbstractResultMessage performPlayerAction(@NotNull AbstractPlayerActionMessage playerActionMessage) throws SuspendExecution, InterruptedException;

    void setGameSession(@NotNull ActorRef<Object> frontend, Server<IncomingMessage, OutgoingMessage, Object> gameSessionActor) throws SuspendExecution, InterruptedException;

    void resetGameSession() throws SuspendExecution;

    boolean askGameStateUpdate(@NotNull ActorRef<Object> frontend) throws SuspendExecution, InterruptedException;

    void dropPlayer(@NotNull ActorRef<Object> frontend) throws SuspendExecution, InterruptedException;

    void softDropPlayer(@NotNull ActorRef<Object> frontend) throws SuspendExecution;

}


text:
import logging
from contextlib import ExitStack

from share.search.elastic_manager import ElasticManager
from share.search.messages import MessageType

__all__ = ('SearchIndexer', 'MessageType')

logger = logging.getLogger(__name__)

class SearchIndexer:

    retry_policy = {
        'interval_start': 0,  # First retry immediately,
        'interval_step': 2,   # then increase by 2s for every retry.
        'interval_max': 30,   # but don't exceed 30s between retries.
        'max_retries': 30,    # give up after 30 tries.
    }

    def __init__(self, celery_app=None, elastic_manager=None):
        self.elastic_manager = elastic_manager or ElasticManager()

        if celery_app is None:
            import celery
            self.app = celery.current_app
        else:
            self.app = celery_app

    def send_messages(self, message_type, target_ids, urgent=False, index_names=None):
        if not index_names:
            index_names = self.elastic_manager.settings['ACTIVE_INDEXES']

        messages = (
            {
                'version': 2,
                'message_type': message_type.value,
                'target_id': target_id,
            }
            for target_id in target_ids
        )

        # gather the queues to send to, based on the index setups' supported message types
        queue_key = 'URGENT_QUEUE' if urgent else 'DEFAULT_QUEUE'
        queue_names = [
            self.elastic_manager.settings['INDEXES'][index_name][queue_key]
            for index_name in index_names
            if message_type in self.elastic_manager.get_index_setup(index_name).supported_message_types
        ]
        queue_settings = self.elastic_manager.settings['KOMBU_QUEUE_SETTINGS']

        with self.app.pool.acquire(block=True) as connection:
            # gather all the queues in one context manager so they're all closed
            # once we're done
            with ExitStack() as kombu_queue_contexts:
                kombu_queues = [
                    kombu_queue_contexts.enter_context(
                        connection.SimpleQueue(queue_name, **queue_settings)
                    )
                    for queue_name in queue_names
                ]

                for message in messages:
                    for kombu_queue in kombu_queues:
                        kombu_queue.put(message, retry=True, retry_policy=self.retry_policy)

    def handle_messages_sync(self, message_type, target_ids, index_names=None):
        if not index_names:
            index_names = self.elastic_manager.settings['ACTIVE_INDEXES']

        for index_name in index_names:
            index_setup = self.elastic_manager.get_index_setup(index_name)
            if message_type not in index_setup.supported_message_types:
                logger.error(f'skipping: {index_name} does not support {message_type}')
                continue

            action_generator = index_setup.build_action_generator(index_name, message_type)
            elastic_actions = [
                elastic_action
                for (_, elastic_action) in action_generator(target_ids)
            ]
            self.elastic_manager.send_actions_sync(elastic_actions)
        self.elastic_manager.refresh_indexes(index_names)


text:
//
//  strategy_recomendations.h
//  Prisoners Dilemma II
//
//  Created by Sergey Bogolepov on 12/24/14.
//  Copyright (c) 2014 Sergey Bogolepov. All rights reserved.
//

#ifndef __Prisoners_Dilemma_II__strategy_recomendations__
#define __Prisoners_Dilemma_II__strategy_recomendations__

#include "strategy.h"
#include "strategy_usualguy.h"

class StrategyRecomendations : Strategy {
public:
    virtual Decision make_decision();

    virtual void remember_decision(std::string name, Decision decision);

    virtual void notify_start(std::string name);

    virtual void notify_end();

    virtual ~StrategyRecomendations() {}
private:
    StrategyUsualguy guy;
};

#endif /* defined(__Prisoners_Dilemma_II__strategy_recomendations__) */


text:
OBJS1 = TCPModbusServer.o DieWithError.o HandleModbusTCPClient.o crc16.o utility.o
OBJS6 = TCPModbusClient.o DieWithError.o crc16.o utility.o
TRG = TCPModbusServer TCPModbusClient
CC = gcc
DEBUG = -g
CFLAGS = -Wall -c $(DEBUG)
LFLAGS = -Wall $(DEBUG)

all : $(TRG)

TCPModbusServer : $(OBJS1)
	$(CC) $(LFLAGS) $(OBJS1) -o TCPModbusServer

TCPModbusClient : $(OBJS6)
	$(CC) $(LFLAGS) $(OBJS6) -o TCPModbusClient

TCPModbusServer.o : TCPModbusServer.c
	$(CC) $(CFLAGS) TCPModbusServer.c

TCPModbusClient.o : TCPModbusClient.c
	$(CC) $(CFLAGS) TCPModbusClient.c -lzmq

DieWithError.o : DieWithError.c
	$(CC) $(CFLAGS) DieWithError.c

HandleModbusTCPClient.o : HandleModbusTCPClient.c E30ModbusMsg.h
	$(CC) $(CFLAGS) HandleModbusTCPClient.c

crc16.o : crc16.c
	$(CC) $(CFLAGS) crc16.c

utility.o : utility.c zhelpers.h
	$(CC) $(CFLAGS) utility.c -lzmq

clean:
	rm *.o $(TRG)


text:
module Benchmarks
    import UUID
    export @benchmark

    include("01_clock_resolution.jl")
    include("02_environment.jl")
    include("03_samples.jl")
    include("04_results.jl")
    include("benchmarkable.jl")
    include("ols.jl")
    include("execute.jl")
    include("benchmark.jl")
    include("wrapper_types.jl")
end


text:
package org.vaadin.viritin.util;

import java.lang.reflect.Method;
import java.util.*;

import org.vaadin.viritin.util.VaadinLocale.LocaleNegotiationStrategey;

import com.vaadin.server.VaadinRequest;

/**
 * Implementation of {@link LocaleNegotiationStrategey} which uses java 1.8
 * {@link Locale#lookup(List, Collection)} to negotiate the best locale.
 *
 * @author Daniel Nordhoff-Vergien
 *
 */
public class Java8LocaleNegotiationStrategy implements
        LocaleNegotiationStrategey {

    @Override
    public Locale negotiate(List<Locale> supportedLocales,
            VaadinRequest vaadinRequest) {
        String languages = vaadinRequest.getHeader("Accept-Language");
        try {
            // Use reflection here, so the code compiles with jdk 1.7
            Class<?> languageRange = Class
                    .forName("java.util.Locale$LanguageRange");
            Method parse = languageRange.getMethod("parse", String.class);
            Object priorityList = parse.invoke(null, languages);
            Method lookup = Locale.class.getMethod("lookup", List.class,
                    Collection.class);
            return (Locale) lookup.invoke(null, priorityList, supportedLocales);
        } catch (Exception e) {
            throw new RuntimeException(
                    "Java8LocaleNegotiontionStrategy need java 1.8 or newer.",
                    e);
        }

    }
}


text:
extern const Class_t Class;

void loadObject(Class_t class);

void loadClass(Class_t class);

#endif