performance.js 5.9 KB

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