|
| 1 | +(* This file is part of Lwt, released under the MIT license. See LICENSE.md for |
| 2 | + details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) |
| 3 | + |
| 4 | +exception Skip |
| 5 | + |
| 6 | +type test = { |
| 7 | + test_name : string; |
| 8 | + skip_if_this_is_false : unit -> bool; |
| 9 | + sequential : bool; (* Sequential is ignored in Alcotest *) |
| 10 | + run : [`Lwt of unit -> bool Lwt.t | `Direct of unit -> bool ]; |
| 11 | + } |
| 12 | + |
| 13 | +let test_direct test_name ?(only_if = fun () -> true) run = |
| 14 | + { test_name; skip_if_this_is_false = only_if; sequential = false; run = `Direct run; } |
| 15 | +
|
| 16 | +let test test_name ?(only_if = fun () -> true) ?(sequential = false) run = |
| 17 | + { test_name; skip_if_this_is_false = only_if; sequential; run = `Lwt run; } |
| 18 | +
|
| 19 | +
|
| 20 | +(* Alcotest_lwt 1.5.0 |
| 21 | + * |
| 22 | + * Copyright (c) 2017 Thomas Gazagnaire <thomas@gazagnaire.org> |
| 23 | + * |
| 24 | + * Permission to use, copy, modify, and distribute this software for any |
| 25 | + * purpose with or without fee is hereby granted, provided that the above |
| 26 | + * copyright notice and this permission notice appear in all copies. |
| 27 | + * |
| 28 | + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 29 | + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 30 | + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 31 | + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 32 | + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 33 | + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 34 | + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 35 | +*) |
| 36 | +
|
| 37 | +module Alcotest_lwt_intf = struct |
| 38 | + module type V1 = sig |
| 39 | + include Alcotest_engine.V1.Cli.S with type return = unit Lwt.t |
| 40 | +
|
| 41 | + val test_case : |
| 42 | + string -> |
| 43 | + Alcotest.speed_level -> |
| 44 | + (Lwt_switch.t -> 'a -> unit Lwt.t) -> |
| 45 | + 'a test_case |
| 46 | +
|
| 47 | + val test_case_sync : |
| 48 | + string -> Alcotest.speed_level -> ('a -> unit) -> 'a test_case |
| 49 | + end |
| 50 | +
|
| 51 | + module type Alcotest_lwt = sig |
| 52 | + include V1 |
| 53 | +
|
| 54 | + (** {1 Versioned APIs} *) |
| 55 | +
|
| 56 | + module V1 : V1 |
| 57 | + (** An alias of the above API that provides a stability guarantees over major |
| 58 | + version changes. *) |
| 59 | + end |
| 60 | +end |
| 61 | +
|
| 62 | +module Alcotest_lwt : sig |
| 63 | + include Alcotest_lwt_intf.Alcotest_lwt |
| 64 | +end = struct |
| 65 | + let run_test fn args = |
| 66 | + let async_ex, async_waker = Lwt.wait () in |
| 67 | + let handle_exn ex = |
| 68 | + Printf.eprintf "Uncaught async exception: %s\n%s" (Printexc.to_string ex) (Printexc.get_backtrace ()); |
| 69 | + if Lwt.state async_ex = Lwt.Sleep then Lwt.wakeup_exn async_waker ex |
| 70 | + in |
| 71 | + Lwt.async_exception_hook := handle_exn; |
| 72 | + Lwt_switch.with_switch (fun sw -> Lwt.pick [ fn sw args; async_ex ]) |
| 73 | +
|
| 74 | + module V1 = struct |
| 75 | + module Tester = Alcotest_engine.V1.Cli.Make (Alcotest.Unix_platform) (Lwt) |
| 76 | + include Tester |
| 77 | +
|
| 78 | + let test_case_sync n s f = test_case n s (fun x -> Lwt.return (f x)) |
| 79 | + let test_case n s f = test_case n s (run_test f) |
| 80 | + end |
| 81 | +
|
| 82 | + include V1 |
| 83 | +end |
| 84 | +
|
| 85 | +
|
| 86 | +let fold_left_map f accu l = |
| 87 | + let rec aux accu l_accu = function |
| 88 | + | [] -> accu, List.rev l_accu |
| 89 | + | x :: l -> |
| 90 | + let accu, x = f accu x in |
| 91 | + aux accu (x :: l_accu) l in |
| 92 | + aux accu [] l |
| 93 | +
|
| 94 | +let fold_left_mapi f accu l = |
| 95 | + let rec aux i accu l_accu = function |
| 96 | + | [] -> accu, List.rev l_accu |
| 97 | + | x :: l -> |
| 98 | + let accu, x = f i accu x in |
| 99 | + aux (i + 1) accu (x :: l_accu) l in |
| 100 | + aux 0 accu [] l |
| 101 | +
|
| 102 | +
|
| 103 | +open Lwt.Infix |
| 104 | +
|
| 105 | +type suite = { |
| 106 | + suite_name : string; |
| 107 | + suite_tests : unit Alcotest_lwt.test_case list; |
| 108 | + skip_suite_if_this_is_false : unit -> bool; |
| 109 | + skip_indexes : int list; |
| 110 | +} |
| 111 | +
|
| 112 | +let suite name ?(only_if = fun () -> true) tests = |
| 113 | + let to_test_case test = |
| 114 | + match test.run with |
| 115 | + | `Lwt run -> |
| 116 | + Alcotest_lwt.test_case test.test_name `Quick (fun _switch () -> |
| 117 | + run () >|= fun b -> |
| 118 | + Alcotest.(check bool) "success" b true) |
| 119 | + | `Direct run -> |
| 120 | + Alcotest_lwt.test_case_sync test.test_name `Quick (fun () -> |
| 121 | + let b = run () in |
| 122 | + Alcotest.(check bool) "success" b true) |
| 123 | + in |
| 124 | + let skip_indexes, tests = |
| 125 | + fold_left_mapi (fun i skip_indexes test -> |
| 126 | + if test.skip_if_this_is_false () then skip_indexes, to_test_case test |
| 127 | + else i :: skip_indexes, to_test_case test) |
| 128 | + [] (tests : test list) |
| 129 | + in |
| 130 | + {suite_name = name; |
| 131 | + suite_tests = tests; |
| 132 | + skip_suite_if_this_is_false = only_if; |
| 133 | + skip_indexes} |
| 134 | +
|
| 135 | +let run library_name suites = |
| 136 | + let skip = Hashtbl.create 16 in |
| 137 | + let skip_names, tests = |
| 138 | + fold_left_map (fun skip_names suite -> |
| 139 | + if suite.skip_suite_if_this_is_false () then |
| 140 | + begin |
| 141 | + Hashtbl.add skip suite.suite_name suite.skip_indexes; |
| 142 | + skip_names, (suite.suite_name, suite.suite_tests) |
| 143 | + end |
| 144 | + else |
| 145 | + suite.suite_name :: skip_names, (suite.suite_name, suite.suite_tests)) |
| 146 | + [] suites in |
| 147 | + let filter ~name ~index = |
| 148 | + if List.mem name skip_names then `Skip |
| 149 | + else |
| 150 | + let skip_indexes = Hashtbl.find skip name in |
| 151 | + if List.mem index skip_indexes then `Skip |
| 152 | + else `Run |
| 153 | + in |
| 154 | + Alcotest_lwt.run ~filter library_name tests |
| 155 | +
|
| 156 | +let run library_name suites = Lwt_main.run @@ run library_name suites |
| 157 | +let concurrent = run |
| 158 | +
|
| 159 | +let with_async_exception_hook hook f = |
| 160 | + let old_hook = !Lwt.async_exception_hook in |
| 161 | + Lwt.async_exception_hook := hook; |
| 162 | + Lwt.finalize f (fun () -> |
| 163 | + Lwt.async_exception_hook := old_hook; |
| 164 | + Lwt.return ()) |
| 165 | +
|
| 166 | +let instrument = function |
| 167 | + | true -> Printf.ksprintf (fun _s -> true) |
| 168 | + | false -> Printf.ksprintf (fun s -> prerr_endline ("\n" ^ s); false) |
0 commit comments