@@ -28,7 +28,8 @@ defmodule ExUnit.CaptureIO do
28
28
named device like `:stderr` is also possible globally by
29
29
giving the registered device name explicitly as argument.
30
30
31
- The input is mocked to return `:eof`.
31
+ A developer can set a string as an input. The default
32
+ input is `:eof`.
32
33
33
34
## Examples
34
35
@@ -38,19 +39,36 @@ defmodule ExUnit.CaptureIO do
38
39
true
39
40
iex> capture_io(:stderr, fn -> IO.write(:stderr, "josé") end) == "josé"
40
41
true
42
+ iex> capture_io("this is input", fn->
43
+ ...> input = IO.gets ""
44
+ ...> IO.write input
45
+ ...> end) == "this is input"
46
+ true
41
47
42
48
"""
43
- def capture_io( device // :stdio , fun ) when is_atom ( device ) do
44
- do_capture_io ( map_dev ( device ) , fun )
49
+ def capture_io( device, input , fun ) do
50
+ do_capture_io ( map_dev ( device ) , input , fun )
51
+ end
52
+
53
+ def capture_io( device, fun) when is_atom ( device ) do
54
+ do_capture_io ( map_dev ( device ) , "" , fun )
55
+ end
56
+
57
+ def capture_io( input, fun) when is_binary ( input ) do
58
+ do_capture_io ( :standard_io , input , fun )
59
+ end
60
+
61
+ def capture_io ( fun ) do
62
+ do_capture_io ( :standard_io , "" , fun )
45
63
end
46
64
47
65
defp map_dev ( :stdio ) , do: :standard_io
48
66
defp map_dev ( :stderr ) , do: :standard_error
49
67
defp map_dev ( other ) , do: other
50
68
51
- defp do_capture_io( :standard_io , fun) do
69
+ defp do_capture_io ( :standard_io , input , fun ) do
52
70
original_gl = :erlang . group_leader
53
- capture_gl = new_group_leader ( self )
71
+ capture_gl = new_group_leader ( self , input )
54
72
:erlang . group_leader ( capture_gl , self )
55
73
56
74
try do
@@ -65,13 +83,13 @@ defmodule ExUnit.CaptureIO do
65
83
end
66
84
end
67
85
68
- defp do_capture_io ( device , fun ) do
86
+ defp do_capture_io ( device , input , fun ) do
69
87
unless original_io = Process . whereis ( device ) do
70
88
raise "could not find IO device registered at #{ inspect device } "
71
89
end
72
90
73
91
Process . unregister ( device )
74
- capture_io = new_group_leader ( self )
92
+ capture_io = new_group_leader ( self , input )
75
93
Process . register ( capture_io , device )
76
94
77
95
try do
@@ -87,14 +105,36 @@ defmodule ExUnit.CaptureIO do
87
105
end
88
106
end
89
107
90
- defp new_group_leader ( runner ) do
91
- spawn_link ( fn -> group_leader_process ( runner ) end )
108
+ defp new_group_leader ( runner , input ) do
109
+ spawn_link ( fn -> group_leader_process ( runner , input ) end )
92
110
end
93
111
94
- defp group_leader_process ( runner ) do
112
+ defp group_leader_process ( runner , input ) do
113
+ register_input ( input )
95
114
group_leader_loop ( runner , :infinity , [ ] )
96
115
end
97
116
117
+ defp register_input ( nil ) do
118
+ set_input ( nil )
119
+ end
120
+
121
+ defp register_input ( input ) do
122
+ chars = :unicode . characters_to_list ( input )
123
+ set_input ( chars )
124
+ end
125
+
126
+ defp set_input ( :eof ) do
127
+ set_input ( [ ] )
128
+ end
129
+
130
+ defp set_input ( input ) do
131
+ Process . put ( :capture_io_input , input )
132
+ end
133
+
134
+ defp get_input do
135
+ Process . get ( :capture_io_input )
136
+ end
137
+
98
138
defp group_leader_loop ( runner , wait , buf ) do
99
139
receive do
100
140
{ :io_request , from , reply_as , req } ->
@@ -141,24 +181,28 @@ defmodule ExUnit.CaptureIO do
141
181
io_request ( { :put_chars , mod , func , args } , buf )
142
182
end
143
183
144
- defp io_request ( { :get_chars , _enc , _propmpt , _n } , buf ) do
145
- { :eof , buf }
184
+ defp io_request ( { :get_chars , _enc , _prompt , n } , buf ) when n >= 0 do
185
+ { get_chars ( n ) , buf }
146
186
end
147
187
148
- defp io_request ( { :get_chars , _prompt , _n } , buf ) do
149
- { :eof , buf }
188
+ defp io_request ( { :get_chars , _prompt , n } , buf ) when n >= 0 do
189
+ { get_chars ( n ) , buf }
150
190
end
151
191
152
192
defp io_request ( { :get_line , _prompt } , buf ) do
153
- { :eof , buf }
193
+ { get_line , buf }
154
194
end
155
195
156
196
defp io_request ( { :get_line , _enc , _prompt } , buf ) do
157
- { :eof , buf }
197
+ { get_line , buf }
158
198
end
159
199
160
- defp io_request ( { :get_until , _prompt , _m , _f , _as } , buf ) do
161
- { :eof , buf }
200
+ defp io_request ( { :get_until , _prompt , mod , fun , args } , buf ) do
201
+ { get_until ( mod , fun , args ) , buf }
202
+ end
203
+
204
+ defp io_request ( { :get_until , _encoding , _prompt , mod , fun , args } , buf ) do
205
+ { get_until ( mod , fun , args ) , buf }
162
206
end
163
207
164
208
defp io_request ( { :setopts , _opts } , buf ) do
@@ -193,6 +237,76 @@ defmodule ExUnit.CaptureIO do
193
237
result
194
238
end
195
239
240
+ defp get_line do
241
+ input = get_input
242
+
243
+ case input do
244
+ [ ] ->
245
+ :eof
246
+ _ ->
247
+ { line , rest } = Enum . split_while ( input , fn ( char ) -> char != ?\n end )
248
+ case rest do
249
+ [ ] ->
250
+ set_input ( [ ] )
251
+ :unicode . characters_to_binary ( line )
252
+ [ _ | t ] ->
253
+ set_input ( t )
254
+ :unicode . characters_to_binary ( line ++ '\n ' )
255
+ end
256
+ end
257
+ end
258
+
259
+ defp get_chars ( n ) do
260
+ input = get_input
261
+
262
+ case input do
263
+ [ ] ->
264
+ :eof
265
+ _ ->
266
+ { chars , rest } = Enum . split ( input , n )
267
+ set_input ( rest )
268
+ :unicode . characters_to_binary ( chars )
269
+ end
270
+ end
271
+
272
+ defp get_until ( mod , fun , args ) do
273
+ input = get_input
274
+ do_get_until ( input , mod , fun , args )
275
+ end
276
+
277
+ defp do_get_until ( [ ] , mod , fun , args , continuation // [ ] ) do
278
+ case apply ( mod , fun , [ continuation , :eof | args ] ) do
279
+ { :done , result , rest_chars } ->
280
+ set_input ( rest_chars )
281
+ result
282
+ { :more , next_continuation } ->
283
+ do_get_until ( [ ] , mod , fun , args , next_continuation )
284
+ end
285
+ end
286
+
287
+ defp do_get_until ( input , mod , fun , args , continuation // [ ] ) do
288
+ { line , rest } = Enum . split_while ( input , fn ( char ) -> char != ?\n end )
289
+
290
+ case rest do
291
+ [ ] ->
292
+ case apply ( mod , fun , [ continuation , line | args ] ) do
293
+ { :done , result , rest_chars } ->
294
+ set_input ( rest_chars )
295
+ result
296
+ { :more , next_continuation } ->
297
+ do_get_until ( [ ] , mod , fun , args , next_continuation )
298
+ end
299
+ [ _ | t ] ->
300
+ case apply ( mod , fun , [ continuation , line ++ '\n ' | args ] ) do
301
+ { :done , result , rest_chars } ->
302
+ set_input ( rest_chars ++ t )
303
+ result
304
+ { :more , next_continuation } ->
305
+ do_get_until ( t , mod , fun , args , next_continuation )
306
+ end
307
+ end
308
+ end
309
+
196
310
defp buffer_to_result ( [ ] ) do
197
311
nil
198
312
end
0 commit comments