emqx.proper.ex 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. defmodule Mix.Tasks.Emqx.Proper do
  2. use Mix.Task
  3. # Code.require_file("emqx.ct.ex", __DIR__)
  4. alias Mix.Tasks.Emqx.Ct, as: ECt
  5. # todo: invoke the equivalent of `make merge-config` as a requirement...
  6. @requirements ["compile", "loadpaths"]
  7. @impl true
  8. def run(args) do
  9. Mix.debug(true)
  10. IO.inspect(args)
  11. Enum.each([:common_test, :eunit, :mnesia], &ECt.add_to_path_and_cache/1)
  12. ECt.ensure_whole_emqx_project_is_loaded!()
  13. ECt.unload_emqx_applications!()
  14. {_, 0} = System.cmd("epmd", ["-daemon"])
  15. node_name = :"test@127.0.0.1"
  16. :net_kernel.start([node_name, :longnames])
  17. # unmangle PROFILE env because some places (`:emqx_conf.resolve_schema_module`) expect
  18. # the version without the `-test` suffix.
  19. System.fetch_env!("PROFILE")
  20. |> String.replace_suffix("-test", "")
  21. |> then(& System.put_env("PROFILE", &1))
  22. for {mod, fun} <- discover_props() do
  23. Mix.shell().info("testing #{mod}:#{fun}")
  24. opts = fetch_opts(mod, fun)
  25. :proper.quickcheck(apply(mod, fun, []), opts)
  26. end
  27. |> IO.inspect()
  28. end
  29. defp add_to_path_and_cache(lib_name) do
  30. :code.lib_dir()
  31. |> Path.join("#{lib_name}-*")
  32. |> Path.wildcard()
  33. |> hd()
  34. |> Path.join("ebin")
  35. |> to_charlist()
  36. |> :code.add_path(:cache)
  37. end
  38. ## TODO: allow filtering modules and test names
  39. defp discover_props() do
  40. Mix.Dep.Umbrella.cached()
  41. |> Enum.map(fn dep ->
  42. dep.opts[:path]
  43. |> Path.join("test")
  44. end)
  45. |> Mix.Utils.extract_files("prop_*.erl")
  46. |> Enum.flat_map(fn suite_path ->
  47. suite_path
  48. |> Path.basename(".erl")
  49. |> String.to_atom()
  50. |> then(fn suite_mod ->
  51. suite_mod.module_info(:exports)
  52. |> Enum.filter(fn {name, _arity} -> to_string(name) =~ ~r/^prop_/ end)
  53. |> Enum.map(fn {name, _arity} -> {suite_mod, name} end)
  54. end)
  55. end)
  56. end
  57. defp fetch_opts(mod, fun) do
  58. try do
  59. mod.fun(:opts)
  60. rescue
  61. e in [FunctionClauseError, UndefinedFunctionError] -> []
  62. end
  63. end
  64. end