• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1//// [parserharness.ts]
2//
3// Copyright (c) Microsoft Corporation.  All rights reserved.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//   http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17///<reference path='..\compiler\io.ts'/>
18///<reference path='..\compiler\typescript.ts'/>
19///<reference path='..\services\typescriptServices.ts' />
20///<reference path='diff.ts'/>
21
22declare var assert: Harness.Assert;
23declare var it;
24declare var describe;
25declare var run;
26declare var IO: IIO;
27declare var __dirname; // Node-specific
28
29function switchToForwardSlashes(path: string) {
30    return path.replace(/\\/g, "/");
31}
32
33function filePath(fullPath: string) {
34    fullPath = switchToForwardSlashes(fullPath);
35    var components = fullPath.split("/");
36    var path: string[] = components.slice(0, components.length - 1);
37    return path.join("/") + "/";
38}
39
40var typescriptServiceFileName = filePath(IO.getExecutingFilePath()) + "typescriptServices.js";
41var typescriptServiceFile = IO.readFile(typescriptServiceFileName);
42if (typeof ActiveXObject === "function") {
43    eval(typescriptServiceFile);
44} else if (typeof require === "function") {
45    var vm = require('vm');
46    vm.runInThisContext(typescriptServiceFile, 'typescriptServices.js');
47} else {
48    throw new Error('Unknown context');
49}
50
51declare module process {
52    export function nextTick(callback: () => any): void;
53    export function on(event: string, listener: Function);
54}
55
56module Harness {
57    // Settings
58    export var userSpecifiedroot = "";
59    var global = <any>Function("return this").call(null);
60    export var usePull = false;
61
62    export interface ITestMetadata {
63        id: string;
64        desc: string;
65        pass: boolean;
66        perfResults: {
67            mean: number;
68            min: number;
69            max: number;
70            stdDev: number;
71            trials: number[];
72        };
73    }
74    export interface IScenarioMetadata {
75        id: string;
76        desc: string;
77        pass: boolean;
78        bugs: string[];
79    }
80
81    // Assert functions
82    export module Assert {
83        export var bugIds: string[] = [];
84        export var throwAssertError = (error: Error) => {
85            throw error;
86        };
87
88        // Marks that the current scenario is impacted by a bug
89        export function bug(id: string) {
90            if (bugIds.indexOf(id) < 0) {
91                bugIds.push(id);
92            }
93        }
94
95        // If there are any bugs in the test code, mark the scenario as impacted appropriately
96        export function bugs(content: string) {
97            var bugs = content.match(/\bbug (\d+)/i);
98            if (bugs) {
99                bugs.forEach(bug => assert.bug(bug));
100            }
101        }
102
103        export function is(result: boolean, msg?: string) {
104            if (!result) {
105                throwAssertError(new Error(msg || "Expected true, got false."));
106            }
107        }
108
109        export function arrayLengthIs(arr: any[], length: number) {
110            if (arr.length != length) {
111                var actual = '';
112                arr.forEach(n => actual = actual + '\n      ' + n.toString());
113                throwAssertError(new Error('Expected array to have ' + length + ' elements. Actual elements were:' + actual));
114            }
115        }
116
117        export function equal(actual, expected) {
118            if (actual !== expected) {
119                throwAssertError(new Error("Expected " + actual + " to equal " + expected));
120            }
121        }
122
123        export function notEqual(actual, expected) {
124            if (actual === expected) {
125                throwAssertError(new Error("Expected " + actual + " to *not* equal " + expected));
126            }
127        }
128
129        export function notNull(result) {
130            if (result === null) {
131                throwAssertError(new Error("Expected " + result + " to *not* be null"));
132            }
133        }
134
135        export function compilerWarning(result: Compiler.CompilerResult, line: number, column: number, desc: string) {
136            if (!result.isErrorAt(line, column, desc)) {
137                var actual = '';
138                result.errors.forEach(err => {
139                    actual = actual + '\n     ' + err.toString();
140                });
141
142                throwAssertError(new Error("Expected compiler warning at (" + line + ", " + column + "): " + desc + "\nActual errors follow: " + actual));
143            }
144        }
145
146        export function noDiff(text1, text2) {
147            text1 = text1.replace(/^\s+|\s+$/g, "").replace(/\r\n?/g, "\n");
148            text2 = text2.replace(/^\s+|\s+$/g, "").replace(/\r\n?/g, "\n");
149
150            if (text1 !== text2) {
151                var errorString = "";
152                var text1Lines = text1.split(/\n/);
153                var text2Lines = text2.split(/\n/);
154                for (var i = 0; i < text1Lines.length; i++) {
155                    if (text1Lines[i] !== text2Lines[i]) {
156                        errorString += "Difference at line " + (i + 1) + ":\n";
157                        errorString += "                  Left File: " + text1Lines[i] + "\n";
158                        errorString += "                 Right File: " + text2Lines[i] + "\n\n";
159                    }
160                }
161                throwAssertError(new Error(errorString));
162            }
163        }
164
165        export function arrayContains(arr: any[], contains: any[]) {
166            var found;
167
168            for (var i = 0; i < contains.length; i++) {
169                found = false;
170
171                for (var j = 0; j < arr.length; j++) {
172                    if (arr[j] === contains[i]) {
173                        found = true;
174                        break;
175                    }
176                }
177
178                if (!found) {
179                    throwAssertError(new Error("Expected array to contain \"" + contains[i] + "\""));
180                }
181            }
182        }
183
184        export function arrayContainsOnce(arr: any[], filter: (item: any) => boolean) {
185            var foundCount = 0;
186
187            for (var i = 0; i < arr.length; i++) {
188                if (filter(arr[i])) {
189                    foundCount++;
190                }
191            }
192
193            if (foundCount !== 1) {
194                throwAssertError(new Error("Expected array to match element only once (instead of " + foundCount + " times)"));
195            }
196        }
197    }
198
199    /** Splits the given string on \r\n or on only \n if that fails */
200    export function splitContentByNewlines(content: string) {
201        // Split up the input file by line
202        // Note: IE JS engine incorrectly handles consecutive delimiters here when using RegExp split, so
203        // we have to string-based splitting instead and try to figure out the delimiting chars
204        var lines = content.split('\r\n');
205        if (lines.length === 1) {
206            lines = content.split('\n');
207        }
208        return lines;
209    }
210
211    /** Reads a file under /tests */
212    export function readFile(path: string) {
213
214        if (path.indexOf('tests') < 0) {
215            path = "tests/" + path;
216        }
217
218        var content = IO.readFile(Harness.userSpecifiedroot + path);
219        if (content == null) {
220            throw new Error("failed to read file at: '" + Harness.userSpecifiedroot + path + "'");
221        }
222
223        return content;
224    }
225
226    // Logger
227    export interface ILogger {
228        start: (fileName?: string, priority?: number) => void;
229        end: (fileName?: string) => void;
230        scenarioStart: (scenario: IScenarioMetadata) => void;
231        scenarioEnd: (scenario: IScenarioMetadata, error?: Error) => void;
232        testStart: (test: ITestMetadata) => void;
233        pass: (test: ITestMetadata) => void;
234        bug: (test: ITestMetadata) => void;
235        fail: (test: ITestMetadata) => void;
236        error: (test: ITestMetadata, error: Error) => void;
237        comment: (comment: string) => void;
238        verify: (test: ITestMetadata, passed: boolean, actual: any, expected: any, message: string) => void;
239    }
240
241    export class Logger implements ILogger {
242        public start(fileName?: string, priority?: number) { }
243        public end(fileName?: string) { }
244        public scenarioStart(scenario: IScenarioMetadata) { }
245        public scenarioEnd(scenario: IScenarioMetadata, error?: Error) { }
246        public testStart(test: ITestMetadata) { }
247        public pass(test: ITestMetadata) { }
248        public bug(test: ITestMetadata) { }
249        public fail(test: ITestMetadata) { }
250        public error(test: ITestMetadata, error: Error) { }
251        public comment(comment: string) { }
252        public verify(test: ITestMetadata, passed: boolean, actual: any, expected: any, message: string) { }
253    }
254
255    // Logger-related functions
256    var loggers: ILogger[] = [];
257    export function registerLogger(logger: ILogger) {
258        loggers.push(logger);
259    }
260    export function emitLog(field: string, ...params: any[]) {
261        for (var i = 0; i < loggers.length; i++) {
262            if (typeof loggers[i][field] === 'function') {
263                loggers[i][field].apply(loggers[i], params);
264            }
265        }
266    }
267
268    // BDD Framework
269    export interface IDone {
270        (e?: Error): void;
271    }
272    export class Runnable {
273        constructor(public description: string, public block: any) { }
274
275        // The current stack of Runnable objects
276        static currentStack: Runnable[] = [];
277
278        // The error, if any, that occurred when running 'block'
279        public error: Error = null;
280
281        // Whether or not this object has any failures (including in its descendants)
282        public passed = null;
283
284        // A list of bugs impacting this object
285        public bugs: string[] = [];
286
287        // A list of all our child Runnables
288        public children: Runnable[] = [];
289
290        public addChild(child: Runnable): void {
291            this.children.push(child);
292        }
293
294        /** Call function fn, which may take a done function and may possibly execute
295         *  asynchronously, calling done when finished. Returns true or false depending
296         *  on whether the function was asynchronous or not.
297         */
298        public call(fn: (done?: IDone) => void , done: IDone) {
299            var isAsync = true;
300
301            try {
302                if (fn.length === 0) {
303                    // No async.
304                    fn();
305                    done();
306
307                    return false;
308                } else {
309                    // Possibly async
310
311                    Runnable.pushGlobalErrorHandler(done);
312
313                    fn(function () {
314                        isAsync = false; // If we execute synchronously, this will get called before the return below.
315                        Runnable.popGlobalErrorHandler();
316                        done();
317                    });
318
319                    return isAsync;
320                }
321
322            } catch (e) {
323                done(e);
324
325                return false;
326            }
327        }
328
329        public run(done: IDone) { }
330
331        public runBlock(done: IDone) {
332            return this.call(this.block, done);
333        }
334
335        public runChild(index: number, done: IDone) {
336            return this.call(<any>((done) => this.children[index].run(done)), done);
337        }
338
339        static errorHandlerStack: { (e: Error): void; }[] = [];
340
341        static pushGlobalErrorHandler(done: IDone) {
342            errorHandlerStack.push(function (e) {
343                done(e);
344            });
345        }
346
347        static popGlobalErrorHandler() {
348            errorHandlerStack.pop();
349        }
350
351        static handleError(e: Error) {
352            if (errorHandlerStack.length === 0) {
353                IO.printLine('Global error: ' + e);
354            } else {
355                errorHandlerStack[errorHandlerStack.length - 1](e);
356            }
357        }
358    }
359    export class TestCase extends Runnable {
360        public description: string;
361        public block;
362
363        constructor(description: string, block: any) {
364            super(description, block);
365            this.description = description;
366            this.block = block;
367        }
368
369        public addChild(child: Runnable): void {
370            throw new Error("Testcases may not be nested inside other testcases");
371        }
372
373        /** Run the test case block and fail the test if it raised an error. If no error is raised, the test passes. */
374        public run(done: IDone) {
375            var that = this;
376
377            Runnable.currentStack.push(this);
378
379            emitLog('testStart', { desc: this.description });
380
381            if (this.block) {
382                var async = this.runBlock(<any>function (e) {
383                    if (e) {
384                        that.passed = false;
385                        that.error = e;
386                        emitLog('error', { desc: this.description, pass: false }, e);
387                    } else {
388                        that.passed = true;
389
390                        emitLog('pass', { desc: this.description, pass: true });
391                    }
392
393                    Runnable.currentStack.pop();
394
395                    done()
396                });
397            }
398
399        }
400    }
401
402    export class Scenario extends Runnable {
403        public description: string;
404        public block;
405
406        constructor(description: string, block: any) {
407            super(description, block);
408            this.description = description;
409            this.block = block;
410        }
411
412        /** Run the block, and if the block doesn't raise an error, run the children. */
413        public run(done: IDone) {
414            var that = this;
415
416            Runnable.currentStack.push(this);
417
418            emitLog('scenarioStart', { desc: this.description });
419
420            var async = this.runBlock(<any>function (e) {
421                Runnable.currentStack.pop();
422                if (e) {
423                    that.passed = false;
424                    that.error = e;
425                    var metadata: IScenarioMetadata = { id: undefined, desc: this.description, pass: false, bugs: assert.bugIds };
426                    // Report all bugs affecting this scenario
427                    assert.bugIds.forEach(desc => emitLog('bug', metadata, desc));
428                    emitLog('scenarioEnd', metadata, e);
429                    done();
430                } else {
431                    that.passed = true; // so far so good.
432                    that.runChildren(done);
433                }
434            });
435        }
436
437        /** Run the children of the scenario (other scenarios and test cases). If any fail,
438         *  set this scenario to failed. Synchronous tests will run synchronously without
439         *  adding stack frames.
440         */
441        public runChildren(done: IDone, index = 0) {
442            var that = this;
443            var async = false;
444
445            for (; index < this.children.length; index++) {
446                async = this.runChild(index, <any>function (e) {
447                    that.passed = that.passed && that.children[index].passed;
448
449                    if (async)
450                        that.runChildren(done, index + 1);
451                });
452
453                if (async)
454                    return;
455            }
456
457            var metadata: IScenarioMetadata = { id: undefined, desc: this.description, pass: this.passed, bugs: assert.bugIds };
458            // Report all bugs affecting this scenario
459            assert.bugIds.forEach(desc => emitLog('bug', metadata, desc));
460            emitLog('scenarioEnd', metadata);
461
462            done();
463        }
464    }
465    export class Run extends Runnable {
466        constructor() {
467            super('Test Run', null);
468        }
469
470        public run() {
471            emitLog('start');
472            this.runChildren();
473        }
474
475        public runChildren(index = 0) {
476            var async = false;
477            var that = this;
478
479            for (; index < this.children.length; index++) {
480                // Clear out bug descriptions
481                assert.bugIds = [];
482
483                async = this.runChild(index, <any>function (e) {
484                    if (async) {
485                        that.runChildren(index + 1);
486                    }
487                });
488
489                if (async) {
490                    return;
491                }
492            }
493
494            Perf.runBenchmarks();
495            emitLog('end');
496        }
497    }
498
499    // Performance test
500    export module Perf {
501        export module Clock {
502            export var now: () => number;
503            export var resolution: number;
504
505            declare module WScript {
506                export function InitializeProjection();
507            }
508
509            declare module TestUtilities {
510                export function QueryPerformanceCounter(): number;
511                export function QueryPerformanceFrequency(): number;
512            }
513
514            if (typeof WScript !== "undefined" && typeof global['WScript'].InitializeProjection !== "undefined") {
515                // Running in JSHost.
516                global['WScript'].InitializeProjection();
517
518                now = function () {
519                    return TestUtilities.QueryPerformanceCounter();
520                }
521
522                resolution = TestUtilities.QueryPerformanceFrequency();
523            } else {
524                now = function () {
525                    return Date.now();
526                }
527
528                resolution = 1000;
529            }
530        }
531
532        export class Timer {
533            public startTime;
534            public time = 0;
535
536            public start() {
537                this.time = 0;
538                this.startTime = Clock.now();
539            }
540
541            public end() {
542                // Set time to MS.
543                this.time = (Clock.now() - this.startTime) / Clock.resolution * 1000;
544            }
545        }
546
547        export class Dataset {
548            public data: number[] = [];
549
550            public add(value: number) {
551                this.data.push(value);
552            }
553
554            public mean() {
555                var sum = 0;
556                for (var i = 0; i < this.data.length; i++) {
557                    sum += this.data[i];
558                }
559
560                return sum / this.data.length;
561            }
562
563            public min() {
564                var min = this.data[0];
565
566                for (var i = 1; i < this.data.length; i++) {
567                    if (this.data[i] < min) {
568                        min = this.data[i];
569                    }
570                }
571
572                return min;
573            }
574
575            public max() {
576                var max = this.data[0];
577
578                for (var i = 1; i < this.data.length; i++) {
579                    if (this.data[i] > max) {
580                        max = this.data[i];
581                    }
582                }
583
584                return max;
585            }
586
587            public stdDev() {
588                var sampleMean = this.mean();
589                var sumOfSquares = 0;
590                for (var i = 0; i < this.data.length; i++) {
591                    sumOfSquares += Math.pow(this.data[i] - sampleMean, 2);
592                }
593
594                return Math.sqrt(sumOfSquares / this.data.length);
595            }
596        }
597
598        // Base benchmark class with some defaults.
599        export class Benchmark {
600            public iterations = 10;
601            public description = "";
602            public bench(subBench?: () => void ) { }
603            public before() { }
604            public beforeEach() { }
605            public after() { }
606            public afterEach() { }
607            public results: { [x: string]: Dataset; } = <{ [x: string]: Dataset; }>{};
608
609            public addTimingFor(name: string, timing: number) {
610                this.results[name] = this.results[name] || new Dataset();
611                this.results[name].add(timing);
612            }
613        }
614
615        export var benchmarks: { new (): Benchmark; }[] = [];
616
617        var timeFunction: (
618            benchmark: Benchmark,
619            description?: string,
620            name?: string,
621            f?: (bench?: { (): void; }) => void
622        ) => void;
623
624        timeFunction = function (
625            benchmark: Benchmark,
626            description: string = benchmark.description,
627            name: string = '',
628            f = benchmark.bench
629        ): void {
630
631            var t = new Timer();
632            t.start();
633
634            var subBenchmark = function (name, f): void {
635                timeFunction(benchmark, description, name, f);
636            }
637
638            f.call(benchmark, subBenchmark);
639
640            t.end();
641
642            benchmark.addTimingFor(name, t.time);
643        }
644
645        export function runBenchmarks() {
646            for (var i = 0; i < benchmarks.length; i++) {
647                var b = new benchmarks[i]();
648
649
650                var t = new Timer();
651                b.before();
652                for (var j = 0; j < b.iterations; j++) {
653                    b.beforeEach();
654                    timeFunction(b);
655                    b.afterEach();
656                }
657                b.after();
658
659                for (var prop in b.results) {
660                    var description = b.description + (prop ? ": " + prop : '');
661
662                    emitLog('testStart', { desc: description });
663
664                    emitLog('pass', {
665                        desc: description, pass: true, perfResults: {
666                            mean: b.results[prop].mean(),
667                            min: b.results[prop].min(),
668                            max: b.results[prop].max(),
669                            stdDev: b.results[prop].stdDev(),
670                            trials: b.results[prop].data
671                        }
672                    });
673                }
674
675            }
676        }
677
678        // Replace with better type when classes are assignment compatible with
679        // the below type.
680        // export function addBenchmark(BenchmarkClass: {new(): Benchmark;}) {
681        export function addBenchmark(BenchmarkClass: any) {
682            benchmarks.push(BenchmarkClass);
683        }
684
685    }
686
687    /** Functionality for compiling TypeScript code */
688    export module Compiler {
689        /** Aggregate various writes into a single array of lines. Useful for passing to the
690         *  TypeScript compiler to fill with source code or errors.
691         */
692        export class WriterAggregator implements ITextWriter {
693            public lines: string[] = [];
694            public currentLine = "";
695
696            public Write(str) {
697                this.currentLine += str;
698            }
699
700            public WriteLine(str) {
701                this.lines.push(this.currentLine + str);
702                this.currentLine = "";
703            }
704
705            public Close() {
706                if (this.currentLine.length > 0) { this.lines.push(this.currentLine); }
707                this.currentLine = "";
708            }
709
710            public reset() {
711                this.lines = [];
712                this.currentLine = "";
713            }
714        }
715
716        /** Mimics having multiple files, later concatenated to a single file. */
717        export class EmitterIOHost implements TypeScript.EmitterIOHost {
718
719            private fileCollection = {};
720
721            /** create file gets the whole path to create, so this works as expected with the --out parameter */
722            public createFile(s: string, useUTF8?: boolean): ITextWriter {
723
724                if (this.fileCollection[s]) {
725                    return <ITextWriter>this.fileCollection[s];
726                }
727
728                var writer = new Harness.Compiler.WriterAggregator();
729                this.fileCollection[s] = writer;
730                return writer;
731            }
732
733            public directoryExists(s: string) { return false; }
734            public fileExists(s: string) { return typeof this.fileCollection[s] !== 'undefined'; }
735            public resolvePath(s: string) { return s; }
736
737            public reset() { this.fileCollection = {}; }
738
739            public toArray(): { filename: string; file: WriterAggregator; }[] {
740                var result: { filename: string; file: WriterAggregator; }[] = [];
741
742                for (var p in this.fileCollection) {
743                    if (this.fileCollection.hasOwnProperty(p)) {
744                        var current = <Harness.Compiler.WriterAggregator>this.fileCollection[p];
745                        if (current.lines.length > 0) {
746                            if (p !== '0.js') { current.lines.unshift('////[' + p + ']'); }
747                            result.push({ filename: p, file: this.fileCollection[p] });
748                        }
749                    }
750                }
751                return result;
752            }
753        }
754
755        var libFolder: string = global['WScript'] ? TypeScript.filePath(global['WScript'].ScriptFullName) : (__dirname + '/');
756        export var libText = IO ? IO.readFile(libFolder + "lib.d.ts") : '';
757
758        var stdout = new EmitterIOHost();
759        var stderr = new WriterAggregator();
760
761        export function isDeclareFile(filename: string) {
762            return /\.d\.ts$/.test(filename);
763        }
764
765        export function makeDefaultCompilerForTest(c?: TypeScript.TypeScriptCompiler) {
766            var compiler = c || new TypeScript.TypeScriptCompiler(stderr);
767            compiler.parser.errorRecovery = true;
768            compiler.settings.codeGenTarget = TypeScript.CodeGenTarget.ES5;
769            compiler.settings.controlFlow = true;
770            compiler.settings.controlFlowUseDef = true;
771            if (Harness.usePull) {
772                compiler.settings.usePull = true;
773                compiler.settings.useFidelity = true;
774            }
775
776            compiler.parseEmitOption(stdout);
777            TypeScript.moduleGenTarget = TypeScript.ModuleGenTarget.Synchronous;
778            compiler.addUnit(Harness.Compiler.libText, "lib.d.ts", true);
779            return compiler;
780        }
781
782        var compiler: TypeScript.TypeScriptCompiler;
783        recreate();
784
785        // pullUpdateUnit is sufficient if an existing unit is updated, if a new unit is added we need to do a full typecheck
786        var needsFullTypeCheck = true;
787        export function compile(code?: string, filename?: string) {
788            if (usePull) {
789                if (needsFullTypeCheck) {
790                    compiler.pullTypeCheck(true);
791                    needsFullTypeCheck = false;
792                }
793                else {
794                    // requires unit to already exist in the compiler
795                    compiler.pullUpdateUnit(new TypeScript.StringSourceText(""), filename, true);
796                    compiler.pullUpdateUnit(new TypeScript.StringSourceText(code), filename, true);
797                }
798            }
799            else {
800                compiler.reTypeCheck();
801            }
802        }
803
804        // Types
805        export class Type {
806            constructor(public type, public code, public identifier) { }
807
808            public normalizeToArray(arg: any) {
809                if ((Array.isArray && Array.isArray(arg)) || arg instanceof Array)
810                    return arg;
811
812                return [arg];
813            }
814
815            public compilesOk(testCode): boolean {
816                var errors = null;
817                compileString(testCode, 'test.ts', function (compilerResult) {
818                    errors = compilerResult.errors;
819                })
820
821                return errors.length === 0;
822            }
823
824            public isSubtypeOf(other: Type) {
825                var testCode = 'class __test1__ {\n';
826                testCode += '    public test() {\n';
827                testCode += '        ' + other.code + ';\n';
828                testCode += '        return ' + other.identifier + ';\n';
829                testCode += '    }\n';
830                testCode += '}\n';
831                testCode += 'class __test2__ extends __test1__ {\n';
832                testCode += '    public test() {\n';
833                testCode += '        ' + this.code + ';\n';
834                testCode += '        return ' + other.identifier + ';\n';
835                testCode += '    }\n';
836                testCode += '}\n';
837
838                return this.compilesOk(testCode);
839            }
840
841            // TODO: Find an implementation of isIdenticalTo that works.
842            //public isIdenticalTo(other: Type) {
843            //    var testCode = 'module __test1__ {\n';
844            //    testCode += '    ' + this.code + ';\n';
845            //    testCode += '    export var __val__ = ' + this.identifier + ';\n';
846            //    testCode += '}\n';
847            //    testCode += 'var __test1__val__ = __test1__.__val__;\n';
848
849            //    testCode += 'module __test2__ {\n';
850            //    testCode += '    ' + other.code + ';\n';
851            //    testCode += '    export var __val__ = ' + other.identifier + ';\n';
852            //    testCode += '}\n';
853            //    testCode += 'var __test2__val__ = __test2__.__val__;\n';
854
855            //    testCode += 'function __test__function__() { if(true) { return __test1__val__ }; return __test2__val__; }';
856
857            //    return this.compilesOk(testCode);
858            //}
859
860            public assertSubtypeOf(others: any) {
861                others = this.normalizeToArray(others);
862
863                for (var i = 0; i < others.length; i++) {
864                    if (!this.isSubtypeOf(others[i])) {
865                        throw new Error("Expected " + this.type + " to be a subtype of " + others[i].type);
866                    }
867                }
868            }
869
870            public assertNotSubtypeOf(others: any) {
871                others = this.normalizeToArray(others);
872
873                for (var i = 0; i < others.length; i++) {
874                    if (this.isSubtypeOf(others[i])) {
875                        throw new Error("Expected " + this.type + " to be a subtype of " + others[i].type);
876                    }
877                }
878            }
879
880            //public assertIdenticalTo(other: Type) {
881            //    if (!this.isIdenticalTo(other)) {
882            //        throw new Error("Expected " + this.type + " to be identical to " + other.type);
883            //    }
884            //}
885
886            //public assertNotIdenticalTo(other: Type) {
887            //    if (!this.isIdenticalTo(other)) {
888            //        throw new Error("Expected " + this.type + " to not be identical to " + other.type);
889            //    }
890            //}
891
892            public isAssignmentCompatibleWith(other: Type) {
893                var testCode = 'module __test1__ {\n';
894                testCode += '    ' + this.code + ';\n';
895                testCode += '    export var __val__ = ' + this.identifier + ';\n';
896                testCode += '}\n';
897                testCode += 'var __test1__val__ = __test1__.__val__;\n';
898
899                testCode += 'module __test2__ {\n';
900                testCode += '    export ' + other.code + ';\n';
901                testCode += '    export var __val__ = ' + other.identifier + ';\n';
902                testCode += '}\n';
903                testCode += 'var __test2__val__ = __test2__.__val__;\n';
904
905                testCode += '__test2__val__ = __test1__val__;';
906
907                return this.compilesOk(testCode);
908            }
909
910            public assertAssignmentCompatibleWith(others: any) {
911                others = this.normalizeToArray(others);
912
913                for (var i = 0; i < others.length; i++) {
914                    var other = others[i];
915
916                    if (!this.isAssignmentCompatibleWith(other)) {
917                        throw new Error("Expected " + this.type + " to be assignment compatible with " + other.type);
918                    }
919                }
920            }
921
922            public assertNotAssignmentCompatibleWith(others: any) {
923                others = this.normalizeToArray(others);
924
925                for (var i = 0; i < others.length; i++) {
926                    var other = others[i];
927
928                    if (this.isAssignmentCompatibleWith(other)) {
929                        throw new Error("Expected " + this.type + " to not be assignment compatible with " + other.type);
930                    }
931                }
932            }
933
934            public assertThisCanBeAssignedTo(desc: string, these: any[], notThese: any[]) {
935                it(desc + " is assignable to ", () => {
936                    this.assertAssignmentCompatibleWith(these);
937                });
938
939                it(desc + " not assignable to ", () => {
940                    this.assertNotAssignmentCompatibleWith(notThese);
941                });
942            }
943
944        }
945
946        export class TypeFactory {
947            public any: Type;
948            public number: Type;
949            public string: Type;
950            public boolean: Type;
951
952            constructor() {
953                this.any = this.get('var x : any', 'x');
954                this.number = this.get('var x : number', 'x');
955                this.string = this.get('var x : string', 'x');
956                this.boolean = this.get('var x : boolean', 'x');
957            }
958
959            public get (code: string, target: any) {
960                var targetIdentifier = '';
961                var targetPosition = -1;
962                if (typeof target === "string") {
963                    targetIdentifier = target;
964                }
965                else if (typeof target === "number") {
966                    targetPosition = target;
967                }
968                else {
969                    throw new Error("Expected string or number not " + (typeof target));
970                }
971
972                var errors = null;
973                compileString(code, 'test.ts', function (compilerResult) {
974                    errors = compilerResult.errors;
975                })
976
977                if (errors.length > 0)
978                    throw new Error("Type definition contains errors: " + errors.join(","));
979
980                var matchingIdentifiers: Type[] = [];
981
982                if (!usePull) {
983                    // This will find the requested identifier in the first script where it's present, a naive search of each member in each script,
984                    // which means this won't play nicely if the same identifier is used in multiple units, but it will enable this to work on multi-file tests.
985                    // m = 1 because the first script will always be lib.d.ts which we don't want to search.
986                    for (var m = 1; m < compiler.scripts.members.length; m++) {
987                        var script = compiler.scripts.members[m];
988                        var enclosingScopeContext = TypeScript.findEnclosingScopeAt(new TypeScript.NullLogger(), <TypeScript.Script>script, new TypeScript.StringSourceText(code), 0, false);
989                        var entries = new TypeScript.ScopeTraversal(compiler).getScopeEntries(enclosingScopeContext);
990
991                        for (var i = 0; i < entries.length; i++) {
992                            if (entries[i].name === targetIdentifier) {
993                                matchingIdentifiers.push(new Type(entries[i].type, code, targetIdentifier));
994                            }
995                        }
996                    }
997                }
998                else {
999                    for (var m = 0; m < compiler.scripts.members.length; m++) {
1000                        var script2 = <TypeScript.Script>compiler.scripts.members[m];
1001                        if (script2.locationInfo.filename !== 'lib.d.ts') {
1002                            if (targetPosition > -1) {
1003                                var tyInfo = compiler.pullGetTypeInfoAtPosition(targetPosition, script2);
1004                                var name = this.getTypeInfoName(tyInfo.ast);
1005                                var foundValue = new Type(tyInfo.typeInfo, code, name);
1006                                if (!matchingIdentifiers.some(value => (value.identifier === foundValue.identifier) && (value.code === foundValue.code) && (value.type === foundValue.type))) {
1007                                    matchingIdentifiers.push(foundValue);
1008                                }
1009                            }
1010                            else {
1011                                for (var pos = 0; pos < code.length; pos++) {
1012                                    var tyInfo = compiler.pullGetTypeInfoAtPosition(pos, script2);
1013                                    var name = this.getTypeInfoName(tyInfo.ast);
1014                                    if (name === targetIdentifier) {
1015                                        var foundValue = new Type(tyInfo.typeInfo, code, targetIdentifier);
1016                                        if (!matchingIdentifiers.some(value => (value.identifier === foundValue.identifier) && (value.code === foundValue.code) && (value.type === foundValue.type))) {
1017                                            matchingIdentifiers.push(foundValue);
1018                                        }
1019                                    }
1020                                }
1021                            }
1022                        }
1023                    }
1024                }
1025
1026                if (matchingIdentifiers.length === 0) {
1027                    if (targetPosition > -1) {
1028                        throw new Error("Could not find an identifier at position " + targetPosition);
1029                    }
1030                    else {
1031                        throw new Error("Could not find an identifier " + targetIdentifier + " in any known scopes");
1032                    }
1033                }
1034                else if (matchingIdentifiers.length > 1) {
1035                    throw new Error("Found multiple matching identifiers for " + target);
1036                }
1037                else {
1038                    return matchingIdentifiers[0];
1039                }
1040            }
1041
1042            private getTypeInfoName(ast : TypeScript.AST) {
1043                var name = '';
1044                switch (ast.nodeType) {
1045                    case TypeScript.NodeType.Name: // Type Name?
1046                    case TypeScript.NodeType.Null:
1047                    case TypeScript.NodeType.List:
1048                    case TypeScript.NodeType.Empty:
1049                    case TypeScript.NodeType.EmptyExpr:
1050                    case TypeScript.NodeType.Asg:
1051                    case TypeScript.NodeType.True:
1052                    case TypeScript.NodeType.False:
1053                    case TypeScript.NodeType.ArrayLit:
1054                    case TypeScript.NodeType.TypeRef:
1055                        break;
1056                    case TypeScript.NodeType.Super:
1057                        name = (<any>ast).text;
1058                        break;
1059                    case TypeScript.NodeType.Regex:
1060                        name = (<TypeScript.RegexLiteral>ast).text;
1061                        break;
1062                    case TypeScript.NodeType.QString:
1063                        name = (<any>ast).text;
1064                        break;
1065                    case TypeScript.NodeType.NumberLit:
1066                        name = (<TypeScript.NumberLiteral>ast).text;
1067                        break;
1068                    case TypeScript.NodeType.Return:
1069                        //name = (<TypeScript.ReturnStatement>tyInfo.ast).returnExpression.actualText; // why is this complaining?
1070                        break;
1071                    case TypeScript.NodeType.InterfaceDeclaration:
1072                        name = (<TypeScript.InterfaceDeclaration>ast).name.actualText;
1073                        break;
1074                    case TypeScript.NodeType.ModuleDeclaration:
1075                        name = (<TypeScript.ModuleDeclaration>ast).name.actualText;
1076                        break;
1077                    case TypeScript.NodeType.ClassDeclaration:
1078                        name = (<TypeScript.ClassDeclaration>ast).name.actualText;
1079                        break;
1080                    case TypeScript.NodeType.FuncDecl:
1081                        name = !(<TypeScript.FuncDecl>ast).name ? "" : (<TypeScript.FuncDecl>ast).name.actualText; // name == null for lambdas
1082                        break;
1083                    default:
1084                        // TODO: is there a reason to mess with all the special cases above and not just do this (ie take whatever property is there and works?)
1085                        var a = <any>ast;
1086                        name = (a.id) ? (a.id.actualText) : (a.name) ? a.name.actualText : (a.text) ? a.text : '';
1087                        break;
1088                }
1089
1090                return name;
1091            }
1092
1093            public isOfType(expr: string, expectedType: string) {
1094                var actualType = this.get('var _v_a_r_ = ' + expr, '_v_a_r_');
1095
1096                it('Expression "' + expr + '" is of type "' + expectedType + '"', function () {
1097                    assert.equal(actualType.type, expectedType);
1098                });
1099            }
1100        }
1101
1102        /** Generates a .d.ts file for the given code
1103          * @param verifyNoDeclFile pass true when the given code should generate no decl file, false otherwise
1104          * @param unitName add the given code under thie name, else use '0.ts'
1105          * @param compilationContext a set of functions to be run before and after compiling this code for doing things like adding dependencies first
1106          * @param references the set of referenced files used by the given code
1107          */
1108        export function generateDeclFile(code: string, verifyNoDeclFile: boolean, unitName?: string, compilationContext?: Harness.Compiler.CompilationContext, references?: TypeScript.IFileReference[]): string {
1109            reset();
1110
1111            compiler.settings.generateDeclarationFiles = true;
1112            var oldOutputOption = compiler.settings.outputOption;
1113            var oldEmitterIOHost = compiler.emitSettings.ioHost;
1114            try {
1115                if (compilationContext && compilationContext.preCompile) {
1116                    compilationContext.preCompile();
1117                }
1118
1119                addUnit(code, unitName, false, false, references);
1120                compiler.reTypeCheck();
1121
1122                var outputs = {};
1123
1124                compiler.settings.outputOption = "";
1125                compiler.parseEmitOption(
1126                    {
1127                        createFile: (fn: string) => {
1128                            outputs[fn] = new Harness.Compiler.WriterAggregator();
1129                            return outputs[fn];
1130                        },
1131                        directoryExists: (path: string) => true,
1132                        fileExists: (path: string) => true,
1133                        resolvePath: (path: string) => path
1134                    });
1135                compiler.emitDeclarations();
1136
1137                var results: string = null;
1138                for (var fn in outputs) {
1139                    if (fn.indexOf('.d.ts') >= 0) {
1140                        var writer = <Harness.Compiler.WriterAggregator>outputs[fn];
1141                        writer.Close();
1142                        results = writer.lines.join('\n');
1143                        if (verifyNoDeclFile && results != "") {
1144                            throw new Error('Compilation should not produce ' + fn);
1145                        }
1146                    }
1147                }
1148
1149                if (results) {
1150                    return results;
1151                }
1152
1153                if (!verifyNoDeclFile) {
1154                    throw new Error('Compilation did not produce .d.ts files');
1155                }
1156            } finally {
1157                compiler.settings.generateDeclarationFiles = false;
1158                compiler.settings.outputOption = oldOutputOption;
1159                compiler.parseEmitOption(oldEmitterIOHost);
1160                if (compilationContext && compilationContext.postCompile) {
1161                    compilationContext.postCompile();
1162                }
1163
1164                var uName = unitName || '0.ts';
1165                updateUnit('', uName);
1166            }
1167
1168            return '';
1169        }
1170
1171        /** Contains the code and errors of a compilation and some helper methods to check its status. */
1172        export class CompilerResult {
1173            public code: string;
1174            public errors: CompilerError[];
1175
1176            /** @param fileResults an array of strings for the filename and an ITextWriter with its code */
1177            constructor(public fileResults: { filename: string; file: WriterAggregator; }[], errorLines: string[], public scripts: TypeScript.Script[]) {
1178                var lines = [];
1179                fileResults.forEach(v => lines = lines.concat(v.file.lines));
1180                this.code = lines.join("\n")
1181
1182                this.errors = [];
1183
1184                for (var i = 0; i < errorLines.length; i++) {
1185                    if (Harness.usePull) {
1186                        var err = <any>errorLines[i]; // TypeScript.PullError
1187                        this.errors.push(new CompilerError(err.filename, 0, 0, err.message));
1188                    } else {
1189                        var match = errorLines[i].match(/([^\(]*)\((\d+),(\d+)\):\s+((.*[\s\r\n]*.*)+)\s*$/);
1190                        if (match) {
1191                            this.errors.push(new CompilerError(match[1], parseFloat(match[2]), parseFloat(match[3]), match[4]));
1192                        }
1193                        else {
1194                            WScript.Echo("non-match on: " + errorLines[i]);
1195                        }
1196                    }
1197                }
1198            }
1199
1200            public isErrorAt(line: number, column: number, message: string) {
1201                for (var i = 0; i < this.errors.length; i++) {
1202                    if (this.errors[i].line === line && this.errors[i].column === column && this.errors[i].message === message)
1203                        return true;
1204                }
1205
1206                return false;
1207            }
1208        }
1209
1210        // Compiler Error.
1211        export class CompilerError {
1212            constructor(public file: string,
1213                    public line: number,
1214                    public column: number,
1215                    public message: string) { }
1216
1217            public toString() {
1218                return this.file + "(" + this.line + "," + this.column + "): " + this.message;
1219            }
1220        }
1221
1222        /** Create a new instance of the compiler with default settings and lib.d.ts, then typecheck */
1223        export function recreate() {
1224            compiler = makeDefaultCompilerForTest();
1225            if (usePull) {
1226                compiler.pullTypeCheck(true);
1227            }
1228            else {
1229                compiler.typeCheck();
1230            }
1231        }
1232
1233        export function reset() {
1234            stdout.reset();
1235            stderr.reset();
1236
1237            var files = compiler.units.map((value) => value.filename);
1238
1239            for (var i = 0; i < files.length; i++) {
1240                var fname = files[i];
1241                if(fname !== 'lib.d.ts') {
1242                    updateUnit('', fname);
1243                    }
1244            }
1245
1246            compiler.errorReporter.hasErrors = false;
1247        }
1248
1249        // Defines functions to invoke before compiling a piece of code and a post compile action intended to clean up the
1250        // effects of preCompile, preferably with something lighter weight than a full recreate()
1251        export interface CompilationContext {
1252            filename: string;
1253            preCompile: () => void;
1254            postCompile: () => void;
1255        }
1256
1257        export function addUnit(code: string, unitName?: string, isResident?: boolean, isDeclareFile?: boolean, references?: TypeScript.IFileReference[]) {
1258            var script: TypeScript.Script = null;
1259            var uName = unitName || '0' + (isDeclareFile ? '.d.ts' : '.ts');
1260
1261            for (var i = 0; i < compiler.units.length; i++) {
1262                if (compiler.units[i].filename === uName) {
1263                    updateUnit(code, uName);
1264                    script = <TypeScript.Script>compiler.scripts.members[i];
1265                }
1266            }
1267            if (!script) {
1268                // TODO: make this toggleable, shouldn't be necessary once typecheck bugs are cleaned up
1269                // but without it subsequent tests are treated as edits, making for somewhat useful stress testing
1270                // of persistent typecheck state
1271                //compiler.addUnit("", uName, isResident, references); // equivalent to compiler.deleteUnit(...)
1272                script = compiler.addUnit(code, uName, isResident, references);
1273                needsFullTypeCheck = true;
1274            }
1275
1276            return script;
1277        }
1278
1279        export function updateUnit(code: string, unitName: string, setRecovery?: boolean) {
1280            if (Harness.usePull) {
1281                compiler.pullUpdateUnit(new TypeScript.StringSourceText(code), unitName, setRecovery);
1282            } else {
1283                compiler.updateUnit(code, unitName, setRecovery);
1284            }
1285        }
1286
1287        export function compileFile(path: string, callback: (res: CompilerResult) => void , settingsCallback?: (settings?: TypeScript.CompilationSettings) => void , context?: CompilationContext, references?: TypeScript.IFileReference[]) {
1288            path = switchToForwardSlashes(path);
1289            var filename = path.match(/[^\/]*$/)[0];
1290            var code = readFile(path);
1291
1292            compileUnit(code, filename, callback, settingsCallback, context, references);
1293        }
1294
1295        export function compileUnit(code: string, filename: string, callback: (res: CompilerResult) => void , settingsCallback?: (settings?: TypeScript.CompilationSettings) => void , context?: CompilationContext, references?: TypeScript.IFileReference[]) {
1296            // not recursive
1297            function clone/* <T> */(source: any, target: any) {
1298                for (var prop in source) {
1299                    target[prop] = source[prop];
1300                }
1301            }
1302
1303            var oldCompilerSettings = new TypeScript.CompilationSettings();
1304            clone(compiler.settings, oldCompilerSettings);
1305            var oldEmitSettings = new TypeScript.EmitOptions(compiler.settings);
1306            clone(compiler.emitSettings, oldEmitSettings);
1307
1308            var oldModuleGenTarget = TypeScript.moduleGenTarget;
1309
1310            if (settingsCallback) {
1311                settingsCallback(compiler.settings);
1312                compiler.emitSettings = new TypeScript.EmitOptions(compiler.settings);
1313            }
1314            try {
1315                compileString(code, filename, callback, context, references);
1316            } finally {
1317                // If settingsCallback exists, assume that it modified the global compiler instance's settings in some way.
1318                // So that a test doesn't have side effects for tests run after it, restore the compiler settings to their previous state.
1319                if (settingsCallback) {
1320                    compiler.settings = oldCompilerSettings;
1321                    compiler.emitSettings = oldEmitSettings;
1322                    TypeScript.moduleGenTarget = oldModuleGenTarget;
1323                }
1324            }
1325        }
1326
1327        export function compileUnits(units: TestCaseParser.TestUnitData[], callback: (res: Compiler.CompilerResult) => void , settingsCallback?: () => void ) {
1328            var lastUnit = units[units.length - 1];
1329            var unitName = switchToForwardSlashes(lastUnit.name).match(/[^\/]*$/)[0];
1330
1331            var dependencies = units.slice(0, units.length - 1);
1332            var compilationContext = Harness.Compiler.defineCompilationContextForTest(unitName, dependencies);
1333
1334            compileUnit(lastUnit.content, unitName, callback, settingsCallback, compilationContext, lastUnit.references);
1335        }
1336
1337        export function emitToOutfile(outfile: WriterAggregator) {
1338            compiler.emitToOutfile(outfile);
1339        }
1340
1341        export function emit(ioHost: TypeScript.EmitterIOHost, usePullEmitter?: boolean) {
1342            compiler.emit(ioHost, usePullEmitter);
1343        }
1344
1345        export function compileString(code: string, unitName: string, callback: (res: Compiler.CompilerResult) => void , context?: CompilationContext, references?: TypeScript.IFileReference[]) {
1346            var scripts: TypeScript.Script[] = [];
1347
1348            reset();
1349
1350            if (context) {
1351                context.preCompile();
1352            }
1353
1354            var isDeclareFile = Harness.Compiler.isDeclareFile(unitName);
1355            // for single file tests just add them as using the old '0.ts' naming scheme
1356            var uName = context ? unitName : ((isDeclareFile) ? '0.d.ts' : '0.ts');
1357            scripts.push(addUnit(code, uName, false, isDeclareFile, references));
1358            compile(code, uName);
1359
1360            var errors;
1361            if (usePull) {
1362                // TODO: no emit support with pull yet
1363                errors = compiler.pullGetErrorsForFile(uName);
1364                emit(stdout, true);
1365            }
1366            else {
1367                errors = stderr.lines;
1368                emit(stdout, false);
1369                //output decl file
1370                compiler.emitDeclarations();
1371            }
1372
1373            if (context) {
1374                context.postCompile();
1375            }
1376
1377            callback(new CompilerResult(stdout.toArray(), errors, scripts));
1378        }
1379
1380        /** Returns a set of functions which can be later executed to add and remove given dependencies to the compiler so that
1381         *  a file can be successfully compiled. These functions will add/remove named units and code to the compiler for each dependency.
1382         */
1383        export function defineCompilationContextForTest(filename: string, dependencies: TestCaseParser.TestUnitData[]): CompilationContext {
1384            // if the given file has no dependencies, there is no context to return, it can be compiled without additional work
1385            if (dependencies.length == 0) {
1386                return null;
1387            } else {
1388                var addedFiles = [];
1389                var precompile = () => {
1390                    // REVIEW: if any dependency has a triple slash reference then does postCompile potentially have to do a recreate since we can't update references with updateUnit?
1391                    // easy enough to do if so, prefer to avoid the recreate cost until it proves to be an issue
1392                    dependencies.forEach(dep => {
1393                        addUnit(dep.content, dep.name, false, Harness.Compiler.isDeclareFile(dep.name));
1394                        addedFiles.push(dep.name);
1395                    });
1396                };
1397                var postcompile = () => {
1398                    addedFiles.forEach(file => {
1399                        updateUnit('', file);
1400                    });
1401                };
1402                var context = {
1403                    filename: filename,
1404                    preCompile: precompile,
1405                    postCompile: postcompile
1406                };
1407                return context;
1408            }
1409        }
1410    }
1411
1412    /** Parses the test cases files
1413     *  extracts options and individual files in a multifile test
1414     */
1415    export module TestCaseParser {
1416        /** all the necesarry information to set the right compiler settings */
1417        export interface CompilerSetting {
1418            flag: string;
1419            value: string;
1420        }
1421
1422        /** All the necessary information to turn a multi file test into useful units for later compilation */
1423        export interface TestUnitData {
1424            content: string;
1425            name: string;
1426            originalFilePath: string;
1427            references: TypeScript.IFileReference[];
1428        }
1429
1430        // Regex for parsing options in the format "@Alpha: Value of any sort"
1431        private optionRegex = /^[\/]{2}\s*@(\w+):\s*(\S*)/gm;  // multiple matches on multiple lines
1432
1433        // List of allowed metadata names
1434        var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out"];
1435
1436        function extractCompilerSettings(content: string): CompilerSetting[] {
1437
1438            var opts = [];
1439
1440            var match;
1441            while ((match = optionRegex.exec(content)) != null) {
1442                opts.push({ flag: match[1], value: match[2] });
1443            }
1444
1445            return opts;
1446        }
1447
1448        /** Given a test file containing // @Filename directives, return an array of named units of code to be added to an existing compiler instance */
1449        export function makeUnitsFromTest(code: string, filename: string): { settings: CompilerSetting[]; testUnitData: TestUnitData[]; } {
1450
1451            var settings = extractCompilerSettings(code);
1452
1453            // List of all the subfiles we've parsed out
1454            var files: TestUnitData[] = [];
1455
1456            var lines = splitContentByNewlines(code);
1457
1458            // Stuff related to the subfile we're parsing
1459            var currentFileContent: string = null;
1460            var currentFileOptions = {};
1461            var currentFileName = null;
1462            var refs: TypeScript.IFileReference[] = [];
1463
1464            for (var i = 0; i < lines.length; i++) {
1465                var line = lines[i];
1466                var isTripleSlashReference = /[\/]{3}\s*<reference path/.test(line);
1467                var testMetaData = optionRegex.exec(line);
1468                // Triple slash references need to be tracked as they are added to the compiler as an additional parameter to addUnit
1469                if (isTripleSlashReference) {
1470                    var isRef = line.match(/reference\spath='(\w*_?\w*\.?d?\.ts)'/);
1471                    if (isRef) {
1472                        var ref = {
1473                            minChar: 0,
1474                            limChar: 0,
1475                            startLine:0,
1476                            startCol:0,
1477                            path: isRef[1],
1478                            isResident: false
1479                        };
1480
1481                        refs.push(ref);
1482                    }
1483                } else if (testMetaData) {
1484                    // Comment line, check for global/file @options and record them
1485                    optionRegex.lastIndex = 0;
1486                    var fileNameIndex = fileMetadataNames.indexOf(testMetaData[1].toLowerCase());
1487                    if (fileNameIndex == -1) {
1488                        throw new Error('Unrecognized metadata name "' + testMetaData[1] + '". Available file metadata names are: ' + fileMetadataNames.join(', '));
1489                    } else if (fileNameIndex == 0) {
1490                        currentFileOptions[testMetaData[1]] = testMetaData[2];
1491                    } else {
1492                        continue;
1493                    }
1494
1495                    // New metadata statement after having collected some code to go with the previous metadata
1496                    if (currentFileName) {
1497                        // Store result file
1498                        var newTestFile =
1499                            {
1500                                content: currentFileContent,
1501                                name: currentFileName,
1502                                fileOptions: currentFileOptions,
1503                                originalFilePath: filename,
1504                                references: refs
1505                            };
1506                        files.push(newTestFile);
1507
1508                        // Reset local data
1509                        currentFileContent = null;
1510                        currentFileOptions = {};
1511                        currentFileName = testMetaData[2];
1512                        refs = [];
1513                    } else {
1514                        // First metadata marker in the file
1515                        currentFileName = testMetaData[2];
1516                    }
1517                } else {
1518                    // Subfile content line
1519                    // Append to the current subfile content, inserting a newline needed
1520                    if (currentFileContent === null) {
1521                        currentFileContent = '';
1522                    } else {
1523                        // End-of-line
1524                        currentFileContent = currentFileContent + '\n';
1525                    }
1526                    currentFileContent = currentFileContent + line;
1527                }
1528            }
1529
1530            // normalize the filename for the single file case
1531            currentFileName = files.length > 0 ? currentFileName : '0.ts';
1532
1533            // EOF, push whatever remains
1534            var newTestFile = {
1535                content: currentFileContent || '',
1536                name: currentFileName,
1537                fileOptions: currentFileOptions,
1538                originalFilePath: filename,
1539                references: refs
1540            };
1541            files.push(newTestFile);
1542
1543            return { settings: settings, testUnitData: files };
1544        }
1545    }
1546
1547    export class ScriptInfo {
1548        public version: number;
1549        public editRanges: { length: number; editRange: TypeScript.ScriptEditRange; }[] = [];
1550
1551        constructor(public name: string, public content: string, public isResident: boolean, public maxScriptVersions: number) {
1552            this.version = 1;
1553        }
1554
1555        public updateContent(content: string, isResident: boolean) {
1556            this.editRanges = [];
1557            this.content = content;
1558            this.isResident = isResident;
1559            this.version++;
1560        }
1561
1562        public editContent(minChar: number, limChar: number, newText: string) {
1563            // Apply edits
1564            var prefix = this.content.substring(0, minChar);
1565            var middle = newText;
1566            var suffix = this.content.substring(limChar);
1567            this.content = prefix + middle + suffix;
1568
1569            // Store edit range + new length of script
1570            this.editRanges.push({
1571                length: this.content.length,
1572                editRange: new TypeScript.ScriptEditRange(minChar, limChar, (limChar - minChar) + newText.length)
1573            });
1574
1575            if (this.editRanges.length > this.maxScriptVersions) {
1576                this.editRanges.splice(0, this.maxScriptVersions - this.editRanges.length);
1577            }
1578
1579            // Update version #
1580            this.version++;
1581        }
1582
1583        public getEditRangeSinceVersion(version: number): TypeScript.ScriptEditRange {
1584            if (this.version == version) {
1585                // No edits!
1586                return null;
1587            }
1588
1589            var initialEditRangeIndex = this.editRanges.length - (this.version - version);
1590            if (initialEditRangeIndex < 0 || initialEditRangeIndex >= this.editRanges.length) {
1591                // Too far away from what we know
1592                return TypeScript.ScriptEditRange.unknown();
1593            }
1594
1595            var entries = this.editRanges.slice(initialEditRangeIndex);
1596
1597            var minDistFromStart = entries.map(x => x.editRange.minChar).reduce((prev, current) => Math.min(prev, current));
1598            var minDistFromEnd = entries.map(x => x.length - x.editRange.limChar).reduce((prev, current) => Math.min(prev, current));
1599            var aggDelta = entries.map(x => x.editRange.delta).reduce((prev, current) => prev + current);
1600
1601            return new TypeScript.ScriptEditRange(minDistFromStart, entries[0].length - minDistFromEnd, aggDelta);
1602        }
1603    }
1604
1605    export class TypeScriptLS implements Services.ILanguageServiceShimHost {
1606        private ls: Services.ILanguageServiceShim = null;
1607
1608        public scripts: ScriptInfo[] = [];
1609        public maxScriptVersions = 100;
1610
1611        public addDefaultLibrary() {
1612            this.addScript("lib.d.ts", Harness.Compiler.libText, true);
1613        }
1614
1615        public addFile(name: string, isResident = false) {
1616            var code: string = readFile(name);
1617            this.addScript(name, code, isResident);
1618        }
1619
1620        public addScript(name: string, content: string, isResident = false) {
1621            var script = new ScriptInfo(name, content, isResident, this.maxScriptVersions);
1622            this.scripts.push(script);
1623        }
1624
1625        public updateScript(name: string, content: string, isResident = false) {
1626            for (var i = 0; i < this.scripts.length; i++) {
1627                if (this.scripts[i].name == name) {
1628                    this.scripts[i].updateContent(content, isResident);
1629                    return;
1630                }
1631            }
1632
1633            this.addScript(name, content, isResident);
1634        }
1635
1636        public editScript(name: string, minChar: number, limChar: number, newText: string) {
1637            for (var i = 0; i < this.scripts.length; i++) {
1638                if (this.scripts[i].name == name) {
1639                    this.scripts[i].editContent(minChar, limChar, newText);
1640                    return;
1641                }
1642            }
1643
1644            throw new Error("No script with name '" + name + "'");
1645        }
1646
1647        public getScriptContent(scriptIndex: number): string {
1648            return this.scripts[scriptIndex].content;
1649        }
1650
1651        //////////////////////////////////////////////////////////////////////
1652        // ILogger implementation
1653        //
1654        public information(): boolean { return false; }
1655        public debug(): boolean { return true; }
1656        public warning(): boolean { return true; }
1657        public error(): boolean { return true; }
1658        public fatal(): boolean { return true; }
1659
1660        public log(s: string): void {
1661            // For debugging...
1662            //IO.printLine("TypeScriptLS:" + s);
1663        }
1664
1665        //////////////////////////////////////////////////////////////////////
1666        // ILanguageServiceShimHost implementation
1667        //
1668
1669        public getCompilationSettings(): string/*json for Tools.CompilationSettings*/ {
1670            return ""; // i.e. default settings
1671        }
1672
1673        public getScriptCount(): number {
1674            return this.scripts.length;
1675        }
1676
1677        public getScriptSourceText(scriptIndex: number, start: number, end: number): string {
1678            return this.scripts[scriptIndex].content.substring(start, end);
1679        }
1680
1681        public getScriptSourceLength(scriptIndex: number): number {
1682            return this.scripts[scriptIndex].content.length;
1683        }
1684
1685        public getScriptId(scriptIndex: number): string {
1686            return this.scripts[scriptIndex].name;
1687        }
1688
1689        public getScriptIsResident(scriptIndex: number): boolean {
1690            return this.scripts[scriptIndex].isResident;
1691        }
1692
1693        public getScriptVersion(scriptIndex: number): number {
1694            return this.scripts[scriptIndex].version;
1695        }
1696
1697        public getScriptEditRangeSinceVersion(scriptIndex: number, scriptVersion: number): string {
1698            var range = this.scripts[scriptIndex].getEditRangeSinceVersion(scriptVersion);
1699            var result = (range.minChar + "," + range.limChar + "," + range.delta);
1700            return result;
1701        }
1702
1703        /** Return a new instance of the language service shim, up-to-date wrt to typecheck.
1704         *  To access the non-shim (i.e. actual) language service, use the "ls.languageService" property.
1705         */
1706        public getLanguageService(): Services.ILanguageServiceShim {
1707            var ls = new Services.TypeScriptServicesFactory().createLanguageServiceShim(this);
1708            ls.refresh(true);
1709            this.ls = ls;
1710            return ls;
1711        }
1712
1713        /** Parse file given its source text */
1714        public parseSourceText(fileName: string, sourceText: TypeScript.ISourceText): TypeScript.Script {
1715            var parser = new TypeScript.Parser();
1716            parser.setErrorRecovery(null);
1717            parser.errorCallback = (a, b, c, d) => { };
1718
1719            var script = parser.parse(sourceText, fileName, 0);
1720            return script;
1721        }
1722
1723        /** Parse a file on disk given its filename */
1724        public parseFile(fileName: string) {
1725            var sourceText = new TypeScript.StringSourceText(IO.readFile(fileName))
1726            return this.parseSourceText(fileName, sourceText);
1727        }
1728
1729        /**
1730         * @param line 1 based index
1731         * @param col 1 based index
1732        */
1733        public lineColToPosition(fileName: string, line: number, col: number): number {
1734            var script = this.ls.languageService.getScriptAST(fileName);
1735            assert.notNull(script);
1736            assert.is(line >= 1);
1737            assert.is(col >= 1);
1738            assert.is(line <= script.locationInfo.lineMap.length);
1739
1740            return TypeScript.getPositionFromZeroBasedLineColumn(script, line - 1, col - 1);
1741        }
1742
1743        /**
1744         * @param line 0 based index
1745         * @param col 0 based index
1746        */
1747        public positionToZeroBasedLineCol(fileName: string, position: number): TypeScript.ILineCol {
1748            var script = this.ls.languageService.getScriptAST(fileName);
1749            assert.notNull(script);
1750
1751            var result = TypeScript.getZeroBasedLineColumnFromPosition(script, position);
1752
1753            assert.is(result.line >= 0);
1754            assert.is(result.col >= 0);
1755            return result;
1756        }
1757
1758        /** Verify that applying edits to sourceFileName result in the content of the file baselineFileName */
1759        public checkEdits(sourceFileName: string, baselineFileName: string, edits: Services.TextEdit[]) {
1760            var script = readFile(sourceFileName);
1761            var formattedScript = this.applyEdits(script, edits);
1762            var baseline = readFile(baselineFileName);
1763
1764            assert.noDiff(formattedScript, baseline);
1765            assert.equal(formattedScript, baseline);
1766        }
1767
1768
1769        /** Apply an array of text edits to a string, and return the resulting string. */
1770        public applyEdits(content: string, edits: Services.TextEdit[]): string {
1771            var result = content;
1772            edits = this.normalizeEdits(edits);
1773
1774            for (var i = edits.length - 1; i >= 0; i--) {
1775                var edit = edits[i];
1776                var prefix = result.substring(0, edit.minChar);
1777                var middle = edit.text;
1778                var suffix = result.substring(edit.limChar);
1779                result = prefix + middle + suffix;
1780            }
1781            return result;
1782        }
1783
1784        /** Normalize an array of edits by removing overlapping entries and sorting entries on the minChar position. */
1785        private normalizeEdits(edits: Services.TextEdit[]): Services.TextEdit[] {
1786            var result: Services.TextEdit[] = [];
1787
1788            function mapEdits(edits: Services.TextEdit[]): { edit: Services.TextEdit; index: number; }[] {
1789                var result = [];
1790                for (var i = 0; i < edits.length; i++) {
1791                    result.push({ edit: edits[i], index: i });
1792                }
1793                return result;
1794            }
1795
1796            var temp = mapEdits(edits).sort(function (a, b) {
1797                var result = a.edit.minChar - b.edit.minChar;
1798                if (result == 0)
1799                    result = a.index - b.index;
1800                return result;
1801            });
1802
1803            var current = 0;
1804            var next = 1;
1805            while (current < temp.length) {
1806                var currentEdit = temp[current].edit;
1807
1808                // Last edit
1809                if (next >= temp.length) {
1810                    result.push(currentEdit);
1811                    current++;
1812                    continue;
1813                }
1814                var nextEdit = temp[next].edit;
1815
1816                var gap = nextEdit.minChar - currentEdit.limChar;
1817
1818                // non-overlapping edits
1819                if (gap >= 0) {
1820                    result.push(currentEdit);
1821                    current = next;
1822                    next++;
1823                    continue;
1824                }
1825
1826                // overlapping edits: for now, we only support ignoring an next edit
1827                // entirely contained in the current edit.
1828                if (currentEdit.limChar >= nextEdit.limChar) {
1829                    next++;
1830                    continue;
1831                }
1832                else {
1833                    throw new Error("Trying to apply overlapping edits");
1834                }
1835            }
1836
1837            return result;
1838        }
1839
1840        public getHostSettings(): string {
1841            return JSON.stringify({ usePullLanguageService: usePull });
1842        }
1843    }
1844
1845    // Describe/it definitions
1846    export function describe(description: string, block: () => any) {
1847        var newScenario = new Scenario(description, block);
1848
1849        if (Runnable.currentStack.length === 0) {
1850            Runnable.currentStack.push(currentRun);
1851        }
1852
1853        Runnable.currentStack[Runnable.currentStack.length - 1].addChild(newScenario);
1854    }
1855    export function it(description: string, block: () => void ) {
1856        var testCase = new TestCase(description, block);
1857        Runnable.currentStack[Runnable.currentStack.length - 1].addChild(testCase);
1858    }
1859
1860    export function run() {
1861        if (typeof process !== "undefined") {
1862            process.on('uncaughtException', Runnable.handleError);
1863        }
1864
1865        Baseline.reset();
1866        currentRun.run();
1867    }
1868
1869    /** Runs TypeScript or Javascript code. */
1870    export module Runner {
1871        export function runCollateral(path: string, callback: (error: Error, result: any) => void ) {
1872            path = switchToForwardSlashes(path);
1873            runString(readFile(path), path.match(/[^\/]*$/)[0], callback);
1874        }
1875
1876        export function runJSString(code: string, callback: (error: Error, result: any) => void ) {
1877            // List of names that get overriden by various test code we eval
1878            var dangerNames: any = ['Array'];
1879
1880            var globalBackup: any = {};
1881            var n: string = null;
1882            for (n in dangerNames) {
1883                globalBackup[dangerNames[n]] = global[dangerNames[n]];
1884            }
1885
1886            try {
1887                var res = eval(code);
1888
1889                for (n in dangerNames) {
1890                    global[dangerNames[n]] = globalBackup[dangerNames[n]];
1891                }
1892
1893                callback(null, res);
1894            } catch (e) {
1895                for (n in dangerNames) {
1896                    global[dangerNames[n]] = globalBackup[dangerNames[n]];
1897                }
1898
1899                callback(e, null);
1900            }
1901        }
1902
1903        export function runString(code: string, unitName: string, callback: (error: Error, result: any) => void ) {
1904            Compiler.compileString(code, unitName, function (res) {
1905                runJSString(res.code, callback);
1906            });
1907        }
1908    }
1909
1910    /** Support class for baseline files */
1911    export module Baseline {
1912        var reportFilename = 'baseline-report.html';
1913
1914        var firstRun = true;
1915        var htmlTrailer = '</body></html>';
1916        var htmlLeader = '<html><head><title>Baseline Report</title>';
1917        htmlLeader += ("<style>");
1918        htmlLeader += '\r\n' + (".code { font: 9pt 'Courier New'; }");
1919        htmlLeader += '\r\n' + (".old { background-color: #EE1111; }");
1920        htmlLeader += '\r\n' + (".new { background-color: #FFFF11; }");
1921        htmlLeader += '\r\n' + (".from { background-color: #EE1111; color: #1111EE; }");
1922        htmlLeader += '\r\n' + (".to { background-color: #EEEE11; color: #1111EE; }");
1923        htmlLeader += '\r\n' + ("h2 { margin-bottom: 0px; }");
1924        htmlLeader += '\r\n' + ("h2 { padding-bottom: 0px; }");
1925        htmlLeader += '\r\n' + ("h4 { font-weight: normal; }");
1926        htmlLeader += '\r\n' + ("</style>");
1927
1928        export interface BaselineOptions {
1929            LineEndingSensitive?: boolean;
1930        }
1931
1932        function localPath(filename: string) {
1933            if (global.runners[0].testType === 'prototyping') {
1934                return Harness.userSpecifiedroot + 'tests/baselines/prototyping/local/' + filename;
1935            }
1936            else {
1937                return Harness.userSpecifiedroot + 'tests/baselines/local/' + filename;
1938            }
1939        }
1940
1941        function referencePath(filename: string) {
1942            if (global.runners[0].testType === 'prototyping') {
1943                return Harness.userSpecifiedroot + 'tests/baselines/prototyping/reference/' + filename;
1944            }
1945            else {
1946                return Harness.userSpecifiedroot + 'tests/baselines/reference/' + filename;
1947            }
1948        }
1949
1950        export function reset() {
1951            if (IO.fileExists(reportFilename)) {
1952                IO.deleteFile(reportFilename);
1953            }
1954        }
1955
1956        function prepareBaselineReport(): string {
1957            var reportContent = htmlLeader;
1958            // Delete the baseline-report.html file if needed
1959            if (IO.fileExists(reportFilename)) {
1960                reportContent = IO.readFile(reportFilename);
1961                reportContent = reportContent.replace(htmlTrailer, '');
1962            } else {
1963                reportContent = htmlLeader;
1964            }
1965            return reportContent;
1966        }
1967
1968        function generateActual(actualFilename: string, generateContent: () => string): string {
1969            // Create folders if needed
1970            IO.createDirectory(IO.dirName(IO.dirName(actualFilename)));
1971            IO.createDirectory(IO.dirName(actualFilename));
1972
1973            // Delete the actual file in case it fails
1974            if (IO.fileExists(actualFilename)) {
1975                IO.deleteFile(actualFilename);
1976            }
1977
1978            var actual = generateContent();
1979
1980            if (actual === undefined) {
1981                throw new Error('The generated content was "undefined". Return "null" if no baselining is required."');
1982            }
1983
1984            // Store the content in the 'local' folder so we
1985            // can accept it later (manually)
1986            if (actual !== null) {
1987                IO.writeFile(actualFilename, actual);
1988            }
1989
1990            return actual;
1991        }
1992
1993        function compareToBaseline(actual: string, relativeFilename: string, opts: BaselineOptions) {
1994            // actual is now either undefined (the generator had an error), null (no file requested),
1995            // or some real output of the function
1996            if (actual === undefined) {
1997                // Nothing to do
1998                return;
1999            }
2000
2001            var refFilename = referencePath(relativeFilename);
2002
2003            if (actual === null) {
2004                actual = '<no content>';
2005            }
2006
2007            var expected = '<no content>';
2008            if (IO.fileExists(refFilename)) {
2009                expected = IO.readFile(refFilename);
2010            }
2011
2012            var lineEndingSensitive = opts && opts.LineEndingSensitive;
2013
2014            if (!lineEndingSensitive) {
2015                expected = expected.replace(/\r\n?/g, '\n')
2016                actual = actual.replace(/\r\n?/g, '\n')
2017            }
2018
2019            return { expected: expected, actual: actual };
2020        }
2021
2022        function writeComparison(expected: string, actual: string, relativeFilename: string, actualFilename: string, descriptionForDescribe: string) {
2023            if (expected != actual) {
2024                // Overwrite & issue error
2025                var errMsg = 'The baseline file ' + relativeFilename + ' has changed. Please refer to baseline-report.html and ';
2026                errMsg += 'either fix the regression (if unintended) or run nmake baseline-accept (if intended).'
2027
2028                var refFilename = referencePath(relativeFilename);
2029
2030                // Append diff to the report
2031                var diff = new Diff.StringDiff(expected, actual);
2032                var header = '<h2>' + descriptionForDescribe + '</h2>';
2033                header += '<h4>Left file: ' + actualFilename + '; Right file: ' + refFilename + '</h4>';
2034                var trailer = '<hr>';
2035
2036                var reportContentSoFar = prepareBaselineReport();
2037                reportContentSoFar = reportContentSoFar + header + '<div class="code">' + diff.mergedHtml + '</div>' + trailer + htmlTrailer;
2038                IO.writeFile(reportFilename, reportContentSoFar);
2039
2040                throw new Error(errMsg);
2041            }
2042        }
2043
2044        export function runBaseline(
2045            descriptionForDescribe: string,
2046            relativeFilename: string,
2047            generateContent: () => string,
2048            runImmediately? = false,
2049            opts?: BaselineOptions) {
2050
2051            var actual = <string>undefined;
2052            var actualFilename = localPath(relativeFilename);
2053
2054            if (runImmediately) {
2055                var actual = generateActual(actualFilename, generateContent);
2056                var comparison = compareToBaseline(actual, relativeFilename, opts);
2057                writeComparison(comparison.expected, comparison.actual, relativeFilename, actualFilename, descriptionForDescribe);
2058            } else {
2059                describe(descriptionForDescribe, () => {
2060                    var actual: string;
2061
2062                    it('Can generate the content without error', () => {
2063                        actual = generateActual(actualFilename, generateContent);
2064                    });
2065
2066                    it('Matches the baseline file', () => {
2067                        var comparison = compareToBaseline(actual, relativeFilename, opts);
2068                        writeComparison(comparison.expected, comparison.actual, relativeFilename, actualFilename, descriptionForDescribe);
2069                    });
2070                });
2071            }
2072        }
2073    }
2074
2075    var currentRun = new Run();
2076
2077    global.describe = describe;
2078    global.run = run;
2079    global.it = it;
2080    global.assert = Harness.Assert;
2081}
2082
2083
2084//// [parserharness.js]
2085//
2086// Copyright (c) Microsoft Corporation.  All rights reserved.
2087//
2088// Licensed under the Apache License, Version 2.0 (the "License");
2089// you may not use this file except in compliance with the License.
2090// You may obtain a copy of the License at
2091//   http://www.apache.org/licenses/LICENSE-2.0
2092//
2093// Unless required by applicable law or agreed to in writing, software
2094// distributed under the License is distributed on an "AS IS" BASIS,
2095// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2096// See the License for the specific language governing permissions and
2097// limitations under the License.
2098//
2099var __extends = (this && this.__extends) || (function () {
2100    var extendStatics = function (d, b) {
2101        extendStatics = Object.setPrototypeOf ||
2102            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
2103            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
2104        return extendStatics(d, b);
2105    };
2106    return function (d, b) {
2107        if (typeof b !== "function" && b !== null)
2108            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
2109        extendStatics(d, b);
2110        function __() { this.constructor = d; }
2111        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
2112    };
2113})();
2114///<reference path='..\compiler\io.ts'/>
2115///<reference path='..\compiler\typescript.ts'/>
2116///<reference path='..\services\typescriptServices.ts' />
2117///<reference path='diff.ts'/>
2118function switchToForwardSlashes(path) {
2119    return path.replace(/\\/g, "/");
2120}
2121function filePath(fullPath) {
2122    fullPath = switchToForwardSlashes(fullPath);
2123    var components = fullPath.split("/");
2124    var path = components.slice(0, components.length - 1);
2125    return path.join("/") + "/";
2126}
2127var typescriptServiceFileName = filePath(IO.getExecutingFilePath()) + "typescriptServices.js";
2128var typescriptServiceFile = IO.readFile(typescriptServiceFileName);
2129if (typeof ActiveXObject === "function") {
2130    eval(typescriptServiceFile);
2131}
2132else if (typeof require === "function") {
2133    var vm = require('vm');
2134    vm.runInThisContext(typescriptServiceFile, 'typescriptServices.js');
2135}
2136else {
2137    throw new Error('Unknown context');
2138}
2139var Harness;
2140(function (Harness) {
2141    // Settings
2142    Harness.userSpecifiedroot = "";
2143    var global = Function("return this").call(null);
2144    Harness.usePull = false;
2145    // Assert functions
2146    var Assert;
2147    (function (Assert) {
2148        Assert.bugIds = [];
2149        Assert.throwAssertError = function (error) {
2150            throw error;
2151        };
2152        // Marks that the current scenario is impacted by a bug
2153        function bug(id) {
2154            if (Assert.bugIds.indexOf(id) < 0) {
2155                Assert.bugIds.push(id);
2156            }
2157        }
2158        Assert.bug = bug;
2159        // If there are any bugs in the test code, mark the scenario as impacted appropriately
2160        function bugs(content) {
2161            var bugs = content.match(/\bbug (\d+)/i);
2162            if (bugs) {
2163                bugs.forEach(function (bug) { return assert.bug(bug); });
2164            }
2165        }
2166        Assert.bugs = bugs;
2167        function is(result, msg) {
2168            if (!result) {
2169                Assert.throwAssertError(new Error(msg || "Expected true, got false."));
2170            }
2171        }
2172        Assert.is = is;
2173        function arrayLengthIs(arr, length) {
2174            if (arr.length != length) {
2175                var actual = '';
2176                arr.forEach(function (n) { return actual = actual + '\n      ' + n.toString(); });
2177                Assert.throwAssertError(new Error('Expected array to have ' + length + ' elements. Actual elements were:' + actual));
2178            }
2179        }
2180        Assert.arrayLengthIs = arrayLengthIs;
2181        function equal(actual, expected) {
2182            if (actual !== expected) {
2183                Assert.throwAssertError(new Error("Expected " + actual + " to equal " + expected));
2184            }
2185        }
2186        Assert.equal = equal;
2187        function notEqual(actual, expected) {
2188            if (actual === expected) {
2189                Assert.throwAssertError(new Error("Expected " + actual + " to *not* equal " + expected));
2190            }
2191        }
2192        Assert.notEqual = notEqual;
2193        function notNull(result) {
2194            if (result === null) {
2195                Assert.throwAssertError(new Error("Expected " + result + " to *not* be null"));
2196            }
2197        }
2198        Assert.notNull = notNull;
2199        function compilerWarning(result, line, column, desc) {
2200            if (!result.isErrorAt(line, column, desc)) {
2201                var actual = '';
2202                result.errors.forEach(function (err) {
2203                    actual = actual + '\n     ' + err.toString();
2204                });
2205                Assert.throwAssertError(new Error("Expected compiler warning at (" + line + ", " + column + "): " + desc + "\nActual errors follow: " + actual));
2206            }
2207        }
2208        Assert.compilerWarning = compilerWarning;
2209        function noDiff(text1, text2) {
2210            text1 = text1.replace(/^\s+|\s+$/g, "").replace(/\r\n?/g, "\n");
2211            text2 = text2.replace(/^\s+|\s+$/g, "").replace(/\r\n?/g, "\n");
2212            if (text1 !== text2) {
2213                var errorString = "";
2214                var text1Lines = text1.split(/\n/);
2215                var text2Lines = text2.split(/\n/);
2216                for (var i = 0; i < text1Lines.length; i++) {
2217                    if (text1Lines[i] !== text2Lines[i]) {
2218                        errorString += "Difference at line " + (i + 1) + ":\n";
2219                        errorString += "                  Left File: " + text1Lines[i] + "\n";
2220                        errorString += "                 Right File: " + text2Lines[i] + "\n\n";
2221                    }
2222                }
2223                Assert.throwAssertError(new Error(errorString));
2224            }
2225        }
2226        Assert.noDiff = noDiff;
2227        function arrayContains(arr, contains) {
2228            var found;
2229            for (var i = 0; i < contains.length; i++) {
2230                found = false;
2231                for (var j = 0; j < arr.length; j++) {
2232                    if (arr[j] === contains[i]) {
2233                        found = true;
2234                        break;
2235                    }
2236                }
2237                if (!found) {
2238                    Assert.throwAssertError(new Error("Expected array to contain \"" + contains[i] + "\""));
2239                }
2240            }
2241        }
2242        Assert.arrayContains = arrayContains;
2243        function arrayContainsOnce(arr, filter) {
2244            var foundCount = 0;
2245            for (var i = 0; i < arr.length; i++) {
2246                if (filter(arr[i])) {
2247                    foundCount++;
2248                }
2249            }
2250            if (foundCount !== 1) {
2251                Assert.throwAssertError(new Error("Expected array to match element only once (instead of " + foundCount + " times)"));
2252            }
2253        }
2254        Assert.arrayContainsOnce = arrayContainsOnce;
2255    })(Assert = Harness.Assert || (Harness.Assert = {}));
2256    /** Splits the given string on \r\n or on only \n if that fails */
2257    function splitContentByNewlines(content) {
2258        // Split up the input file by line
2259        // Note: IE JS engine incorrectly handles consecutive delimiters here when using RegExp split, so
2260        // we have to string-based splitting instead and try to figure out the delimiting chars
2261        var lines = content.split('\r\n');
2262        if (lines.length === 1) {
2263            lines = content.split('\n');
2264        }
2265        return lines;
2266    }
2267    Harness.splitContentByNewlines = splitContentByNewlines;
2268    /** Reads a file under /tests */
2269    function readFile(path) {
2270        if (path.indexOf('tests') < 0) {
2271            path = "tests/" + path;
2272        }
2273        var content = IO.readFile(Harness.userSpecifiedroot + path);
2274        if (content == null) {
2275            throw new Error("failed to read file at: '" + Harness.userSpecifiedroot + path + "'");
2276        }
2277        return content;
2278    }
2279    Harness.readFile = readFile;
2280    var Logger = /** @class */ (function () {
2281        function Logger() {
2282        }
2283        Logger.prototype.start = function (fileName, priority) { };
2284        Logger.prototype.end = function (fileName) { };
2285        Logger.prototype.scenarioStart = function (scenario) { };
2286        Logger.prototype.scenarioEnd = function (scenario, error) { };
2287        Logger.prototype.testStart = function (test) { };
2288        Logger.prototype.pass = function (test) { };
2289        Logger.prototype.bug = function (test) { };
2290        Logger.prototype.fail = function (test) { };
2291        Logger.prototype.error = function (test, error) { };
2292        Logger.prototype.comment = function (comment) { };
2293        Logger.prototype.verify = function (test, passed, actual, expected, message) { };
2294        return Logger;
2295    }());
2296    Harness.Logger = Logger;
2297    // Logger-related functions
2298    var loggers = [];
2299    function registerLogger(logger) {
2300        loggers.push(logger);
2301    }
2302    Harness.registerLogger = registerLogger;
2303    function emitLog(field) {
2304        var params = [];
2305        for (var _i = 1; _i < arguments.length; _i++) {
2306            params[_i - 1] = arguments[_i];
2307        }
2308        for (var i = 0; i < loggers.length; i++) {
2309            if (typeof loggers[i][field] === 'function') {
2310                loggers[i][field].apply(loggers[i], params);
2311            }
2312        }
2313    }
2314    Harness.emitLog = emitLog;
2315    var Runnable = /** @class */ (function () {
2316        function Runnable(description, block) {
2317            this.description = description;
2318            this.block = block;
2319            // The error, if any, that occurred when running 'block'
2320            this.error = null;
2321            // Whether or not this object has any failures (including in its descendants)
2322            this.passed = null;
2323            // A list of bugs impacting this object
2324            this.bugs = [];
2325            // A list of all our child Runnables
2326            this.children = [];
2327        }
2328        Runnable.prototype.addChild = function (child) {
2329            this.children.push(child);
2330        };
2331        /** Call function fn, which may take a done function and may possibly execute
2332         *  asynchronously, calling done when finished. Returns true or false depending
2333         *  on whether the function was asynchronous or not.
2334         */
2335        Runnable.prototype.call = function (fn, done) {
2336            var isAsync = true;
2337            try {
2338                if (fn.length === 0) {
2339                    // No async.
2340                    fn();
2341                    done();
2342                    return false;
2343                }
2344                else {
2345                    // Possibly async
2346                    Runnable.pushGlobalErrorHandler(done);
2347                    fn(function () {
2348                        isAsync = false; // If we execute synchronously, this will get called before the return below.
2349                        Runnable.popGlobalErrorHandler();
2350                        done();
2351                    });
2352                    return isAsync;
2353                }
2354            }
2355            catch (e) {
2356                done(e);
2357                return false;
2358            }
2359        };
2360        Runnable.prototype.run = function (done) { };
2361        Runnable.prototype.runBlock = function (done) {
2362            return this.call(this.block, done);
2363        };
2364        Runnable.prototype.runChild = function (index, done) {
2365            var _this = this;
2366            return this.call((function (done) { return _this.children[index].run(done); }), done);
2367        };
2368        Runnable.pushGlobalErrorHandler = function (done) {
2369            errorHandlerStack.push(function (e) {
2370                done(e);
2371            });
2372        };
2373        Runnable.popGlobalErrorHandler = function () {
2374            errorHandlerStack.pop();
2375        };
2376        Runnable.handleError = function (e) {
2377            if (errorHandlerStack.length === 0) {
2378                IO.printLine('Global error: ' + e);
2379            }
2380            else {
2381                errorHandlerStack[errorHandlerStack.length - 1](e);
2382            }
2383        };
2384        // The current stack of Runnable objects
2385        Runnable.currentStack = [];
2386        Runnable.errorHandlerStack = [];
2387        return Runnable;
2388    }());
2389    Harness.Runnable = Runnable;
2390    var TestCase = /** @class */ (function (_super) {
2391        __extends(TestCase, _super);
2392        function TestCase(description, block) {
2393            var _this = _super.call(this, description, block) || this;
2394            _this.description = description;
2395            _this.block = block;
2396            return _this;
2397        }
2398        TestCase.prototype.addChild = function (child) {
2399            throw new Error("Testcases may not be nested inside other testcases");
2400        };
2401        /** Run the test case block and fail the test if it raised an error. If no error is raised, the test passes. */
2402        TestCase.prototype.run = function (done) {
2403            var that = this;
2404            Runnable.currentStack.push(this);
2405            emitLog('testStart', { desc: this.description });
2406            if (this.block) {
2407                var async = this.runBlock(function (e) {
2408                    if (e) {
2409                        that.passed = false;
2410                        that.error = e;
2411                        emitLog('error', { desc: this.description, pass: false }, e);
2412                    }
2413                    else {
2414                        that.passed = true;
2415                        emitLog('pass', { desc: this.description, pass: true });
2416                    }
2417                    Runnable.currentStack.pop();
2418                    done();
2419                });
2420            }
2421        };
2422        return TestCase;
2423    }(Runnable));
2424    Harness.TestCase = TestCase;
2425    var Scenario = /** @class */ (function (_super) {
2426        __extends(Scenario, _super);
2427        function Scenario(description, block) {
2428            var _this = _super.call(this, description, block) || this;
2429            _this.description = description;
2430            _this.block = block;
2431            return _this;
2432        }
2433        /** Run the block, and if the block doesn't raise an error, run the children. */
2434        Scenario.prototype.run = function (done) {
2435            var that = this;
2436            Runnable.currentStack.push(this);
2437            emitLog('scenarioStart', { desc: this.description });
2438            var async = this.runBlock(function (e) {
2439                Runnable.currentStack.pop();
2440                if (e) {
2441                    that.passed = false;
2442                    that.error = e;
2443                    var metadata = { id: undefined, desc: this.description, pass: false, bugs: assert.bugIds };
2444                    // Report all bugs affecting this scenario
2445                    assert.bugIds.forEach(function (desc) { return emitLog('bug', metadata, desc); });
2446                    emitLog('scenarioEnd', metadata, e);
2447                    done();
2448                }
2449                else {
2450                    that.passed = true; // so far so good.
2451                    that.runChildren(done);
2452                }
2453            });
2454        };
2455        /** Run the children of the scenario (other scenarios and test cases). If any fail,
2456         *  set this scenario to failed. Synchronous tests will run synchronously without
2457         *  adding stack frames.
2458         */
2459        Scenario.prototype.runChildren = function (done, index) {
2460            if (index === void 0) { index = 0; }
2461            var that = this;
2462            var async = false;
2463            for (; index < this.children.length; index++) {
2464                async = this.runChild(index, function (e) {
2465                    that.passed = that.passed && that.children[index].passed;
2466                    if (async)
2467                        that.runChildren(done, index + 1);
2468                });
2469                if (async)
2470                    return;
2471            }
2472            var metadata = { id: undefined, desc: this.description, pass: this.passed, bugs: assert.bugIds };
2473            // Report all bugs affecting this scenario
2474            assert.bugIds.forEach(function (desc) { return emitLog('bug', metadata, desc); });
2475            emitLog('scenarioEnd', metadata);
2476            done();
2477        };
2478        return Scenario;
2479    }(Runnable));
2480    Harness.Scenario = Scenario;
2481    var Run = /** @class */ (function (_super) {
2482        __extends(Run, _super);
2483        function Run() {
2484            return _super.call(this, 'Test Run', null) || this;
2485        }
2486        Run.prototype.run = function () {
2487            emitLog('start');
2488            this.runChildren();
2489        };
2490        Run.prototype.runChildren = function (index) {
2491            if (index === void 0) { index = 0; }
2492            var async = false;
2493            var that = this;
2494            for (; index < this.children.length; index++) {
2495                // Clear out bug descriptions
2496                assert.bugIds = [];
2497                async = this.runChild(index, function (e) {
2498                    if (async) {
2499                        that.runChildren(index + 1);
2500                    }
2501                });
2502                if (async) {
2503                    return;
2504                }
2505            }
2506            Perf.runBenchmarks();
2507            emitLog('end');
2508        };
2509        return Run;
2510    }(Runnable));
2511    Harness.Run = Run;
2512    // Performance test
2513    var Perf;
2514    (function (Perf) {
2515        var Clock;
2516        (function (Clock) {
2517            if (typeof WScript !== "undefined" && typeof global['WScript'].InitializeProjection !== "undefined") {
2518                // Running in JSHost.
2519                global['WScript'].InitializeProjection();
2520                Clock.now = function () {
2521                    return TestUtilities.QueryPerformanceCounter();
2522                };
2523                Clock.resolution = TestUtilities.QueryPerformanceFrequency();
2524            }
2525            else {
2526                Clock.now = function () {
2527                    return Date.now();
2528                };
2529                Clock.resolution = 1000;
2530            }
2531        })(Clock = Perf.Clock || (Perf.Clock = {}));
2532        var Timer = /** @class */ (function () {
2533            function Timer() {
2534                this.time = 0;
2535            }
2536            Timer.prototype.start = function () {
2537                this.time = 0;
2538                this.startTime = Clock.now();
2539            };
2540            Timer.prototype.end = function () {
2541                // Set time to MS.
2542                this.time = (Clock.now() - this.startTime) / Clock.resolution * 1000;
2543            };
2544            return Timer;
2545        }());
2546        Perf.Timer = Timer;
2547        var Dataset = /** @class */ (function () {
2548            function Dataset() {
2549                this.data = [];
2550            }
2551            Dataset.prototype.add = function (value) {
2552                this.data.push(value);
2553            };
2554            Dataset.prototype.mean = function () {
2555                var sum = 0;
2556                for (var i = 0; i < this.data.length; i++) {
2557                    sum += this.data[i];
2558                }
2559                return sum / this.data.length;
2560            };
2561            Dataset.prototype.min = function () {
2562                var min = this.data[0];
2563                for (var i = 1; i < this.data.length; i++) {
2564                    if (this.data[i] < min) {
2565                        min = this.data[i];
2566                    }
2567                }
2568                return min;
2569            };
2570            Dataset.prototype.max = function () {
2571                var max = this.data[0];
2572                for (var i = 1; i < this.data.length; i++) {
2573                    if (this.data[i] > max) {
2574                        max = this.data[i];
2575                    }
2576                }
2577                return max;
2578            };
2579            Dataset.prototype.stdDev = function () {
2580                var sampleMean = this.mean();
2581                var sumOfSquares = 0;
2582                for (var i = 0; i < this.data.length; i++) {
2583                    sumOfSquares += Math.pow(this.data[i] - sampleMean, 2);
2584                }
2585                return Math.sqrt(sumOfSquares / this.data.length);
2586            };
2587            return Dataset;
2588        }());
2589        Perf.Dataset = Dataset;
2590        // Base benchmark class with some defaults.
2591        var Benchmark = /** @class */ (function () {
2592            function Benchmark() {
2593                this.iterations = 10;
2594                this.description = "";
2595                this.results = {};
2596            }
2597            Benchmark.prototype.bench = function (subBench) { };
2598            Benchmark.prototype.before = function () { };
2599            Benchmark.prototype.beforeEach = function () { };
2600            Benchmark.prototype.after = function () { };
2601            Benchmark.prototype.afterEach = function () { };
2602            Benchmark.prototype.addTimingFor = function (name, timing) {
2603                this.results[name] = this.results[name] || new Dataset();
2604                this.results[name].add(timing);
2605            };
2606            return Benchmark;
2607        }());
2608        Perf.Benchmark = Benchmark;
2609        Perf.benchmarks = [];
2610        var timeFunction;
2611        timeFunction = function (benchmark, description, name, f) {
2612            if (description === void 0) { description = benchmark.description; }
2613            if (name === void 0) { name = ''; }
2614            if (f === void 0) { f = benchmark.bench; }
2615            var t = new Timer();
2616            t.start();
2617            var subBenchmark = function (name, f) {
2618                timeFunction(benchmark, description, name, f);
2619            };
2620            f.call(benchmark, subBenchmark);
2621            t.end();
2622            benchmark.addTimingFor(name, t.time);
2623        };
2624        function runBenchmarks() {
2625            for (var i = 0; i < Perf.benchmarks.length; i++) {
2626                var b = new Perf.benchmarks[i]();
2627                var t = new Timer();
2628                b.before();
2629                for (var j = 0; j < b.iterations; j++) {
2630                    b.beforeEach();
2631                    timeFunction(b);
2632                    b.afterEach();
2633                }
2634                b.after();
2635                for (var prop in b.results) {
2636                    var description = b.description + (prop ? ": " + prop : '');
2637                    emitLog('testStart', { desc: description });
2638                    emitLog('pass', {
2639                        desc: description, pass: true, perfResults: {
2640                            mean: b.results[prop].mean(),
2641                            min: b.results[prop].min(),
2642                            max: b.results[prop].max(),
2643                            stdDev: b.results[prop].stdDev(),
2644                            trials: b.results[prop].data
2645                        }
2646                    });
2647                }
2648            }
2649        }
2650        Perf.runBenchmarks = runBenchmarks;
2651        // Replace with better type when classes are assignment compatible with
2652        // the below type.
2653        // export function addBenchmark(BenchmarkClass: {new(): Benchmark;}) {
2654        function addBenchmark(BenchmarkClass) {
2655            Perf.benchmarks.push(BenchmarkClass);
2656        }
2657        Perf.addBenchmark = addBenchmark;
2658    })(Perf = Harness.Perf || (Harness.Perf = {}));
2659    /** Functionality for compiling TypeScript code */
2660    var Compiler;
2661    (function (Compiler) {
2662        /** Aggregate various writes into a single array of lines. Useful for passing to the
2663         *  TypeScript compiler to fill with source code or errors.
2664         */
2665        var WriterAggregator = /** @class */ (function () {
2666            function WriterAggregator() {
2667                this.lines = [];
2668                this.currentLine = "";
2669            }
2670            WriterAggregator.prototype.Write = function (str) {
2671                this.currentLine += str;
2672            };
2673            WriterAggregator.prototype.WriteLine = function (str) {
2674                this.lines.push(this.currentLine + str);
2675                this.currentLine = "";
2676            };
2677            WriterAggregator.prototype.Close = function () {
2678                if (this.currentLine.length > 0) {
2679                    this.lines.push(this.currentLine);
2680                }
2681                this.currentLine = "";
2682            };
2683            WriterAggregator.prototype.reset = function () {
2684                this.lines = [];
2685                this.currentLine = "";
2686            };
2687            return WriterAggregator;
2688        }());
2689        Compiler.WriterAggregator = WriterAggregator;
2690        /** Mimics having multiple files, later concatenated to a single file. */
2691        var EmitterIOHost = /** @class */ (function () {
2692            function EmitterIOHost() {
2693                this.fileCollection = {};
2694            }
2695            /** create file gets the whole path to create, so this works as expected with the --out parameter */
2696            EmitterIOHost.prototype.createFile = function (s, useUTF8) {
2697                if (this.fileCollection[s]) {
2698                    return this.fileCollection[s];
2699                }
2700                var writer = new Harness.Compiler.WriterAggregator();
2701                this.fileCollection[s] = writer;
2702                return writer;
2703            };
2704            EmitterIOHost.prototype.directoryExists = function (s) { return false; };
2705            EmitterIOHost.prototype.fileExists = function (s) { return typeof this.fileCollection[s] !== 'undefined'; };
2706            EmitterIOHost.prototype.resolvePath = function (s) { return s; };
2707            EmitterIOHost.prototype.reset = function () { this.fileCollection = {}; };
2708            EmitterIOHost.prototype.toArray = function () {
2709                var result = [];
2710                for (var p in this.fileCollection) {
2711                    if (this.fileCollection.hasOwnProperty(p)) {
2712                        var current = this.fileCollection[p];
2713                        if (current.lines.length > 0) {
2714                            if (p !== '0.js') {
2715                                current.lines.unshift('////[' + p + ']');
2716                            }
2717                            result.push({ filename: p, file: this.fileCollection[p] });
2718                        }
2719                    }
2720                }
2721                return result;
2722            };
2723            return EmitterIOHost;
2724        }());
2725        Compiler.EmitterIOHost = EmitterIOHost;
2726        var libFolder = global['WScript'] ? TypeScript.filePath(global['WScript'].ScriptFullName) : (__dirname + '/');
2727        Compiler.libText = IO ? IO.readFile(libFolder + "lib.d.ts") : '';
2728        var stdout = new EmitterIOHost();
2729        var stderr = new WriterAggregator();
2730        function isDeclareFile(filename) {
2731            return /\.d\.ts$/.test(filename);
2732        }
2733        Compiler.isDeclareFile = isDeclareFile;
2734        function makeDefaultCompilerForTest(c) {
2735            var compiler = c || new TypeScript.TypeScriptCompiler(stderr);
2736            compiler.parser.errorRecovery = true;
2737            compiler.settings.codeGenTarget = TypeScript.CodeGenTarget.ES5;
2738            compiler.settings.controlFlow = true;
2739            compiler.settings.controlFlowUseDef = true;
2740            if (Harness.usePull) {
2741                compiler.settings.usePull = true;
2742                compiler.settings.useFidelity = true;
2743            }
2744            compiler.parseEmitOption(stdout);
2745            TypeScript.moduleGenTarget = TypeScript.ModuleGenTarget.Synchronous;
2746            compiler.addUnit(Harness.Compiler.libText, "lib.d.ts", true);
2747            return compiler;
2748        }
2749        Compiler.makeDefaultCompilerForTest = makeDefaultCompilerForTest;
2750        var compiler;
2751        recreate();
2752        // pullUpdateUnit is sufficient if an existing unit is updated, if a new unit is added we need to do a full typecheck
2753        var needsFullTypeCheck = true;
2754        function compile(code, filename) {
2755            if (Harness.usePull) {
2756                if (needsFullTypeCheck) {
2757                    compiler.pullTypeCheck(true);
2758                    needsFullTypeCheck = false;
2759                }
2760                else {
2761                    // requires unit to already exist in the compiler
2762                    compiler.pullUpdateUnit(new TypeScript.StringSourceText(""), filename, true);
2763                    compiler.pullUpdateUnit(new TypeScript.StringSourceText(code), filename, true);
2764                }
2765            }
2766            else {
2767                compiler.reTypeCheck();
2768            }
2769        }
2770        Compiler.compile = compile;
2771        // Types
2772        var Type = /** @class */ (function () {
2773            function Type(type, code, identifier) {
2774                this.type = type;
2775                this.code = code;
2776                this.identifier = identifier;
2777            }
2778            Type.prototype.normalizeToArray = function (arg) {
2779                if ((Array.isArray && Array.isArray(arg)) || arg instanceof Array)
2780                    return arg;
2781                return [arg];
2782            };
2783            Type.prototype.compilesOk = function (testCode) {
2784                var errors = null;
2785                compileString(testCode, 'test.ts', function (compilerResult) {
2786                    errors = compilerResult.errors;
2787                });
2788                return errors.length === 0;
2789            };
2790            Type.prototype.isSubtypeOf = function (other) {
2791                var testCode = 'class __test1__ {\n';
2792                testCode += '    public test() {\n';
2793                testCode += '        ' + other.code + ';\n';
2794                testCode += '        return ' + other.identifier + ';\n';
2795                testCode += '    }\n';
2796                testCode += '}\n';
2797                testCode += 'class __test2__ extends __test1__ {\n';
2798                testCode += '    public test() {\n';
2799                testCode += '        ' + this.code + ';\n';
2800                testCode += '        return ' + other.identifier + ';\n';
2801                testCode += '    }\n';
2802                testCode += '}\n';
2803                return this.compilesOk(testCode);
2804            };
2805            // TODO: Find an implementation of isIdenticalTo that works.
2806            //public isIdenticalTo(other: Type) {
2807            //    var testCode = 'module __test1__ {\n';
2808            //    testCode += '    ' + this.code + ';\n';
2809            //    testCode += '    export var __val__ = ' + this.identifier + ';\n';
2810            //    testCode += '}\n';
2811            //    testCode += 'var __test1__val__ = __test1__.__val__;\n';
2812            //    testCode += 'module __test2__ {\n';
2813            //    testCode += '    ' + other.code + ';\n';
2814            //    testCode += '    export var __val__ = ' + other.identifier + ';\n';
2815            //    testCode += '}\n';
2816            //    testCode += 'var __test2__val__ = __test2__.__val__;\n';
2817            //    testCode += 'function __test__function__() { if(true) { return __test1__val__ }; return __test2__val__; }';
2818            //    return this.compilesOk(testCode);
2819            //}
2820            Type.prototype.assertSubtypeOf = function (others) {
2821                others = this.normalizeToArray(others);
2822                for (var i = 0; i < others.length; i++) {
2823                    if (!this.isSubtypeOf(others[i])) {
2824                        throw new Error("Expected " + this.type + " to be a subtype of " + others[i].type);
2825                    }
2826                }
2827            };
2828            Type.prototype.assertNotSubtypeOf = function (others) {
2829                others = this.normalizeToArray(others);
2830                for (var i = 0; i < others.length; i++) {
2831                    if (this.isSubtypeOf(others[i])) {
2832                        throw new Error("Expected " + this.type + " to be a subtype of " + others[i].type);
2833                    }
2834                }
2835            };
2836            //public assertIdenticalTo(other: Type) {
2837            //    if (!this.isIdenticalTo(other)) {
2838            //        throw new Error("Expected " + this.type + " to be identical to " + other.type);
2839            //    }
2840            //}
2841            //public assertNotIdenticalTo(other: Type) {
2842            //    if (!this.isIdenticalTo(other)) {
2843            //        throw new Error("Expected " + this.type + " to not be identical to " + other.type);
2844            //    }
2845            //}
2846            Type.prototype.isAssignmentCompatibleWith = function (other) {
2847                var testCode = 'module __test1__ {\n';
2848                testCode += '    ' + this.code + ';\n';
2849                testCode += '    export var __val__ = ' + this.identifier + ';\n';
2850                testCode += '}\n';
2851                testCode += 'var __test1__val__ = __test1__.__val__;\n';
2852                testCode += 'module __test2__ {\n';
2853                testCode += '    export ' + other.code + ';\n';
2854                testCode += '    export var __val__ = ' + other.identifier + ';\n';
2855                testCode += '}\n';
2856                testCode += 'var __test2__val__ = __test2__.__val__;\n';
2857                testCode += '__test2__val__ = __test1__val__;';
2858                return this.compilesOk(testCode);
2859            };
2860            Type.prototype.assertAssignmentCompatibleWith = function (others) {
2861                others = this.normalizeToArray(others);
2862                for (var i = 0; i < others.length; i++) {
2863                    var other = others[i];
2864                    if (!this.isAssignmentCompatibleWith(other)) {
2865                        throw new Error("Expected " + this.type + " to be assignment compatible with " + other.type);
2866                    }
2867                }
2868            };
2869            Type.prototype.assertNotAssignmentCompatibleWith = function (others) {
2870                others = this.normalizeToArray(others);
2871                for (var i = 0; i < others.length; i++) {
2872                    var other = others[i];
2873                    if (this.isAssignmentCompatibleWith(other)) {
2874                        throw new Error("Expected " + this.type + " to not be assignment compatible with " + other.type);
2875                    }
2876                }
2877            };
2878            Type.prototype.assertThisCanBeAssignedTo = function (desc, these, notThese) {
2879                var _this = this;
2880                it(desc + " is assignable to ", function () {
2881                    _this.assertAssignmentCompatibleWith(these);
2882                });
2883                it(desc + " not assignable to ", function () {
2884                    _this.assertNotAssignmentCompatibleWith(notThese);
2885                });
2886            };
2887            return Type;
2888        }());
2889        Compiler.Type = Type;
2890        var TypeFactory = /** @class */ (function () {
2891            function TypeFactory() {
2892                this.any = this.get('var x : any', 'x');
2893                this.number = this.get('var x : number', 'x');
2894                this.string = this.get('var x : string', 'x');
2895                this.boolean = this.get('var x : boolean', 'x');
2896            }
2897            TypeFactory.prototype.get = function (code, target) {
2898                var targetIdentifier = '';
2899                var targetPosition = -1;
2900                if (typeof target === "string") {
2901                    targetIdentifier = target;
2902                }
2903                else if (typeof target === "number") {
2904                    targetPosition = target;
2905                }
2906                else {
2907                    throw new Error("Expected string or number not " + (typeof target));
2908                }
2909                var errors = null;
2910                compileString(code, 'test.ts', function (compilerResult) {
2911                    errors = compilerResult.errors;
2912                });
2913                if (errors.length > 0)
2914                    throw new Error("Type definition contains errors: " + errors.join(","));
2915                var matchingIdentifiers = [];
2916                if (!Harness.usePull) {
2917                    // This will find the requested identifier in the first script where it's present, a naive search of each member in each script,
2918                    // which means this won't play nicely if the same identifier is used in multiple units, but it will enable this to work on multi-file tests.
2919                    // m = 1 because the first script will always be lib.d.ts which we don't want to search.
2920                    for (var m = 1; m < compiler.scripts.members.length; m++) {
2921                        var script = compiler.scripts.members[m];
2922                        var enclosingScopeContext = TypeScript.findEnclosingScopeAt(new TypeScript.NullLogger(), script, new TypeScript.StringSourceText(code), 0, false);
2923                        var entries = new TypeScript.ScopeTraversal(compiler).getScopeEntries(enclosingScopeContext);
2924                        for (var i = 0; i < entries.length; i++) {
2925                            if (entries[i].name === targetIdentifier) {
2926                                matchingIdentifiers.push(new Type(entries[i].type, code, targetIdentifier));
2927                            }
2928                        }
2929                    }
2930                }
2931                else {
2932                    for (var m = 0; m < compiler.scripts.members.length; m++) {
2933                        var script2 = compiler.scripts.members[m];
2934                        if (script2.locationInfo.filename !== 'lib.d.ts') {
2935                            if (targetPosition > -1) {
2936                                var tyInfo = compiler.pullGetTypeInfoAtPosition(targetPosition, script2);
2937                                var name = this.getTypeInfoName(tyInfo.ast);
2938                                var foundValue = new Type(tyInfo.typeInfo, code, name);
2939                                if (!matchingIdentifiers.some(function (value) { return (value.identifier === foundValue.identifier) && (value.code === foundValue.code) && (value.type === foundValue.type); })) {
2940                                    matchingIdentifiers.push(foundValue);
2941                                }
2942                            }
2943                            else {
2944                                for (var pos = 0; pos < code.length; pos++) {
2945                                    var tyInfo = compiler.pullGetTypeInfoAtPosition(pos, script2);
2946                                    var name = this.getTypeInfoName(tyInfo.ast);
2947                                    if (name === targetIdentifier) {
2948                                        var foundValue = new Type(tyInfo.typeInfo, code, targetIdentifier);
2949                                        if (!matchingIdentifiers.some(function (value) { return (value.identifier === foundValue.identifier) && (value.code === foundValue.code) && (value.type === foundValue.type); })) {
2950                                            matchingIdentifiers.push(foundValue);
2951                                        }
2952                                    }
2953                                }
2954                            }
2955                        }
2956                    }
2957                }
2958                if (matchingIdentifiers.length === 0) {
2959                    if (targetPosition > -1) {
2960                        throw new Error("Could not find an identifier at position " + targetPosition);
2961                    }
2962                    else {
2963                        throw new Error("Could not find an identifier " + targetIdentifier + " in any known scopes");
2964                    }
2965                }
2966                else if (matchingIdentifiers.length > 1) {
2967                    throw new Error("Found multiple matching identifiers for " + target);
2968                }
2969                else {
2970                    return matchingIdentifiers[0];
2971                }
2972            };
2973            TypeFactory.prototype.getTypeInfoName = function (ast) {
2974                var name = '';
2975                switch (ast.nodeType) {
2976                    case TypeScript.NodeType.Name: // Type Name?
2977                    case TypeScript.NodeType.Null:
2978                    case TypeScript.NodeType.List:
2979                    case TypeScript.NodeType.Empty:
2980                    case TypeScript.NodeType.EmptyExpr:
2981                    case TypeScript.NodeType.Asg:
2982                    case TypeScript.NodeType.True:
2983                    case TypeScript.NodeType.False:
2984                    case TypeScript.NodeType.ArrayLit:
2985                    case TypeScript.NodeType.TypeRef:
2986                        break;
2987                    case TypeScript.NodeType.Super:
2988                        name = ast.text;
2989                        break;
2990                    case TypeScript.NodeType.Regex:
2991                        name = ast.text;
2992                        break;
2993                    case TypeScript.NodeType.QString:
2994                        name = ast.text;
2995                        break;
2996                    case TypeScript.NodeType.NumberLit:
2997                        name = ast.text;
2998                        break;
2999                    case TypeScript.NodeType.Return:
3000                        //name = (<TypeScript.ReturnStatement>tyInfo.ast).returnExpression.actualText; // why is this complaining?
3001                        break;
3002                    case TypeScript.NodeType.InterfaceDeclaration:
3003                        name = ast.name.actualText;
3004                        break;
3005                    case TypeScript.NodeType.ModuleDeclaration:
3006                        name = ast.name.actualText;
3007                        break;
3008                    case TypeScript.NodeType.ClassDeclaration:
3009                        name = ast.name.actualText;
3010                        break;
3011                    case TypeScript.NodeType.FuncDecl:
3012                        name = !ast.name ? "" : ast.name.actualText; // name == null for lambdas
3013                        break;
3014                    default:
3015                        // TODO: is there a reason to mess with all the special cases above and not just do this (ie take whatever property is there and works?)
3016                        var a = ast;
3017                        name = (a.id) ? (a.id.actualText) : (a.name) ? a.name.actualText : (a.text) ? a.text : '';
3018                        break;
3019                }
3020                return name;
3021            };
3022            TypeFactory.prototype.isOfType = function (expr, expectedType) {
3023                var actualType = this.get('var _v_a_r_ = ' + expr, '_v_a_r_');
3024                it('Expression "' + expr + '" is of type "' + expectedType + '"', function () {
3025                    assert.equal(actualType.type, expectedType);
3026                });
3027            };
3028            return TypeFactory;
3029        }());
3030        Compiler.TypeFactory = TypeFactory;
3031        /** Generates a .d.ts file for the given code
3032          * @param verifyNoDeclFile pass true when the given code should generate no decl file, false otherwise
3033          * @param unitName add the given code under thie name, else use '0.ts'
3034          * @param compilationContext a set of functions to be run before and after compiling this code for doing things like adding dependencies first
3035          * @param references the set of referenced files used by the given code
3036          */
3037        function generateDeclFile(code, verifyNoDeclFile, unitName, compilationContext, references) {
3038            reset();
3039            compiler.settings.generateDeclarationFiles = true;
3040            var oldOutputOption = compiler.settings.outputOption;
3041            var oldEmitterIOHost = compiler.emitSettings.ioHost;
3042            try {
3043                if (compilationContext && compilationContext.preCompile) {
3044                    compilationContext.preCompile();
3045                }
3046                addUnit(code, unitName, false, false, references);
3047                compiler.reTypeCheck();
3048                var outputs = {};
3049                compiler.settings.outputOption = "";
3050                compiler.parseEmitOption({
3051                    createFile: function (fn) {
3052                        outputs[fn] = new Harness.Compiler.WriterAggregator();
3053                        return outputs[fn];
3054                    },
3055                    directoryExists: function (path) { return true; },
3056                    fileExists: function (path) { return true; },
3057                    resolvePath: function (path) { return path; }
3058                });
3059                compiler.emitDeclarations();
3060                var results = null;
3061                for (var fn in outputs) {
3062                    if (fn.indexOf('.d.ts') >= 0) {
3063                        var writer = outputs[fn];
3064                        writer.Close();
3065                        results = writer.lines.join('\n');
3066                        if (verifyNoDeclFile && results != "") {
3067                            throw new Error('Compilation should not produce ' + fn);
3068                        }
3069                    }
3070                }
3071                if (results) {
3072                    return results;
3073                }
3074                if (!verifyNoDeclFile) {
3075                    throw new Error('Compilation did not produce .d.ts files');
3076                }
3077            }
3078            finally {
3079                compiler.settings.generateDeclarationFiles = false;
3080                compiler.settings.outputOption = oldOutputOption;
3081                compiler.parseEmitOption(oldEmitterIOHost);
3082                if (compilationContext && compilationContext.postCompile) {
3083                    compilationContext.postCompile();
3084                }
3085                var uName = unitName || '0.ts';
3086                updateUnit('', uName);
3087            }
3088            return '';
3089        }
3090        Compiler.generateDeclFile = generateDeclFile;
3091        /** Contains the code and errors of a compilation and some helper methods to check its status. */
3092        var CompilerResult = /** @class */ (function () {
3093            /** @param fileResults an array of strings for the filename and an ITextWriter with its code */
3094            function CompilerResult(fileResults, errorLines, scripts) {
3095                this.fileResults = fileResults;
3096                this.scripts = scripts;
3097                var lines = [];
3098                fileResults.forEach(function (v) { return lines = lines.concat(v.file.lines); });
3099                this.code = lines.join("\n");
3100                this.errors = [];
3101                for (var i = 0; i < errorLines.length; i++) {
3102                    if (Harness.usePull) {
3103                        var err = errorLines[i]; // TypeScript.PullError
3104                        this.errors.push(new CompilerError(err.filename, 0, 0, err.message));
3105                    }
3106                    else {
3107                        var match = errorLines[i].match(/([^\(]*)\((\d+),(\d+)\):\s+((.*[\s\r\n]*.*)+)\s*$/);
3108                        if (match) {
3109                            this.errors.push(new CompilerError(match[1], parseFloat(match[2]), parseFloat(match[3]), match[4]));
3110                        }
3111                        else {
3112                            WScript.Echo("non-match on: " + errorLines[i]);
3113                        }
3114                    }
3115                }
3116            }
3117            CompilerResult.prototype.isErrorAt = function (line, column, message) {
3118                for (var i = 0; i < this.errors.length; i++) {
3119                    if (this.errors[i].line === line && this.errors[i].column === column && this.errors[i].message === message)
3120                        return true;
3121                }
3122                return false;
3123            };
3124            return CompilerResult;
3125        }());
3126        Compiler.CompilerResult = CompilerResult;
3127        // Compiler Error.
3128        var CompilerError = /** @class */ (function () {
3129            function CompilerError(file, line, column, message) {
3130                this.file = file;
3131                this.line = line;
3132                this.column = column;
3133                this.message = message;
3134            }
3135            CompilerError.prototype.toString = function () {
3136                return this.file + "(" + this.line + "," + this.column + "): " + this.message;
3137            };
3138            return CompilerError;
3139        }());
3140        Compiler.CompilerError = CompilerError;
3141        /** Create a new instance of the compiler with default settings and lib.d.ts, then typecheck */
3142        function recreate() {
3143            compiler = makeDefaultCompilerForTest();
3144            if (Harness.usePull) {
3145                compiler.pullTypeCheck(true);
3146            }
3147            else {
3148                compiler.typeCheck();
3149            }
3150        }
3151        Compiler.recreate = recreate;
3152        function reset() {
3153            stdout.reset();
3154            stderr.reset();
3155            var files = compiler.units.map(function (value) { return value.filename; });
3156            for (var i = 0; i < files.length; i++) {
3157                var fname = files[i];
3158                if (fname !== 'lib.d.ts') {
3159                    updateUnit('', fname);
3160                }
3161            }
3162            compiler.errorReporter.hasErrors = false;
3163        }
3164        Compiler.reset = reset;
3165        function addUnit(code, unitName, isResident, isDeclareFile, references) {
3166            var script = null;
3167            var uName = unitName || '0' + (isDeclareFile ? '.d.ts' : '.ts');
3168            for (var i = 0; i < compiler.units.length; i++) {
3169                if (compiler.units[i].filename === uName) {
3170                    updateUnit(code, uName);
3171                    script = compiler.scripts.members[i];
3172                }
3173            }
3174            if (!script) {
3175                // TODO: make this toggleable, shouldn't be necessary once typecheck bugs are cleaned up
3176                // but without it subsequent tests are treated as edits, making for somewhat useful stress testing
3177                // of persistent typecheck state
3178                //compiler.addUnit("", uName, isResident, references); // equivalent to compiler.deleteUnit(...)
3179                script = compiler.addUnit(code, uName, isResident, references);
3180                needsFullTypeCheck = true;
3181            }
3182            return script;
3183        }
3184        Compiler.addUnit = addUnit;
3185        function updateUnit(code, unitName, setRecovery) {
3186            if (Harness.usePull) {
3187                compiler.pullUpdateUnit(new TypeScript.StringSourceText(code), unitName, setRecovery);
3188            }
3189            else {
3190                compiler.updateUnit(code, unitName, setRecovery);
3191            }
3192        }
3193        Compiler.updateUnit = updateUnit;
3194        function compileFile(path, callback, settingsCallback, context, references) {
3195            path = switchToForwardSlashes(path);
3196            var filename = path.match(/[^\/]*$/)[0];
3197            var code = readFile(path);
3198            compileUnit(code, filename, callback, settingsCallback, context, references);
3199        }
3200        Compiler.compileFile = compileFile;
3201        function compileUnit(code, filename, callback, settingsCallback, context, references) {
3202            // not recursive
3203            function clone /* <T> */(source, target) {
3204                for (var prop in source) {
3205                    target[prop] = source[prop];
3206                }
3207            }
3208            var oldCompilerSettings = new TypeScript.CompilationSettings();
3209            clone(compiler.settings, oldCompilerSettings);
3210            var oldEmitSettings = new TypeScript.EmitOptions(compiler.settings);
3211            clone(compiler.emitSettings, oldEmitSettings);
3212            var oldModuleGenTarget = TypeScript.moduleGenTarget;
3213            if (settingsCallback) {
3214                settingsCallback(compiler.settings);
3215                compiler.emitSettings = new TypeScript.EmitOptions(compiler.settings);
3216            }
3217            try {
3218                compileString(code, filename, callback, context, references);
3219            }
3220            finally {
3221                // If settingsCallback exists, assume that it modified the global compiler instance's settings in some way.
3222                // So that a test doesn't have side effects for tests run after it, restore the compiler settings to their previous state.
3223                if (settingsCallback) {
3224                    compiler.settings = oldCompilerSettings;
3225                    compiler.emitSettings = oldEmitSettings;
3226                    TypeScript.moduleGenTarget = oldModuleGenTarget;
3227                }
3228            }
3229        }
3230        Compiler.compileUnit = compileUnit;
3231        function compileUnits(units, callback, settingsCallback) {
3232            var lastUnit = units[units.length - 1];
3233            var unitName = switchToForwardSlashes(lastUnit.name).match(/[^\/]*$/)[0];
3234            var dependencies = units.slice(0, units.length - 1);
3235            var compilationContext = Harness.Compiler.defineCompilationContextForTest(unitName, dependencies);
3236            compileUnit(lastUnit.content, unitName, callback, settingsCallback, compilationContext, lastUnit.references);
3237        }
3238        Compiler.compileUnits = compileUnits;
3239        function emitToOutfile(outfile) {
3240            compiler.emitToOutfile(outfile);
3241        }
3242        Compiler.emitToOutfile = emitToOutfile;
3243        function emit(ioHost, usePullEmitter) {
3244            compiler.emit(ioHost, usePullEmitter);
3245        }
3246        Compiler.emit = emit;
3247        function compileString(code, unitName, callback, context, references) {
3248            var scripts = [];
3249            reset();
3250            if (context) {
3251                context.preCompile();
3252            }
3253            var isDeclareFile = Harness.Compiler.isDeclareFile(unitName);
3254            // for single file tests just add them as using the old '0.ts' naming scheme
3255            var uName = context ? unitName : ((isDeclareFile) ? '0.d.ts' : '0.ts');
3256            scripts.push(addUnit(code, uName, false, isDeclareFile, references));
3257            compile(code, uName);
3258            var errors;
3259            if (Harness.usePull) {
3260                // TODO: no emit support with pull yet
3261                errors = compiler.pullGetErrorsForFile(uName);
3262                emit(stdout, true);
3263            }
3264            else {
3265                errors = stderr.lines;
3266                emit(stdout, false);
3267                //output decl file
3268                compiler.emitDeclarations();
3269            }
3270            if (context) {
3271                context.postCompile();
3272            }
3273            callback(new CompilerResult(stdout.toArray(), errors, scripts));
3274        }
3275        Compiler.compileString = compileString;
3276        /** Returns a set of functions which can be later executed to add and remove given dependencies to the compiler so that
3277         *  a file can be successfully compiled. These functions will add/remove named units and code to the compiler for each dependency.
3278         */
3279        function defineCompilationContextForTest(filename, dependencies) {
3280            // if the given file has no dependencies, there is no context to return, it can be compiled without additional work
3281            if (dependencies.length == 0) {
3282                return null;
3283            }
3284            else {
3285                var addedFiles = [];
3286                var precompile = function () {
3287                    // REVIEW: if any dependency has a triple slash reference then does postCompile potentially have to do a recreate since we can't update references with updateUnit?
3288                    // easy enough to do if so, prefer to avoid the recreate cost until it proves to be an issue
3289                    dependencies.forEach(function (dep) {
3290                        addUnit(dep.content, dep.name, false, Harness.Compiler.isDeclareFile(dep.name));
3291                        addedFiles.push(dep.name);
3292                    });
3293                };
3294                var postcompile = function () {
3295                    addedFiles.forEach(function (file) {
3296                        updateUnit('', file);
3297                    });
3298                };
3299                var context = {
3300                    filename: filename,
3301                    preCompile: precompile,
3302                    postCompile: postcompile
3303                };
3304                return context;
3305            }
3306        }
3307        Compiler.defineCompilationContextForTest = defineCompilationContextForTest;
3308    })(Compiler = Harness.Compiler || (Harness.Compiler = {}));
3309    /** Parses the test cases files
3310     *  extracts options and individual files in a multifile test
3311     */
3312    var TestCaseParser;
3313    (function (TestCaseParser) {
3314        optionRegex = /^[\/]{2}\s*@(\w+):\s*(\S*)/gm; // multiple matches on multiple lines
3315        // List of allowed metadata names
3316        var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out"];
3317        function extractCompilerSettings(content) {
3318            var opts = [];
3319            var match;
3320            while ((match = optionRegex.exec(content)) != null) {
3321                opts.push({ flag: match[1], value: match[2] });
3322            }
3323            return opts;
3324        }
3325        /** Given a test file containing // @Filename directives, return an array of named units of code to be added to an existing compiler instance */
3326        function makeUnitsFromTest(code, filename) {
3327            var settings = extractCompilerSettings(code);
3328            // List of all the subfiles we've parsed out
3329            var files = [];
3330            var lines = splitContentByNewlines(code);
3331            // Stuff related to the subfile we're parsing
3332            var currentFileContent = null;
3333            var currentFileOptions = {};
3334            var currentFileName = null;
3335            var refs = [];
3336            for (var i = 0; i < lines.length; i++) {
3337                var line = lines[i];
3338                var isTripleSlashReference = /[\/]{3}\s*<reference path/.test(line);
3339                var testMetaData = optionRegex.exec(line);
3340                // Triple slash references need to be tracked as they are added to the compiler as an additional parameter to addUnit
3341                if (isTripleSlashReference) {
3342                    var isRef = line.match(/reference\spath='(\w*_?\w*\.?d?\.ts)'/);
3343                    if (isRef) {
3344                        var ref = {
3345                            minChar: 0,
3346                            limChar: 0,
3347                            startLine: 0,
3348                            startCol: 0,
3349                            path: isRef[1],
3350                            isResident: false
3351                        };
3352                        refs.push(ref);
3353                    }
3354                }
3355                else if (testMetaData) {
3356                    // Comment line, check for global/file @options and record them
3357                    optionRegex.lastIndex = 0;
3358                    var fileNameIndex = fileMetadataNames.indexOf(testMetaData[1].toLowerCase());
3359                    if (fileNameIndex == -1) {
3360                        throw new Error('Unrecognized metadata name "' + testMetaData[1] + '". Available file metadata names are: ' + fileMetadataNames.join(', '));
3361                    }
3362                    else if (fileNameIndex == 0) {
3363                        currentFileOptions[testMetaData[1]] = testMetaData[2];
3364                    }
3365                    else {
3366                        continue;
3367                    }
3368                    // New metadata statement after having collected some code to go with the previous metadata
3369                    if (currentFileName) {
3370                        // Store result file
3371                        var newTestFile = {
3372                            content: currentFileContent,
3373                            name: currentFileName,
3374                            fileOptions: currentFileOptions,
3375                            originalFilePath: filename,
3376                            references: refs
3377                        };
3378                        files.push(newTestFile);
3379                        // Reset local data
3380                        currentFileContent = null;
3381                        currentFileOptions = {};
3382                        currentFileName = testMetaData[2];
3383                        refs = [];
3384                    }
3385                    else {
3386                        // First metadata marker in the file
3387                        currentFileName = testMetaData[2];
3388                    }
3389                }
3390                else {
3391                    // Subfile content line
3392                    // Append to the current subfile content, inserting a newline needed
3393                    if (currentFileContent === null) {
3394                        currentFileContent = '';
3395                    }
3396                    else {
3397                        // End-of-line
3398                        currentFileContent = currentFileContent + '\n';
3399                    }
3400                    currentFileContent = currentFileContent + line;
3401                }
3402            }
3403            // normalize the filename for the single file case
3404            currentFileName = files.length > 0 ? currentFileName : '0.ts';
3405            // EOF, push whatever remains
3406            var newTestFile = {
3407                content: currentFileContent || '',
3408                name: currentFileName,
3409                fileOptions: currentFileOptions,
3410                originalFilePath: filename,
3411                references: refs
3412            };
3413            files.push(newTestFile);
3414            return { settings: settings, testUnitData: files };
3415        }
3416        TestCaseParser.makeUnitsFromTest = makeUnitsFromTest;
3417    })(TestCaseParser = Harness.TestCaseParser || (Harness.TestCaseParser = {}));
3418    var ScriptInfo = /** @class */ (function () {
3419        function ScriptInfo(name, content, isResident, maxScriptVersions) {
3420            this.name = name;
3421            this.content = content;
3422            this.isResident = isResident;
3423            this.maxScriptVersions = maxScriptVersions;
3424            this.editRanges = [];
3425            this.version = 1;
3426        }
3427        ScriptInfo.prototype.updateContent = function (content, isResident) {
3428            this.editRanges = [];
3429            this.content = content;
3430            this.isResident = isResident;
3431            this.version++;
3432        };
3433        ScriptInfo.prototype.editContent = function (minChar, limChar, newText) {
3434            // Apply edits
3435            var prefix = this.content.substring(0, minChar);
3436            var middle = newText;
3437            var suffix = this.content.substring(limChar);
3438            this.content = prefix + middle + suffix;
3439            // Store edit range + new length of script
3440            this.editRanges.push({
3441                length: this.content.length,
3442                editRange: new TypeScript.ScriptEditRange(minChar, limChar, (limChar - minChar) + newText.length)
3443            });
3444            if (this.editRanges.length > this.maxScriptVersions) {
3445                this.editRanges.splice(0, this.maxScriptVersions - this.editRanges.length);
3446            }
3447            // Update version #
3448            this.version++;
3449        };
3450        ScriptInfo.prototype.getEditRangeSinceVersion = function (version) {
3451            if (this.version == version) {
3452                // No edits!
3453                return null;
3454            }
3455            var initialEditRangeIndex = this.editRanges.length - (this.version - version);
3456            if (initialEditRangeIndex < 0 || initialEditRangeIndex >= this.editRanges.length) {
3457                // Too far away from what we know
3458                return TypeScript.ScriptEditRange.unknown();
3459            }
3460            var entries = this.editRanges.slice(initialEditRangeIndex);
3461            var minDistFromStart = entries.map(function (x) { return x.editRange.minChar; }).reduce(function (prev, current) { return Math.min(prev, current); });
3462            var minDistFromEnd = entries.map(function (x) { return x.length - x.editRange.limChar; }).reduce(function (prev, current) { return Math.min(prev, current); });
3463            var aggDelta = entries.map(function (x) { return x.editRange.delta; }).reduce(function (prev, current) { return prev + current; });
3464            return new TypeScript.ScriptEditRange(minDistFromStart, entries[0].length - minDistFromEnd, aggDelta);
3465        };
3466        return ScriptInfo;
3467    }());
3468    Harness.ScriptInfo = ScriptInfo;
3469    var TypeScriptLS = /** @class */ (function () {
3470        function TypeScriptLS() {
3471            this.ls = null;
3472            this.scripts = [];
3473            this.maxScriptVersions = 100;
3474        }
3475        TypeScriptLS.prototype.addDefaultLibrary = function () {
3476            this.addScript("lib.d.ts", Harness.Compiler.libText, true);
3477        };
3478        TypeScriptLS.prototype.addFile = function (name, isResident) {
3479            if (isResident === void 0) { isResident = false; }
3480            var code = readFile(name);
3481            this.addScript(name, code, isResident);
3482        };
3483        TypeScriptLS.prototype.addScript = function (name, content, isResident) {
3484            if (isResident === void 0) { isResident = false; }
3485            var script = new ScriptInfo(name, content, isResident, this.maxScriptVersions);
3486            this.scripts.push(script);
3487        };
3488        TypeScriptLS.prototype.updateScript = function (name, content, isResident) {
3489            if (isResident === void 0) { isResident = false; }
3490            for (var i = 0; i < this.scripts.length; i++) {
3491                if (this.scripts[i].name == name) {
3492                    this.scripts[i].updateContent(content, isResident);
3493                    return;
3494                }
3495            }
3496            this.addScript(name, content, isResident);
3497        };
3498        TypeScriptLS.prototype.editScript = function (name, minChar, limChar, newText) {
3499            for (var i = 0; i < this.scripts.length; i++) {
3500                if (this.scripts[i].name == name) {
3501                    this.scripts[i].editContent(minChar, limChar, newText);
3502                    return;
3503                }
3504            }
3505            throw new Error("No script with name '" + name + "'");
3506        };
3507        TypeScriptLS.prototype.getScriptContent = function (scriptIndex) {
3508            return this.scripts[scriptIndex].content;
3509        };
3510        //////////////////////////////////////////////////////////////////////
3511        // ILogger implementation
3512        //
3513        TypeScriptLS.prototype.information = function () { return false; };
3514        TypeScriptLS.prototype.debug = function () { return true; };
3515        TypeScriptLS.prototype.warning = function () { return true; };
3516        TypeScriptLS.prototype.error = function () { return true; };
3517        TypeScriptLS.prototype.fatal = function () { return true; };
3518        TypeScriptLS.prototype.log = function (s) {
3519            // For debugging...
3520            //IO.printLine("TypeScriptLS:" + s);
3521        };
3522        //////////////////////////////////////////////////////////////////////
3523        // ILanguageServiceShimHost implementation
3524        //
3525        TypeScriptLS.prototype.getCompilationSettings = function () {
3526            return ""; // i.e. default settings
3527        };
3528        TypeScriptLS.prototype.getScriptCount = function () {
3529            return this.scripts.length;
3530        };
3531        TypeScriptLS.prototype.getScriptSourceText = function (scriptIndex, start, end) {
3532            return this.scripts[scriptIndex].content.substring(start, end);
3533        };
3534        TypeScriptLS.prototype.getScriptSourceLength = function (scriptIndex) {
3535            return this.scripts[scriptIndex].content.length;
3536        };
3537        TypeScriptLS.prototype.getScriptId = function (scriptIndex) {
3538            return this.scripts[scriptIndex].name;
3539        };
3540        TypeScriptLS.prototype.getScriptIsResident = function (scriptIndex) {
3541            return this.scripts[scriptIndex].isResident;
3542        };
3543        TypeScriptLS.prototype.getScriptVersion = function (scriptIndex) {
3544            return this.scripts[scriptIndex].version;
3545        };
3546        TypeScriptLS.prototype.getScriptEditRangeSinceVersion = function (scriptIndex, scriptVersion) {
3547            var range = this.scripts[scriptIndex].getEditRangeSinceVersion(scriptVersion);
3548            var result = (range.minChar + "," + range.limChar + "," + range.delta);
3549            return result;
3550        };
3551        /** Return a new instance of the language service shim, up-to-date wrt to typecheck.
3552         *  To access the non-shim (i.e. actual) language service, use the "ls.languageService" property.
3553         */
3554        TypeScriptLS.prototype.getLanguageService = function () {
3555            var ls = new Services.TypeScriptServicesFactory().createLanguageServiceShim(this);
3556            ls.refresh(true);
3557            this.ls = ls;
3558            return ls;
3559        };
3560        /** Parse file given its source text */
3561        TypeScriptLS.prototype.parseSourceText = function (fileName, sourceText) {
3562            var parser = new TypeScript.Parser();
3563            parser.setErrorRecovery(null);
3564            parser.errorCallback = function (a, b, c, d) { };
3565            var script = parser.parse(sourceText, fileName, 0);
3566            return script;
3567        };
3568        /** Parse a file on disk given its filename */
3569        TypeScriptLS.prototype.parseFile = function (fileName) {
3570            var sourceText = new TypeScript.StringSourceText(IO.readFile(fileName));
3571            return this.parseSourceText(fileName, sourceText);
3572        };
3573        /**
3574         * @param line 1 based index
3575         * @param col 1 based index
3576        */
3577        TypeScriptLS.prototype.lineColToPosition = function (fileName, line, col) {
3578            var script = this.ls.languageService.getScriptAST(fileName);
3579            assert.notNull(script);
3580            assert.is(line >= 1);
3581            assert.is(col >= 1);
3582            assert.is(line <= script.locationInfo.lineMap.length);
3583            return TypeScript.getPositionFromZeroBasedLineColumn(script, line - 1, col - 1);
3584        };
3585        /**
3586         * @param line 0 based index
3587         * @param col 0 based index
3588        */
3589        TypeScriptLS.prototype.positionToZeroBasedLineCol = function (fileName, position) {
3590            var script = this.ls.languageService.getScriptAST(fileName);
3591            assert.notNull(script);
3592            var result = TypeScript.getZeroBasedLineColumnFromPosition(script, position);
3593            assert.is(result.line >= 0);
3594            assert.is(result.col >= 0);
3595            return result;
3596        };
3597        /** Verify that applying edits to sourceFileName result in the content of the file baselineFileName */
3598        TypeScriptLS.prototype.checkEdits = function (sourceFileName, baselineFileName, edits) {
3599            var script = readFile(sourceFileName);
3600            var formattedScript = this.applyEdits(script, edits);
3601            var baseline = readFile(baselineFileName);
3602            assert.noDiff(formattedScript, baseline);
3603            assert.equal(formattedScript, baseline);
3604        };
3605        /** Apply an array of text edits to a string, and return the resulting string. */
3606        TypeScriptLS.prototype.applyEdits = function (content, edits) {
3607            var result = content;
3608            edits = this.normalizeEdits(edits);
3609            for (var i = edits.length - 1; i >= 0; i--) {
3610                var edit = edits[i];
3611                var prefix = result.substring(0, edit.minChar);
3612                var middle = edit.text;
3613                var suffix = result.substring(edit.limChar);
3614                result = prefix + middle + suffix;
3615            }
3616            return result;
3617        };
3618        /** Normalize an array of edits by removing overlapping entries and sorting entries on the minChar position. */
3619        TypeScriptLS.prototype.normalizeEdits = function (edits) {
3620            var result = [];
3621            function mapEdits(edits) {
3622                var result = [];
3623                for (var i = 0; i < edits.length; i++) {
3624                    result.push({ edit: edits[i], index: i });
3625                }
3626                return result;
3627            }
3628            var temp = mapEdits(edits).sort(function (a, b) {
3629                var result = a.edit.minChar - b.edit.minChar;
3630                if (result == 0)
3631                    result = a.index - b.index;
3632                return result;
3633            });
3634            var current = 0;
3635            var next = 1;
3636            while (current < temp.length) {
3637                var currentEdit = temp[current].edit;
3638                // Last edit
3639                if (next >= temp.length) {
3640                    result.push(currentEdit);
3641                    current++;
3642                    continue;
3643                }
3644                var nextEdit = temp[next].edit;
3645                var gap = nextEdit.minChar - currentEdit.limChar;
3646                // non-overlapping edits
3647                if (gap >= 0) {
3648                    result.push(currentEdit);
3649                    current = next;
3650                    next++;
3651                    continue;
3652                }
3653                // overlapping edits: for now, we only support ignoring an next edit
3654                // entirely contained in the current edit.
3655                if (currentEdit.limChar >= nextEdit.limChar) {
3656                    next++;
3657                    continue;
3658                }
3659                else {
3660                    throw new Error("Trying to apply overlapping edits");
3661                }
3662            }
3663            return result;
3664        };
3665        TypeScriptLS.prototype.getHostSettings = function () {
3666            return JSON.stringify({ usePullLanguageService: Harness.usePull });
3667        };
3668        return TypeScriptLS;
3669    }());
3670    Harness.TypeScriptLS = TypeScriptLS;
3671    // Describe/it definitions
3672    function describe(description, block) {
3673        var newScenario = new Scenario(description, block);
3674        if (Runnable.currentStack.length === 0) {
3675            Runnable.currentStack.push(currentRun);
3676        }
3677        Runnable.currentStack[Runnable.currentStack.length - 1].addChild(newScenario);
3678    }
3679    Harness.describe = describe;
3680    function it(description, block) {
3681        var testCase = new TestCase(description, block);
3682        Runnable.currentStack[Runnable.currentStack.length - 1].addChild(testCase);
3683    }
3684    Harness.it = it;
3685    function run() {
3686        if (typeof process !== "undefined") {
3687            process.on('uncaughtException', Runnable.handleError);
3688        }
3689        Baseline.reset();
3690        currentRun.run();
3691    }
3692    Harness.run = run;
3693    /** Runs TypeScript or Javascript code. */
3694    var Runner;
3695    (function (Runner) {
3696        function runCollateral(path, callback) {
3697            path = switchToForwardSlashes(path);
3698            runString(readFile(path), path.match(/[^\/]*$/)[0], callback);
3699        }
3700        Runner.runCollateral = runCollateral;
3701        function runJSString(code, callback) {
3702            // List of names that get overriden by various test code we eval
3703            var dangerNames = ['Array'];
3704            var globalBackup = {};
3705            var n = null;
3706            for (n in dangerNames) {
3707                globalBackup[dangerNames[n]] = global[dangerNames[n]];
3708            }
3709            try {
3710                var res = eval(code);
3711                for (n in dangerNames) {
3712                    global[dangerNames[n]] = globalBackup[dangerNames[n]];
3713                }
3714                callback(null, res);
3715            }
3716            catch (e) {
3717                for (n in dangerNames) {
3718                    global[dangerNames[n]] = globalBackup[dangerNames[n]];
3719                }
3720                callback(e, null);
3721            }
3722        }
3723        Runner.runJSString = runJSString;
3724        function runString(code, unitName, callback) {
3725            Compiler.compileString(code, unitName, function (res) {
3726                runJSString(res.code, callback);
3727            });
3728        }
3729        Runner.runString = runString;
3730    })(Runner = Harness.Runner || (Harness.Runner = {}));
3731    /** Support class for baseline files */
3732    var Baseline;
3733    (function (Baseline) {
3734        var reportFilename = 'baseline-report.html';
3735        var firstRun = true;
3736        var htmlTrailer = '</body></html>';
3737        var htmlLeader = '<html><head><title>Baseline Report</title>';
3738        htmlLeader += ("<style>");
3739        htmlLeader += '\r\n' + (".code { font: 9pt 'Courier New'; }");
3740        htmlLeader += '\r\n' + (".old { background-color: #EE1111; }");
3741        htmlLeader += '\r\n' + (".new { background-color: #FFFF11; }");
3742        htmlLeader += '\r\n' + (".from { background-color: #EE1111; color: #1111EE; }");
3743        htmlLeader += '\r\n' + (".to { background-color: #EEEE11; color: #1111EE; }");
3744        htmlLeader += '\r\n' + ("h2 { margin-bottom: 0px; }");
3745        htmlLeader += '\r\n' + ("h2 { padding-bottom: 0px; }");
3746        htmlLeader += '\r\n' + ("h4 { font-weight: normal; }");
3747        htmlLeader += '\r\n' + ("</style>");
3748        function localPath(filename) {
3749            if (global.runners[0].testType === 'prototyping') {
3750                return Harness.userSpecifiedroot + 'tests/baselines/prototyping/local/' + filename;
3751            }
3752            else {
3753                return Harness.userSpecifiedroot + 'tests/baselines/local/' + filename;
3754            }
3755        }
3756        function referencePath(filename) {
3757            if (global.runners[0].testType === 'prototyping') {
3758                return Harness.userSpecifiedroot + 'tests/baselines/prototyping/reference/' + filename;
3759            }
3760            else {
3761                return Harness.userSpecifiedroot + 'tests/baselines/reference/' + filename;
3762            }
3763        }
3764        function reset() {
3765            if (IO.fileExists(reportFilename)) {
3766                IO.deleteFile(reportFilename);
3767            }
3768        }
3769        Baseline.reset = reset;
3770        function prepareBaselineReport() {
3771            var reportContent = htmlLeader;
3772            // Delete the baseline-report.html file if needed
3773            if (IO.fileExists(reportFilename)) {
3774                reportContent = IO.readFile(reportFilename);
3775                reportContent = reportContent.replace(htmlTrailer, '');
3776            }
3777            else {
3778                reportContent = htmlLeader;
3779            }
3780            return reportContent;
3781        }
3782        function generateActual(actualFilename, generateContent) {
3783            // Create folders if needed
3784            IO.createDirectory(IO.dirName(IO.dirName(actualFilename)));
3785            IO.createDirectory(IO.dirName(actualFilename));
3786            // Delete the actual file in case it fails
3787            if (IO.fileExists(actualFilename)) {
3788                IO.deleteFile(actualFilename);
3789            }
3790            var actual = generateContent();
3791            if (actual === undefined) {
3792                throw new Error('The generated content was "undefined". Return "null" if no baselining is required."');
3793            }
3794            // Store the content in the 'local' folder so we
3795            // can accept it later (manually)
3796            if (actual !== null) {
3797                IO.writeFile(actualFilename, actual);
3798            }
3799            return actual;
3800        }
3801        function compareToBaseline(actual, relativeFilename, opts) {
3802            // actual is now either undefined (the generator had an error), null (no file requested),
3803            // or some real output of the function
3804            if (actual === undefined) {
3805                // Nothing to do
3806                return;
3807            }
3808            var refFilename = referencePath(relativeFilename);
3809            if (actual === null) {
3810                actual = '<no content>';
3811            }
3812            var expected = '<no content>';
3813            if (IO.fileExists(refFilename)) {
3814                expected = IO.readFile(refFilename);
3815            }
3816            var lineEndingSensitive = opts && opts.LineEndingSensitive;
3817            if (!lineEndingSensitive) {
3818                expected = expected.replace(/\r\n?/g, '\n');
3819                actual = actual.replace(/\r\n?/g, '\n');
3820            }
3821            return { expected: expected, actual: actual };
3822        }
3823        function writeComparison(expected, actual, relativeFilename, actualFilename, descriptionForDescribe) {
3824            if (expected != actual) {
3825                // Overwrite & issue error
3826                var errMsg = 'The baseline file ' + relativeFilename + ' has changed. Please refer to baseline-report.html and ';
3827                errMsg += 'either fix the regression (if unintended) or run nmake baseline-accept (if intended).';
3828                var refFilename = referencePath(relativeFilename);
3829                // Append diff to the report
3830                var diff = new Diff.StringDiff(expected, actual);
3831                var header = '<h2>' + descriptionForDescribe + '</h2>';
3832                header += '<h4>Left file: ' + actualFilename + '; Right file: ' + refFilename + '</h4>';
3833                var trailer = '<hr>';
3834                var reportContentSoFar = prepareBaselineReport();
3835                reportContentSoFar = reportContentSoFar + header + '<div class="code">' + diff.mergedHtml + '</div>' + trailer + htmlTrailer;
3836                IO.writeFile(reportFilename, reportContentSoFar);
3837                throw new Error(errMsg);
3838            }
3839        }
3840        function runBaseline(descriptionForDescribe, relativeFilename, generateContent, runImmediately, opts) {
3841            if (runImmediately === void 0) { runImmediately = false; }
3842            var actual = undefined;
3843            var actualFilename = localPath(relativeFilename);
3844            if (runImmediately) {
3845                var actual = generateActual(actualFilename, generateContent);
3846                var comparison = compareToBaseline(actual, relativeFilename, opts);
3847                writeComparison(comparison.expected, comparison.actual, relativeFilename, actualFilename, descriptionForDescribe);
3848            }
3849            else {
3850                describe(descriptionForDescribe, function () {
3851                    var actual;
3852                    it('Can generate the content without error', function () {
3853                        actual = generateActual(actualFilename, generateContent);
3854                    });
3855                    it('Matches the baseline file', function () {
3856                        var comparison = compareToBaseline(actual, relativeFilename, opts);
3857                        writeComparison(comparison.expected, comparison.actual, relativeFilename, actualFilename, descriptionForDescribe);
3858                    });
3859                });
3860            }
3861        }
3862        Baseline.runBaseline = runBaseline;
3863    })(Baseline = Harness.Baseline || (Harness.Baseline = {}));
3864    var currentRun = new Run();
3865    global.describe = describe;
3866    global.run = run;
3867    global.it = it;
3868    global.assert = Harness.Assert;
3869})(Harness || (Harness = {}));
3870