1defmodule ExUnit.DocTest do
2  @moduledoc """
3  Extract test cases from the documentation.
4
5  Doctests allow us to generate tests from code examples found
6  in `@moduledoc` and `@doc` attributes. To do this, invoke the
7  `doctest/1` macro from within your test case and ensure your
8  code examples are written according to the syntax and guidelines
9  below.
10
11  ## Syntax
12
13  Every new test starts on a new line, with an `iex>` prefix.
14  Multiline expressions can be used by prefixing subsequent lines
15  with either `...>` (recommended) or `iex>`.
16
17  The expected result should start the line after the `iex>`
18  and `...>` line(s) and be terminated by a newline.
19
20  ## Examples
21
22  To run doctests include them in an ExUnit case with a `doctest` macro:
23
24      defmodule MyModuleTest do
25        use ExUnit.Case, async: true
26        doctest MyModule
27      end
28
29  The `doctest` macro loops through all functions and
30  macros defined in `MyModule`, parsing their documentation in
31  search of code examples.
32
33  A very basic example is:
34
35      iex> 1 + 1
36      2
37
38  Expressions on multiple lines are also supported:
39
40      iex> Enum.map([1, 2, 3], fn x ->
41      ...>   x * 2
42      ...> end)
43      [2, 4, 6]
44
45  Multiple results can be checked within the same test:
46
47      iex> a = 1
48      1
49      iex> a + 1
50      2
51
52  If you want to keep any two tests separate,
53  add an empty line between them:
54
55      iex> a = 1
56      1
57
58      iex> a + 1 # will fail with a "undefined function a/0" error
59      2
60
61  If you don't want to assert for every result in a doctest, you can omit
62  the result. You can do so between expressions:
63
64      iex> pid = spawn(fn -> :ok end)
65      iex> is_pid(pid)
66      true
67
68  As well as at the end:
69
70      iex> Mod.do_a_call_that_should_not_raise!(...)
71
72  This is useful when the result is something variable (like a PID in the
73  example above) or when the result is a complicated data structure and you
74  don't want to show it all, but just parts of it or some of its properties.
75
76  Similarly to IEx you can use numbers in your "prompts":
77
78      iex(1)> [1 + 2,
79      ...(1)>  3]
80      [3, 3]
81
82  This is useful in two cases:
83
84    * being able to refer to specific numbered scenarios
85    * copy-pasting examples from an actual IEx session
86
87  You can also select or skip functions when calling
88  `doctest`. See the documentation on the `:except` and `:only` options below
89  for more information.
90
91  ## Opaque types
92
93  Some types' internal structures are kept hidden and instead show a
94  user-friendly structure when inspected. The idiom in
95  Elixir is to print those data types in the format `#Name<...>`. Because those
96  values are treated as comments in Elixir code due to the leading
97  `#` sign, they require special care when being used in doctests.
98
99  Imagine you have a map that contains a MapSet and is printed as:
100
101      %{users: #MapSet<[:foo, :bar]>}
102
103  If you try to match on such an expression, `doctest` will fail to compile.
104  There are two ways to resolve this.
105
106  The first is to rely on the fact that doctest can compare internal
107  structures as long as they are at the root. So one could write:
108
109      iex> map = %{users: Enum.into([:foo, :bar], MapSet.new())}
110      iex> map.users
111      #MapSet<[:foo, :bar]>
112
113  Whenever a doctest starts with "#Name<", `doctest` will perform a string
114  comparison. For example, the above test will perform the following match:
115
116      inspect(map.users) == "#MapSet<[:foo, :bar]>"
117
118  Alternatively, since doctest results are actually evaluated, you can have
119  the MapSet building expression as the doctest result:
120
121      iex> %{users: Enum.into([:foo, :bar], MapSet.new())}
122      %{users: Enum.into([:foo, :bar], MapSet.new())}
123
124  The downside of this approach is that the doctest result is not really
125  what users would see in the terminal.
126
127  ## Exceptions
128
129  You can also showcase expressions raising an exception, for example:
130
131      iex(1)> raise "some error"
132      ** (RuntimeError) some error
133
134  Doctest will looking for a line starting with `** (` and it will parse it
135  accordingly to extract the exception name and message. The exception parser
136  will consider all following lines part of the exception message until there
137  is an empty line or there is a new expression prefixed with `iex>`.
138  Therefore, it is possible to match on multiline messages as long as there
139  are no empty lines on the message itself.
140
141  ## When not to use doctest
142
143  In general, doctests are not recommended when your code examples contain
144  side effects. For example, if a doctest prints to standard output, doctest
145  will not try to capture the output.
146
147  Similarly, doctests do not run in any kind of sandbox. So any module
148  defined in a code example is going to linger throughout the whole test
149  suite run.
150  """
151
152  @opaque_type_regex ~r/#[\w\.]+</
153
154  defmodule Error do
155    defexception [:message]
156
157    @impl true
158    def exception(opts) do
159      module = Keyword.fetch!(opts, :module)
160      message = Keyword.fetch!(opts, :message)
161
162      file = module.module_info(:compile)[:source] |> Path.relative_to_cwd()
163      info = Exception.format_file_line(file, opts[:line])
164      %__MODULE__{message: info <> " " <> message}
165    end
166  end
167
168  @doc """
169  This macro is used to generate ExUnit test cases for doctests.
170
171  Calling `doctest(Module)` will generate tests for all doctests found
172  in the `module`.
173
174  Options can also be given:
175
176    * `:except` - generates tests for all functions except those listed
177      (list of `{function, arity}` tuples, and/or `:moduledoc`).
178
179    * `:only` - generates tests only for functions listed
180      (list of `{function, arity}` tuples, and/or `:moduledoc`).
181
182    * `:import` - when `true`, one can test a function defined in the module
183      without referring to the module name. However, this is not feasible when
184      there is a clash with a module like `Kernel`. In these cases, `:import`
185      should be set to `false` and a full `Module.function` construct should be
186      used.
187
188    * `:tags` - a list of tags to apply to all generated doctests.
189
190  ## Examples
191
192      doctest MyModule, except: [:moduledoc, trick_fun: 1]
193
194  This macro is auto-imported with every `ExUnit.Case`.
195  """
196  defmacro doctest(module, opts \\ []) do
197    require =
198      if is_atom(Macro.expand(module, __CALLER__)) do
199        quote do
200          require unquote(module)
201        end
202      end
203
204    tests =
205      quote bind_quoted: [module: module, opts: opts] do
206        env = __ENV__
207        file = ExUnit.DocTest.__file__(module)
208
209        for {name, test} <- ExUnit.DocTest.__doctests__(module, opts) do
210          if tags = Keyword.get(opts, :tags) do
211            @tag tags
212          end
213
214          @file file
215          doc = ExUnit.Case.register_test(env, :doctest, name, [])
216          def unquote(doc)(_), do: unquote(test)
217        end
218      end
219
220    [require, tests]
221  end
222
223  @doc false
224  def __file__(module) do
225    source =
226      module.module_info(:compile)[:source] ||
227        raise "#{inspect(module)} does not have compile-time source information"
228
229    "(for doctest at) " <> Path.relative_to_cwd(source)
230  end
231
232  @doc false
233  def __doctests__(module, opts) do
234    do_import = Keyword.get(opts, :import, false)
235
236    extract(module)
237    |> filter_by_opts(opts)
238    |> Stream.with_index()
239    |> Enum.map(fn {test, acc} ->
240      compile_test(test, module, do_import, acc + 1)
241    end)
242  end
243
244  defp filter_by_opts(tests, opts) do
245    except = Keyword.get(opts, :except, [])
246
247    case Keyword.fetch(opts, :only) do
248      {:ok, []} ->
249        []
250
251      {:ok, only} ->
252        tests
253        |> Stream.reject(&(&1.fun_arity in except))
254        |> Stream.filter(&(&1.fun_arity in only))
255
256      :error ->
257        Stream.reject(tests, &(&1.fun_arity in except))
258    end
259  end
260
261  ## Compilation of extracted tests
262
263  defp compile_test(test, module, do_import, n) do
264    {test_name(test, module, n), test_content(test, module, do_import)}
265  end
266
267  defp test_name(%{fun_arity: :moduledoc}, m, n) do
268    "module #{inspect(m)} (#{n})"
269  end
270
271  defp test_name(%{fun_arity: {f, a}}, m, n) do
272    "#{inspect(m)}.#{f}/#{a} (#{n})"
273  end
274
275  defp test_content(%{exprs: exprs, line: line}, module, do_import) do
276    file = module.module_info(:compile)[:source] |> Path.relative_to_cwd()
277    location = [line: line, file: file]
278    stack = Macro.escape([{module, :__MODULE__, 0, location}])
279
280    if multiple_exceptions?(exprs) do
281      raise Error,
282        line: line,
283        module: module,
284        message:
285          "multiple exceptions in the same doctest example are not supported, " <>
286            "please separate your iex> prompts by multiple newlines to start new examples"
287    end
288
289    tests =
290      Enum.map(exprs, fn {expr, expected, formatted} ->
291        test_case_content(expr, expected, location, stack, formatted)
292      end)
293
294    {:__block__, [], test_import(module, do_import) ++ tests}
295  end
296
297  defp multiple_exceptions?(exprs) do
298    Enum.count(exprs, fn
299      {_, {:error, _, _}, _} -> true
300      _ -> false
301    end) > 1
302  end
303
304  defp test_case_content(expr, :test, location, stack, formatted) do
305    string_to_quoted(location, stack, expr, "\n" <> formatted) |> insert_assertions()
306  end
307
308  defp test_case_content(expr, {:test, expected}, location, stack, formatted) do
309    doctest = "\n" <> formatted <> "\n" <> expected
310    expr_ast = string_to_quoted(location, stack, expr, doctest) |> insert_assertions()
311    expected_ast = string_to_quoted(location, stack, expected, doctest)
312    last_expr = Macro.to_string(last_expr(expr_ast))
313
314    quote do
315      value = unquote(expr_ast)
316      expected = unquote(expected_ast)
317      formatted = unquote(formatted)
318      last_expr = unquote(last_expr)
319      expected_expr = unquote(expected)
320      stack = unquote(stack)
321
322      ExUnit.DocTest.__test__(value, expected, formatted, last_expr, expected_expr, stack)
323    end
324  end
325
326  defp test_case_content(expr, {:inspect, expected}, location, stack, formatted) do
327    doctest = "\n" <> formatted <> "\n" <> expected
328    expr_ast = string_to_quoted(location, stack, expr, doctest) |> insert_assertions()
329    expected_ast = string_to_quoted(location, stack, expected, doctest)
330    last_expr = Macro.to_string(last_expr(expr_ast))
331
332    quote do
333      value = unquote(expr_ast)
334      expected = unquote(expected_ast)
335      formatted = unquote(formatted)
336      last_expr = unquote(last_expr)
337      expected_expr = unquote(expected)
338      stack = unquote(stack)
339
340      ExUnit.DocTest.__inspect__(value, expected, formatted, last_expr, expected_expr, stack)
341    end
342  end
343
344  defp test_case_content(expr, {:error, exception, message}, location, stack, formatted) do
345    doctest = "\n" <> formatted <> "\n** (#{inspect(exception)}) #{inspect(message)}"
346    expr_ast = string_to_quoted(location, stack, expr, doctest)
347
348    quote do
349      stack = unquote(stack)
350      message = unquote(message)
351      formatted = unquote(formatted)
352      exception = unquote(exception)
353      ExUnit.DocTest.__error__(fn -> unquote(expr_ast) end, message, exception, formatted, stack)
354    end
355  end
356
357  @doc false
358  def __test__(value, expected, formatted, last_expr, expected_expr, stack) do
359    case value do
360      ^expected ->
361        :ok
362
363      _ ->
364        error = [
365          message: "Doctest failed",
366          doctest: "\n" <> formatted <> "\n" <> expected_expr,
367          expr: "#{last_expr} === #{String.trim(expected_expr)}",
368          left: value,
369          right: expected
370        ]
371
372        reraise ExUnit.AssertionError, error, stack
373    end
374  end
375
376  @doc false
377  def __inspect__(value, expected, formatted, last_expr, expected_expr, parent_stack) do
378    result =
379      try do
380        inspect(value, safe: false)
381      rescue
382        e ->
383          stack = Enum.drop(__STACKTRACE__, 1)
384          {[message: Exception.message(e)], ExUnit.Runner.prune_stacktrace(stack)}
385      else
386        ^expected -> :ok
387        actual -> {[left: actual, right: expected, message: "Doctest failed"], []}
388      end
389
390    case result do
391      :ok ->
392        :ok
393
394      {extra, stack} ->
395        doctest = "\n" <> formatted <> "\n" <> expected_expr
396        expr = "inspect(#{last_expr}) === #{String.trim(expected_expr)}"
397        error = [doctest: doctest, expr: expr] ++ extra
398        reraise ExUnit.AssertionError, error, stack ++ parent_stack
399    end
400  end
401
402  @doc false
403  def __error__(fun, message, exception, formatted, stack) do
404    try do
405      fun.()
406    rescue
407      error ->
408        actual_exception = error.__struct__
409        actual_message = Exception.message(error)
410
411        failed =
412          cond do
413            actual_exception != exception ->
414              "Doctest failed: expected exception #{inspect(exception)} but got " <>
415                "#{inspect(actual_exception)} with message #{inspect(actual_message)}"
416
417            actual_message != message ->
418              "Doctest failed: wrong message for #{inspect(actual_exception)}\n" <>
419                "expected:\n" <>
420                "  #{inspect(message)}\n" <>
421                "actual:\n" <> "  #{inspect(actual_message)}"
422
423            true ->
424              nil
425          end
426
427        if failed do
428          doctest = "\n" <> formatted <> "\n** (#{inspect(exception)}) #{inspect(message)}"
429          reraise ExUnit.AssertionError, [message: failed, doctest: doctest], stack
430        end
431    else
432      _ ->
433        doctest = "\n" <> formatted <> "\n** (#{inspect(exception)}) #{inspect(message)}"
434        failed = "Doctest failed: expected exception #{inspect(exception)} but nothing was raised"
435        error = [message: failed, doctest: doctest]
436        reraise ExUnit.AssertionError, error, stack
437    end
438  end
439
440  defp test_import(_mod, false), do: []
441  defp test_import(mod, _), do: [quote(do: import(unquote(mod)))]
442
443  defp string_to_quoted(location, stack, expr, doctest) do
444    try do
445      Code.string_to_quoted!(expr, location)
446    rescue
447      e ->
448        ex_message = "(#{inspect(e.__struct__)}) #{Exception.message(e)}"
449        message = "Doctest did not compile, got: #{ex_message}"
450
451        message =
452          if e.__struct__ == TokenMissingError and expr =~ @opaque_type_regex do
453            message <>
454              """
455              \nIf you are planning to assert on the result of an iex> expression \
456              which contains a value inspected as #Name<...>, please make sure \
457              the inspected value is placed at the beginning of the expression; \
458              otherwise Elixir will treat it as a comment due to the leading sign #.\
459              """
460          else
461            message
462          end
463
464        opts =
465          if String.valid?(doctest) do
466            [message: message, doctest: doctest]
467          else
468            [message: message]
469          end
470
471        quote do
472          reraise ExUnit.AssertionError, unquote(opts), unquote(stack)
473        end
474    end
475  end
476
477  ## Extraction of the tests
478
479  defp extract(module) do
480    case Code.fetch_docs(module) do
481      {:docs_v1, annotation, _, _, moduledoc, _, docs} ->
482        extract_from_moduledoc(annotation, moduledoc, module) ++
483          extract_from_docs(Enum.sort(docs), module)
484
485      {:error, reason} ->
486        raise Error,
487          module: module,
488          message:
489            "could not retrieve the documentation for module #{inspect(module)}. " <>
490              explain_docs_error(reason)
491    end
492  end
493
494  defp explain_docs_error(:module_not_found),
495    do: "The BEAM file of the module cannot be accessed"
496
497  defp explain_docs_error(:chunk_not_found),
498    do: "The module was not compiled with documentation"
499
500  defp explain_docs_error({:invalid_chunk, _}),
501    do: "The documentation chunk in the module is invalid"
502
503  defp extract_from_moduledoc(annotation, %{"en" => doc}, module) do
504    for test <- extract_tests(:erl_anno.line(annotation), doc, module) do
505      normalize_test(test, :moduledoc)
506    end
507  end
508
509  defp extract_from_moduledoc(_, _doc, _module), do: []
510
511  defp extract_from_docs(docs, module) do
512    for doc <- docs, doc <- extract_from_doc(doc, module), do: doc
513  end
514
515  defp extract_from_doc({{_, name, arity}, annotation, _, %{"en" => doc}, _}, module) do
516    line = :erl_anno.line(annotation)
517
518    for test <- extract_tests(line, doc, module) do
519      normalize_test(test, {name, arity})
520    end
521  end
522
523  defp extract_from_doc(_doc, _module),
524    do: []
525
526  defp extract_tests(line_no, doc, module) do
527    all_lines = String.split(doc, ["\r\n", "\n"], trim: false)
528    lines = adjust_indent(all_lines, line_no + 1, module)
529    extract_tests(lines, "", "", [], true, module, "")
530  end
531
532  @iex_prompt ["iex>", "iex("]
533  @dot_prompt ["...>", "...("]
534
535  defp adjust_indent(lines, line_no, module) do
536    adjust_indent(:text, lines, line_no, [], 0, module)
537  end
538
539  defp adjust_indent(_kind, [], _line_no, adjusted_lines, _indent, _module) do
540    Enum.reverse(adjusted_lines)
541  end
542
543  defp adjust_indent(:text, [line | rest], line_no, adjusted_lines, indent, module) do
544    case String.starts_with?(String.trim_leading(line), @iex_prompt) do
545      true ->
546        line_indent = get_indent(line, indent)
547        adjust_indent(:prompt, [line | rest], line_no, adjusted_lines, line_indent, module)
548
549      false ->
550        adjust_indent(:text, rest, line_no + 1, adjusted_lines, indent, module)
551    end
552  end
553
554  defp adjust_indent(kind, [line | rest], line_no, adjusted_lines, indent, module)
555       when kind in [:prompt, :after_prompt] do
556    stripped_line = strip_indent(line, indent)
557
558    case String.trim_leading(line) do
559      "" ->
560        :ok
561
562      ^stripped_line ->
563        :ok
564
565      _ ->
566        n_spaces = if indent == 1, do: "#{indent} space", else: "#{indent} spaces"
567
568        raise Error,
569          line: line_no,
570          module: module,
571          message: """
572          indentation level mismatch on doctest line: #{inspect(line)}
573
574          If you are planning to assert on the result of an `iex>` expression, \
575          make sure the result is indented at the beginning of `iex>`, which \
576          in this case is exactly #{n_spaces}.
577
578          If instead you have an `iex>` expression that spans over multiple lines, \
579          please make sure that each line after the first one begins with `...>`.
580          """
581    end
582
583    adjusted_lines = [{stripped_line, line_no} | adjusted_lines]
584
585    next =
586      cond do
587        kind == :prompt -> :after_prompt
588        String.starts_with?(stripped_line, @iex_prompt ++ @dot_prompt) -> :after_prompt
589        true -> :code
590      end
591
592    adjust_indent(next, rest, line_no + 1, adjusted_lines, indent, module)
593  end
594
595  defp adjust_indent(:code, [line | rest], line_no, adjusted_lines, indent, module) do
596    stripped_line = strip_indent(line, indent)
597
598    cond do
599      stripped_line == "" ->
600        adjusted_lines = [{stripped_line, line_no} | adjusted_lines]
601        adjust_indent(:text, rest, line_no + 1, adjusted_lines, 0, module)
602
603      String.starts_with?(String.trim_leading(line), @iex_prompt) ->
604        adjust_indent(:prompt, [line | rest], line_no, adjusted_lines, indent, module)
605
606      true ->
607        adjusted_lines = [{stripped_line, line_no} | adjusted_lines]
608        adjust_indent(:code, rest, line_no + 1, adjusted_lines, indent, module)
609    end
610  end
611
612  defp get_indent(line, current_indent) do
613    case :binary.match(line, "iex") do
614      {pos, _len} -> pos
615      :nomatch -> current_indent
616    end
617  end
618
619  defp strip_indent(line, indent) do
620    length = byte_size(line) - indent
621
622    if length > 0 do
623      binary_part(line, indent, length)
624    else
625      ""
626    end
627  end
628
629  @fences ["```", "~~~"]
630
631  defp extract_tests(lines, expr_acc, expected_acc, acc, new_test, module, formatted)
632
633  defp extract_tests([], "", "", [], _, _, _) do
634    []
635  end
636
637  defp extract_tests([], "", "", acc, _, _, _) do
638    Enum.reverse(acc)
639  end
640
641  # End of input and we've still got a test pending.
642  defp extract_tests([], expr_acc, expected_acc, [test | rest], _, _, formatted) do
643    test = add_expr(test, expr_acc, expected_acc, formatted)
644    Enum.reverse([test | rest])
645  end
646
647  # We've encountered the next test on an adjacent line. Put them into one group.
648  defp extract_tests(
649         [{"iex>" <> _, _} | _] = list,
650         expr_acc,
651         expected_acc,
652         [test | rest],
653         new_test,
654         module,
655         formatted
656       )
657       when expr_acc != "" and expected_acc != "" do
658    test = add_expr(test, expr_acc, expected_acc, formatted)
659    extract_tests(list, "", "", [test | rest], new_test, module, "")
660  end
661
662  # Store expr_acc and start a new test case.
663  defp extract_tests(
664         [{"iex>" <> string = line, line_no} | lines],
665         "",
666         expected_acc,
667         acc,
668         true,
669         module,
670         _
671       ) do
672    test = %{line: line_no, fun_arity: nil, exprs: []}
673    extract_tests(lines, string, expected_acc, [test | acc], false, module, line)
674  end
675
676  # Store expr_acc.
677  defp extract_tests(
678         [{"iex>" <> string = line, _} | lines],
679         "",
680         expected_acc,
681         acc,
682         false,
683         module,
684         _
685       ) do
686    extract_tests(lines, string, expected_acc, acc, false, module, line)
687  end
688
689  # Still gathering expr_acc. Synonym for the next clause.
690  defp extract_tests(
691         [{"iex>" <> string = line, _} | lines],
692         expr_acc,
693         expected_acc,
694         acc,
695         new_test,
696         module,
697         formatted
698       ) do
699    extract_tests(
700      lines,
701      expr_acc <> "\n" <> string,
702      expected_acc,
703      acc,
704      new_test,
705      module,
706      formatted <> "\n" <> line
707    )
708  end
709
710  # Still gathering expr_acc. Synonym for the previous clause.
711  defp extract_tests(
712         [{"...>" <> string = line, _} | lines],
713         expr_acc,
714         expected_acc,
715         acc,
716         new_test,
717         module,
718         formatted
719       )
720       when expr_acc != "" do
721    extract_tests(
722      lines,
723      expr_acc <> "\n" <> string,
724      expected_acc,
725      acc,
726      new_test,
727      module,
728      formatted <> "\n" <> line
729    )
730  end
731
732  # Expression numbers are simply skipped.
733  defp extract_tests(
734         [{<<"iex(", _>> <> string = line, line_no} | lines],
735         expr_acc,
736         expected_acc,
737         acc,
738         new_test,
739         module,
740         formatted
741       ) do
742    new_line = {"iex" <> skip_iex_number(string, module, line_no, line), line_no}
743    extract_tests([new_line | lines], expr_acc, expected_acc, acc, new_test, module, formatted)
744  end
745
746  # Expression numbers are simply skipped redux.
747  defp extract_tests(
748         [{<<"...(", _>> <> string, line_no} = line | lines],
749         expr_acc,
750         expected_acc,
751         acc,
752         new_test,
753         module,
754         formatted
755       ) do
756    new_line = {"..." <> skip_iex_number(string, module, line_no, line), line_no}
757    extract_tests([new_line | lines], expr_acc, expected_acc, acc, new_test, module, formatted)
758  end
759
760  # Skip empty or documentation line.
761  defp extract_tests([_ | lines], "", "", acc, _, module, _formatted) do
762    extract_tests(lines, "", "", acc, true, module, "")
763  end
764
765  # Encountered end of fenced code block, store pending test
766  defp extract_tests(
767         [{<<fence::3-bytes>> <> _, _} | lines],
768         expr_acc,
769         expected_acc,
770         [test | rest],
771         _new_test,
772         module,
773         formatted
774       )
775       when fence in @fences and expr_acc != "" do
776    test = add_expr(test, expr_acc, expected_acc, formatted)
777    extract_tests(lines, "", "", [test | rest], true, module, "")
778  end
779
780  # Encountered an empty line, store pending test
781  defp extract_tests(
782         [{"", _} | lines],
783         expr_acc,
784         expected_acc,
785         [test | rest],
786         _new_test,
787         module,
788         formatted
789       ) do
790    test = add_expr(test, expr_acc, expected_acc, formatted)
791    extract_tests(lines, "", "", [test | rest], true, module, "")
792  end
793
794  # Finally, parse expected_acc.
795  defp extract_tests([{expected, _} | lines], expr_acc, "", acc, new_test, module, formatted) do
796    extract_tests(lines, expr_acc, expected, acc, new_test, module, formatted)
797  end
798
799  defp extract_tests(
800         [{expected, _} | lines],
801         expr_acc,
802         expected_acc,
803         acc,
804         new_test,
805         module,
806         formatted
807       ) do
808    extract_tests(
809      lines,
810      expr_acc,
811      expected_acc <> "\n" <> expected,
812      acc,
813      new_test,
814      module,
815      formatted
816    )
817  end
818
819  defp skip_iex_number(")>" <> string, _module, _line_no, _line) do
820    ">" <> string
821  end
822
823  defp skip_iex_number("", module, line_no, line) do
824    message =
825      "unknown IEx prompt: #{inspect(line)}.\nAccepted formats are: iex>, iex(1)>, ...>, ...(1)>}"
826
827    raise Error, line: line_no, module: module, message: message
828  end
829
830  defp skip_iex_number(<<_>> <> string, module, line_no, line) do
831    skip_iex_number(string, module, line_no, line)
832  end
833
834  defp normalize_test(%{exprs: exprs} = test, fa) do
835    %{test | fun_arity: fa, exprs: Enum.reverse(exprs)}
836  end
837
838  defp add_expr(%{exprs: exprs} = test, expr, expected, formatted) do
839    %{test | exprs: [{expr, tag_expected(expected), formatted} | exprs]}
840  end
841
842  defp tag_expected(string) do
843    case string do
844      "" ->
845        :test
846
847      "** (" <> error ->
848        [mod, message] = :binary.split(error, ")")
849        {:error, Module.concat([mod]), String.trim_leading(message)}
850
851      _ ->
852        if inspectable?(string) do
853          {:inspect, inspect(string)}
854        else
855          {:test, string}
856        end
857    end
858  end
859
860  defp inspectable?(<<?#, char, rest::binary>>) when char in ?A..?Z, do: inspectable_end?(rest)
861  defp inspectable?(_), do: false
862
863  defp inspectable_end?(<<?., char, rest::binary>>) when char in ?A..?Z,
864    do: inspectable_end?(rest)
865
866  defp inspectable_end?(<<char, rest::binary>>)
867       when char in ?A..?Z
868       when char in ?a..?z
869       when char in ?0..?9
870       when char == ?_,
871       do: inspectable_end?(rest)
872
873  defp inspectable_end?(<<?<, _::binary>>), do: true
874  defp inspectable_end?(_), do: false
875
876  defp last_expr({:__block__, _, [_ | _] = block}), do: block |> List.last() |> last_expr()
877  defp last_expr(other), do: other
878
879  defp insert_assertions({:__block__, meta, block}),
880    do: {:__block__, meta, Enum.map(block, &insert_match_assertion/1)}
881
882  defp insert_assertions(ast),
883    do: insert_match_assertion(ast)
884
885  defp insert_match_assertion({:=, _, [{var, _, context}, _]} = ast)
886       when is_atom(var) and is_atom(context),
887       do: ast
888
889  defp insert_match_assertion({:=, meta, [left, right]}),
890    do: {{:., meta, [__MODULE__, :__assert__]}, meta, [{:=, meta, [left, right]}]}
891
892  defp insert_match_assertion(ast),
893    do: ast
894
895  @doc false
896  defmacro __assert__({:=, _, [left, right]} = assertion) do
897    code = Macro.escape(assertion, prune_metadata: true)
898    ExUnit.Assertions.__match__(left, right, code, :ok, __CALLER__)
899  end
900end
901