|
|
open Rnt
module Make (S : Abstract.Storage.STORAGE) (C : Helpers.Storage.CONFIGURATOR) = struct
open Alcotest
module H = Helpers.Storage.Make (S) (C)
let alaric =
{ Concepts.Tuple.type_= "employee";
attributes= BatMap.String.empty |> BatMap.String.add "name" (Concepts.Value.String "Alaric")
}
let not_a_directory () =
Concepts.Condition.condition "not-a-directory"
"A handle was expected to carry the directory protocol and did not" Concepts.Condition.empty
let missing name =
Concepts.Condition.condition "missing" "A name was expected in the directory and was absent"
Concepts.Condition.("name" |=| Concepts.Value.String name)
let employee name age =
{ Concepts.Tuple.type_= "employee";
attributes=
BatMap.String.of_list ["name", Concepts.Value.String name; "age", Concepts.Value.Integer age]
}
(* a plan comes resolved, so it reads nothing through [within] *)
let nowhere = Kernel.Prototype.mixture []
let drain result =
let open Utilities.Result in
let* enumerable = Protocols.Enumerable.require result in
let* cursor = Protocols.Enumerable.enumerate enumerable in
let* scan = Protocols.Cursor.require cursor in
let* tuples = Protocols.Cursor.drain scan () in
Protocols.Handle.release cursor;
Protocols.Handle.release result;
Ok (BatFingerTree.to_list tuples)
let scan_a_base_relation conn =
let scanned =
begin
let open Utilities.Result in
let* employee = H.relation conn ["name"] [alaric] in
let root =
Kernel.Prototype.mixture
[ Kernel.Prototype.Directory.of_properties
["employee", employee; "fol", Rnt.Evaluators.FOL.make ()] ]
in
let* directory =
Protocols.Directory.from root |> Option.to_result ~none:(not_a_directory ())
in
let* found = Protocols.Directory.find directory "fol" in
let* evaluator = Option.to_result ~none:(missing "fol") found in
let* evaluator = Protocols.Evaluator.require evaluator in
let* result =
Rnt.Evaluators.FOL.(program (Base employee))
|> Protocols.Evaluator.invoke evaluator ~within:nowhere
in
drain result
end
|> Helpers.condition_as_failure
in
check int "one tuple scanned" 1 (List.length scanned);
check bool "the tuple that was asserted" true
(List.for_all
(fun tuple ->
Concepts.Hash.hash_equals (Concepts.Tuple.hash tuple) (Concepts.Tuple.hash alaric) )
scanned )
let project_away_an_attribute conn =
let projected =
begin
let open Utilities.Result in
let* employees =
H.relation conn ["name"; "age"]
[employee "Alaric" 42; employee "Alaric" 30; employee "Brunhild" 42]
in
let* evaluator = Protocols.Evaluator.require (Rnt.Evaluators.FOL.make ()) in
let* result =
Rnt.Evaluators.FOL.(program (Project (Base employees, BatFingerTree.singleton "name")))
|> Protocols.Evaluator.invoke evaluator ~within:nowhere
in
drain result
end
|> Helpers.condition_as_failure
in
check
(list (list (pair string Helpers.value)))
"each name once, and nothing else"
[["name", Concepts.Value.String "Alaric"]; ["name", Concepts.Value.String "Brunhild"]]
( projected
|> List.map (fun tuple -> BatMap.String.bindings tuple.Concepts.Tuple.attributes)
|> List.sort compare )
let instantiate_a_program conn =
let projected =
begin
let open Utilities.Result in
let* employees = H.relation conn ["name"; "age"] [employee "Alaric" 42] in
let root = Kernel.Prototype.(mixture [Directory.of_properties ["employee", employees]]) in
let* plan =
Rnt.Evaluators.FOL.(
instantiate (Kernel.Path.lookup root)
(Project (Base Kernel.Path.("employee" @/ this), BatFingerTree.singleton "name")) )
in
let* evaluator = Protocols.Evaluator.require (Rnt.Evaluators.FOL.make ()) in
let* result =
Rnt.Evaluators.FOL.program plan |> Protocols.Evaluator.invoke evaluator ~within:nowhere
in
drain result
end
|> Helpers.condition_as_failure
in
check
(list (list (pair string Helpers.value)))
"the program ran over the relation its path resolved to"
[["name", Concepts.Value.String "Alaric"]]
(projected |> List.map (fun tuple -> BatMap.String.bindings tuple.Concepts.Tuple.attributes))
module E = Kernel.EphemeralRelation.Make (S)
let store conn term =
E.persist conn ~name:"view" ~evaluator:Rnt.Evaluators.FOL.name
~code:(Rnt.Evaluators.FOL.encode term)
(BatMap.String.singleton "name" {Protocols.Schematics.domain= "string"; provenance= []})
let namespace entries =
let open Utilities.Result in
let root = Kernel.Namespace.make () in
let* registry = Protocols.Handle.require Protocols.Registry.from root in
let* _ =
List.fold_left
(fun registered (name, handle) ->
let* _ = registered in
Protocols.Registry.update registry name None (Some handle) )
(Ok true) entries
in
Ok root
let expand_a_stored_program conn =
let projected =
begin
let open Utilities.Result in
let* employees = H.relation conn ["name"; "age"] [employee "Alaric" 42] in
let* view =
store conn
Rnt.Evaluators.FOL.(
Project (Base Kernel.Path.("employee" @/ this), BatFingerTree.singleton "name") )
in
let* root = namespace ["employee", employees; "view", view] in
let* plan =
Rnt.Evaluators.FOL.(
instantiate (Kernel.Path.lookup root) (Base Kernel.Path.("view" @/ this)) )
in
let* evaluator = Protocols.Evaluator.require (Rnt.Evaluators.FOL.make ()) in
let* result =
Rnt.Evaluators.FOL.program plan |> Protocols.Evaluator.invoke evaluator ~within:nowhere
in
drain result
end
|> Helpers.condition_as_failure
in
check
(list (list (pair string Helpers.value)))
"the stored program ran in place of its name"
[["name", Concepts.Value.String "Alaric"]]
(projected |> List.map (fun tuple -> BatMap.String.bindings tuple.Concepts.Tuple.attributes))
let run_a_stored_program_within conn =
let projected =
begin
let open Utilities.Result in
let* employees = H.relation conn ["name"; "age"] [employee "Alaric" 42] in
let* view =
store conn
Rnt.Evaluators.FOL.(
Project (Base Kernel.Path.("employee" @/ this), BatFingerTree.singleton "name") )
in
let* root = namespace ["employee", employees] in
let* evaluator = Protocols.Evaluator.require (Rnt.Evaluators.FOL.make ()) in
let* result = Protocols.Evaluator.invoke evaluator view ~within:root in
drain result
end
|> Helpers.condition_as_failure
in
check
(list (list (pair string Helpers.value)))
"the stored source read its relations where it was told to"
[["name", Concepts.Value.String "Alaric"]]
(projected |> List.map (fun tuple -> BatMap.String.bindings tuple.Concepts.Tuple.attributes))
let refuse_a_program_that_reads_itself conn =
let expanded =
begin
let open Utilities.Result in
let* view = store conn Rnt.Evaluators.FOL.(Base Kernel.Path.("view" @/ this)) in
let* root = namespace ["view", view] in
Rnt.Evaluators.FOL.(
instantiate (Kernel.Path.lookup root) (Base Kernel.Path.("view" @/ this)) )
end
in
check bool "the expansion stopped at the cycle" true
( match expanded with
| Error condition ->
BatString.starts_with (Concepts.Condition.to_string_hum condition) "cyclic-program"
| Ok _ -> false )
let pull_one_tuple_at_a_time () =
let produced = ref 0 and closed = ref false in
let description =
BatMap.String.singleton "value" {Protocols.Schematics.domain= "integer"; provenance= []}
in
let rec count ~yield n =
incr produced;
yield
{ Concepts.Tuple.type_= "counter";
attributes= BatMap.String.singleton "value" (Concepts.Value.Integer n) };
count ~yield (n + 1)
in
let source =
Kernel.EphemeralRelation.instantiate description
{Kernel.EphemeralRelation.name= None; schematics= `Temporary description; program= None}
(fun () -> Ok (Kernel.Generator.cursor_of ~finally:(fun () -> closed := true) (count 0)))
in
let first =
begin
let open Utilities.Result in
let* evaluator = Protocols.Evaluator.require (Rnt.Evaluators.FOL.make ()) in
let* result =
Rnt.Evaluators.FOL.(program (Project (Base source, BatFingerTree.singleton "value")))
|> Protocols.Evaluator.invoke evaluator ~within:nowhere
in
let* enumerable = Protocols.Enumerable.require result in
let* cursor = Protocols.Enumerable.enumerate enumerable in
let* scan = Protocols.Cursor.require cursor in
let* first = Protocols.Cursor.next scan in
Protocols.Handle.release cursor; Protocols.Handle.release result; Ok first
end
|> Helpers.condition_as_failure
in
check bool "a tuple came through" true (Option.is_some first);
check int "the source produced only that tuple" 1 !produced;
check bool "and was closed with its consumer" true !closed
let suite =
( "evaluators/fol",
[ test_case "scan-a-base-relation" `Quick (H.with_connection scan_a_base_relation "fol-test");
test_case "project-away-an-attribute" `Quick
(H.with_connection project_away_an_attribute "fol-test");
test_case "instantiate-a-program" `Quick
(H.with_connection instantiate_a_program "fol-test");
test_case "expand-a-stored-program" `Quick
(H.with_connection expand_a_stored_program "fol-test");
test_case "run-a-stored-program-within" `Quick
(H.with_connection run_a_stored_program_within "fol-test");
test_case "refuse-a-program-that-reads-itself" `Quick
(H.with_connection refuse_a_program_that_reads_itself "fol-test");
test_case "pull-one-tuple-at-a-time" `Quick pull_one_tuple_at_a_time ] )
end
module LMDB = Make (Rnt.Backend.Storage.LMDB) (Helpers.Storage.LMDB_Configurator)
let suites () = [LMDB.suite]
|