MobDev.Plugin.SelfTest (mob_dev v0.7.22)

Copy Markdown View Source

Runs every activated plugin's Mob.Plugin.SelfTest on a device.

A plugin declares selftest: Module in its manifest; run_all/3 calls Module.run/1 on the device's node, one plugin at a time, and returns one entry per activated plugin. mix mob.selftest prints those as a table; mob_ci records them per nightly cell (invariant P12).

The entry's :result is always one of the contract's three shapes:

  • :pass
  • {:fail, reason} — also what a self-test that raised, exited, threw, timed out, is not on the device or returned something outside the contract gets, and what a plugin whose manifest failed signature verification gets. The reason says which.
  • {:skip, :needs_hardware | :needs_user | reason} — including plugins with no selftest: in their manifest (module: nil), so a plugin without one is visible, not silently absent.

Permissions are not granted here: the node is already running, and on an iOS simulator a privacy change can terminate the app. Callers grant before launching the app with grant_permissions/4, as mix mob.selftest does.

Summary

Types

What a self-test is told about where it runs (Mob.Plugin.SelfTest.ctx/0).

One plugin's outcome. :ms is the wall time of the call on the host.

A granted (or attempted) permission.

An activated plugin as MobDev.Plugin.activated_with_verify/0 (3-tuple) or activated/0 (2-tuple) lists them; the first element is the plugin's name or its dependency directory.

Functions

The entries whose result is {:fail, _}.

Grants the permissions the plugins' manifests declare to bundle_id on device, so self-tests do not hit a system prompt. Call it before launching the app: a simulator may terminate a running app whose privacy settings change.

Runs the self-test of every activated plugin on node and returns an entry per plugin.

One line: N passed, N failed, N skipped.

The table mix mob.selftest prints for one device: a header plus one line per entry, plugin outcome ms detail.

Types

ctx()

@type ctx() :: %{
  platform: :ios | :android,
  device: :simulator | :emulator | :physical
}

What a self-test is told about where it runs (Mob.Plugin.SelfTest.ctx/0).

entry()

@type entry() :: %{
  plugin: atom(),
  module: module() | nil,
  result: term(),
  ms: non_neg_integer()
}

One plugin's outcome. :ms is the wall time of the call on the host.

grant()

@type grant() :: %{
  plugin: atom(),
  permission: String.t(),
  status: :ok | {:error, String.t()}
}

A granted (or attempted) permission.

plugin()

@type plugin() ::
  {atom() | Path.t(), map() | nil} | {atom() | Path.t(), map() | nil, term()}

An activated plugin as MobDev.Plugin.activated_with_verify/0 (3-tuple) or activated/0 (2-tuple) lists them; the first element is the plugin's name or its dependency directory.

Functions

failures(entries)

@spec failures([entry()]) :: [entry()]

The entries whose result is {:fail, _}.

grant_permissions(device, plugins, bundle_id, cmd)

@spec grant_permissions(MobDev.Device.t(), [plugin()], String.t(), (String.t(),
                                                              [String.t()] ->
                                                                {String.t(),
                                                                 integer()})) ::
  [
    grant()
  ]

Grants the permissions the plugins' manifests declare to bundle_id on device, so self-tests do not hit a system prompt. Call it before launching the app: a simulator may terminate a running app whose privacy settings change.

  • Android emulator: each android.permissions entry via adb -s <serial> shell pm grant. Only runtime permissions are grantable; a normal or signature permission answers with an error, which is recorded, not raised.
  • iOS simulator: each permissions: [%{capability: cap}] whose capability maps to a simctl privacy service.
  • Physical devices: nothing is granted (returns []); a self-test that needs a permission the user has not given skips with :needs_user.

Returns one grant/0 per attempt. cmd is fn exe, argv -> {output, status} end.

run_all(node, ctx, opts \\ [])

@spec run_all(node(), ctx(), keyword()) :: [entry()]

Runs the self-test of every activated plugin on node and returns an entry per plugin.

Options:

  • :plugins — plugin/0 list (default: MobDev.Plugin.activated_with_verify/0, read from the host project's mob.exs and deps). A plugin whose manifest failed verification is a {:fail, _} entry; one with no manifest (tier 0) is a skip.
  • :timeout_ms — per self-test (default 30000). A test still running at the deadline is killed on the device, so the next plugin's test never runs beside it.
  • :boot_timeout_ms — how long to wait for the plugins' OTP applications to be started on the node before the first test (default 15000). mob starts them after the node is up, so a run right after a relaunch would otherwise test a plugin whose supervisor is not there yet. A plugin whose application never starts is tested anyway and reports it.

summary(entries)

@spec summary([entry()]) :: String.t()

One line: N passed, N failed, N skipped.

table(entries)

@spec table([entry()]) :: [String.t()]

The table mix mob.selftest prints for one device: a header plus one line per entry, plugin outcome ms detail.