blob: f03e25e0b109cf02003d406fc35e3af11ce545b6 (
plain) (
tree)
|
|
open Alcotest
open Rnt
module Beam = Subsystem.Karuta.Beam
let fixtures = "integration/beam"
(* rnt and shelf boot with the node; library is only ever loaded from
the images that carry it. *)
let booted = ["rnt"; "shelf"]
let carried = ["library"]
type node = {root: Protocols.Handle.t; image: string -> string}
let term image module_ function_ heading =
let buffer = Buffer.create 64 in
let binary s =
Buffer.add_uint8 buffer 109;
Buffer.add_int32_be buffer (Int32.of_int (String.length s));
Buffer.add_string buffer s
in
Buffer.add_uint8 buffer 131;
Buffer.add_uint8 buffer 104;
Buffer.add_uint8 buffer 4;
binary module_;
binary function_;
binary (image module_);
Buffer.add_uint8 buffer 116;
Buffer.add_int32_be buffer (Int32.of_int (List.length heading));
List.iter (fun name -> binary name; binary "string") heading;
Buffer.contents buffer
(* \system\evaluator\fol, so the node can define FOL programs. *)
let mount_fol root =
let open Utilities.Result in
let evaluators = Kernel.Namespace.make () in
let system = Kernel.Namespace.make () in
let* _ =
Kernel.Path.update evaluators Kernel.Path.this "fol" None (Some (Evaluators.FOL.make ()))
in
let* _ = Kernel.Path.update system Kernel.Path.this "evaluator" None (Some evaluators) in
let* _ = Kernel.Path.update root Kernel.Path.this "system" None (Some system) in
Ok ()
let with_node test () =
if Sys.command "command -v erl > /dev/null 2>&1 && command -v erlc > /dev/null 2>&1" <> 0 then
skip ();
Sys.set_signal Sys.sigpipe Sys.Signal_ignore;
Helpers.with_temporary_directory "beam" (fun directory ->
let boot = Filename.concat directory "boot" and images = Filename.concat directory "images" in
let compile into modules =
Unix.mkdir into 0o755;
let sources = List.map (fun m -> Filename.concat fixtures (m ^ ".erl")) modules in
if Sys.command (Filename.quote_command "erlc" (["-o"; into] @ sources)) <> 0 then
fail "the fixtures did not compile"
in
compile boot booted;
compile images carried;
let image module_ =
let at = if List.mem module_ booted then boot else images in
In_channel.with_open_bin (Filename.concat at (module_ ^ ".beam")) In_channel.input_all
in
let root = Kernel.Namespace.make () in
Fun.protect
~finally:(fun () -> Protocols.Handle.release root)
(fun () ->
Helpers.condition_as_failure (mount_fol root);
Helpers.condition_as_failure (Beam.register root [boot]);
test {root; image} ) )
let run {root; image} module_ function_ heading =
let open Utilities.Result in
let* program =
Kernel.Program.load root ~evaluator:"karuta:beam" ~source:(term image module_ function_ heading)
in
let* executable = Protocols.Executable.require program in
Protocols.Executable.invoke executable [] ~within:root
let titles batch =
BatFingerTree.to_list batch.Protocols.Cursor.tuples
|> List.map (fun {Concepts.Tuple.attributes; _} ->
Concepts.Value.to_string (BatMap.String.find "title" attributes) )
let reads_in_batches node =
let batches =
begin
let open Utilities.Result in
let* relation = run node "library" "books" ["title"; "author"] in
Protocols.Handle.releasing relation (fun () ->
let* heading = Protocols.Relation.heading relation in
check (list string) "the heading the term declares" ["author"; "title"]
(BatMap.String.bindings heading |> List.map fst);
Protocols.Enumerable.scan relation (fun scan ->
let rec pull read =
let* batch = Protocols.Cursor.fetch scan 4 in
let read = (titles batch, batch.exhausted) :: read in
if batch.exhausted then Ok (List.rev read) else pull read
in
pull [] ) )
end
|> Helpers.condition_as_failure
in
check
(list (pair (list string) bool))
"four, then the rest"
[ ( [ "Structure and Interpretation of Computer Programs";
"The Art of Computer Programming";
"An Introduction to Database Systems";
"Less Than Words Can Say" ],
false );
["The Graves of Academe"; "The Leaning Tower of Babel"; "The Gift of Fire"], true ]
batches
let reads_back_through_the_kernel ({root; image} as node) =
begin
let open Utilities.Result in
let* books =
Kernel.Program.load root ~evaluator:"karuta:beam"
~source:(term image "library" "books" ["title"; "author"])
in
let* bound =
Kernel.Path.update root Kernel.Path.("karuta" @/ "program" @/ this) "books" None (Some books)
in
check bool "the books are defined beside what the node defined" true bound;
let* relation = run node "shelf" "titles" ["title"] in
Protocols.Handle.releasing relation (fun () -> Helpers.titles relation)
end
|> Helpers.condition_as_failure
|> check (list Helpers.value) "the node read its FOL program over the books"
(List.map
(fun title -> Concepts.Value.String title)
[ "An Introduction to Database Systems";
"Less Than Words Can Say";
"Structure and Interpretation of Computer Programs";
"The Art of Computer Programming";
"The Gift of Fire";
"The Graves of Academe";
"The Leaning Tower of Babel" ] )
let reads_a_program_of_no_path ({root; image} as node) =
begin
let open Utilities.Result in
let* books =
Kernel.Program.load root ~evaluator:"karuta:beam"
~source:(term image "library" "books" ["title"; "author"])
in
let* _ =
Kernel.Path.update root Kernel.Path.("karuta" @/ "program" @/ this) "books" None (Some books)
in
let* relation = run node "shelf" "authors" ["author"] in
Protocols.Handle.releasing relation (fun () -> Protocols.Relation.read relation)
end
|> Helpers.condition_as_failure
|> snd
|> List.map (fun {Concepts.Tuple.attributes; _} -> BatMap.String.find "author" attributes)
|> List.sort compare
|> check (list Helpers.value) "each author once"
(List.map
(fun author -> Concepts.Value.String author)
["C. J. Date"; "Donald Knuth"; "Harold Abelson"; "Richard Mitchell"] )
let a_missing_function_fails node =
Helpers.refuses "a missing function fails the call" "beam-failed"
(Result.bind (run node "library" "nope" ["title"]) (fun relation ->
Protocols.Handle.releasing relation (fun () -> Protocols.Relation.read relation) ) )
let a_malformed_term_is_refused {root; _} =
Helpers.refuses "a term that is not a program is refused" "malformed-beam-program"
(Kernel.Program.load root ~evaluator:"karuta:beam" ~source:"not a term")
let suites () =
[ ( "integration/karuta",
[ test_case "reads-in-batches" `Quick (with_node reads_in_batches);
test_case "reads-back-through-the-kernel" `Quick (with_node reads_back_through_the_kernel);
test_case "reads-a-program-of-no-path" `Quick (with_node reads_a_program_of_no_path);
test_case "a-missing-function-fails" `Quick (with_node a_missing_function_fails);
test_case "a-malformed-term-is-refused" `Quick (with_node a_malformed_term_is_refused) ] )
]
|