Skip to content

Commit 73cc6e7

Browse files
authoredFeb 28, 2024
Update capture_io.ex
1 parent 6fd2468 commit 73cc6e7

File tree

1 file changed

+40
-22
lines changed

1 file changed

+40
-22
lines changed
 

Diff for: ‎lib/ex_unit/lib/ex_unit/capture_io.ex

+40-22
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ defmodule ExUnit.CaptureIO do
2929
3030
Returns the binary which is the captured output.
3131
32-
By default, `capture_io` replaces the `group_leader` (`:stdio`)
33-
for the current process. Capturing the group leader is done per
34-
process and therefore can be done concurrently.
32+
By default, `capture_io` replaces the `Process.group_leader/0` of the current
33+
process, which is the process used by default for all IO operations. Capturing
34+
the group leader of the current process is safe to run concurrently, under
35+
`async: true` tests. You may also explicitly capture the group leader of
36+
another process, however that is not safe to do concurrently.
3537
36-
However, the capturing of any other named device, such as `:stderr`,
37-
happens globally and persists until the function has ended. While this means
38-
it is safe to run your tests with `async: true` in many cases, captured output
39-
may include output from a different test and care must be taken when using
40-
`capture_io` with a named process asynchronously.
38+
You may also capture any other named IO device, such as `:stderr`. This is
39+
also safe to run concurrently but, if several tests are writting to the same
40+
device at once, captured output may include output from a different test.
4141
4242
A developer can set a string as an input. The default input is an empty
4343
string. If capturing a named device asynchronously, an input can only be given
@@ -51,15 +51,28 @@ defmodule ExUnit.CaptureIO do
5151
5252
## IO devices
5353
54-
You may capture the IO from any registered IO device. The device name given
55-
must be an atom representing the name of a registered process. In addition,
56-
Elixir provides two shortcuts:
54+
You may capture the IO of the group leader of any process, by passing a `pid`
55+
as argument, or from any registered IO device given as an `atom`. Here are
56+
some example values:
5757
58-
* `:stdio` - a shortcut for `:standard_io`, which maps to
59-
the current `Process.group_leader/0` in Erlang
58+
* `:stdio`, `:standard_io` - a shortcut for capturing the group leader
59+
of the current process. It is equivalent to passing `self()` as the
60+
first argument. This is safe to run concurrently and captures only
61+
the of the current process or any child process spawned inside the
62+
given function
6063
61-
* `:stderr` - a shortcut for the named process `:standard_error`
62-
provided in Erlang
64+
* `:stderr`, `:standard_error` - captures all IO to standard error
65+
(represented internally by an Erlang process named `:standard_error`).
66+
This is safe to run concurrently but it will capture the output
67+
of any other test writing to the same named device
68+
69+
* any other atom - captures all IO to the given device given by the
70+
atom. This is safe to run concurrently but it will capture the output
71+
of any other test writing to the same named device
72+
73+
* any other pid (since v1.17.0) - captures all IO to the group leader
74+
of the given process. This option is not safe to run concurrently
75+
if the pid is not `self()`. Tests using this value must set `async: true`
6376
6477
## Options
6578
@@ -72,9 +85,6 @@ defmodule ExUnit.CaptureIO do
7285
* `:encoding` (since v1.10.0) - encoding of the IO device. Allowed
7386
values are `:unicode` (default) and `:latin1`.
7487
75-
* `:pid` (since v1.17.0) - a process identifier. This option can be
76-
used to capture IO from an already-started process.
77-
7888
## Examples
7989
8090
To capture the standard io:
@@ -94,10 +104,10 @@ defmodule ExUnit.CaptureIO do
94104
...> end) == "this is input"
95105
true
96106
97-
Note it is fine to use `==` with standard IO, because the content is captured
98-
per test process. However, `:stderr` is shared across all tests, so you will
99-
want to use `=~` instead of `==` for assertions on `:stderr` if your tests
100-
are async:
107+
Note it is fine to use `==` with `:stdio` (the default IO device), because
108+
the content is captured per test process. However, `:stderr` is shared
109+
across all tests, so you will want to use `=~` instead of `==` for assertions
110+
on `:stderr` if your tests are async:
101111
102112
iex> capture_io(:stderr, fn -> IO.write(:stderr, "john") end) =~ "john"
103113
true
@@ -113,6 +123,14 @@ defmodule ExUnit.CaptureIO do
113123
Otherwise, if the standard error of any other test is captured, the test will
114124
fail.
115125
126+
To capture the IO from another process, you can pass a `pid`:
127+
128+
capture_io(GenServer.whereis(MyServer), fn ->
129+
GenServer.call(MyServer, :do_something)
130+
end)
131+
132+
Tests that directly capture a PID cannot run concurrently.
133+
116134
## Returning values
117135
118136
As seen in the examples above, `capture_io` returns the captured output.

0 commit comments

Comments
 (0)