diff --git a/lib/blank_assertions.ex b/lib/blank_assertions.ex index 6fb9519..59df636 100644 --- a/lib/blank_assertions.ex +++ b/lib/blank_assertions.ex @@ -1,4 +1,6 @@ defmodule BlankAssertions do + require ExUnit.Assertions + defmacro assert(expr) do if contains_blank?(expr) do code = Macro.escape(expr) @@ -25,6 +27,19 @@ defmodule BlankAssertions do end end + defmacro assert_receive(expr) do + if contains_blank?(expr) do + code = Macro.escape(expr) + quote do + raise ExUnit.AssertionError, expr: unquote(code) + end + else + quote do + ExUnit.Assertions.assert_receive(unquote(expr), 100) + end + end + end + def assert(value, opts) do ExUnit.Assertions.assert(value, opts) end @@ -33,6 +48,10 @@ defmodule BlankAssertions do ExUnit.Assertions.refute(value, opts) end + def flunk(message \\ "Flunked!") do + assert false, message: message + end + defp contains_blank?(expr) do {_, blank} = Macro.prewalk(expr, false, &blank?/2) blank diff --git a/lib/display.ex b/lib/display.ex index 1a23b92..0252510 100644 --- a/lib/display.ex +++ b/lib/display.ex @@ -24,7 +24,8 @@ defmodule Display do end def display_failed_assertion(module, expr) do - "Assertion failed in #{source_file(module)}:#{line_number(expr)}" + file = source_file(module) + "Assertion failed in #{file}:#{line_number(expr, in: file)}" end def format_compile_error(error) do @@ -32,9 +33,39 @@ defmodule Display do IO.puts(format_red(Exception.format(:error, error, trace))) end - defp line_number({_, [line: line], _}) do + defp line_number({_, [line: line], _}, in: _) do line end + defp line_number(expr, in: file) do + expression = expr_to_s(expr) + {:ok, line} = File.open(file, fn(x) -> + IO.read(x, :all) + |> String.split("\n") + |> Enum.find_index(fn(candidate) -> String.contains?(candidate, expression) end) + |> one_based + end) + line + end + + defp one_based(line) when is_number(line), do: line + 1 + defp one_based(nil), do: "???" + + defp expr_to_s(tuple) when is_tuple(tuple) do + elements = tuple + |> Tuple.to_list + |> Enum.map(fn(x) -> to_s(x) end) + + "{#{Enum.join(elements, ", ")}}" + end + + defp expr_to_s(atom) when is_atom(atom) do + to_string(atom) + end + + def to_s(x) when is_atom(x) do + ":#{to_string(x)}" + end + def to_s(x), do: "\"#{x}\"" defp source_file(module) do module.__info__(:compile) diff --git a/lib/koans.ex b/lib/koans.ex index 674cdee..81854fb 100644 --- a/lib/koans.ex +++ b/lib/koans.ex @@ -15,10 +15,10 @@ defmodule Koans do end end - defmacro __using__(_) do + defmacro __using__(opts) do quote do - import Koans require ExUnit.Assertions + import Koans import BlankAssertions end end diff --git a/lib/koans/10_processes.ex b/lib/koans/10_processes.ex new file mode 100644 index 0000000..48168d7 --- /dev/null +++ b/lib/koans/10_processes.ex @@ -0,0 +1,53 @@ +defmodule Processes do + use Koans + + koan "tests run in a process!" do + assert Process.alive?(self) + end + + koan "can spew out information about a process" do + information = Process.info(self) + + assert information[:status] == :running + end + + koan "process can send messages to itself" do + send self(), "hola!" + receive do + message -> assert message == "hola!" + end + end + + koan "a spawned process is independent of the current process" do + pid = spawn(fn -> receive do + {:hello, thing} -> assert thing == "world" + _ -> assert false + end + end) + + send pid, {:hello, "world"} + end + + koan "a common pattern is to include the sender in the message" do + pid = spawn(fn -> receive do + {:hello, sender} -> send sender, :how_are_you? + _ -> assert false + end + end) + + send pid, {:hello, self()} + assert_receive :how_are_you? + end + + koan "you don't have to wait forever for messages" do + parent = self + spawn(fn -> receive do + _ -> assert false + after + 10 -> send parent, {:waited_too_long, "I am inpatient"} + end + end) + + assert_receive {:waited_too_long, "I am inpatient"} + end +end diff --git a/lib/meditate.ex b/lib/meditate.ex index c72e9cb..cc788f6 100644 --- a/lib/meditate.ex +++ b/lib/meditate.ex @@ -3,6 +3,8 @@ defmodule Mix.Tasks.Meditate do alias Options def run(args) do + ExUnit.start + Application.ensure_all_started(:ex_unit) Application.ensure_all_started(:elixir_koans) Code.compiler_options(ignore_module_conflict: true) Watcher.start diff --git a/lib/runner.ex b/lib/runner.ex index eaf8987..b792d46 100644 --- a/lib/runner.ex +++ b/lib/runner.ex @@ -8,7 +8,8 @@ defmodule Runner do Enums, Arithmetic, Structs, - PatternMatching + PatternMatching, + Processes ] def run do