Skip to content

Strategies

provide.testkit.chaos.strategies

Core Hypothesis strategies for chaos testing.

Provides foundational strategies for generating chaotic test inputs that explore edge cases, boundary conditions, and failure scenarios.

Functions

chaos_timings

chaos_timings(
    draw: DrawFn,
    min_value: float = 0.001,
    max_value: float = 10.0,
    allow_zero: bool = False,
) -> float

Generate chaotic timing values for unpredictable delays.

Useful for testing timeout handling, race conditions, and time-sensitive code.

Parameters:

Name Type Description Default
draw DrawFn

Hypothesis draw function

required
min_value float

Minimum timing value in seconds

0.001
max_value float

Maximum timing value in seconds

10.0
allow_zero bool

Whether to allow zero delay

False

Returns:

Type Description
float

A floating-point timing value in seconds

Example
@given(delay=chaos_timings())
async def test_with_delay(delay):
    await asyncio.sleep(delay)
Source code in provide/testkit/chaos/strategies.py
@composite
def chaos_timings(
    draw: DrawFn,
    min_value: float = 0.001,
    max_value: float = 10.0,
    allow_zero: bool = False,
) -> float:
    """Generate chaotic timing values for unpredictable delays.

    Useful for testing timeout handling, race conditions, and time-sensitive code.

    Args:
        draw: Hypothesis draw function
        min_value: Minimum timing value in seconds
        max_value: Maximum timing value in seconds
        allow_zero: Whether to allow zero delay

    Returns:
        A floating-point timing value in seconds

    Example:
        ```python
        @given(delay=chaos_timings())
        async def test_with_delay(delay):
            await asyncio.sleep(delay)
        ```
    """
    min_val = 0.0 if allow_zero else min_value
    return draw(st.floats(min_value=min_val, max_value=max_value, allow_nan=False, allow_infinity=False))

edge_values

edge_values(draw: DrawFn, value_type: type = int) -> Any

Generate boundary and edge-case values for a given type.

Parameters:

Name Type Description Default
draw DrawFn

Hypothesis draw function

required
value_type type

Type to generate edge values for

int

Returns:

Type Description
Any

Edge-case value of the specified type

Example
@given(value=edge_values(value_type=int))
def test_integer_handling(value):
    result = process_int(value)
Source code in provide/testkit/chaos/strategies.py
@composite
def edge_values(  # type: ignore[misc]
    draw: DrawFn,
    value_type: type = int,
) -> Any:
    """Generate boundary and edge-case values for a given type.

    Args:
        draw: Hypothesis draw function
        value_type: Type to generate edge values for

    Returns:
        Edge-case value of the specified type

    Example:
        ```python
        @given(value=edge_values(value_type=int))
        def test_integer_handling(value):
            result = process_int(value)
        ```
    """
    if value_type is int:
        edges = [
            st.just(0),
            st.just(1),
            st.just(-1),
            st.just(sys.maxsize),
            st.just(-sys.maxsize - 1),
            st.just(2**31 - 1),  # INT32_MAX
            st.just(-(2**31)),  # INT32_MIN
            st.just(2**63 - 1),  # INT64_MAX
            st.just(-(2**63)),  # INT64_MIN
        ]
        return draw(st.one_of(*edges))

    elif value_type is float:
        edges = [
            st.just(0.0),
            st.just(-0.0),
            st.just(1.0),
            st.just(-1.0),
            st.just(float("inf")),
            st.just(float("-inf")),
            st.just(float("nan")),
            st.just(sys.float_info.min),
            st.just(sys.float_info.max),
            st.just(sys.float_info.epsilon),
        ]
        return draw(st.one_of(*edges))

    elif value_type is str:
        edges = [
            st.just(""),
            st.just(" "),
            st.just("\n"),
            st.just("\t"),
            st.just("\x00"),
            st.just("0"),
            st.just("-1"),
            st.just("null"),
            st.just("None"),
            st.just("undefined"),
        ]
        return draw(st.one_of(*edges))

    else:
        # Generic approach for other types
        return draw(st.from_type(value_type))

failure_patterns

failure_patterns(
    draw: DrawFn,
    max_failures: int = 10,
    exception_types: (
        tuple[type[Exception], ...] | None
    ) = None,
) -> list[tuple[int, type[Exception]]]

Generate patterns of when and how failures should occur.

Returns a list of (iteration_number, exception_type) tuples for failure injection.

Parameters:

Name Type Description Default
draw DrawFn

Hypothesis draw function

required
max_failures int

Maximum number of failures to generate

10
exception_types tuple[type[Exception], ...] | None

Specific exception types to use (defaults to common types)

None

Returns:

Type Description
list[tuple[int, type[Exception]]]

List of (when_to_fail, exception_class) tuples

Example
@given(failures=failure_patterns())
def test_with_failures(failures):
    for i, result in enumerate(operations):
        if any(i == when and exc for when, exc in failures):
            raise next(exc for when, exc in failures if when == i)
Source code in provide/testkit/chaos/strategies.py
@composite
def failure_patterns(
    draw: DrawFn,
    max_failures: int = 10,
    exception_types: tuple[type[Exception], ...] | None = None,
) -> list[tuple[int, type[Exception]]]:
    """Generate patterns of when and how failures should occur.

    Returns a list of (iteration_number, exception_type) tuples for failure injection.

    Args:
        draw: Hypothesis draw function
        max_failures: Maximum number of failures to generate
        exception_types: Specific exception types to use (defaults to common types)

    Returns:
        List of (when_to_fail, exception_class) tuples

    Example:
        ```python
        @given(failures=failure_patterns())
        def test_with_failures(failures):
            for i, result in enumerate(operations):
                if any(i == when and exc for when, exc in failures):
                    raise next(exc for when, exc in failures if when == i)
        ```
    """
    exc_types = exception_types or COMMON_EXCEPTIONS
    num_failures = draw(st.integers(min_value=0, max_value=max_failures))

    return [
        (
            draw(st.integers(min_value=0, max_value=100)),
            draw(st.sampled_from(exc_types)),
        )
        for _ in range(num_failures)
    ]

malformed_inputs

malformed_inputs(
    draw: DrawFn,
    include_huge: bool = True,
    include_empty: bool = True,
    include_special: bool = True,
) -> Any

Generate edge-case inputs for robust testing.

Creates various malformed, extreme, or unusual inputs that often expose bugs.

Parameters:

Name Type Description Default
draw DrawFn

Hypothesis draw function

required
include_huge bool

Include very large strings/data

True
include_empty bool

Include empty/None values

True
include_special bool

Include special numeric values (NaN, inf)

True

Returns:

Type Description
Any

Various malformed input values

Example
@given(data=malformed_inputs())
def test_parser(data):
    result = parse(data)  # Should handle gracefully
Source code in provide/testkit/chaos/strategies.py
@composite
def malformed_inputs(  # type: ignore[misc]
    draw: DrawFn,
    include_huge: bool = True,
    include_empty: bool = True,
    include_special: bool = True,
) -> Any:
    """Generate edge-case inputs for robust testing.

    Creates various malformed, extreme, or unusual inputs that often expose bugs.

    Args:
        draw: Hypothesis draw function
        include_huge: Include very large strings/data
        include_empty: Include empty/None values
        include_special: Include special numeric values (NaN, inf)

    Returns:
        Various malformed input values

    Example:
        ```python
        @given(data=malformed_inputs())
        def test_parser(data):
            result = parse(data)  # Should handle gracefully
        ```
    """
    strategies = []

    # Text inputs
    strategies.append(st.text(min_size=0, max_size=1000))
    if include_huge:
        # Use 8KB as max to stay within Hypothesis BUFFER_SIZE limits
        strategies.append(st.text(min_size=5000, max_size=8000))

    # Binary inputs
    strategies.append(st.binary(min_size=0, max_size=1000))
    if include_huge:
        # Use 8KB as max to stay within Hypothesis BUFFER_SIZE limits
        strategies.append(st.binary(min_size=5000, max_size=8000))

    # Numeric inputs
    strategies.append(st.integers())
    strategies.append(st.floats(allow_nan=include_special, allow_infinity=include_special))

    # Empty/None values
    if include_empty:
        strategies.extend([st.just(None), st.just(""), st.just(b""), st.just([])])

    # Lists and dicts
    strategies.append(st.lists(st.integers(), min_size=0, max_size=100))
    strategies.append(st.dictionaries(st.text(), st.integers(), min_size=0, max_size=100))

    return draw(st.one_of(*strategies))

resource_limits

resource_limits(
    draw: DrawFn,
    min_memory: int = 1024,
    max_memory: int = 1024 * 1024 * 100,
    min_timeout: float = 0.01,
    max_timeout: float = 60.0,
) -> dict[str, Any]

Generate resource constraint scenarios.

Creates various resource limit configurations for testing behavior under constraints.

Parameters:

Name Type Description Default
draw DrawFn

Hypothesis draw function

required
min_memory int

Minimum memory limit in bytes

1024
max_memory int

Maximum memory limit in bytes

1024 * 1024 * 100
min_timeout float

Minimum timeout in seconds

0.01
max_timeout float

Maximum timeout in seconds

60.0

Returns:

Type Description
dict[str, Any]

Dictionary with resource limit configuration

Example
@given(limits=resource_limits())
def test_with_limits(limits):
    with resource_limiter(limits['memory'], limits['timeout']):
        perform_operation()
Source code in provide/testkit/chaos/strategies.py
@composite
def resource_limits(
    draw: DrawFn,
    min_memory: int = 1024,
    max_memory: int = 1024 * 1024 * 100,  # 100MB
    min_timeout: float = 0.01,
    max_timeout: float = 60.0,
) -> dict[str, Any]:
    """Generate resource constraint scenarios.

    Creates various resource limit configurations for testing behavior under constraints.

    Args:
        draw: Hypothesis draw function
        min_memory: Minimum memory limit in bytes
        max_memory: Maximum memory limit in bytes
        min_timeout: Minimum timeout in seconds
        max_timeout: Maximum timeout in seconds

    Returns:
        Dictionary with resource limit configuration

    Example:
        ```python
        @given(limits=resource_limits())
        def test_with_limits(limits):
            with resource_limiter(limits['memory'], limits['timeout']):
                perform_operation()
        ```
    """
    return {
        "memory": draw(st.integers(min_value=min_memory, max_value=max_memory)),
        "timeout": draw(st.floats(min_value=min_timeout, max_value=max_timeout)),
        "cpu_count": draw(st.integers(min_value=1, max_value=64)),
        "max_threads": draw(st.integers(min_value=1, max_value=1000)),
        "max_open_files": draw(st.integers(min_value=10, max_value=10000)),
    }

unicode_chaos

unicode_chaos(
    draw: DrawFn,
    include_emoji: bool = True,
    include_rtl: bool = True,
    include_zero_width: bool = True,
    include_surrogates: bool = False,
) -> str

Generate problematic Unicode strings for testing.

Creates strings with emoji, RTL text, zero-width characters, and other Unicode edge cases that often cause issues.

Parameters:

Name Type Description Default
draw DrawFn

Hypothesis draw function

required
include_emoji bool

Include emoji characters

True
include_rtl bool

Include right-to-left text

True
include_zero_width bool

Include zero-width characters

True
include_surrogates bool

Include surrogate pairs (can be problematic)

False

Returns:

Type Description
str

A Unicode string with chaotic properties

Example
@given(text=unicode_chaos())
def test_text_handling(text):
    result = process_text(text)
Source code in provide/testkit/chaos/strategies.py
@composite
def unicode_chaos(  # type: ignore[misc]
    draw: DrawFn,
    include_emoji: bool = True,
    include_rtl: bool = True,
    include_zero_width: bool = True,
    include_surrogates: bool = False,
) -> str:
    """Generate problematic Unicode strings for testing.

    Creates strings with emoji, RTL text, zero-width characters, and other
    Unicode edge cases that often cause issues.

    Args:
        draw: Hypothesis draw function
        include_emoji: Include emoji characters
        include_rtl: Include right-to-left text
        include_zero_width: Include zero-width characters
        include_surrogates: Include surrogate pairs (can be problematic)

    Returns:
        A Unicode string with chaotic properties

    Example:
        ```python
        @given(text=unicode_chaos())
        def test_text_handling(text):
            result = process_text(text)
        ```
    """
    strategies = []

    # Basic Unicode text
    strategies.append(st.text(min_size=0, max_size=100))

    # Emoji ranges
    if include_emoji:
        # Emoticons and pictographs
        strategies.append(
            st.text(
                alphabet=st.characters(min_codepoint=0x1F300, max_codepoint=0x1F9FF),
                min_size=1,
                max_size=20,
            )
        )

    # RTL characters (Arabic, Hebrew)
    if include_rtl:
        strategies.append(
            st.text(
                alphabet=st.characters(min_codepoint=0x0590, max_codepoint=0x08FF),
                min_size=1,
                max_size=50,
            )
        )

    # Zero-width characters
    if include_zero_width:
        zero_width_chars = "\u200b\u200c\u200d\ufeff"  # ZWSP, ZWNJ, ZWJ, ZWNBSP
        strategies.append(st.text(alphabet=zero_width_chars, min_size=1, max_size=10))

    # Control characters
    strategies.append(st.text(alphabet=st.characters(max_codepoint=0x001F), min_size=1, max_size=10))

    # Combining characters
    strategies.append(
        st.text(alphabet=st.characters(min_codepoint=0x0300, max_codepoint=0x036F), min_size=1, max_size=20)
    )

    return draw(st.one_of(*strategies))