A fork of mtelver's day10 project
at main2 266 lines 8.7 kB view raw
1(** Node.js test for multiple environment support. 2 3 This tests that multiple isolated execution environments work correctly, 4 including: 5 - Creating and destroying environments 6 - Isolation between environments (values defined in one don't leak to another) 7 - Using the default environment 8 - Listing environments 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 contains s substr = 110 try 111 let _ = Str.search_forward (Str.regexp_string substr) s 0 in 112 true 113 with Not_found -> false 114 115let run_toplevel env_id code = 116 let ( let* ) m f = 117 let open Lwt in 118 m >>= function 119 | Ok x -> f x 120 | Error e -> return (Error e) 121 in 122 let* result = U.exec_toplevel env_id ("# " ^ code) in 123 Lwt.return (Ok result.script) 124 125let _ = 126 Printf.printf "=== Node.js Environment Tests ===\n\n%!"; 127 128 Logs.set_reporter (Logs_fmt.reporter ()); 129 Logs.set_level (Some Logs.Info); 130 131 let ( let* ) m f = 132 let open Lwt in 133 m >>= function 134 | Ok x -> f x 135 | Error e -> return (Error e) 136 in 137 138 let init_config = 139 { stdlib_dcs = None; findlib_requires = []; findlib_index = None; execute = true } 140 in 141 142 let test_sequence = 143 (* Initialize *) 144 let* _ = U.init init_config in 145 146 Printf.printf "--- Section 1: Default Environment ---\n%!"; 147 148 (* Setup default environment *) 149 let* _ = U.setup "" in 150 test "default_setup" true "Default environment setup"; 151 152 (* Define a value in default environment *) 153 let* r = run_toplevel "" "let default_val = 42;;" in 154 test "default_define" (contains r "val default_val : int = 42") r; 155 156 Printf.printf "\n--- Section 2: Creating New Environments ---\n%!"; 157 158 (* Create a new environment "env1" *) 159 let* _ = U.create_env "env1" in 160 test "create_env1" true "Created environment env1"; 161 162 (* Setup env1 *) 163 let* _ = U.setup "env1" in 164 test "setup_env1" true "Setup environment env1"; 165 166 (* Define a different value in env1 *) 167 let* r = run_toplevel "env1" "let env1_val = 100;;" in 168 test "env1_define" (contains r "val env1_val : int = 100") r; 169 170 Printf.printf "\n--- Section 3: Environment Isolation ---\n%!"; 171 172 (* Check that default_val is NOT visible in env1 - the script output 173 should NOT contain "val default_val" if there was an error *) 174 let* r = run_toplevel "env1" "default_val;;" in 175 test "isolation_default_from_env1" (not (contains r "val default_val")) 176 ("No leakage: " ^ String.sub r 0 (min 40 (String.length r))); 177 178 (* Check that env1_val is NOT visible in default env *) 179 let* r = run_toplevel "" "env1_val;;" in 180 test "isolation_env1_from_default" (not (contains r "val env1_val")) 181 ("No leakage: " ^ String.sub r 0 (min 40 (String.length r))); 182 183 (* Check that default_val IS still visible in default env *) 184 let* r = run_toplevel "" "default_val;;" in 185 test "default_still_works" (contains r "- : int = 42") r; 186 187 Printf.printf "\n--- Section 4: Multiple Environments ---\n%!"; 188 189 (* Create a second environment *) 190 let* _ = U.create_env "env2" in 191 let* _ = U.setup "env2" in 192 test "create_and_setup_env2" true "Created and setup env2"; 193 194 (* Define value in env2 *) 195 let* r = run_toplevel "env2" "let env2_val = 200;;" in 196 test "env2_define" (contains r "val env2_val : int = 200") r; 197 198 (* Verify isolation between all three environments *) 199 let* r = run_toplevel "env2" "env1_val;;" in 200 test "isolation_env1_from_env2" (not (contains r "val env1_val")) 201 ("No leakage: " ^ String.sub r 0 (min 40 (String.length r))); 202 203 let* r = run_toplevel "env1" "env2_val;;" in 204 test "isolation_env2_from_env1" (not (contains r "val env2_val")) 205 ("No leakage: " ^ String.sub r 0 (min 40 (String.length r))); 206 207 Printf.printf "\n--- Section 5: List Environments ---\n%!"; 208 209 (* List all environments *) 210 let* envs = U.list_envs () in 211 test "list_envs_count" (List.length envs >= 3) 212 (Printf.sprintf "Found %d environments" (List.length envs)); 213 test "list_envs_has_default" (List.mem "default" envs) 214 (String.concat ", " envs); 215 test "list_envs_has_env1" (List.mem "env1" envs) 216 (String.concat ", " envs); 217 test "list_envs_has_env2" (List.mem "env2" envs) 218 (String.concat ", " envs); 219 220 Printf.printf "\n--- Section 6: Destroy Environment ---\n%!"; 221 222 (* Destroy env2 *) 223 let* _ = U.destroy_env "env2" in 224 test "destroy_env2" true "Destroyed env2"; 225 226 (* Verify env2 is gone from list *) 227 let* envs = U.list_envs () in 228 test "env2_destroyed" (not (List.mem "env2" envs)) 229 (String.concat ", " envs); 230 231 (* env1 should still exist *) 232 test "env1_still_exists" (List.mem "env1" envs) 233 (String.concat ", " envs); 234 235 Printf.printf "\n--- Section 7: Reuse Environment Name ---\n%!"; 236 237 (* Re-create env2 *) 238 let* _ = U.create_env "env2" in 239 let* _ = U.setup "env2" in 240 241 (* Old values should not exist - checking that it doesn't find the old value *) 242 let* r = run_toplevel "env2" "env2_val;;" in 243 test "new_env2_clean" (not (contains r "- : int = 200")) 244 ("Old value gone: " ^ String.sub r 0 (min 40 (String.length r))); 245 246 (* Define new value *) 247 let* r = run_toplevel "env2" "let new_env2_val = 999;;" in 248 test "new_env2_define" (contains r "val new_env2_val : int = 999") r; 249 250 Lwt.return (Ok ()) 251 in 252 253 let promise = test_sequence in 254 (match Lwt.state promise with 255 | Lwt.Return (Ok ()) -> () 256 | Lwt.Return (Error (InternalError s)) -> 257 Printf.printf "\n[ERROR] Test failed with: %s\n%!" s 258 | Lwt.Fail e -> 259 Printf.printf "\n[ERROR] Exception: %s\n%!" (Printexc.to_string e) 260 | Lwt.Sleep -> Printf.printf "\n[ERROR] Promise still pending\n%!"); 261 262 Printf.printf "\n=== Results: %d/%d tests passed ===\n%!" !passed_tests 263 !total_tests; 264 if !passed_tests = !total_tests then 265 Printf.printf "SUCCESS: All environment tests passed!\n%!" 266 else Printf.printf "FAILURE: Some tests failed.\n%!"