Skip to main content

krabka_protocol/
arbitrary_impls.rs

1#![cfg(feature = "arbitrary")]
2
3use arbitrary::{Arbitrary, Unstructured};
4
5use crate::{
6    UnknownTaggedFields,
7    owned::{
8        api_versions_request::ApiVersionsRequest,
9        api_versions_response::{ApiVersion, ApiVersionsResponse},
10    },
11};
12
13fn ascii(u: &mut Unstructured, min: usize, max: usize) -> arbitrary::Result<String> {
14    let len = u.int_in_range(min..=max)?;
15    let mut s = String::with_capacity(len);
16    for _ in 0..len {
17        let c: u8 = u.int_in_range(0x20..=0x7E)?;
18        s.push(c as char);
19    }
20    Ok(s)
21}
22
23impl<'a> Arbitrary<'a> for ApiVersionsRequest {
24    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
25        Ok(Self {
26            client_software_name: ascii(u, 0, 32)?,
27            client_software_version: ascii(u, 0, 32)?,
28            cluster_id: u
29                .arbitrary::<bool>()?
30                .then(|| ascii(u, 0, 32))
31                .transpose()?,
32            node_id: u.arbitrary()?,
33            unknown_tagged_fields: UnknownTaggedFields::default(),
34        })
35    }
36}
37
38impl<'a> Arbitrary<'a> for ApiVersion {
39    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
40        Ok(Self {
41            api_key: u.arbitrary()?,
42            min_version: u.arbitrary()?,
43            max_version: u.arbitrary()?,
44            unknown_tagged_fields: UnknownTaggedFields::default(),
45        })
46    }
47}
48
49impl<'a> Arbitrary<'a> for ApiVersionsResponse {
50    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
51        let n = u.int_in_range(0..=8usize)?;
52        let mut api_keys = Vec::with_capacity(n);
53        for _ in 0..n {
54            api_keys.push(ApiVersion::arbitrary(u)?);
55        }
56        Ok(Self {
57            error_code: u.arbitrary()?,
58            api_keys,
59            throttle_time_ms: u.arbitrary()?,
60            supported_features: Vec::new(),
61            finalized_features_epoch: 0,
62            finalized_features: Vec::new(),
63            zk_migration_ready: false,
64            unknown_tagged_fields: UnknownTaggedFields::default(),
65        })
66    }
67}
68
69#[cfg(test)]
70mod tests {
71
72    use super::*;
73
74    #[test]
75    fn ascii_uses_input_and_stays_printable() {
76        let mut found = None;
77        for seed in 0u8..=u8::MAX {
78            let data = [seed, seed.wrapping_mul(37), seed.wrapping_add(91), 0x7E];
79            let mut u = Unstructured::new(&data);
80            if let Ok(s) = ascii(&mut u, 1, 8)
81                && !s.is_empty()
82                && s != "xyzzy"
83            {
84                found = Some(s);
85                break;
86            }
87        }
88
89        let s = found.expect("expected at least one generated string");
90        assert2::assert!(s.bytes().all(|b| (0x20..=0x7E).contains(&b)));
91    }
92
93    #[test]
94    fn arbitrary_api_versions_request_can_be_non_default() {
95        let mut found = false;
96        for seed in 0u8..=u8::MAX {
97            let data = [seed, seed.wrapping_add(1), seed.wrapping_add(2), 0x55, 0xAA];
98            let mut u = Unstructured::new(&data);
99            if let Ok(req) = ApiVersionsRequest::arbitrary(&mut u)
100                && req != ApiVersionsRequest::default()
101            {
102                assert2::assert!(req.client_software_name.bytes().all(|b| b.is_ascii()));
103                assert2::assert!(req.client_software_version.bytes().all(|b| b.is_ascii()));
104                found = true;
105                break;
106            }
107        }
108
109        assert2::assert!(found);
110    }
111
112    #[test]
113    fn arbitrary_api_version_can_be_non_default() {
114        let mut found = false;
115        for seed in 0u8..=u8::MAX {
116            let data = [
117                seed,
118                seed.wrapping_mul(3),
119                seed.wrapping_add(5),
120                seed.wrapping_add(8),
121                seed.wrapping_add(13),
122                seed.wrapping_add(21),
123            ];
124            let mut u = Unstructured::new(&data);
125            if let Ok(version) = ApiVersion::arbitrary(&mut u)
126                && version != ApiVersion::default()
127            {
128                found = true;
129                break;
130            }
131        }
132
133        assert2::assert!(found);
134    }
135
136    #[test]
137    fn arbitrary_api_versions_response_can_be_non_default() {
138        let mut found = false;
139        for seed in 0u8..=u8::MAX {
140            let data = [
141                seed,
142                seed.wrapping_add(1),
143                seed.wrapping_add(2),
144                seed.wrapping_add(3),
145                seed.wrapping_add(4),
146                seed.wrapping_add(5),
147                seed.wrapping_add(6),
148                seed.wrapping_add(7),
149                seed.wrapping_add(8),
150            ];
151            let mut u = Unstructured::new(&data);
152            if let Ok(resp) = ApiVersionsResponse::arbitrary(&mut u)
153                && resp != ApiVersionsResponse::default()
154            {
155                found = true;
156                break;
157            }
158        }
159
160        assert2::assert!(found);
161    }
162}