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
endThe 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 (touse: an expression, evaluated in each test).context:the host's configuration for the source, passed todescribe/2(nil).requests:what is asked, each a keyword list ofMacaw.Data.Source.Requestfields amongparams,window,box,region,latestandas_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'snow, aDateTime; 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_ofsource is asked as of; a day beforenowby default.push:a function of the source that makes it push; a source naming:pushneeds 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:touseonly: thedescribeblock's name,"source conformance".
Examples
iex> Macaw.Data.Source.Conformance.check(%Macaw.Data.Test.Reference{},
...> requests: [[], [window: [2, 6]]])
[]
Summary
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.
One finding as a line a person reads.
Types
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.
@spec check(Macaw.Data.Source.t(), keyword()) :: [finding()]
Every check's findings for a source, concatenated in the order of checks/0.
@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.
@spec checks() :: [check(), ...]
The eight checks, in the order check/2 runs them.
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"