Roux.Code.Verify (roux v0.3.2)

Copy Markdown View Source

What a computation actually runs, for a test that checks a closure (Roux.Code.closure/2) against it: every module the computation executes must be in the closure of the roots its code version is made of, or an edit to that module would leave stale results in place.

{_result, ran} = Roux.Code.Verify.executed(fn -> MyGraph.extract(db, key) end)
{:ok, closure} = Roux.Code.closure([MyGraph.Extraction])
assert ran -- Enum.map(closure, &elem(&1, 0)) == []

executed/2 counts one run. A test that counts many turns counting on once (counting/2), since turning it on and off again for a few hundred modules takes a fraction of a second, and reads each run as the counts it moved (calls/2):

Roux.Code.Verify.counting(fn session ->
  for key <- keys do
    {_result, calls} = Roux.Code.Verify.calls(session, fn -> MyGraph.extract(db, key) end)
    {key, Roux.Code.Verify.modules(calls)}
  end
end)

Call counts are kept per VM, not per process: anything else the VM runs meanwhile counts too. Run it in a VM of its own (:peer). Two sessions in one VM take turns (counting/2 waits for the other to end), since one ending turns counting off for the modules it watched, which the other may watch too.

A session only reads counters between runs, never sets them back to zero: :erlang.trace_pattern/3's :restart over every function also clears other tracing in the VM, such as a call trace another test set.

Summary

Functions

Runs fun within session: its result, and the functions of the session's modules it called, each with how often, among those still counted (see ignore/2). The counts are read before and after the run, and a run is what it moved.

Runs fun with every function of the watched modules counted, and stops counting after: its result. fun takes the session, to read runs within it with calls/2.

Runs fun with call counting on, and returns its result with the modules it called into, sorted.

Stops counting functions for the rest of session: hot functions, already known to be called, whose counter every worker bumps at once. A run reads them as not called.

The modules among calls/2's functions, sorted.

Functions

calls(session, fun)

@spec calls(Roux.Code.Verify.Session.t(), (-> result)) ::
  {result, %{required(mfa()) => pos_integer()}}
when result: var

Runs fun within session: its result, and the functions of the session's modules it called, each with how often, among those still counted (see ignore/2). The counts are read before and after the run, and a run is what it moved.

Raises Roux.Code.Verify.UncountedError when a module of the session is no longer counted at all.

counting(fun, opts \\ [])

@spec counting(
  (Roux.Code.Verify.Session.t() -> result),
  keyword()
) :: result
when result: var

Runs fun with every function of the watched modules counted, and stops counting after: its result. fun takes the session, to read runs within it with calls/2.

Sessions in one VM take turns: one opened while another process's is open waits for it to end. A process does not wait for its own, so do not open a session within another: the inner one would turn counting off for the outer one's modules when it ends.

Options

  • :modules — the modules to watch, as for executed/2.

executed(fun, opts \\ [])

@spec executed(
  (-> result),
  keyword()
) :: {result, [module()]}
when result: var

Runs fun with call counting on, and returns its result with the modules it called into, sorted.

Options

  • :modules — the modules to watch (default: every module of every loaded application that is not OTP's or Elixir's). A module is loaded before the run, since only loaded code can be counted. This module is never watched.

ignore(session, functions)

@spec ignore(Roux.Code.Verify.Session.t(), [mfa()]) :: :ok

Stops counting functions for the rest of session: hot functions, already known to be called, whose counter every worker bumps at once. A run reads them as not called.

modules(calls)

@spec modules(%{required(mfa()) => pos_integer()}) :: [module()]

The modules among calls/2's functions, sorted.