(* This file is part of Lwt, released under the MIT license. See LICENSE.md for details, or visit https://github.com/ocsigen/lwt/blob/master/LICENSE.md. *) open Test open Lwt.Infix (* Note: due to the time delays in the tests of this suite, it could really benefit from an option to run tests in parallel. *) let suite = suite "Lwt_timeout" [ test "basic" begin fun () -> let p, r = Lwt.wait () in let start_time = Unix.gettimeofday () in let timeout = Lwt_timeout.create 1 (fun () -> let delta = Unix.gettimeofday () -. start_time in Lwt.wakeup_later r delta) in Lwt_timeout.start timeout; p >|= fun delta -> instrument (delta >= 2. && delta < 3.) "Lwt_timeout: basic: %f %f" start_time delta (* The above is a bug of the current implementation: it always gives too long a timeout. *) end; test "not started" begin fun () -> let p, r = Lwt.wait () in Lwt_timeout.create 1 (fun () -> Lwt.wakeup_later r false) |> ignore; Lwt.async (fun () -> Lwt_unix.sleep 3. >|= fun () -> Lwt.wakeup_later r true); p end; test "double start" begin fun () -> let completions = ref 0 in let timeout = Lwt_timeout.create 1 (fun () -> completions := !completions + 1) in Lwt_timeout.start timeout; Lwt_timeout.start timeout; Lwt_unix.sleep 3. >|= fun () -> instrument (!completions = 1) "Lwt_timeout: double start: %i" !completions end; test "restart" begin fun () -> let p, r = Lwt.wait () in let completions = ref 0 in (* A dummy timeout, just to set up the reference. *) let timeout = ref (Lwt_timeout.create 1 ignore) in timeout := Lwt_timeout.create 1 (fun () -> completions := !completions + 1; if !completions < 2 then Lwt_timeout.start !timeout else Lwt.wakeup_later r true); Lwt_timeout.start !timeout; p end; test "stop" begin fun () -> let p, r = Lwt.wait () in let timeout = Lwt_timeout.create 1 (fun () -> Lwt.wakeup_later r false) in Lwt_timeout.start timeout; Lwt_timeout.stop timeout; Lwt.async (fun () -> Lwt_unix.sleep 3. >|= fun () -> Lwt.wakeup_later r true); p end; test "stop when not stopped" begin fun () -> Lwt_timeout.create 1 ignore |> Lwt_timeout.stop; Lwt.return_true end; test "invalid delay" begin fun () -> try ignore (Lwt_timeout.create 0 ignore); Lwt.return_false with Invalid_argument _ -> Lwt.return_true end; test "change" begin fun () -> let p, r = Lwt.wait () in let start_time = Unix.gettimeofday () in let timeout = Lwt_timeout.create 5 (fun () -> let delta = Unix.gettimeofday () -. start_time in Lwt.wakeup_later r delta) in Lwt_timeout.change timeout 1; Lwt_timeout.start timeout; p >|= fun delta -> instrument (delta >= 1.9 && delta < 3.1) "Lwt_timeout: change: %f %f" start_time delta end; test "change does not start" begin fun () -> let p, r = Lwt.wait () in let timeout = Lwt_timeout.create 1 (fun () -> Lwt.wakeup_later r false) in Lwt_timeout.change timeout 1; Lwt.async (fun () -> Lwt_unix.sleep 3. >|= fun () -> Lwt.wakeup_later r true); p end; test "change after start" begin fun () -> let p, r = Lwt.wait () in let start_time = Unix.gettimeofday () in let timeout = Lwt_timeout.create 5 (fun () -> let delta = Unix.gettimeofday () -. start_time in Lwt.wakeup_later r delta) in Lwt_timeout.start timeout; Lwt_timeout.change timeout 1; p >|= fun delta -> instrument (delta >= 1.9 && delta < 3.1) "Lwt_timeout: change after start: %f %f" start_time delta end; test "change: invalid delay" begin fun () -> let timeout = (Lwt_timeout.create 1 ignore) in try Lwt_timeout.change timeout 0; Lwt.return_false with Invalid_argument _ -> Lwt.return_true end; test ~sequential:true "exception in action" begin fun () -> let p, r = Lwt.wait () in Test.with_async_exception_hook (fun exn -> match exn with | Exit -> Lwt.wakeup_later r true | _ -> raise exn) (fun () -> Lwt_timeout.create 1 (fun () -> raise Exit) |> Lwt_timeout.start; p) end; test "set_exn_handler" begin fun () -> let p, r = Lwt.wait () in Lwt_timeout.set_exn_handler (fun exn -> match exn with | Exit -> Lwt.wakeup_later r true | _ -> raise exn); Lwt_timeout.create 1 (fun () -> raise Exit) |> Lwt_timeout.start; p >|= fun result -> Lwt_timeout.set_exn_handler (fun exn -> !Lwt.async_exception_hook exn); result end; test "two" begin fun () -> let p1, r1 = Lwt.wait () in let p2, r2 = Lwt.wait () in let start_time = Unix.gettimeofday () in Lwt_timeout.create 1 (fun () -> let delta = Unix.gettimeofday () -. start_time in Lwt.wakeup r1 delta) |> Lwt_timeout.start; Lwt_timeout.create 2 (fun () -> let delta = Unix.gettimeofday () -. start_time in Lwt.wakeup r2 delta) |> Lwt_timeout.start; p1 >>= fun delta1 -> p2 >|= fun delta2 -> instrument (delta1 >= 1.9 && delta1 < 3. && delta2 >= 2.9 && delta2 < 4.) "Lwt_timeout: two: %f %f %f" start_time delta1 delta2 end; test "simultaneous" begin fun () -> let p1, r1 = Lwt.wait () in let p2, r2 = Lwt.wait () in let start_time = Unix.gettimeofday () in Lwt_timeout.create 1 (fun () -> let delta = Unix.gettimeofday () -. start_time in Lwt.wakeup r1 delta) |> Lwt_timeout.start; Lwt_timeout.create 1 (fun () -> let delta = Unix.gettimeofday () -. start_time in Lwt.wakeup r2 delta) |> Lwt_timeout.start; p1 >>= fun delta1 -> p2 >|= fun delta2 -> instrument (delta1 >= 1. && delta1 < 2.6 && delta2 >= 1. && delta2 < 2.6) "Lwt_timeout: simultaneous: %f %f %f" start_time delta1 delta2 end; test "two, first stopped" begin fun () -> let p1, r1 = Lwt.wait () in let p2, r2 = Lwt.wait () in let start_time = Unix.gettimeofday () in let timeout1 = Lwt_timeout.create 1 (fun () -> Lwt.wakeup r1 false) in Lwt_timeout.start timeout1; Lwt_timeout.create 2 (fun () -> let delta = Unix.gettimeofday () -. start_time in Lwt.wakeup r2 delta) |> Lwt_timeout.start; Lwt_timeout.stop timeout1; Lwt.async (fun () -> Lwt_unix.sleep 3. >|= fun () -> Lwt.wakeup r1 true); p1 >>= fun timeout1_not_fired -> p2 >|= fun delta2 -> instrument (timeout1_not_fired && delta2 >= 1.5 && delta2 < 3.5) "Lwt_timeout: two, first stopped: %b %f %f" timeout1_not_fired start_time delta2 end; ]