From 9a003ee3cf6a644c06fde00b1fa70124cc5ab6f0 Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Thu, 11 Jul 2024 15:28:01 -0300 Subject: [PATCH] feat(mix eunit): add support for filtering test cases --- lib/mix/tasks/emqx.eunit.ex | 78 +++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/lib/mix/tasks/emqx.eunit.ex b/lib/mix/tasks/emqx.eunit.ex index a2c0dfe39..2ec0e7abc 100644 --- a/lib/mix/tasks/emqx.eunit.ex +++ b/lib/mix/tasks/emqx.eunit.ex @@ -1,7 +1,11 @@ defmodule Mix.Tasks.Emqx.Eunit do use Mix.Task - # Code.require_file("emqx.ct.ex", __DIR__) + alias EMQXUmbrella.MixProject, as: UMP + + if UMP.new_mix_build?() do + Code.require_file("emqx.ct.ex", __DIR__) + end alias Mix.Tasks.Emqx.Ct, as: ECt @@ -29,7 +33,9 @@ defmodule Mix.Tasks.Emqx.Eunit do |> String.replace_suffix("-test", "") |> then(& System.put_env("PROFILE", &1)) - discover_tests() + args + |> parse_args!() + |> discover_tests() |> :eunit.test( verbose: true, print_depth: 100 @@ -50,9 +56,73 @@ defmodule Mix.Tasks.Emqx.Eunit do |> :code.add_path(:cache) end - ## TODO: allow filtering modules and test names - defp discover_tests() do + defp parse_args!(args) do + {opts, _rest} = OptionParser.parse!( + args, + strict: [ + cases: :string, + modules: :string, + ] + ) + cases = + opts + |> get_name_list(:cases) + |> Enum.flat_map(&resolve_test_fns!/1) + modules = + opts + |> get_name_list(:modules) + |> Enum.map(&String.to_atom/1) + + %{ + cases: cases, + modules: modules, + } + end + + defp get_name_list(opts, key) do + opts + |> Keyword.get(key, "") + |> String.split(",", trim: true) + end + + defp resolve_test_fns!(mod_fn_str) do + {mod, fun} = case String.split(mod_fn_str, ":") do + [mod, fun] -> + {String.to_atom(mod), String.to_atom(fun)} + _ -> + Mix.raise("Bad test case spec; must of `MOD:FUN` form. Got: #{mod_fn_str}`") + end + if not has_test_case?(mod, fun) do + Mix.raise("Module #{mod} does not export test case #{fun}") + end + + if to_string(fun) =~ ~r/_test_$/ do + apply(mod, fun, []) + else + [Function.capture(mod, fun, 0)] + end + end + + defp has_test_case?(mod, fun) do + try do + mod.module_info(:functions) + |> Enum.find(& &1 == {fun, 0}) + |> then(& !! &1) + rescue + UndefinedFunctionError -> false + end + end + + defp discover_tests(%{cases: [], modules: []} = _opts) do Mix.Dep.Umbrella.cached() |> Enum.map(& {:application, &1.app}) end + defp discover_tests(%{cases: cases, modules: modules}) do + Enum.concat( + [ + cases, + Enum.map(modules, & {:module, &1}) + ] + ) + end end