1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
|
(***********************************************************************)
(* The OUnit library *)
(* *)
(* Copyright 2002, 2003, 2004 Maas-Maarten Zeeman. All rights *)
(* reserved. See *)
(* LICENCE for details. *)
(***********************************************************************)
(* $Id: oUnit.ml,v 1.15 2004/07/24 09:40:58 maas Exp $ *)
let bracket set_up f tear_down () =
let fixture = set_up () in
try
f fixture;
tear_down fixture
with
e ->
tear_down fixture;
raise e
let assert_failure msg = failwith ("OUnit: " ^ msg)
let assert_bool msg b =
if b then ()
else
assert_failure msg
let assert_string s =
if s = "" then ()
else
assert_failure s
let assert_equal ?(cmp = ( = )) ?printer ?msg expected actual =
if cmp expected actual then ()
else
match printer, msg with
None, None ->
assert_failure "not equal"
| None, Some s ->
assert_failure (Format.sprintf "@[%s@\nnot equal@]" s)
| Some p, None ->
assert_failure
(Format.sprintf "expected: %s but got: %s" (p expected) (p actual))
| Some p, Some s ->
assert_failure
(Format.sprintf "@[%s@\nexpected: %s but got: %s@]"
s (p expected) (p actual))
let assert_raises ?msg exn (f: unit -> 'a) =
let pexn = Printexc.to_string in
let expected_exception_raised =
try
f (); false
with
| e -> assert_equal ?msg ~printer:pexn exn e; true
in
if not expected_exception_raised then
let err_str =
Format.sprintf "expected exception %s, but no exception was not raised."
(pexn exn)
in
match msg with
None ->
assert_failure err_str
| Some s ->
assert_failure
(Format.sprintf "@[%s@\n%s@]" s err_str)
(* Compare floats up to a given relative error *)
let cmp_float ?(epsilon = 0.00001) a b =
abs_float (a -. b) <= epsilon *. (abs_float a) ||
abs_float (a -. b) <= epsilon *. (abs_float b)
(* Now some handy shorthands *)
let (@?) = assert_bool
(* The type of tests *)
type test =
TestCase of (unit -> unit)
| TestList of test list
| TestLabel of string * test
(* Some shorthands which allows easy test construction *)
let (>:) s t = TestLabel(s, t) (* infix *)
let (>::) s f = TestLabel(s, TestCase(f)) (* infix *)
let (>:::) s l = TestLabel(s, TestList(l)) (* infix *)
(* Return the number of available tests *)
let rec test_case_count = function
TestCase(_) -> 1
| TestLabel(_, t) -> test_case_count t
| TestList(l) -> List.fold_left (fun c t -> c + test_case_count t) 0 l
type node = ListItem of int | Label of string
type path = node list
let string_of_node = function
ListItem n -> (string_of_int n)
| Label s -> s
let string_of_path path =
List.fold_left
(fun a l ->
if a = "" then l
else l ^ ":" ^ a) "" (List.map string_of_node path)
(* Some helper function, they are generally applicable *)
(* Applies function f in turn to each element in list. Function f takes
one element, and integer indicating its location in the list *)
let mapi f =
let rec rmapi i = function [] -> [] | h::t -> (f h i)::(rmapi (i + 1) t) in
rmapi 0
let fold_lefti f accu =
let rec rfold_lefti i accup = function
[] -> accup
| a::l -> rfold_lefti (i + 1) (f accup a i) l
in
rfold_lefti 0 accu
(* Returns all possible paths in the test. The order is from test case
to root
*)
let test_case_paths test =
let rec tcps path = function
TestCase(_) -> [path]
| TestList(tests) ->
List.concat (mapi (fun t i -> tcps ((ListItem i)::path) t) tests)
| TestLabel(l, t) -> tcps ((Label l)::path) t
in
tcps [] test
(* Collects the test statistics *)
type counts = {cases : int; tried : int; errors : int; failures : int;}
let init_counts n = {cases = n; tried = 0; errors = 0; failures = 0;}
let inc_tried c = {cases = c.cases; tried = c.tried + 1;
errors = c.errors; failures = c.failures}
let inc_errors c = {cases = c.cases; tried = c.tried + 1;
errors = c.errors + 1; failures = c.failures}
let inc_failures c = {cases = c.cases; tried = c.tried + 1;
errors = c.errors; failures = c.failures + 1}
let was_successful c = (c.errors = 0) & (c.failures = 0)
let string_of_counts counts =
"Cases: " ^ string_of_int counts.cases ^
" Tried: " ^ string_of_int counts.tried ^
" Errors: " ^ string_of_int counts.errors ^
" Failures: " ^ string_of_int counts.failures
(* Test events *)
type test_event =
EStart of path * counts
| EEnd of path * counts
| ESuccess of path * counts
| EFailure of path * string * counts
| EError of path * string * counts
let get_counts = function
EStart(_, c) -> c
| EEnd(_, c) -> c
| ESuccess(_, c) -> c
| EFailure(_, _, c) -> c
| EError(_, _, c) -> c
(* Run all tests, report starts, errors, failures, and return counts *)
let perform_test report test =
let report_event event = report event; get_counts event in
(* this function reports the results of the test *)
let really_run f path counts =
try
f ();
report_event (ESuccess (path, inc_tried counts))
with
Failure s ->
report_event (EFailure(path, s, inc_failures counts))
| s ->
report_event (EError(path, (Printexc.to_string s), inc_errors counts))
in
(* this function performs all test-cases in the test *)
let rec pt path counts = function
TestCase(f) ->
report (EStart (path, counts));
report_event (EEnd (path, really_run f path counts))
| TestList(tests) ->
fold_lefti (fun cn t i -> pt ((ListItem i)::path) cn t) counts tests
| TestLabel(l, t) -> pt ((Label l)::path) counts t
in
pt [] (init_counts (test_case_count test)) test
(* Function which runs the given function and returns the running time
of the function, and the original result in a tuple *)
let time_fun f x y =
let begin_time = Unix.gettimeofday () in
(Unix.gettimeofday () -. begin_time, f x y)
(* A simple (currently too simple) text based test runner *)
let run_test_tt ?(verbose=false) test =
let printf = Format.printf in
let separator1 =
"======================================================================" in
let separator2 =
"----------------------------------------------------------------------" in
let errors = ref [] in
let failures = ref [] in
let report_event = function
EStart(p, _) -> if verbose then printf "%s ... @?" (string_of_path p)
| ESuccess _ -> if verbose then printf "ok@." else printf ".@?"
| EFailure(path, str, _) ->
failures := (path, str)::!failures;
if verbose then printf "FAIL@." else printf "F@?"
| EError(path, str, _) ->
errors := (path, str)::!errors;
if verbose then printf "ERROR@." else printf "E@?"
| EEnd _ -> ()
in
let print_error_list flavour = function
[] -> ()
| errors -> List.iter
(fun (p, s) -> printf "%s@[%s: %s@\n@\n%s@]@\n%s@."
separator1 flavour (string_of_path p) s separator2)
errors
in
(* Now start the test *)
let running_time, counts = time_fun perform_test report_event test in
(* And print the final report *)
if verbose = false then printf "@.";
print_error_list "ERROR" (List.rev !errors);
print_error_list "FAIL" (List.rev !failures);
printf "Ran: %d tests in: %.2f Seconds@." counts.tried running_time;
if was_successful counts then
printf "OK@."
else
printf "FAILED: %s@." (string_of_counts counts);
counts
(* Call this one from you test suites *)
let run_test_tt_main suite =
let verbose = ref false in
let set_verbose _ = verbose := true in
Arg.parse
[("-verbose", Arg.Unit set_verbose, "Run the test in verbose mode.");]
(fun x -> raise (Arg.Bad ("Bad argument : " ^ x)))
("usage: " ^ Sys.argv.(0) ^ " [-verbose]");
let result = (run_test_tt ~verbose:!verbose suite) in
if not (was_successful result) then
exit 1;
result
|