Skip to content

Commit 6d07f34

Browse files
authored
chore: add more thest to prefix_right_bound() (#15374)
1 parent 7d3c3bc commit 6d07f34

File tree

1 file changed

+74
-6
lines changed

1 file changed

+74
-6
lines changed

src/meta/raft-store/src/utils.rs

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,95 @@
1313
// limitations under the License.
1414

1515
/// Return the right bound of the prefix, so that `p..right` will cover all strings with prefix `p`.
16+
///
17+
/// If the right bound can not be built, return None.
1618
pub fn prefix_right_bound(p: &str) -> Option<String> {
17-
let last = p.chars().last()?;
18-
let mut next_str = p[..p.len() - last.len_utf8()].to_owned();
19-
let next_char = char::from_u32(last as u32 + 1)?;
20-
next_str.push(next_char);
21-
Some(next_str)
19+
let mut chars = p.chars().collect::<Vec<_>>();
20+
21+
// Start from the end of the character list and look for the first character that is not \u{10FFFF}
22+
for i in (0..chars.len()).rev() {
23+
if chars[i] as u32 != 0x10FFFF {
24+
// Try to increment the character
25+
if let Some(next_char) = char::from_u32(chars[i] as u32 + 1) {
26+
chars[i] = next_char;
27+
// Remove all characters after the incremented one
28+
chars.truncate(i + 1);
29+
return Some(chars.iter().collect());
30+
} else {
31+
// If incrementing results in an invalid character, return None
32+
return None;
33+
}
34+
}
35+
}
36+
37+
// If all characters are \u{10FFFF} or the string is empty, return None
38+
None
2239
}
2340

2441
#[cfg(test)]
2542
mod tests {
2643
use super::prefix_right_bound;
2744

45+
#[test]
46+
fn test_prefix_right_bound_last_unicode() {
47+
// Test with the highest possible Unicode character
48+
assert_eq!(prefix_right_bound("\u{10FFFF}"), None);
49+
assert_eq!(prefix_right_bound("\u{10FFFF}\u{10FFFF}"), None);
50+
assert_eq!(prefix_right_bound("a\u{10FFFF}"), Some(s("b")));
51+
assert_eq!(prefix_right_bound("a\u{10FFFF}\u{10FFFF}"), Some(s("b")));
52+
assert_eq!(prefix_right_bound("aa\u{10FFFF}"), Some(s("ab")));
53+
assert_eq!(prefix_right_bound("aa\u{10FFFF}\u{10FFFF}"), Some(s("ab")));
54+
assert_eq!(
55+
prefix_right_bound("aa\u{10FFFF}\u{10FFFF}\u{10FFFF}"),
56+
Some(s("ab"))
57+
);
58+
}
59+
2860
#[test]
2961
fn test_next_string() {
30-
assert_eq!(None, prefix_right_bound(""));
3162
assert_eq!(Some(s("b")), prefix_right_bound("a"));
3263
assert_eq!(Some(s("{")), prefix_right_bound("z"));
3364
assert_eq!(Some(s("foo0")), prefix_right_bound("foo/"));
3465
assert_eq!(Some(s("foo💰")), prefix_right_bound("foo💯"));
3566
}
3667

68+
#[test]
69+
fn test_prefix_right_bound_basic() {
70+
// Basic functionality test
71+
assert_eq!(prefix_right_bound("abc"), Some(s("abd")));
72+
}
73+
74+
#[test]
75+
fn test_prefix_right_bound_empty() {
76+
// Test with an empty string
77+
assert_eq!(prefix_right_bound(""), None);
78+
}
79+
80+
#[test]
81+
fn test_prefix_right_bound_unicode() {
82+
// Test with Unicode characters
83+
assert_eq!(prefix_right_bound("😀"), Some(s("😁")));
84+
}
85+
86+
#[test]
87+
fn test_prefix_right_bound_increment() {
88+
// Test the boundary condition where the last character increments to the next logical Unicode character
89+
assert_eq!(prefix_right_bound("a"), Some(s("b")));
90+
assert_eq!(prefix_right_bound("z"), Some(s("{"))); // Note: 'z' + 1 = '{' in ASCII
91+
}
92+
93+
#[test]
94+
fn test_prefix_right_bound_non_ascii() {
95+
// Test with non-ASCII characters
96+
assert_eq!(prefix_right_bound("ñ"), Some(s("\u{00f2}")));
97+
}
98+
99+
#[test]
100+
fn test_prefix_right_bound_complex_string() {
101+
// Test with strings that require more complex boundary adjustments
102+
assert_eq!(prefix_right_bound("hello!"), Some(s("hello\"")));
103+
}
104+
37105
fn s(s: impl ToString) -> String {
38106
s.to_string()
39107
}

0 commit comments

Comments
 (0)