aboutsummaryrefslogtreecommitdiffstats
path: root/test/unit/kernel/EphemeralRelation.ml
blob: a72409b0fa07be874030363095d326e2cb9b2555 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
open Alcotest

module Concepts = Rnt.Concepts
module Protocols = Rnt.Protocols

(* A derivation over a fixed set of members. It reads nothing and
   computes nothing, so what the tests observe is the kernel object
   rather than any particular evaluator. *)
module Derivation = struct
  type plan = {name: string; members: Concepts.Tuple.t list; declared: Concepts.Mode.t}

  let identity {name; _} = Concepts.Hash.hash_of_bytes (Bytes.of_string name)

  let describe _ =
    Ok
      (BatMap.String.singleton "value"
         { Protocols.Schematics.domain = "integer";
           provenance = [{Protocols.Schematics.source = ["example"]; attribute = "value"}] } )

  let modes {declared; _} = Ok declared

  let generate {members; _} binding =
    Ok
      (Rnt.Kernel.Generator.cursor_of (fun ~yield ->
           List.iter
             (fun member -> if Protocols.Generative.satisfies binding member then yield member)
             members;
           Ok () ) )
end

module Derived = Rnt.Kernel.EphemeralRelation.Make (Derivation)

let member n =
  { Concepts.Tuple.type_ = "example";
    attributes = BatMap.String.singleton "value" (Concepts.Value.Integer n) }

let plan_over cardinality =
  { Derivation.name = "the-derivation";
    members = [member 1; member 2; member 3];
    declared =
      Concepts.Mode.of_list
        [Concepts.Mode.enumerable cardinality; Concepts.Mode.decides_when ["value"]] }

let derive plan = Derived.derive plan |> Helpers.condition_as_failure

let values tuples =
  BatFingerTree.to_list tuples
  |> List.filter_map (fun tuple ->
         match BatMap.String.find_opt "value" tuple.Concepts.Tuple.attributes with
         | Some (Concepts.Value.Integer n) -> Some n
         | _ -> None )
  |> List.sort compare

let commit = Concepts.Hash.hash_of_bytes (Bytes.of_string "a-commit")

let identity_is_the_derivation () =
  let plan = plan_over Concepts.Cardinality.Finite in
  check bool "unpinned, the identity of the plan" true
    (Concepts.Hash.hash_equals (Derived.identity plan) (Derivation.identity plan));
  check bool "pinned to a commit, another identity" false
    (Concepts.Hash.hash_equals (Derived.identity plan) (Derived.identity ~pinned:commit plan));
  check bool "the same pin twice is the same identity" true
    (Concepts.Hash.hash_equals
       (Derived.identity ~pinned:commit plan)
       (Derived.identity ~pinned:commit plan) )

let an_exhaustible_derivation_enumerates () =
  let derived = derive (plan_over Concepts.Cardinality.Finite) in
  let enumerated =
    begin
      let open Rnt.Utilities.Result in
      let* enumerable = Protocols.Enumerable.require derived 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;
      Ok (values tuples)
    end
    |> Helpers.condition_as_failure
  in
  check (list int) "every member" [1; 2; 3] enumerated

let a_countable_derivation_decides_only () =
  let derived = derive (plan_over Concepts.Cardinality.Countable) in
  let holds, rejects =
    begin
      let open Rnt.Utilities.Result in
      let* relation = Protocols.Relation.require derived in
      let* holds = Protocols.Relation.contains relation (member 2) in
      let* rejects = Protocols.Relation.contains relation (member 9) in
      Ok (holds, rejects)
    end
    |> Helpers.condition_as_failure
  in
  check bool "no enumeration is offered" false
    (Option.is_some (Protocols.Enumerable.from derived));
  check bool "membership is still decided" true holds;
  check bool "and still refused" false rejects

let a_derivation_describes_itself () =
  let derived = derive (plan_over Concepts.Cardinality.Finite) in
  let described =
    begin
      let open Rnt.Utilities.Result in
      let* schematics = Protocols.Handle.require Protocols.Schematics.from derived in
      Protocols.Schematics.describe schematics
    end
    |> Helpers.condition_as_failure
  in
  match described with
  | Protocols.Schematics.Relation description ->
      check (list string) "the attributes of the result" ["value"]
        (BatMap.String.keys description |> BatList.of_enum);
      check (list string) "and where each was drawn from" ["example/value"]
        ( BatMap.String.values description
        |> BatList.of_enum
        |> List.concat_map (fun attribute ->
               attribute.Protocols.Schematics.provenance
               |> List.map (fun origin ->
                      String.concat "/" origin.Protocols.Schematics.source
                      ^ "/" ^ origin.Protocols.Schematics.attribute ) ) )
  | _ -> fail "a derived relation describes itself as a relation"

let releasing_a_derivation_releases_its_inputs () =
  let released = ref false in
  let input =
    object
      method reference = true
      method release = released := true
      method hash = Concepts.Hash.hash_of_int 0
      method protocols : Protocols.Handle.protocol list = []
    end
  in
  let edge = Protocols.Handle.make input in
  let derived =
    Derived.derive ~edges:[edge] (plan_over Concepts.Cardinality.Finite)
    |> Helpers.condition_as_failure
  in
  check bool "an input is held while the derivation lives" false !released;
  Protocols.Handle.release derived;
  check bool "and released with it" true !released

let suites () =
  [ ( "kernel/ephemeral-relation",
      [ test_case "identity-is-the-derivation" `Quick identity_is_the_derivation;
        test_case "an-exhaustible-derivation-enumerates" `Quick
          an_exhaustible_derivation_enumerates;
        test_case "a-countable-derivation-decides-only" `Quick a_countable_derivation_decides_only;
        test_case "a-derivation-describes-itself" `Quick a_derivation_describes_itself;
        test_case "releasing-a-derivation-releases-its-inputs" `Quick
          releasing_a_derivation_releases_its_inputs ] ) ]