mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
170 lines
5.7 KiB
JavaScript
170 lines
5.7 KiB
JavaScript
|
/* global location, describe, it, afterEach, after */
|
||
|
|
||
|
var _ = require('underscore');
|
||
|
var Chance = require('chance');
|
||
|
var assert = require('chai').assert;
|
||
|
|
||
|
function mod(r) { return function(x) { return x%r; }; }
|
||
|
exports.mod = mod;
|
||
|
|
||
|
/**
|
||
|
* Runs the given function for the specified number of iterations and returns the total time taken.
|
||
|
* This function has no side effects.
|
||
|
* @param {Function} func - function to apply
|
||
|
* @param {object} context - this
|
||
|
* @param {Array} args - array of arguments to apply on the function
|
||
|
* @param {Integer} options.iters - number of iterations to apply the given function
|
||
|
* @param {Boolean} options.avg - if true, return the avg iteration time, else return the total time
|
||
|
*/
|
||
|
function time(func, context, args, options) {
|
||
|
console.assert(options.iters > 0, "Number of iterations must be greater than 0");
|
||
|
var start, copy;
|
||
|
var elapsed = 0;
|
||
|
// Apply the function on a copy of the context on each iteration to avoid side effects
|
||
|
for (var i = 0; i < options.iters; i++) {
|
||
|
copy = _.clone(context);
|
||
|
start = Date.now();
|
||
|
func.apply(copy, args);
|
||
|
elapsed += Date.now() - start;
|
||
|
}
|
||
|
|
||
|
if (options.avg) return elapsed/options.iters;
|
||
|
else return elapsed;
|
||
|
}
|
||
|
exports.time = time;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Repeats running the given function on the given arguments count times, returning the last
|
||
|
* result.
|
||
|
*/
|
||
|
function repeat(count, func, varArgs) {
|
||
|
var ret, args = Array.prototype.slice.call(arguments, 2);
|
||
|
for (var i = 0; i < count; i++) {
|
||
|
ret = func.apply(null, args);
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
exports.repeat = repeat;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Defines a test suite for running timing tests. See documentation for exports.timing.
|
||
|
*/
|
||
|
function timingDescribe(desc, func) {
|
||
|
// If under Node, non-empty ENABLE_TIMING_TESTS environment variable turns on the timing tests.
|
||
|
// If under the Browser, we look for 'timing=1' among URL params, set by test/browser.js.
|
||
|
var enableTimingTests = (process.browser ?
|
||
|
(location.search.substr(1).split("&").indexOf("timing=1") !== -1) :
|
||
|
process.env.ENABLE_TIMING_TESTS);
|
||
|
|
||
|
function body() {
|
||
|
func();
|
||
|
|
||
|
// We collect the tests, then check if any of them exceeded the expected timing. We do it in
|
||
|
// one pass in after() (rather than in afterEach()) to allow them all to run, since it's
|
||
|
// useful to see all their timings.
|
||
|
var testsToCheck = [];
|
||
|
afterEach(function() {
|
||
|
testsToCheck.push(this.currentTest);
|
||
|
});
|
||
|
after(function() {
|
||
|
testsToCheck.forEach(function(test) {
|
||
|
if (test.expectedDuration) {
|
||
|
assert.isBelow(test.duration, test.expectedDuration * 1.5, "Test took longer than expected");
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (enableTimingTests) {
|
||
|
return describe(desc, body);
|
||
|
} else {
|
||
|
return describe.skip(desc + " (skipping timing test)", body);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Defines a test case for a timing test. This should be used in place of it() for timing test
|
||
|
* cases created inside utils.timing.describe(). See documentation for exports.timing.
|
||
|
*/
|
||
|
function timingTest(expectedMs, desc, testFunc) {
|
||
|
var test = it(desc + " (exp ~" + expectedMs + "ms)", testFunc);
|
||
|
test.slow(expectedMs * 1.5);
|
||
|
test.timeout(expectedMs * 5 + 2000);
|
||
|
test.expectedDuration = expectedMs;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* To write timing tests, the following pattern is recommended:
|
||
|
*
|
||
|
* (1) Use utils.timing.describe() in place of describe().
|
||
|
* (2) Use utils.timing.it() in place of it(). It takes an extra first parameter with the number
|
||
|
* of expected milliseconds. The test will fail if it takes more than 1.5x longer.
|
||
|
* (3) Place only the code to be timed in utils.timing.it(), and do all setup in before() and all
|
||
|
* non-trivial post-test assertions in after().
|
||
|
*
|
||
|
* These tests only run when ENABLE_TIMING_TESTS environment variable is non-empty. It enables
|
||
|
* timing tests both under Node and running in the browser under Selenium. To enable timing tests
|
||
|
* in the browser when running /test.html manually, go to /test.html?timing=1.
|
||
|
*/
|
||
|
exports.timing = {
|
||
|
describe: timingDescribe,
|
||
|
it: timingTest
|
||
|
};
|
||
|
|
||
|
|
||
|
// Dummy object used for tests
|
||
|
function TestPerson(last, first, age, year, month, day) {
|
||
|
this.last = last;
|
||
|
this.first = first;
|
||
|
this.age = age;
|
||
|
this.year = year;
|
||
|
this.month = month;
|
||
|
this.day = day;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a list of randomly generated TestPersons.
|
||
|
* @param {integer} num - length of people list to return
|
||
|
*/
|
||
|
function genPeople(num, seed) {
|
||
|
if (typeof seed === 'undefined') seed = 0;
|
||
|
var ageOpts = {min: 0, max: 90};
|
||
|
var monthOpts = {min:1, max:12};
|
||
|
var dayOpts = {min:1, max:30};
|
||
|
var people = [];
|
||
|
var chance = new Chance(seed);
|
||
|
for (var i = 0; i < num; i++) {
|
||
|
people.push(new TestPerson(chance.last(),
|
||
|
chance.first(),
|
||
|
chance.integer(ageOpts),
|
||
|
parseInt(chance.year()),
|
||
|
chance.integer(monthOpts),
|
||
|
chance.integer(dayOpts)
|
||
|
));
|
||
|
}
|
||
|
return people;
|
||
|
}
|
||
|
exports.genPeople = genPeople;
|
||
|
|
||
|
/**
|
||
|
* Generates a list of items denoted by the given chanceFunc string.
|
||
|
* Ex : genItems('integers', 10, {min:0, max:20}) generates a list of 10 integers between 0 and 20
|
||
|
* : genItems('string', 10, {length: 6}) generates a list of 10 strings of length 6
|
||
|
* @param {string} chanceFunc - string name of a chance.js function
|
||
|
* @param {integer} num - length of item list to return
|
||
|
* @param {object} options - object denoting options for the given chance.js function
|
||
|
*/
|
||
|
function genItems(chanceFunc, num, options, seed) {
|
||
|
if (typeof seed === 'undefined') seed = 0;
|
||
|
console.assert(typeof new Chance()[chanceFunc] === 'function');
|
||
|
var chance = new Chance(seed);
|
||
|
var items = [];
|
||
|
for (var i = 0; i < num; i++) {
|
||
|
items.push(chance[chanceFunc](options));
|
||
|
}
|
||
|
return items;
|
||
|
}
|
||
|
exports.genItems = genItems;
|