Skip to content

Commit 2df8a3c

Browse files
Merge pull request #561 from HarlanH/master
tests framework
2 parents 8574723 + 18da0c8 commit 2df8a3c

File tree

3 files changed

+196
-0
lines changed

3 files changed

+196
-0
lines changed

jl/sysimg.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ include("darray.jl")
8484
include("version.jl")
8585
include("util.jl")
8686
include("datafmt.jl")
87+
include("test.jl")
8788

8889
## Load optional external libraries
8990

jl/test.jl

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# test suite functions and macros
2+
3+
# tests
4+
# TODO: re-enable when filesystem tests become available!
5+
# function tests(onestr::String, outputter::Function)
6+
# # if onestr is an existing file, pass it to the primary testing function
7+
# stat = strip(readall(`stat -f "%HT" $onestr`))
8+
# if (stat == "Regular File")
9+
# tests([onestr], outputter)
10+
# elseif (stat == "Directory")
11+
# # if it's a directory name, find all test_*.jl in that and subdirectories, and pass
12+
# # that list
13+
# files_str = strip(readall(`find $onestr -name test_*.jl -print`))
14+
# if (length(files_str) > 0)
15+
# tests(split(files_str, "\n"), outputter)
16+
# else
17+
# # otherwise, throw an error
18+
# error("no test_*.jl files in directory: $onestr")
19+
# end
20+
# end
21+
# end
22+
# tests(onestr::String) = tests(onestr, test_printer_raw)
23+
# tests(fn::Function) = tests(".", fn)
24+
# tests() = tests(".")
25+
26+
tests(onestr::String, outputter::Function) = tests([onestr], outputter)
27+
tests(onestr::String) = tests([onestr], test_printer_raw)
28+
29+
function tests(filenames, outputter::Function)
30+
# run these files as a task
31+
hdl = Task(() -> _tests_task(filenames))
32+
outputter(hdl)
33+
end
34+
tests(filenames) = tests(filenames, test_printer_raw)
35+
36+
function _tests_task(filenames)
37+
for fn = filenames
38+
load(fn)
39+
end
40+
end
41+
42+
# the default printer
43+
function test_printer_raw(hdl::Task)
44+
for t = hdl
45+
if (t.result)
46+
print(".")
47+
else
48+
println("")
49+
show(t) # TODO spiff up
50+
println("")
51+
end
52+
end
53+
end
54+
55+
# things to set state
56+
function test_context(context::String)
57+
tls(:context, context)
58+
end
59+
function test_group(group::String)
60+
tls(:group, group)
61+
end
62+
63+
# data structures
64+
type NoException <: Exception
65+
end
66+
67+
type TestResult
68+
context
69+
group
70+
expr_str::String
71+
result::Bool
72+
elapsed::Float
73+
exception_thrown::Exception
74+
operation
75+
arg1
76+
arg2
77+
arg3
78+
end
79+
TestResult() = TestResult("", "", "", false, NaN, NoException(), Nothing, Nothing, Nothing, Nothing)
80+
81+
# the macro just wraps the expression in a couple layers of quotes, then passes it to a function
82+
# that does the real work
83+
macro test(ex)
84+
quote
85+
$_test(expr(:quote, ex))
86+
end
87+
end
88+
89+
function _test(ex::Expr)
90+
local tr = TestResult()
91+
tr.context = tls(:context)
92+
tr.group = tls(:group)
93+
94+
# unwrap once
95+
ex = eval(ex)
96+
97+
# save the string
98+
tr.expr_str = string(ex)
99+
100+
# eval the whole thing, capturing exceptions and times
101+
try
102+
tr.elapsed = @elapsed tr.result = eval(ex)
103+
catch except
104+
tr.exception_thrown = except
105+
end
106+
107+
# if we failed without an exception, pull apart the expression and see about evaluating
108+
# the parts
109+
if (tr.result == false && tr.exception_thrown == NoException())
110+
if (ex.head == :comparison)
111+
tr.operation = ex.head
112+
tr.arg1 = eval(ex.args[1])
113+
tr.arg2 = eval(ex.args[3])
114+
elseif (ex.head == :call) # is it a helper we know about?
115+
if (ex.args[1] == :approx_eq)
116+
tr.operation = ex.args[1]
117+
tr.arg1 = eval(ex.args[2])
118+
tr.arg2 = eval(ex.args[3])
119+
elseif (ex.args[1] == :prints)
120+
tr.operation = ex.args[1]
121+
tr.arg1 = print_to_string(eval(ex.args[2]), eval(ex.args[3])...)
122+
tr.arg2 = eval(ex.args[4])
123+
end
124+
end
125+
end
126+
127+
# if we're running takes_less_than, see how we did
128+
if (ex.args[1] == :takes_less_than)
129+
tr.result = tr.elapsed < eval(ex.args[3])
130+
tr.operation = ex.args[1]
131+
tr.arg1 = tr.elapsed
132+
tr.arg2 = eval(ex.args[3])
133+
end
134+
135+
136+
produce(tr)
137+
end
138+
139+
# helpful utility tests, supported by the macro
140+
#
141+
142+
function approx_eq(a, b, tol)
143+
abs(a - b) < tol
144+
end
145+
approx_eq(a, b) = approx_eq(a, b, 1e-6)
146+
147+
function prints(fn::Function, args, expected::String)
148+
print_to_string(fn, args...) == expected
149+
end
150+
151+
function takes_less_than(anything, expected)
152+
# the magic happens in _test
153+
true
154+
end
155+

test/test_test.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# test file to test testing
2+
3+
4+
test_context("Testing test tests")
5+
# setup goes here
6+
7+
test_group("string tests")
8+
@test strip("\t hi \n") == "hi"
9+
@test strip("\t this should fail \n") == "hi" #fail
10+
11+
12+
test_group("numeric tests")
13+
@test approx_eq(airy(1.8), 0.0470362)
14+
@test approx_eq(airy(1, 1.8), 1 + -0.0685248) # fail, using helper function
15+
16+
test_group("array tests")
17+
a = Array(Float64, 2, 2, 2, 2, 2)
18+
a[1,1,1,1,1] = 10
19+
@test a[1,1,1,1,1] == 10
20+
@test a[1,1,1,1,1] == 2 # fail
21+
22+
23+
test_group("random tests")
24+
@test rand() != rand() # not very likely to fail
25+
@test rand() == rand() # fail
26+
27+
test_group("exception tests")
28+
@test complex(1,2) > 0 # fail
29+
30+
test_group("printing tests")
31+
@test print_to_string(show, :(1+2)) == "+(1,2)"
32+
@test prints(print_joined, ([1,2,3], " : "), "1 : 2 : 3") # prints is a helper
33+
@test prints(print_joined, ([1,2,3], " ! "), "1 : 2 : 3") # fail
34+
35+
test_group("performance tests")
36+
fib(n) = n < 2 ? n : fib(n-1) + fib(n-2)
37+
@test fib(20) == 6765
38+
@test takes_less_than(fib(20), 1e-6)
39+
40+
# shutdown goes here

0 commit comments

Comments
 (0)