MobDev.Bench.DeviceObserver (mob_dev v0.5.3)

Copy Markdown View Source

Subscribes to Mob.Device events on the running app over Erlang distribution and tracks ground-truth screen/app state for the bench.

Without this, the bench only knows what it asked the device to do ("we just ran lock_screen, so the screen should be off"). With this, the bench learns from the device what's actually happening ({:mob_device, :did_enter_background}, {:mob_device, :screen_off}), and the probe snapshots reflect reality.

Lifecycle

observer = DeviceObserver.subscribe(node, categories: [:app, :display])
...
observer = DeviceObserver.consume_messages(observer)  # call each tick
observer.screen   # => :on | :off | :unknown
observer.app      # => :running | :background | :suspended | :unknown
observer.events   # => list of recent events (most recent first)

Subscription is best-effort — if the device's BEAM doesn't have Mob.Device.subscribe/1 exported (older app build), subscribe/2 returns an observer that just passes through the caller's expected state.

Summary

Functions

Merge the observer's ground-truth state into a Probe snapshot. If the observer has authoritative state, prefer it over what the probe inferred; fall back to the probe's view otherwise.

Drain the calling process's mailbox of pending Mob.Device messages and update the observer's tracked state. Returns the updated observer.

Try to subscribe the calling process to Mob.Device events on node. Returns an observer struct, possibly with subscribed?: false if the device's app doesn't support it (older build).

Types

app_state()

@type app_state() :: :running | :background | :suspended | :unknown

screen_state()

@type screen_state() :: :on | :off | :unknown

t()

@type t() :: %MobDev.Bench.DeviceObserver{
  app: app_state(),
  events: [{integer(), atom(), term()}],
  last_event_ts_ms: integer() | nil,
  node: atom() | nil,
  screen: screen_state(),
  subscribed?: boolean()
}

Functions

apply_to_probe(obs, probe)

@spec apply_to_probe(t(), MobDev.Bench.Probe.t()) :: MobDev.Bench.Probe.t()

Merge the observer's ground-truth state into a Probe snapshot. If the observer has authoritative state, prefer it over what the probe inferred; fall back to the probe's view otherwise.

consume_messages(obs)

@spec consume_messages(t()) :: t()

Drain the calling process's mailbox of pending Mob.Device messages and update the observer's tracked state. Returns the updated observer.

Call this at the top of each poll cycle. Non-blocking — uses receive with after 0.

subscribe(node, opts)

@spec subscribe(
  atom() | nil,
  keyword()
) :: t()

Try to subscribe the calling process to Mob.Device events on node. Returns an observer struct, possibly with subscribed?: false if the device's app doesn't support it (older build).