close
Skip to content

Model testing using Qcheck - #21

Merged
dlesbre merged 24 commits into
codex-semantics-library:mainfrom
Julow:model-testing
Jan 19, 2026
Merged

Model testing using Qcheck#21
dlesbre merged 24 commits into
codex-semantics-library:mainfrom
Julow:model-testing

Conversation

@Julow

@Julow Julow commented Dec 18, 2025

Copy link
Copy Markdown
Contributor

This adds extensive testing for the API generated by MakeMap using Qcheck and model-based testing.
These new tests don't replace the existing ones but complete them with a slightly different approach.
We choose to follow the method from QuickChecking Patricia Trees by Jan Midtgaard.

To summarize, the test framework is composed of these parts:

  • Model: Models PatriciaTree's API. The implementation is very simple and we can have good confidence in its correctness by review alone.
    It can also serve as specification or documentation when developing new operations.
  • Symbolic: Generate a sequence of operation that construct trees. It is used to generate trees of all shapes and is shrinkable by Qcheck.
  • Test: Defines tests for functions in the API and Qcheck generators for their arguments.
    Unlike the existing tests, the output of functions is not validated, instead it is compared with the output from the model.

During the development of this PR, we caught a bug (#19) and had a question (#18).
This PR is based on top of #20 so we could monitor the coverage.
According to bisect_ppx, the test coverage increases from 63% to 83%.

Julow and others added 20 commits December 18, 2025 15:53
The model and the tests were initially developped for Frama-C's Intmap
and are not yet exhaustive.

The tests consist of:

- model.ml implements a data structure that models a Intmap while being
  much simpler.
- symbolic.ml describes operations for building trees and builds random
  trees using both the model's and patricia-tree's APIs.
- test.ml defines and runs Qcheck tests for the API's functions. The
  tests compare the result computed by patricia-tree and the one
  computed by the model.

They are run with `dune runtest`.

Some tests are not passing at this stage: fold (due to the order of
iteration) and intersection.
The model doesn't compute the same result as the implementation for any
reconciliation function. This is due to optimisations that use physical
equality to avoid traversing part of the datastructure.
The result is a list literal containing the test definitions inline.
This might not seem like an improvement at first as the list doesn't
contain any line break and is uglier than the previous code. It is a
time saver however to not have to name each test at the top-level and
then to edit the list of tests separately.
Model the behavior when values at the same key are physically equals.
It was not correct when `f` returned None.

The qcheck arbitrary also contained a bug, the `choose` function uses
the printer of the first element (which is not intuitive) and doesn't
tries to magically call the right printer.
In the model implementation, same-domain checks and universal
quantification are checked separately for readability purpose.

Reflexivity of the predicate is guaranteed by wrapping the (not
necessarily reflexive) generated one.
This is the same test than for the `reflexive` version, without the
wrapping make the predicate reflexive.
The function assumes `f k v v = None`. This is guaranteed here by
wrapping the generated function.
- `update` and `mapi`: Cover the case where the new value is physically equal.
- `reflexive_compare`: Cover trees with an intersection.
- `max_binding_inter`: Fix typo in test definition.
Generate random and intersecting trees more often than identical trees.
@dlesbre

dlesbre commented Jan 9, 2026

Copy link
Copy Markdown
Collaborator

Thanks for this PR ! Having more tests is always good.

There are a few issues remaining though:

  • QCheck has recently completely changed their interface. I've added an upper bound to exclude the latest qcheck to main, which you need to merge.
  • We need to keep supporting OCaml 4.14, and that means we can't use more recent Stdlib additions like Fun.compose
  • There was already a test folder in src. Could you move your code there instead of having a second test folder ?

Julow added 3 commits January 9, 2026 11:58
The only source of incompatibility was the use of Fun.compose, which can
be easily avoided. The "with_" combinators are removed as their
expansion is trivial.
The model for this function needs to change since the recent change.
@Julow

Julow commented Jan 9, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for your feedback! I've merged main, updated the model for nonreflexive_same_domain_for_all2 and fixed the compatibility with 4.14.

Is it OK if I move the tests in src/test into test ? I think the test directory at the root of the project is clearer for new contributors and for readers. I'd create a hierarchy inside test to keep the two separate.

@dlesbre

dlesbre commented Jan 9, 2026

Copy link
Copy Markdown
Collaborator

Yes you're right, having the test directory outside src makes more sense. However, you'll have to keep the mdx_prelude.ml file in src.

@dlesbre dlesbre added the enhancement New feature or request label Jan 9, 2026
@Julow

Julow commented Jan 15, 2026

Copy link
Copy Markdown
Contributor Author

Hi! I reorganized the tests in the last commit.

Comment thread test/property/setTest.ml Outdated
The existing tests are moved into test/property.
The new tests are placed into test/model.
@dlesbre
dlesbre merged commit 7d80b63 into codex-semantics-library:main Jan 19, 2026
@Julow

Julow commented Jan 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks :)

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants