@@ -28,7 +28,8 @@ defmodule ExUnit.CaptureIO do
2828 named device like `:stderr` is also possible globally by
2929 giving the registered device name explicitly as argument.
3030
31- The input is mocked to return `:eof`.
31+ A developer can set a string as an input. The default
32+ input is `:eof`.
3233
3334 ## Examples
3435
@@ -38,19 +39,36 @@ defmodule ExUnit.CaptureIO do
3839 true
3940 iex> capture_io(:stderr, fn -> IO.write(:stderr, "josé") end) == "josé"
4041 true
42+ iex> capture_io("this is input", fn->
43+ ...> input = IO.gets ""
44+ ...> IO.write input
45+ ...> end) == "this is input"
46+ true
4147
4248 """
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 )
4563 end
4664
4765 defp map_dev ( :stdio ) , do: :standard_io
4866 defp map_dev ( :stderr ) , do: :standard_error
4967 defp map_dev ( other ) , do: other
5068
51- defp do_capture_io( :standard_io , fun) do
69+ defp do_capture_io ( :standard_io , input , fun ) do
5270 original_gl = :erlang . group_leader
53- capture_gl = new_group_leader ( self )
71+ capture_gl = new_group_leader ( self , input )
5472 :erlang . group_leader ( capture_gl , self )
5573
5674 try do
@@ -65,13 +83,13 @@ defmodule ExUnit.CaptureIO do
6583 end
6684 end
6785
68- defp do_capture_io ( device , fun ) do
86+ defp do_capture_io ( device , input , fun ) do
6987 unless original_io = Process . whereis ( device ) do
7088 raise "could not find IO device registered at #{ inspect device } "
7189 end
7290
7391 Process . unregister ( device )
74- capture_io = new_group_leader ( self )
92+ capture_io = new_group_leader ( self , input )
7593 Process . register ( capture_io , device )
7694
7795 try do
@@ -87,14 +105,36 @@ defmodule ExUnit.CaptureIO do
87105 end
88106 end
89107
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 )
92110 end
93111
94- defp group_leader_process ( runner ) do
112+ defp group_leader_process ( runner , input ) do
113+ register_input ( input )
95114 group_leader_loop ( runner , :infinity , [ ] )
96115 end
97116
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+
98138 defp group_leader_loop ( runner , wait , buf ) do
99139 receive do
100140 { :io_request , from , reply_as , req } ->
@@ -141,24 +181,28 @@ defmodule ExUnit.CaptureIO do
141181 io_request ( { :put_chars , mod , func , args } , buf )
142182 end
143183
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 }
146186 end
147187
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 }
150190 end
151191
152192 defp io_request ( { :get_line , _prompt } , buf ) do
153- { :eof , buf }
193+ { get_line , buf }
154194 end
155195
156196 defp io_request ( { :get_line , _enc , _prompt } , buf ) do
157- { :eof , buf }
197+ { get_line , buf }
158198 end
159199
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 }
162206 end
163207
164208 defp io_request ( { :setopts , _opts } , buf ) do
@@ -193,6 +237,76 @@ defmodule ExUnit.CaptureIO do
193237 result
194238 end
195239
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+
196310 defp buffer_to_result ( [ ] ) do
197311 nil
198312 end
0 commit comments