"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
var fs_1 = require("fs");
var Reporter_1 = require("./Reporter");
var Benchmark = (function (_super) {
    __extends(Benchmark, _super);
    function Benchmark(executor, config) {
        if (config === void 0) { config = {}; }
        var _this = _super.call(this, executor, config) || this;
        _this.mode = config.mode;
        _this.thresholds = config.thresholds;
        _this.verbosity = config.verbosity;
        if (_this.mode === 'test') {
            try {
                _this.baseline = JSON.parse(fs_1.readFileSync(_this.filename, { encoding: 'utf8' }));
            }
            catch (error) {
                _this.console.warn('Unable to load benchmark baseline data from ' + _this.filename);
                _this.console.warn('Switching to "baseline" mode');
                _this.mode = 'baseline';
            }
        }
        if (!_this.baseline) {
            _this.baseline = { environments: {} };
        }
        _this.sessions = {};
        return _this;
    }
    Benchmark.prototype._getSession = function (testOrSuite) {
        var sessionId = testOrSuite.sessionId || 'local';
        var session = this.sessions[sessionId];
        if (!session) {
            var environment = void 0;
            session = this.sessions[sessionId] = {
                suites: {}
            };
            if (testOrSuite.sessionId) {
                environment = session.environment = {
                    client: testOrSuite.remote.environmentType.browserName,
                    version: testOrSuite.remote.environmentType.version,
                    platform: testOrSuite.remote.environmentType.platform
                };
            }
            else {
                environment = session.environment = {
                    client: process.title,
                    version: process.version,
                    platform: process.platform
                };
            }
            session.environment.id = environment.client + ':' + environment.version + ':' + environment.platform;
        }
        return session;
    };
    Benchmark.prototype.runEnd = function () {
        if (this.mode === 'baseline') {
            var existingBaseline_1;
            try {
                existingBaseline_1 = JSON.parse(fs_1.readFileSync(this.filename, { encoding: 'utf8' }));
            }
            catch (error) {
                existingBaseline_1 = { environments: {} };
            }
            var baseline_1 = this.baseline;
            Object.keys(baseline_1.environments).forEach(function (environmentId) {
                existingBaseline_1.environments[environmentId] = baseline_1.environments[environmentId];
            });
            fs_1.writeFileSync(this.filename, JSON.stringify(existingBaseline_1, null, '    '));
        }
    };
    Benchmark.prototype.suiteEnd = function (suite) {
        var session = this._getSession(suite);
        if (!suite.hasParent) {
            var environment = session.environment;
            this.console.log('Finished ' + environment.client + ' ' + environment.version + ' on ' + environment.platform);
        }
        else if (this.mode === 'test') {
            var suiteInfo = session.suites[suite.id];
            var numTests = suiteInfo.numBenchmarks;
            if (numTests > 0) {
                var numFailedTests = suiteInfo.numFailedBenchmarks;
                var message = numFailedTests + '/' + numTests + ' benchmarks failed in ' + suite.id;
                if (numFailedTests > 0) {
                    this.console.warn(message);
                }
                else {
                    this.console.log(message);
                }
            }
        }
    };
    Benchmark.prototype.suiteStart = function (suite) {
        var session = this._getSession(suite);
        if (!suite.hasParent) {
            var environment = session.environment;
            var environmentName = environment.client + ' ' + environment.version + ' on ' + environment.platform;
            this.console.log((this.mode === 'baseline' ? 'Baselining' : 'Benchmark testing') + ' ' + environmentName);
            if (this.mode === 'baseline') {
                this.baseline.environments[environment.id] = {
                    client: environment.client,
                    version: environment.version,
                    platform: environment.platform,
                    tests: {}
                };
            }
            else if (!this.baseline.environments[environment.id]) {
                this.console.warn('No baseline data for ' + environmentName + '!');
            }
        }
        else {
            session.suites[suite.id] = {
                numBenchmarks: 0,
                numFailedBenchmarks: 0
            };
        }
    };
    Benchmark.prototype.testPass = function (test, benchmark) {
        var _this = this;
        var checkTest = function (baseline) {
            var warn = [];
            var fail = [];
            var list;
            var baselineMean = baseline.stats.mean;
            var percentDifference = 100 * (benchmark.stats.mean - baselineMean) / baselineMean;
            if (Math.abs(percentDifference) > _this.thresholds.warn.mean) {
                list = warn;
                if (Math.abs(percentDifference) > _this.thresholds.fail.mean) {
                    list = fail;
                }
                list.push('Execution time is ' + percentDifference.toFixed(1) + '% off');
            }
            var baselineRme = baseline.stats.rme;
            percentDifference = benchmark.stats.rme - baselineRme;
            if (Math.abs(percentDifference) > _this.thresholds.warn.rme) {
                list = warn;
                if (Math.abs(percentDifference) > _this.thresholds.fail.rme) {
                    list = fail;
                }
                list.push('RME is ' + percentDifference.toFixed(1) + '% off');
            }
            if (fail.length) {
                _this.console.error('FAIL ' + test.id + ' (' + fail.join(', ') + ')');
                return false;
            }
            else if (warn.length) {
                _this.console.warn('WARN ' + test.id + ' (' + warn.join(', ') + ')');
            }
            else {
                _this.console.log('PASS ' + test.id);
            }
            return true;
        };
        if (!benchmark) {
            this.executor.log('Ignoring non-benchmark test', test.id);
            return;
        }
        var session = this._getSession(test);
        var environment = session.environment;
        var suiteInfo = session.suites[test.parentId];
        suiteInfo.numBenchmarks++;
        if (this.mode === 'baseline') {
            this.baseline.environments[environment.id].tests[test.id] = {
                hz: benchmark.hz,
                times: benchmark.times,
                stats: {
                    rme: benchmark.stats.rme,
                    moe: benchmark.stats.moe,
                    mean: benchmark.stats.mean
                }
            };
            this.console.log('Baselined ' + test.name);
            this.executor.log('Time per run:', formatSeconds(benchmark.stats.mean), '\xb1', benchmark.stats.rme.toFixed(2), '%');
        }
        else {
            var baseline = this.baseline.environments[environment.id];
            if (baseline) {
                var testData = baseline.tests[test.id];
                var result = checkTest(testData);
                this.executor.log('Expected time per run:', formatSeconds(baseline.stats.mean), '\xb1', baseline.stats.rme.toFixed(2), '%');
                this.executor.log('Actual time per run:', formatSeconds(benchmark.stats.mean), '\xb1', benchmark.stats.rme.toFixed(2), '%');
                if (!result) {
                    suiteInfo.numFailedBenchmarks++;
                }
            }
        }
    };
    Benchmark.prototype.testFail = function (test) {
        var session = this._getSession(test);
        var suiteInfo = session.suites[test.parentId];
        suiteInfo.numBenchmarks++;
        suiteInfo.numFailedBenchmarks++;
        this.console.error('ERROR: ' + test.id);
        this.console.error(this.executor.formatter.format(test.error));
    };
    return Benchmark;
}(Reporter_1.default));
exports.default = Benchmark;
function formatSeconds(value) {
    var units = 's';
    if (value < 1) {
        var places = Math.ceil(Math.log(value) / Math.log(10)) - 1;
        if (places < -9) {
            value *= Math.pow(10, 12);
            units = 'ps';
        }
        else if (places < -6) {
            value *= Math.pow(10, 9);
            units = 'ns';
        }
        else if (places < -3) {
            value *= Math.pow(10, 6);
            units = 'µs';
        }
        else if (places < 0) {
            value *= Math.pow(10, 3);
            units = 'ms';
        }
    }
    return value.toFixed(3) + units;
}
//# sourceMappingURL=Benchmark.js.map