partial_io/
proptest_types.rs

1// Copyright (c) The partial-io Contributors
2// SPDX-License-Identifier: MIT
3
4//! Proptest support for partial IO operations.
5//!
6//! This module allows sequences of [`PartialOp`]s to be randomly generated. These
7//! sequences can then be fed into a [`PartialRead`](crate::PartialRead),
8//! [`PartialWrite`](crate::PartialWrite), [`PartialAsyncRead`](crate::PartialAsyncRead) or
9//! [`PartialAsyncWrite`](crate::PartialAsyncWrite).
10//!
11//! Once `proptest` has identified a failing test case, it will shrink the sequence of `PartialOp`s
12//! and find a minimal test case. This minimal case can then be used to reproduce the issue.
13//!
14//! Basic implementations are provided for:
15//! - generating errors some of the time
16//! - generating [`PartialOp`] instances, given a way to generate errors.
17//!
18//! # Examples
19//!
20//! ```rust
21//! use partial_io::proptest_types::{partial_op_strategy, interrupted_strategy};
22//! use proptest::{collection::vec, prelude::*};
23//!
24//! proptest! {
25//!     #[test]
26//!     fn proptest_something(ops: vec(partial_op_strategy(interrupted_strategy(), 128), 0..128)) {
27//!         // Example buffer to read from, substitute with your own.
28//!         let reader = std::io::repeat(42);
29//!         let partial_reader = PartialRead::new(reader, ops);
30//!         // ...
31//!
32//!         true
33//!     }
34//! }
35//! ```
36//!
37//! For a detailed example, see `examples/buggy_write.rs` in this repository.
38
39use crate::PartialOp;
40use proptest::{option::weighted, prelude::*};
41use std::io;
42
43/// Returns a strategy that generates `PartialOp` instances given a way to generate errors.
44///
45/// To not generate any errors and only limit reads, pass in `Just(None)` as the error strategy.
46pub fn partial_op_strategy(
47    error_strategy: impl Strategy<Value = Option<io::ErrorKind>>,
48    limit_bytes: usize,
49) -> impl Strategy<Value = PartialOp> {
50    // Don't generate 0 because for writers it can mean that writes are no longer accepted.
51    (error_strategy, 1..=limit_bytes).prop_map(|(error_kind, limit)| match error_kind {
52        Some(kind) => PartialOp::Err(kind),
53        None => PartialOp::Limited(limit),
54    })
55}
56
57/// Returns a strategy that generates `Interrupted` errors 20% of the time.
58pub fn interrupted_strategy() -> impl Strategy<Value = Option<io::ErrorKind>> {
59    weighted(0.2, Just(io::ErrorKind::Interrupted))
60}
61
62/// Returns a strategy that generates `WouldBlock` errors 20% of the time.
63pub fn would_block_strategy() -> impl Strategy<Value = Option<io::ErrorKind>> {
64    weighted(0.2, Just(io::ErrorKind::WouldBlock))
65}
66
67/// Returns a strategy that generates `Interrupted` errors 10% of the time and `WouldBlock` errors
68/// 10% of the time.
69pub fn interrupted_would_block_strategy() -> impl Strategy<Value = Option<io::ErrorKind>> {
70    weighted(
71        0.2,
72        prop_oneof![
73            Just(io::ErrorKind::Interrupted),
74            Just(io::ErrorKind::WouldBlock),
75        ],
76    )
77}