Short (up to 65,535 bytes) immutable strings to e.g. parse tokens, implemented in Rust. These are sometimes called "German Strings", because Germans have written the paper mentioning them
1// SPDX-FileCopyrightText: Copyright (C) 2024 Roland Csaszar
2// SPDX-License-Identifier: MPL-2.0
3//
4// Project: token-string
5// File: test_builder.rs
6// Date: 24.Nov.2024
7// =============================================================================
8//! Test the concatenation of `TokenStrings`s.
9
10#![expect(clippy::tests_outside_test_module, reason = "tests directory")]
11
12mod errors {
13
14 use assert2::{check, let_assert};
15 use token_string::{Builder, MAX_LENGTH, TkStrError, TokenString};
16
17 #[test]
18 fn too_many() {
19 let_assert!(Ok(s1) = TokenString::try_from("Hello"));
20 let_assert!(Ok(s2) = TokenString::try_from(", "));
21 let_assert!(Ok(s3) = TokenString::try_from("world"));
22 let_assert!(Ok(s4) = TokenString::try_from("!"));
23 let mut builder = Builder::<'_, 3>::new(&s1);
24 let_assert!(Ok(_) = builder.concat_checked(&s2));
25 let_assert!(Ok(_) = builder.concat_checked(&s3));
26 let_assert!(Err(e) = builder.concat_checked(&s4));
27 check!(e == TkStrError::TooMany(3));
28 }
29
30 #[test]
31 fn too_big() {
32 let_assert!(Ok(s1) = TokenString::try_from("Hello"));
33 let_assert!(Ok(s2) = TokenString::try_from(", "));
34 let_assert!(Ok(s3) = TokenString::try_from("world"));
35 let big = vec![b'1'; MAX_LENGTH].into_boxed_slice();
36 let_assert!(Ok(s4) = TokenString::try_from(&*big));
37 let mut builder = Builder::<'_, 4>::new(&s1);
38 let_assert!(Ok(_) = builder.concat_checked(&s2));
39 let_assert!(Ok(_) = builder.concat_checked(&s3));
40 let_assert!(Err(e) = builder.concat_checked(&s4));
41 check!(e == TkStrError::TooBig(65547));
42 }
43
44 #[test]
45 fn too_many_panic() {
46 let_assert!(Ok(s1) = TokenString::try_from("Hello"));
47 let_assert!(Ok(s2) = TokenString::try_from(", "));
48 let_assert!(Ok(s3) = TokenString::try_from("world"));
49 let_assert!(Ok(s4) = TokenString::try_from("!"));
50
51 let_assert!(
52 Err(panics) = std::panic::catch_unwind(|| {
53 let mut builder = Builder::<'_, 3>::new(&s1);
54 let_assert!(Ok(_) = builder.concat_checked(&s2));
55 let_assert!(Ok(_) = builder.concat_checked(&s3));
56 let _c = builder.concat_unchecked(&s4);
57 })
58 );
59 let_assert!(Some(msg) = panics.downcast_ref::<&str>());
60 check!(
61 msg == &"more strings concatenated than reserved space in Builder"
62 );
63 }
64
65 #[test]
66 fn not_zero_panic() {
67 let_assert!(Ok(s1) = TokenString::try_from("Hello"));
68
69 let_assert!(
70 Err(panics) = std::panic::catch_unwind(|| {
71 let mut _builder = Builder::<'_, 0>::new(&s1);
72 })
73 );
74 let_assert!(Some(msg) = panics.downcast_ref::<&str>());
75 check!(msg == &"the number of elements must not be 0");
76 }
77}
78
79mod func {
80 use assert2::{check, let_assert};
81 use token_string::{Builder, Collect as _, Concat as _, TokenString};
82
83 #[test]
84 fn concat_collect() {
85 let_assert!(Ok(s1) = TokenString::try_from("Hello"));
86 let_assert!(Ok(s2) = TokenString::try_from(", "));
87 let_assert!(Ok(s3) = TokenString::try_from("world"));
88 let_assert!(Ok(s4) = TokenString::try_from("!"));
89
90 let mut builder = Builder::<'_, 4>::new(&s1);
91 let builder_2 = builder.concat(&s2).concat(&s3).concat(&s4);
92 let_assert!(Ok(res) = builder_2.collect());
93 check!(&res == "Hello, world!");
94 }
95
96 #[test]
97 fn collect_unchecked_concat() {
98 let_assert!(Ok(s1) = TokenString::try_from("Hello"));
99 let_assert!(Ok(s2) = TokenString::try_from(", "));
100 let_assert!(Ok(s3) = TokenString::try_from("world"));
101 let_assert!(Ok(s4) = TokenString::try_from("!"));
102
103 let mut builder = Builder::<'_, 4>::new(&s1);
104 let builder_2 = builder
105 .concat_unchecked(&s2)
106 .concat_unchecked(&s3)
107 .concat_unchecked(&s4);
108 let_assert!(Ok(res) = builder_2.collect());
109 check!(&res == "Hello, world!");
110 }
111
112 #[test]
113 fn to_string() {
114 let_assert!(Ok(s1) = TokenString::try_from("Hello"));
115 let_assert!(Ok(s2) = TokenString::try_from(", "));
116 let_assert!(Ok(s3) = TokenString::try_from("world"));
117 let_assert!(Ok(s4) = TokenString::try_from("!"));
118
119 let mut builder = Builder::<'_, 4>::new(&s1);
120 let_assert!(
121 Ok(builder_2) = builder.concat(&s2).concat(&s3).concat(&s4)
122 );
123 check!(
124 builder_2.to_string()
125 == "Builder < 'Hello' + ', ' + 'world' + '!' >"
126 );
127 }
128}
129
130mod properties {
131 use assert2::let_assert;
132 use proptest::prelude::*;
133 use token_string::{Builder, TokenString};
134
135 fn vec_of_string(len: usize) -> impl Strategy<Value = Vec<String>> {
136 prop::collection::vec(".*", 1 .. len).prop_flat_map(Just)
137 }
138
139 proptest! {
140
141 #[test]
142 fn concat_2(a in ".*", b in ".*") {
143 let_assert!(Ok(s1) = TokenString::try_from(&a));
144 let_assert!(Ok(s2) = TokenString::try_from(&b));
145 let mut builder = Builder::<'_, 2>::new(&s1);
146 let_assert!(Ok(c) = builder.concat_checked(&s2));
147 let_assert!(Ok(res) = c.collect_checked());
148 let mut exp = a;
149 exp.push_str(&b);
150 prop_assert!(res == exp);
151 }
152
153 #[test]
154 #[cfg_attr(miri, ignore)] // takes too long for miri - 10 hours!
155 fn concat_many(v in vec_of_string(100)) {
156 let strings: Vec<TokenString> = v.iter()
157 .map(|s| {TokenString::from_str_unchecked(s)}).collect();
158
159 let mut builder = Builder::<'_, 100>::new(&strings[0]);
160 let mut exp = strings[0].as_string();
161 for s in strings.iter().skip(1) {
162 let_assert!(Ok(_c) = builder.concat_checked(s));
163 exp.push_str(s.as_str());
164 }
165 let_assert!(Ok(res) = builder.collect_checked());
166 prop_assert!(res == exp);
167 }
168
169 }
170}