Skip to content
Permalink
Browse files

Slightly refactor and improve doctests code

  • Loading branch information
josevalim committed Nov 29, 2013
1 parent 9607790 commit bbc0fe0a6344f6c698e5bdfee2a7744f5097c4f4
Showing with 40 additions and 28 deletions.
  1. +40 −28 lib/ex_unit/lib/ex_unit/doc_test.ex
@@ -132,25 +132,32 @@ defmodule ExUnit.DocTest do
defmacro doctest(mod, opts // []) do
quote bind_quoted: binding do
lc { name, test } inlist ExUnit.DocTest.__doctests__(mod, opts) do
@file '(for doctest at) ' ++ Path.relative_to_cwd(mod.__info__(:compile)[:source])
def unquote(name)(_), do: unquote(test)
end
end
end

@doc false
def __doctests__(module, opts) do
do_import = Keyword.get(opts, :import, false)

extract(module)
|> filter_by_opts(opts)
|> Stream.with_index
|> Enum.map(fn { test, acc } ->
compile_test(test, module, do_import, acc + 1)
end)
end

defp filter_by_opts(tests, opts) do
only = opts[:only] || []
except = opts[:except] || []
do_import = Keyword.get(opts, :import, false)

tests = Enum.filter(extract(module), fn(test) ->
Stream.filter(tests, fn(test) ->
fa = test.fun_arity
Enum.all?(except, &(&1 != fa)) and Enum.all?(only, &(&1 == fa))
end)

Enum.map_reduce(tests, 1, fn(test, acc) ->
{ compile_test(test, module, do_import, acc), acc + 1 }
end) |> elem(0)
end

## Compilation of extracted tests
@@ -172,35 +179,40 @@ defmodule ExUnit.DocTest do
location = [line: line, file: Path.relative_to_cwd(file)]
stack = Macro.escape [{ module, :__MODULE__, 0, location }]

exc_filter_fn = fn
{ _, {:error, _, _} } -> true
_ -> false
if multiple_exceptions?(exprs) do
{ fun, arity } = fun_arity
raise Error, message: "multiple exceptions in one doctest case are not supported. "
"Invalid doctest for #{inspect module}.#{fun}/#{arity}"
end

exceptions_num = Enum.count exprs, exc_filter_fn
if exceptions_num > 1 do
# Format the info about error location as if it were a part of the stacktrace
{ fun, arity } = fun_arity
error_info = " #{file}:#{line}: #{inspect module}.#{fun}/#{arity}"
raise Error, message: "multiple exceptions in one doctest case are not supported.\n#{error_info}"
tests = Enum.map exprs, fn { expr, expected } ->
test_case_content(expr, expected, module, line, file, stack)
end

{ tests, whole_expr } = Enum.map_reduce exprs, "", fn {expr, expected}, acc ->
{ test_case_content(expr, expected, module, line, file, stack), acc <> expr <> "\n" }
quote do
unquote_splicing(test_import(module, do_import))
unquote(gen_code_for_tests(tests, whole_expr(exprs), exception_expr(exprs), stack))
end
whole_expr = String.strip(whole_expr)
end

defp whole_expr(exprs) do
Enum.map_join(exprs, "\n", &elem(&1, 0))
end

exception = case Enum.find(exprs, exc_filter_fn) do
defp exception_expr(exprs) do
Enum.find_value(exprs, "nothing", fn
{ _, {:error, exception, message} } ->
inspect(exception) <> " with message " <> message
nil ->
"nothing"
end
_ ->
nil
end)
end

quote do
unquote_splicing(test_import(module, do_import))
unquote(gen_code_for_tests(tests, whole_expr, exception, stack))
end
defp multiple_exceptions?(exprs) do
Enum.count(exprs, fn
{ _, {:error, _, _} } -> true
_ -> false
end) > 1
end

defp gen_code_for_tests(tests, whole_expr, exception, stack) do
@@ -316,13 +328,13 @@ defmodule ExUnit.DocTest do
moduledocs ++ docs
end

defp extract_from_moduledoc({_, negative}) when negative in [false, nil], do: []
defp extract_from_moduledoc({_, doc}) when doc in [false, nil], do: []

defp extract_from_moduledoc({line, doc}) do
extract_tests(line, doc)
end

defp extract_from_doc({_, _, _, _, negative}) when negative in [false, nil], do: []
defp extract_from_doc({_, _, _, _, doc}) when doc in [false, nil], do: []

defp extract_from_doc({ fa, line, _, _, doc}) do
lc test inlist extract_tests(line, doc) do

0 comments on commit bbc0fe0

Please sign in to comment.
You can’t perform that action at this time.