Shells in OCaml
1(* Thorough testing of word expansion *)
2open Merry
3module C = Merry.Eval.Make (Merry_posix.State) (Merry_posix.Exec)
4
5let expand ctx cst = C.word_expansion ctx cst |> snd |> List.concat
6let fragment = Alcotest.of_pp Merry.Ast.Fragment.pp
7let fragments = Alcotest.list fragment
8let frags = List.map Ast.Fragment.make
9
10let with_default_ctx ?(args = []) ?(params = []) ?(home = "/home/merry/") env fn
11 =
12 let executor = Merry_posix.Exec.{ mgr = env#process_mgr } in
13 let interactive = false in
14 let pos_zero = "msh" in
15 Eio.Switch.run @@ fun async_switch ->
16 let signal_handler f = Eio_posix.run @@ fun _ -> f () in
17 let state = Merry_posix.State.make ~home (Fpath.v (Merry.Eunix.cwd ())) in
18 let state =
19 List.fold_left
20 (fun s (k, v) -> Merry_posix.State.update s ~param:k v |> Result.get_ok)
21 state params
22 in
23 let ctx =
24 C.make_ctx ~interactive state executor ~fs:env#fs ~stdin:env#stdin
25 ~stdout:env#stdout ~async_switch ~argv:(Array.of_list args)
26 ~program:pos_zero ~signal_handler
27 in
28 fn ctx
29
30module W = struct
31 let name s = Ast.WordName s
32 let lit s = Ast.WordLiteral s
33 let dquote c = Ast.WordDoubleQuoted c
34 let glob_all = Ast.WordGlobAll
35 let tilde s = Ast.WordTildePrefix s
36
37 (* let squote c = Ast.WordSingleQuoted c *)
38 (* let arith a = Ast.WordArithmeticExpression a *)
39 let var v = Ast.WordVariable (Ast.VariableAtom (v, Ast.NoAttribute))
40end
41
42let test_no_expansions env () =
43 let args = [ "echo"; "hello" ] in
44 let cargs = W.[ name "echo"; lit "hello" ] in
45 with_default_ctx ~args env @@ fun ctx ->
46 let expected = frags args in
47 let actual = expand ctx cargs in
48 Alcotest.check fragments "same fragments" expected actual
49
50let test_dquote env () =
51 let args = [ "echo"; "\"hello there\"" ] in
52 let cargs = W.[ name "echo"; dquote [ lit "hello there" ] ] in
53 with_default_ctx ~args env @@ fun ctx ->
54 let expected =
55 Ast.[ Fragment.make "echo"; Fragment.make ~join:`No "hello there" ]
56 in
57 let actual = expand ctx cargs in
58 Alcotest.check fragments "same fragments" expected actual
59
60let test_dquote_expansion env () =
61 let args = [ "echo"; "\"hello $FOO...\"" ] in
62 let cargs =
63 W.[ name "echo"; dquote [ lit "hello "; var "FOO"; lit "..." ] ]
64 in
65 with_default_ctx ~args ~params:[ ("FOO", "there") ] env @@ fun ctx ->
66 let expected = Ast.Fragment.[ make "echo"; make "hello there..." ] in
67 let actual = expand ctx cargs in
68 Alcotest.check fragments "same fragments" expected actual
69
70let test_single_expansion env () =
71 let args = [ "echo"; "$FOO" ] in
72 let cargs = W.[ name "echo"; var "FOO" ] in
73 with_default_ctx ~args ~params:[ ("FOO", "bar") ] env @@ fun ctx ->
74 let expected = [ Ast.Fragment.make "echo"; Ast.Fragment.make "bar" ] in
75 let actual = expand ctx cargs in
76 Alcotest.check fragments "same fragments" expected actual
77
78let test_argv_expansion env () =
79 let cargs = W.[ name "echo"; var "@" ] in
80 with_default_ctx ~args:[ "echo"; "a"; "b"; "c d" ] env @@ fun ctx ->
81 let expected = frags [ "echo"; "a"; "b"; "c"; "d" ] in
82 let actual = expand ctx cargs in
83 Alcotest.check fragments "same fragments" expected actual
84
85let test_argv_in_quotes_expansion env () =
86 let cargs = W.[ name "echo"; dquote [ lit "got ["; var "@"; lit "]" ] ] in
87 with_default_ctx ~args:[ "echo"; "a"; "b"; "c" ] env @@ fun ctx ->
88 let expected =
89 Ast.Fragment.[ make "echo"; make "got [a"; make "b"; make "c]" ]
90 in
91 let actual = expand ctx cargs in
92 Alcotest.check fragments "same fragments" expected actual
93
94let test_glob env () =
95 let cargs = W.[ glob_all; lit ".ml" ] in
96 with_default_ctx ~args:[ "*.ml" ] env @@ fun ctx ->
97 let expected = Ast.Fragment.[ make "test_merry.ml"; make "wordexp.ml" ] in
98 let actual = expand ctx cargs in
99 Alcotest.check fragments "same fragments" expected actual
100
101let test_tilde env () =
102 let cargs = W.[ tilde "~"; lit "documents" ] in
103 with_default_ctx ~args:[ "~/documents" ] env @@ fun ctx ->
104 let expected = Ast.Fragment.[ make "/home/merry/documents" ] in
105 let actual = expand ctx cargs in
106 Alcotest.check fragments "same fragments" expected actual
107
108let simple env =
109 [
110 ("no expansions", `Quick, test_no_expansions env);
111 ("double quote", `Quick, test_dquote env);
112 ("double quote expansion", `Quick, test_dquote_expansion env);
113 ("single expansion", `Quick, test_single_expansion env);
114 ("argv expansion", `Quick, test_argv_expansion env);
115 ("argv expansion dquote", `Quick, test_argv_in_quotes_expansion env);
116 ("glob all", `Quick, test_glob env);
117 ("tilde", `Quick, test_tilde env);
118 ]
119
120let () =
121 Eio_posix.run @@ fun env -> Alcotest.run "wordexp" [ ("simple", simple env) ]