diff --git a/dune-project b/dune-project
index 7011d7a5c..a9a377bcd 100644
--- a/dune-project
+++ b/dune-project
@@ -56,7 +56,9 @@
(description "An eio implementation that is compatible with programs running in the browser.")
(depends
(eio (= :version))
- (brr (>= 0.0.4))))
+ (brr (>= 0.0.4))
+ (alcotest (and (>= 1.6.0) :with-test))
+ (ansi :with-test)))
(package
(name eio_main)
(synopsis "Effect-based direct-style IO mainloop for OCaml")
diff --git a/lib_eio_js/browser/example/dune b/lib_eio_js/browser/example/dune
new file mode 100644
index 000000000..8541da5c1
--- /dev/null
+++ b/lib_eio_js/browser/example/dune
@@ -0,0 +1,13 @@
+; We compile by hand because dune is a little broken w.r.t to
+; separate compilation, js_of_ocaml and effects.
+(executable
+ (name index)
+ (modes js)
+ (js_of_ocaml (flags --target-env=nodejs --enable=effects --debug-info --source-map --pretty))
+ (libraries eio_browser))
+
+(rule
+ (alias default)
+ (deps index.html index.bc.js)
+ (targets index.js)
+ (action (copy index.bc.js index.js)))
diff --git a/lib_eio_js/browser/example/index.html b/lib_eio_js/browser/example/index.html
new file mode 100644
index 000000000..0c35cf098
--- /dev/null
+++ b/lib_eio_js/browser/example/index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ Eio in the Browser
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib_eio_js/browser/test/index.ml b/lib_eio_js/browser/example/index.ml
similarity index 100%
rename from lib_eio_js/browser/test/index.ml
rename to lib_eio_js/browser/example/index.ml
diff --git a/lib_eio_js/browser/test/dune b/lib_eio_js/browser/test/dune
index 8541da5c1..959149572 100644
--- a/lib_eio_js/browser/test/dune
+++ b/lib_eio_js/browser/test/dune
@@ -1,13 +1,17 @@
-; We compile by hand because dune is a little broken w.r.t to
-; separate compilation, js_of_ocaml and effects.
+(library
+ (name eio_test)
+ (modules eio_test)
+ (libraries eio alcotest))
+
(executable
- (name index)
+ (name test)
+ (modules test)
(modes js)
- (js_of_ocaml (flags --target-env=nodejs --enable=effects --debug-info --source-map --pretty))
- (libraries eio_browser))
+ (js_of_ocaml (flags --target-env=nodejs --enable=effects --setenv=ALCOTEST_COLOR=always))
+ (libraries alcotest ansi eio_test eio_browser))
(rule
(alias default)
- (deps index.html index.bc.js)
- (targets index.js)
- (action (copy index.bc.js index.js)))
+ (deps index.html test.bc.js)
+ (targets test.js)
+ (action (copy test.bc.js test.js)))
\ No newline at end of file
diff --git a/lib_eio_js/browser/test/eio_test.ml b/lib_eio_js/browser/test/eio_test.ml
new file mode 100644
index 000000000..aa811ae3b
--- /dev/null
+++ b/lib_eio_js/browser/test/eio_test.ml
@@ -0,0 +1,156 @@
+open Eio
+
+(* Filesystem tests *)
+module Fs = struct
+ let ( / ) = Path.(/)
+ let test_read_write env () =
+ let test_file = "test.txt" in
+ let text = "abcdefgh" in
+ let len = String.length text in
+ let _write =
+ Path.with_open_out ~create:(`If_missing 0o644) (env#fs / test_file) @@ fun sink ->
+ Flow.copy_string text sink
+ in
+ let buf = Cstruct.create len in
+ let _read =
+ Path.with_open_in (env#fs / test_file) @@ fun source ->
+ Flow.read_exact source buf
+ in
+ Path.unlink (env#fs / test_file);
+ Alcotest.(check string) "same string" text (Cstruct.to_string buf)
+
+ let test_append env () =
+ let test_file = "test.txt" in
+ let text = "abcdefgh" in
+ let len = String.length text in
+ let _write =
+ Path.with_open_out ~create:(`If_missing 0o644) (env#fs / test_file) @@ fun sink ->
+ Flow.copy_string text sink
+ in
+ let _write2 =
+ Path.with_open_out ~append:true ~create:(`If_missing 0o644) (env#fs / test_file) @@ fun sink ->
+ Flow.copy_string text sink
+ in
+ let buf = Cstruct.create (2 * len) in
+ let _read =
+ Path.with_open_in (env#fs / test_file) @@ fun source ->
+ Flow.read_exact source buf
+ in
+ Path.unlink (env#fs / test_file);
+ Alcotest.(check string) "same string" (text ^ text) (Cstruct.to_string buf)
+
+ let optint_int63 = Alcotest.testable Optint.Int63.pp Optint.Int63.equal
+
+ let test_fails_if_exists env () =
+ let test_file = "./test.txt" in
+ let text = "abcdefgh" in
+ let _write =
+ Path.with_open_out ~create:(`If_missing 0o644) (env#fs / test_file) @@ fun sink ->
+ Flow.copy_string text sink
+ in
+ Fun.protect
+ (fun () ->
+ try
+ Path.with_open_out ~create:(`Exclusive 0o644) (env#fs / test_file) @@ fun _ ->
+ Alcotest.failf "Expected to fail with already exists"
+ with
+ | Eio.Exn.Io (Eio.Fs.E (Fs.Already_exists _), _) -> ()
+ | exn -> Alcotest.failf "Expected already exists exception, got: %a" Fmt.exn exn
+ ) ~finally:(fun () -> Path.unlink (env#fs / test_file))
+
+ let test_read_write env () =
+ let test_file = "test.txt" in
+ let text = "abcdefgh" in
+ let len = String.length text in
+ let _write =
+ Path.with_open_out ~create:(`If_missing 0o644) (env#fs / test_file) @@ fun sink ->
+ Flow.copy_string text sink
+ in
+ let stat =
+ Eio.Switch.run @@ fun sw ->
+ let fd = Path.open_in ~sw (env#fs / test_file) in
+ File.stat fd
+ in
+ let is_file = stat.kind = `Regular_file in
+ Alcotest.(check bool) "is file" true is_file;
+ Alcotest.(check optint_int63) "same size" (Optint.Int63.of_int len) stat.size;
+ Path.unlink (env#fs / test_file)
+
+ let tests env = [
+ Alcotest.test_case "read and write" `Quick (test_read_write env);
+ Alcotest.test_case "append" `Quick (test_append env);
+ Alcotest.test_case "fail if exists" `Quick (test_fails_if_exists env);
+ Alcotest.test_case "fstat" `Quick (test_fails_if_exists env);
+ ]
+end
+
+module Fibers = struct
+ let test_yield () =
+ let expect = [1; 2] in
+ let res = ref [] in
+ Fiber.both
+ (fun () -> Fiber.yield (); res := 1 :: !res)
+ (fun () -> res := 2 :: !res);
+ Alcotest.(check (list int)) "same list" expect !res
+
+ let test_simple_cancel () =
+ let res =
+ Fiber.first
+ (fun () -> "a")
+ (fun () -> Fiber.yield (); failwith "b crashed")
+ in
+ Alcotest.(check string) "same string" "a" res;
+ let p, _r = Promise.create () in
+ let res = Fiber.first
+ (fun () -> "a")
+ (fun () -> Promise.await p)
+ in
+ Alcotest.(check string) "promise cancelled" "a" res
+
+
+ let tests = [
+ Alcotest.test_case "yielding" `Quick test_yield;
+ Alcotest.test_case "fiber first cancel" `Quick test_simple_cancel;
+ ]
+end
+
+module Stream = struct
+ type op = [ `Add of int | `Take of int ]
+
+ let pp_op ppf = function
+ | `Add i -> Fmt.pf ppf "add %i" i
+ | `Take i -> Fmt.pf ppf "take %i" i
+
+ let op = Alcotest.of_pp pp_op
+
+ let test_stream () =
+ let l = ref [] in
+ let add s v =
+ Stream.add s v;
+ l := (`Add v) :: !l
+ in
+ let take s =
+ l := (`Take (Stream.take s)) :: !l
+ in
+ let t = Stream.create 3 in
+ add t 1;
+ Fiber.both
+ (fun () ->
+ add t 2;
+ add t 3;
+ add t 4;
+ )
+ (fun () ->
+ take t;
+ take t;
+ take t;
+ take t
+ );
+ let actual = List.rev !l in
+ let expected = [ `Add 1; `Add 2; `Add 3; `Take 1; `Take 2; `Take 3; `Take 4; `Add 4 ] in
+ Alcotest.(check (list op)) "same sequence" expected actual
+
+ let tests = [
+ Alcotest.test_case "stream1" `Quick test_stream
+ ]
+end
\ No newline at end of file
diff --git a/lib_eio_js/browser/test/index.html b/lib_eio_js/browser/test/index.html
index 0c35cf098..fc524af40 100644
--- a/lib_eio_js/browser/test/index.html
+++ b/lib_eio_js/browser/test/index.html
@@ -4,12 +4,15 @@
- Eio in the Browser
+ Eio Browser Tests
+
-
-
-
-
+