/** * Created by Tivie on 21/12/2016. */ 'use strict'; var now = require('performance-now'), fs = require('fs'), semverSort = require('semver-sort'), performance = { version: '', libraryName: '', MDFile: 'performance.log.md', logFile: 'performance.json', testSuites: [], silent: false, githubLink: '' }; performance.setVersion = function (version) { performance.version = version; }; performance.setLibraryName = function (name) { performance.libraryName = name; }; performance.setGithubLink = function (url) { performance.githubLink = url; }; performance.generateLog = function (filename, MDFilename, asTable) { filename = filename || performance.logFile; MDFilename = MDFilename || performance.MDFile; asTable = !!asTable; fs.closeSync(fs.openSync(filename, 'a')); var json = fs.readFileSync(filename), jsonParsed; try { jsonParsed = JSON.parse(json); } catch (err) { jsonParsed = {}; } var jData = []; for (var i = 0; i < performance.testSuites.length; ++i) { // Suite var suiteName = performance.testSuites[i].getSuiteName(), cycles = performance.testSuites[i].getOption('cycles'), subJData = { suiteName: suiteName, cycles: cycles, tests: [] }, testSuite = performance.testSuites[i].getTests(); //make sure tests have ran first if (!performance.testSuites[i].hasRun()) { performance.testSuites[i].run(); } // loop through tests for (var ii = 0; ii < testSuite.length; ++ii) { // Test var test = testSuite[ii]; subJData.tests.push({ name: test.name, time: test.time, maxTime: test.maxTime, minTime: test.minTime }); } jData.push(subJData); } jsonParsed[performance.version] = jData; //Sort jsonParsed var versions = []; for (var version in jsonParsed) { if (jsonParsed.hasOwnProperty(version)) { versions.push(version); } } semverSort.desc(versions); var finalJsonObj = {}; for (i = 0; i < versions.length; ++i) { if (jsonParsed.hasOwnProperty(versions[i])) { finalJsonObj[versions[i]] = jsonParsed[versions[i]]; } } fs.writeFileSync(filename, JSON.stringify(finalJsonObj)); generateMD(MDFilename, finalJsonObj, asTable); }; function generateMD (filename, obj, asTable) { fs.closeSync(fs.openSync(filename, 'w')); asTable = !!asTable; // generate MD var otp = '# Performance Tests for ' + performance.libraryName + '\n\n\n'; for (var version in obj) { if (obj.hasOwnProperty(version)) { otp += '## [version ' + version + '](' + performance.githubLink + version + ')\n\n'; var testSuite = obj[version]; for (var i = 0; i < testSuite.length; ++i) { otp += '### Test Suite: ' + testSuite[i].suiteName + ' (' + testSuite[i].cycles + ' cycles)\n'; var tests = testSuite[i].tests; if (asTable) { otp += '| test | avgTime | max | min |\n'; otp += '|:-----|--------:|----:|----:|\n'; } for (var ii = 0; ii < tests.length; ++ii) { var time = parseFloat(tests[ii].time).toFixed(3), maxTime = parseFloat(tests[ii].maxTime).toFixed(3), minTime = parseFloat(tests[ii].minTime).toFixed(3); if (asTable) { otp += '|' + tests[ii].name + '|' + time + '|' + maxTime + '|' + minTime + '|\n'; } else { otp += ' - **' + tests[ii].name + ':** took ' + time + 'ms (*max: ' + maxTime + 'ms; min: ' + minTime + 'ms*)\n'; } } otp += '\n'; } otp += '\n'; } } fs.writeFileSync(filename, otp); } performance.Suite = function (name) { var suiteName = name || '', tests = [], hasRunFlag = false, options = { cycles: 20 }; this.setOption = function (key, val) { options[key] = val; return this; }; this.getOption = function (key) { return options[key]; }; this.add = function (name, obj) { if (typeof obj === 'function') { obj = { prepare: function () {}, test: obj, teardown: function () {} }; } if (!obj.hasOwnProperty('test')) { throw 'obj must have a property called test'; } if (typeof obj.test !== 'function') { throw 'obj test property must be a function'; } if (!obj.hasOwnProperty('prepare')) { obj.prepare = function () {}; } if (!obj.hasOwnProperty('teardown')) { obj.teardown = function () {}; } if (typeof obj.prepare !== 'function') { throw 'obj prepare property must be a function'; } if (typeof obj.teardown !== 'function') { throw 'obj teardown property must be a function'; } tests.push({ name: name, obj: obj, time: 0, maxTime: 0, minTime: 0 }); return this; }; this.run = function run () { var nn = options.cycles; console.log('running tests: ' + nn + ' cycles each.'); for (var i = 0; i < tests.length; ++i) { var times = [], passVar = tests[i].obj.prepare(); for (var ii = 0; ii < nn; ++ii) { var before = now(); tests[i].obj.test(passVar); var after = now(); times.push(after - before); } var total = times.reduce(function (a, b) {return a + b;}, 0); tests[i].time = total / options.cycles; tests[i].minTime = Math.min.apply(null, times); tests[i].maxTime = Math.max.apply(null, times); if (!options.silent) { console.log(tests[i].name + ' took an average of ' + tests[i].time + 'ms (min: ' + tests[i].minTime + 'ms; max: ' + tests[i].maxTime + 'ms'); } } hasRunFlag = true; return this; }; this.hasRun = function () { return hasRunFlag; }; this.getSuiteName = function () { return suiteName; }; this.getTests = function () { return tests; }; performance.testSuites.push(this); }; module.exports = performance;