Skip to content

Instantly share code, notes, and snippets.

@fimars
Last active March 29, 2018 03:38
Show Gist options
  • Save fimars/4e47fe741ba974dc2918d1a200081b79 to your computer and use it in GitHub Desktop.
Save fimars/4e47fe741ba974dc2918d1a200081b79 to your computer and use it in GitHub Desktop.
Elixir Meta PG
# Further Exploration
#
# We journeyed from simple control flow transformations all the way through a mini testing framework. Along the way,
# you learned all the tools necessary to define your own macros and perform AST transformations in a responsible way.
# Next, we’ll discover a few advanced compile-time code-generation tech- niques to create highly performant and
# maintainable programs.
#
# On your own, explore ways you can enhance your Assertion test framework and define new macro constructs. Here are a few
# basic experiments to get you started:
# • Implement assert for every operator in Elixir.
# • Add Boolean assertions, such as assert true.
# • Implement a refute macro for refutations.
# And some that are more advanced:
# • Run test cases in parallel within Assertion.Test.run/2 via spawned processes.
# • Add reports for the module. Include pass/fail counts and execution time.
defmodule Assertion do
defmacro __using__(_options \\ []) do
quote do
import unquote(__MODULE__)
Module.register_attribute __MODULE__, :tests, accumulate: true
@before_compile unquote(__MODULE__)
end
end
defmacro __before_compile__(_env) do
quote do
def run, do: Assertion.Test.run(@tests, __MODULE__)
end
end
defmacro test(description, do: test_block) do
test_func = String.to_atom(description)
quote do
@tests {unquote(test_func), unquote(description)}
def unquote(test_func)(), do: unquote(test_block)
end
end
defmacro assert({operator, _, [lhs, rhs]}) do
quote bind_quoted: [operator: operator, lhs: lhs, rhs: rhs] do
Assertion.Test.assert(operator, lhs, rhs)
end
end
defmacro assert(bool) do
quote do
Assertion.Test.assert(unquote(bool))
end
end
end
defmodule Assertion.Test do
def run(tests, module) do
Enum.each tests, fn {test_func, description} ->
case apply(module, test_func, []) do
:ok -> IO.write "."
{:fail, reason} -> IO.puts """
=====================================================
FAILURE: #{description}
=====================================================
#{reason}
"""
end
end
end
def assert(true) do
:ok
end
def assert(false) do
{:fail, """
Expected: true
Get: false
"""
}
end
def assert(:==, lhs, rhs) when lhs == rhs do
:ok
end
def assert(:==, lhs, rhs) do
{:fail, """
Expected: #{lhs}
to be equal to: #{rhs}
"""
}
end
def assert(:>, lhs, rhs) when lhs > rhs do
:ok
end
def assert(:>, lhs, rhs) do
{:fail, """
Expected: #{lhs}
to be greater than: #{rhs}
"""
}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment