timeseries-julia-python
random julia and python scripts analyzing stock timeseries data
quickcheck.jl
(3105B)
1 # A Julia implementation of QuickCheck, a specification-based tester
2 #
3 # QuickCheck was originally written for Haskell by Koen Claessen and John Hughes
4 # http://www.cse.chalmers.se/~rjmh/QuickCheck/
5
6 module QuickCheck
7
8 export property
9 export condproperty
10 export quantproperty
11
12 function lambda_arg_types(f::Function)
13 if !isa(f.code, LambdaStaticData)
14 error("You must supply either an anonymous function with typed arguments or an array of argument types.")
15 end
16 [eval(var.args[2]) for var in Base.uncompressed_ast(f.code).args[1]]
17 end
18
19 # Simple properties
20 function property(prop::Function, typs::Vector, ntests)
21 arggens = [size -> generator(typ, size) for typ in typs]
22 quantproperty(prop, typs, ntests, arggens...)
23 end
24 property(prop::Function, typs::Vector) = property(prop, typs, 100)
25 property(prop::Function, ntests) = property(prop, lambda_arg_types(prop), ntests)
26 property(prop::Function) = property(prop, 100)
27
28 # Conditional properties
29 function condproperty(prop::Function, typs::Vector, ntests, maxtests, argconds...)
30 arggens = [size -> generator(typ, size) for typ in typs]
31 check_property(prop, arggens, argconds, ntests, maxtests)
32 end
33 condproperty(prop::Function, args...) = condproperty(prop, lambda_arg_types(prop), args...)
34
35 # Quantified properties (custom generators)
36 function quantproperty(prop::Function, typs::Vector, ntests, arggens...)
37 argconds = [(_...)->true for t in typs]
38 check_property(prop, arggens, argconds, ntests, ntests)
39 end
40 quantproperty(prop::Function, args...) = quantproperty(prop, lambda_arg_types(prop), args...)
41
42 function check_property(prop::Function, arggens, argconds, ntests, maxtests)
43 totalTests = 0
44 for i in 1:ntests
45 goodargs = false
46 args = {}
47 while !goodargs
48 totalTests += 1
49 if totalTests > maxtests
50 println("Arguments exhausted after $i tests.")
51 return
52 end
53 args = [arggen(div(i,2)+3) for arggen in arggens]
54 goodargs = all([cond(args...) for cond in argconds])
55 end
56 if !prop(args...)
57 error("Falsifiable, after $i tests:\n$args")
58 end
59 end
60 println("OK, passed $ntests tests.")
61 end
62
63 # Default generators for primitive types
64 generator{T<:Unsigned}(::Type{T}, size) = convert(T, rand(1:size))
65 generator{T<:Signed}(::Type{T}, size) = convert(T, rand(-size:size))
66 generator{T<:FloatingPoint}(::Type{T}, size) = convert(T, (rand()-0.5).*size)
67 # This won't generate interesting UTF-8, but doing that is a Hard Problem
68 generator{T<:String}(::Type{T}, size) = convert(T, randstring(size))
69
70 generator(::Type{Any}, size) = error("Property variables cannot by typed Any.")
71
72 # Generator for array types
73 function generator{T,n}(::Type{Array{T,n}}, size)
74 dims = [rand(1:size) for i in 1:n]
75 reshape([generator(T, size) for x in 1:prod(dims)], dims...)
76 end
77
78 # Generator for composite types
79 function generator{C}(::Type{C}, size)
80 if C.types == ()
81 error("No generator defined for type $C.")
82 end
83 C([generator(T, size) for T in C.types]...)
84 end
85
86 end