(* TEST
*)

let debug = false

open Printf
open Ephemeron

let is_true test s b = printf "%s %s: %s\n" test s (if b then "OK" else "FAIL")
let is_false test s b = is_true test s (not b)

let final r v = Gc.finalise_last (fun () -> r := false) v

let is_key_value test (key_alive, _) = is_true test "key set" !key_alive
let is_data_value test (_, data_alive) = is_true test "data set" !data_alive

let is_key_unset test (key_alive, _) = is_false test "key unset" !key_alive
let is_data_unset test (_, data_alive) = is_false test "data unset" !data_alive

let make_ra () = ref (ref 1) [@@inline never]
let make_rb () = ref (ref (ref 2)) [@@inline never]
let ra = make_ra ()
let rb = make_rb ()

let create key data =
  let key_alive = ref true in
  let data_alive = ref true in
  let eph = K1.make key data in
  final key_alive key;
  final data_alive data;
  (eph, (key_alive, data_alive))

(** test: key alive data dangling *)
let test1 () =
  let test = "test1" in
  Gc.minor ();
  Gc.full_major ();
  let (eph, flags) = create !ra (ref 42) in
  is_key_value test flags;
  is_data_value test flags;
  Gc.minor ();
  is_key_value test flags;
  is_data_value test flags;
  Gc.full_major ();
  is_key_value test flags;
  is_data_value test flags;
  ra := ref 12;
  Gc.full_major ();
  is_key_unset test flags;
  is_data_unset test flags;
  ignore (Sys.opaque_identity eph)
let () = (test1 [@inlined never]) ()

(** test: key dangling data dangling *)
let test2 () =
  let test = "test2" in
  Gc.minor ();
  Gc.full_major ();
  let (eph, flags) = create (ref 125) (ref 42) in
  is_key_value test flags;
  is_data_value test flags;
  ra := ref 13;
  Gc.full_major ();
  is_key_unset test flags;
  is_data_unset test flags;
  ignore (Sys.opaque_identity eph)
let () = (test2 [@inlined never]) ()

(** test: key dangling data alive *)
let test3 () =
  let test = "test3" in
  Gc.minor ();
  Gc.full_major ();
  let (eph, flags) = create (ref 125) !ra in
  is_key_value test flags;
  is_data_value test flags;
  Gc.full_major ();
  ra := ref 14;
  is_key_unset test flags;
  is_data_value test flags;
  ignore (Sys.opaque_identity eph)
let () = (test3 [@inlined never]) ()

(** test: key alive but one away, data dangling *)
let test4 () =
  let test = "test4" in
  Gc.minor ();
  Gc.full_major ();
  rb := ref (ref 3);
  let (eph, flags) = create !(!rb) (ref 43) in
  is_key_value test flags;
  is_data_value test flags;
  Gc.minor ();
  Gc.minor ();
  is_key_value test flags;
  is_data_value test flags;
  ignore (Sys.opaque_identity eph)
let () = (test4 [@inlined never]) ()

(** test: key dangling but one away, data dangling *)
let test5 () =
  let test = "test5" in
  Gc.minor ();
  Gc.full_major ();
  rb := ref (ref 3);
  let (eph, flags) = create !(!rb) (ref 43) in
  is_key_value test flags;
  is_data_value test flags;
  !rb := ref 4;
  Gc.full_major ();
  is_key_unset test flags;
  is_data_unset test flags;
  ignore (Sys.opaque_identity eph)
let () = (test5 [@inlined never]) ()

(** test: key accessible from data but all dangling *)
let test6 () =
  let test = "test6" in
  Gc.minor ();
  Gc.full_major ();
  rb := ref (ref 3);
  let (eph, flags) = create !(!rb) (ref !(!rb)) in
  Gc.minor ();
  is_key_value test flags;
  !rb := ref 4;
  Gc.full_major ();
  is_key_unset test flags;
  is_data_unset test flags;
  ignore (Sys.opaque_identity eph)
let () = (test6 [@inlined never]) ()

(** test: ephemeron accessible from data but they are dangling *)
type t =
  | No
  | Ephe of (int ref, t ref) K1.t

let make_rc () = ref (ref No) [@@inline never]
let rc = make_rc ()

let test7 () =
  let test = "test7" in
  Gc.minor ();
  Gc.full_major ();
  ra := ref 42;
  let weak : t ref Weak.t = Weak.create 1 in
  let eph = ref (K1.make !ra !rc) in
  !rc := Ephe !eph;
  Weak.set weak 0 (Some !rc);
  Gc.minor ();
  is_true test "before" (Weak.check weak 0);
  eph := K1.make (ref 0) (ref No);
  rc := ref No;
  Gc.full_major ();
  Gc.full_major ();
  Gc.full_major ();
  is_false test "after" (Weak.check weak 0)
let () = (test7 [@inlined never]) ()
