performance.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /**
  2. * Created by Tivie on 21/12/2016.
  3. */
  4. 'use strict';
  5. var now = require('performance-now'),
  6. fs = require('fs'),
  7. semverSort = require('semver-sort'),
  8. performance = {
  9. version: '',
  10. libraryName: '',
  11. MDFile: 'performance.log.md',
  12. logFile: 'performance.json',
  13. testSuites: [],
  14. silent: false,
  15. githubLink: ''
  16. };
  17. performance.setVersion = function (version) {
  18. performance.version = version;
  19. };
  20. performance.setLibraryName = function (name) {
  21. performance.libraryName = name;
  22. };
  23. performance.setGithubLink = function (url) {
  24. performance.githubLink = url;
  25. };
  26. performance.generateLog = function (filename, MDFilename, asTable) {
  27. filename = filename || performance.logFile;
  28. MDFilename = MDFilename || performance.MDFile;
  29. asTable = !!asTable;
  30. fs.closeSync(fs.openSync(filename, 'a'));
  31. var json = fs.readFileSync(filename),
  32. jsonParsed;
  33. try {
  34. jsonParsed = JSON.parse(json);
  35. } catch (err) {
  36. jsonParsed = {};
  37. }
  38. var jData = [];
  39. for (var i = 0; i < performance.testSuites.length; ++i) {
  40. // Suite
  41. var suiteName = performance.testSuites[i].getSuiteName(),
  42. cycles = performance.testSuites[i].getOption('cycles'),
  43. subJData = {
  44. suiteName: suiteName,
  45. cycles: cycles,
  46. tests: []
  47. },
  48. testSuite = performance.testSuites[i].getTests();
  49. //make sure tests have ran first
  50. if (!performance.testSuites[i].hasRun()) {
  51. performance.testSuites[i].run();
  52. }
  53. // loop through tests
  54. for (var ii = 0; ii < testSuite.length; ++ii) {
  55. // Test
  56. var test = testSuite[ii];
  57. subJData.tests.push({
  58. name: test.name,
  59. time: test.time,
  60. maxTime: test.maxTime,
  61. minTime: test.minTime
  62. });
  63. }
  64. jData.push(subJData);
  65. }
  66. jsonParsed[performance.version] = jData;
  67. //Sort jsonParsed
  68. var versions = [];
  69. for (var version in jsonParsed) {
  70. if (jsonParsed.hasOwnProperty(version)) {
  71. versions.push(version);
  72. }
  73. }
  74. semverSort.desc(versions);
  75. var finalJsonObj = {};
  76. for (i = 0; i < versions.length; ++i) {
  77. if (jsonParsed.hasOwnProperty(versions[i])) {
  78. finalJsonObj[versions[i]] = jsonParsed[versions[i]];
  79. }
  80. }
  81. fs.writeFileSync(filename, JSON.stringify(finalJsonObj));
  82. generateMD(MDFilename, finalJsonObj, asTable);
  83. };
  84. function generateMD (filename, obj, asTable) {
  85. fs.closeSync(fs.openSync(filename, 'w'));
  86. asTable = !!asTable;
  87. // generate MD
  88. var otp = '# Performance Tests for ' + performance.libraryName + '\n\n\n';
  89. for (var version in obj) {
  90. if (obj.hasOwnProperty(version)) {
  91. otp += '## [version ' + version + '](' + performance.githubLink + version + ')\n\n';
  92. var testSuite = obj[version];
  93. for (var i = 0; i < testSuite.length; ++i) {
  94. otp += '### Test Suite: ' + testSuite[i].suiteName + ' (' + testSuite[i].cycles + ' cycles)\n';
  95. var tests = testSuite[i].tests;
  96. if (asTable) {
  97. otp += '| test | avgTime | max | min |\n';
  98. otp += '|:-----|--------:|----:|----:|\n';
  99. }
  100. for (var ii = 0; ii < tests.length; ++ii) {
  101. var time = parseFloat(tests[ii].time).toFixed(3),
  102. maxTime = parseFloat(tests[ii].maxTime).toFixed(3),
  103. minTime = parseFloat(tests[ii].minTime).toFixed(3);
  104. if (asTable) {
  105. otp += '|' + tests[ii].name + '|' + time + '|' + maxTime + '|' + minTime + '|\n';
  106. } else {
  107. otp += ' - **' + tests[ii].name + ':** took ' + time + 'ms (*max: ' + maxTime + 'ms; min: ' + minTime + 'ms*)\n';
  108. }
  109. }
  110. otp += '\n';
  111. }
  112. otp += '\n';
  113. }
  114. }
  115. fs.writeFileSync(filename, otp);
  116. }
  117. performance.Suite = function (name) {
  118. var suiteName = name || '',
  119. tests = [],
  120. hasRunFlag = false,
  121. options = {
  122. cycles: 20
  123. };
  124. this.setOption = function (key, val) {
  125. options[key] = val;
  126. return this;
  127. };
  128. this.getOption = function (key) {
  129. return options[key];
  130. };
  131. this.add = function (name, obj) {
  132. if (typeof obj === 'function') {
  133. obj = {
  134. prepare: function () {},
  135. test: obj,
  136. teardown: function () {}
  137. };
  138. }
  139. if (!obj.hasOwnProperty('test')) {
  140. throw 'obj must have a property called test';
  141. }
  142. if (typeof obj.test !== 'function') {
  143. throw 'obj test property must be a function';
  144. }
  145. if (!obj.hasOwnProperty('prepare')) {
  146. obj.prepare = function () {};
  147. }
  148. if (!obj.hasOwnProperty('teardown')) {
  149. obj.teardown = function () {};
  150. }
  151. if (typeof obj.prepare !== 'function') {
  152. throw 'obj prepare property must be a function';
  153. }
  154. if (typeof obj.teardown !== 'function') {
  155. throw 'obj teardown property must be a function';
  156. }
  157. tests.push({
  158. name: name,
  159. obj: obj,
  160. time: 0,
  161. maxTime: 0,
  162. minTime: 0
  163. });
  164. return this;
  165. };
  166. this.run = function run () {
  167. var nn = options.cycles;
  168. console.log('running tests: ' + nn + ' cycles each.');
  169. for (var i = 0; i < tests.length; ++i) {
  170. var times = [],
  171. passVar = tests[i].obj.prepare();
  172. for (var ii = 0; ii < nn; ++ii) {
  173. var before = now();
  174. tests[i].obj.test(passVar);
  175. var after = now();
  176. times.push(after - before);
  177. }
  178. var total = times.reduce(function (a, b) {return a + b;}, 0);
  179. tests[i].time = total / options.cycles;
  180. tests[i].minTime = Math.min.apply(null, times);
  181. tests[i].maxTime = Math.max.apply(null, times);
  182. if (!options.silent) {
  183. console.log(tests[i].name + ' took an average of ' + tests[i].time + 'ms (min: ' + tests[i].minTime + 'ms; max: ' + tests[i].maxTime + 'ms');
  184. }
  185. }
  186. hasRunFlag = true;
  187. return this;
  188. };
  189. this.hasRun = function () {
  190. return hasRunFlag;
  191. };
  192. this.getSuiteName = function () {
  193. return suiteName;
  194. };
  195. this.getTests = function () {
  196. return tests;
  197. };
  198. performance.testSuites.push(this);
  199. };
  200. module.exports = performance;