this repo has no description
1(** Node.js test for cell dependency system.
2
3 This tests that cell dependencies work correctly, including:
4 - Linear dependencies (c1 → c2 → c3)
5 - Diamond dependencies (c1 → c2, c3 → c4)
6 - Missing dependencies (referencing non-existent cell)
7 - Dependency update propagation
8 - Type shadowing across cells
9*)
10
11open Js_top_worker
12open Impl
13
14(* Flusher that writes to process.stdout in Node.js *)
15let console_flusher (s : string) : unit =
16 let open Js_of_ocaml in
17 let process = Js.Unsafe.get Js.Unsafe.global (Js.string "process") in
18 let stdout = Js.Unsafe.get process (Js.string "stdout") in
19 let write = Js.Unsafe.get stdout (Js.string "write") in
20 ignore (Js.Unsafe.call write stdout [| Js.Unsafe.inject (Js.string s) |])
21
22let capture : (unit -> 'a) -> unit -> Impl.captured * 'a =
23 fun f () ->
24 let stdout_buff = Buffer.create 1024 in
25 let stderr_buff = Buffer.create 1024 in
26 Js_of_ocaml.Sys_js.set_channel_flusher stdout (Buffer.add_string stdout_buff);
27 let x = f () in
28 let captured =
29 {
30 Impl.stdout = Buffer.contents stdout_buff;
31 stderr = Buffer.contents stderr_buff;
32 }
33 in
34 Js_of_ocaml.Sys_js.set_channel_flusher stdout console_flusher;
35 (captured, x)
36
37module S : Impl.S = struct
38 type findlib_t = Js_top_worker_web.Findlibish.t
39
40 let capture = capture
41
42 let sync_get f =
43 let f = Fpath.v ("_opam/" ^ f) in
44 try Some (In_channel.with_open_bin (Fpath.to_string f) In_channel.input_all)
45 with _ -> None
46
47 let async_get f =
48 let f = Fpath.v ("_opam/" ^ f) in
49 try
50 let content =
51 In_channel.with_open_bin (Fpath.to_string f) In_channel.input_all
52 in
53 Lwt.return (Ok content)
54 with e -> Lwt.return (Error (`Msg (Printexc.to_string e)))
55
56 let create_file = Js_of_ocaml.Sys_js.create_file
57
58 let import_scripts urls =
59 let open Js_of_ocaml.Js in
60 let import_scripts_fn = Unsafe.get Unsafe.global (string "importScripts") in
61 List.iter
62 (fun url ->
63 let (_ : 'a) =
64 Unsafe.fun_call import_scripts_fn [| Unsafe.inject (string url) |]
65 in
66 ())
67 urls
68
69 let init_function _ () = failwith "Not implemented"
70 let findlib_init = Js_top_worker_web.Findlibish.init async_get
71
72 let get_stdlib_dcs uri =
73 Js_top_worker_web.Findlibish.fetch_dynamic_cmis sync_get uri
74 |> Result.to_list
75
76 let find_stdlib_dcs v =
77 let pkg = match Js_top_worker_web.Findlibish.find_dcs_url v "stdlib" with
78 | Some _ as r -> r
79 | None -> Js_top_worker_web.Findlibish.find_dcs_url v "ocaml"
80 in
81 match pkg with
82 | Some url ->
83 Js_top_worker_web.Findlibish.fetch_dynamic_cmis sync_get url
84 |> Result.to_list
85 | None -> []
86
87 let require b v = function
88 | [] -> []
89 | packages ->
90 Js_top_worker_web.Findlibish.require ~import_scripts sync_get b v
91 packages
92
93 let path = "/static/cmis"
94end
95
96module U = Impl.Make (S)
97
98(* Test result tracking *)
99let total_tests = ref 0
100let passed_tests = ref 0
101
102let test name check message =
103 incr total_tests;
104 let passed = check in
105 if passed then incr passed_tests;
106 let status = if passed then "PASS" else "FAIL" in
107 Printf.printf "[%s] %s: %s\n%!" status name message
108
109let query_errors env_id cell_id deps source =
110 U.query_errors env_id cell_id deps false source
111
112let _ =
113 Printf.printf "=== Node.js Cell Dependency Tests ===\n\n%!";
114
115 Logs.set_reporter (Logs_fmt.reporter ());
116 Logs.set_level (Some Logs.Warning);
117
118 let ( let* ) m f =
119 let open Lwt in
120 m >>= function
121 | Ok x -> f x
122 | Error e -> return (Error e)
123 in
124
125 let init_config =
126 { stdlib_dcs = None; findlib_requires = []; findlib_index = None; execute = true }
127 in
128
129 let test_sequence =
130 (* Initialize and setup *)
131 let* _ = U.init init_config in
132 let* _ = U.setup "" in
133 test "init" true "Initialized and setup";
134
135 Printf.printf "\n--- Section 1: Linear Dependencies ---\n%!";
136
137 (* c1: base definition *)
138 let* errors = query_errors "" (Some "c1") [] "type t = int;;" in
139 test "linear_c1" (List.length errors = 0)
140 (Printf.sprintf "%d errors" (List.length errors));
141
142 (* c2 depends on c1 *)
143 let* errors = query_errors "" (Some "c2") ["c1"] "let x : t = 42;;" in
144 test "linear_c2" (List.length errors = 0)
145 (Printf.sprintf "%d errors" (List.length errors));
146
147 (* c3 depends on c2 (and transitively c1) *)
148 let* errors = query_errors "" (Some "c3") ["c2"] "let y = x + 1;;" in
149 test "linear_c3" (List.length errors = 0)
150 (Printf.sprintf "%d errors" (List.length errors));
151
152 (* c4 depends on c3 *)
153 let* errors = query_errors "" (Some "c4") ["c3"] "let z = y * 2;;" in
154 test "linear_c4" (List.length errors = 0)
155 (Printf.sprintf "%d errors" (List.length errors));
156
157 Printf.printf "\n--- Section 2: Diamond Dependencies ---\n%!";
158
159 (* d1: base type *)
160 let* errors = query_errors "" (Some "d1") []
161 "type point = { x: int; y: int };;" in
162 test "diamond_d1" (List.length errors = 0)
163 (Printf.sprintf "%d errors" (List.length errors));
164
165 (* d2 depends on d1 *)
166 let* errors = query_errors "" (Some "d2") ["d1"]
167 "let origin : point = { x = 0; y = 0 };;" in
168 test "diamond_d2" (List.length errors = 0)
169 (Printf.sprintf "%d errors" (List.length errors));
170
171 (* d3 depends on d1 (parallel to d2) *)
172 let* errors = query_errors "" (Some "d3") ["d1"]
173 "let unit_x : point = { x = 1; y = 0 };;" in
174 test "diamond_d3" (List.length errors = 0)
175 (Printf.sprintf "%d errors" (List.length errors));
176
177 (* d4 depends on d2, d3, and transitively needs d1 for the point type *)
178 let* errors = query_errors "" (Some "d4") ["d1"; "d2"; "d3"]
179 "let add p1 p2 : point = { x = p1.x + p2.x; y = p1.y + p2.y };;\n\
180 let result = add origin unit_x;;" in
181 test "diamond_d4" (List.length errors = 0)
182 (Printf.sprintf "%d errors" (List.length errors));
183
184 Printf.printf "\n--- Section 3: Missing Dependencies ---\n%!";
185
186 (* Try to use a type from a cell that doesn't exist in deps *)
187 let* errors = query_errors "" (Some "m1") []
188 "let bad : point = { x = 1; y = 2 };;" in
189 test "missing_dep_error" (List.length errors > 0)
190 (Printf.sprintf "%d errors (expected > 0)" (List.length errors));
191
192 (* Reference with missing dependency - should fail *)
193 let* errors = query_errors "" (Some "m2") ["nonexistent"]
194 "let a = 1;;" in
195 (* Even with a missing dep in the list, simple code should work *)
196 test "missing_dep_simple_ok" (List.length errors = 0)
197 (Printf.sprintf "%d errors" (List.length errors));
198
199 Printf.printf "\n--- Section 4: Dependency Update Propagation ---\n%!";
200
201 (* u1: initial type *)
202 let* errors = query_errors "" (Some "u1") [] "type u = int;;" in
203 test "update_u1_initial" (List.length errors = 0)
204 (Printf.sprintf "%d errors" (List.length errors));
205
206 (* u2: depends on u1, uses type u as int *)
207 let* errors = query_errors "" (Some "u2") ["u1"] "let val_u : u = 42;;" in
208 test "update_u2_initial" (List.length errors = 0)
209 (Printf.sprintf "%d errors" (List.length errors));
210
211 (* Now update u1 to change type u to string *)
212 let* errors = query_errors "" (Some "u1") [] "type u = string;;" in
213 test "update_u1_changed" (List.length errors = 0)
214 (Printf.sprintf "%d errors" (List.length errors));
215
216 (* u2 with same code should now error (42 is not string) *)
217 let* errors = query_errors "" (Some "u2") ["u1"] "let val_u : u = 42;;" in
218 test "update_u2_error" (List.length errors > 0)
219 (Printf.sprintf "%d errors (expected > 0)" (List.length errors));
220
221 (* Fix u2 to work with string type *)
222 let* errors = query_errors "" (Some "u2") ["u1"]
223 "let val_u : u = \"hello\";;" in
224 test "update_u2_fixed" (List.length errors = 0)
225 (Printf.sprintf "%d errors" (List.length errors));
226
227 Printf.printf "\n--- Section 5: Type Shadowing ---\n%!";
228
229 (* s1: defines type t = int *)
230 let* errors = query_errors "" (Some "s1") [] "type t = int;;" in
231 test "shadow_s1" (List.length errors = 0)
232 (Printf.sprintf "%d errors" (List.length errors));
233
234 (* s2: depends on s1, also defines type t = string (shadows) *)
235 let* errors = query_errors "" (Some "s2") ["s1"]
236 "type t = string;;" in
237 test "shadow_s2" (List.length errors = 0)
238 (Printf.sprintf "%d errors" (List.length errors));
239
240 (* s3: depends on s2 - should see t as string, not int *)
241 let* errors = query_errors "" (Some "s3") ["s2"]
242 "let shadowed : t = \"works\";;" in
243 test "shadow_s3_string" (List.length errors = 0)
244 (Printf.sprintf "%d errors" (List.length errors));
245
246 (* s4: depends only on s1 - should see t as int *)
247 let* errors = query_errors "" (Some "s4") ["s1"]
248 "let original : t = 123;;" in
249 test "shadow_s4_int" (List.length errors = 0)
250 (Printf.sprintf "%d errors" (List.length errors));
251
252 Printf.printf "\n--- Section 6: Complex Dependency Graph ---\n%!";
253
254 (*
255 g1 ─┬─→ g2 ───→ g4
256 │ │
257 └─→ g3 ─────┘
258
259 g1 defines base
260 g2 and g3 both depend on g1
261 g4 depends on g2 and g3
262 *)
263
264 let* errors = query_errors "" (Some "g1") []
265 "module Base = struct\n\
266 \ type id = int\n\
267 \ let make_id x = x\n\
268 end;;" in
269 test "graph_g1" (List.length errors = 0)
270 (Printf.sprintf "%d errors" (List.length errors));
271
272 let* errors = query_errors "" (Some "g2") ["g1"]
273 "module User = struct\n\
274 \ type t = { id: Base.id; name: string }\n\
275 \ let create id name = { id; name }\n\
276 end;;" in
277 test "graph_g2" (List.length errors = 0)
278 (Printf.sprintf "%d errors" (List.length errors));
279
280 let* errors = query_errors "" (Some "g3") ["g1"]
281 "module Item = struct\n\
282 \ type t = { id: Base.id; value: int }\n\
283 \ let create id value = { id; value }\n\
284 end;;" in
285 test "graph_g3" (List.length errors = 0)
286 (Printf.sprintf "%d errors" (List.length errors));
287
288 (* g4 needs g1 for Base module, plus g2 and g3 *)
289 let* errors = query_errors "" (Some "g4") ["g1"; "g2"; "g3"]
290 "let user = User.create (Base.make_id 1) \"Alice\";;\n\
291 let item = Item.create (Base.make_id 100) 42;;" in
292 test "graph_g4" (List.length errors = 0)
293 (Printf.sprintf "%d errors" (List.length errors));
294
295 Printf.printf "\n--- Section 7: Empty and Self Dependencies ---\n%!";
296
297 (* Cell with no deps *)
298 let* errors = query_errors "" (Some "e1") []
299 "let standalone = 999;;" in
300 test "empty_deps" (List.length errors = 0)
301 (Printf.sprintf "%d errors" (List.length errors));
302
303 (* Cell that tries to reference itself should fail or have errors *)
304 let* errors = query_errors "" (Some "self") []
305 "let self_ref = 1;;" in
306 test "self_define" (List.length errors = 0)
307 (Printf.sprintf "%d errors" (List.length errors));
308
309 Lwt.return (Ok ())
310 in
311
312 let promise = test_sequence in
313 (match Lwt.state promise with
314 | Lwt.Return (Ok ()) -> ()
315 | Lwt.Return (Error (InternalError s)) ->
316 Printf.printf "\n[ERROR] Test failed with: %s\n%!" s
317 | Lwt.Fail e ->
318 Printf.printf "\n[ERROR] Exception: %s\n%!" (Printexc.to_string e)
319 | Lwt.Sleep -> Printf.printf "\n[ERROR] Promise still pending\n%!");
320
321 Printf.printf "\n=== Results: %d/%d tests passed ===\n%!" !passed_tests
322 !total_tests;
323 if !passed_tests = !total_tests then
324 Printf.printf "SUCCESS: All dependency tests passed!\n%!"
325 else Printf.printf "FAILURE: Some tests failed.\n%!"