Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_matches_builtin(ls):
assert sorted(ls) == my_sort(ls)
```

This randomized testing can catch bugs and edge cases that you didn't think of and wouldn't have found. In addition, when Hypothesis does find a bug, it doesn't just report any failing example — it reports the simplest possible one. This makes property-based tests a powerful tool for debugging, as well as testing.
This randomized testing can catch bugs and edge cases that you didn't think of and wouldn't have found. In addition, when Hypothesis does find a bug, it doesn't just report any failing test case — it reports the simplest possible one. This makes property-based tests a powerful tool for debugging, as well as testing.

For instance,

Expand All @@ -30,10 +30,10 @@ def my_sort(ls):
return sorted(set(ls))
```

fails with the simplest possible failing example:
fails with the simplest possible failing test case:

```
Falsifying example: test_matches_builtin(ls=[0, 0])
Failing test case: test_matches_builtin(ls=[0, 0])
```

### Installation
Expand Down
7 changes: 7 additions & 0 deletions hypothesis/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
RELEASE_TYPE: minor

Hypothesis has historically referred to the single execution of a test function as an "example". However, we have in recent years tended to call a single execution a "test case" instead, which we think is a more precise and less overloaded term.

This release updates user-facing documentation, log messages, and error messages to use the "test case" terminology.

This is not a breaking change, as we have intentionally not changed any code APIs. For example, |settings.max_examples| has not been changed.
13 changes: 0 additions & 13 deletions hypothesis/docs/_static/better-signatures.css

This file was deleted.

28 changes: 0 additions & 28 deletions hypothesis/docs/_static/dark-fix.css

This file was deleted.

5 changes: 0 additions & 5 deletions hypothesis/docs/_static/no-scroll.css

This file was deleted.

71 changes: 71 additions & 0 deletions hypothesis/docs/_static/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* dl gets used both for defining each top-level `.. autofunc` on a page (where we want vertical margsin)
and is wrapped around multiline signatures (where we don't).
If a dl is being used inside a .sig, that's a multiline signature; remove its margins. */
.sig > dl {
margin-block-start: 0rem;
margin-block-end: 0rem;
}

/* with thanks to https://github.com/pradyunsg/furo/discussions/749 */
.sig:not(.sig-inline) {
padding-left: 0.5em;
text-indent: 0;
}

/* override table width restrictions */
/* thanks to https://github.com/readthedocs/sphinx_rtd_theme/issues/117#issuecomment-153083280 */
@media screen and (min-width: 767px) {

.wy-table-responsive table td {
/* !important prevents the common CSS stylesheets from
overriding this as on RTD they are loaded after this stylesheet */
white-space: normal !important;
}

.wy-table-responsive {
overflow: visible !important;
}

}

/* disable autoscroll-to-target behavior
https://github.com/pradyunsg/furo/discussions/384#discussioncomment-2249243 */
html {
scroll-behavior: auto;
}

/*
See https://github.com/HypothesisWorks/hypothesis/issues/4588 and
https://github.com/pradyunsg/furo/discussions/909. Once this is fixed in furo,
we can remove this.
*/

body[data-theme="dark"] .tooltip .tooltip-content {
background-color: var(--color-background-primary);
}

body[data-theme="dark"] .tooltip .arrow {
background: var(--color-background-primary);
}

/*
Furo also renders dark when the theme is "auto" (the default) and the OS
prefers dark, via this same media query. Cover that case too; see
https://github.com/HypothesisWorks/hypothesis/issues/4734.
*/
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) .tooltip .tooltip-content {
background-color: var(--color-background-primary);
}

body:not([data-theme="light"]) .tooltip .arrow {
background: var(--color-background-primary);
}
}

a:has(> .std-term),
a:has(> .std-term):hover {
color: inherit;
text-decoration-color: currentColor;
text-decoration-style: dotted;
}
Comment on lines +65 to +71

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combining our css files into one. The new content here is what I've highlighted here: removing blue links and adding underlines to glossary terms, for a more low-key look.

15 changes: 0 additions & 15 deletions hypothesis/docs/_static/wrap-in-tables.css

This file was deleted.

2 changes: 1 addition & 1 deletion hypothesis/docs/compatibility.rst
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,4 @@ We ship type hints with Hypothesis itself. Though we always try to minimize brea

We may also find more precise ways to describe the type of various interfaces, or change their type and runtime behaviour together in a way which is otherwise backwards-compatible.

There are known issues with inferring the type of examples generated by |st.deferred|, |st.recursive|, |st.one_of|, |st.dictionaries|, and |st.fixed_dictionaries|. We're following proposed updates to Python's typing standards, but unfortunately the long-standing interfaces of these strategies cannot (yet) be statically typechecked.
There are known issues with inferring the type of values generated by |st.deferred|, |st.recursive|, |st.one_of|, |st.dictionaries|, and |st.fixed_dictionaries|. We're following proposed updates to Python's typing standards, but unfortunately the long-standing interfaces of these strategies cannot (yet) be statically typechecked.
7 changes: 1 addition & 6 deletions hypothesis/docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,7 @@ def process_signature(app, what, name, obj, options, signature, return_annotatio
# remove "Hypothesis <version> documentation" from just below logo on the sidebar
html_theme_options = {"sidebar_hide_name": True}
html_static_path = ["_static"]
html_css_files = [
"better-signatures.css",
"wrap-in-tables.css",
"no-scroll.css",
"dark-fix.css",
]
html_css_files = ["styles.css"]
htmlhelp_basename = "Hypothesisdoc"
html_favicon = "../../brand/favicon.ico"
html_logo = "../../brand/dragonfly-rainbow-150w.svg"
Expand Down
4 changes: 2 additions & 2 deletions hypothesis/docs/explanation/domain.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ An exact answer depends on both the strategy or strategies for the tests, and th

Hypothesis' default configuration uses a distribution which is tuned to maximize the chance of finding bugs, in as few executions as possible. We explicitly *don't* aim for a uniform distribution, nor for a 'realistic' distribution of inputs; Hypothesis' goal is to search the domain for a failing input as efficiently as possible.

The test case distribution remains an active area of research and development, and we change it whenever we think that would be a net improvement for users. Today, Hypothesis' default distribution is shaped by a wide variety of techniques and heuristics:
The |test case| distribution remains an active area of research and development, and we change it whenever we think that would be a net improvement for users. Today, Hypothesis' default distribution is shaped by a wide variety of techniques and heuristics:

* some are statically designed into strategies - for example, |st.integers| upweights range endpoints, and samples from a mixed distribution over integer bit-widths.
* some are dynamic features of the engine - like replaying prior examples with subsections of the input 'cloned' or otherwise altered, for bugs which trigger only when different fields have the same value (which is otherwise exponentially unlikely).
* some are dynamic features of the engine - like replaying prior test cases with subsections of the input 'cloned' or otherwise altered, for bugs which trigger only when different fields have the same value (which is otherwise exponentially unlikely).
* some vary depending on the code under test - we collect interesting-looking constants from imported source files as seeds for test cases.
* `swarm testing <https://www.cs.utah.edu/~regehr/papers/swarm12.pdf>`__ adds further randomization when choosing which rules to execute in stateful testing.

Expand Down
26 changes: 13 additions & 13 deletions hypothesis/docs/explanation/example-count.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ How many times will Hypothesis run my test?
This is a trickier question than you might expect. The short answer is "exactly |max_examples| times", with the following exceptions:

- Less than |max_examples| times, if Hypothesis exhausts the search space early.
- More than |max_examples| times, if Hypothesis retries some examples because either:
- More than |max_examples| times, if Hypothesis retries some |test cases| because either:

- They failed an |assume| or |.filter| condition, or
- They were too large to continue generating.

- Either less or more than |max_examples| times, if Hypothesis finds a failing example.
- Either less or more than |max_examples| times, if Hypothesis finds a |failing test case|.

Read on for details.

Search space exhaustion
-----------------------

If Hypothesis detects that there are no more examples left to try, it may stop generating examples before it hits |max_examples|. For example:
If Hypothesis detects that there are no more test cases left to try, it may stop generating test cases before it hits |max_examples|. For example:

.. code-block:: python

Expand Down Expand Up @@ -45,7 +45,7 @@ The search space tracking in Hypothesis is good, but not perfect. We treat this
|assume| and |.filter|
----------------------

If an example fails to satisfy an |assume| or |.filter| condition, Hypothesis will retry generating that example and will not count it towards the |max_examples| limit. For instance:
If a test case fails to satisfy an |assume| or |.filter| condition, Hypothesis will retry generating that test case and will not count it towards the |max_examples| limit. For instance:

.. code-block:: python

Expand All @@ -55,25 +55,25 @@ If an example fails to satisfy an |assume| or |.filter| condition, Hypothesis wi
def test_function(n):
assume(n % 2 == 0)

will run roughly 200 times, since half of the examples are discarded from the |assume|.
will run roughly 200 times, since half of the test cases are discarded from the |assume|.

Note that while failing an |assume| triggers an immediate retry of the entire example, Hypothesis will try several times in the same example to satisfy a |.filter| condition. This makes expressing the same condition using |.filter| more efficient than |assume|.
Note that while failing an |assume| triggers an immediate retry of the entire test case, Hypothesis will try several times in the same test case to satisfy a |.filter| condition. This makes expressing the same condition using |.filter| more efficient than |assume|.

Also note that even if your code does not explicitly use |assume| or |.filter|, a builtin strategy may still use them and cause retries. We try to directly satisfy conditions where possible instead of relying on rejection sampling, so this should be relatively uncommon.

Examples which are too large
----------------------------
Test cases which are too large
------------------------------

For performance reasons, Hypothesis places an internal limit on the size of a single example. If an example exceeds this size limit, we will retry generating it and will not count it towards the |max_examples| limit. (And if we see too many of these large examples, we will raise |HealthCheck.data_too_large|, unless suppressed with |settings.suppress_health_check|).
For performance reasons, Hypothesis places an internal limit on the size of a single test case. If a test case exceeds this size limit, we will retry generating it and will not count it towards the |max_examples| limit. (And if we see too many of these large test cases, we will raise |HealthCheck.data_too_large|, unless suppressed with |settings.suppress_health_check|).

The specific value of this size limit is an undocumented implementation detail. The majority of Hypothesis tests do not come close to hitting it.

Failing examples
----------------
Failing test cases
------------------

If Hypothesis finds a failing example, it stops generation early, and may call the test function additional times during the |Phase.shrink| and |Phase.explain| phases. Sometimes, Hypothesis determines that the initial failing example was already as simple as possible, in which case |Phase.shrink| will not result in additional test executions (but |Phase.explain| might).
If Hypothesis finds a failing test case, it stops generation early, and may call the test function additional times during the |Phase.shrink| and |Phase.explain| phases. Sometimes, Hypothesis determines that the initial failing test case was already as simple as possible, in which case |Phase.shrink| will not result in additional test executions (but |Phase.explain| might).

Regardless of whether Hypothesis runs the test during the shrinking and explain phases, it will always run the minimal failing example one additional time to check for flakiness. For instance, the following trivial test runs with ``n=0`` *twice*, even though it only uses the |Phase.generate| phase:
Regardless of whether Hypothesis runs the test during the shrinking and explain phases, it will always run the minimal failing test case one additional time to check for flakiness. For instance, the following trivial test runs with ``n=0`` *twice*, even though it only uses the |Phase.generate| phase:

.. code-block:: python

Expand Down
86 changes: 86 additions & 0 deletions hypothesis/docs/glossary.rst

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new glossary page

Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
Glossary
========

User glossary
-------------

Terms that are part of our public API.

.. glossary::

explicit example
A |test case| from |@example|.

.. code-block:: python

@example(42)
def f(n):
pass

Here, ``42`` is an explicit example.

Explicit examples are always run, in the |Phase.explicit| phase. Unlike Hypothesis-generated test cases, Hypothesis does not shrink explicit examples.

failing test case
A |test case| which causes the test to fail, usually by causing an exception to be raised.

minimal failing test case
The |failing test case| which has been fully shrunk (minimized) by Hypothesis. Hypothesis reports only the minimal failing test case to the user at the end of the test.

.. code-block:: python

@given(st.integers())
def f(n):
print("called with", n)
assert n < 10

.. code-block:: none

called with 0
called with 921
called with 212
...
called with 10
...
Failing test case: f(
n=10,
)

Here, each of ``921``, ``212``, and ``10`` are failing test cases. ``10`` is the minimal failing test case.

test case
The Hypothesis-generated input to a test function.

.. code-block:: python

@given(st.integers())
def f(n):
print("called with", n)
assume(n >= 0)

.. code-block:: none

called with 0
called with 18588
called with 672780074
called with -32616
...

Here, the first four test cases are ``0``, ``18588``, ``672780074``, and ``-32616``.

"Test case" may also refer to the resulting execution of an input in a test function. Above, we might say that "the test case ``-32616`` failed the |assume|", referring to its entire execution.


Developer glossary
------------------

Terms that are part of our developer API. The developer API is intended for advanced users, researchers, and developers building on top of Hypothesis.

.. glossary::

choice sequence
The underlying sequence of primitive values corresponding to a :term:`test case`. Conceptually, a choice sequence is the sequence of random choices made in the course of generating a value in a strategy. Each test case is represented internally as a choice sequence.

The exact representation is an implementation detail and depends on the strategy. For example, the value ``True`` from |st.booleans| is represented by the choice sequence ``[True]``, while the value ``[2, 42]`` from ``st.lists(st.integers())`` is represented by the choice sequence ``[True, 2, True, 42, False]``.

Currently, a choice sequence consists of booleans, integers, floats, strings, and bytes.
2 changes: 1 addition & 1 deletion hypothesis/docs/how-to/external-fuzzers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Sometimes you might want to point a traditional fuzzer like `python-afl <https:/
If you already have Hypothesis tests and want to fuzz them, or are targeting pure Python code, we strongly recommend the purpose-built `HypoFuzz <https://hypofuzz.com/>`_.
This page is about writing traditional 'fuzz harnesses' with an external fuzzer, using parts of Hypothesis.

In order to support this workflow, Hypothesis exposes the |fuzz_one_input| method. |fuzz_one_input| takes a bytestring, parses it into a test case, and executes the corresponding test once. This means you can treat each of your Hypothesis tests as a traditional fuzz target, by pointing the fuzzer at |fuzz_one_input|.
In order to support this workflow, Hypothesis exposes the |fuzz_one_input| method. |fuzz_one_input| takes a bytestring, parses it into a |test case|, and executes the corresponding test once. This means you can treat each of your Hypothesis tests as a traditional fuzz target, by pointing the fuzzer at |fuzz_one_input|.

For example:

Expand Down
2 changes: 1 addition & 1 deletion hypothesis/docs/how-to/suppress-healthchecks.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Suppress a health check everywhere
==================================

Hypothesis sometimes raises a |HealthCheck| to indicate that your test may be less effective than you expect, slower than you expect, unlikely to generate effective examples, or otherwise has silently degraded performance.
Hypothesis sometimes raises a |HealthCheck| to indicate that your test may be less effective than you expect, slower than you expect, unlikely to generate effective |test cases|, or otherwise has silently degraded performance.

While |HealthCheck| can be useful to proactively identify issues, you may not care about certain classes of them. If you want to disable a |HealthCheck| everywhere, you can register and load a settings profile with |settings.register_profile| and |settings.load_profile|. Place the following code in any file which is loaded before running your tests (or in ``conftest.py``, if using pytest):

Expand Down
2 changes: 1 addition & 1 deletion hypothesis/docs/how-to/type-strategies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Hypothesis provides type hints for all strategies and functions which return a s
reveal_type(st.lists(st.integers()))
# SearchStrategy[list[int]]

|SearchStrategy| is the type of a strategy. It is parametrized by the type of the example it generates. You can use it to write type hints for your functions which return a strategy:
|SearchStrategy| is the type of a strategy. It is parametrized by the type of the values it generates. You can use it to write type hints for your functions which return a strategy:

.. code-block:: python

Expand Down
1 change: 1 addition & 0 deletions hypothesis/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ Technical API reference.
reference/index
stateful
Extras <extras>
glossary
changelog

.. toctree::
Expand Down
Loading
Loading