Skip to content

Commit 82518c3

Browse files
author
José Valim
committed
Ensure doctests failures are properly tested
1 parent 8ab8663 commit 82518c3

File tree

6 files changed

+111
-66
lines changed

6 files changed

+111
-66
lines changed

lib/ex_unit/examples/one_of_each.exs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,5 @@
11
ExUnit.start [seed: 0]
22

3-
defmodule DocTest do
4-
@moduledoc """
5-
6-
iex> 1 + * 1
7-
1
8-
9-
iex> 1 + 1
10-
3
11-
12-
iex> :oops
13-
#HashDict<[]>
14-
15-
iex> Hello.world
16-
:world
17-
18-
iex> raise "oops"
19-
** (WhatIsThis) oops
20-
21-
iex> raise "oops"
22-
** (RuntimeError) hello
23-
24-
"""
25-
end
26-
273
defmodule TestOneOfEach do
284
@moduledoc """
295
This module contains one of each type of failing test.
@@ -146,8 +122,6 @@ defmodule TestOneOfEach do
146122
end
147123
end
148124

149-
doctest DocTest
150-
151125
defp blows_up do
152126
ignite(0) + 1
153127
end

lib/ex_unit/lib/ex_unit/assertions.ex

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
defexception ExUnit.AssertionError,
2-
left: :ex_unit_no_meaningful_value,
3-
right: :ex_unit_no_meaningful_value,
4-
message: :ex_unit_no_meaningful_value,
5-
expr: :ex_unit_no_meaningful_value do
1+
defmodule ExUnit.AssertionError do
2+
@no_value :ex_unit_no_meaningful_value
3+
4+
defexception left: @no_value,
5+
right: @no_value,
6+
message: @no_value,
7+
expr: @no_value
68

79
@doc """
810
Indicates no meaningful value for a field.
911
"""
1012
def no_value do
11-
:ex_unit_no_meaningful_value
13+
@no_value
1214
end
1315
end
1416

lib/ex_unit/lib/ex_unit/case.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ defmodule ExUnit.Case do
136136
via `ExUnit.configure/1`:
137137
138138
# Exclude all external tests from running
139-
ExUnit.configure exclude: [external: true]
139+
ExUnit.configure(exclude: [external: true])
140140
141141
From now on, ExUnit will not run any test that has the `external` flag
142142
set to true. This behaviour can be reversed with the `:include` option
@@ -150,7 +150,7 @@ defmodule ExUnit.Case do
150150
a particular tag by default, regardless of its value, and include only
151151
a certain subset:
152152
153-
ExUnit.configure exclude: :os, include: [os: :unix]
153+
ExUnit.configure(exclude: :os, include: [os: :unix])
154154
155155
Keep in mind that all tests are included by default, so unless they are
156156
excluded first, the `include` option has no effect.

lib/ex_unit/lib/ex_unit/doc_test.ex

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ defmodule ExUnit.DocTest do
6969
This is useful in two use cases:
7070
7171
* Being able to refer to specific numbered scenarios
72-
* Copy-pasting examples from an actual iex sessions
72+
* Copy-pasting examples from an actual iex session
7373
7474
We also allow you to select or skip some functions when calling
7575
`doctest`. See the documentation for more info.
@@ -107,7 +107,7 @@ defmodule ExUnit.DocTest do
107107
side effects. For example, if a doctest prints to standard output, doctest
108108
will not try to capture the output.
109109
110-
Similarly, doctest does not run in any kind of sandbox. So any module
110+
Similarly, doctests do not run in any kind of sandbox. So any module
111111
defined in a code example is going to linger throughout the whole test
112112
suite run.
113113
"""
@@ -235,11 +235,11 @@ defmodule ExUnit.DocTest do
235235
unquote_splicing(tests)
236236
rescue
237237
e in [ExUnit.AssertionError] ->
238-
reraise e, [], stack
238+
reraise e, stack
239239

240240
error ->
241241
reraise ExUnit.AssertionError,
242-
[message: "Doctest failed: got #{inspect(elem(error, 0))} with message #{Exception.message(error)}",
242+
[message: "Doctest failed: got #{inspect(error.__struct__)} with message #{Exception.message(error)}",
243243
expr: unquote(whole_expr)],
244244
stack
245245
end
@@ -257,8 +257,8 @@ defmodule ExUnit.DocTest do
257257
actual ->
258258
reraise ExUnit.AssertionError,
259259
[message: "Doctest failed",
260-
expr: "#{unquote(String.strip(expr))} === #{unquote(String.strip(expected))}",
261-
left: actual],
260+
expr: "#{unquote(String.strip(expr))} === #{unquote(String.strip(expected))}",
261+
left: actual],
262262
unquote(stack)
263263
end
264264
end
@@ -274,9 +274,9 @@ defmodule ExUnit.DocTest do
274274
^expected -> :ok
275275
actual ->
276276
reraise ExUnit.AssertionError,
277-
[ message: "Doctest failed",
278-
expr: "inspect(#{unquote(String.strip(expr))}) === #{unquote(String.strip(expected))}",
279-
left: actual],
277+
[message: "Doctest failed",
278+
expr: "inspect(#{unquote(String.strip(expr))}) === #{unquote(String.strip(expected))}",
279+
left: actual],
280280
unquote(stack)
281281
end
282282
end
@@ -296,7 +296,7 @@ defmodule ExUnit.DocTest do
296296
error ->
297297
unless error.__struct__ == unquote(exception) and
298298
Exception.message(error) == unquote(message) do
299-
got = inspect(elem(error, 0)) <> " with message " <> inspect(Exception.message(error))
299+
got = inspect(error.__struct__) <> " with message " <> inspect(Exception.message(error))
300300
reraise ExUnit.AssertionError,
301301
[message: "Doctest failed: expected exception #{spec} but got #{got}",
302302
expr: expr],
@@ -328,7 +328,7 @@ defmodule ExUnit.DocTest do
328328
quote do
329329
reraise ExUnit.AssertionError,
330330
[message: "Doctest did not compile, got: #{unquote(message)}",
331-
expr: unquote(String.strip(expr))]
331+
expr: unquote(String.strip(expr))],
332332
unquote(stack)
333333
end
334334
end

lib/ex_unit/test/ex_unit/callbacks_test.exs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ defmodule ExUnit.CallbacksTest do
5555

5656
test "doesn't choke on setup errors" do
5757
defmodule SetupTest do
58-
use ExUnit.Case, async: false
58+
use ExUnit.Case
5959

6060
setup _ do
6161
:ok = error
@@ -127,8 +127,6 @@ defmodule ExUnit.CallbacksTest do
127127
defp error, do: :error
128128
end
129129

130-
ExUnit.configure(formatters: [ExUnit.CLIFormatter])
131-
132130
assert capture_io(fn -> ExUnit.run end) =~
133131
"** (MatchError) no match of right hand side value: :error"
134132
end

lib/ex_unit/test/ex_unit/doc_test_test.exs

Lines changed: 89 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ defmodule ExUnit.DocTestTest.GoodModule do
4747
def inspect2_test, do: :ok
4848
end
4949

50-
defmodule ExUnit.DocTestTest.ExceptionModule do
50+
defmodule ExUnit.DocTestTest.MultipleExceptions do
5151
@doc """
5252
iex> 1 + ""
5353
** (ArithmeticError) bad argument in arithmetic expression
@@ -102,14 +102,27 @@ defmodule ExUnit.DocTestTest.NoImport do
102102
end
103103

104104
defmodule ExUnit.DocTestTest.Invalid do
105-
@doc """
106-
iex> _a = 1
107-
1
105+
@moduledoc """
106+
107+
iex> 1 + * 1
108+
1
109+
110+
iex> 1 + hd(List.flatten([1]))
111+
3
112+
113+
iex> :oops
114+
#HashDict<[]>
115+
116+
iex> Hello.world
117+
:world
118+
119+
iex> raise "oops"
120+
** (WhatIsThis) oops
121+
122+
iex> raise "oops"
123+
** (RuntimeError) hello
108124
109-
iex> _a + 1
110-
2
111125
"""
112-
def no_leak, do: :ok
113126
end
114127

115128
defmodule ExUnit.DocTestTest.IndentationHeredocs do
@@ -163,7 +176,7 @@ defmodule ExUnit.DocTestTest.Incomplete do
163176
end
164177

165178
defmodule ExUnit.DocTestTest do
166-
use ExUnit.Case, async: true
179+
use ExUnit.Case
167180

168181
# This is intentional. The doctests in DocTest's docs
169182
# fail for demonstration purposes.
@@ -175,20 +188,79 @@ defmodule ExUnit.DocTestTest do
175188
doctest ExUnit.DocTestTest.NoImport
176189
doctest ExUnit.DocTestTest.IndentationHeredocs
177190

178-
test "multiple exceptions in one test case is not supported" do
179-
assert_raise ExUnit.DocTest.Error, ~r"multiple exceptions in one doctest case are not supported", fn ->
180-
defmodule NeverCompiled do
181-
import ExUnit.DocTest
182-
doctest ExUnit.DocTestTest.ExceptionModule
183-
end
191+
import ExUnit.CaptureIO
192+
193+
test "doctest failures" do
194+
defmodule ActuallyCompiled do
195+
use ExUnit.Case
196+
doctest ExUnit.DocTestTest.Invalid
184197
end
198+
199+
ExUnit.configure(seed: 0)
200+
output = capture_io(fn -> ExUnit.run end)
201+
202+
assert output =~ """
203+
1) test moduledoc at ExUnit.DocTestTest.Invalid (1) (ExUnit.DocTestTest.ActuallyCompiled)
204+
test/ex_unit/doc_test_test.exs:196
205+
Doctest did not compile, got: (SyntaxError) test/ex_unit/doc_test_test.exs:104: syntax error before: '*'
206+
code: 1 + * 1
207+
stacktrace:
208+
test/ex_unit/doc_test_test.exs:104: ExUnit.DocTestTest.Invalid (module)
209+
"""
210+
211+
assert output =~ """
212+
2) test moduledoc at ExUnit.DocTestTest.Invalid (2) (ExUnit.DocTestTest.ActuallyCompiled)
213+
test/ex_unit/doc_test_test.exs:196
214+
Doctest failed
215+
code: 1 + hd(List.flatten([1])) === 3
216+
lhs: 2
217+
stacktrace:
218+
test/ex_unit/doc_test_test.exs:104: ExUnit.DocTestTest.Invalid (module)
219+
"""
220+
221+
assert output =~ """
222+
3) test moduledoc at ExUnit.DocTestTest.Invalid (3) (ExUnit.DocTestTest.ActuallyCompiled)
223+
test/ex_unit/doc_test_test.exs:196
224+
Doctest failed
225+
code: inspect(:oops) === "#HashDict<[]>"
226+
lhs: ":oops"
227+
stacktrace:
228+
test/ex_unit/doc_test_test.exs:104: ExUnit.DocTestTest.Invalid (module)
229+
"""
230+
231+
assert output =~ """
232+
4) test moduledoc at ExUnit.DocTestTest.Invalid (4) (ExUnit.DocTestTest.ActuallyCompiled)
233+
test/ex_unit/doc_test_test.exs:196
234+
Doctest failed: got UndefinedFunctionError with message undefined function: Hello.world/0
235+
code: Hello.world
236+
stacktrace:
237+
test/ex_unit/doc_test_test.exs:104: ExUnit.DocTestTest.Invalid (module)
238+
"""
239+
240+
assert output =~ """
241+
5) test moduledoc at ExUnit.DocTestTest.Invalid (5) (ExUnit.DocTestTest.ActuallyCompiled)
242+
test/ex_unit/doc_test_test.exs:196
243+
Doctest failed: expected exception WhatIsThis with message "oops" but got RuntimeError with message "oops"
244+
code: raise "oops"
245+
stacktrace:
246+
test/ex_unit/doc_test_test.exs:104: ExUnit.DocTestTest.Invalid (module)
247+
"""
248+
249+
assert output =~ """
250+
6) test moduledoc at ExUnit.DocTestTest.Invalid (6) (ExUnit.DocTestTest.ActuallyCompiled)
251+
test/ex_unit/doc_test_test.exs:196
252+
Doctest failed: expected exception RuntimeError with message "hello" but got RuntimeError with message "oops"
253+
code: raise "oops"
254+
stacktrace:
255+
test/ex_unit/doc_test_test.exs:104: ExUnit.DocTestTest.Invalid (module)
256+
"""
185257
end
186258

187-
test "variables in heredocs do not leak" do
188-
assert_raise ArgumentError, fn ->
259+
test "multiple exceptions in one test case is not supported" do
260+
assert_raise ExUnit.DocTest.Error, ~r"multiple exceptions in one doctest case are not supported", fn ->
189261
defmodule NeverCompiled do
190262
import ExUnit.DocTest
191-
doctest ExUnit.DocTestTest.Invalid
263+
doctest ExUnit.DocTestTest.MultipleExceptions
192264
end
193265
end
194266
end
@@ -221,6 +293,5 @@ defmodule ExUnit.DocTestTest do
221293
doctest ExUnit.DocTestTest.Incomplete
222294
end
223295
end
224-
225296
end
226297
end

0 commit comments

Comments
 (0)