Skip to content

Commit

Permalink
yank logic out of Mocha#randomize() and into utils; add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
boneskull committed Sep 19, 2016
1 parent a6f6e6e commit 5ed7851
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 38 deletions.
42 changes: 16 additions & 26 deletions lib/mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ var path = require('path');
var reporters = require('./reporters');
var utils = require('./utils');
var Random = require('random-js');
// Number.MAX_SAFE_INTEGER only available in ES2015
var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 0x1fffffffffffff;

/**
* Expose `Mocha`.
Expand Down Expand Up @@ -311,41 +309,33 @@ Mocha.prototype.ignoreLeaks = function(ignore) {
/**
* Enable or disable randomization of test execution order within suites.
*
* @param {(boolean|number|string)} [seed] Optional random seed. Seed must be
* a 32-bit unsigned integer, or a string which can convert to one. If
* `true`, a seed will be created. If `false`, randomization will be
* globally disabled (if it was enabled previously).
* @param {(boolean|number|string)} seed Random seed. `seed` must be
* a 32-bit unsigned integer, or a string which can convert to one.
* If `false`, randomization will be globally disabled (if it was enabled
* previously).
* @return {Mocha}
* @api public
*/
Mocha.prototype.randomize = function randomize(seed) {
if (!arguments.length) {
throw new Error('Generate a random seed using --generate-seed on the ' +
'command line, or Mocha.seed() in the browser.');
}
if (seed !== false) {
if (!arguments.length || seed === true) {
// generate a number for seeding
seed = Mocha.seed();
} else if (!(typeof seed === 'number' && seed > 0
&& seed <= MAX_SAFE_INTEGER && seed % 1 === 0)) {
// this would be a hexadecimal number represented as a string
if (/^(0[xX])?[a-fA-F0-9]{1,14}$'/.test(seed)) {
seed = parseInt(seed, 16);
} else {
// a decimal number as string, presumably
seed = parseInt(seed, 10);
if (isNaN(seed)) {
throw new Error('Invalid random seed. Expected unsigned 32-bit '
+ 'integer; got: "' + seed + '"');
}
}
var validSeed = utils.toSafeUnsignedInteger(seed);
if (isNaN(validSeed)) {
throw new Error('Invalid random seed. Expected unsigned 32-bit '
+ 'integer or value which can convert.');
}

var engine = Random.engines.mt19937().seed(seed);
var engine = Random.engines.mt19937().seed(validSeed);
this.options.randomConfig = {
shuffleTests: function shuffleTests(tests) {
Random.shuffle(tests, engine);
return tests;
},
seed: seed,
hex: '0x' + Number(seed).toString(16) // for display
seed: validSeed,
hex: '0x' + Number(validSeed).toString(16) // for display
};
} else {
delete this.options.randomConfig;
Expand All @@ -358,7 +348,7 @@ Mocha.prototype.randomize = function randomize(seed) {
* @returns {number} Unsigned 32-bit integer
*/
Mocha.seed = function seed() {
return Random.integer(0, MAX_SAFE_INTEGER)(Random.engines.mt19937()
return Random.integer(0, utils.MAX_SAFE_INTEGER)(Random.engines.mt19937()
.autoSeed());
};

Expand Down
66 changes: 54 additions & 12 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,9 @@ exports.slug = function(str) {
exports.clean = function(str) {
str = str
.replace(/\r\n?|[\n\u2028\u2029]/g, '\n').replace(/^\uFEFF/, '')
// (traditional)-> space/name parameters body (lambda)-> parameters body multi-statement/single keep body content
// (traditional)-> space/name parameters body (lambda)->
// parameters body multi-statement/single keep body
// content
.replace(/^function(?:\s*|\s+[^(]*)\([^)]*\)\s*\{((?:.|\n)*?)\s*\}$|^\([^)]*\)\s*=>\s*(?:\{((?:.|\n)*?)\s*\}|((?:.|\n)*))$/, '$1$2$3');

var spaces = str.match(/^\n?( *)/)[1].length;
Expand Down Expand Up @@ -420,12 +422,15 @@ exports.type = function type(value) {
/**
* Stringify `value`. Different behavior depending on type of value:
*
* - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
* - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
* - If `value` is an *empty* object, function, or array, return result of function
* - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`,
* respectively.
* - If `value` is not an object, function or array, return result of
* `value.toString()` wrapped in double-quotes.
* - If `value` is an *empty* object, function, or array, return result of
* function
* {@link emptyRepresentation}.
* - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
* JSON.stringify().
* - If `value` has properties, call {@link exports.canonicalize} on it, then
* return result of JSON.stringify().
*
* @api private
* @see exports.type
Expand Down Expand Up @@ -555,9 +560,12 @@ exports.isBuffer = function(value) {
* - is `undefined`, return string `'[undefined]'`
* - is `null`, return value `null`
* - is some other primitive, return the value
* - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
* - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
* - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
* - is not a primitive or an `Array`, `Object`, or `Function`, return the
* value of the Thing's `toString()` method
* - is a non-empty `Array`, `Object`, or `Function`, return the result of
* calling this function again.
* - is an empty `Array`, `Object`, or `Function`, return the result of calling
* `emptyRepresentation()`
*
* @api private
* @see {@link exports.stringify}
Expand Down Expand Up @@ -711,10 +719,11 @@ exports.getError = function(err) {

/**
* @summary
* This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
* This Filter based on `mocha-clean` module.(see:
* `github.com/rstacruz/mocha-clean`)
* @description
* When invoking this function you get a filter function that get the Error.stack as an input,
* and return a prettify output.
* When invoking this function you get a filter function that get the
* Error.stack as an input, and return a prettify output.
* (i.e: strip Mocha and internal node functions from stack trace).
* @returns {Function}
*/
Expand Down Expand Up @@ -767,6 +776,29 @@ exports.stackTraceFilter = function() {
};
};

//
/**
* Number.MAX_SAFE_INTEGER only available in ES2015.
* @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
* @type {number}
*/
exports.MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 0x1fffffffffffff;

/**
* Attempt to convert `value` an integer, if it's a representation
* of a "safe" unsigned integer.
* @param {*} [value] Value to convert
* @see https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/parseInt
* @returns {(number|NaN)} result
*/
exports.toSafeUnsignedInteger = function toSafeUnsignedInteger(value) {
// absence of second parameter is correct.
var intVal = parseInt(value);
return parseFloat(intVal) === intVal && exports.isSafeUnsignedInteger(intVal)
? intVal
: NaN;
};

/**
* Crude, but effective.
* @api
Expand All @@ -776,3 +808,13 @@ exports.stackTraceFilter = function() {
exports.isPromise = function isPromise(value) {
return typeof value === 'object' && typeof value.then === 'function';
};

/**
* Returns `true` if `value` is a "safe" 32-bit unsigned int.
* @param {*} [value] Value to test
* @returns {boolean} result
*/
exports.isSafeUnsignedInteger = function isUnsignedInteger(value) {
return typeof value === 'number' && value > 0 &&
value <= exports.MAX_SAFE_INTEGER && value % 1 === 0;
};
45 changes: 45 additions & 0 deletions test/utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,50 @@ describe('utils', function() {
});
});

describe('.isSafeUnsignedInteger', function() {
it('should return `false` if the value is not a number', function() {
utils.isSafeUnsignedInteger('boo-boo butt').should.be.false;
});

it('should return `false` if the value is negative', function() {
utils.isSafeUnsignedInteger(-1).should.be.false;
});

it('should return `false` if the value is greater than Number.MAX_SAFE_INTEGER', function() {
utils.isSafeUnsignedInteger(utils.MAX_SAFE_INTEGER + 1).should.be.false;
});

it('should return `false` if the value is not an integer', function() {
utils.isSafeUnsignedInteger(1.5).should.be.false;
});

it('should return `true` if the value is a positive integer lteq MAX_SAFE_INTEGER', function() {
utils.isSafeUnsignedInteger(10).should.be.true;
});
});

describe('.toSafeUnsignedInteger()', function() {
it('should return NaN if isSafeUnsignedInteger() would return false', function() {
utils.toSafeUnsignedInteger(Infinity).should.be.NaN;
});

it('should return NaN if a string representation of a float value', function() {
utils.toSafeUnsignedInteger('10.5').should.be.NaN;
});

it('should return an integer if a safe hex string value', function() {
utils.toSafeUnsignedInteger('0xDEADBEEF').should.equal(0xDEADBEEF);
});

it('should return an integer if a safe integer string value', function() {
utils.toSafeUnsignedInteger('10').should.equal(10);
});

it('should return an integer if a safe octal string value', function() {
utils.toSafeUnsignedInteger('01234').should.equal(01234);
});
});

describe('.isPromise', function() {
it('should return true if the value is Promise-ish', function() {
utils.isPromise({then: function() {}}).should.be.true;
Expand All @@ -197,4 +241,5 @@ describe('utils', function() {
utils.isPromise({}).should.be.false;
});
});

});

0 comments on commit 5ed7851

Please sign in to comment.