performance.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  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) {
  27. filename = filename || performance.logFile;
  28. MDFilename = MDFilename || performance.MDFile;
  29. fs.closeSync(fs.openSync(filename, 'a'));
  30. var json = fs.readFileSync(filename),
  31. jsonParsed;
  32. try {
  33. jsonParsed = JSON.parse(json);
  34. }
  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);
  83. };
  84. function generateMD(filename, obj) {
  85. fs.closeSync(fs.openSync(filename, 'w'));
  86. // generate MD
  87. var otp = '# Performance Tests for ' + performance.libraryName + '\n\n\n';
  88. for (var version in obj) {
  89. if (obj.hasOwnProperty(version)) {
  90. otp += '## [version ' + version + '](' + performance.githubLink + ')\n\n';
  91. var testSuite = obj[version];
  92. for (var i = 0; i < testSuite.length; ++i) {
  93. otp += '### Test Suite: ' + testSuite[i].suiteName + ' (' + testSuite[i].cycles + ' cycles)\n';
  94. var tests = testSuite[i].tests;
  95. for (var ii = 0; ii < tests.length; ++ii) {
  96. var time = parseFloat(tests[ii].time).toFixed(3),
  97. maxTime = parseFloat(tests[ii].maxTime).toFixed(3),
  98. minTime = parseFloat(tests[ii].minTime).toFixed(3);
  99. otp += ' - **' + tests[ii].name + ':** took ' + time + 'ms (*max: ' + maxTime + 'ms; min: ' + minTime + 'ms*)\n';
  100. }
  101. otp += '\n';
  102. }
  103. otp += '\n';
  104. }
  105. }
  106. fs.writeFileSync(filename, otp);
  107. }
  108. performance.Suite = function (name) {
  109. var suiteName = name || '',
  110. tests = [],
  111. hasRunFlag = false,
  112. options = {
  113. cycles: 20
  114. };
  115. this.setOption = function (key, val) {
  116. options[key] = val;
  117. return this;
  118. };
  119. this.getOption = function (key) {
  120. return options[key];
  121. };
  122. this.add = function (name, obj) {
  123. if (typeof obj === 'function') {
  124. obj = {
  125. prepare: function () {},
  126. test: obj,
  127. teardown: function () {}
  128. };
  129. }
  130. if (!obj.hasOwnProperty('test')) {
  131. throw 'obj must have a property called test';
  132. }
  133. if (typeof obj.test !== 'function') {
  134. throw 'obj test property must be a function';
  135. }
  136. if (!obj.hasOwnProperty('prepare')) {
  137. obj.prepare = function () {};
  138. }
  139. if (!obj.hasOwnProperty('teardown')) {
  140. obj.teardown = function () {};
  141. }
  142. if (typeof obj.prepare !== 'function') {
  143. throw 'obj prepare property must be a function';
  144. }
  145. if (typeof obj.teardown !== 'function') {
  146. throw 'obj teardown property must be a function';
  147. }
  148. tests.push({
  149. name: name,
  150. obj: obj,
  151. time: 0,
  152. maxTime: 0,
  153. minTime: 0
  154. });
  155. return this;
  156. };
  157. this.run = function run () {
  158. var nn = options.cycles;
  159. console.log('running tests: ' + nn + ' cycles each.');
  160. for (var i = 0; i < tests.length; ++i) {
  161. var times = [],
  162. passVar = tests[i].obj.prepare();
  163. for (var ii = 0; ii < nn; ++ii) {
  164. var before = now();
  165. tests[i].obj.test(passVar);
  166. var after = now();
  167. times.push(after - before);
  168. }
  169. var total = times.reduce(function (a, b) {return a + b;}, 0);
  170. tests[i].time = total / options.cycles;
  171. tests[i].minTime = Math.min.apply(null, times);
  172. tests[i].maxTime = Math.max.apply(null, times);
  173. if (!options.silent) {
  174. console.log(tests[i].name + ' took an average of ' + tests[i].time + 'ms (min: ' + tests[i].minTime + 'ms; max: ' + tests[i].maxTime + 'ms');
  175. }
  176. }
  177. hasRunFlag = true;
  178. return this;
  179. };
  180. this.hasRun = function () {
  181. return hasRunFlag;
  182. };
  183. this.getSuiteName = function () {
  184. return suiteName;
  185. };
  186. this.getTests = function () {
  187. return tests;
  188. };
  189. performance.testSuites.push(this);
  190. };
  191. module.exports = performance;