[ANN] QCheck 0.90: The Great Renaming

It is my pleasure to announce release 0.90 of the QCheck packages. QCheck is an
OCaml library for randomized property-based testing in the style of Haskell’s
QuickCheck.

Release 0.90 · c-cube/qcheck · GitHub

It has been over 12 years and 40 releases since @c-cube released version 0.1
back in October 2013. Over this period QCheck has grown organically

  • with new combinators on a “by-need” basis and
  • with a separate QCheck2 module offering generators with integrated shrinking.

This has unfortunately resulted in a bit of a naming mess with inconsistent
generator names. For example, the (now deprecated) small_int combinator will
generate only small non-negative numbers, and a combinator for generating
positive integers uniformly is named either pint or pos_int across different
QCheck modules.

The 0.90 release thus takes on a cleanup under the heading “The Great Renaming”.
To guide the renaming process, we have assembled a list of hard-learned
naming principles:

  • Generator names should align with type names (bool, char, … list, option) to be as predictable as possible
  • We should have short, unparameterized generators (int, string, …) to lower the barrier to entry
  • Specialized generators also start with the type name, but use a consistent suffix (_pos, _neg, _size, _of, …) to help find them, e.g., with tab-completion
  • We may include a few shorthand names for convenience (e.g., nat)
  • Overall we aim to be as consistent as possible, e.g., offering similar signatures
    across generator interfaces (QCheck.Gen, QCheck.arbitrary, and QCheck2.Gen)

The 0.90 release thus both

  • introduces a range of new (and hopefully more consistent) combinator names and
  • deprecates a sizable number of old, inconsistent combinator names

The deprecated combinators have been annotated with @@deprecated attributes.

Rather than let a couple more years pass with an even bigger and more confusing
name pool, we are using this opportunity to prepare a long overdue 1.0.0
release, where we will remove the old, deprecated combinator names.

We understand that updating existing tests to the new names takes some effort, but appeal to
users that this should be a one-time investment to

  • offer more consistent and easier to recall combinator names and simultaneously
  • let us clean up QCheck tech debt and address a long-time pain point.

The changes are summarized in a record-long CHANGELOG section for the release:

qcheck/CHANGELOG.md at v0.90 · c-cube/qcheck · GitHub

and c-cube/qcheck#366 provides a run down of the renaming process.

For more details, see the following list of PRs:

Finally, on behalf of the maintainers I would like to thank

  • the various folks contributing to QCheck over the past 12 years and
  • the OCaml Software Foundation for financially supporting the work on these
    past three releases.

Merry Christmas and happy testing! :evergreen_tree: :wrapped_gift:

13 Likes

Version 0.91 of the QCheck packages is now available:

Release 0.91 · c-cube/qcheck · GitHub

This release adds ocamlmig annotations to most of the renamed combinators, in an attempt to ease migration for users. From my experiments so far, these work pretty well and allowed me to port multicoretests with little effort. Thanks to @raphael-proust for the suggestion and @v-gb for a nice tool! :folded_hands:

Happy migration and testing! :smiley:

3 Likes

Thanks for maintaining that nice package. I take this opportunity to make three remarks:

  • I already miss int_corners.

  • I use QCheck2 together with Alcotest (as QCheck_alcotest.to_alcotest permits), and a few descrepancies (slightly) annoy me:

    • You can define an “alcotestable” type by providing (an equality function and) a pretty-printer, whereas Qcheck2.Print.t is the type of a to_string function, not a pretty-printer. It would probably be better to align on Alcotest and use pretty-printers.
    • For tuples, QCheck2 uses tup2, tup3… whereas Alcotest uses pair, triple… and I never remember which one uses which. Probably Alcotest should adopt QCheck2’s convention.
  • As much as I prefer the French convention to the English one, we cannot single-handedly reverse a convention adopted by half a billion native English speakers:

    • nonnegative: >= 0
    • nonpositive: <= 0
    • negative: < 0
    • positive: > 0
    • Fortunately, “natural” almost always means “nonnegative integer” (although this is less universal than in French).

    For “positive” and “negative”, I generally write “(strictly) positive” and “(strictly) negative” respectively, to avoid any ambiguity.

Thanks for your feedback!

  • I already miss int_corners.

Can you show an example using it? I couldn’t find even a single usage in practice at
neither Sherlocode nor Code search results · GitHub

Note: we are not removing the combinator producing the int corner cases, we are removing the unused int list from the public API (for now).

  • I use QCheck2 together with Alcotest (as QCheck_alcotest.to_alcotest permits), and a few descrepancies (slightly) annoy me:
    • You can define an “alcotestable” type by providing (an equality function and) a pretty-printer, whereas Qcheck2.Print.t is the type of a to_string function, not a pretty-printer. It would probably be better to align on Alcotest and use pretty-printers.

Please open an issue upstream on the repo with this GitHub - c-cube/qcheck: QuickCheck inspired property-based testing for OCaml. with a bit more detail. I don’t use QCheck_alcotest much myself, but we are certainly open to making the improving the integration.

  • For tuples, QCheck2 uses tup2, tup3… whereas Alcotest uses pair, triple… and I never remember which one uses which. Probably Alcotest should adopt QCheck2’s convention.

This is not entirely accurate. QCheck2 and QCheck both offer tup2, tup3, …, as well as pair, triple and quad:

  • As much as I prefer the French convention to the English one, we cannot single-handedly reverse a convention adopted by half a billion native English speakers:

    • nonnegative: >= 0
    • nonpositive: <= 0
    • negative: < 0
    • positive: > 0
    • Fortunately, “natural” almost always means “nonnegative integer” (although this is less universal than in French).

    For “positive” and “negative”, I generally write “(strictly) positive” and “(strictly) negative” respectively, to avoid any ambiguity.

Your point is well taken. The renaming replaces, e.g., pos_int with int_pos. As such I have made no attempt to come up with a better adjective prefix/suffix. Getting names and distributions more consistent has already been quite some effort! FTR, I don’t find int_nonneg as a combinator name to necessarily be better, as it describes by complement (“doesn’t produce negatives”) in contrast to stating what is produced… :thinking:

1 Like