metta.testing
Source: extensions/python/metta/testing.py.
Hypothesis strategies for property-testing code built on this library, the pandas.testing reading: the exact generators the library's own suite fuzzes itself with, exported, so user operations, translators and spaces get tested against atoms the engine actually reads back. The filters encode engine truths worth not rediscovering: which characters the tokeniser reads back whole, that true/false ARE the boolean atoms so their symbol spellings canonicalize, and that
_is the anonymous variable, fresh at every occurrence.The conformance surfaces live here too, one layer per audience: check_space_provider and check_codec run in process against an author's own object, SpaceComplianceSuite and GatewayComplianceSuite are pytest classes that run the engine's own expectations against a provider or a URL. .
- check_twin compares encoded atoms, preserving integer, float, and boolean grounded species
- minted-space conformance recognizes decoded Space handles in provider answers
- numpy_scalars generates non-primitive scalar objects whose identity and numeric dispatch survive an engine round trip
- from_pattern generates ground substitutions, preserving repeated named variables while drawing anonymous occurrences independently
The entries below reproduce the source signatures and docstrings.
names
def names():Symbol and variable names MeTTa's tokeniser reads back whole: no whitespace, parens or quotes, none of the characters that mean something else at the front, and never the boolean spellings (the engine holds its booleans as those very atoms, so True and true are one term there and a round trip canonicalizes) or the anonymous
_(fresh at every occurrence by contract, so it never shares).
symbols
def symbols():Symbol atoms with engine-readable names.
variables
def variables():Variable atoms with engine-readable names.
numbers
def numbers():Numbers the engine's printer round-trips: integers within the tagged-integer range, floats without NaN (never compares equal) or infinity (prints as a symbol), both printer limits, not carried bugs.
numpy_scalars
def numpy_scalars():Generate NumPy integer and real scalar values.
These retain identity while MeTTa accepts them as Number operands and dispatches through Python operators.
NumPy is optional. Install
pymetta[arrays,test]before requesting this strategy.
texts
def texts():Strings as the engine stores them; NUL is the one exclusion.
grounded
def grounded():Grounded atoms over numbers, booleans and strings.
atoms
def atoms(max_leaves: int = 8, *, ground: bool = False):Whole atoms: symbols, variables (unless ground=True), grounded values, and expressions recursively over all of them; max_leaves is hypothesis's own size knob for the recursion.
from hypothesis import given from metta import testing @given(testing.atoms()) def test_my_translator_round_trips(atom): assert decode(encode(atom)) == atom
expressions
def expressions(max_leaves: int = 8, *, ground: bool = False):Non-empty expression-rooted atoms, the shape spaces store.
ground_atoms
def ground_atoms(max_leaves: int = 8):Atoms carrying no variables: what a store holds after matching. atoms(ground=True) under the name provider fuzzing reaches for.
patterns
def patterns(max_leaves: int = 8):Expression-rooted atoms guaranteed to carry at least one variable: the query side of match, built rather than filtered so hypothesis never discards an example.
from_pattern
def from_pattern(pattern, max_leaves: int = 8):Generate ground instances of
patternby consistent substitution.Repeated named variables share one draw. Each anonymous
V._occurrence receives its own draw, matching the engine's non-binding anonymous law.
check_space_provider
def check_space_provider(provider, *, atoms_to_store=None, source='repeated') -> list[str]:Prove a SpaceProvider before its users find out. Answers the checks run.
The platform ships the conformance suite for its own extension points, which is the CSI sanity suite's reading, and JDBC's, and pytest's own
pytester. Without it a downstream library learns its provider is wrong from a bug report.from metta import testing def test_my_provider_conforms(): testing.check_space_provider(MyProvider(rows))Three things are checked, and the second is the one worth having.
Every declared capability is reachable.
can_runmay say yes to an operation whose method is absent, which is a registration-time mistake that otherwise surfaces as an AttributeError inside an engine callback.Match over-approximates rather than under-approximates. The provider contract's central soundness claim is that a provider may yield more than the pattern asks for, because the engine keeps unification, and may never yield less. Every stored atom vouches for a whole pattern family, itself, each position opened to a variable, and repeated-variable folds, and the provider's answers for each are compared with a brute-force unification scan of
atoms(). A provider that filters too eagerly, or that only handles ground patterns, or whose filter treats a repeated variable's occurrences independently, fails here rather than answering wrongly in production. An exact pushdown claim is held to the same family.A refusal names itself. An operation the provider declines raises with a sentence rather than failing, so a caller learns what to do instead.
sourcenames the provider's consumption discipline, matching its (source ...) declaration. A linear provider is one-shot, so every check that consumes more than once is skipped and said so; repeated and peek providers are enumerated twice and the two enumerations must agree, which is the promise those words make.Raises AssertionError on the first violation, naming the provider class, the operation and the atom.
THE CHECK IS UNIVERSAL: a provider is any foreign substrate, not only a Python object, and every substrate implements the space-provider protocol. Handed a
Spacehandle, this runs the engine's own checker (lib/lib_conformance/lib_conformance.pl'scheck-space-provider), which holds the same laws (capability reachability, the match pattern family, the declared source discipline, the canary round trip, the pushdown claim) asked through that protocol, so a provider written in Prolog, C, or anything else is held to one contract. The object form stays the pre-registration half for Python authors;source=applies to it alone, because a registered space carries its declared(source ...)class and the engine checker reads that instead of trusting a claim.
record_replay
def record_replay(provider):Wrap a provider so its answers append to a log, with the replayer.
The CakeML-oracle shape for host-stateful contexts: an append-only log makes a nondeterministic context's run replayable, and the differential replays the log instead of demanding a determinism the world does not have. Returns (recording, replay) where
recordingstands in for the provider andreplay()builds a provider serving the log verbatim.
check_replay
def check_replay(provider, patterns) -> list[str]:The ec_determ lane: for a fixed host state, evaluation is a function. Each pattern is matched live and recorded, then the log's replay must serve byte-identical answers, which is what makes a recorded session a differential oracle for a backend nobody can re-run.
check_minted_handles
def check_minted_handles(provider, registered=()) -> list[str]:The engine-minted-handles law: space identities are the engine's to mint, and a backend answers INTO spaces, never fabricates one.
Every &-headed symbol in the provider's answers must be a space the engine registered; a fabricated one is the reference nobody can resolve, cheap to refuse now and expensive to chase after a program stores it.
registerednames the spaces this provider may mention.
check_twin
def check_twin(defined, cases) -> list[str]:Prove a definition and its Python twin answer the same. Answers the cases run.
@m.definekeeps the original Python reachable as.py, and@m.define(prolog=...)keeps it when the fast side is written in Prolog instead. Either way the pair is a differential oracle, and this runs it:from metta import testing def test_the_fast_one_still_agrees(): testing.check_twin(vec_dot, [((1, 2), (3, 4)), ((0,), (9,))])
casesis an iterable of argument tuples. Drive it with hypothesis for a real sweep;metta.testingexports the strategies the library fuzzes itself with.A generator twin is compared answer by answer in order, since a generator compiles to nondeterminism and order is part of the answer. A twin that RAISES on a case requires the engine to answer nothing for it: a reference that has no answer and a fast side that invents one is the disagreement most worth catching.
Raises AssertionError on the first case where they differ, naming the case and both answers.