(* 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;
   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 = "";
   ocamlc.byte;
   run;
 }{
   native-dynlink;
   setup-ocamlopt.byte-build-env;
   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";
   ocamlopt.byte;
   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 ()
