aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/test/lux/world/shell.lux
blob: 0e3b16ce9140db05eb1b2d4b3d0c8d8d43ed4935 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
(.require
 [library
  [lux (.except)
   [abstract
    [monad (.only do)]]
   [control
    ["[0]" try (.only Try)]
    ["[0]" exception (.only exception)]
    ["[0]" io (.only IO)]
    [concurrency
     ["[0]" async (.only Async)]]]
   [data
    ["[0]" text (.use "[1]#[0]" equivalence)]
    [collection
     ["[0]" list]]]
   [math
    ["[0]" random]
    [number
     ["n" nat]
     ["i" int]]]
   ["_" test (.only Test)
    ["[0]" unit]]]]
 [\\library
  ["[0]" / (.only)
   [//
    [file (.only Path)]
    ["[0]" environment
     ["[1]" \\parser (.only Environment)]]]]]
 [\\specification
  ["$[0]" /]])

(exception dead)

(def (mock [environment working_directory command arguments])
  (-> [Environment Path /.Command (List /.Argument)]
      (/.Mock Bit))
  (implementation
   (def (on_read dead?)
     (if dead?
       (exception.except ..dead [])
       (do try.monad
         [echo (try.of_maybe (list.head arguments))]
         (in [dead? echo]))))
   
   (def (on_fail dead?)
     (if dead?
       (exception.except ..dead [])
       {try.#Success [dead? ""]}))
   
   (def (on_write message dead?)
     (if dead?
       (exception.except ..dead [])
       {try.#Success dead?}))

   (def (on_destroy dead?)
     (if dead?
       (exception.except ..dead [])
       {try.#Success true}))

   (def (on_await dead?)
     (if dead?
       (exception.except ..dead [])
       {try.#Success [true /.normal]}))))

(def (io_shell command oops input destruction exit)
  (-> /.Command Text Text Text /.Exit (/.Shell IO))
  (implementation
   (def (execute [environment working_directory command arguments])
     (<| io.io
         {try.#Success}
         (is (/.Process IO))
         (implementation
          (def (read _)
            (io.io {try.#Success command}))
          (def (fail _)
            (io.io {try.#Success oops}))
          (def (write message)
            (io.io {try.#Failure message}))
          (def (destroy _)
            (io.io {try.#Failure destruction}))
          (def (await _)
            (io.io {try.#Success exit})))))))

(def .public test
  Test
  (<| (_.covering /._)
      (all _.and
           (_.for [/.async /.mock /.Mock]
                  ($/.spec (/.async (/.mock (|>> ..mock {try.#Success})
                                            false))))
           (_.coverage [/.error]
             (not (i.= /.normal /.error)))
           (do random.monad
             [command (random.alphabetic 5)
              oops (random.alphabetic 5)
              input (random.alphabetic 5)
              destruction (random.alphabetic 5)
              exit random.int
              .let [shell (/.async (..io_shell command oops input destruction exit))]]
             (in (do [! async.monad]
                   [verdict (do (try.with !)
                              [process (at shell execute [environment.empty "~" command (list)])
                               read (at process read [])
                               failure (at process fail [])
                               wrote! (do !
                                        [write (at process write input)]
                                        (in {try.#Success (case write
                                                            {try.#Success _}
                                                            false
                                                            
                                                            {try.#Failure write}
                                                            (text#= input write))}))
                               destroyed! (do !
                                            [destroy (at process destroy [])]
                                            (in {try.#Success (case destroy
                                                                {try.#Success _}
                                                                false
                                                                
                                                                {try.#Failure destroy}
                                                                (text#= destruction destroy))}))
                               await (at process await [])]
                              (in (and (text#= command read)
                                       (text#= oops failure)
                                       wrote!
                                       destroyed!
                                       (i.= exit await))))]
                   (unit.coverage [/.Shell]
                     (try.else false verdict)))))
           )))