<commit_before>/// <reference path='harness.ts' />
/// <reference path='runnerbase.ts' />
/// <reference path='typeWriter.ts' />
/// <reference path='syntacticCleaner.ts' />

enum CompilerTestType {
    Conformance,
    Regressions,
    Test262
}

class CompilerBaselineRunner extends RunnerBase {
    private basePath = 'tests/cases';
    private errors: boolean;
    private emit: boolean;
    private decl: boolean;
    private output: boolean;

    public options: string;

    constructor(public testType?: CompilerTestType) {
        super();
        this.errors = true;
        this.emit = true;
        this.decl = true;
        this.output = true;
        if (testType === CompilerTestType.Conformance) {
            this.basePath += '/conformance';
        }
        else if (testType === CompilerTestType.Regressions) {
            this.basePath += '/compiler';
        }
        else if (testType === CompilerTestType.Test262) {
            this.basePath += '/test262';
        } else {
            this.basePath += '/compiler'; // default to this for historical reasons
        }
    }

    public checkTestCodeOutput(fileName: string) {
        describe('compiler tests for ' + fileName, () => {
            // strips the fileName from the path.
            var justName = fileName.replace(/^.*[\\\/]/, '');
            var content = Harness.IO.readFile(fileName);
            var testCaseContent = Harness.TestCaseParser.makeUnitsFromTest(content, fileName);

            var units = testCaseContent.testUnitData;
            var tcSettings = testCaseContent.settings;
            var createNewInstance = false;

            var lastUnit = units[units.length - 1];
            var rootDir = lastUnit.originalFilePath.indexOf('conformance') === -1 ? 'tests/cases/compiler/' : lastUnit.originalFilePath.substring(0, lastUnit.originalFilePath.lastIndexOf('/')) + '/';

            var result: Harness.Compiler.CompilerResult;
            var options: ts.CompilerOptions;
            // equivalent to the files that will be passed on the command line
            var toBeCompiled: { unitName: string; content: string }[];
            // equivalent to other files on the file system not directly passed to the compiler (ie things that are referenced by other files)
            var otherFiles: { unitName: string; content: string }[];
            var harnessCompiler: Harness.Compiler.HarnessCompiler;

            var declToBeCompiled: { unitName: string; content: string }[] = [];
            var declOtherFiles: { unitName: string; content: string }[] = [];
            var declResult: Harness.Compiler.CompilerResult;

            var createNewInstance = false;

            before(() => {
                harnessCompiler = Harness.Compiler.getCompiler();
                // We need to assemble the list of input files for the compiler and other related files on the 'filesystem' (ie in a multi-file test)
                // If the last file in a test uses require or a triple slash reference we'll assume all other files will be brought in via references,
                // otherwise, assume all files are just meant to be in the same compilation session without explicit references to one another.
                toBeCompiled = [];
                otherFiles = [];
                if (/require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
                    toBeCompiled.push({ unitName: rootDir + lastUnit.name, content: lastUnit.content });
                    units.forEach(unit => {
                        if (unit.name !== lastUnit.name) {
                            otherFiles.push({ unitName: rootDir + unit.name, content: unit.content });
                        }
                    });
                } else {
                    toBeCompiled = units.map(unit => {
                        return { unitName: rootDir + unit.name, content: unit.content };
                    });
                }

                options = harnessCompiler.compileFiles(toBeCompiled, otherFiles, function (compileResult) {
                    result = compileResult;
                }, function (settings) {
                        harnessCompiler.setCompilerSettings(tcSettings);
                    });
            });

            beforeEach(() => {
                /* The compiler doesn't handle certain flags flipping during a single compilation setting. Tests on these flags will need 
                   a fresh compiler instance for themselves and then create a fresh one for the next test. Would be nice to get dev fixes
                   eventually to remove this limitation. */
                for (var i = 0; i < tcSettings.length; ++i) {
                    if (!createNewInstance && (tcSettings[i].flag == "noimplicitany" || tcSettings[i].flag === 'target')) {
                        harnessCompiler = Harness.Compiler.getCompiler({
                            useExistingInstance: false,
                            optionsForFreshInstance: { useMinimalDefaultLib: true, noImplicitAny: tcSettings[i].flag === "noimplicitany" }
                        });
                        harnessCompiler.setCompilerSettings(tcSettings);
                        createNewInstance = true;
                    }
                }
            });

            afterEach(() => {
                if (createNewInstance) {
                    harnessCompiler = Harness.Compiler.getCompiler({
                        useExistingInstance: false,
                        optionsForFreshInstance: { useMinimalDefaultLib: true, noImplicitAny: false }
                    });
                    createNewInstance = false;
                }
            });

            function getErrorBaseline(toBeCompiled: { unitName: string; content: string }[],
                otherFiles: { unitName: string; content: string }[],
                result: Harness.Compiler.CompilerResult
                ) {

                var outputLines: string[] = [];
                // Count up all the errors we find so we don't miss any
                var totalErrorsReported = 0;
                
                function outputErrorText(error: Harness.Compiler.MinimalDiagnostic) {
                    var errLines = RunnerBase.removeFullPaths(error.message)
                        .split('\n')
                        .map(s => s.length > 0 && s.charAt(s.length - 1) === '\r' ? s.substr(0, s.length - 1) : s)
                        .filter(s => s.length > 0)
                        .map(s => '!!! ' + s);
                    errLines.forEach(e => outputLines.push(e));

                    totalErrorsReported++;
                }

                // Report glovbal errors:
                var globalErrors = result.errors.filter(err => !err.filename);
                globalErrors.forEach(err => outputErrorText(err));

                // 'merge' the lines of each input file with any errors associated with it
                toBeCompiled.concat(otherFiles).forEach(inputFile => {
                    // Filter down to the errors in the file
                    var fileErrors = result.errors.filter(e => {
                        var errFn = e.filename;
                        return errFn && errFn === inputFile.unitName;
                    });


                    // Header
                    outputLines.push('==== ' + inputFile.unitName + ' (' + fileErrors.length + ' errors) ====');

                    // Make sure we emit something for every error
                    var markedErrorCount = 0;
                    // For each line, emit the line followed by any error squiggles matching this line
                    // Note: IE JS engine incorrectly handles consecutive delimiters here when using RegExp split, so
                    // we have to string-based splitting instead and try to figure out the delimiting chars

                    var lineStarts = ts.getLineStarts(inputFile.content);
                    var lines = inputFile.content.split('\n');
                    lines.forEach((line, lineIndex) => {
                        if (line.length > 0 && line.charAt(line.length - 1) === '\r') {
                            line = line.substr(0, line.length - 1);
                        }

                        var thisLineStart = lineStarts[lineIndex];
                        var nextLineStart: number;
                        // On the last line of the file, fake the next line start number so that we handle errors on the last character of the file correctly
                        if (lineIndex === lines.length - 1) {
                            nextLineStart = inputFile.content.length;
                        } else {
                            nextLineStart = lineStarts[lineIndex + 1];
                        }
                        // Emit this line from the original file
                        outputLines.push('    ' + line);
                        fileErrors.forEach(err => {
                            // Does any error start or continue on to this line? Emit squiggles
                            if ((err.end >= thisLineStart) && ((err.start < nextLineStart) || (lineIndex === lines.length - 1))) {
                                // How many characters from the start of this line the error starts at (could be positive or negative)
                                var relativeOffset = err.start - thisLineStart;
                                // How many characters of the error are on this line (might be longer than this line in reality)
                                var length = (err.end - err.start) - Math.max(0, thisLineStart - err.start);
                                // Calculate the start of the squiggle
                                var squiggleStart = Math.max(0, relativeOffset);
                                // TODO/REVIEW: this doesn't work quite right in the browser if a multi file test has files whose names are just the right length relative to one another
                                outputLines.push('    ' + line.substr(0, squiggleStart).replace(/[^\s]/g, ' ') + new Array(Math.min(length, line.length - squiggleStart) + 1).join('~'));

                                // If the error ended here, or we're at the end of the file, emit its message
                                if ((lineIndex === lines.length - 1) || nextLineStart > err.end) {
                                    // Just like above, we need to do a split on a string instead of on a regex
                                    // because the JS engine does regexes wrong

                                    outputErrorText(err);
                                    markedErrorCount++;
                                }
                            }
                        });
                    });

                    // Verify we didn't miss any errors in this file
                    assert.equal(markedErrorCount, fileErrors.length, 'count of errors in ' + inputFile.unitName);
                });

                // Verify we didn't miss any errors in total
                assert.equal(totalErrorsReported, result.errors.length, 'total number of errors');

                return outputLines.join('\r\n');
            }

            // check errors
            it('Correct errors for ' + fileName, () => {
                if (this.errors) {
                    Harness.Baseline.runBaseline('Correct errors for ' + fileName, justName.replace(/\.ts$/, '.errors.txt'), (): string => {
                        if (result.errors.length === 0) return null;

                        return getErrorBaseline(toBeCompiled, otherFiles, result);
                    });
                }
            });

            // Source maps?
            it('Correct sourcemap content for ' + fileName, () => {
                if (result.sourceMapRecord) {
                    Harness.Baseline.runBaseline('Correct sourcemap content for ' + fileName, justName.replace(/\.ts$/, '.sourcemap.txt'), () => {
                        return result.sourceMapRecord;
                    });
                }
            });

            // Compile .d.ts files
            it('Compiler generated .d.ts files for ' + fileName, () => {
                if (options.declaration && result.errors.length === 0 && result.declFilesCode.length !== result.files.length) {
                    throw new Error('There were no errors and declFiles generated did not match number of js files generated');
                }

                // if the .d.ts is non-empty, confirm it compiles correctly as well
                if (options.declaration && result.errors.length === 0 && result.declFilesCode.length > 0) {
                    function getDtsFile(file: { unitName: string; content: string }) {
                        if (Harness.Compiler.isDTS(file.unitName)) {
                            return file;
                        }
                        else {
                            var declFile = ts.forEach(result.declFilesCode,
                                declFile => declFile.fileName === (file.unitName.substr(0, file.unitName.length - ".ts".length) + ".d.ts")
                                    ? declFile : undefined);
                            return { unitName: rootDir + Harness.Path.getFileName(declFile.fileName), content: declFile.code };
                        }
                    }

                    ts.forEach(toBeCompiled, file => { declToBeCompiled.push(getDtsFile(file)); });
                    ts.forEach(otherFiles, file => { declOtherFiles.push(getDtsFile(file)); });
                    harnessCompiler.compileFiles(declToBeCompiled, declOtherFiles, function (compileResult) {
                        declResult = compileResult;
                    }, function (settings) {
                            harnessCompiler.setCompilerSettings(tcSettings);
                        });
                }
            });


            it('Correct JS output for ' + fileName, () => {
                if (!ts.fileExtensionIs(lastUnit.name, '.d.ts') && this.emit) {
                    if (result.files.length === 0 && result.errors.length === 0) {
                        throw new Error('Expected at least one js file to be emitted or at least one error to be created.');
                    }
<commit_msg>Updating with code review feedback<commit_after>/// <reference path='harness.ts' />
/// <reference path='runnerbase.ts' />
/// <reference path='typeWriter.ts' />
/// <reference path='syntacticCleaner.ts' />

enum CompilerTestType {
    Conformance,
    Regressions,
    Test262
}

class CompilerBaselineRunner extends RunnerBase {
    private basePath = 'tests/cases';
    private errors: boolean;
    private emit: boolean;
    private decl: boolean;
    private output: boolean;

    public options: string;

    constructor(public testType?: CompilerTestType) {
        super();
        this.errors = true;
        this.emit = true;
        this.decl = true;
        this.output = true;
        if (testType === CompilerTestType.Conformance) {
            this.basePath += '/conformance';
        }
        else if (testType === CompilerTestType.Regressions) {
            this.basePath += '/compiler';
        }
        else if (testType === CompilerTestType.Test262) {
            this.basePath += '/test262';
        } else {
            this.basePath += '/compiler'; // default to this for historical reasons
        }
    }

    public checkTestCodeOutput(fileName: string) {
        describe('compiler tests for ' + fileName, () => {
            // strips the fileName from the path.
            var justName = fileName.replace(/^.*[\\\/]/, '');
            var content = Harness.IO.readFile(fileName);
            var testCaseContent = Harness.TestCaseParser.makeUnitsFromTest(content, fileName);

            var units = testCaseContent.testUnitData;
            var tcSettings = testCaseContent.settings;
            var createNewInstance = false;

            var lastUnit = units[units.length - 1];
            var rootDir = lastUnit.originalFilePath.indexOf('conformance') === -1 ? 'tests/cases/compiler/' : lastUnit.originalFilePath.substring(0, lastUnit.originalFilePath.lastIndexOf('/')) + '/';

            var result: Harness.Compiler.CompilerResult;
            var options: ts.CompilerOptions;
            // equivalent to the files that will be passed on the command line
            var toBeCompiled: { unitName: string; content: string }[];
            // equivalent to other files on the file system not directly passed to the compiler (ie things that are referenced by other files)
            var otherFiles: { unitName: string; content: string }[];
            var harnessCompiler: Harness.Compiler.HarnessCompiler;

            var declToBeCompiled: { unitName: string; content: string }[] = [];
            var declOtherFiles: { unitName: string; content: string }[] = [];
            var declResult: Harness.Compiler.CompilerResult;

            var createNewInstance = false;

            before(() => {
                harnessCompiler = Harness.Compiler.getCompiler();
                // We need to assemble the list of input files for the compiler and other related files on the 'filesystem' (ie in a multi-file test)
                // If the last file in a test uses require or a triple slash reference we'll assume all other files will be brought in via references,
                // otherwise, assume all files are just meant to be in the same compilation session without explicit references to one another.
                toBeCompiled = [];
                otherFiles = [];
                if (/require\(/.test(lastUnit.content) || /reference\spath/.test(lastUnit.content)) {
                    toBeCompiled.push({ unitName: rootDir + lastUnit.name, content: lastUnit.content });
                    units.forEach(unit => {
                        if (unit.name !== lastUnit.name) {
                            otherFiles.push({ unitName: rootDir + unit.name, content: unit.content });
                        }
                    });
                } else {
                    toBeCompiled = units.map(unit => {
                        return { unitName: rootDir + unit.name, content: unit.content };
                    });
                }

                options = harnessCompiler.compileFiles(toBeCompiled, otherFiles, function (compileResult) {
                    result = compileResult;
                }, function (settings) {
                        harnessCompiler.setCompilerSettings(tcSettings);
                    });
            });

            beforeEach(() => {
                /* The compiler doesn't handle certain flags flipping during a single compilation setting. Tests on these flags will need 
                   a fresh compiler instance for themselves and then create a fresh one for the next test. Would be nice to get dev fixes
                   eventually to remove this limitation. */
                for (var i = 0; i < tcSettings.length; ++i) {
                    if (!createNewInstance && (tcSettings[i].flag == "noimplicitany" || tcSettings[i].flag === 'target')) {
                        harnessCompiler = Harness.Compiler.getCompiler({
                            useExistingInstance: false,
                            optionsForFreshInstance: { useMinimalDefaultLib: true, noImplicitAny: tcSettings[i].flag === "noimplicitany" }
                        });
                        harnessCompiler.setCompilerSettings(tcSettings);
                        createNewInstance = true;
                    }
                }
            });

            afterEach(() => {
                if (createNewInstance) {
                    harnessCompiler = Harness.Compiler.getCompiler({
                        useExistingInstance: false,
                        optionsForFreshInstance: { useMinimalDefaultLib: true, noImplicitAny: false }
                    });
                    createNewInstance = false;
                }
            });

            function getErrorBaseline(toBeCompiled: { unitName: string; content: string }[],
                otherFiles: { unitName: string; content: string }[],
                result: Harness.Compiler.CompilerResult
                ) {

                var outputLines: string[] = [];
                // Count up all the errors we find so we don't miss any
                var totalErrorsReported = 0;
                
                function outputErrorText(error: Harness.Compiler.MinimalDiagnostic) {
                    var errLines = RunnerBase.removeFullPaths(error.message)
                        .split('\n')
                        .map(s => s.length > 0 && s.charAt(s.length - 1) === '\r' ? s.substr(0, s.length - 1) : s)
                        .filter(s => s.length > 0)
                        .map(s => '!!! ' + s);
                    errLines.forEach(e => outputLines.push(e));

                    totalErrorsReported++;
                }

                // Report glovbal errors:
                var globalErrors = result.errors.filter(err => !err.filename);
                globalErrors.forEach(err => outputErrorText(err));

                // 'merge' the lines of each input file with any errors associated with it
                toBeCompiled.concat(otherFiles).forEach(inputFile => {
                    // Filter down to the errors in the file
                    var fileErrors = result.errors.filter(e => {
                        var errFn = e.filename;
                        return errFn && errFn === inputFile.unitName;
                    });


                    // Header
                    outputLines.push('==== ' + inputFile.unitName + ' (' + fileErrors.length + ' errors) ====');

                    // Make sure we emit something for every error
                    var markedErrorCount = 0;
                    // For each line, emit the line followed by any error squiggles matching this line
                    // Note: IE JS engine incorrectly handles consecutive delimiters here when using RegExp split, so
                    // we have to string-based splitting instead and try to figure out the delimiting chars

                    var lineStarts = ts.getLineStarts(inputFile.content);
                    var lines = inputFile.content.split('\n');
                    lines.forEach((line, lineIndex) => {
                        if (line.length > 0 && line.charAt(line.length - 1) === '\r') {
                            line = line.substr(0, line.length - 1);
                        }

                        var thisLineStart = lineStarts[lineIndex];
                        var nextLineStart: number;
                        // On the last line of the file, fake the next line start number so that we handle errors on the last character of the file correctly
                        if (lineIndex === lines.length - 1) {
                            nextLineStart = inputFile.content.length;
                        } else {
                            nextLineStart = lineStarts[lineIndex + 1];
                        }
                        // Emit this line from the original file
                        outputLines.push('    ' + line);
                        fileErrors.forEach(err => {
                            // Does any error start or continue on to this line? Emit squiggles
                            if ((err.end >= thisLineStart) && ((err.start < nextLineStart) || (lineIndex === lines.length - 1))) {
                                // How many characters from the start of this line the error starts at (could be positive or negative)
                                var relativeOffset = err.start - thisLineStart;
                                // How many characters of the error are on this line (might be longer than this line in reality)
                                var length = (err.end - err.start) - Math.max(0, thisLineStart - err.start);
                                // Calculate the start of the squiggle
                                var squiggleStart = Math.max(0, relativeOffset);
                                // TODO/REVIEW: this doesn't work quite right in the browser if a multi file test has files whose names are just the right length relative to one another
                                outputLines.push('    ' + line.substr(0, squiggleStart).replace(/[^\s]/g, ' ') + new Array(Math.min(length, line.length - squiggleStart) + 1).join('~'));

                                // If the error ended here, or we're at the end of the file, emit its message
                                if ((lineIndex === lines.length - 1) || nextLineStart > err.end) {
                                    // Just like above, we need to do a split on a string instead of on a regex
                                    // because the JS engine does regexes wrong

                                    outputErrorText(err);
                                    markedErrorCount++;
                                }
                            }
                        });
                    });

                    // Verify we didn't miss any errors in this file
                    assert.equal(markedErrorCount, fileErrors.length, 'count of errors in ' + inputFile.unitName);
                });

                // Verify we didn't miss any errors in total
                assert.equal(totalErrorsReported, result.errors.length, 'total number of errors');

                return outputLines.join('\r\n');
            }

            // check errors
            it('Correct errors for ' + fileName, () => {
                if (this.errors) {
                    Harness.Baseline.runBaseline('Correct errors for ' + fileName, justName.replace(/\.ts$/, '.errors.txt'), (): string => {
                        if (result.errors.length === 0) return null;

                        return getErrorBaseline(toBeCompiled, otherFiles, result);
                    });
                }
            });

            // Source maps?
            it('Correct sourcemap content for ' + fileName, () => {
                if (result.sourceMapRecord) {
                    Harness.Baseline.runBaseline('Correct sourcemap content for ' + fileName, justName.replace(/\.ts$/, '.sourcemap.txt'), () => {
                        return result.sourceMapRecord;
                    });
                }
            });

            // Compile .d.ts files
            it('Correct compiler generated.d.ts for ' + fileName, () => {
                if (options.declaration && result.errors.length === 0 && result.declFilesCode.length !== result.files.length) {
                    throw new Error('There were no errors and declFiles generated did not match number of js files generated');
                }

                // if the .d.ts is non-empty, confirm it compiles correctly as well
                if (options.declaration && result.errors.length === 0 && result.declFilesCode.length > 0) {
                    function getDtsFile(file: { unitName: string; content: string }) {
                        if (Harness.Compiler.isDTS(file.unitName)) {
                            return file;
                        } else {
                            var declFile = ts.forEach(result.declFilesCode,
                                declFile => declFile.fileName === (file.unitName.substr(0, file.unitName.length - ".ts".length) + ".d.ts")
                                    ? declFile : undefined);
                            return { unitName: rootDir + Harness.Path.getFileName(declFile.fileName), content: declFile.code };
                        }
                    }

                    ts.forEach(toBeCompiled, file => { declToBeCompiled.push(getDtsFile(file)); });
                    ts.forEach(otherFiles, file => { declOtherFiles.push(getDtsFile(file)); });
                    harnessCompiler.compileFiles(declToBeCompiled, declOtherFiles, function (compileResult) {
                        declResult = compileResult;
                    }, function (settings) {
                            harnessCompiler.setCompilerSettings(tcSettings);
                    });
                }
            });


            it('Correct JS output for ' + fileName, () => {
                if (!ts.fileExtensionIs(lastUnit.name, '.d.ts') && this.emit) {
                    if (result.files.length === 0 && result.errors.length === 0) {
                        throw new Error('Expected at least one js file to be emitted or at least one error to be created.');
                    }

{'fraction_non_alphanumeric': 0.09767864634316879, 'fraction_numerical': 0.0027268913438679905, 'mean_word_length': 1.4515769626328419, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 32, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5370055', 'n_tokens_mistral': 6876, 'n_tokens_neox': 6431, 'n_words': 1949}