this repo has no description
at main 325 lines 12 kB view raw
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%!"