• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 25357679674

04 May 2026 07:12PM UTC coverage: 89.323% (-0.05%) from 89.377%
25357679674

push

github

web-flow
Merge pull request #5567 from randombit/jack/http-crlf

Check for newline and null characters in HTTP inputs

107390 of 120226 relevant lines covered (89.32%)

11491029.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.5
/src/tests/test_strong_type.cpp
1
/*
2
 * (C) 2023,2026 Jack Lloyd
3
 *     2023 René Meusel - Rohde & Schwarz Cybersecurity
4
 *
5
 * Botan is released under the Simplified BSD License (see license.txt)
6
 */
7

8
#include "tests.h"
9

10
#include "test_rng.h"
11

12
#include <botan/hex.h>
13
#include <botan/rng.h>
14
#include <botan/secmem.h>
15
#include <botan/strong_type.h>
16
#include <algorithm>
17
#include <array>
18
#include <map>
19
#include <sstream>
20
#include <string>
21
#include <vector>
22

23
namespace Botan_Tests {
24

25
namespace {
26

27
using Test_Size = Botan::Strong<size_t, struct Test_Size_>;
28
using Test_Length = Botan::Strong<size_t, struct Test_Length_>;
29

30
std::string foo(Test_Size /*unused*/) {
1✔
31
   return "some size";
1✔
32
}
33

34
std::string foo(Test_Length /*unused*/) {
1✔
35
   return "some length";
1✔
36
}
37

38
using Test_Nonce = Botan::Strong<std::vector<uint8_t>, struct Test_Nonce_>;
39
using Test_Hash_Name = Botan::Strong<std::string, struct Test_Hash_Name_>;
40

41
using Test_Foo = Botan::Strong<std::vector<uint8_t>, struct Test_Foo_>;
42
using Test_Bar = Botan::Strong<std::vector<uint8_t>, struct Test_Bar_>;
43

44
[[maybe_unused]] uint32_t test_strong_helper(const Botan::StrongSpan<Test_Foo>& /*unused*/) {
45
   return 0;
46
}
47

48
[[maybe_unused]] uint32_t test_strong_helper(const Botan::StrongSpan<const Test_Foo>& /*unused*/) {
49
   return 1;
50
}
51

52
[[maybe_unused]] uint32_t test_strong_helper(const Botan::StrongSpan<Test_Bar>& /*unused*/) {
53
   return 2;
54
}
55

56
class Strong_Type_Tests final : public Test {
1✔
57
   public:
58
      std::vector<Test::Result> run() override {
1✔
59
         return {
1✔
60
            test_strong_type_initialization(),
61
            test_value_retrieval(),
62
            test_comparisons(),
63
            test_function_overloading(),
64
            test_is_strong_type(),
65

66
            test_container_initialization(),
67
            test_behaves_like_standard_container(),
68
            test_container_concepts(),
69
            test_binds_to_span(),
70
            test_string_container(),
71
            test_sortable(),
72
            test_random_generation(),
73
            test_subscript_accessors(),
74

75
            test_int_comparison_with_pod(),
76
            test_int_increment_decrement(),
77
            test_int_comparison_with_strong(),
78
            test_int_arithmetic_with_strong(),
79
            test_int_arithmetic_with_pod(),
80
            test_int_arithmetic_with_pod_is_strong(),
81

82
            test_strong_span(),
83

84
            test_wrapping_object_into_strong_type(),
85
            test_wrapping_strong_type_from_itself(),
86
            test_unwrapping_return_reference_type(),
87
            test_unwrapping_non_strong_type_return_type(),
88
            test_unwrapping_object_from_strong_type(),
89
            test_unwrapping_non_strong_type(),
90
         };
27✔
91
      }
1✔
92

93
   private:
94
      static Test::Result test_strong_type_initialization() {
1✔
95
         Test::Result result("strong type initialization");
1✔
96

97
         // default constructor
98
         Test_Size size1;
1✔
99

100
         // value initialization
101
         [[maybe_unused]] const Test_Size size2(42);
1✔
102

103
         // assignment operator
104
         size1 = Test_Size(42);
1✔
105

106
         return result;
1✔
107
      }
108

109
      static Test::Result test_value_retrieval() {
1✔
110
         Test::Result result("value retrieval");
1✔
111

112
         Test_Size a(42);
1✔
113
         const Test_Size b(42);
1✔
114

115
         result.test_sz_eq("get()", a.get(), 42);
1✔
116
         result.test_sz_eq("const get()", b.get(), 42);
1✔
117

118
         return result;
1✔
119
      }
×
120

121
      static Test::Result test_comparisons() {
1✔
122
         Test::Result result("comparisons");
1✔
123

124
         const Test_Size a(42);
1✔
125
         const Test_Size b(42);
1✔
126

127
         result.test_is_true("equal", a == b);
1✔
128
         result.test_is_true("lower than", a < Test_Size(1337));
1✔
129
         result.test_is_true("greater than", Test_Size(1337) > b);
1✔
130

131
         return result;
1✔
132
      }
×
133

134
      static Test::Result test_function_overloading() {
1✔
135
         Test::Result result("function overloading");
1✔
136

137
         result.test_str_eq("overloading size", foo(Test_Size(42)), "some size");
1✔
138
         result.test_str_eq("overloading size", foo(Test_Length(42)), "some length");
1✔
139

140
         return result;
1✔
141
      }
×
142

143
      static Test::Result test_is_strong_type() {
1✔
144
         Test::Result result("is_strong_type");
1✔
145

146
         result.test_is_true("strong type (int)", Botan::is_strong_type_v<Test_Size>);
1✔
147
         result.test_is_true("no strong type (int)", !Botan::is_strong_type_v<size_t>);
1✔
148
         result.test_is_true("strong type (vector)", Botan::is_strong_type_v<Test_Nonce>);
1✔
149
         result.test_is_true("no strong type (vector)", !Botan::is_strong_type_v<std::vector<uint8_t>>);
1✔
150
         result.test_is_true("strong type (const vector)", Botan::is_strong_type_v<const Test_Nonce>);
1✔
151
         result.test_is_true("no strong type (const vector)", !Botan::is_strong_type_v<const std::vector<uint8_t>>);
1✔
152

153
         return result;
1✔
154
      }
×
155

156
      static Test::Result test_container_initialization() {
1✔
157
         Test::Result result("initialization");
1✔
158

159
         [[maybe_unused]] const Test_Nonce empty_nonce;
1✔
160
         [[maybe_unused]] const Test_Nonce short_nonce(Botan::hex_decode("DEADBEEF"));
1✔
161

162
         return result;
1✔
163
      }
1✔
164

165
      static Test::Result test_behaves_like_standard_container() {
1✔
166
         Test::Result result("behaves like a standard container");
1✔
167

168
         auto base_nonce = Botan::hex_decode("DEADBEEF");
1✔
169
         auto* dataptr = base_nonce.data();
1✔
170
         auto nonce = Test_Nonce(std::move(base_nonce));
1✔
171

172
         result.test_sz_eq("size()", nonce.size(), 4);
1✔
173
         result.test_is_true("empty()", !nonce.empty());
1✔
174
         result.test_is_true("data()", nonce.data() == dataptr);
1✔
175

176
         for(const auto& c : nonce) {
5✔
177
            result.test_is_true("iteration", c > 0);
4✔
178
         }
179

180
         return result;
2✔
181
      }
1✔
182

183
      static Test::Result test_container_concepts() {
1✔
184
         Test::Result result("container concepts are satisfied");
1✔
185

186
         using Test_Map = Botan::Strong<std::map<int, std::string>, struct Test_Map_>;
1✔
187
         using Test_Array = Botan::Strong<std::array<uint64_t, 32>, struct Test_Array_>;
1✔
188

189
         result.test_is_true("Test_Nonce is container", Botan::concepts::container<Test_Nonce>);
1✔
190
         result.test_is_true("Test_Array is container", Botan::concepts::container<Test_Array>);
1✔
191
         result.test_is_true("Test_Map is container", Botan::concepts::container<Test_Map>);
1✔
192
         result.test_is_true("Test_Size is not container", !Botan::concepts::container<Test_Size>);
1✔
193

194
         result.test_is_true("Test_Nonce is contiguous_container", Botan::concepts::contiguous_container<Test_Nonce>);
1✔
195
         result.test_is_true("Test_Array is contiguous_container", Botan::concepts::contiguous_container<Test_Array>);
1✔
196
         result.test_is_true("Test_Map is not contiguous_container", !Botan::concepts::contiguous_container<Test_Map>);
1✔
197
         result.test_is_true("Test_Size is not contiguous_container",
1✔
198
                             !Botan::concepts::contiguous_container<Test_Size>);
199

200
         result.test_is_true("Test_Nonce is resizable_container", Botan::concepts::resizable_container<Test_Nonce>);
1✔
201
         result.test_is_true("Test_Array is not resizable_container",
1✔
202
                             !Botan::concepts::resizable_container<Test_Array>);
203
         result.test_is_true("Test_Map is not resizable_container", !Botan::concepts::resizable_container<Test_Map>);
1✔
204
         result.test_is_true("Test_Size is not resizable_container", !Botan::concepts::resizable_container<Test_Size>);
1✔
205

206
         return result;
1✔
207
      }
×
208

209
      static Test::Result test_binds_to_span() {
1✔
210
         Test::Result result("binds to a std::span<>");
1✔
211

212
         auto get_size = [](std::span<const uint8_t> data) { return data.size(); };
2✔
213

214
         const auto nonce = Test_Nonce(Botan::hex_decode("DEADBEEF"));
1✔
215

216
         result.test_sz_eq("can bind to std::span<>", get_size(nonce), nonce.size());
1✔
217

218
         return result;
1✔
219
      }
2✔
220

221
      static Test::Result test_string_container() {
1✔
222
         Test::Result result("std::string container");
1✔
223

224
         const Test_Hash_Name name("SHA-1");
1✔
225

226
         std::stringstream stream;
1✔
227
         stream << name;
1✔
228
         result.test_str_eq("strong types are streamable", stream.str(), std::string("SHA-1"));
1✔
229

230
         return result;
2✔
231
      }
2✔
232

233
      static Test::Result test_sortable() {
1✔
234
         Test::Result result("strong types are sortable");
1✔
235

236
         using Test_Length_List = Botan::Strong<std::vector<Test_Length>, struct Test_Length_List_>;
1✔
237

238
         Test_Length_List hashes({Test_Length(3), Test_Length(1), Test_Length(4), Test_Length(2)});
1✔
239

240
         std::ranges::sort(hashes);
1✔
241

242
         result.test_sz_eq("1", hashes.get().at(0).get(), size_t(1));
1✔
243
         result.test_sz_eq("2", hashes.get().at(1).get(), size_t(2));
1✔
244
         result.test_sz_eq("3", hashes.get().at(2).get(), size_t(3));
1✔
245
         result.test_sz_eq("4", hashes.get().at(3).get(), size_t(4));
1✔
246

247
         return result;
1✔
248
      }
1✔
249

250
      static Test::Result test_random_generation() {
1✔
251
         Test::Result result("byte-container strong types can be randomly generated");
1✔
252

253
         using Test_Buffer = Botan::Strong<std::vector<uint8_t>, struct Test_Buffer_>;
1✔
254
         using Test_Secure_Buffer = Botan::Strong<Botan::secure_vector<uint8_t>, struct Test_Secure_Buffer_>;
1✔
255
         using Test_Fixed_Array = Botan::Strong<std::array<uint8_t, 4>, struct Test_Fixed_Array_>;
1✔
256

257
         Fixed_Output_RNG rng;
1✔
258
         const auto e1 = Botan::hex_decode("deadbeef");
1✔
259
         const auto e2 = Botan::hex_decode("baadcafe");
1✔
260
         const auto e3 = Botan::hex_decode("baadf00d");
1✔
261
         rng.add_entropy(e1.data(), e1.size());
1✔
262
         rng.add_entropy(e2.data(), e2.size());
1✔
263
         rng.add_entropy(e3.data(), e3.size());
1✔
264

265
         auto tb = rng.random_vec<Test_Buffer>(4);
1✔
266
         auto tsb = rng.random_vec<Test_Secure_Buffer>(4);
1✔
267
         Test_Fixed_Array tfa;
1✔
268
         rng.random_vec(tfa);
1✔
269

270
         result.test_bin_eq("generated expected output", tb.get(), "deadbeef");
1✔
271
         result.test_bin_eq("generated expected secure output", tsb.get(), "baadcafe");
1✔
272
         result.test_bin_eq("generated expected fixed output", std::vector(tfa.begin(), tfa.end()), "baadf00d");
1✔
273

274
         return result;
1✔
275
      }
1✔
276

277
      static Test::Result test_subscript_accessors() {
1✔
278
         Test::Result result("subscript accessors are exposed");
1✔
279

280
         using Test_Array = Botan::Strong<std::array<uint8_t, 4>, struct Test_Array_>;
1✔
281
         using Test_Map = Botan::Strong<std::map<int, std::string>, struct Test_Map_>;
1✔
282
         using Test_Vector = Botan::Strong<std::vector<uint8_t>, struct Test_Vector_>;
1✔
283

284
         Test_Array a({1, 2, 3, 4});
1✔
285
         result.test_u8_eq("[] returns 3", a[2], 3);
1✔
286

287
         Test_Map m({{1, "one"}, {2, "two"}, {3, "three"}});
5✔
288
         result.test_str_eq("[] returns 'two'", m[2], "two");
1✔
289

290
         Test_Vector v({1, 2, 3, 4});
1✔
291
         result.test_u8_eq("[] returns 2", v[1], 2);
1✔
292

293
         return result;
1✔
294
      }
2✔
295

296
      static Test::Result test_int_comparison_with_pod() {
1✔
297
         Test::Result result("comparison operators with POD are always allowed");
1✔
298

299
         using StrongInt = Botan::Strong<int, struct StrongInt_>;
1✔
300
         const StrongInt i(42);
1✔
301

302
         result.test_is_true("i ==", i == 42);
1✔
303
         result.test_is_true("i !=", i != 0);
1✔
304
         result.test_is_true("i >", i > 41);
1✔
305
         result.test_is_true("i >= 1", i >= 41);
1✔
306
         result.test_is_true("i >= 2", i >= 42);
1✔
307
         result.test_is_true("i <", i < 43);
1✔
308
         result.test_is_true("i <= 1", i <= 43);
1✔
309
         result.test_is_true("i <= 2", i <= 42);
1✔
310

311
         result.test_is_true("== i", 42 == i);
1✔
312
         result.test_is_true("!= i", 0 != i);
1✔
313
         result.test_is_true("> i", 43 > i);
1✔
314
         result.test_is_true(">= 1 i", 43 >= i);
1✔
315
         result.test_is_true(">= 2 i", 42 >= i);
1✔
316
         result.test_is_true("< i", 41 < i);
1✔
317
         result.test_is_true("<= 1 i", 41 <= i);
1✔
318
         result.test_is_true("<= 2 i", 42 <= i);
1✔
319

320
         return result;
1✔
321
      }
×
322

323
      static Test::Result test_int_increment_decrement() {
1✔
324
         Test::Result result("increment/decrement are always allowed");
1✔
325

326
         using StrongInt = Botan::Strong<int, struct StrongInt_>;
1✔
327
         StrongInt i(42);
1✔
328

329
         result.test_is_true("i++", i++ == 42);
1✔
330
         result.test_is_true("i post-incremented", i == 43);
1✔
331
         result.test_is_true("++i", ++i == 44);
1✔
332
         result.test_is_true("i pre-incremented", i == 44);
1✔
333

334
         result.test_is_true("i--", i-- == 44);
1✔
335
         result.test_is_true("i post-decremented", i == 43);
1✔
336
         result.test_is_true("--i", --i == 42);
1✔
337
         result.test_is_true("i pre-decremented", i == 42);
1✔
338

339
         return result;
1✔
340
      }
×
341

342
      static Test::Result test_int_comparison_with_strong() {
1✔
343
         Test::Result result("comparison operators with Strong<>");
1✔
344

345
         using StrongInt = Botan::Strong<int, struct StrongInt_>;
1✔
346
         const StrongInt i(42);
1✔
347
         const StrongInt i42(42);
1✔
348
         const StrongInt i41(41);
1✔
349
         const StrongInt i43(43);
1✔
350
         const StrongInt i0(0);
1✔
351

352
         result.test_is_true("==", i == i42);
1✔
353
         result.test_is_true("!=", i != i0);
1✔
354
         result.test_is_true(">", i > i41);
1✔
355
         result.test_is_true(">= 1", i >= i41);
1✔
356
         result.test_is_true(">= 2", i >= i42);
1✔
357
         result.test_is_true("<", i < i43);
1✔
358
         result.test_is_true("<= 1", i <= i43);
1✔
359
         result.test_is_true("<= 2", i <= i42);
1✔
360

361
         return result;
1✔
362
      }
×
363

364
      static Test::Result test_int_arithmetic_with_strong() {
1✔
365
         Test::Result result("arithmetics with Strong<>");
1✔
366

367
         using StrongInt = Botan::Strong<int, struct StrongInt_>;
1✔
368
         StrongInt i(42);
1✔
369
         const StrongInt i2(2);
1✔
370
         const StrongInt i4(4);
1✔
371
         const StrongInt i12(12);
1✔
372

373
         result.test_is_true("+", i + i == 84);
1✔
374
         result.test_is_true("-", i - i == 0);
1✔
375
         result.test_is_true("*", i * i == 1764);
1✔
376
         result.test_is_true("/", i / i == 1);
1✔
377
         result.test_is_true("^", (i ^ i) == 0);
1✔
378
         result.test_is_true("&", (i & i) == 42);
1✔
379
         result.test_is_true("|", (i | i) == 42);
1✔
380
         result.test_is_true(">>", (i >> i2) == 10);
1✔
381
         result.test_is_true("<<", (i << i2) == 168);
1✔
382

383
         result.test_is_true("+=", (i += i2) == 44);
1✔
384
         result.test_is_true("-=", (i -= i2) == 42);
1✔
385
         result.test_is_true("*=", (i *= i2) == 84);
1✔
386
         result.test_is_true("/=", (i /= i2) == 42);
1✔
387
         result.test_is_true("^=", (i ^= i2) == 40);
1✔
388
         result.test_is_true("&=", (i &= i12) == 8);
1✔
389
         result.test_is_true("|=", (i |= i2) == 10);
1✔
390
         result.test_is_true("<<=", (i <<= i2) == 40);
1✔
391
         result.test_is_true(">>=", (i >>= i4) == 2);
1✔
392

393
         return result;
1✔
394
      }
×
395

396
      static Test::Result test_int_arithmetic_with_pod() {
1✔
397
         Test::Result result("arithmetics with POD");
1✔
398

399
         using StrongIntWithPodArithmetics =
1✔
400
            Botan::Strong<int, struct StrongInt_, Botan::EnableArithmeticWithPlainNumber>;
401
         StrongIntWithPodArithmetics i(42);
1✔
402
         const StrongIntWithPodArithmetics i2(2);
1✔
403

404
         result.test_is_true("i +", i + 1 == 43);
1✔
405
         result.test_is_true("i -", i - 1 == 41);
1✔
406
         result.test_is_true("i *", i * 2 == 84);
1✔
407
         result.test_is_true("i /", i / 2 == 21);
1✔
408
         result.test_is_true("i ^", (i ^ 10) == 32);
1✔
409
         result.test_is_true("i &", (i & 15) == 10);
1✔
410
         result.test_is_true("i |", (i | 4) == 46);
1✔
411
         result.test_is_true("i >>", (i >> 2) == 10);
1✔
412
         result.test_is_true("i <<", (i << 2) == 168);
1✔
413

414
         result.test_is_true("+ i", 1 + i == 43);
1✔
415
         result.test_is_true("- i", 1 - i == -41);
1✔
416
         result.test_is_true("* i", 2 * i == 84);
1✔
417
         result.test_is_true("/ i", 84 / i == 2);
1✔
418
         result.test_is_true("^ i", (10 ^ i) == 32);
1✔
419
         result.test_is_true("& i", (15 & i) == 10);
1✔
420
         result.test_is_true("| i", (4 | i) == 46);
1✔
421
         result.test_is_true(">> i", (4 >> i2) == 1);
1✔
422
         result.test_is_true("<< i", (2 << i2) == 8);
1✔
423

424
         result.test_is_true("i +=", (i += 2) == 44);
1✔
425
         result.test_is_true("i -=", (i -= 2) == 42);
1✔
426
         result.test_is_true("i *=", (i *= 2) == 84);
1✔
427
         result.test_is_true("i /=", (i /= 2) == 42);
1✔
428
         result.test_is_true("i ^=", (i ^= 2) == 40);
1✔
429
         result.test_is_true("i &=", (i &= 12) == 8);
1✔
430
         result.test_is_true("i |=", (i |= 2) == 10);
1✔
431
         result.test_is_true("i <<=", (i <<= 2) == 40);
1✔
432
         result.test_is_true("i >>=", (i >>= 4) == 2);
1✔
433

434
         return result;
1✔
435
      }
×
436

437
      static Test::Result test_int_arithmetic_with_pod_is_strong() {
1✔
438
         Test::Result result("arithmetics with POD is still Strong<>");
1✔
439

440
         using StrongIntWithPodArithmetics =
1✔
441
            Botan::Strong<int, struct StrongInt_, Botan::EnableArithmeticWithPlainNumber>;
442
         StrongIntWithPodArithmetics i(42);  // NOLINT(*-const-correctness) clang-tidy bug
1✔
443
         const StrongIntWithPodArithmetics i2(2);
1✔
444

445
         result.test_is_true("i +", Botan::is_strong_type_v<decltype(i + 1)>);
1✔
446
         result.test_is_true("i -", Botan::is_strong_type_v<decltype(i - 1)>);
1✔
447
         result.test_is_true("i *", Botan::is_strong_type_v<decltype(i * 2)>);
1✔
448
         result.test_is_true("i /", Botan::is_strong_type_v<decltype(i / 2)>);
1✔
449
         result.test_is_true("i ^", Botan::is_strong_type_v<decltype((i ^ 10))>);
1✔
450
         result.test_is_true("i &", Botan::is_strong_type_v<decltype((i & 15))>);
1✔
451
         result.test_is_true("i |", Botan::is_strong_type_v<decltype((i | 4))>);
1✔
452
         result.test_is_true("i >>", Botan::is_strong_type_v<decltype((i >> 2))>);
1✔
453
         result.test_is_true("i <<", Botan::is_strong_type_v<decltype((i << 2))>);
1✔
454

455
         result.test_is_true("+ i", Botan::is_strong_type_v<decltype(1 + i)>);
1✔
456
         result.test_is_true("- i", Botan::is_strong_type_v<decltype(1 - i)>);
1✔
457
         result.test_is_true("* i", Botan::is_strong_type_v<decltype(2 * i)>);
1✔
458
         result.test_is_true("/ i", Botan::is_strong_type_v<decltype(84 / i)>);
1✔
459
         result.test_is_true("^ i", Botan::is_strong_type_v<decltype((10 ^ i))>);
1✔
460
         result.test_is_true("& i", Botan::is_strong_type_v<decltype((15 & i))>);
1✔
461
         result.test_is_true("| i", Botan::is_strong_type_v<decltype((4 | i))>);
1✔
462
         result.test_is_true(">> i", Botan::is_strong_type_v<decltype((4 >> i2))>);
1✔
463
         result.test_is_true("<< i", Botan::is_strong_type_v<decltype((2 << i2))>);
1✔
464

465
         result.test_is_true("i +=", Botan::is_strong_type_v<decltype(i += 2)>);
1✔
466
         result.test_is_true("i -=", Botan::is_strong_type_v<decltype(i -= 2)>);
1✔
467
         result.test_is_true("i *=", Botan::is_strong_type_v<decltype(i *= 2)>);
1✔
468
         result.test_is_true("i /=", Botan::is_strong_type_v<decltype(i /= 2)>);
1✔
469
         result.test_is_true("i ^=", Botan::is_strong_type_v<decltype(i ^= 2)>);
1✔
470
         result.test_is_true("i &=", Botan::is_strong_type_v<decltype(i &= 12)>);
1✔
471
         result.test_is_true("i |=", Botan::is_strong_type_v<decltype(i |= 2)>);
1✔
472
         result.test_is_true("i <<=", Botan::is_strong_type_v<decltype(i <<= 2)>);
1✔
473
         result.test_is_true("i >>=", Botan::is_strong_type_v<decltype(i >>= 4)>);
1✔
474

475
         return result;
1✔
476
      }
×
477

478
      static Test::Result test_strong_span() {
1✔
479
         Test::Result result("StrongSpan<>");
1✔
480

481
         const Test_Foo foo(Botan::hex_decode("DEADBEEF"));
1✔
482
         result.test_u32_eq("binds to StrongSpan<const Test_Foo>", test_strong_helper(foo), 1);
1✔
483

484
         Test_Bar bar(Botan::hex_decode("CAFECAFE"));
1✔
485
         result.test_u32_eq("binds to StrongSpan<Test_Bar>", test_strong_helper(bar), 2);
1✔
486

487
         const Botan::StrongSpan<const Test_Foo> span(foo);
1✔
488

489
         result.test_is_true("underlying type is uint8_t", std::is_same_v<decltype(span)::value_type, uint8_t>);
1✔
490
         result.test_is_true("strong type is a contiguous buffer",
1✔
491
                             Botan::concepts::contiguous_container<decltype(foo)>);
492
         result.test_is_true("strong type is a contiguous strong type buffer",
1✔
493
                             Botan::concepts::contiguous_strong_type<decltype(foo)>);
494
         result.test_is_true("strong span is not a contiguous buffer",
1✔
495
                             !Botan::concepts::contiguous_container<decltype(span)>);
496
         result.test_is_true("strong span is not a contiguous strong type buffer",
1✔
497
                             !Botan::concepts::contiguous_strong_type<decltype(span)>);
498

499
         return result;
1✔
500
      }
1✔
501

502
      static Test::Result test_wrapping_object_into_strong_type() {
1✔
503
         Test::Result result("generically wrapping an object into a strong type");
1✔
504

505
         using Strong_String = Botan::Strong<std::string, struct Strong_String_>;
1✔
506
         using Strong_Unique = Botan::Strong<std::unique_ptr<std::string>, struct Strong_Unique_>;
1✔
507
         using namespace std::string_literals;
1✔
508

509
         const std::string expl("explicit creation"s);
1✔
510
         std::string rval("rvalue-ref creation"s);
1✔
511
         auto stt_copy = Botan::wrap_strong_type<Strong_String>(expl);
1✔
512
         auto stt_implicit = Botan::wrap_strong_type<Strong_String>("implicit conversion from const char*");
1✔
513
         auto stt_rvalue_ref = Botan::wrap_strong_type<Strong_String>(std::move(rval));
1✔
514
         auto stt_rvalue = Botan::wrap_strong_type<Strong_String>("rvalue creation from std::string (literal)"s);
1✔
515

516
         result.test_str_eq("stt_copy", stt_copy.get(), "explicit creation");
1✔
517
         result.test_str_eq("stt_implicit", stt_implicit.get(), "implicit conversion from const char*");
1✔
518
         result.test_str_eq("stt_rvalue_ref", stt_rvalue_ref.get(), "rvalue-ref creation");
1✔
519
         result.test_str_eq("stt_rvalue", stt_rvalue.get(), "rvalue creation from std::string (literal)");
1✔
520

521
         // unique_ptr does not support copy construction and prohibits
522
         // implicit conversion from a raw pointer of its wrapped type.
523
         auto rval_ptr = std::make_unique<std::string>("rvalue creation from ptr");
1✔
524
         auto stt_implicit_ptr =
1✔
525
            // NOLINTNEXTLINE(*-owning-memory)
526
            Botan::wrap_strong_type<Strong_Unique>(new std::string("implicit creation from ptr"));
1✔
527
         auto stt_rvalue_ptr = Botan::wrap_strong_type<Strong_Unique>(std::move(rval_ptr));
1✔
528

529
         result.test_str_eq("stt_implicit_ptr", *stt_implicit_ptr.get(), "implicit creation from ptr");
1✔
530
         result.test_str_eq("stt_rvalue_ptr", *stt_rvalue_ptr.get(), "rvalue creation from ptr");
1✔
531

532
         return result;
1✔
533
      }
1✔
534

535
      static Test::Result test_wrapping_strong_type_from_itself() {
1✔
536
         Test::Result result("generically wrapping a strong type from itself");
1✔
537

538
         using Strong_String = Botan::Strong<std::string, struct Strong_String_>;
1✔
539
         using Strong_Unique = Botan::Strong<std::unique_ptr<std::string>, struct Strong_Unique_>;
1✔
540

541
         const Strong_String stt("wrapped");
1✔
542
         Strong_String stt_rval("wrapped and moved");
1✔
543

544
         auto stt_copy = Botan::wrap_strong_type<Strong_String>(stt);
1✔
545
         auto stt_move = Botan::wrap_strong_type<Strong_String>(std::move(stt_rval));
1✔
546
         auto stt_inplace = Botan::wrap_strong_type<Strong_String>(Strong_String("inplace"));
1✔
547

548
         result.test_str_eq("stt_copy", stt_copy.get(), "wrapped");
1✔
549
         result.test_str_eq("stt_move", stt_move.get(), "wrapped and moved");
1✔
550
         result.test_str_eq("stt_inplace", stt_inplace.get(), "inplace");
1✔
551

552
         Strong_Unique stt_ptr(std::make_unique<std::string>("wrapped ptr"));
1✔
553

554
         auto stt_ptr_move = Botan::wrap_strong_type<Strong_Unique>(std::move(stt_ptr));
1✔
555

556
         result.test_str_eq("stt_ptr_move", *stt_ptr_move.get(), "wrapped ptr");
1✔
557

558
         return result;
1✔
559
      }
1✔
560

561
      static Test::Result test_unwrapping_return_reference_type() {
1✔
562
         Test::Result result("unwrapping a strong type wisely chooses return reference type");
1✔
563

564
         using Strong_String = Botan::Strong<std::string, struct Strong_String_>;
1✔
565

566
         Strong_String stt("wrapped");  // NOLINT(*-const-correctness) clang-tidy bug
1✔
567
         const Strong_String stt_const("const wrapped");
1✔
568

569
         using lvalue_ref = decltype(Botan::unwrap_strong_type(stt));
1✔
570
         result.test_is_true("unpack() on non-const is an lvalue reference", std::is_lvalue_reference_v<lvalue_ref>);
1✔
571
         result.test_is_true("unpack() on non-const is a non-const lvalue reference",
1✔
572
                             !std::is_const_v<std::remove_reference_t<lvalue_ref>>);
573
         result.test_is_true(
1✔
574
            "wrapped_type on non-const lvalue strong type",
575
            std::same_as<Botan::strong_type_wrapped_type<decltype(stt)>, std::remove_cvref_t<lvalue_ref>>);
576

577
         using const_lvalue_ref = decltype(Botan::unwrap_strong_type(stt_const));
1✔
578
         result.test_is_true("unpack() on const is an lvalue reference", std::is_lvalue_reference_v<const_lvalue_ref>);
1✔
579
         result.test_is_true("unpack() on const is a const lvalue reference",
1✔
580
                             std::is_const_v<std::remove_reference_t<const_lvalue_ref>>);
581
         result.test_is_true(
1✔
582
            "wrapped_type on const lvalue strong type",
583
            std::same_as<Botan::strong_type_wrapped_type<decltype(stt_const)>, std::remove_cvref_t<lvalue_ref>>);
584

585
         using lvalue = decltype(Botan::unwrap_strong_type(std::move(stt)));
1✔
586
         result.test_is_true("unpack() on rvalue reference is an rvalue reference", std::is_rvalue_reference_v<lvalue>);
1✔
587
         result.test_is_true("unpack() on rvalue is a non-const rvalue reference",
1✔
588
                             !std::is_const_v<std::remove_reference_t<lvalue>>);
589
         result.test_is_true(
1✔
590
            "wrapped_type on rvalue reference strong type",
591
            std::same_as<Botan::strong_type_wrapped_type<decltype(std::move(stt))>, std::remove_cvref_t<lvalue>>);
592

593
         using lvalue2 = decltype(Botan::unwrap_strong_type(Strong_String("wrapped rvalue")));
1✔
594
         result.test_is_true("unpack() on rvalue is an rvalue reference", std::is_rvalue_reference_v<lvalue2>);
1✔
595
         result.test_is_true("unpack() on rvalue is a non-const rvalue reference",
1✔
596
                             !std::is_const_v<std::remove_reference_t<lvalue2>>);
597
         result.test_is_true("wrapped_type on rvalue strong type",
1✔
598
                             std::same_as<Botan::strong_type_wrapped_type<decltype(Strong_String("wrapped rvalue"))>,
599
                                          std::remove_cvref_t<lvalue2>>);
600

601
         return result;
1✔
602
      }
1✔
603

604
      static Test::Result test_unwrapping_non_strong_type_return_type() {
1✔
605
         Test::Result result("unwrapping a non-strong type does not alter return reference type");
1✔
606

607
         std::string stt("wrapped");  // NOLINT(*-const-correctness) required for the test
1✔
608
         const std::string stt_const("const wrapped");
1✔
609

610
         using lvalue_ref = decltype(Botan::unwrap_strong_type(stt));
1✔
611
         result.test_is_true("unpack() on non-const is an lvalue reference", std::is_lvalue_reference_v<lvalue_ref>);
1✔
612
         result.test_is_true("unpack() on non-const is a non-const lvalue reference",
1✔
613
                             !std::is_const_v<std::remove_reference_t<lvalue_ref>>);
614
         result.test_is_true(
1✔
615
            "wrapped_type on lvalue non-strong type",
616
            std::same_as<Botan::strong_type_wrapped_type<decltype(stt)>, std::remove_cvref_t<lvalue_ref>>);
617

618
         using const_lvalue_ref = decltype(Botan::unwrap_strong_type(stt_const));
1✔
619
         result.test_is_true("unpack() on const is an lvalue reference", std::is_lvalue_reference_v<const_lvalue_ref>);
1✔
620
         result.test_is_true("unpack() on const is a const lvalue reference",
1✔
621
                             std::is_const_v<std::remove_reference_t<const_lvalue_ref>>);
622
         result.test_is_true(
1✔
623
            "wrapped_type on const lvalue non-strong type",
624
            std::same_as<Botan::strong_type_wrapped_type<decltype(stt_const)>, std::remove_cvref_t<const_lvalue_ref>>);
625

626
         using lvalue = decltype(Botan::unwrap_strong_type(std::move(stt)));
1✔
627
         result.test_is_true("unpack() on rvalue reference is an rvalue reference", std::is_rvalue_reference_v<lvalue>);
1✔
628
         result.test_is_true("unpack() on rvalue is a non-const rvalue reference",
1✔
629
                             !std::is_const_v<std::remove_reference_t<lvalue>>);
630
         result.test_is_true(
1✔
631
            "wrapped_type on rvalue reference non-strong type",
632
            std::same_as<Botan::strong_type_wrapped_type<decltype(std::move(stt))>, std::remove_cvref_t<lvalue>>);
633

634
         using lvalue2 = decltype(Botan::unwrap_strong_type(std::string("rvalue")));
1✔
635
         result.test_is_true("unpack() on rvalue reference is an rvalue reference",
1✔
636
                             std::is_rvalue_reference_v<lvalue2>);
637
         result.test_is_true("unpack() on rvalue is a non-const rvalue reference",
1✔
638
                             !std::is_const_v<std::remove_reference_t<lvalue2>>);
639
         result.test_is_true("wrapped_type on rvalue non-strong type",
1✔
640
                             std::same_as<Botan::strong_type_wrapped_type<decltype(std::string("rvalue"))>,
641
                                          std::remove_cvref_t<lvalue2>>);
642

643
         return result;
1✔
644
      }
1✔
645

646
      static Test::Result test_unwrapping_object_from_strong_type() {
1✔
647
         Test::Result result("generically unwrapping an object from a strong type");
1✔
648

649
         using Strong_String = Botan::Strong<std::string, struct Strong_String_>;
1✔
650
         using Strong_Unique = Botan::Strong<std::unique_ptr<std::string>, struct Strong_Unique_>;
1✔
651

652
         Strong_String stt("wrapped lvalue");
1✔
653
         Strong_String stt_move("wrapped lvalue to be moved");
1✔
654
         const Strong_String const_stt("wrapped const lvalue");
1✔
655

656
         auto& unwrapped_stt = Botan::unwrap_strong_type(stt);
1✔
657
         const auto& unwrapped_const_stt = Botan::unwrap_strong_type(const_stt);
1✔
658
         auto unwrapped_rvalue = Botan::unwrap_strong_type(std::move(stt_move));
1✔
659
         auto unwrapped_rvalue2 = Botan::unwrap_strong_type(Strong_String("wrapped rvalue"));
1✔
660

661
         result.test_str_eq("unwrapped_stt", unwrapped_stt, "wrapped lvalue");
1✔
662
         result.test_str_eq("unwrapped_const_stt", unwrapped_const_stt, "wrapped const lvalue");
1✔
663
         result.test_str_eq("unwrapped_rvalue", unwrapped_rvalue, "wrapped lvalue to be moved");
1✔
664
         result.test_str_eq("unwrapped_rvalue2", unwrapped_rvalue2, "wrapped rvalue");
1✔
665

666
         Strong_Unique stt_ptr(std::make_unique<std::string>("wrapped ptr"));
1✔
667
         Strong_Unique stt_ptr_move(std::make_unique<std::string>("wrapped ptr to be moved"));
1✔
668

669
         auto& unwrapped_ptr = Botan::unwrap_strong_type(stt_ptr);
1✔
670
         auto unwrapped_ptr_move = Botan::unwrap_strong_type(std::move(stt_ptr_move));
1✔
671
         auto unwrapped_ptr_rvalue = Botan::unwrap_strong_type(std::make_unique<std::string>("wrapped ptr rvalue"));
1✔
672

673
         result.test_str_eq("unwrapped_ptr", *unwrapped_ptr, "wrapped ptr");
1✔
674
         result.test_str_eq("unwrapped_ptr_move", *unwrapped_ptr_move, "wrapped ptr to be moved");
1✔
675
         result.test_str_eq("unwrapped_ptr_rvalue", *unwrapped_ptr_rvalue, "wrapped ptr rvalue");
1✔
676

677
         return result;
2✔
678
      }
1✔
679

680
      static Test::Result test_unwrapping_non_strong_type() {
1✔
681
         Test::Result result("generically unwrapping an object that isn't a strong type");
1✔
682

683
         std::string stt("wrapped lvalue");
1✔
684
         std::string stt_move("wrapped lvalue to be moved");
1✔
685
         const std::string const_stt("wrapped const lvalue");
1✔
686

687
         auto& unwrapped_stt = Botan::unwrap_strong_type(stt);
1✔
688
         const auto& unwrapped_const_stt = Botan::unwrap_strong_type(const_stt);
1✔
689
         auto unwrapped_rvalue = Botan::unwrap_strong_type(std::move(stt_move));
1✔
690
         auto unwrapped_rvalue2 = Botan::unwrap_strong_type(std::string("wrapped rvalue"));
1✔
691

692
         result.test_str_eq("unwrapped_stt", unwrapped_stt, "wrapped lvalue");
1✔
693
         result.test_str_eq("unwrapped_const_stt", unwrapped_const_stt, "wrapped const lvalue");
1✔
694
         result.test_str_eq("unwrapped_rvalue", unwrapped_rvalue, "wrapped lvalue to be moved");
1✔
695
         result.test_str_eq("unwrapped_rvalue2", unwrapped_rvalue2, "wrapped rvalue");
1✔
696

697
         std::unique_ptr<std::string> stt_ptr(std::make_unique<std::string>("wrapped ptr"));
1✔
698
         std::unique_ptr<std::string> stt_ptr_move(std::make_unique<std::string>("wrapped ptr to be moved"));
1✔
699

700
         auto& unwrapped_ptr = Botan::unwrap_strong_type(stt_ptr);
1✔
701
         auto unwrapped_ptr_move = Botan::unwrap_strong_type(std::move(stt_ptr_move));
1✔
702
         auto unwrapped_ptr_rvalue = Botan::unwrap_strong_type(std::make_unique<std::string>("wrapped ptr rvalue"));
1✔
703

704
         result.test_str_eq("unwrapped_ptr", *unwrapped_ptr, "wrapped ptr");
1✔
705
         result.test_str_eq("unwrapped_ptr_move", *unwrapped_ptr_move, "wrapped ptr to be moved");
1✔
706
         result.test_str_eq("unwrapped_ptr_rvalue", *unwrapped_ptr_rvalue, "wrapped ptr rvalue");
1✔
707

708
         return result;
2✔
709
      }
1✔
710
};
711

712
BOTAN_REGISTER_TEST("utils", "strong_type", Strong_Type_Tests);
713

714
}  // namespace
715

716
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc