(* TEST

include dynlink
libraries = ""
readonly_files = "sheep.mli sheep.ml pig.mli"
subdirectories = "plugin1 plugin2 plugin2b plugin2c plugin3 plugin4 \
  plugin5 plugin6"

* shared-libraries
** setup-ocamlc.byte-build-env
*** ocamlc.byte
module = "sheep.mli"
**** ocamlc.byte
module = "sheep.ml"
***** ocamlc.byte
module = "pig.mli"
****** ocamlc.byte
module = "test.ml"
******* ocamlc.byte
module = "plugin1/sheep.mli"
******** ocamlc.byte
flags = "-I plugin1"
module = "plugin1/sheep.ml"
********* ocamlc.byte
flags = ""
module = "plugin2/cow.mli"
********** ocamlc.byte
flags = "-I plugin2"
module = "plugin2/cow.ml"
*********** ocamlc.byte
flags = ""
module = "plugin2b/cow.mli"
************ ocamlc.byte
flags = "-I plugin2b"
module = "plugin2b/cow.ml"
************* ocamlc.byte
flags = ""
module = "plugin2c/cow.mli"
************** ocamlc.byte
flags = "-I plugin2c"
module = "plugin2c/cow.ml"
*************** ocamlc.byte
flags = ""
module = "plugin3/pig.mli"
**************** ocamlc.byte
flags = "-I plugin3"
module = "plugin3/pig.ml"
***************** ocamlc.byte
flags = ""
module = "plugin4/chicken.mli"
****************** ocamlc.byte
flags = "-I plugin4"
module = "plugin4/chicken.ml"
******************* ocamlc.byte
flags = ""
module = "plugin5/chicken.mli"
******************** ocamlc.byte
flags = "-I plugin5"
module = "plugin5/chicken.ml"
********************* ocamlc.byte
flags = ""
module = "plugin6/pheasant.mli"
********************** ocamlc.byte
flags = "-I plugin6"
module = "plugin6/pheasant.ml"
*********************** ocamlc.byte
flags = ""
module = "plugin6/partridge.mli"
************************ ocamlc.byte
flags = "-I plugin6"
module = "plugin6/partridge.ml"
************************* ocamlc.byte
flags = ""
program = "./test.byte.exe"
libraries = "dynlink"
all_modules = "sheep.cmo test.cmo"
module = ""
************************** run

** native-dynlink
*** setup-ocamlopt.byte-build-env
**** ocamlopt.byte
module = "sheep.mli"
***** ocamlopt.byte
module = "sheep.ml"
****** ocamlopt.byte
module = "pig.mli"
******* ocamlopt.byte
module = "test.ml"
******** ocamlopt.byte
flags = ""
module = "plugin1/sheep.mli"
********* ocamlopt.byte
program = "plugin1/sheep.cmxs"
flags = "-I plugin1 -shared"
module = ""
all_modules = "plugin1/sheep.ml"
********** ocamlopt.byte
flags = ""
module = "plugin2/cow.mli"
*********** ocamlopt.byte
program = "plugin2/cow.cmxs"
flags = "-I plugin2 -shared"
module = ""
all_modules = "plugin2/cow.ml"
************ ocamlopt.byte
flags = ""
module = "plugin2b/cow.mli"
************* ocamlopt.byte
program = "plugin2b/cow.cmxs"
flags = "-I plugin2b -shared"
module = ""
all_modules = "plugin2b/cow.ml"
************** ocamlopt.byte
flags = ""
module = "plugin2c/cow.mli"
*************** ocamlopt.byte
program = "plugin2c/cow.cmxs"
flags = "-I plugin2c -shared"
module = ""
all_modules = "plugin2c/cow.ml"
**************** ocamlopt.byte
flags = ""
module = "plugin3/pig.mli"
***************** ocamlopt.byte
program = "plugin3/pig.cmxs"
flags = "-I plugin3 -shared"
module = ""
all_modules = "plugin3/pig.ml"
****************** ocamlopt.byte
flags = ""
module = "plugin4/chicken.mli"
******************* ocamlopt.byte
program = "plugin4/chicken.cmxs"
flags = "-I plugin4 -shared"
module = ""
all_modules = "plugin4/chicken.ml"
******************** ocamlopt.byte
flags = ""
module = "plugin5/chicken.mli"
********************* ocamlopt.byte
program = "plugin5/chicken.cmxs"
flags = "-I plugin5 -shared"
module = ""
all_modules = "plugin5/chicken.ml"
********************** ocamlopt.byte
flags = ""
module = "plugin6/pheasant.mli"
*********************** ocamlopt.byte
program = "plugin6/pheasant.cmxs"
flags = "-I plugin6 -shared"
module = ""
all_modules = "plugin6/pheasant.ml"
************************ ocamlopt.byte
flags = ""
module = "plugin6/partridge.mli"
************************* ocamlopt.byte
program = "plugin6/partridge.cmxs"
flags = "-I plugin6 -shared"
module = ""
all_modules = "plugin6/partridge.ml"
************************** ocamlopt.byte
flags = ""
program = "./test.opt.exe"
libraries = "dynlink"
all_modules = "sheep.cmx test.cmx"
*************************** run
*)

let () = Sheep.baa Sheep.s (* Use Sheep module *)
let _ = fun (x : Pig.t) -> x (* Reference Pig module *)

(* Test that a privately loaded module cannot have the same name as a
   module in the program. *)
let test_sheep () =
  match
    if Dynlink.is_native then
      Dynlink.loadfile_private "plugin1/sheep.cmxs"
    else
      Dynlink.loadfile_private "plugin1/sheep.cmo"
  with
  | () -> assert false
  | exception Dynlink.Error (
      Dynlink.Module_already_loaded "Sheep") -> ()

(* Test repeated loading of a privately-loaded module. *)
let test_cow_repeated () =
  if Dynlink.is_native then
    Dynlink.loadfile_private "plugin2/cow.cmxs"
  else
    Dynlink.loadfile_private "plugin2/cow.cmo"

(* Test that a privately loaded module can have the same name as a
   previous privately loaded module, in the case where the interfaces are
   the same, but the implementations differ. *)
let test_cow_same_name_same_mli () =
  if Dynlink.is_native then
    Dynlink.loadfile_private "plugin2b/cow.cmxs"
  else
    Dynlink.loadfile_private "plugin2b/cow.cmo"

(* Test that a privately loaded module can have the same name as a
   previous privately loaded module, in the case where neither the interfaces
   nor the implementations are the same. *)
let test_cow_same_name_different_mli () =
  if Dynlink.is_native then
    Dynlink.loadfile_private "plugin2c/cow.cmxs"
  else
    Dynlink.loadfile_private "plugin2c/cow.cmo"

(* Test that a privately loaded module cannot have the same name as an
   interface depended on by modules the program. *)
let test_pig () =
  match
    if Dynlink.is_native then
      Dynlink.loadfile_private "plugin3/pig.cmxs"
    else
      Dynlink.loadfile_private "plugin3/pig.cmo"
  with
  | () -> assert false
  | exception Dynlink.Error (
      Dynlink.Private_library_cannot_implement_interface "Pig") -> ()

(* Test that a privately loaded module can recursively load a module of
   the same name. *)
let test_chicken () =
  if Dynlink.is_native then
    Dynlink.loadfile_private "plugin4/chicken.cmxs"
  else
    Dynlink.loadfile_private "plugin4/chicken.cmo"

(* Test that a public load of a module M inside a privately-loaded module,
   followed by a public load of M, causes an error. *)
let test_pheasant () =
  begin
    if Dynlink.is_native then
      Dynlink.loadfile_private "plugin6/pheasant.cmxs"
    else
      Dynlink.loadfile_private "plugin6/pheasant.cmo"
  end;
  match
    if Dynlink.is_native then
      Dynlink.loadfile "plugin6/partridge.cmxs"
    else
      Dynlink.loadfile "plugin6/partridge.cmo"
  with
  | () -> assert false
  | exception Dynlink.Error (
      Dynlink.Module_already_loaded "Partridge") -> ()

let debug_runtime = String.equal (Sys.runtime_variant ()) "d"

(* test_cow_repeated is disabled when running with the debug runtime.
   Reloading multiple times a module can cause an already initialized block
   to be overwritten. See https://github.com/ocaml/ocaml/issues/11016 *)
let () =
  test_sheep ();
  if (not debug_runtime) then (
    test_cow_repeated ();
    test_cow_repeated ()
  );
  test_cow_same_name_same_mli ();
  test_cow_same_name_different_mli ();
  test_pig ();
  test_chicken ();
  test_pheasant ()
