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 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
|
(***********************************************************************)
(* The OUnit library *)
(* *)
(* Copyright (C) 2002, 2003, 2004, 2005 Maas-Maarten Zeeman. *)
(* All rights reserved. See LICENCE for details. *)
(***********************************************************************)
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 not b then assert_failure msg
let assert_string str =
if not (str = "") then assert_failure str
let assert_equal ?(cmp = ( = )) ?printer ?msg expected actual =
let get_error_string _ =
match printer, msg with
None, None -> "not equal"
| None, Some s -> (Format.sprintf "%s\nnot equal" s)
| Some p, None -> (Format.sprintf "expected: %s but got: %s"
(p expected) (p actual))
| Some p, Some s -> (Format.sprintf "%s\nexpected: %s but got: %s"
s (p expected) (p actual))
in
if not (cmp expected actual) then
assert_failure (get_error_string ())
let raises f =
try
f ();
None
with
e -> Some e
let assert_raises ?msg exn (f: unit -> 'a) =
let pexn = Printexc.to_string in
let get_error_string _ =
let str = Format.sprintf
"expected exception %s, but no exception was not raised." (pexn exn)
in
match msg with
None -> assert_failure str
| Some s -> assert_failure (Format.sprintf "%s\n%s" s str)
in
match raises f with
None -> assert_failure (get_error_string ())
| Some e -> assert_equal ?msg ~printer:pexn exn e
(* 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 test =
match test with
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 node =
match node with
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 l =
let rec rmapi cnt l =
match l with
[] -> []
| h::t -> (f h cnt)::(rmapi (cnt + 1) t)
in
rmapi 0 l
let fold_lefti f accu l =
let rec rfold_lefti cnt accup l =
match l with
[] -> accup
| h::t -> rfold_lefti (cnt + 1) (f accup h cnt) t
in
rfold_lefti 0 accu l
(* Returns all possible paths in the test. The order is from test case
to root
*)
let test_case_paths test =
let rec tcps path test =
match test with
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
(* The possible test results *)
type test_result =
RSuccess of path
| RFailure of path * string
| RError of path * string
let is_success = function
RSuccess _ -> true
| RError _ -> false
| RFailure _ -> false
let is_failure = function
RFailure _ -> true
| RError _ -> false
| RSuccess _ -> false
let is_error = function
RError _ -> true
| RFailure _ -> false
| RSuccess _ -> false
let result_flavour = function
RError _ -> "Error"
| RFailure _ -> "Failure"
| RSuccess _ -> "Success"
let result_path = function
RSuccess path -> path
| RError (path, _) -> path
| RFailure (path, _) -> path
let result_msg = function
RSuccess _ -> "Success"
| RError (_, msg) -> msg
| RFailure (_, msg) -> msg
(* Returns true if the result list contains successes only *)
let rec was_successful results =
match results with
[] -> true
| RSuccess _::t -> was_successful t
| RFailure _::t -> false
| RError _::t -> false
(* Events which can happen during testing *)
type test_event =
EStart of path
| EEnd of path
| EResult of test_result
(* Run all tests, report starts, errors, failures, and return the results *)
let perform_test report test =
let run_test_case f path =
try
f ();
RSuccess path
with
Failure s -> RFailure (path, s)
| s -> RError (path, (Printexc.to_string s))
in
let rec run_test path results test =
match test with
TestCase(f) ->
report (EStart path);
let result = run_test_case f path in
report (EResult result);
report (EEnd path);
result::results
| TestList (tests) ->
fold_lefti
(fun results t cnt -> run_test ((ListItem cnt)::path) results t)
results tests
| TestLabel (label, t) ->
run_test ((Label label)::path) results t
in
run_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 string_of_result = function
RSuccess path ->
if verbose then "ok\n" else "."
| RFailure (path, _) ->
if verbose then "FAIL\n" else "F"
| RError (path, _) ->
if verbose then "ERROR\n" else "E"
in
let report_event = function
EStart p ->
if verbose then printf "%s ... " (string_of_path p)
| EEnd _ -> ()
| EResult result ->
printf "%s@?" (string_of_result result);
in
let print_result_list results =
List.iter
(fun result -> printf "%s\n%s: %s\n\n%s\n%s\n"
separator1
(result_flavour result)
(string_of_path (result_path result))
(result_msg result)
separator2)
results
in
(* Now start the test *)
let running_time, results = time_fun perform_test report_event test in
let errors = List.filter is_error results in
let failures = List.filter is_failure results in
if not verbose then printf "\n";
(* Print test report *)
print_result_list errors;
print_result_list failures;
printf "Ran: %d tests in: %.2f seconds.\n"
(List.length results) running_time;
(* Print final verdict *)
if was_successful results then
printf "OK\n"
else
printf "FAILED: Cases: %d Tried: %d Errors: %d Failures: %d\n"
(test_case_count test) (List.length results)
(List.length errors) (List.length failures);
(* Return the results possibly for further processing *)
results
(* 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
else
result
|