2020-06-03 02:48:38 +03:00
|
|
|
#!/usr/bin/env node
|
2020-05-24 02:41:30 +03:00
|
|
|
/**********************************************************************
|
|
|
|
|
*
|
2020-06-01 04:09:40 +03:00
|
|
|
* This is an experimental test framework...
|
|
|
|
|
*
|
|
|
|
|
* The idea is that we can split the tests into:
|
|
|
|
|
* - setups
|
|
|
|
|
* Construct as set of testable things.
|
|
|
|
|
* On this stage the construction process can be tested.
|
|
|
|
|
* - modifiers
|
|
|
|
|
* Take a set of testable things as returned by a setup/modifier and
|
|
|
|
|
* modify them or produce new things based on the old ones.
|
|
|
|
|
* Here the modification process can be tested.
|
|
|
|
|
* - tests
|
|
|
|
|
* Take a set of things as returned by a setup/modifier and run a
|
|
|
|
|
* set of tests on them.
|
|
|
|
|
* This stage tests a specific state and interactions within it.
|
|
|
|
|
* - specific cases
|
|
|
|
|
* A specific manual construction of a thing, its modification and
|
|
|
|
|
* test.
|
|
|
|
|
*
|
|
|
|
|
* Testing is done by building/running a number chains, starting with a
|
|
|
|
|
* setup, then chaining the results through zero or more modifiers and
|
|
|
|
|
* finally into a test.
|
|
|
|
|
*
|
|
|
|
|
* The actual testing is dune via assert(..) functions and is not
|
|
|
|
|
* restricted to the test stage.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* NOTE: tests can be used as modifiers if they modify state and return
|
|
|
|
|
* the modified input.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* XXX thins to simplify:
|
|
|
|
|
* - would be nice if the actual code could be readable...
|
|
|
|
|
* - can we automate assert callas?
|
2020-05-24 02:41:30 +03:00
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
|
|
|
|
(function(require){ var module={} // make module AMD/node compatible...
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
2020-06-04 00:11:45 +03:00
|
|
|
var colors = require('colors')
|
|
|
|
|
|
2020-05-24 02:41:30 +03:00
|
|
|
var object = require('./object')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
2020-05-30 00:35:06 +03:00
|
|
|
// NOTE: to test in verbose mode do:
|
|
|
|
|
// $ export VERBOSE=1 && npm test
|
|
|
|
|
// or
|
|
|
|
|
// $ export VERBOSE=1 && node test.js
|
|
|
|
|
// or set this manually after require('./test') but before running
|
|
|
|
|
// the runner(..)
|
2020-06-01 04:09:40 +03:00
|
|
|
// NOTE: this may change in the future...
|
2020-05-30 00:35:06 +03:00
|
|
|
module.VERBOSE = process ?
|
|
|
|
|
process.env.VERBOSE
|
|
|
|
|
: false
|
2020-05-24 23:59:01 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-24 02:41:30 +03:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
// helpers...
|
|
|
|
|
|
2020-06-04 00:11:45 +03:00
|
|
|
Object.defineProperty(String.prototype, 'raw', {
|
|
|
|
|
get: function(){
|
|
|
|
|
return this.replace(/\x1b\[..?m/g, '') }, })
|
|
|
|
|
|
|
|
|
|
|
2020-06-03 21:09:19 +03:00
|
|
|
// get all keys accessible from object...
|
2020-06-01 02:49:00 +03:00
|
|
|
var deepKeys = function(obj, stop){
|
|
|
|
|
var res = []
|
|
|
|
|
while(obj !== stop && obj != null){
|
|
|
|
|
res.push(Object.keys(obj))
|
|
|
|
|
obj = obj.__proto__ }
|
|
|
|
|
return [...(new Set(res.flat()))] }
|
|
|
|
|
|
2020-06-04 00:20:45 +03:00
|
|
|
|
2020-06-03 21:09:19 +03:00
|
|
|
// compare two arrays by items...
|
|
|
|
|
var arrayCmp = function(a, b){
|
|
|
|
|
var ka = Object.keys(a)
|
|
|
|
|
var kb = Object.keys(a)
|
|
|
|
|
return a === b
|
2020-06-07 21:28:04 +03:00
|
|
|
|| (a.length == b.length
|
|
|
|
|
&& ka
|
|
|
|
|
// keep only non matching stuff...
|
|
|
|
|
.filter(function(k){
|
|
|
|
|
return a[k] !== b[k]
|
|
|
|
|
&& a[k] != a[k] })
|
|
|
|
|
.length == 0) }
|
2020-06-03 21:09:19 +03:00
|
|
|
|
2020-06-03 02:48:38 +03:00
|
|
|
|
2020-06-04 00:11:45 +03:00
|
|
|
|
2020-06-04 00:20:45 +03:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
|
|
|
|
|
// a constructor is a thing that starts with a capital and has a .prototype
|
|
|
|
|
var constructors = function(obj){
|
|
|
|
|
return Object.entries(obj)
|
|
|
|
|
.filter(function([k, o]){
|
2020-06-08 04:16:03 +03:00
|
|
|
return !k.startsWith('_')
|
|
|
|
|
&& k[0] == k[0].toUpperCase()
|
2020-06-04 00:20:45 +03:00
|
|
|
&& o.prototype }) }
|
|
|
|
|
|
|
|
|
|
// an instance is a thing that starts with a lowercase and has a .constructor
|
|
|
|
|
var instances = function(obj){
|
|
|
|
|
return Object.entries(obj)
|
|
|
|
|
.filter(function([k, o]){
|
2020-06-08 04:16:03 +03:00
|
|
|
return !k.startsWith('_')
|
|
|
|
|
&& k[0] == k[0].toLowerCase()
|
2020-06-04 00:20:45 +03:00
|
|
|
&& o.constructor }) }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var makeAssert = function(pre, stats){
|
2020-06-08 16:07:54 +03:00
|
|
|
return Object.assign(
|
|
|
|
|
function(e, msg, ...args){
|
|
|
|
|
stats
|
|
|
|
|
&& (stats.assertions += 1)
|
|
|
|
|
&& !e
|
|
|
|
|
&& (stats.failures += 1)
|
|
|
|
|
module.VERBOSE
|
|
|
|
|
&& console.log(pre +': '+ msg.bold, ...args)
|
|
|
|
|
console.assert(e, pre.bold +': '+ msg.bold.yellow, ...args)
|
|
|
|
|
return e },
|
|
|
|
|
{
|
|
|
|
|
// XXX should have a real path...
|
|
|
|
|
path: pre
|
|
|
|
|
}) }
|
2020-06-04 00:20:45 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
|
2020-06-03 02:48:38 +03:00
|
|
|
// basic argv parser...
|
|
|
|
|
//
|
|
|
|
|
// Format:
|
|
|
|
|
// {
|
|
|
|
|
// // alias...
|
|
|
|
|
// v: 'verbose',
|
|
|
|
|
// // handler...
|
|
|
|
|
// verbose: function(opt, rest){
|
|
|
|
|
// ...
|
|
|
|
|
// },
|
|
|
|
|
//
|
|
|
|
|
// t: 'test',
|
|
|
|
|
// test: {
|
|
|
|
|
// doc: 'test option.',
|
|
|
|
|
// arg: 'VALUE',
|
|
|
|
|
// handler: function(value, opt, rest){
|
|
|
|
|
// ...
|
|
|
|
|
// }},
|
|
|
|
|
//
|
|
|
|
|
// ...
|
|
|
|
|
// }
|
|
|
|
|
//
|
2020-06-03 21:09:19 +03:00
|
|
|
// XXX add features:
|
|
|
|
|
// - option groups -- nested specs...
|
|
|
|
|
// - arg value type conversion???
|
|
|
|
|
// - make this a constructor???
|
|
|
|
|
// - extend this to support command calling...
|
2020-06-03 02:48:38 +03:00
|
|
|
// XXX do we handle = for options with values???
|
|
|
|
|
// XXX move this to it's own lib...
|
2020-06-03 21:09:19 +03:00
|
|
|
// argv-handler
|
|
|
|
|
// ...
|
2020-06-03 02:48:38 +03:00
|
|
|
// XXX need better test processing:
|
|
|
|
|
// - line breaks
|
|
|
|
|
// - ...
|
|
|
|
|
// XXX revise...
|
|
|
|
|
var ArgvParser = function(spec){
|
|
|
|
|
// spec defaults...
|
|
|
|
|
// NOTE: this is intentionally not dynamic...
|
|
|
|
|
spec = Object.assign({
|
|
|
|
|
// builtin options...
|
|
|
|
|
h: 'help',
|
2020-06-03 04:11:49 +03:00
|
|
|
// XXX revise...
|
2020-06-03 02:48:38 +03:00
|
|
|
help: {
|
|
|
|
|
doc: 'print this message and exit.',
|
|
|
|
|
handler: function(){
|
2020-06-03 04:11:49 +03:00
|
|
|
var spec = this.spec
|
|
|
|
|
var that = this
|
2020-06-03 02:48:38 +03:00
|
|
|
console.log([
|
|
|
|
|
`Usage: ${
|
|
|
|
|
typeof(spec.__usage__) == 'function' ?
|
|
|
|
|
spec.__usage__.call(this)
|
|
|
|
|
: spec.__usage__ }`,
|
2020-06-03 04:11:49 +03:00
|
|
|
// doc...
|
|
|
|
|
...(spec.__doc__ ?
|
|
|
|
|
['', typeof(spec.__doc__) == 'function' ?
|
|
|
|
|
spec.__doc__()
|
|
|
|
|
: spec.__doc__]
|
|
|
|
|
: []),
|
|
|
|
|
// options...
|
2020-06-03 02:48:38 +03:00
|
|
|
'',
|
|
|
|
|
'Options:',
|
2020-06-03 04:11:49 +03:00
|
|
|
...(spec.__getoptions__()
|
2020-06-03 02:48:38 +03:00
|
|
|
.map(function([opts, arg, doc]){
|
2020-06-03 04:11:49 +03:00
|
|
|
return ['-'+opts.join(' | -') +' '+ (arg || ''), doc] })),
|
|
|
|
|
// examples...
|
|
|
|
|
...(this.spec.__examples__ ?
|
|
|
|
|
['', 'Examples:', ...(
|
|
|
|
|
this.spec.__examples__ instanceof Array ?
|
|
|
|
|
spec.__examples__
|
|
|
|
|
.map(function(e){
|
|
|
|
|
return e instanceof Array ? e : [e] })
|
|
|
|
|
: spec.__examples__.call(this) )]
|
|
|
|
|
: []),
|
|
|
|
|
// footer...
|
|
|
|
|
...(this.spec.__footer__?
|
|
|
|
|
['', typeof(this.spec.__footer__) == 'function' ?
|
|
|
|
|
spec.__footer__.call(this)
|
|
|
|
|
: spec.__footer__]
|
|
|
|
|
: []) ]
|
|
|
|
|
.map(function(e){
|
|
|
|
|
return e instanceof Array ?
|
|
|
|
|
spec.__align__(...e
|
|
|
|
|
.map(function(s){
|
|
|
|
|
return s.replace(/\$scriptname/g, that.scriptname) }))
|
|
|
|
|
// indent lists...
|
|
|
|
|
.map(function(s){
|
|
|
|
|
return '\t'+ s })
|
|
|
|
|
: e })
|
|
|
|
|
.flat()
|
|
|
|
|
.join('\n')
|
|
|
|
|
.replace(/\$scriptname/g, this.scriptname))
|
|
|
|
|
|
2020-06-03 02:48:38 +03:00
|
|
|
process.exit() }},
|
|
|
|
|
|
|
|
|
|
// special values and methods...
|
|
|
|
|
__opts_width__: 3,
|
2020-06-03 04:11:49 +03:00
|
|
|
__doc_prefix__: '- ',
|
2020-06-03 02:48:38 +03:00
|
|
|
|
|
|
|
|
// these is run in the same context as the handlers... (XXX ???)
|
2020-06-04 02:45:25 +03:00
|
|
|
__align__: function(a, b, ...rest){
|
2020-06-03 04:11:49 +03:00
|
|
|
var opts_width = this.__opts_width__ || 4
|
|
|
|
|
var prefix = this.__doc_prefix__ || ''
|
2020-06-04 02:45:25 +03:00
|
|
|
b = [b, ...rest].join('\n'+ ('\t'.repeat(opts_width+1) + ' '.repeat(prefix.length)))
|
2020-06-03 04:11:49 +03:00
|
|
|
return b ?
|
2020-06-04 00:11:45 +03:00
|
|
|
(a.raw.length < opts_width*8 ?
|
|
|
|
|
[a +'\t'.repeat(opts_width - Math.floor(a.raw.length/8))+ prefix + b]
|
2020-06-03 04:11:49 +03:00
|
|
|
: [a, '\t'.repeat(opts_width)+ prefix + b])
|
|
|
|
|
: [a] },
|
|
|
|
|
|
2020-06-03 02:48:38 +03:00
|
|
|
__usage__: function(){
|
|
|
|
|
return `${ this.scriptname } [OPTIONS]` },
|
2020-06-03 04:11:49 +03:00
|
|
|
__examples__: undefined,
|
|
|
|
|
__footer__: undefined,
|
|
|
|
|
|
2020-06-03 02:48:38 +03:00
|
|
|
__unknown__: function(key){
|
|
|
|
|
console.error('Unknown option:', key)
|
|
|
|
|
process.exit(1) },
|
|
|
|
|
|
|
|
|
|
// these are run in the context of spec...
|
|
|
|
|
__getoptions__: function(){
|
|
|
|
|
var that = this
|
|
|
|
|
var handlers = {}
|
|
|
|
|
Object.keys(this)
|
|
|
|
|
.forEach(function(opt){
|
|
|
|
|
// skip special methods...
|
|
|
|
|
if(/^__.*__$/.test(opt)){
|
|
|
|
|
return }
|
|
|
|
|
var [k, h] = that.__gethandler__(opt)
|
|
|
|
|
handlers[k] ?
|
|
|
|
|
handlers[k][0].push(opt)
|
|
|
|
|
: (handlers[k] = [[opt], h.arg, h.doc || k, h]) })
|
|
|
|
|
return Object.values(handlers) },
|
|
|
|
|
__gethandler__: function(key){
|
|
|
|
|
var seen = new Set([key])
|
|
|
|
|
while(key in this
|
|
|
|
|
&& typeof(this[key]) == typeof('str')){
|
|
|
|
|
key = this[key]
|
|
|
|
|
// check for loops...
|
|
|
|
|
if(seen.has(key)){
|
|
|
|
|
throw Error('Option loop detected: '+ ([...seen, key].join(' -> '))) }
|
|
|
|
|
seen.add(key) }
|
|
|
|
|
return [key, this[key]] },
|
|
|
|
|
}, spec)
|
|
|
|
|
|
|
|
|
|
// sanity check -- this will detect argument loops...
|
|
|
|
|
spec.__getoptions__()
|
|
|
|
|
|
|
|
|
|
return function(argv){
|
|
|
|
|
var pattern = /^--?[a-zA-Z-]*$/
|
|
|
|
|
argv = argv.slice()
|
|
|
|
|
var context = {
|
|
|
|
|
spec: spec,
|
|
|
|
|
argv: argv.slice(),
|
|
|
|
|
|
|
|
|
|
interpreter: argv.shift(),
|
|
|
|
|
script: argv[0],
|
|
|
|
|
scriptname: argv.shift().split(/[\\\/]/).pop(),
|
|
|
|
|
|
|
|
|
|
rest: argv,
|
|
|
|
|
}
|
|
|
|
|
var other = []
|
|
|
|
|
while(argv.length > 0){
|
|
|
|
|
var arg = argv.shift()
|
|
|
|
|
// options...
|
|
|
|
|
if(pattern.test(arg)){
|
|
|
|
|
var handler = spec.__gethandler__(arg.replace(/^--?/, '')).pop()
|
|
|
|
|
|| spec.__unknown__
|
|
|
|
|
// get option value...
|
|
|
|
|
var value = (handler.arg && !pattern.test(argv[0])) ?
|
|
|
|
|
argv.shift()
|
|
|
|
|
: undefined
|
|
|
|
|
// run handler...
|
|
|
|
|
;(typeof(handler) == 'function' ?
|
|
|
|
|
handler
|
|
|
|
|
: handler.handler)
|
|
|
|
|
.call(context,
|
|
|
|
|
// pass value...
|
|
|
|
|
...(handler.arg ? [value] : []),
|
|
|
|
|
arg,
|
|
|
|
|
argv)
|
|
|
|
|
continue }
|
|
|
|
|
other.push(arg) }
|
|
|
|
|
return other } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-24 02:41:30 +03:00
|
|
|
//---------------------------------------------------------------------
|
2020-06-04 00:20:45 +03:00
|
|
|
// Tests...
|
2020-05-24 02:41:30 +03:00
|
|
|
|
2020-06-01 04:09:40 +03:00
|
|
|
var setups =
|
|
|
|
|
module.setups = {
|
2020-05-30 00:20:07 +03:00
|
|
|
// basic constructor and inheritance...
|
2020-06-08 04:54:40 +03:00
|
|
|
//
|
|
|
|
|
// X
|
|
|
|
|
// Y <- A <- B <- C
|
|
|
|
|
//
|
2020-05-24 23:59:01 +03:00
|
|
|
basic: function(assert){
|
2020-05-24 02:41:30 +03:00
|
|
|
var X, Y, A, B, C
|
|
|
|
|
return {
|
2020-06-08 04:54:40 +03:00
|
|
|
X: X = assert(object.Constructor('X'), `.Constructor(..)`),
|
|
|
|
|
Y: Y = assert(object.C('Y', {
|
|
|
|
|
method: function(){
|
|
|
|
|
var x
|
|
|
|
|
assert(
|
|
|
|
|
(x = object.parentCall(Y.prototype.method, this, ...arguments)) === undefined,
|
|
|
|
|
'y.method(..): expected:', undefined, 'got:', x)
|
|
|
|
|
return 'Y'
|
|
|
|
|
},
|
|
|
|
|
}), `.C(..)`),
|
2020-05-24 02:41:30 +03:00
|
|
|
|
2020-06-08 04:54:40 +03:00
|
|
|
A: A = assert(object.C('A', Y, {
|
|
|
|
|
method: function(){
|
|
|
|
|
var x
|
|
|
|
|
assert(
|
|
|
|
|
(x = object.parentCall(A.prototype.method, this, ...arguments)) == 'Y',
|
|
|
|
|
'a.method(..): expected:', 'Y', 'got:', x)
|
|
|
|
|
return 'A'
|
|
|
|
|
},
|
|
|
|
|
}), `inherit (gen1)`),
|
|
|
|
|
B: B = assert(object.C('B', A, {
|
|
|
|
|
// XXX constructor methods...
|
|
|
|
|
}, {
|
|
|
|
|
// XXX methods...
|
|
|
|
|
}), `inherit (gen2) with constructor mixin`),
|
|
|
|
|
C: C = assert(object.C('C', B, {
|
|
|
|
|
method: function(){
|
|
|
|
|
var x
|
|
|
|
|
assert(
|
|
|
|
|
(x = object.parentCall(C.prototype.method, this, ...arguments)) == 'A',
|
|
|
|
|
'c.method(..): expected:', 'A', 'got:', x)
|
|
|
|
|
return 'C'
|
|
|
|
|
},
|
|
|
|
|
}), `inherit (gen3)`),
|
2020-05-24 02:41:30 +03:00
|
|
|
} },
|
2020-05-30 00:20:07 +03:00
|
|
|
|
|
|
|
|
// initialization...
|
2020-05-24 23:59:01 +03:00
|
|
|
init: function(assert){
|
2020-06-01 02:49:00 +03:00
|
|
|
var A, B, C
|
2020-05-24 02:41:30 +03:00
|
|
|
return {
|
2020-06-01 02:49:00 +03:00
|
|
|
// init...
|
|
|
|
|
A: A = assert(object.C('A', {
|
|
|
|
|
msg: '.__init__()',
|
|
|
|
|
__init__: function(){
|
|
|
|
|
this.init_has_run = true },
|
|
|
|
|
test_init: function(){
|
|
|
|
|
this.__created_raw ?
|
|
|
|
|
assert(!this.init_has_run, this.msg+' did not run')
|
|
|
|
|
: assert(this.init_has_run, this.msg+' run') },
|
|
|
|
|
}), 'basic .__init__(..)'),
|
|
|
|
|
// new...
|
|
|
|
|
B: B = assert(object.C('B', {
|
|
|
|
|
__new__: function(){
|
|
|
|
|
var o = {}
|
|
|
|
|
o.new_has_run = true
|
|
|
|
|
return o
|
|
|
|
|
},
|
|
|
|
|
test_new: function(){
|
|
|
|
|
assert(this.new_has_run, '.__new__() run') },
|
|
|
|
|
}), 'basic .__new__(..)'),
|
|
|
|
|
// new + init...
|
|
|
|
|
C: C = assert(object.C('C', B, {
|
|
|
|
|
msg: '.__init__() after .__new__()',
|
|
|
|
|
__init__: function(){
|
|
|
|
|
this.init_has_run = true },
|
|
|
|
|
test_init: A.prototype.test_init,
|
|
|
|
|
}), `inherit .__new__()`),
|
|
|
|
|
|
|
|
|
|
// XXX
|
2020-05-24 02:41:30 +03:00
|
|
|
} },
|
2020-05-30 00:20:07 +03:00
|
|
|
|
|
|
|
|
// callable instances...
|
2020-05-24 23:59:01 +03:00
|
|
|
call: function(assert){
|
2020-05-30 00:20:07 +03:00
|
|
|
// constructors...
|
2020-05-24 23:59:01 +03:00
|
|
|
var A, B, C, D, F, G
|
2020-05-30 00:20:07 +03:00
|
|
|
var res = {
|
2020-05-24 23:59:01 +03:00
|
|
|
A: A = assert(object.C('A',
|
|
|
|
|
function(){
|
2020-05-25 00:53:56 +03:00
|
|
|
return 'A'
|
2020-05-24 23:59:01 +03:00
|
|
|
}), 'callable'),
|
|
|
|
|
B: B = assert(object.C('B', {
|
2020-06-01 02:49:00 +03:00
|
|
|
__non_function: true,
|
2020-05-24 23:59:01 +03:00
|
|
|
__call__: function(){
|
2020-06-08 04:16:03 +03:00
|
|
|
assert(
|
|
|
|
|
object.parentCall(B.prototype, '__call__', this, ...arguments) === undefined,
|
|
|
|
|
'call non-existent parent method', 'B')
|
2020-05-25 00:53:56 +03:00
|
|
|
return 'B'
|
2020-05-24 23:59:01 +03:00
|
|
|
},
|
|
|
|
|
}), 'callable'),
|
|
|
|
|
|
|
|
|
|
C: C = assert(object.C('C', A, {}), 'basic inherit'),
|
|
|
|
|
D: D = assert(object.C('D', B, {}), 'basic inherit'),
|
|
|
|
|
|
|
|
|
|
E: E = assert(object.C('E', A,
|
|
|
|
|
function(){
|
2020-05-25 00:53:56 +03:00
|
|
|
assert(
|
|
|
|
|
object.parentCall(E.prototype, '__call__', this, ...arguments) == 'A',
|
|
|
|
|
'parrent call')
|
|
|
|
|
return 'E'
|
2020-05-24 23:59:01 +03:00
|
|
|
}), 'call parent'),
|
|
|
|
|
F: F = assert(object.C('F', B, {
|
|
|
|
|
__call__: function(){
|
2020-05-25 00:53:56 +03:00
|
|
|
assert(
|
|
|
|
|
object.parentCall(F.prototype, '__call__', this, ...arguments) == 'B',
|
|
|
|
|
'parent call')
|
|
|
|
|
return 'F'
|
2020-05-24 23:59:01 +03:00
|
|
|
},
|
|
|
|
|
}), 'call parent\'s .__call__'),
|
2020-05-30 00:20:07 +03:00
|
|
|
}
|
|
|
|
|
// create instances...
|
|
|
|
|
var objs = tests.instance(assert, res)
|
|
|
|
|
// all instances must be callable...
|
|
|
|
|
// NOTE: not all instances are going to be instanceof Function...
|
|
|
|
|
Object.entries(objs)
|
|
|
|
|
.forEach(function([k, o]){
|
|
|
|
|
assert(typeof(o) == 'function', 'instance is callable', k) })
|
2020-06-07 21:28:04 +03:00
|
|
|
|
2020-05-30 00:20:07 +03:00
|
|
|
return Object.assign(res, objs) },
|
|
|
|
|
|
|
|
|
|
// inherit from native constructors...
|
2020-05-24 23:59:01 +03:00
|
|
|
native: function(assert){
|
2020-05-30 00:20:07 +03:00
|
|
|
return [
|
|
|
|
|
Object,
|
|
|
|
|
Array,
|
|
|
|
|
Number,
|
|
|
|
|
Map,
|
|
|
|
|
Set,
|
|
|
|
|
].reduce(function(res, type){
|
|
|
|
|
var n = type.name
|
|
|
|
|
// direct inherit...
|
|
|
|
|
var O = res[n] =
|
|
|
|
|
assert(object.C(n, type, {}), 'inherit from '+n)
|
|
|
|
|
return res
|
|
|
|
|
}, {}) },
|
|
|
|
|
|
2020-06-03 21:09:19 +03:00
|
|
|
// compatibility: native constructors...
|
|
|
|
|
js_constructors: function(assert){
|
|
|
|
|
var X, Y, Z, a, b, c, d
|
|
|
|
|
return {
|
|
|
|
|
Object,
|
|
|
|
|
Array,
|
|
|
|
|
Number,
|
|
|
|
|
Map,
|
|
|
|
|
Set,
|
|
|
|
|
}},
|
2020-06-04 00:11:45 +03:00
|
|
|
// compatibility: prototype tree...
|
2020-06-03 21:09:19 +03:00
|
|
|
js_prototype: function(assert){
|
|
|
|
|
var a, b, c, d
|
|
|
|
|
return {
|
|
|
|
|
a: a = {
|
|
|
|
|
x: 'a',
|
|
|
|
|
method: function(){
|
|
|
|
|
return 'a' },
|
|
|
|
|
},
|
|
|
|
|
b: b = {
|
|
|
|
|
__proto__: a,
|
|
|
|
|
x: 'b',
|
|
|
|
|
},
|
|
|
|
|
c: c = {
|
|
|
|
|
__proto__: b,
|
|
|
|
|
x: 'c',
|
|
|
|
|
method: function(){
|
2020-06-08 19:23:41 +03:00
|
|
|
var x, y
|
2020-06-03 21:09:19 +03:00
|
|
|
assert(arrayCmp(
|
2020-06-08 19:23:41 +03:00
|
|
|
x = object.values(c, 'x'),
|
|
|
|
|
y = ['c', 'a', 'b']),
|
|
|
|
|
'reach all values of attr: expected:', x, 'got:'.bold.yellow, y)
|
2020-06-07 21:28:04 +03:00
|
|
|
assert(arrayCmp(
|
2020-06-08 19:23:41 +03:00
|
|
|
x = object.values(c, 'x', function(v, o){
|
2020-06-07 21:28:04 +03:00
|
|
|
return v.toUpperCase() }),
|
2020-06-08 19:23:41 +03:00
|
|
|
y = ['C', 'A', 'B']),
|
|
|
|
|
'reach all values of attr: expected:', x, 'got:'.bold.yellow, y)
|
2020-06-03 21:09:19 +03:00
|
|
|
assert(arrayCmp(
|
2020-06-08 19:23:41 +03:00
|
|
|
x = object.sources(c, 'method'),
|
|
|
|
|
// NOTE: not passing an explicit list as we need
|
|
|
|
|
// to account for mixins...
|
|
|
|
|
y = object.sources(c)
|
|
|
|
|
.filter(function(s){
|
|
|
|
|
return s.hasOwnProperty('method') })),
|
|
|
|
|
'reach all values of method: expected:', y, 'got:'.bold.yellow, x)
|
2020-06-07 21:28:04 +03:00
|
|
|
assert(
|
2020-06-08 19:23:41 +03:00
|
|
|
(x = object.parent(c, 'x')) == 'b',
|
|
|
|
|
'reach parent attr: expected:', 'b', 'got:'.bold.yellow, x)
|
2020-06-07 21:28:04 +03:00
|
|
|
assert(
|
2020-06-08 19:23:41 +03:00
|
|
|
(x = object.parentCall(c.method, this)) == (y = a.method()),
|
|
|
|
|
'reach parent method: expected:', y, 'got:'.bold.yellow, x)
|
2020-06-03 21:09:19 +03:00
|
|
|
return 'c' },
|
|
|
|
|
},
|
|
|
|
|
d: d = {
|
|
|
|
|
__proto__: c,
|
|
|
|
|
method: function(){
|
|
|
|
|
assert(object.parentCall(d.method, this) == 'c', 'reach parent method', 'd')
|
|
|
|
|
return 'd' },
|
|
|
|
|
},
|
|
|
|
|
}},
|
2020-06-04 00:11:45 +03:00
|
|
|
// compatibility: class/instance...
|
2020-06-03 21:09:19 +03:00
|
|
|
js_class: function(assert){
|
|
|
|
|
var X, Y, Z
|
|
|
|
|
return {
|
|
|
|
|
X: X = class {
|
|
|
|
|
x = 'x'
|
|
|
|
|
method(){
|
|
|
|
|
return 'x' }
|
|
|
|
|
},
|
|
|
|
|
Y: Y = class extends X {
|
|
|
|
|
x = 'y'
|
|
|
|
|
},
|
|
|
|
|
Z: Z = class extends Y {
|
|
|
|
|
x = 'z'
|
|
|
|
|
method(){
|
2020-06-07 21:28:04 +03:00
|
|
|
// XXX this is almost the same as for js_prototype...
|
2020-06-03 21:09:19 +03:00
|
|
|
assert(arrayCmp(
|
2020-06-07 21:28:04 +03:00
|
|
|
object.values(c, 'x'),
|
2020-06-03 21:09:19 +03:00
|
|
|
['z', 'y', 'x']),
|
|
|
|
|
'reach all values of attr (class)')
|
2020-06-07 21:28:04 +03:00
|
|
|
assert(arrayCmp(
|
|
|
|
|
object.values(c, 'x', function(v, o){
|
|
|
|
|
return v.toUpperCase() }),
|
|
|
|
|
['C', 'A', 'B']),
|
|
|
|
|
'reach all values of attr (class)')
|
2020-06-03 21:09:19 +03:00
|
|
|
assert(arrayCmp(
|
|
|
|
|
object.sources(c, 'method'),
|
|
|
|
|
[Z.prototype, X.prototype]),
|
|
|
|
|
'reach all values of method (class)')
|
|
|
|
|
assert(
|
|
|
|
|
object.parent(c, 'x') == super.x,
|
|
|
|
|
'reach super attr (class)')
|
|
|
|
|
assert(
|
|
|
|
|
object.parentCall(c.method, this) == super.method(),
|
|
|
|
|
'reach super method (class)')
|
|
|
|
|
return 'c' }
|
|
|
|
|
},
|
|
|
|
|
}},
|
2020-05-24 02:41:30 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-01 04:09:40 +03:00
|
|
|
|
|
|
|
|
var modifiers =
|
|
|
|
|
module.modifiers = {
|
2020-05-24 23:59:01 +03:00
|
|
|
// default...
|
2020-06-02 03:05:55 +03:00
|
|
|
//
|
2020-05-24 23:59:01 +03:00
|
|
|
'as-is': function(assert, setup){
|
2020-05-30 00:20:07 +03:00
|
|
|
return setup },
|
|
|
|
|
|
|
|
|
|
// make gen2-3 constructors...
|
|
|
|
|
//
|
|
|
|
|
// NOTE: there is almost no need to test below gen3...
|
|
|
|
|
gen2: function(assert, setup, gen){
|
|
|
|
|
gen = gen || 2
|
|
|
|
|
return constructors(setup)
|
|
|
|
|
.reduce(function(res, [n, O]){
|
|
|
|
|
res[n+'g'+gen] = object.C(n+'g'+gen, O, {})
|
|
|
|
|
return res }, {}) },
|
|
|
|
|
gen3: function(assert, setup){
|
2020-06-01 02:49:00 +03:00
|
|
|
return this.gen2(assert, this.gen2(assert, setup), '3') },
|
2020-05-24 23:59:01 +03:00
|
|
|
|
2020-06-01 02:49:00 +03:00
|
|
|
// generate instances...
|
2020-06-02 03:05:55 +03:00
|
|
|
//
|
2020-06-01 02:49:00 +03:00
|
|
|
// NOTE: these are re-used as tests too...
|
|
|
|
|
instance: function(assert, setup, mode){
|
|
|
|
|
return constructors(setup)
|
|
|
|
|
.reduce(function(res, [k, O]){
|
2020-06-03 21:09:19 +03:00
|
|
|
// native JS constructors do not support no_new or raw modes...
|
|
|
|
|
if((mode == 'raw' || mode == 'no_new') && !O.__rawinstance__){
|
|
|
|
|
return res }
|
2020-06-02 03:05:55 +03:00
|
|
|
// create instance with lowercase name of constructor...
|
|
|
|
|
// NOTE: constructor is expected to be capitalized...
|
2020-06-01 02:49:00 +03:00
|
|
|
var o = res[k.toLowerCase()] =
|
|
|
|
|
mode == 'no_new' ?
|
|
|
|
|
assert(O(), `new:`, k)
|
|
|
|
|
: mode == 'raw' ?
|
|
|
|
|
assert(O.__rawinstance__(), `.__rawinstance__()`, k)
|
|
|
|
|
: assert(new O(), `new:`, k)
|
2020-06-08 16:07:54 +03:00
|
|
|
|
2020-06-01 02:49:00 +03:00
|
|
|
assert(o instanceof O, `instanceof:`, k)
|
2020-06-08 16:07:54 +03:00
|
|
|
|
2020-06-01 02:49:00 +03:00
|
|
|
O.__proto__ instanceof Function
|
2020-06-08 16:07:54 +03:00
|
|
|
// XXX need to test this for constructor mixins too...
|
|
|
|
|
&& !(O.__mixin_constructors && !O.__mixin_flat)
|
|
|
|
|
&& assert(o instanceof o.constructor.__proto__, `instanceof-nested:`, k)
|
|
|
|
|
|
2020-06-01 02:49:00 +03:00
|
|
|
assert(o.constructor === O, `.constructor:`, k)
|
2020-06-08 16:07:54 +03:00
|
|
|
|
2020-06-01 02:49:00 +03:00
|
|
|
assert(o.__proto__ === O.prototype, `.__proto__:`, k)
|
2020-06-08 16:07:54 +03:00
|
|
|
|
2020-06-01 02:49:00 +03:00
|
|
|
return res }, {}) },
|
|
|
|
|
instance_no_new: function(assert, setup){
|
|
|
|
|
return this.instance(assert, setup, 'no_new') },
|
2020-06-02 03:05:55 +03:00
|
|
|
// NOTE: here we mark the raw instances with .__created_raw, this is
|
|
|
|
|
// done to be able to distinguish them from fully initialized
|
|
|
|
|
// instances...
|
2020-06-01 02:49:00 +03:00
|
|
|
instance_raw: function(assert, setup){
|
|
|
|
|
var res = this.instance(assert, setup, 'raw')
|
|
|
|
|
Object.values(res)
|
|
|
|
|
.forEach(function(e){
|
|
|
|
|
Object.assign(
|
|
|
|
|
e,
|
|
|
|
|
{__created_raw: true}) })
|
|
|
|
|
return res },
|
2020-06-01 04:09:40 +03:00
|
|
|
|
2020-06-08 04:16:03 +03:00
|
|
|
// mixins...
|
2020-06-08 19:23:41 +03:00
|
|
|
// XXX might be a good idea to get the method name from the context... how?
|
2020-06-08 16:07:54 +03:00
|
|
|
mixin_instance: function(assert, setup, flat, filter, get){
|
|
|
|
|
filter = filter || instances
|
|
|
|
|
var attr = '__mixin_' + filter.name
|
|
|
|
|
|
|
|
|
|
var mixin = setup[attr] = {
|
2020-06-08 19:23:41 +03:00
|
|
|
__mixin: true,
|
2020-06-08 16:07:54 +03:00
|
|
|
[attr]: true,
|
|
|
|
|
__mixin_flat: !!flat,
|
2020-06-08 04:16:03 +03:00
|
|
|
|
2020-06-08 19:23:41 +03:00
|
|
|
method: function(){
|
|
|
|
|
var res = object.parent(mixin.method, this) !== undefined ?
|
|
|
|
|
assert(
|
|
|
|
|
object.parentCall(mixin.method, this, ...arguments),
|
|
|
|
|
'mixin method parent call')
|
|
|
|
|
: false
|
|
|
|
|
return res
|
|
|
|
|
|| 'mixin' },
|
|
|
|
|
|
|
|
|
|
mixinMethod: function(){
|
|
|
|
|
return 'mixin' },
|
2020-06-08 04:16:03 +03:00
|
|
|
}
|
2020-06-08 16:07:54 +03:00
|
|
|
|
|
|
|
|
mixin[attr] = mixin
|
|
|
|
|
filter(setup)
|
2020-06-08 04:16:03 +03:00
|
|
|
.forEach(function([n, o]){
|
2020-06-08 16:07:54 +03:00
|
|
|
o = get ? get(o) : o
|
2020-06-08 04:16:03 +03:00
|
|
|
// mixin once per chain...
|
2020-06-08 16:07:54 +03:00
|
|
|
if(!o || o[attr]){
|
2020-06-08 04:16:03 +03:00
|
|
|
return }
|
|
|
|
|
assert(!object.hasMixin(o, mixin), 'pre mixin test', n)
|
|
|
|
|
assert(flat ?
|
|
|
|
|
object.mixinFlat(o, mixin)
|
|
|
|
|
: object.mixin(o, mixin),
|
|
|
|
|
flat ?
|
|
|
|
|
'mixin (flat)'
|
|
|
|
|
:'mixin', n)
|
|
|
|
|
assert(object.hasMixin(o, mixin), 'mixin test', n)
|
|
|
|
|
})
|
|
|
|
|
return setup },
|
|
|
|
|
mixin_instance_flat: function(assert, setup){
|
|
|
|
|
return this.mixin_instance(assert, setup, true) },
|
|
|
|
|
mixin_constructor: function(assert, setup, flat){
|
2020-06-08 16:07:54 +03:00
|
|
|
return this.mixin_instance(assert, setup, false, constructors) },
|
|
|
|
|
mixin_constructor_proto: function(assert, setup, flat){
|
|
|
|
|
return this.mixin_instance(assert, setup, false, constructors,
|
|
|
|
|
function(o){
|
|
|
|
|
// skip mixing into Object.prototype...
|
|
|
|
|
return o !== Object
|
|
|
|
|
&& o.prototype }) },
|
2020-06-08 04:16:03 +03:00
|
|
|
mixin_constructor_flat: function(assert, setup){
|
2020-06-08 16:07:54 +03:00
|
|
|
return this.mixin_constructor_proto(assert, setup, true) },
|
2020-06-08 04:16:03 +03:00
|
|
|
/*/ XXX
|
|
|
|
|
mixout: function(assert, setup){
|
|
|
|
|
return {}
|
|
|
|
|
},
|
|
|
|
|
//*/
|
2020-06-01 04:09:40 +03:00
|
|
|
|
|
|
|
|
// sanity checks...
|
2020-06-02 03:05:55 +03:00
|
|
|
//
|
2020-06-01 04:09:40 +03:00
|
|
|
// NOTE: these should have no side-effects but since we can run
|
2020-06-02 03:05:55 +03:00
|
|
|
// them why not run them and verify ;)
|
2020-06-01 04:09:40 +03:00
|
|
|
get methods(){ return tests.methods },
|
|
|
|
|
get constructor_methods(){ return tests.constructor_methods },
|
|
|
|
|
get callables(){ return tests.callables },
|
2020-05-24 02:41:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-06-01 04:09:40 +03:00
|
|
|
var tests =
|
|
|
|
|
module.tests = {
|
2020-05-30 00:20:07 +03:00
|
|
|
// instance creation...
|
2020-06-01 02:49:00 +03:00
|
|
|
instance: modifiers.instance,
|
|
|
|
|
instance_no_new: modifiers.instance_no_new,
|
|
|
|
|
instance_raw: modifiers.instance_raw,
|
2020-05-24 23:59:01 +03:00
|
|
|
|
2020-05-30 00:53:17 +03:00
|
|
|
/*/ XXX
|
2020-05-24 23:59:01 +03:00
|
|
|
attributes: function(assert, setup){
|
2020-05-30 00:53:17 +03:00
|
|
|
return {} },
|
|
|
|
|
//*/
|
2020-05-24 23:59:01 +03:00
|
|
|
|
2020-06-01 04:09:40 +03:00
|
|
|
// methods...
|
2020-05-24 23:59:01 +03:00
|
|
|
methods: function(assert, setup){
|
2020-06-01 02:49:00 +03:00
|
|
|
instances(setup)
|
|
|
|
|
.forEach(function([k, o]){
|
|
|
|
|
deepKeys(o)
|
|
|
|
|
.forEach(function(m){
|
|
|
|
|
typeof(o[m]) == 'function'
|
|
|
|
|
// skip special methods...
|
|
|
|
|
&& !m.startsWith('__')
|
|
|
|
|
&& o[m]() }) })
|
2020-06-01 04:09:40 +03:00
|
|
|
return setup },
|
2020-06-01 02:49:00 +03:00
|
|
|
constructor_methods: function(assert, setup){
|
2020-05-24 23:59:01 +03:00
|
|
|
constructors(setup)
|
|
|
|
|
.forEach(function([k, O]){
|
2020-06-01 02:49:00 +03:00
|
|
|
deepKeys(O)
|
|
|
|
|
.forEach(function(m){
|
|
|
|
|
typeof(O[m]) == 'function'
|
|
|
|
|
// skip special methods...
|
|
|
|
|
&& !m.startsWith('__')
|
|
|
|
|
&& O[m]() }) })
|
2020-06-01 04:09:40 +03:00
|
|
|
return setup },
|
|
|
|
|
|
|
|
|
|
// callables...
|
2020-05-25 00:53:56 +03:00
|
|
|
callables: function(assert, setup){
|
2020-06-07 21:28:04 +03:00
|
|
|
// test special case .values(x, '__call__')
|
|
|
|
|
var test = function(obj, name){
|
|
|
|
|
var a, b
|
|
|
|
|
return assert(arrayCmp(
|
|
|
|
|
a = object.values(obj, '__call__')
|
|
|
|
|
.map(function(func){
|
|
|
|
|
return func.call(obj) })
|
|
|
|
|
.flat(),
|
|
|
|
|
// get all callables in prototype chain and call them...
|
|
|
|
|
b = object.sources(obj)
|
|
|
|
|
.filter(function(o){
|
|
|
|
|
return typeof(o) == 'function'
|
|
|
|
|
|| o.hasOwnProperty('__call__') })
|
|
|
|
|
.map(function(o){
|
|
|
|
|
return o.hasOwnProperty('__call__') ?
|
|
|
|
|
o.__call__.call(obj)
|
|
|
|
|
// NOTE: not all callables are instances of Function...
|
|
|
|
|
: Reflect.apply(Function.prototype, o, [obj]) })),
|
|
|
|
|
'values of .__call__ of '+ name +': got:', a, 'expected:', b) }
|
|
|
|
|
|
2020-06-01 04:09:40 +03:00
|
|
|
instances(setup)
|
2020-06-07 21:28:04 +03:00
|
|
|
.filter(function([_, o]){
|
2020-05-28 14:20:47 +03:00
|
|
|
// NOTE: not all callables are instances of Function...
|
2020-06-07 21:28:04 +03:00
|
|
|
return typeof(o) == 'function' })
|
|
|
|
|
.forEach(function([k, o]){
|
|
|
|
|
o.__non_function ?
|
|
|
|
|
assert(!(o instanceof Function), 'non-instanceof Function', k)
|
|
|
|
|
: assert(o instanceof Function, 'instanceof Function', k)
|
|
|
|
|
|
|
|
|
|
assert(o(), 'call', k)
|
|
|
|
|
|
|
|
|
|
test(o, k)
|
|
|
|
|
})
|
2020-06-01 04:09:40 +03:00
|
|
|
return setup },
|
2020-05-24 02:41:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// specific independent cases...
|
2020-05-30 00:53:17 +03:00
|
|
|
//
|
|
|
|
|
// NOTE: it is a good idea to migrate tests from here into the main
|
|
|
|
|
// framework so as to be able to use them on more setups...
|
2020-06-01 04:09:40 +03:00
|
|
|
var cases =
|
|
|
|
|
module.cases = {
|
2020-05-30 00:53:17 +03:00
|
|
|
'example': function(assert){
|
|
|
|
|
assert(true, 'example.')
|
|
|
|
|
},
|
2020-05-24 02:41:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-24 23:59:01 +03:00
|
|
|
//---------------------------------------------------------------------
|
2020-05-30 00:53:17 +03:00
|
|
|
// Test runner...
|
|
|
|
|
//
|
2020-06-02 03:05:55 +03:00
|
|
|
// runner()
|
2020-06-02 15:56:03 +03:00
|
|
|
// runner('*')
|
2020-06-02 03:05:55 +03:00
|
|
|
// -> stats
|
|
|
|
|
//
|
2020-06-02 15:56:03 +03:00
|
|
|
// runner('case')
|
2020-06-02 03:05:55 +03:00
|
|
|
// runner('setup:test')
|
|
|
|
|
// runner('setup:mod:test')
|
|
|
|
|
// -> stats
|
|
|
|
|
//
|
|
|
|
|
//
|
2020-05-30 00:53:17 +03:00
|
|
|
// This will run
|
|
|
|
|
// test(modifier(setup))
|
|
|
|
|
// for each test in tests
|
|
|
|
|
// for each modifier in modifiers
|
|
|
|
|
// for each setup in setups
|
|
|
|
|
// case()
|
|
|
|
|
// for each case in cases
|
|
|
|
|
//
|
2020-06-02 03:05:55 +03:00
|
|
|
//
|
|
|
|
|
// NOTE: chaining more than one modifier is not yet supported (XXX)
|
2020-06-01 04:09:40 +03:00
|
|
|
var runner =
|
|
|
|
|
module.runner =
|
2020-06-02 15:56:03 +03:00
|
|
|
function(chain, stats){
|
2020-06-02 03:05:55 +03:00
|
|
|
// parse chain...
|
2020-06-02 15:56:03 +03:00
|
|
|
chain = (chain == '*' || chain == null) ?
|
|
|
|
|
[]
|
|
|
|
|
: chain
|
2020-06-02 03:05:55 +03:00
|
|
|
chain = chain instanceof Array ?
|
|
|
|
|
chain
|
|
|
|
|
: chain.split(/:/)
|
|
|
|
|
var chain_length = chain.length
|
|
|
|
|
var setup = chain.shift() || '*'
|
|
|
|
|
var test = chain.pop() || '*'
|
|
|
|
|
var mod = chain.pop() || '*'
|
2020-06-02 19:26:14 +03:00
|
|
|
mod = chain_length == 2 ?
|
|
|
|
|
'as-is'
|
|
|
|
|
: mod
|
2020-06-02 03:05:55 +03:00
|
|
|
|
2020-06-02 15:56:03 +03:00
|
|
|
// stats...
|
|
|
|
|
stats = stats || {}
|
|
|
|
|
Object.assign(stats, {
|
|
|
|
|
tests: stats.tests || 0,
|
|
|
|
|
assertions: stats.assertions || 0,
|
|
|
|
|
failures: stats.failures || 0,
|
|
|
|
|
time: stats.time || 0,
|
|
|
|
|
})
|
|
|
|
|
|
2020-06-01 04:09:40 +03:00
|
|
|
var started = Date.now()
|
2020-05-24 02:41:30 +03:00
|
|
|
// tests...
|
2020-06-02 03:05:55 +03:00
|
|
|
chain_length != 1
|
|
|
|
|
&& Object.keys(tests)
|
|
|
|
|
.filter(function(t){
|
|
|
|
|
return test == '*' || test == t })
|
|
|
|
|
.forEach(function(t){
|
|
|
|
|
// modifiers...
|
|
|
|
|
Object.keys(modifiers)
|
|
|
|
|
.filter(function(m){
|
|
|
|
|
return mod == '*' || mod == m })
|
|
|
|
|
.forEach(function(m){
|
|
|
|
|
// setups...
|
|
|
|
|
Object.keys(setups)
|
|
|
|
|
.filter(function(s){
|
|
|
|
|
return setup == '*' || setup == s })
|
|
|
|
|
.forEach(function(s){
|
|
|
|
|
if(typeof(setups[s]) != 'function'){
|
|
|
|
|
return }
|
|
|
|
|
// run the test...
|
|
|
|
|
stats.tests += 1
|
|
|
|
|
// XXX revise order...
|
2020-06-02 15:56:03 +03:00
|
|
|
var _assert = makeAssert(`test: ${s}:${m}:${t}`, stats)
|
2020-06-02 03:05:55 +03:00
|
|
|
tests[t](_assert,
|
|
|
|
|
modifiers[m](_assert,
|
|
|
|
|
setups[s](_assert))) }) }) })
|
2020-05-24 02:41:30 +03:00
|
|
|
// cases...
|
2020-06-02 03:05:55 +03:00
|
|
|
chain_length <= 1
|
|
|
|
|
&& Object.keys(cases)
|
|
|
|
|
.filter(function(s){
|
|
|
|
|
return setup == '*' || setup == s })
|
|
|
|
|
.forEach(function(c){
|
|
|
|
|
stats.tests += 1
|
2020-06-02 15:56:03 +03:00
|
|
|
cases[c]( makeAssert(`case: ${c}`, stats) ) })
|
2020-06-01 04:09:40 +03:00
|
|
|
// runtime...
|
2020-06-02 15:56:03 +03:00
|
|
|
stats.time += Date.now() - started
|
2020-05-28 14:20:47 +03:00
|
|
|
return stats }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
2020-06-02 03:05:55 +03:00
|
|
|
// we are run from command line -> test...
|
2020-06-01 04:09:40 +03:00
|
|
|
//
|
|
|
|
|
// NOTE: normally this would be require.main === module but we are
|
|
|
|
|
// overriding module in the compatibility wrapper so we have to do
|
|
|
|
|
// things differently...
|
|
|
|
|
//
|
2020-06-02 03:05:55 +03:00
|
|
|
// XXX update wrapper to make the condition simpler...
|
2020-06-01 04:09:40 +03:00
|
|
|
if(typeof(__filename) != 'undefined'
|
|
|
|
|
&& __filename == (require.main || {}).filename){
|
2020-05-28 14:20:47 +03:00
|
|
|
|
2020-06-02 03:05:55 +03:00
|
|
|
// parse args...
|
2020-06-03 02:48:38 +03:00
|
|
|
var chains =
|
|
|
|
|
ArgvParser({
|
2020-06-03 04:11:49 +03:00
|
|
|
// doc...
|
|
|
|
|
__usage__: `$scriptname [OPTIONS] [CHAIN] ...`,
|
2020-06-04 02:45:25 +03:00
|
|
|
__doc__: object.normalizeTextIndent(
|
|
|
|
|
`Run tests on object.js module.
|
|
|
|
|
|
|
|
|
|
Tests run by $scriptname can be specified in one of the
|
|
|
|
|
following formats:
|
|
|
|
|
|
|
|
|
|
<case>
|
|
|
|
|
<setup>:<test>
|
|
|
|
|
<setup>:<modifier>:<test>
|
|
|
|
|
|
|
|
|
|
Each of the items in the test spec can be a "*" indicating
|
|
|
|
|
that all relevant items should be used, for example:
|
|
|
|
|
|
2020-06-06 03:53:59 +03:00
|
|
|
${ '$ ./$scriptname basic:*:*' }
|
2020-06-04 02:45:25 +03:00
|
|
|
|
|
|
|
|
Here $scriptname is instructed to run all tests and modifiers
|
|
|
|
|
only on the basic setup.
|
|
|
|
|
|
|
|
|
|
Zero or more sets of tests can be specified.
|
|
|
|
|
|
|
|
|
|
When no tests specified $scriptname will run all tests.
|
|
|
|
|
`),
|
2020-06-03 04:11:49 +03:00
|
|
|
__examples__: [
|
2020-06-06 03:53:59 +03:00
|
|
|
['$ ./$scriptname',
|
|
|
|
|
'run all tests.'.gray],
|
|
|
|
|
['$ ./$scriptname basic:*:*',
|
|
|
|
|
'run all tests and modifiers on "basic" setup.'.gray,
|
|
|
|
|
'(see $scriptname -l for more info)'.gray],
|
|
|
|
|
['$ ./$scriptname -v example',
|
|
|
|
|
'run "example" test in verbose mode.'.gray],
|
|
|
|
|
['$ ./$scriptname native:gen3:methods init:gen3:methods',
|
|
|
|
|
'run two tests/patterns.'.gray],
|
|
|
|
|
['$ export VERBOSE=1 && ./$scriptname',
|
|
|
|
|
'set verbose mode globally and run tests.'.gray],
|
2020-06-03 04:11:49 +03:00
|
|
|
],
|
2020-06-03 02:48:38 +03:00
|
|
|
// options...
|
|
|
|
|
l: 'list',
|
|
|
|
|
list: {
|
|
|
|
|
doc: 'list available tests.',
|
|
|
|
|
handler: function(){
|
|
|
|
|
console.log(object.normalizeTextIndent(
|
2020-06-05 22:26:19 +03:00
|
|
|
`Tests run by %s can be of the following forms:
|
2020-06-04 02:45:25 +03:00
|
|
|
|
|
|
|
|
<case>
|
|
|
|
|
<setup>:<test>
|
|
|
|
|
<setup>:<modifier>:<test>
|
|
|
|
|
|
|
|
|
|
Setups:
|
2020-06-03 02:48:38 +03:00
|
|
|
${ Object.keys(setups).join('\n\
|
|
|
|
|
') }
|
|
|
|
|
|
|
|
|
|
Modifiers:
|
|
|
|
|
${ Object.keys(modifiers).join('\n\
|
|
|
|
|
') }
|
|
|
|
|
|
|
|
|
|
Tests:
|
|
|
|
|
${ Object.keys(tests).join('\n\
|
|
|
|
|
') }
|
|
|
|
|
|
|
|
|
|
Standalone test cases:
|
|
|
|
|
${ Object.keys(cases).join('\n\
|
|
|
|
|
') }
|
2020-06-04 02:45:25 +03:00
|
|
|
`), this.scriptname)
|
2020-06-03 02:48:38 +03:00
|
|
|
process.exit() }},
|
|
|
|
|
v: 'verbose',
|
|
|
|
|
verbose: {
|
2020-06-03 04:11:49 +03:00
|
|
|
doc: 'verbose mode (defaults to: $VERBOSE).',
|
2020-06-03 02:48:38 +03:00
|
|
|
handler: function(){
|
|
|
|
|
module.VERBOSE = true }},
|
|
|
|
|
})(process.argv)
|
|
|
|
|
|
2020-06-02 03:05:55 +03:00
|
|
|
// run the tests...
|
2020-06-02 15:56:03 +03:00
|
|
|
var stats = {}
|
|
|
|
|
chains.length > 0 ?
|
|
|
|
|
chains
|
|
|
|
|
.forEach(function(chain){
|
|
|
|
|
runner(chain, stats) })
|
|
|
|
|
: runner('*', stats)
|
|
|
|
|
|
|
|
|
|
// print stats...
|
|
|
|
|
console.log('Tests run:', stats.tests,
|
2020-06-03 02:48:38 +03:00
|
|
|
' Assertions:', stats.assertions,
|
|
|
|
|
' Failures:', stats.failures,
|
2020-06-04 00:11:45 +03:00
|
|
|
` (${stats.time}ms)`.bold.black)
|
2020-06-02 03:05:55 +03:00
|
|
|
|
|
|
|
|
// report error status to the OS...
|
|
|
|
|
process.exit(stats.failures)
|
2020-06-01 04:09:40 +03:00
|
|
|
}
|
2020-05-24 02:41:30 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-06-03 02:48:38 +03:00
|
|
|
|
2020-05-24 02:41:30 +03:00
|
|
|
/**********************************************************************
|
|
|
|
|
* vim:set ts=4 sw=4 : */ return module })
|