Skip to content

Commit b8703cb

Browse files
authored
use hegel's ergonomic enum generator support (#18)
1 parent a655aa9 commit b8703cb

1 file changed

Lines changed: 98 additions & 96 deletions

File tree

src/cursor/tests.rs

Lines changed: 98 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33

44
// Property-based tests for Cursor.
55

6+
// The DefaultGenerator derive generates PascalCase variable names for enum variants.
7+
#![expect(non_snake_case)]
8+
69
use crate::BufList;
710
use anyhow::{Context, Result, bail, ensure};
811
use bytes::{Buf, Bytes};
9-
use hegel::{DefaultGenerator, generators};
12+
// Import the Generator and DefaultGenerator *traits* (distinct from the DefaultGenerator derive
13+
// macro imported above) so that .map() and .default_generator() are available.
14+
use hegel::generators::DefaultGenerator as _;
15+
use hegel::{DefaultGenerator, Generator, generators};
1016
use std::{
1117
fmt,
1218
io::{self, BufRead, IoSliceMut, Read, Seek, SeekFrom},
@@ -51,102 +57,7 @@ fn buf_lists(tc: hegel::TestCase) -> BufList {
5157
chunks.into_iter().map(Bytes::from).collect()
5258
}
5359

54-
/// Unit enum for variant selection.
55-
///
56-
/// The derived DefaultGenerator picks a variant uniformly at random, without
57-
/// generating any field data.
5860
#[derive(Clone, Debug, DefaultGenerator)]
59-
enum CursorOpKind {
60-
SetPosition,
61-
SeekStart,
62-
SeekEnd,
63-
SeekCurrent,
64-
Read,
65-
ReadVectored,
66-
ReadExact,
67-
Consume,
68-
BufChunk,
69-
BufAdvance,
70-
BufChunksVectored,
71-
BufCopyToBytes,
72-
BufGetU8,
73-
BufGetU64,
74-
BufGetU64Le,
75-
#[cfg(feature = "tokio1")]
76-
PollRead,
77-
}
78-
79-
#[hegel::composite]
80-
fn cursor_ops(tc: hegel::TestCase, num_bytes: usize) -> CursorOp {
81-
match tc.draw(generators::default::<CursorOpKind>()) {
82-
CursorOpKind::SetPosition => {
83-
// Allow going past the end of the list a bit.
84-
let pos = tc.draw(generators::integers::<usize>().max_value(num_bytes * 5 / 4)) as u64;
85-
CursorOp::SetPosition(pos)
86-
}
87-
CursorOpKind::SeekStart => {
88-
// Allow going past the end of the list a bit.
89-
let pos = tc.draw(generators::integers::<usize>().max_value(num_bytes * 5 / 4)) as u64;
90-
CursorOp::SeekStart(pos)
91-
}
92-
CursorOpKind::SeekEnd => {
93-
// Allow going past the beginning and end of the list a bit.
94-
let raw = tc.draw(generators::integers::<usize>().max_value(num_bytes * 3 / 2));
95-
let offset = raw as i64 - (1 + num_bytes * 5 / 4) as i64;
96-
CursorOp::SeekEnd(offset)
97-
}
98-
CursorOpKind::SeekCurrent => {
99-
let raw = tc.draw(generators::integers::<usize>().max_value(num_bytes * 3 / 2));
100-
// Center the index at roughly 0.
101-
let offset = raw as i64 - (num_bytes * 3 / 4) as i64;
102-
CursorOp::SeekCurrent(offset)
103-
}
104-
CursorOpKind::Read => {
105-
let buf_size = tc.draw(generators::integers::<usize>().max_value(num_bytes * 5 / 4));
106-
CursorOp::Read(buf_size)
107-
}
108-
CursorOpKind::ReadVectored => {
109-
let n_bufs = tc.draw(generators::integers::<usize>().max_value(7));
110-
let sizes = (0..n_bufs)
111-
.map(|_| tc.draw(generators::integers::<usize>().max_value(num_bytes)))
112-
.collect();
113-
CursorOp::ReadVectored(sizes)
114-
}
115-
CursorOpKind::ReadExact => {
116-
let buf_size = tc.draw(generators::integers::<usize>().max_value(num_bytes * 5 / 4));
117-
CursorOp::ReadExact(buf_size)
118-
}
119-
CursorOpKind::Consume => {
120-
let amt = tc.draw(generators::integers::<usize>().max_value(num_bytes * 5 / 4));
121-
CursorOp::Consume(amt)
122-
}
123-
CursorOpKind::BufChunk => CursorOp::BufChunk,
124-
CursorOpKind::BufAdvance => {
125-
let amt = tc.draw(generators::integers::<usize>().max_value(num_bytes * 5 / 4));
126-
CursorOp::BufAdvance(amt)
127-
}
128-
CursorOpKind::BufChunksVectored => {
129-
let num_iovs = tc.draw(generators::integers::<usize>().max_value(num_bytes));
130-
CursorOp::BufChunksVectored(num_iovs)
131-
}
132-
CursorOpKind::BufCopyToBytes => {
133-
let len = tc.draw(generators::integers::<usize>().max_value(num_bytes * 5 / 4));
134-
CursorOp::BufCopyToBytes(len)
135-
}
136-
CursorOpKind::BufGetU8 => CursorOp::BufGetU8,
137-
CursorOpKind::BufGetU64 => CursorOp::BufGetU64,
138-
CursorOpKind::BufGetU64Le => CursorOp::BufGetU64Le,
139-
#[cfg(feature = "tokio1")]
140-
CursorOpKind::PollRead => {
141-
let capacity = tc.draw(generators::integers::<usize>().max_value(num_bytes * 5 / 4));
142-
// filled is in 0..=capacity, to sometimes fill the whole buffer.
143-
let filled = tc.draw(generators::integers::<usize>().max_value(capacity));
144-
CursorOp::PollRead { capacity, filled }
145-
}
146-
}
147-
}
148-
149-
#[derive(Clone, Debug)]
15061
enum CursorOp {
15162
SetPosition(u64),
15263
SeekStart(u64),
@@ -174,6 +85,97 @@ enum CursorOp {
17485
},
17586
}
17687

88+
/// Build a CursorOp generator with field constraints that depend on the
89+
/// BufList's size.
90+
///
91+
/// Uses `#[derive(DefaultGenerator)]` on `CursorOp` for variant selection,
92+
/// with per-variant field generators configured inline. This removes the need
93+
/// for a separate discriminant enum.
94+
fn cursor_ops(num_bytes: usize) -> impl Generator<CursorOp> {
95+
// `d` provides access to default_*() methods for building per-variant generators.
96+
let d = CursorOp::default_generator();
97+
98+
let gen = CursorOp::default_generator()
99+
.SetPosition(
100+
d.default_SetPosition()
101+
// Allow going past the end of the list a bit.
102+
.value(
103+
generators::integers::<usize>()
104+
.max_value(num_bytes * 5 / 4)
105+
.map(|v| v as u64),
106+
),
107+
)
108+
.SeekStart(
109+
d.default_SeekStart()
110+
// Allow going past the end of the list a bit.
111+
.value(
112+
generators::integers::<usize>()
113+
.max_value(num_bytes * 5 / 4)
114+
.map(|v| v as u64),
115+
),
116+
)
117+
.SeekEnd(
118+
d.default_SeekEnd()
119+
// Allow going past the beginning and end of the list a bit.
120+
.value(
121+
generators::integers::<usize>()
122+
.max_value(num_bytes * 3 / 2)
123+
.map(move |raw| raw as i64 - (1 + num_bytes * 5 / 4) as i64),
124+
),
125+
)
126+
.SeekCurrent(
127+
d.default_SeekCurrent()
128+
// Center the index at roughly 0.
129+
.value(
130+
generators::integers::<usize>()
131+
.max_value(num_bytes * 3 / 2)
132+
.map(move |raw| raw as i64 - (num_bytes * 3 / 4) as i64),
133+
),
134+
)
135+
.Read(
136+
d.default_Read()
137+
.value(generators::integers::<usize>().max_value(num_bytes * 5 / 4)),
138+
)
139+
.ReadVectored(d.default_ReadVectored().value(
140+
generators::vecs(generators::integers::<usize>().max_value(num_bytes)).max_size(7),
141+
))
142+
.ReadExact(
143+
d.default_ReadExact()
144+
.value(generators::integers::<usize>().max_value(num_bytes * 5 / 4)),
145+
)
146+
.Consume(
147+
d.default_Consume()
148+
.value(generators::integers::<usize>().max_value(num_bytes * 5 / 4)),
149+
)
150+
.BufAdvance(
151+
d.default_BufAdvance()
152+
.value(generators::integers::<usize>().max_value(num_bytes * 5 / 4)),
153+
)
154+
.BufChunksVectored(
155+
d.default_BufChunksVectored()
156+
.value(generators::integers::<usize>().max_value(num_bytes)),
157+
)
158+
.BufCopyToBytes(
159+
d.default_BufCopyToBytes()
160+
.value(generators::integers::<usize>().max_value(num_bytes * 5 / 4)),
161+
);
162+
163+
#[cfg(feature = "tokio1")]
164+
let gen = gen.PollRead(poll_read_op(num_bytes));
165+
166+
gen
167+
}
168+
169+
/// Generates `CursorOp::PollRead` with the constraint that `filled <= capacity`.
170+
#[cfg(feature = "tokio1")]
171+
#[hegel::composite]
172+
fn poll_read_op(tc: hegel::TestCase, num_bytes: usize) -> CursorOp {
173+
let capacity = tc.draw(generators::integers::<usize>().max_value(num_bytes * 5 / 4));
174+
// filled is in 0..=capacity, to sometimes fill the whole buffer.
175+
let filled = tc.draw(generators::integers::<usize>().max_value(capacity));
176+
CursorOp::PollRead { capacity, filled }
177+
}
178+
177179
impl CursorOp {
178180
fn apply_and_compare(
179181
self,

0 commit comments

Comments
 (0)