]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #include "include/buffer.h" |
2 | #include "include/encoding.h" | |
3 | ||
4 | #include "gtest/gtest.h" | |
5 | ||
6 | using namespace std; | |
7 | ||
8 | template < typename T > | |
9 | static void test_encode_and_decode(const T& src) | |
10 | { | |
11 | bufferlist bl(1000000); | |
12 | encode(src, bl); | |
13 | T dst; | |
11fdf7f2 | 14 | auto i = bl.cbegin(); |
7c673cae FG |
15 | decode(dst, i); |
16 | ASSERT_EQ(src, dst) << "Encoding roundtrip changed the string: orig=" << src << ", but new=" << dst; | |
17 | } | |
18 | ||
19 | TEST(EncodingRoundTrip, StringSimple) { | |
20 | string my_str("I am the very model of a modern major general"); | |
21 | test_encode_and_decode < std::string >(my_str); | |
22 | } | |
23 | ||
24 | TEST(EncodingRoundTrip, StringEmpty) { | |
25 | string my_str(""); | |
26 | test_encode_and_decode < std::string >(my_str); | |
27 | } | |
28 | ||
29 | TEST(EncodingRoundTrip, StringNewline) { | |
30 | string my_str("foo bar baz\n"); | |
31 | test_encode_and_decode < std::string >(my_str); | |
32 | } | |
33 | ||
34 | template <typename Size, typename T> | |
35 | static void test_encode_and_nohead_nohead(Size len, const T& src) | |
36 | { | |
37 | bufferlist bl(1000000); | |
38 | encode(len, bl); | |
39 | encode_nohead(src, bl); | |
40 | T dst; | |
11fdf7f2 | 41 | auto i = bl.cbegin(); |
7c673cae FG |
42 | decode(len, i); |
43 | decode_nohead(len, dst, i); | |
44 | ASSERT_EQ(src, dst) << "Encoding roundtrip changed the string: orig=" << src << ", but new=" << dst; | |
45 | } | |
46 | ||
47 | TEST(EncodingRoundTrip, StringNoHead) { | |
48 | const string str("The quick brown fox jumps over the lazy dog"); | |
49 | auto size = str.size(); | |
50 | test_encode_and_nohead_nohead(static_cast<int>(size), str); | |
51 | test_encode_and_nohead_nohead(static_cast<unsigned>(size), str); | |
52 | test_encode_and_nohead_nohead(static_cast<uint32_t>(size), str); | |
53 | test_encode_and_nohead_nohead(static_cast<__u32>(size), str); | |
54 | test_encode_and_nohead_nohead(static_cast<size_t>(size), str); | |
55 | } | |
56 | ||
57 | TEST(EncodingRoundTrip, BufferListNoHead) { | |
58 | bufferlist bl; | |
59 | bl.append("is this a dagger which i see before me?"); | |
60 | auto size = bl.length(); | |
61 | test_encode_and_nohead_nohead(static_cast<int>(size), bl); | |
62 | test_encode_and_nohead_nohead(static_cast<unsigned>(size), bl); | |
63 | test_encode_and_nohead_nohead(static_cast<uint32_t>(size), bl); | |
64 | test_encode_and_nohead_nohead(static_cast<__u32>(size), bl); | |
65 | test_encode_and_nohead_nohead(static_cast<size_t>(size), bl); | |
66 | } | |
67 | ||
68 | typedef std::multimap < int, std::string > multimap_t; | |
69 | typedef multimap_t::value_type my_val_ty; | |
70 | ||
71 | namespace std { | |
72 | static std::ostream& operator<<(std::ostream& oss, const multimap_t &multimap) | |
73 | { | |
74 | for (multimap_t::const_iterator m = multimap.begin(); | |
75 | m != multimap.end(); | |
76 | ++m) | |
77 | { | |
78 | oss << m->first << "->" << m->second << " "; | |
79 | } | |
80 | return oss; | |
81 | } | |
82 | } | |
83 | ||
84 | TEST(EncodingRoundTrip, Multimap) { | |
85 | multimap_t multimap; | |
86 | multimap.insert( my_val_ty(1, "foo") ); | |
87 | multimap.insert( my_val_ty(2, "bar") ); | |
88 | multimap.insert( my_val_ty(2, "baz") ); | |
89 | multimap.insert( my_val_ty(3, "lucky number 3") ); | |
90 | multimap.insert( my_val_ty(10000, "large number") ); | |
91 | ||
92 | test_encode_and_decode < multimap_t >(multimap); | |
93 | } | |
94 | ||
95 | ||
96 | ||
97 | /////////////////////////////////////////////////////// | |
98 | // ConstructorCounter | |
99 | /////////////////////////////////////////////////////// | |
100 | template <typename T> | |
101 | class ConstructorCounter | |
102 | { | |
103 | public: | |
104 | ConstructorCounter() : data(0) | |
105 | { | |
106 | default_ctor++; | |
107 | } | |
108 | ||
109 | explicit ConstructorCounter(const T& data_) | |
110 | : data(data_) | |
111 | { | |
112 | one_arg_ctor++; | |
113 | } | |
114 | ||
115 | ConstructorCounter(const ConstructorCounter &rhs) | |
116 | : data(rhs.data) | |
117 | { | |
118 | copy_ctor++; | |
119 | } | |
120 | ||
121 | ConstructorCounter &operator=(const ConstructorCounter &rhs) | |
122 | { | |
123 | data = rhs.data; | |
124 | assigns++; | |
125 | return *this; | |
126 | } | |
127 | ||
128 | static void init(void) | |
129 | { | |
130 | default_ctor = 0; | |
131 | one_arg_ctor = 0; | |
132 | copy_ctor = 0; | |
133 | assigns = 0; | |
134 | } | |
135 | ||
136 | static int get_default_ctor(void) | |
137 | { | |
138 | return default_ctor; | |
139 | } | |
140 | ||
141 | static int get_one_arg_ctor(void) | |
142 | { | |
143 | return one_arg_ctor; | |
144 | } | |
145 | ||
146 | static int get_copy_ctor(void) | |
147 | { | |
148 | return copy_ctor; | |
149 | } | |
150 | ||
151 | static int get_assigns(void) | |
152 | { | |
153 | return assigns; | |
154 | } | |
155 | ||
156 | bool operator<(const ConstructorCounter &rhs) const | |
157 | { | |
158 | return data < rhs.data; | |
159 | } | |
160 | ||
161 | bool operator==(const ConstructorCounter &rhs) const | |
162 | { | |
163 | return data == rhs.data; | |
164 | } | |
165 | ||
11fdf7f2 | 166 | friend void decode(ConstructorCounter &s, bufferlist::const_iterator& p) |
7c673cae | 167 | { |
11fdf7f2 | 168 | decode(s.data, p); |
7c673cae FG |
169 | } |
170 | ||
171 | friend void encode(const ConstructorCounter &s, bufferlist& p) | |
172 | { | |
11fdf7f2 | 173 | encode(s.data, p); |
7c673cae FG |
174 | } |
175 | ||
176 | friend ostream& operator<<(ostream &oss, const ConstructorCounter &cc) | |
177 | { | |
178 | oss << cc.data; | |
179 | return oss; | |
180 | } | |
181 | ||
182 | T data; | |
183 | private: | |
184 | static int default_ctor; | |
185 | static int one_arg_ctor; | |
186 | static int copy_ctor; | |
187 | static int assigns; | |
188 | }; | |
189 | ||
190 | template class ConstructorCounter <int32_t>; | |
191 | template class ConstructorCounter <int16_t>; | |
192 | ||
193 | typedef ConstructorCounter <int32_t> my_key_t; | |
194 | typedef ConstructorCounter <int16_t> my_val_t; | |
195 | typedef std::multimap < my_key_t, my_val_t > multimap2_t; | |
196 | typedef multimap2_t::value_type val2_ty; | |
197 | ||
198 | template <class T> int ConstructorCounter<T>::default_ctor = 0; | |
199 | template <class T> int ConstructorCounter<T>::one_arg_ctor = 0; | |
200 | template <class T> int ConstructorCounter<T>::copy_ctor = 0; | |
201 | template <class T> int ConstructorCounter<T>::assigns = 0; | |
202 | ||
203 | static std::ostream& operator<<(std::ostream& oss, const multimap2_t &multimap) | |
204 | { | |
205 | for (multimap2_t::const_iterator m = multimap.begin(); | |
206 | m != multimap.end(); | |
207 | ++m) | |
208 | { | |
209 | oss << m->first << "->" << m->second << " "; | |
210 | } | |
211 | return oss; | |
212 | } | |
213 | ||
214 | TEST(EncodingRoundTrip, MultimapConstructorCounter) { | |
215 | multimap2_t multimap2; | |
216 | multimap2.insert( val2_ty(my_key_t(1), my_val_t(10)) ); | |
217 | multimap2.insert( val2_ty(my_key_t(2), my_val_t(20)) ); | |
218 | multimap2.insert( val2_ty(my_key_t(2), my_val_t(30)) ); | |
219 | multimap2.insert( val2_ty(my_key_t(3), my_val_t(40)) ); | |
220 | multimap2.insert( val2_ty(my_key_t(10000), my_val_t(1)) ); | |
221 | ||
222 | my_key_t::init(); | |
223 | my_val_t::init(); | |
224 | test_encode_and_decode < multimap2_t >(multimap2); | |
225 | ||
226 | EXPECT_EQ(my_key_t::get_default_ctor(), 5); | |
227 | EXPECT_EQ(my_key_t::get_one_arg_ctor(), 0); | |
228 | EXPECT_EQ(my_key_t::get_copy_ctor(), 5); | |
229 | EXPECT_EQ(my_key_t::get_assigns(), 0); | |
230 | ||
231 | EXPECT_EQ(my_val_t::get_default_ctor(), 5); | |
232 | EXPECT_EQ(my_val_t::get_one_arg_ctor(), 0); | |
233 | EXPECT_EQ(my_val_t::get_copy_ctor(), 5); | |
234 | EXPECT_EQ(my_val_t::get_assigns(), 0); | |
235 | } | |
236 | ||
11fdf7f2 | 237 | namespace ceph { |
7c673cae FG |
238 | // make sure that the legacy encode/decode methods are selected |
239 | // over the ones defined using templates. the later is likely to | |
240 | // be slower, see also the definition of "WRITE_INT_DENC" in | |
241 | // include/denc.h | |
242 | template<> | |
243 | void encode<uint64_t, denc_traits<uint64_t>>(const uint64_t&, | |
244 | bufferlist&, | |
245 | uint64_t f) { | |
246 | static_assert(denc_traits<uint64_t>::supported, | |
247 | "should support new encoder"); | |
248 | static_assert(!denc_traits<uint64_t>::featured, | |
249 | "should not be featured"); | |
250 | ASSERT_EQ(0UL, f); | |
251 | // make sure the test fails if i get called | |
252 | ASSERT_TRUE(false); | |
253 | } | |
254 | ||
255 | template<> | |
256 | void encode<ceph_le64, denc_traits<ceph_le64>>(const ceph_le64&, | |
257 | bufferlist&, | |
258 | uint64_t f) { | |
259 | static_assert(denc_traits<ceph_le64>::supported, | |
260 | "should support new encoder"); | |
261 | static_assert(!denc_traits<ceph_le64>::featured, | |
262 | "should not be featured"); | |
263 | ASSERT_EQ(0UL, f); | |
264 | // make sure the test fails if i get called | |
265 | ASSERT_TRUE(false); | |
266 | } | |
11fdf7f2 TL |
267 | } |
268 | ||
269 | namespace { | |
270 | // search `underlying_type` in denc.h for supported underlying types | |
271 | enum class Colour : int8_t { R,G,B }; | |
272 | ostream& operator<<(ostream& os, Colour c) { | |
273 | switch (c) { | |
274 | case Colour::R: | |
275 | return os << "Colour::R"; | |
276 | case Colour::G: | |
277 | return os << "Colour::G"; | |
278 | case Colour::B: | |
279 | return os << "Colour::B"; | |
280 | default: | |
281 | return os << "Colour::???"; | |
282 | } | |
283 | } | |
284 | } | |
7c673cae FG |
285 | |
286 | TEST(EncodingRoundTrip, Integers) { | |
287 | // int types | |
288 | { | |
289 | uint64_t i = 42; | |
290 | test_encode_and_decode(i); | |
291 | } | |
292 | { | |
293 | int16_t i = 42; | |
294 | test_encode_and_decode(i); | |
295 | } | |
296 | { | |
297 | bool b = true; | |
298 | test_encode_and_decode(b); | |
299 | } | |
300 | { | |
301 | bool b = false; | |
302 | test_encode_and_decode(b); | |
303 | } | |
304 | // raw encoder | |
305 | { | |
306 | ceph_le64 i; | |
307 | i = 42; | |
308 | test_encode_and_decode(i); | |
309 | } | |
11fdf7f2 TL |
310 | // enum |
311 | { | |
312 | test_encode_and_decode(Colour::R); | |
313 | // this should not build, as the size of unsigned is not the same on | |
314 | // different archs, that's why denc_traits<> intentionally leaves | |
315 | // `int` and `unsigned int` out of supported types. | |
316 | // | |
317 | // enum E { R, G, B }; | |
318 | // test_encode_and_decode(R); | |
319 | } | |
7c673cae FG |
320 | } |
321 | ||
322 | const char* expected_what[] = { | |
f67539c2 TL |
323 | "void lame_decoder(int) no longer understand old encoding version 100 < 200: Malformed input", |
324 | "void lame_decoder(int) decode past end of struct encoding: Malformed input" | |
7c673cae FG |
325 | }; |
326 | ||
327 | void lame_decoder(int which) { | |
328 | switch (which) { | |
329 | case 0: | |
330 | throw buffer::malformed_input(DECODE_ERR_OLDVERSION(__PRETTY_FUNCTION__, 100, 200)); | |
331 | case 1: | |
332 | throw buffer::malformed_input(DECODE_ERR_PAST(__PRETTY_FUNCTION__)); | |
333 | } | |
334 | } | |
335 | ||
336 | TEST(EncodingException, Macros) { | |
337 | for (unsigned i = 0; i < sizeof(expected_what)/sizeof(expected_what[0]); i++) { | |
338 | try { | |
339 | lame_decoder(i); | |
340 | } catch (const exception& e) { | |
341 | ASSERT_EQ(string(expected_what[i]), string(e.what())); | |
342 | } | |
343 | } | |
344 | } | |
345 | ||
346 | ||
347 | TEST(small_encoding, varint) { | |
348 | uint32_t v[][4] = { | |
349 | /* value, varint bytes, signed varint bytes, signed varint bytes (neg) */ | |
350 | {0, 1, 1, 1}, | |
351 | {1, 1, 1, 1}, | |
352 | {2, 1, 1, 1}, | |
353 | {31, 1, 1, 1}, | |
354 | {32, 1, 1, 1}, | |
355 | {0xff, 2, 2, 2}, | |
356 | {0x100, 2, 2, 2}, | |
357 | {0xfff, 2, 2, 2}, | |
358 | {0x1000, 2, 2, 2}, | |
359 | {0x2000, 2, 3, 3}, | |
360 | {0x3fff, 2, 3, 3}, | |
361 | {0x4000, 3, 3, 3}, | |
362 | {0x4001, 3, 3, 3}, | |
363 | {0x10001, 3, 3, 3}, | |
364 | {0x20001, 3, 3, 3}, | |
365 | {0x40001, 3, 3, 3}, | |
366 | {0x80001, 3, 3, 3}, | |
367 | {0x7f0001, 4, 4, 4}, | |
368 | {0xff00001, 4, 5, 5}, | |
369 | {0x1ff00001, 5, 5, 5}, | |
370 | {0xffff0001, 5, 5, 5}, | |
371 | {0xffffffff, 5, 5, 5}, | |
372 | {1074790401, 5, 5, 5}, | |
373 | {0, 0, 0, 0} | |
374 | }; | |
375 | for (unsigned i=0; v[i][1]; ++i) { | |
376 | { | |
377 | bufferlist bl; | |
378 | { | |
379 | auto app = bl.get_contiguous_appender(16, true); | |
380 | denc_varint(v[i][0], app); | |
381 | } | |
382 | cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t"; | |
383 | bl.hexdump(cout, false); | |
384 | cout << std::endl; | |
385 | ASSERT_EQ(bl.length(), v[i][1]); | |
386 | uint32_t u; | |
11fdf7f2 | 387 | auto p = bl.begin().get_current_ptr().cbegin(); |
7c673cae FG |
388 | denc_varint(u, p); |
389 | ASSERT_EQ(v[i][0], u); | |
390 | } | |
391 | { | |
392 | bufferlist bl; | |
393 | { | |
394 | auto app = bl.get_contiguous_appender(16, true); | |
395 | denc_signed_varint(v[i][0], app); | |
396 | } | |
397 | cout << std::hex << v[i][0] << "\t" << v[i][2] << "\t"; | |
398 | bl.hexdump(cout, false); | |
399 | cout << std::endl; | |
400 | ASSERT_EQ(bl.length(), v[i][2]); | |
401 | int32_t u; | |
11fdf7f2 | 402 | auto p = bl.begin().get_current_ptr().cbegin(); |
7c673cae FG |
403 | denc_signed_varint(u, p); |
404 | ASSERT_EQ((int32_t)v[i][0], u); | |
405 | } | |
406 | { | |
407 | bufferlist bl; | |
408 | int64_t x = -(int64_t)v[i][0]; | |
409 | { | |
410 | auto app = bl.get_contiguous_appender(16, true); | |
411 | denc_signed_varint(x, app); | |
412 | } | |
413 | cout << std::dec << x << std::hex << "\t" << v[i][3] << "\t"; | |
414 | bl.hexdump(cout, false); | |
415 | cout << std::endl; | |
416 | ASSERT_EQ(bl.length(), v[i][3]); | |
417 | int64_t u; | |
11fdf7f2 | 418 | auto p = bl.begin().get_current_ptr().cbegin(); |
7c673cae FG |
419 | denc_signed_varint(u, p); |
420 | ASSERT_EQ(x, u); | |
421 | } | |
422 | } | |
423 | } | |
424 | ||
425 | TEST(small_encoding, varint_lowz) { | |
426 | uint32_t v[][4] = { | |
427 | /* value, bytes encoded */ | |
428 | {0, 1, 1, 1}, | |
429 | {1, 1, 1, 1}, | |
430 | {2, 1, 1, 1}, | |
431 | {15, 1, 1, 1}, | |
432 | {16, 1, 1, 1}, | |
433 | {31, 1, 2, 2}, | |
434 | {63, 2, 2, 2}, | |
435 | {64, 1, 1, 1}, | |
436 | {0xff, 2, 2, 2}, | |
437 | {0x100, 1, 1, 1}, | |
438 | {0x7ff, 2, 2, 2}, | |
439 | {0xfff, 2, 3, 3}, | |
440 | {0x1000, 1, 1, 1}, | |
441 | {0x4000, 1, 1, 1}, | |
442 | {0x8000, 1, 1, 1}, | |
443 | {0x10000, 1, 2, 2}, | |
444 | {0x20000, 2, 2, 2}, | |
445 | {0x40000, 2, 2, 2}, | |
446 | {0x80000, 2, 2, 2}, | |
447 | {0x7f0000, 2, 2, 2}, | |
448 | {0xffff0000, 4, 4, 4}, | |
449 | {0xffffffff, 5, 5, 5}, | |
450 | {0x41000000, 3, 4, 4}, | |
451 | {0, 0, 0, 0} | |
452 | }; | |
453 | for (unsigned i=0; v[i][1]; ++i) { | |
454 | { | |
455 | bufferlist bl; | |
456 | { | |
457 | auto app = bl.get_contiguous_appender(16, true); | |
458 | denc_varint_lowz(v[i][0], app); | |
459 | } | |
460 | cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t"; | |
461 | bl.hexdump(cout, false); | |
462 | cout << std::endl; | |
463 | ASSERT_EQ(bl.length(), v[i][1]); | |
464 | uint32_t u; | |
11fdf7f2 | 465 | auto p = bl.begin().get_current_ptr().cbegin(); |
7c673cae FG |
466 | denc_varint_lowz(u, p); |
467 | ASSERT_EQ(v[i][0], u); | |
468 | } | |
469 | { | |
470 | bufferlist bl; | |
471 | int64_t x = v[i][0]; | |
472 | { | |
473 | auto app = bl.get_contiguous_appender(16, true); | |
474 | denc_signed_varint_lowz(x, app); | |
475 | } | |
476 | cout << std::hex << x << "\t" << v[i][1] << "\t"; | |
477 | bl.hexdump(cout, false); | |
478 | cout << std::endl; | |
479 | ASSERT_EQ(bl.length(), v[i][2]); | |
480 | int64_t u; | |
11fdf7f2 | 481 | auto p = bl.begin().get_current_ptr().cbegin(); |
7c673cae FG |
482 | denc_signed_varint_lowz(u, p); |
483 | ASSERT_EQ(x, u); | |
484 | } | |
485 | { | |
486 | bufferlist bl; | |
487 | int64_t x = -(int64_t)v[i][0]; | |
488 | { | |
489 | auto app = bl.get_contiguous_appender(16, true); | |
490 | denc_signed_varint_lowz(x, app); | |
491 | } | |
492 | cout << std::dec << x << "\t" << v[i][1] << "\t"; | |
493 | bl.hexdump(cout, false); | |
494 | cout << std::endl; | |
495 | ASSERT_EQ(bl.length(), v[i][3]); | |
496 | int64_t u; | |
11fdf7f2 | 497 | auto p = bl.begin().get_current_ptr().cbegin(); |
7c673cae FG |
498 | denc_signed_varint_lowz(u, p); |
499 | ASSERT_EQ(x, u); | |
500 | } | |
501 | } | |
502 | } | |
503 | ||
504 | TEST(small_encoding, lba) { | |
505 | uint64_t v[][2] = { | |
506 | /* value, bytes encoded */ | |
507 | {0, 4}, | |
508 | {1, 4}, | |
509 | {0xff, 4}, | |
510 | {0x10000, 4}, | |
511 | {0x7f0000, 4}, | |
512 | {0xffff0000, 4}, | |
513 | {0x0fffffff, 4}, | |
514 | {0x1fffffff, 5}, | |
515 | {0xffffffff, 5}, | |
516 | {0x3fffffff000, 4}, | |
517 | {0x7fffffff000, 5}, | |
518 | {0x1fffffff0000, 4}, | |
519 | {0x3fffffff0000, 5}, | |
520 | {0xfffffff00000, 4}, | |
521 | {0x1fffffff00000, 5}, | |
522 | {0x41000000, 4}, | |
523 | {0, 0} | |
524 | }; | |
525 | for (unsigned i=0; v[i][1]; ++i) { | |
526 | bufferlist bl; | |
527 | { | |
528 | auto app = bl.get_contiguous_appender(16, true); | |
529 | denc_lba(v[i][0], app); | |
530 | } | |
531 | cout << std::hex << v[i][0] << "\t" << v[i][1] << "\t"; | |
532 | bl.hexdump(cout, false); | |
533 | cout << std::endl; | |
534 | ASSERT_EQ(bl.length(), v[i][1]); | |
535 | uint64_t u; | |
11fdf7f2 | 536 | auto p = bl.begin().get_current_ptr().cbegin(); |
7c673cae FG |
537 | denc_lba(u, p); |
538 | ASSERT_EQ(v[i][0], u); | |
539 | } | |
540 | ||
541 | } |