Macaw.Data.Source.Conformance (macaw_data v0.5.3)

Copy Markdown View Source

The source conformance kit: the contract of spec/01 §2–§4 as checks every source runs (spec/01 §9).

check/3 runs one of eight checks against a source and answers its findings, {check, reason} terms, [] for a source that passes; check/2 runs them all and format/1 words a finding. use Macaw.Data.Source.Conformance, opts inside a module that uses ExUnit.Case writes one test per check that fails with the findings:

defmodule MyApp.SourceTest do
  use ExUnit.Case, async: true

  use Macaw.Data.Source.Conformance,
    source: MyApp.Source.new(),
    requests: [[], [window: [10, 20]]],
    push: &MyApp.Source.emit/1
end

The kit drives the source through Macaw.Data.Source alone and calls every callback in a process of its own (Macaw.Data.Bounded), so a callback that raises is a finding ({:raised, call, reason}), not a crash of the check. It references no ExUnit function itself: describe, test and flunk appear only in the code use writes into the caller's module, so the kit loads in production. A callback is judged by the reductions its process spends, never by the clock, so a loaded host changes no verdict.

Ported from visualize's Visualize.Feed.Conformance, with the checks a source's superset of a feed needs: a describe context, parameters, as_of, one answer per now and viewer independence.

Options

  • source: the source (to use: an expression, evaluated in each test).
  • context: the host's configuration for the source, passed to describe/2 (nil).
  • requests: what is asked, each a keyword list of Macaw.Data.Source.Request fields among params, window, box, region, latest and as_of; [[]] by default. A source naming a capability needs a request that probes it.
  • budgets: the budgets each request is fetched with, [nil, 5].
  • now: the request's now, a DateTime; the moment the check starts by default.
  • viewers: two opaque viewers, the first asking every request and the second held to the same answer when the source declares itself viewer independent.
  • as_of: the instant an :as_of source is asked as of; a day before now by default.
  • push: a function of the source that makes it push; a source naming :push needs one.
  • down: a source of the same kind that cannot answer, held to error values.
  • timeout: the liveness bound: milliseconds before a callback that has not answered is hung (blocked, not slow), and the wait for a first push (30_000).
  • reductions: the most reductions one callback may spend, the source author's cost budget; nil, the default, makes no cost claim.
  • processes: pids or registered names whose reductions during a callback count toward its budget, for a source that does its work in a server ([]).
  • quiet: milliseconds of silence that end a push (100).
  • name: to use only: the describe block's name, "source conformance".

Examples

iex> Macaw.Data.Source.Conformance.check(%Macaw.Data.Test.Reference{},
...>   requests: [[], [window: [2, 6]]])
[]

Summary

Types

One of the eight checks.

A fault one check found: {check, reason} (spec/01 §9).

Functions

One ExUnit test per check, in a describe block, written into a module that uses ExUnit.Case (spec/01 §9). source: and down: are expressions evaluated in each test, in the test process.

Every check's findings for a source, concatenated in the order of checks/0.

One check's findings for a source, [] when it passes (spec/01 §9). Raises ArgumentError for an unknown check, an unknown option or a request with a key that is not one of params, window, box, region, latest and as_of.

The eight checks, in the order check/2 runs them.

One finding as a line a person reads.

Types

check()

@type check() ::
  :describe
  | :deliveries
  | :supports
  | :push
  | :errors
  | :unsubscribe
  | :determinism
  | :viewers

One of the eight checks.

finding()

@type finding() :: {check(), term()}

A fault one check found: {check, reason} (spec/01 §9).

Functions

__using__(opts)

(macro)

One ExUnit test per check, in a describe block, written into a module that uses ExUnit.Case (spec/01 §9). source: and down: are expressions evaluated in each test, in the test process.

check(source, opts)

@spec check(Macaw.Data.Source.t(), keyword()) :: [finding()]

Every check's findings for a source, concatenated in the order of checks/0.

check(check, source, opts)

@spec check(check(), Macaw.Data.Source.t(), keyword()) :: [finding()]

One check's findings for a source, [] when it passes (spec/01 §9). Raises ArgumentError for an unknown check, an unknown option or a request with a key that is not one of params, window, box, region, latest and as_of.

checks()

@spec checks() :: [check(), ...]

The eight checks, in the order check/2 runs them.

format(arg)

@spec format(finding()) :: String.t()

One finding as a line a person reads.

iex> Macaw.Data.Source.Conformance.format({:supports, {:over_budget, {[], 5}, 9}})
"supports: asked for at most 5 rows (fetch [] with budget 5), the source names :budget and served 9"