Skip to content

test(diagram): reference model and properties for Diagram.expand - #1621

Open
dimitri-yatsenko wants to merge 4 commits into
masterfrom
test/expand-reference-model
Open

dimitri-yatsenko wants to merge 4 commits into
masterfrom
test/expand-reference-model

Conversation

@dimitri-yatsenko

Copy link
Copy Markdown
Member

The first part of the test harness for Diagram.expand: an executable reference model of the traversal specification in datajoint/datajoint-docs#249, and tests that need no database.

What it adds

  • tests/unit/expand_model.py, the reference model. It defines D, D1, U and both as closures over rows, together with:
    • owned parts and their owning attributes;
    • the group quotient;
    • the specification's computation rules: the one pass over the quotient, the groups that iterate, R1 copies, and relabeling of materialized keys.
  • tests/unit/test_expand_spec.py checks the derived results T1–T8 and those rules with Hypothesis, on random schemas and databases.
  • tests/unit/test_expand_shapes_model.py mirrors the Traversal Shape Catalog of docs(specs): normative spec of Diagram.expand, with a traversal shape catalog datajoint-docs#249 one to one, under the same shape IDs. Every expected row is copied from the catalog by hand, not computed by the model.

Two T8 examples are strict xfails, because topo_sort still groups every part with its master by name (#1619). They start passing when #1620 merges, and the strict marker then flags them for removal.

Running

pytest tests/unit/test_expand_spec.py tests/unit/test_expand_shapes_model.py

HYPOTHESIS_PROFILE selects the profile:

Profile Examples Default when
dev 50 running locally
ci 500, the same on every run CI is set
thorough 2000, random; failing examples are saved under the gitignored .hypothesis/ never; select it explicitly

Dependencies

  • hypothesis joins the test dependency group and the test and dev extras.
  • pixi.lock is re-solved. The lock on master was stale, so the diff also records the test-py310 and test-py314 environments and packaging. No package version changes in the default, dev or test environments.

Next

The database layers (L1–L3) follow in later PRs. They run the engine against the same model on MySQL and PostgreSQL.

Add hypothesis to the test dependency group and to the test and dev
extras, for the property tests of Diagram.expand.

tests/conftest.py registers three profiles, selected with
HYPOTHESIS_PROFILE (default: ci when CI is set, else dev):
- dev: 50 examples, for fast local runs;
- ci: 500 examples, derandomized, no deadline;
- thorough: 2000 random examples, with failing examples saved under the
  gitignored .hypothesis/ directory.

pixi.lock is re-solved. Besides hypothesis and sortedcontainers in the
test and dev environments, it now records the test-py310 and test-py314
environments that pyproject.toml already declares, and packaging in the
default environment; no existing package changes version.
Add an executable reference model of Diagram.expand (L0 of the test
program) that needs no database:

- tests/unit/expand_model.py: TableSpec, FK and Schema; databases as
  {table: {pk_tuple: row}}; ref_e under MATCH SIMPLE (A2); the owning
  reference (A1), with parts that violate it traversed as plain tables;
  the row relations child, parent, inst, down and up; D, D1, U and both
  as breadth-first closures; the checkers ri_ok, group_whole,
  group_closed, lift_closed and q_acyclic; apply_delete; the
  self.upstream of a row not inserted yet; and the Dependencies graph
  built the way Dependencies.load builds it.
- tests/unit/test_expand_spec.py: Hypothesis generators for schemas and
  consistent databases, and properties for T1-T8. Minimality (T4, T5) is
  checked by brute force on databases of at most eight rows. T8 calls
  datajoint.dependencies.topo_sort with MySQL and PostgreSQL quoting.
  Two examples show that T8 needs A1, and parametrized checks confirm
  that the generators reach each shape the properties rely on.
- tests/unit/test_expand_shapes_model.py: one test per shape (S1-S7,
  G1-G6, K1, K2, K5, K6) with the expected rows written by hand.
Rewrite tests/unit/test_expand_shapes_model.py to mirror the Traversal
Shape Catalog (traversal-shapes.md) one to one: each shape's schema,
rows and seeds, the rows it states, and its node set, including the
tables listed as none. The catalog's claims about copies, relabeling,
delete modes and the rounds of an iterating group are checked too. The
seed evaluations that need a database (string seeds, Top, the warning)
and X1 are left to the integration suite; X2's cycle in Q is checked.

Extend the reference model:
- inst(r) = {r} for a row in no group, as diagram.md defines it;
- table_reach, the node set of an expanded diagram;
- RowGraph.delete with the three part_integrity modes;
- one_pass, the computation over Q evaluated on rows, with the
  iteration rule of diagram.md or the exact one;
- condition semantics for the R1 copy rule and for relabeling keys;
- Schema(..., a1="chain"), A1 generalized to owning chains with
  renames (#1429), with the owning attributes composed along the chain.

In test_expand_spec.py, T1-T8 now run under both readings of A1, T1 and
T2 include D1, and T6 checks monotonicity and minimality. New properties
check the one pass, the copy rule and relabeling. Three strict xfails
record where diagram.md's rules disagree with the definitions: a sibling
FK that does not carry the master key links instances without renaming
it; relabeling drops a key on a secondary attribute of the parent; and
a NULL key value on a nullable FK relabels to the NULL child rows.
Follow the revised traversal specification (datajoint-docs#249):

- A1 is ownership: a part is owned if an FK in its primary key
  references the master or an owned part of the same master, renames
  allowed. Its owning attributes come from that chain, preferring an
  unrenamed -> master and otherwise the first owning reference. This
  replaces the strict reading and the a1 switch; a part that references
  its master only below the line is no longer in its group.
- The lift goes through the owning attributes, and a group iterates if
  and only if some FK inside it does not map owning attributes onto
  owning attributes. The relabel rule is the revised one.
- The shape catalog gains the rewritten G6 (a part its master does not
  own) and G7 (an owned part-of-part with renamed attributes, #1429).

The three xfails for the spec's iteration and relabel rules are gone:
the docs now state the model's rules, and the tests assert them. Two
strict xfails remain for T8: topo_sort still groups every part with its
master by name, while the spec makes a part that is not owned a plain
table (#1619).

test_expand_spec.py opens with a description of the test layers and of
the Hypothesis profiles.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Indicates new improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant