tests_fuzz/ir/
string_value.rs1use datatypes::value::Value;
16use rand::Rng;
17
18use crate::error::{self, Result};
19use crate::generator::Random;
20use crate::ir::Ident;
21
22const READABLE_CHARSET: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
23
24fn readable_token(index: usize) -> String {
25 let base = READABLE_CHARSET.len();
26 let mut n = index + 1;
27 let mut buf = Vec::new();
28
29 while n > 0 {
30 let rem = (n - 1) % base;
31 buf.push(READABLE_CHARSET[rem] as char);
32 n = (n - 1) / base;
33 }
34
35 buf.iter().rev().collect()
36}
37
38pub fn generate_data_string_value<R: Rng>(
39 rng: &mut R,
40 random_str: Option<&dyn Random<Ident, R>>,
41) -> Value {
42 match random_str {
43 Some(random) => Value::from(random.generate(rng).value),
44 None => {
45 let idx = rng.random_range(0..(READABLE_CHARSET.len() * READABLE_CHARSET.len() * 4));
46 Value::from(readable_token(idx))
47 }
48 }
49}
50
51pub fn generate_partition_bounds(bounds: usize) -> Vec<Value> {
53 let token_space = READABLE_CHARSET.len() * READABLE_CHARSET.len() * 1024;
54 (1..=bounds)
55 .map(|i| {
56 let idx = i * token_space / (bounds + 1);
57 Value::from(readable_token(idx))
58 })
59 .collect()
60}
61
62pub fn generate_partition_value(bounds: &[Value], bound_idx: usize) -> Value {
64 let first = bounds.first().unwrap();
65 let last = bounds.last().unwrap();
66 let upper = match first {
67 Value::String(v) => v.as_utf8(),
68 _ => "",
69 };
70
71 if bound_idx == 0 {
72 if upper <= "0" {
73 Value::from("")
74 } else {
75 Value::from("0")
76 }
77 } else if bound_idx < bounds.len() {
78 bounds[bound_idx - 1].clone()
79 } else {
80 last.clone()
81 }
82}
83
84pub fn generate_unique_partition_bound<R: Rng>(rng: &mut R, bounds: &[Value]) -> Result<Value> {
86 let search_space = READABLE_CHARSET.len() * READABLE_CHARSET.len() * 1024;
87 let start = rng.random_range(0..search_space);
88 for offset in 0..search_space {
89 let idx = start + offset;
90 let candidate = Value::from(readable_token(idx));
91 if !bounds.contains(&candidate) {
92 return Ok(candidate);
93 }
94 }
95
96 error::UnexpectedSnafu {
97 violated: "unable to generate unique string partition bound".to_string(),
98 }
99 .fail()
100}
101
102#[cfg(test)]
103mod tests {
104 use rand::SeedableRng;
105 use rand_chacha::ChaCha8Rng;
106
107 use super::*;
108
109 #[test]
110 fn test_readable_token_grows_length() {
111 assert_eq!("0", readable_token(0));
112 assert_eq!("9", readable_token(9));
113 assert_eq!("A", readable_token(10));
114 assert_eq!("z", readable_token(61));
115 assert_eq!("00", readable_token(62));
116 }
117
118 #[test]
119 fn test_generate_partition_bounds_are_readable_and_unique() {
120 let bounds = generate_partition_bounds(8);
121 assert_eq!(8, bounds.len());
122
123 let mut values = bounds
124 .iter()
125 .map(|v| match v {
126 Value::String(s) => s.as_utf8().to_string(),
127 _ => panic!("expected string value"),
128 })
129 .collect::<Vec<_>>();
130 let mut dedup = values.clone();
131 dedup.sort();
132 dedup.dedup();
133 assert_eq!(values.len(), dedup.len());
134
135 for s in values.drain(..) {
136 assert!(s.chars().all(|c| c.is_ascii_alphanumeric()));
137 }
138 }
139
140 #[test]
141 fn test_generate_partition_value_for_string_bounds() {
142 let bounds = vec![Value::from("A"), Value::from("M")];
143 assert_eq!(Value::from("0"), generate_partition_value(&bounds, 0));
144 assert_eq!(Value::from("A"), generate_partition_value(&bounds, 1));
145 assert_eq!(Value::from("M"), generate_partition_value(&bounds, 2));
146 }
147
148 #[test]
149 fn test_generate_unique_partition_bound_not_in_existing() {
150 let mut rng = ChaCha8Rng::seed_from_u64(42);
151 let bounds = vec![Value::from("0"), Value::from("1"), Value::from("2")];
152 let candidate = generate_unique_partition_bound(&mut rng, &bounds).unwrap();
153 assert!(!bounds.contains(&candidate));
154 match candidate {
155 Value::String(s) => {
156 assert!(!s.as_utf8().is_empty());
157 assert!(s.as_utf8().chars().all(|c| c.is_ascii_alphanumeric()));
158 }
159 _ => panic!("expected string value"),
160 }
161 }
162}