add .formatter.exs + format

This commit is contained in:
Martino Visintin
2018-05-22 21:46:54 +01:00
parent 08c90ce0d8
commit b07df7c780
52 changed files with 332 additions and 264 deletions

3
.formatter.exs Normal file
View File

@@ -0,0 +1,3 @@
[
inputs: ["{config,lib,test}/**/*.{ex,exs}"]
]

View File

@@ -1,6 +1,7 @@
defmodule Blanks do defmodule Blanks do
def replace(ast, replacements) do def replace(ast, replacements) do
replacements = List.wrap(replacements) replacements = List.wrap(replacements)
ast ast
|> Macro.prewalk(replacements, &pre/2) |> Macro.prewalk(replacements, &pre/2)
|> elem(0) |> elem(0)
@@ -10,6 +11,7 @@ defmodule Blanks do
{args, replacements} = Macro.prewalk(args, replacements, &pre_pin/2) {args, replacements} = Macro.prewalk(args, replacements, &pre_pin/2)
{put_elem(node, 2, args), replacements} {put_elem(node, 2, args), replacements}
end end
defp pre({:___, _, _}, [first | remainder]), do: {first, remainder} defp pre({:___, _, _}, [first | remainder]), do: {first, remainder}
defp pre(node, acc), do: {node, acc} defp pre(node, acc), do: {node, acc}
@@ -21,6 +23,7 @@ defmodule Blanks do
^unquote(var) ^unquote(var)
end end
end end
defp pin(var), do: var defp pin(var), do: var
def count(ast) do def count(ast) do
@@ -29,16 +32,18 @@ defmodule Blanks do
|> elem(1) |> elem(1)
end end
defp count({:___, _, _} = node, acc), do: {node, acc+1} defp count({:___, _, _} = node, acc), do: {node, acc + 1}
defp count(node, acc), do: {node, acc} defp count(node, acc), do: {node, acc}
def replace_line({:__block__, meta, lines}, replacement_fn) do def replace_line({:__block__, meta, lines}, replacement_fn) do
replaced_lines = Enum.map(lines, fn(line) -> replaced_lines =
replace_line(line, replacement_fn) Enum.map(lines, fn line ->
end) replace_line(line, replacement_fn)
end)
{:__block__, meta, replaced_lines} {:__block__, meta, replaced_lines}
end end
def replace_line(line, replacement_fn) do def replace_line(line, replacement_fn) do
if count(line) > 0 do if count(line) > 0 do
replacement_fn.(line) replacement_fn.(line)

View File

@@ -17,33 +17,34 @@ defmodule Display do
end end
def handle_cast(:clear_screen, state = %{clear_screen: true}) do def handle_cast(:clear_screen, state = %{clear_screen: true}) do
IO.puts(ANSI.clear) IO.puts(ANSI.clear())
IO.puts(ANSI.home) IO.puts(ANSI.home())
{:noreply, state} {:noreply, state}
end end
def handle_cast(:clear_screen, state) do def handle_cast(:clear_screen, state) do
{:noreply, state} {:noreply, state}
end end
def invalid_koan(koan, modules) do def invalid_koan(koan, modules) do
Notifications.invalid_koan(koan, modules) Notifications.invalid_koan(koan, modules)
|> IO.puts |> IO.puts()
end end
def show_failure(failure, module, name) do def show_failure(failure, module, name) do
format(failure, module, name) format(failure, module, name)
|> IO.puts |> IO.puts()
end end
def show_compile_error(error) do def show_compile_error(error) do
Failure.show_compile_error(error) Failure.show_compile_error(error)
|> IO.puts |> IO.puts()
end end
def congratulate do def congratulate do
Notifications.congratulate Notifications.congratulate()
|> IO.puts |> IO.puts()
end end
def clear_screen do def clear_screen do
@@ -52,9 +53,9 @@ defmodule Display do
defp format(failure, module, name) do defp format(failure, module, name) do
""" """
#{Intro.intro(module, Tracker.visited)} #{Intro.intro(module, Tracker.visited())}
Now meditate upon #{format_module(module)} Now meditate upon #{format_module(module)}
#{ProgressBar.progress_bar(Tracker.summarize)} #{ProgressBar.progress_bar(Tracker.summarize())}
---------------------------------------- ----------------------------------------
#{name} #{name}
#{Failure.format_failure(failure)} #{Failure.format_failure(failure)}
@@ -62,6 +63,6 @@ defmodule Display do
end end
defp format_module(module) do defp format_module(module) do
Module.split(module) |> List.last Module.split(module) |> List.last()
end end
end end

View File

@@ -5,7 +5,7 @@ defmodule Display.Paint do
def yellow(str), do: painter().yellow(str) def yellow(str), do: painter().yellow(str)
defp painter do defp painter do
case Mix.env do case Mix.env() do
:test -> Display.Uncoloured :test -> Display.Uncoloured
_ -> Display.Colours _ -> Display.Colours
end end
@@ -15,13 +15,13 @@ end
defmodule Display.Colours do defmodule Display.Colours do
alias IO.ANSI alias IO.ANSI
def red(str), do: colourize(ANSI.red, str) def red(str), do: colourize(ANSI.red(), str)
def cyan(str), do: colourize(ANSI.cyan, str) def cyan(str), do: colourize(ANSI.cyan(), str)
def green(str), do: colourize(ANSI.green, str) def green(str), do: colourize(ANSI.green(), str)
def yellow(str), do: colourize(ANSI.yellow, str) def yellow(str), do: colourize(ANSI.yellow(), str)
defp colourize(color, message) do defp colourize(color, message) do
Enum.join([color, message, ANSI.reset], "") Enum.join([color, message, ANSI.reset()], "")
end end
end end

View File

@@ -3,12 +3,17 @@ defmodule Display.Failure do
@no_value :ex_unit_no_meaningful_value @no_value :ex_unit_no_meaningful_value
def format_failure(%{error: %ExUnit.AssertionError{expr: @no_value, message: message}, file: file, line: line}) do def format_failure(%{
error: %ExUnit.AssertionError{expr: @no_value, message: message},
file: file,
line: line
}) do
""" """
#{Paint.cyan("Assertion failed in #{file}:#{line}")} #{Paint.cyan("Assertion failed in #{file}:#{line}")}
#{Paint.red(message)} #{Paint.red(message)}
""" """
end end
def format_failure(%{error: %ExUnit.AssertionError{expr: expr} = error, file: file, line: line}) do def format_failure(%{error: %ExUnit.AssertionError{expr: expr} = error, file: file, line: line}) do
""" """
#{Paint.cyan("Assertion failed in #{file}:#{line}")} #{Paint.cyan("Assertion failed in #{file}:#{line}")}
@@ -16,6 +21,7 @@ defmodule Display.Failure do
""" """
|> format_inequality(error) |> format_inequality(error)
end end
def format_failure(%{error: error, file: file, line: line}) do def format_failure(%{error: error, file: file, line: line}) do
""" """
#{Paint.cyan("Error in #{file}:#{line}")} #{Paint.cyan("Error in #{file}:#{line}")}
@@ -26,22 +32,24 @@ defmodule Display.Failure do
defp format_inequality(message, %{left: @no_value, right: @no_value}) do defp format_inequality(message, %{left: @no_value, right: @no_value}) do
message message
end end
defp format_inequality(message, %{left: @no_value, right: match_value}) do defp format_inequality(message, %{left: @no_value, right: match_value}) do
""" """
#{message} #{message}
value does not match: #{match_value |> inspect |> Paint.yellow} value does not match: #{match_value |> inspect |> Paint.yellow()}
""" """
end end
defp format_inequality(message, %{left: left, right: right}) do defp format_inequality(message, %{left: left, right: right}) do
""" """
#{message} #{message}
left: #{left |> inspect |> Paint.yellow} left: #{left |> inspect |> Paint.yellow()}
right: #{right |> inspect |> Paint.yellow} right: #{right |> inspect |> Paint.yellow()}
""" """
end end
defp format_error(error) do defp format_error(error) do
trace = System.stacktrace |> Enum.take(2) trace = System.stacktrace() |> Enum.take(2)
Paint.red(Exception.format(:error, error, trace)) Paint.red(Exception.format(:error, error, trace))
end end

View File

@@ -2,7 +2,7 @@ defmodule Display.Intro do
alias Display.Paint alias Display.Paint
def intro(module, modules) do def intro(module, modules) do
if not module in modules do if not (module in modules) do
show_intro(module.intro) show_intro(module.intro)
else else
"" ""
@@ -10,7 +10,7 @@ defmodule Display.Intro do
end end
def show_intro(message) do def show_intro(message) do
message <> "\n" (message <> "\n")
|> Paint.green |> Paint.green()
end end
end end

View File

@@ -15,7 +15,7 @@ defmodule Display.Notifications do
|> Enum.map(&Atom.to_string/1) |> Enum.map(&Atom.to_string/1)
|> Enum.map(&name/1) |> Enum.map(&name/1)
|> Enum.join(", ") |> Enum.join(", ")
|> Paint.red |> Paint.red()
end end
defp name("Elixir." <> module), do: module defp name("Elixir." <> module), do: module

View File

@@ -1,5 +1,4 @@
defmodule Display.ProgressBar do defmodule Display.ProgressBar do
@progress_bar_length 30 @progress_bar_length 30
def progress_bar(%{current: current, total: total}) do def progress_bar(%{current: current, total: total}) do
@@ -9,11 +8,12 @@ defmodule Display.ProgressBar do
end end
defp calculate_progress(current, total) do defp calculate_progress(current, total) do
round( (current/total) * @progress_bar_length) round(current / total * @progress_bar_length)
end end
defp build_arrow(0), do: "" defp build_arrow(0), do: ""
defp build_arrow(length) do defp build_arrow(length) do
String.duplicate("=", length-1) <> ">" String.duplicate("=", length - 1) <> ">"
end end
end end

View File

@@ -1,6 +1,6 @@
defmodule Execute do defmodule Execute do
def run_module(module, callback \\ fn(_result, _module, _koan) -> nil end) do def run_module(module, callback \\ fn _result, _module, _koan -> nil end) do
Enum.reduce_while(module.all_koans, :passed, fn(koan, _) -> Enum.reduce_while(module.all_koans, :passed, fn koan, _ ->
module module
|> run_koan(koan) |> run_koan(koan)
|> hook(module, koan, callback) |> hook(module, koan, callback)
@@ -24,7 +24,7 @@ defmodule Execute do
def listen_for_result(module, name) do def listen_for_result(module, name) do
receive do receive do
:ok -> :passed :ok -> :passed
%{error: _} = failure -> {:failed, failure, module, name} %{error: _} = failure -> {:failed, failure, module, name}
_ -> listen_for_result(module, name) _ -> listen_for_result(module, name)
end end
@@ -32,23 +32,25 @@ defmodule Execute do
defp exec(module, name, args, parent) do defp exec(module, name, args, parent) do
result = apply(module, name, args) result = apply(module, name, args)
send parent, expand(result, module) send(parent, expand(result, module))
Process.exit(self(), :kill) Process.exit(self(), :kill)
end end
defp expand(:ok, _), do: :ok defp expand(:ok, _), do: :ok
defp expand(error, module) do defp expand(error, module) do
{file, line} = System.stacktrace {file, line} =
|> Enum.drop_while(&!in_koan?(&1, module)) System.stacktrace()
|> List.first |> Enum.drop_while(&(!in_koan?(&1, module)))
|> List.first()
|> extract_file_and_line |> extract_file_and_line
%{error: error, file: file, line: line} %{error: error, file: file, line: line}
end end
defp in_koan?({module, _, _, _}, koan), do: module == koan defp in_koan?({module, _, _, _}, koan), do: module == koan
defp extract_file_and_line({_, _, _, [file: file, line: line]}) do defp extract_file_and_line({_, _, _, [file: file, line: line]}) do
{file, line} {file, line}
end end
end end

View File

@@ -29,8 +29,10 @@ defmodule Koans do
end end
defmacro generate_test_method(_name, 0, _body), do: false defmacro generate_test_method(_name, 0, _body), do: false
defmacro generate_test_method(name, 1, body) do defmacro generate_test_method(name, 1, body) do
single_var = Blanks.replace(body, Macro.var(:answer, __MODULE__)) single_var = Blanks.replace(body, Macro.var(:answer, __MODULE__))
quote do quote do
def unquote(name)(answer) do def unquote(name)(answer) do
try do try do
@@ -42,8 +44,11 @@ defmodule Koans do
end end
end end
end end
defmacro generate_test_method(name, number_of_args, body) do defmacro generate_test_method(name, number_of_args, body) do
answer_vars = for id <- 1..number_of_args, do: Macro.var(String.to_atom("answer#{id}"), __MODULE__) answer_vars =
for id <- 1..number_of_args, do: Macro.var(String.to_atom("answer#{id}"), __MODULE__)
multi_var = Blanks.replace(body, answer_vars) multi_var = Blanks.replace(body, answer_vars)
quote do quote do
@@ -60,12 +65,12 @@ defmodule Koans do
defp blank_line_replacement({:assert, _meta, [expr]}) do defp blank_line_replacement({:assert, _meta, [expr]}) do
code = Macro.escape(expr) code = Macro.escape(expr)
quote do: raise ExUnit.AssertionError, expr: unquote(code) quote do: raise(ExUnit.AssertionError, expr: unquote(code))
end end
defp blank_line_replacement(line) do defp blank_line_replacement(line) do
code = Macro.escape(line) code = Macro.escape(line)
quote do: raise ExUnit.AssertionError, expr: unquote(code) quote do: raise(ExUnit.AssertionError, expr: unquote(code))
end end
defmacro __using__(_opts) do defmacro __using__(_opts) do
@@ -83,6 +88,7 @@ defmodule Koans do
defmacro __before_compile__(env) do defmacro __before_compile__(env) do
koans = koans(env) koans = koans(env)
quote do quote do
def all_koans do def all_koans do
unquote(koans) unquote(koans)
@@ -95,6 +101,6 @@ defmodule Koans do
defp koans(env) do defp koans(env) do
env.module env.module
|> Module.get_attribute(:koans) |> Module.get_attribute(:koans)
|> Enum.reverse |> Enum.reverse()
end end
end end

View File

@@ -1,7 +1,7 @@
defmodule Equalities do defmodule Equalities do
use Koans use Koans
@intro """ @intro """
Welcome to the Elixir koans. Welcome to the Elixir koans.
Let these be your first humble steps towards learning a new language. Let these be your first humble steps towards learning a new language.

View File

@@ -11,12 +11,12 @@ defmodule Atoms do
koan "It is surprising to find out that booleans are atoms" do koan "It is surprising to find out that booleans are atoms" do
assert is_atom(true) == ___ assert is_atom(true) == ___
assert is_boolean(false) == ___ assert is_boolean(false) == ___
assert :true == ___ assert true == ___
assert :false == ___ assert false == ___
end end
koan "Like booleans, the nil value is also an atom" do koan "Like booleans, the nil value is also an atom" do
assert is_atom(nil) == ___ assert is_atom(nil) == ___
assert :nil == ___ assert nil == ___
end end
end end

View File

@@ -6,13 +6,11 @@ defmodule Maps do
@person %{ @person %{
first_name: "Jon", first_name: "Jon",
last_name: "Snow", last_name: "Snow",
age: 27, age: 27
} }
koan "Maps represent structured data, like a person" do koan "Maps represent structured data, like a person" do
assert @person == %{first_name: ___, assert @person == %{first_name: ___, last_name: "Snow", age: 27}
last_name: "Snow",
age: 27 }
end end
koan "Fetching a value returns a tuple with ok when it exists" do koan "Fetching a value returns a tuple with ok when it exists" do

View File

@@ -49,7 +49,7 @@ defmodule Structs do
koan "Use the update_in macro to modify a nested value" do koan "Use the update_in macro to modify a nested value" do
airline = %Airline{plane: %Plane{maker: :boeing, passengers: 200}} airline = %Airline{plane: %Plane{maker: :boeing, passengers: 200}}
assert update_in(airline.plane.passengers, fn(x) -> (x + 2) end) == ___ assert update_in(airline.plane.passengers, fn x -> x + 2 end) == ___
end end
koan "Use the put_in macro with atoms to replace a nested value in a non-struct" do koan "Use the put_in macro with atoms to replace a nested value in a non-struct" do

View File

@@ -17,7 +17,7 @@ defmodule Sigils do
end end
koan "The lowercase ~s sigil supports string interpolation" do koan "The lowercase ~s sigil supports string interpolation" do
assert ~s[1 + 1 = #{1+1}] == ___ assert ~s[1 + 1 = #{1 + 1}] == ___
end end
koan "The ~S sigil is similar to ~s but doesn't do interpolation" do koan "The ~S sigil is similar to ~s but doesn't do interpolation" do
@@ -29,7 +29,7 @@ defmodule Sigils do
end end
koan "The ~w sigil also allows interpolation" do koan "The ~w sigil also allows interpolation" do
assert ~w(Hello 1#{1+1}3) == ___ assert ~w(Hello 1#{1 + 1}3) == ___
end end
koan "The ~W sigil behaves to ~w as ~S behaves to ~s" do koan "The ~W sigil behaves to ~w as ~S behaves to ~s" do

View File

@@ -41,7 +41,7 @@ defmodule PatternMatching do
koan "Lists must match exactly" do koan "Lists must match exactly" do
assert_raise ___, fn -> assert_raise ___, fn ->
[a, b] = [1,2,3] [a, b] = [1, 2, 3]
end end
end end
@@ -71,9 +71,9 @@ defmodule PatternMatching do
koan "And they will only run the code that matches the argument" do koan "And they will only run the code that matches the argument" do
name = fn name = fn
("duck") -> "Donald" "duck" -> "Donald"
("mouse") -> "Mickey" "mouse" -> "Mickey"
(_other) -> "I need a name!" _other -> "I need a name!"
end end
assert name.("mouse") == ___ assert name.("mouse") == ___
@@ -84,10 +84,11 @@ defmodule PatternMatching do
koan "Errors are shaped differently than successful results" do koan "Errors are shaped differently than successful results" do
dog = %{type: "dog"} dog = %{type: "dog"}
result = case Map.fetch(dog, :type) do result =
{:ok, value} -> value case Map.fetch(dog, :type) do
:error -> "not present" {:ok, value} -> value
end :error -> "not present"
end
assert result == ___ assert result == ___
end end
@@ -133,10 +134,11 @@ defmodule PatternMatching do
pinned_variable = 1 pinned_variable = 1
example = fn example = fn
(^pinned_variable) -> "The number One" ^pinned_variable -> "The number One"
(2) -> "The number Two" 2 -> "The number Two"
(number) -> "The number #{number}" number -> "The number #{number}"
end end
assert example.(1) == ___ assert example.(1) == ___
assert example.(2) == ___ assert example.(2) == ___
assert example.(3) == ___ assert example.(3) == ___
@@ -144,17 +146,20 @@ defmodule PatternMatching do
koan "Pinning works anywhere one would match, including 'case'" do koan "Pinning works anywhere one would match, including 'case'" do
pinned_variable = 1 pinned_variable = 1
result = case 1 do
^pinned_variable -> "same" result =
other -> "different #{other}" case 1 do
end ^pinned_variable -> "same"
other -> "different #{other}"
end
assert result == ___ assert result == ___
end end
koan "Trying to rebind a pinned variable will result in an error" do koan "Trying to rebind a pinned variable will result in an error" do
a = 1 a = 1
assert_raise MatchError, fn() ->
assert_raise MatchError, fn ->
^a = ___ ^a = ___
end end
end end

View File

@@ -12,6 +12,7 @@ defmodule Functions do
end end
def multiply(a, b), do: a * b def multiply(a, b), do: a * b
koan "Single line functions are cool, but mind the comma and the colon!" do koan "Single line functions are cool, but mind the comma and the colon!" do
assert 6 == multiply(2, ___) assert 6 == multiply(2, ___)
end end
@@ -41,8 +42,8 @@ defmodule Functions do
assert sum_up(1) == ___ assert sum_up(1) == ___
end end
def bigger(a,b) when a > b, do: "#{a} is bigger than #{b}" def bigger(a, b) when a > b, do: "#{a} is bigger than #{b}"
def bigger(a,b) when a <= b, do: "#{a} is not bigger than #{b}" def bigger(a, b) when a <= b, do: "#{a} is not bigger than #{b}"
koan "Intricate guards are possible, but be mindful of the reader" do koan "Intricate guards are possible, but be mindful of the reader" do
assert bigger(10, 5) == ___ assert bigger(10, 5) == ___
@@ -58,13 +59,13 @@ defmodule Functions do
end end
koan "Little anonymous functions are common, and called with a dot" do koan "Little anonymous functions are common, and called with a dot" do
multiply = fn (a,b) -> a * b end multiply = fn a, b -> a * b end
assert multiply.(2,3) == ___ assert multiply.(2, 3) == ___
end end
koan "You can even go shorter, by using capture syntax `&()` and positional arguments" do koan "You can even go shorter, by using capture syntax `&()` and positional arguments" do
multiply = &(&1 * &2) multiply = &(&1 * &2)
assert multiply.(2,3) == ___ assert multiply.(2, 3) == ___
end end
koan "Prefix a string with & to build a simple anonymous greet function" do koan "Prefix a string with & to build a simple anonymous greet function" do
@@ -77,7 +78,7 @@ defmodule Functions do
assert three_times.("foo") == ___ assert three_times.("foo") == ___
end end
def times_five_and_then(number, fun), do: fun.(number*5) def times_five_and_then(number, fun), do: fun.(number * 5)
def square(number), do: number * number def square(number), do: number * number
koan "You can pass functions around as arguments. Place an '&' before the name and state the arity" do koan "You can pass functions around as arguments. Place an '&' before the name and state the arity" do
@@ -90,10 +91,11 @@ defmodule Functions do
end end
koan "The result of a function can be piped into another function as its first argument" do koan "The result of a function can be piped into another function as its first argument" do
result = "full-name" result =
|> String.split("-") "full-name"
|> Enum.map(&String.capitalize/1) |> String.split("-")
|> Enum.join(" ") |> Enum.map(&String.capitalize/1)
|> Enum.join(" ")
assert result == ___ assert result == ___
end end

View File

@@ -12,12 +12,14 @@ defmodule Enums do
end end
def less_than_five?(n), do: n < 5 def less_than_five?(n), do: n < 5
koan "Elements can have a lot in common" do koan "Elements can have a lot in common" do
assert Enum.all?([1, 2, 3], &less_than_five?/1) == ___ assert Enum.all?([1, 2, 3], &less_than_five?/1) == ___
assert Enum.all?([4, 6, 8], &less_than_five?/1) == ___ assert Enum.all?([4, 6, 8], &less_than_five?/1) == ___
end end
def even?(n), do: rem(n, 2) == 0 def even?(n), do: rem(n, 2) == 0
koan "Sometimes you just want to know if there are any elements fulfilling a condition" do koan "Sometimes you just want to know if there are any elements fulfilling a condition" do
assert Enum.any?([1, 2, 3], &even?/1) == ___ assert Enum.any?([1, 2, 3], &even?/1) == ___
assert Enum.any?([1, 3, 5], &even?/1) == ___ assert Enum.any?([1, 3, 5], &even?/1) == ___
@@ -30,6 +32,7 @@ defmodule Enums do
end end
def multiply_by_ten(n), do: 10 * n def multiply_by_ten(n), do: 10 * n
koan "Map converts each element of a list by running some function with it" do koan "Map converts each element of a list by running some function with it" do
assert Enum.map([1, 2, 3], &multiply_by_ten/1) == ___ assert Enum.map([1, 2, 3], &multiply_by_ten/1) == ___
end end
@@ -67,6 +70,7 @@ defmodule Enums do
end end
def divisible_by_five?(n), do: rem(n, 5) == 0 def divisible_by_five?(n), do: rem(n, 5) == 0
koan "...but you don't quite find it..." do koan "...but you don't quite find it..." do
assert Enum.find([1, 2, 3], &divisible_by_five?/1) == ___ assert Enum.find([1, 2, 3], &divisible_by_five?/1) == ___
end end
@@ -76,6 +80,6 @@ defmodule Enums do
end end
koan "Collapse an entire list of elements down to a single one by repeating a function." do koan "Collapse an entire list of elements down to a single one by repeating a function." do
assert Enum.reduce([1, 2, 3], 0, fn(element, accumulator) -> element + accumulator end) == ___ assert Enum.reduce([1, 2, 3], 0, fn element, accumulator -> element + accumulator end) == ___
end end
end end

View File

@@ -18,9 +18,11 @@ defmodule Processes do
end end
koan "New processes are spawned functions" do koan "New processes are spawned functions" do
value = spawn(fn -> receive do value =
end spawn(fn ->
end) receive do
end
end)
assert is_pid(value) == ___ assert is_pid(value) == ___
end end
@@ -39,7 +41,7 @@ defmodule Processes do
end end
koan "Processes can send and receive messages" do koan "Processes can send and receive messages" do
send self(), "hola!" send(self(), "hola!")
receive do receive do
msg -> assert msg == ___ msg -> assert msg == ___
@@ -58,8 +60,8 @@ defmodule Processes do
end end
koan "Received messages are queued, first in first out" do koan "Received messages are queued, first in first out" do
send self(), "hola!" send(self(), "hola!")
send self(), "como se llama?" send(self(), "como se llama?")
assert_receive ___ assert_receive ___
assert_receive ___ assert_receive ___
@@ -68,20 +70,20 @@ defmodule Processes do
koan "A common pattern is to include the sender in the message, so that it can reply" do koan "A common pattern is to include the sender in the message, so that it can reply" do
greeter = fn -> greeter = fn ->
receive do receive do
{:hello, sender} -> send sender, :how_are_you? {:hello, sender} -> send(sender, :how_are_you?)
end end
end end
pid = spawn(greeter) pid = spawn(greeter)
send pid, {:hello, self()} send(pid, {:hello, self()})
assert_receive ___ assert_receive ___
end end
def yelling_echo_loop do def yelling_echo_loop do
receive do receive do
{caller, value} -> {caller, value} ->
send caller, String.upcase(value) send(caller, String.upcase(value))
yelling_echo_loop() yelling_echo_loop()
end end
end end
@@ -89,18 +91,19 @@ defmodule Processes do
koan "Use tail recursion to receive multiple messages" do koan "Use tail recursion to receive multiple messages" do
pid = spawn_link(&yelling_echo_loop/0) pid = spawn_link(&yelling_echo_loop/0)
send pid, {self(), "o"} send(pid, {self(), "o"})
assert_receive ___ assert_receive ___
send pid, {self(), "hai"} send(pid, {self(), "hai"})
assert_receive ___ assert_receive ___
end end
def state(value) do def state(value) do
receive do receive do
{caller, :get} -> {caller, :get} ->
send caller, value send(caller, value)
state(value) state(value)
{caller, :set, new_value} -> {caller, :set, new_value} ->
state(new_value) state(new_value)
end end
@@ -108,38 +111,45 @@ defmodule Processes do
koan "Processes can be used to hold state" do koan "Processes can be used to hold state" do
initial_state = "foo" initial_state = "foo"
pid = spawn(fn ->
state(initial_state)
end)
send pid, {self(), :get} pid =
spawn(fn ->
state(initial_state)
end)
send(pid, {self(), :get})
assert_receive ___ assert_receive ___
send pid, {self(), :set, "bar"} send(pid, {self(), :set, "bar"})
send pid, {self(), :get} send(pid, {self(), :get})
assert_receive ___ assert_receive ___
end end
koan "Waiting for a message can get boring" do koan "Waiting for a message can get boring" do
parent = self() parent = self()
spawn(fn -> receive do
after spawn(fn ->
5 -> send parent, {:waited_too_long, "I am impatient"} receive do
end after
end) 5 -> send(parent, {:waited_too_long, "I am impatient"})
end
end)
assert_receive ___ assert_receive ___
end end
koan "Trapping will allow you to react to someone terminating the process" do koan "Trapping will allow you to react to someone terminating the process" do
parent = self() parent = self()
pid = spawn(fn ->
Process.flag(:trap_exit, true) pid =
send parent, :ready spawn(fn ->
receive do Process.flag(:trap_exit, true)
{:EXIT, _pid, reason} -> send parent, {:exited, reason} send(parent, :ready)
end
end) receive do
{:EXIT, _pid, reason} -> send(parent, {:exited, reason})
end
end)
receive do receive do
:ready -> true :ready -> true

View File

@@ -10,23 +10,26 @@ defmodule Tasks do
end end
koan "If you don't need a result, use start_link/1" do koan "If you don't need a result, use start_link/1" do
{result, _pid} = Task.start_link(fn -> 1+1 end) {result, _pid} = Task.start_link(fn -> 1 + 1 end)
assert result == ___ assert result == ___
end end
koan "Yield returns nil if the task isn't done yet" do koan "Yield returns nil if the task isn't done yet" do
handle = Task.async(fn -> handle =
:timer.sleep(100) Task.async(fn ->
3 * 3 :timer.sleep(100)
end) 3 * 3
end)
assert Task.yield(handle, 10) == ___ assert Task.yield(handle, 10) == ___
end end
koan "Tasks can be aborted with shutdown" do koan "Tasks can be aborted with shutdown" do
handle = Task.async(fn -> handle =
:timer.sleep(100) Task.async(fn ->
3 * 3 :timer.sleep(100)
end) 3 * 3
end)
%Task{pid: pid} = handle %Task{pid: pid} = handle
Task.shutdown(handle) Task.shutdown(handle)
@@ -41,10 +44,11 @@ defmodule Tasks do
end end
koan "You can yield to multiple tasks at once and extract the results" do koan "You can yield to multiple tasks at once and extract the results" do
squares = [1, 2, 3, 4] squares =
|> Enum.map(fn(number) -> Task.async(fn -> number * number end) end) [1, 2, 3, 4]
|> Task.yield_many(100) |> Enum.map(fn number -> Task.async(fn -> number * number end) end)
|> Enum.map(fn({_task,{:ok, result}}) -> result end) |> Task.yield_many(100)
|> Enum.map(fn {_task, {:ok, result}} -> result end)
assert squares == ___ assert squares == ___
end end

View File

@@ -5,36 +5,38 @@ defmodule Agents do
koan "Agents maintain state, so you can ask them about it" do koan "Agents maintain state, so you can ask them about it" do
{:ok, pid} = Agent.start_link(fn -> "Hi there" end) {:ok, pid} = Agent.start_link(fn -> "Hi there" end)
assert Agent.get(pid, &(&1)) == ___ assert Agent.get(pid, & &1) == ___
end end
koan "Agents may also be named so that you don't have to keep the pid around" do koan "Agents may also be named so that you don't have to keep the pid around" do
Agent.start_link(fn -> "Why hello" end, name: AgentSmith) Agent.start_link(fn -> "Why hello" end, name: AgentSmith)
assert Agent.get(AgentSmith, &(&1)) == ___ assert Agent.get(AgentSmith, & &1) == ___
end end
koan "Update to update the state" do koan "Update to update the state" do
Agent.start_link(fn() -> "Hi there" end, name: __MODULE__) Agent.start_link(fn -> "Hi there" end, name: __MODULE__)
Agent.update(__MODULE__, fn(old) -> Agent.update(__MODULE__, fn old ->
String.upcase(old) String.upcase(old)
end) end)
assert Agent.get(__MODULE__, &(&1)) == ___
assert Agent.get(__MODULE__, & &1) == ___
end end
koan "Use get_and_update when you need to read and change a value in one go" do koan "Use get_and_update when you need to read and change a value in one go" do
Agent.start_link(fn() -> ["Milk"] end, name: __MODULE__) Agent.start_link(fn -> ["Milk"] end, name: __MODULE__)
old_list = Agent.get_and_update(__MODULE__, fn(old) -> old_list =
{old, ["Bread" | old]} Agent.get_and_update(__MODULE__, fn old ->
end) {old, ["Bread" | old]}
end)
assert old_list == ___ assert old_list == ___
assert Agent.get(__MODULE__, &(&1)) == ___ assert Agent.get(__MODULE__, & &1) == ___
end end
koan "Somebody has to switch off the light at the end of the day" do koan "Somebody has to switch off the light at the end of the day" do
{:ok, pid} = Agent.start_link(fn() -> ["Milk"] end, name: __MODULE__) {:ok, pid} = Agent.start_link(fn -> ["Milk"] end, name: __MODULE__)
Agent.stop(__MODULE__) Agent.stop(__MODULE__)

View File

@@ -46,7 +46,7 @@ defmodule GenServers do
# GenServer implementation # GenServer implementation
def handle_call(:get_password, _from, current_password) do def handle_call(:get_password, _from, current_password) do
{:reply, current_password, current_password} {:reply, current_password, current_password}
end end
def handle_call(:get_manufacturer, _from, current_state) do def handle_call(:get_manufacturer, _from, current_state) do
@@ -73,6 +73,7 @@ defmodule GenServers do
case password do case password do
password when password === current_password -> password when password === current_password ->
{:reply, {:ok, "Laptop unlocked!"}, current_password} {:reply, {:ok, "Laptop unlocked!"}, current_password}
_ -> _ ->
{:reply, {:error, "Incorrect password!"}, current_password} {:reply, {:error, "Incorrect password!"}, current_password}
end end
@@ -82,6 +83,7 @@ defmodule GenServers do
case old_password do case old_password do
old_password when old_password == current_password -> old_password when old_password == current_password ->
{:noreply, new_password} {:noreply, new_password}
_ -> _ ->
{:noreply, current_password} {:noreply, current_password}
end end
@@ -147,7 +149,7 @@ defmodule GenServers do
{_, response} = Laptop.unlock("EL!73") {_, response} = Laptop.unlock("EL!73")
assert response == ___ assert response == ___
{_, response} = Laptop.owner_name {_, response} = Laptop.owner_name()
assert response == ___ assert response == ___
end end
end end

View File

@@ -3,7 +3,7 @@ defmodule Protocols do
@intro "Want to follow the rules? Adhere to the protocol!" @intro "Want to follow the rules? Adhere to the protocol!"
defprotocol School, do: def enroll(person) defprotocol(School, do: def(enroll(person)))
defimpl School, for: Any do defimpl School, for: Any do
def enroll(_) do def enroll(_) do
@@ -16,9 +16,9 @@ defmodule Protocols do
defstruct name: "" defstruct name: ""
end end
defmodule Musician, do: defstruct name: "", instrument: "" defmodule(Musician, do: defstruct(name: "", instrument: ""))
defmodule Dancer, do: defstruct name: "", dance_style: "" defmodule(Dancer, do: defstruct(name: "", dance_style: ""))
defmodule Baker, do: defstruct name: "" defmodule(Baker, do: defstruct(name: ""))
defimpl School, for: Musician do defimpl School, for: Musician do
def enroll(musician) do def enroll(musician) do

View File

@@ -9,10 +9,11 @@ defmodule Mix.Tasks.Meditate do
{parsed, _, _} = OptionParser.parse(args) {parsed, _, _} = OptionParser.parse(args)
modules = parsed modules =
|> initial_module parsed
|> ok? |> initial_module
|> Runner.modules_to_run |> ok?
|> Runner.modules_to_run()
Tracker.set_total(modules) Tracker.set_total(modules)
Tracker.notify_on_complete(self()) Tracker.notify_on_complete(self())
@@ -26,7 +27,7 @@ defmodule Mix.Tasks.Meditate do
defp initial_module(parsed) do defp initial_module(parsed) do
name = Keyword.get(parsed, :koan, "Equalities") name = Keyword.get(parsed, :koan, "Equalities")
String.to_atom("Elixir."<> name) String.to_atom("Elixir." <> name)
end end
defp set_clear_screen(parsed) do defp set_clear_screen(parsed) do
@@ -39,7 +40,7 @@ defmodule Mix.Tasks.Meditate do
if Runner.koan?(koan) do if Runner.koan?(koan) do
koan koan
else else
Display.invalid_koan(koan, Runner.modules) Display.invalid_koan(koan, Runner.modules())
exit(:normal) exit(:normal)
end end
end end

View File

@@ -13,7 +13,8 @@ defmodule Runner do
modules modules
|> Stream.map(&(&1.module_info |> get_in([:compile, :source]))) |> Stream.map(&(&1.module_info |> get_in([:compile, :source])))
|> Stream.map(&to_string/1) # Paths are charlists # Paths are charlists
|> Stream.map(&to_string/1)
|> Stream.zip(modules) |> Stream.zip(modules)
|> Stream.filter(fn {_path, mod} -> koan?(mod) end) |> Stream.filter(fn {_path, mod} -> koan?(mod) end)
|> Stream.map(fn {path, mod} -> {path_to_number(path), mod} end) |> Stream.map(fn {path, mod} -> {path_to_number(path), mod} end)
@@ -74,6 +75,7 @@ defmodule Runner do
Display.show_failure(error, module, name) Display.show_failure(error, module, name)
:failed :failed
end end
defp display(_), do: :passed defp display(_), do: :passed
defp flush do defp flush do

View File

@@ -2,9 +2,9 @@ defmodule Tracker do
alias __MODULE__ alias __MODULE__
defstruct total: 0, defstruct total: 0,
koans: MapSet.new(), koans: MapSet.new(),
visited_modules: MapSet.new(), visited_modules: MapSet.new(),
on_complete: :noop on_complete: :noop
def start_link do def start_link do
Agent.start_link(fn -> %Tracker{} end, name: __MODULE__) Agent.start_link(fn -> %Tracker{} end, name: __MODULE__)
@@ -15,15 +15,17 @@ defmodule Tracker do
end end
def set_total(modules) do def set_total(modules) do
total = modules total =
|> Enum.flat_map(&(&1.all_koans)) modules
|> Enum.count |> Enum.flat_map(& &1.all_koans)
|> Enum.count()
Agent.update(__MODULE__, fn _ -> %Tracker{total: total} end) Agent.update(__MODULE__, fn _ -> %Tracker{total: total} end)
end end
def completed(module, koan) do def completed(module, koan) do
Agent.update(__MODULE__, &mark_koan_completed(&1, module, koan)) Agent.update(__MODULE__, &mark_koan_completed(&1, module, koan))
if complete?() do if complete?() do
Agent.cast(__MODULE__, fn state -> Agent.cast(__MODULE__, fn state ->
send(state.on_complete, {self(), :complete}) send(state.on_complete, {self(), :complete})
@@ -34,14 +36,18 @@ defmodule Tracker do
def wait_until_complete() do def wait_until_complete() do
pid = Process.whereis(Tracker) pid = Process.whereis(Tracker)
receive do receive do
{^pid, :complete} -> :ok {^pid, :complete} -> :ok
end end
end end
defp mark_koan_completed(state, module, koan) do defp mark_koan_completed(state, module, koan) do
%{state | koans: MapSet.put(state.koans, koan), %{
visited_modules: MapSet.put(state.visited_modules, module)} state
| koans: MapSet.put(state.koans, koan),
visited_modules: MapSet.put(state.visited_modules, module)
}
end end
def visited do def visited do
@@ -54,7 +60,7 @@ defmodule Tracker do
end end
def summarize do def summarize do
state = Agent.get(__MODULE__, &(&1)) state = Agent.get(__MODULE__, & &1)
%{ %{
total: state.total, total: state.total,

View File

@@ -2,7 +2,7 @@ defmodule Watcher do
use GenServer use GenServer
def start_link() do def start_link() do
GenServer.start_link(__MODULE__, [dirs: ["lib/koans"]]) GenServer.start_link(__MODULE__, dirs: ["lib/koans"])
end end
def init(args) do def init(args) do
@@ -11,10 +11,11 @@ defmodule Watcher do
{:ok, %{watcher_pid: watcher_pid}} {:ok, %{watcher_pid: watcher_pid}}
end end
def handle_info({:file_event, watcher_pid, {path, events}}, %{watcher_pid: watcher_pid}=state) do def handle_info({:file_event, watcher_pid, {path, events}}, %{watcher_pid: watcher_pid} = state) do
if Enum.member?(events, :modified) do if Enum.member?(events, :modified) do
path |> normalize |> reload path |> normalize |> reload
end end
{:noreply, state} {:noreply, state}
end end
@@ -22,11 +23,11 @@ defmodule Watcher do
if Path.extname(file) == ".ex" do if Path.extname(file) == ".ex" do
try do try do
file file
|> Code.load_file |> Code.load_file()
|> Enum.map(&(elem(&1, 0))) |> Enum.map(&elem(&1, 0))
|> Enum.find(&Runner.koan?/1) |> Enum.find(&Runner.koan?/1)
|> Runner.modules_to_run |> Runner.modules_to_run()
|> Runner.run |> Runner.run()
rescue rescue
e -> Display.show_compile_error(e) e -> Display.show_compile_error(e)
end end

View File

@@ -8,30 +8,32 @@ defmodule BlanksTest do
end end
test "Work with multiple different replacements" do test "Work with multiple different replacements" do
[koan | _] = SampleKoan.all_koans [koan | _] = SampleKoan.all_koans()
assert :ok == apply(SampleKoan, koan, [{:multiple, [3,4]}]) assert :ok == apply(SampleKoan, koan, [{:multiple, [3, 4]}])
end end
test "complex example" do test "complex example" do
ast = quote do: assert true == ___ ast = quote do: assert(true == ___)
assert Blanks.replace(ast, true) == quote(do: assert true == true) assert Blanks.replace(ast, true) == quote(do: assert(true == true))
end end
test "multiple arguments" do test "multiple arguments" do
ast = quote do: assert ___ == ___ ast = quote do: assert(___ == ___)
assert Blanks.replace(ast, [true, false]) == quote(do: assert true == false) assert Blanks.replace(ast, [true, false]) == quote(do: assert(true == false))
end end
test "pins variables in assert_receive replacement" do test "pins variables in assert_receive replacement" do
ast = quote do: assert_receive ___ ast = quote do: assert_receive(___)
assert Blanks.replace(ast, Macro.var(:answer, __MODULE__)) == quote(do: assert_receive ^answer)
assert Blanks.replace(ast, Macro.var(:answer, __MODULE__)) ==
quote(do: assert_receive(^answer))
end end
test "does not pin values in assert_receive replacement" do test "does not pin values in assert_receive replacement" do
ast = quote do: assert_receive ___ ast = quote do: assert_receive(___)
assert Blanks.replace(ast, :lolwat) == quote(do: assert_receive :lolwat) assert Blanks.replace(ast, :lolwat) == quote(do: assert_receive(:lolwat))
end end
test "counts simple blanks" do test "counts simple blanks" do
@@ -41,41 +43,46 @@ defmodule BlanksTest do
end end
test "counts multiple blanks" do test "counts multiple blanks" do
ast = quote do: assert ___ == ___ ast = quote do: assert(___ == ___)
assert Blanks.count(ast) == 2 assert Blanks.count(ast) == 2
end end
test "replaces whole line containing blank" do test "replaces whole line containing blank" do
ast = quote do ast =
1 + 2 quote do
2 + ___ 1 + 2
end 2 + ___
end
expected_result = quote do expected_result =
1 + 2 quote do
true 1 + 2
end true
end
actual_result = Blanks.replace_line(ast, fn(_) -> true end) actual_result = Blanks.replace_line(ast, fn _ -> true end)
assert actual_result == expected_result assert actual_result == expected_result
end end
test "replacement fn can access line" do test "replacement fn can access line" do
ast = quote do ast =
1 + 2 quote do
2 + ___ 1 + 2
end 2 + ___
end
expected_result = quote do expected_result =
1 + 2 quote do
some_fun(2 + ___) 1 + 2
end some_fun(2 + ___)
end
actual_result = Blanks.replace_line(ast, fn(line) -> actual_result =
quote do: some_fun(unquote(line)) Blanks.replace_line(ast, fn line ->
end) quote do: some_fun(unquote(line))
end)
assert actual_result == expected_result assert actual_result == expected_result
end end

View File

@@ -18,39 +18,39 @@ defmodule FailureTests do
error = error(%ExUnit.AssertionError{expr: quote(do: :lol == :wat), left: :lol, right: :wat}) error = error(%ExUnit.AssertionError{expr: quote(do: :lol == :wat), left: :lol, right: :wat})
assert Failure.format_failure(error) == """ assert Failure.format_failure(error) == """
Assertion failed in some_file.ex:42 Assertion failed in some_file.ex:42
:lol == :wat :lol == :wat
left: :lol left: :lol
right: :wat right: :wat
""" """
end end
test "match failure" do test "match failure" do
error = error(%ExUnit.AssertionError{expr: quote(do: match?(:lol,:wat)), right: :wat}) error = error(%ExUnit.AssertionError{expr: quote(do: match?(:lol, :wat)), right: :wat})
assert Failure.format_failure(error) == """ assert Failure.format_failure(error) == """
Assertion failed in some_file.ex:42 Assertion failed in some_file.ex:42
match?(:lol, :wat) match?(:lol, :wat)
value does not match: :wat value does not match: :wat
""" """
end end
test "only offending lines are displayed for errors" do test "only offending lines are displayed for errors" do
[koan] = SingleArity.all_koans [koan] = SingleArity.all_koans()
error = apply(SingleArity, koan, []) |> error() error = apply(SingleArity, koan, []) |> error()
assert Failure.format_failure(error) == """ assert Failure.format_failure(error) == """
Assertion failed in some_file.ex:42\nmatch?(:foo, ___) Assertion failed in some_file.ex:42\nmatch?(:foo, ___)
""" """
end end
defp error(error) do defp error(error) do
%{ %{
error: error, error: error,
file: "some_file.ex", file: "some_file.ex",
line: 42 line: 42
} }
end end
end end

View File

@@ -3,7 +3,7 @@ defmodule NotificationTest do
alias Display.Notifications alias Display.Notifications
test "shows possible koans when a koan can not be found" do test "shows possible koans when a koan can not be found" do
message = Notifications.invalid_koan(SampleKoan, [PassingKoan]) message = Notifications.invalid_koan(SampleKoan, [PassingKoan])
assert message == "Did not find koan SampleKoan in PassingKoan" assert message == "Did not find koan SampleKoan in PassingKoan"
end end
end end

View File

@@ -13,7 +13,6 @@ defmodule ProgressBarTest do
assert bar == "|=======> | 3 of 12" assert bar == "|=======> | 3 of 12"
end end
test "full bar" do test "full bar" do
bar = ProgressBar.progress_bar(%{total: 12, current: 12}) bar = ProgressBar.progress_bar(%{total: 12, current: 12})
assert bar == "|=============================>| 12 of 12" assert bar == "|=============================>| 12 of 12"

View File

@@ -12,6 +12,6 @@ defmodule ExecuteTest do
end end
test "can access intro" do test "can access intro" do
assert SampleKoan.intro == "There is something\n" assert SampleKoan.intro() == "There is something\n"
end end
end end

View File

@@ -8,7 +8,7 @@ defmodule AgentTests do
"Why hello", "Why hello",
"HI THERE", "HI THERE",
{:multiple, [["Milk"], ["Bread", "Milk"]]}, {:multiple, [["Milk"], ["Bread", "Milk"]]},
false, false
] ]
test_all(Agents, answers) test_all(Agents, answers)

View File

@@ -9,17 +9,17 @@ defmodule EnumTests do
{:multiple, [true, false]}, {:multiple, [true, false]},
{:multiple, [true, false]}, {:multiple, [true, false]},
{:multiple, [true, false]}, {:multiple, [true, false]},
[10,20,30], [10, 20, 30],
[1,3], [1, 3],
[2], [2],
[1,2,3], [1, 2, 3],
[1,2,3,4,5], [1, 2, 3, 4, 5],
[1,2,3], [1, 2, 3],
[a: 1, b: 2, c: 3], [a: 1, b: 2, c: 3],
2, 2,
nil, nil,
:no_such_element, :no_such_element,
6, 6
] ]
test_all(Enums, answers) test_all(Enums, answers)

View File

@@ -10,7 +10,7 @@ defmodule EqualitiesTests do
2, 2,
1, 1,
4, 4,
2, 2
] ]
test_all(Equalities, answers) test_all(Equalities, answers)

View File

@@ -7,7 +7,7 @@ defmodule FunctionsTests do
"Hello, World!", "Hello, World!",
3, 3,
{:multiple, ["One and Two", "Only One"]}, {:multiple, ["One and Two", "Only One"]},
{:multiple, ["Hello Hello Hello Hello Hello ","Hello Hello "]}, {:multiple, ["Hello Hello Hello Hello Hello ", "Hello Hello "]},
{:multiple, [:entire_list, :single_thing]}, {:multiple, [:entire_list, :single_thing]},
{:multiple, ["10 is bigger than 5", "4 is not bigger than 27"]}, {:multiple, ["10 is bigger than 5", "4 is not bigger than 27"]},
{:multiple, ["The number was zero", "The number was 5"]}, {:multiple, ["The number was zero", "The number was 5"]},
@@ -18,7 +18,7 @@ defmodule FunctionsTests do
100, 100,
1000, 1000,
"Full Name", "Full Name",
{:multiple, ["GOOD", "good"]}, {:multiple, ["GOOD", "good"]}
] ]
test_all(Functions, answers) test_all(Functions, answers)

View File

@@ -13,7 +13,7 @@ defmodule GenServersTests do
{:error, "Incorrect password!"}, {:error, "Incorrect password!"},
"Congrats! Your process was successfully named.", "Congrats! Your process was successfully named.",
{:ok, "Laptop unlocked!"}, {:ok, "Laptop unlocked!"},
{:multiple, ["Laptop unlocked!", "Incorrect password!", "Jack Sparrow"]}, {:multiple, ["Laptop unlocked!", "Incorrect password!", "Jack Sparrow"]}
] ]
test_all(GenServers, answers) test_all(GenServers, answers)

View File

@@ -8,7 +8,7 @@ defmodule KeywordListsTests do
"bar", "bar",
"baz", "baz",
{:multiple, [:foo, "bar"]}, {:multiple, [:foo, "bar"]},
"foo", "foo"
] ]
test_all(KeywordLists, answers) test_all(KeywordLists, answers)

View File

@@ -3,24 +3,25 @@ defmodule ListsTests do
import TestHarness import TestHarness
test "Lists" do test "Lists" do
answers = [1, answers = [
3, 1,
[1, 2, :a, "b"], 3,
[1,2], [1, 2, :a, "b"],
[:a, :c], [1, 2],
[:a, :b], [:a, :c],
["life", "life", "life"], [:a, :b],
[1, 2, 3, 4, 5], ["life", "life", "life"],
[1, 4, 2, 3], [1, 2, 3, 4, 5],
[10, 2, 3], [1, 4, 2, 3],
[1, 2, 3], [10, 2, 3],
[1, 2, 3, 4], [1, 2, 3],
[1, 2, 3, 4], [1, 2, 3, 4],
{1, 2, 3}, [1, 2, 3, 4],
["value"], {1, 2, 3},
[], ["value"],
["value"], [],
] ["value"]
]
test_all(Lists, answers) test_all(Lists, answers)
end end

View File

@@ -14,7 +14,7 @@ defmodule MapSetsTest do
false, false,
true, true,
7, 7,
[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]
] ]
test_all(MapSets, answers) test_all(MapSets, answers)

View File

@@ -13,7 +13,7 @@ defmodule MapsTests do
false, false,
%{:first_name => "Jon", :last_name => "Snow"}, %{:first_name => "Jon", :last_name => "Snow"},
{:ok, "Baratheon"}, {:ok, "Baratheon"},
%{:first_name => "Jon", :last_name => "Snow"}, %{:first_name => "Jon", :last_name => "Snow"}
] ]
test_all(Maps, answers) test_all(Maps, answers)

View File

@@ -30,7 +30,7 @@ defmodule NumbersTests do
{:multiple, [6.0, 5.0, 8.9, -5.567]}, {:multiple, [6.0, 5.0, 8.9, -5.567]},
{:multiple, [1, 10]}, {:multiple, [1, 10]},
{:multiple, [true, true, false]}, {:multiple, [true, true, false]},
{:multiple, [true, false]}, {:multiple, [true, false]}
] ]
test_all(Numbers, answers) test_all(Numbers, answers)

View File

@@ -5,14 +5,14 @@ defmodule PatternsTests do
test "Pattern Matching" do test "Pattern Matching" do
answers = [ answers = [
1, 1,
{:multiple, [1, [2,3,4]]}, {:multiple, [1, [2, 3, 4]]},
[1,2,3,4], [1, 2, 3, 4],
3, 3,
"eggs, milk", "eggs, milk",
"Honda", "Honda",
MatchError, MatchError,
{:multiple, [:make, "Honda"]}, {:multiple, [:make, "Honda"]},
[1,2,3], [1, 2, 3],
{:multiple, ["Meow", "Woof", "Eh?"]}, {:multiple, ["Meow", "Woof", "Eh?"]},
{:multiple, ["Mickey", "Donald", "I need a name!"]}, {:multiple, ["Mickey", "Donald", "I need a name!"]},
"dog", "dog",

View File

@@ -18,7 +18,7 @@ defmodule ProcessesTests do
{:waited_too_long, "I am impatient"}, {:waited_too_long, "I am impatient"},
{:exited, :random_reason}, {:exited, :random_reason},
:normal, :normal,
:normal, :normal
] ]
test_all(Processes, answers) test_all(Processes, answers)

View File

@@ -11,7 +11,7 @@ defmodule SigilsTests do
~S(1 + 1 = #{1+1}), ~S(1 + 1 = #{1+1}),
["Hello", "world"], ["Hello", "world"],
["Hello", "123"], ["Hello", "123"],
["Hello", ~S(#{1+1})], ["Hello", ~S(#{1+1})]
] ]
test_all(Sigils, answers) test_all(Sigils, answers)

View File

@@ -13,7 +13,7 @@ defmodule StringTests do
"banana", "banana",
"banana", "banana",
"StringStringString", "StringStringString",
"LISTEN", "LISTEN"
] ]
test_all(Strings, answers) test_all(Strings, answers)

View File

@@ -11,7 +11,7 @@ defmodule StructsTests do
{:ok, 22}, {:ok, 22},
%Structs.Airline{plane: %Structs.Plane{maker: :airbus}, name: "Southwest"}, %Structs.Airline{plane: %Structs.Plane{maker: :airbus}, name: "Southwest"},
%Structs.Airline{plane: %Structs.Plane{maker: :boeing, passengers: 202}, name: "Southwest"}, %Structs.Airline{plane: %Structs.Plane{maker: :boeing, passengers: 202}, name: "Southwest"},
%{plane: %{maker: :cessna}, name: "Southwest"}, %{plane: %{maker: :cessna}, name: "Southwest"}
] ]
test_all(Structs, answers) test_all(Structs, answers)

View File

@@ -9,8 +9,8 @@ defmodule TasksTests do
nil, nil,
false, false,
9, 9,
[1,4,9,16], [1, 4, 9, 16]
] ]
test_all(Tasks, answers) test_all(Tasks, answers)
end end

View File

@@ -11,7 +11,7 @@ defmodule TupleTests do
{:a, :new_thing, "hi"}, {:a, :new_thing, "hi"},
{"Huey", "Dewey", "Louie"}, {"Huey", "Dewey", "Louie"},
{:this, :is, :awesome}, {:this, :is, :awesome},
[:this, :can, :be, :a, :list], [:this, :can, :be, :a, :list]
] ]
test_all(Tuples, answers) test_all(Tuples, answers)

View File

@@ -5,5 +5,4 @@ defmodule RunnerTest do
path = "lib/koans/01_just_an_example.ex" path = "lib/koans/01_just_an_example.ex"
assert Runner.path_to_number(path) == 1 assert Runner.path_to_number(path) == 1
end end
end end

View File

@@ -23,10 +23,10 @@ defmodule TestHarness do
end end
defp check_results(results) do defp check_results(results) do
Enum.each(results, &(assert &1 == :passed)) Enum.each(results, &assert(&1 == :passed))
end end
def run_all(pairs, module) do def run_all(pairs, module) do
Enum.map(pairs, fn ({koan, answer}) -> Execute.run_koan(module, koan, [answer]) end) Enum.map(pairs, fn {koan, answer} -> Execute.run_koan(module, koan, [answer]) end)
end end
end end

View File

@@ -5,24 +5,24 @@ defmodule TrackerTest do
test "can start" do test "can start" do
Tracker.set_total(@sample_modules) Tracker.set_total(@sample_modules)
assert Tracker.summarize == %{total: 2, current: 0, visited_modules: []} assert Tracker.summarize() == %{total: 2, current: 0, visited_modules: []}
end end
test "can be notified of completed koans" do test "can be notified of completed koans" do
Tracker.set_total(@sample_modules) Tracker.set_total(@sample_modules)
Tracker.completed(SampleKoan, :"Hi there") Tracker.completed(SampleKoan, :"Hi there")
assert Tracker.summarize == %{total: 2, current: 1, visited_modules: [SampleKoan]} assert Tracker.summarize() == %{total: 2, current: 1, visited_modules: [SampleKoan]}
end end
test "multiple comletions of the same koan count only once" do test "multiple comletions of the same koan count only once" do
Tracker.set_total(@sample_modules) Tracker.set_total(@sample_modules)
Tracker.completed(SampleKoan, :"Hi there") Tracker.completed(SampleKoan, :"Hi there")
Tracker.completed(SampleKoan, :"Hi there") Tracker.completed(SampleKoan, :"Hi there")
assert Tracker.summarize == %{total: 2, current: 1, visited_modules: [SampleKoan]} assert Tracker.summarize() == %{total: 2, current: 1, visited_modules: [SampleKoan]}
end end
test "knows when koans are not complete" do test "knows when koans are not complete" do
Tracker.set_total(@sample_modules) Tracker.set_total(@sample_modules)
refute Tracker.complete? refute Tracker.complete?()
end end
end end