lib/aoc/case.ex

defmodule AOC.Case do
  @moduledoc """
  `ExUnit.CaseTemplate` for writing advent of code test cases.

  This template inserts module tags and helper functions into the unit test module. Concretely, it
  inserts several module tags, which can be used to selectively test certain days using `mix
  test`. It also inserts several helpers which make it possible to access the example and puzzle
  input within the tests.

  The `use` statement expects a `year` and `day` option, which are used to generate the correct
  module tags and helper functions.

  Consider using `AOC.aoc_test/4` instead of this case template.

  ## Module tags and filtering

  This case adds the following module tags:

  * `year`: the year of the puzzle
  * `day`: the day of the puzzle
  * `aoc`: the year and day of the puzzle, separated by a dash, e.g. `"1991-8"`

  `mix test` allows you to filter tests based on tags, therefore, you can do the following:

  * `mix test --only year:1991`: will run all tests associated with year 1991.
  * `mix test --only day:8`: will run all tests associated with day 8.

  Specifying multiple `--only` options _combines_ the options, i.e., running
  `mix test --only day:8 --year:1991` will run all tests of year 1991 _and_ all the tests of any
  day 8. This is problematic if your solution repository spans multiple years. Therefore, we
  include the `aoc` tag, which uniquely identifies a day:

  * `mix test --only aoc:1991-8`: will run all the tests associated with day 8 of year 1991.

  If your solution repository only contains modules for a single year, this behaves identical to
  `mix test --only day:8`.

  ## Helpers

  This template injects several helpers into the test module which can be used to access the
  example and puzzle input. These helpers behave similar to those defined in `AOC.IEx`, but always
  fetch the input or example associated with the solution module. The following helpers are
  injected:

  * `example_string/0`: Fetch the example input with trailing newlines removed.
  * `input_string/0`: Fetch the puzzle input, trailing newlines are removed.
  * `input_path/0`: Get the path to the puzzle input.
  * `example_path/0`: Get the path to the example input.
  """
  use ExUnit.CaseTemplate

  using opts do
    day = Keyword.fetch!(opts, :day)
    year = Keyword.fetch!(opts, :year)

    quote do
      @moduletag aoc: "#{unquote(year)}-#{unquote(day)}"
      @moduletag year: unquote(year)
      @moduletag day: unquote(day)

      def input_path, do: AOC.Helpers.input_path(unquote(year), unquote(day))
      def example_path, do: AOC.Helpers.example_path(unquote(year), unquote(day))
      def input_string, do: AOC.Helpers.input_string(unquote(year), unquote(day))
      def example_string, do: AOC.Helpers.example_string(unquote(year), unquote(day))
    end
  end
end