Created
February 18, 2012 14:20
-
-
Save VoQn/1859524 to your computer and use it in GitHub Desktop.
prototyping quickcheck for js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var TestEnvironment = (function(){ | |
function createArgs( generator, prop ){ | |
var as = []; | |
for ( var i = 0, l = prop.length; i < l; i++ ) { | |
as[i] = generator.arbitrary(); | |
} | |
return as; | |
}; | |
return { | |
seed: 1, | |
count: 100, | |
current: { | |
args: [], | |
isPassed: false, | |
isSkipped: false, | |
set: function( args, prop ){ | |
var result = prop.apply( this, args ); | |
this.args = args; | |
if ( !!result.wasSkip ) { | |
this.isSkipped = result.wasSkip; | |
} else { | |
this.isSkipped = false; | |
this.isPassed = result; | |
} | |
}, | |
evaluate: function( verbose, score ){ | |
var mark = '', | |
shouldView = !( this.isSkipped || this.isPassed ); | |
if ( this.isSkipped ) { | |
mark = 'Skipped:'; | |
score.skipped++; | |
} | |
else if ( this.isPassed ) { | |
mark = 'Passed:'; | |
score.passed++; | |
} else { | |
mark = 'Faild:'; | |
score.failure++; | |
} | |
if ( verbose || shouldView ) { | |
console.log( mark ); | |
for( var i = 0, l = this.args.length; i < l; i++ ){ | |
console.log( this.args[i] ); | |
} | |
} | |
} | |
}, | |
result: { | |
passed: 0, | |
failure: 0, | |
skipped: 0, | |
get: function() { | |
var _ = this, total = _.passed + _.skipped + _.failure, | |
msg = '', mark = '+++', isPassed = true; | |
if ( total == _.passed ) { | |
msg = 'OK, passed ' + _.passed + ' tests.'; | |
} else if ( total == _.passed + _.skipped ) { | |
msg = 'OK, passed ' + _.passed + ' tests (skipped ' + _.skipped + ' tests)'; | |
} else { | |
mark = '***' | |
msg = 'Faild! ' + _.fail + ' test case.' + | |
'(passed ' + _.passed + | |
(_.skipped ? ' )' : ', skipped ' + _.skipped + ' )'); | |
} | |
return { | |
score: _, | |
mark: mark, | |
msg: msg, | |
isPassed: isPassed | |
} | |
} | |
}, | |
clean: function(){ | |
this.seed = 0; | |
this.current.args = []; | |
this.result.passed = 0; | |
this.result.failure = 0; | |
this.result.skipped = 0; | |
}, | |
getRange: function(){ | |
return Math.pow( 2, Math.round( this.seed / 2 ) ); | |
}, | |
check: function( prop, verbose ) { | |
var i = 0, result;// _ = this; | |
while ( i++ < this.count ) { | |
prop(); | |
this.current.evaluate( verbose, this.result ); | |
this.seed++; | |
} | |
result = this.result.get(); | |
console.log( result.mark + ' ' + result.msg ); | |
this.clean(); | |
} | |
}; | |
})(); | |
var NotImplementException = function( _interface, _identifier ){ | |
var error = new Error(); | |
error.name = 'NotImplementException'; | |
error.message = _identifier + ' is not instance of ' + _interface; | |
return error; | |
}; | |
var JsCheck = (function(){ | |
var E = TestEnvironment; | |
function createArgs( generator, prop ){ | |
var as = []; | |
for ( var i = 0, l = prop.length; i < l; i++ ) { | |
as[i] = generator.arbitrary(); | |
} | |
return as; | |
}; | |
function check( prop, supplier, verbose ){ | |
if ( !supplier ) { // call forAll | |
E.check( prop, verbose ); | |
} else { // prop is simple function, supplier is apply generated args for prop | |
E.check( supplier( prop ), verbose ); | |
} | |
} | |
return { | |
forAll: function( generator, prop ){ | |
if ( !generator.arbitrary ){ | |
throw new NotImplementException('Arbitrary', 'generator'); | |
} | |
return function(){ | |
var args = createArgs( generator, prop ); | |
E.current.set( args, prop ); | |
}; | |
}, | |
quickCheck: function( test, supplier ) { check( test, supplier ); }, | |
verboseCheck: function( test, supplier ) { check( test, supplier, true ); } | |
}; | |
})(); | |
var Arbitrary = (function( env ){ | |
return { | |
Integer: fromGen( genInt ), | |
Double: fromGen( genNumber ), | |
create: fromGen | |
}; | |
function genNumber(){ | |
var sign = Math.random() < 0.5 ? (-1) : 1, | |
seed = Math.random(), | |
width = env.getRange(); | |
return sign * seed * width; | |
} | |
function genInt(){ | |
return Math.round( genNumber() ); | |
} | |
function fromGen( generator ) { | |
return { arbitrary: generator }; | |
} | |
})( TestEnvironment ); | |
Arbitrary.Bool = elements_of([false, true]); | |
function elements_of( list ){ | |
var len = list.length; | |
return Arbitrary.create( function(){ | |
return list[ genIndex() ]; | |
}); | |
function genIndex() { | |
var i = Math.floor( Math.random() * len ); | |
return Math.min( i, len - 1 ); | |
} | |
} | |
function one_of( generators ) { | |
return elements_of( generators ); | |
} | |
function where( before, proc ) { | |
if ( shouldSkip( before ) ) { | |
return { wasSkip: true } | |
} | |
return proc(); | |
function shouldSkip( cond ) { | |
if ( cond instanceof Array ) { | |
for (var i = 0, l = cond.length; i < l; i ++){ | |
if ( !cond[i] ) { | |
return true; | |
} | |
} | |
} else { | |
return !cond; | |
} | |
return false; | |
} | |
} | |
function types(/* Arbitrary types */){ | |
var arbs = arguments, args = [], E = TestEnvironment; | |
return function( prop ){ | |
return function() { | |
for (var i = 0, l = arbs.length; i < l; i++) { | |
if ( !arbs[i].arbitrary ) { | |
throw new NotImplementException('Arbitrary', 'Type hinting'); | |
} | |
args[i] = arbs[i].arbitrary(); | |
} | |
E.current.set( args, prop ); | |
} | |
} | |
} | |
/** | |
* Test | |
*/ | |
(function() { | |
var C = JsCheck, A = Arbitrary; | |
C.quickCheck( | |
function( x, y ) { | |
return where( x != y, function(){ | |
return x - y != y - x; | |
}); | |
}, types(A.Integer, A.Integer)); | |
var list = "abcdefghijklmnopqrstuvwxyz"; | |
C.quickCheck( | |
C.forAll( elements_of( list ), function( x ) { | |
return -1 < list.indexOf( x ); | |
})); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment