]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph distributed storage system | |
5 | * | |
6 | * Copyright (C) 2016 Red Hat | |
7 | * | |
8 | * Author: Sage Weil <sage@redhat.com> | |
9 | * | |
10 | * This library is free software; you can redistribute it and/or | |
11 | * modify it under the terms of the GNU Lesser General Public | |
12 | * License as published by the Free Software Foundation; either | |
13 | * version 2.1 of the License, or (at your option) any later version. | |
14 | * | |
15 | */ | |
16 | ||
17 | #include <stdio.h> | |
18 | ||
19 | #include "global/global_init.h" | |
20 | #include "common/ceph_argparse.h" | |
21 | #include "global/global_context.h" | |
22 | #include "gtest/gtest.h" | |
23 | ||
24 | #include "include/denc.h" | |
25 | ||
26 | // test helpers | |
27 | ||
28 | template<typename T> | |
29 | void test_encode_decode(T v) { | |
30 | bufferlist bl; | |
31 | ::encode(v, bl); | |
32 | bufferlist::iterator p = bl.begin(); | |
33 | T out; | |
34 | ::decode(out, p); | |
35 | ASSERT_EQ(v, out); | |
36 | } | |
37 | ||
38 | template<typename T> | |
39 | void test_denc(T v) { | |
40 | // estimate | |
41 | size_t s = 0; | |
42 | denc(v, s); | |
43 | ASSERT_NE(s, 0u); | |
44 | ||
45 | // encode | |
46 | bufferlist bl; | |
47 | { | |
48 | auto a = bl.get_contiguous_appender(s); | |
49 | denc(v, a); | |
50 | } | |
51 | ASSERT_LE(bl.length(), s); | |
52 | ||
53 | // decode | |
54 | bl.rebuild(); | |
55 | T out; | |
56 | auto bpi = bl.front().begin(); | |
57 | denc(out, bpi); | |
58 | ASSERT_EQ(v, out); | |
59 | ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length()); | |
60 | ||
61 | // test glue | |
62 | test_encode_decode(v); | |
63 | } | |
64 | ||
65 | template<typename T> | |
66 | void test_encode_decode_featured(T v) { | |
67 | bufferlist bl; | |
68 | ::encode(v, bl, 123); | |
69 | bufferlist::iterator p = bl.begin(); | |
70 | T out; | |
71 | ::decode(out, p); | |
72 | ASSERT_EQ(v, out); | |
73 | } | |
74 | ||
75 | template<typename T> | |
76 | void test_denc_featured(T v) { | |
77 | // estimate | |
78 | size_t s = 0; | |
79 | denc(v, s, 0); | |
80 | ASSERT_GT(s, 0u); | |
81 | ||
82 | // encode | |
83 | bufferlist bl; | |
84 | { | |
85 | auto a = bl.get_contiguous_appender(s); | |
86 | denc(v, a, 1); | |
87 | } | |
88 | ASSERT_LE(bl.length(), s); | |
89 | ||
90 | // decode | |
91 | bl.rebuild(); | |
92 | T out; | |
93 | auto bpi = bl.front().begin(); | |
94 | denc(out, bpi, 1); | |
95 | ASSERT_EQ(v, out); | |
96 | ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length()); | |
97 | ||
98 | // test glue | |
99 | test_encode_decode_featured(v); | |
100 | } | |
101 | ||
102 | ||
103 | // hooks to count bound calls | |
104 | ||
105 | struct counts_t { | |
106 | int num_bound_encode = 0; | |
107 | int num_encode = 0; | |
108 | int num_decode = 0; | |
109 | void reset() { | |
110 | num_bound_encode = 0; | |
111 | num_encode = 0; | |
112 | num_decode = 0; | |
113 | } | |
114 | } counts; | |
115 | ||
116 | struct denc_counter_t { | |
117 | void bound_encode(size_t& p) const { | |
118 | ++counts.num_bound_encode; | |
119 | ++p; // denc.h does not like 0-length objects | |
120 | } | |
121 | void encode(buffer::list::contiguous_appender& p) const { | |
122 | p.append("a", 1); | |
123 | ++counts.num_encode; | |
124 | } | |
125 | void decode(buffer::ptr::iterator &p) { | |
126 | p.advance(1); | |
127 | ++counts.num_decode; | |
128 | } | |
129 | }; | |
130 | WRITE_CLASS_DENC(denc_counter_t) | |
131 | ||
132 | struct denc_counter_bounded_t { | |
133 | void bound_encode(size_t& p) const { | |
134 | ++counts.num_bound_encode; | |
135 | ++p; // denc.h does not like 0-length objects | |
136 | } | |
137 | void encode(buffer::list::contiguous_appender& p) const { | |
138 | p.append("a", 1); | |
139 | ++counts.num_encode; | |
140 | } | |
141 | void decode(buffer::ptr::iterator &p) { | |
142 | p.advance(1); | |
143 | ++counts.num_decode; | |
144 | } | |
145 | }; | |
146 | WRITE_CLASS_DENC_BOUNDED(denc_counter_bounded_t) | |
147 | ||
148 | TEST(denc, denc_counter) | |
149 | { | |
150 | denc_counter_t single, single2; | |
151 | { | |
152 | bufferlist bl; | |
153 | ::encode(single, bl); | |
154 | ::decode(single2, bl); | |
155 | } | |
156 | ASSERT_EQ(counts.num_bound_encode, 1); | |
157 | ASSERT_EQ(counts.num_encode, 1); | |
158 | ASSERT_EQ(counts.num_decode, 1); | |
159 | counts.reset(); | |
160 | } | |
161 | ||
162 | TEST(denc, simple) | |
163 | { | |
164 | test_denc((uint8_t)4); | |
165 | test_denc((int8_t)-5); | |
166 | test_denc((uint16_t)6); | |
167 | test_denc((int16_t)-7); | |
168 | test_denc((uint32_t)8); | |
169 | test_denc((int32_t)-9); | |
170 | test_denc((uint64_t)10); | |
171 | test_denc((int64_t)-11); | |
172 | } | |
173 | ||
174 | TEST(denc, string) | |
175 | { | |
176 | string a, b("hi"), c("multi\nline\n"); | |
177 | test_denc(a); | |
178 | test_denc(b); | |
179 | test_denc(c); | |
180 | } | |
181 | ||
182 | struct legacy_t { | |
183 | int32_t a = 1; | |
184 | void encode(bufferlist& bl) const { | |
185 | ::encode(a, bl); | |
186 | } | |
187 | void decode(bufferlist::iterator& p) { | |
188 | ::decode(a, p); | |
189 | } | |
190 | legacy_t() {} | |
191 | legacy_t(int32_t i) : a(i) {} | |
192 | friend bool operator<(const legacy_t& l, const legacy_t& r) { | |
193 | return l.a < r.a; | |
194 | } | |
195 | friend bool operator==(const legacy_t& l, const legacy_t& r) { | |
196 | return l.a == r.a; | |
197 | } | |
198 | }; | |
199 | WRITE_CLASS_ENCODER(legacy_t) | |
200 | ||
201 | template<template<class> class C> | |
202 | void test_common_veclist(const char* c) { | |
203 | { | |
204 | cout << c << "<std::string>" << std::endl; | |
205 | C<std::string> s; | |
206 | s.push_back("foo"); | |
207 | s.push_back("bar"); | |
208 | s.push_back("baz"); | |
209 | counts.reset(); | |
210 | test_denc(s); | |
211 | } | |
212 | { | |
213 | cout << c << "<int32_t>" << std::endl; | |
214 | C<int32_t> s; | |
215 | s.push_back(1); | |
216 | s.push_back(2); | |
217 | s.push_back(3); | |
218 | test_denc(s); | |
219 | } | |
220 | { | |
221 | cout << c << "<legacy_t>" << std::endl; | |
222 | C<legacy_t> s; | |
223 | s.push_back(legacy_t(1)); | |
224 | s.push_back(legacy_t(2)); | |
225 | test_encode_decode(s); | |
226 | } | |
227 | } | |
228 | ||
229 | // We only care about specializing the type, all other template | |
230 | // parameters should have the default values. (Like first-class | |
231 | // functions, first-class templates do not bring their defaults.) | |
232 | ||
233 | template<typename T> | |
234 | using default_vector = std::vector<T>; | |
235 | ||
236 | TEST(denc, vector) | |
237 | { | |
238 | test_common_veclist<default_vector>("std::vector"); | |
239 | { | |
240 | counts.reset(); | |
241 | vector<denc_counter_t> v, v2; | |
242 | v.resize(100); | |
243 | { | |
244 | bufferlist bl; | |
245 | ::encode(v, bl); | |
246 | ::decode(v2, bl); | |
247 | } | |
248 | ASSERT_EQ(counts.num_bound_encode, 100); | |
249 | ASSERT_EQ(counts.num_encode, 100); | |
250 | ASSERT_EQ(counts.num_decode, 100); | |
251 | } | |
252 | { | |
253 | counts.reset(); | |
254 | vector<denc_counter_bounded_t> v, v2; | |
255 | v.resize(100); | |
256 | { | |
257 | bufferlist bl; | |
258 | ::encode(v, bl); | |
259 | ::decode(v2, bl); | |
260 | } | |
261 | ASSERT_EQ(counts.num_bound_encode, 1); | |
262 | ASSERT_EQ(counts.num_encode, 100); | |
263 | ASSERT_EQ(counts.num_decode, 100); | |
264 | } | |
265 | } | |
266 | ||
267 | template<typename T> | |
268 | using default_list = std::list<T>; | |
269 | ||
270 | TEST(denc, list) | |
271 | { | |
272 | test_common_veclist<default_list>("std::list"); | |
273 | { | |
274 | counts.reset(); | |
275 | list<denc_counter_bounded_t> l, l2; | |
276 | for (unsigned i=0; i<100; ++i) { | |
277 | l.emplace_back(denc_counter_bounded_t()); | |
278 | } | |
279 | { | |
280 | bufferlist bl; | |
281 | ::encode(l, bl); | |
282 | ::decode(l2, bl); | |
283 | } | |
284 | ASSERT_EQ(counts.num_bound_encode, 1); | |
285 | ASSERT_EQ(counts.num_encode, 100); | |
286 | ASSERT_EQ(counts.num_decode, 100); | |
287 | } | |
288 | } | |
289 | ||
290 | template<template<class> class C> | |
291 | void test_setlike(const char* c) { | |
292 | { | |
293 | cout << c << "<std::string>" << std::endl; | |
294 | C<std::string> s; | |
295 | s.insert("foo"); | |
296 | s.insert("bar"); | |
297 | s.insert("baz"); | |
298 | test_denc(s); | |
299 | } | |
300 | { | |
301 | cout << c << "<int32_t>" << std::endl; | |
302 | C<int32_t> s; | |
303 | s.insert(1); | |
304 | s.insert(2); | |
305 | s.insert(3); | |
306 | test_denc(s); | |
307 | } | |
308 | { | |
309 | cout << c << "<legacy_t>" << std::endl; | |
310 | C<legacy_t> s; | |
311 | s.insert(legacy_t(1)); | |
312 | s.insert(legacy_t(2)); | |
313 | test_encode_decode(s); | |
314 | } | |
315 | } | |
316 | ||
317 | template<typename T> | |
318 | using default_set = std::set<T>; | |
319 | ||
320 | TEST(denc, set) | |
321 | { | |
322 | test_setlike<default_set>("std::set"); | |
323 | } | |
324 | ||
325 | template<typename T> | |
326 | using default_flat_set= boost::container::flat_set<T>; | |
327 | ||
328 | TEST(denc, flat_set) | |
329 | { | |
330 | test_setlike<default_flat_set>("std::set"); | |
331 | } | |
332 | ||
333 | struct foo_t { | |
334 | int32_t a = 0; | |
335 | uint64_t b = 123; | |
336 | ||
337 | DENC(foo_t, v, p) { | |
338 | DENC_START(1, 1, p); | |
339 | ::denc(v.a, p); | |
340 | ::denc(v.b, p); | |
341 | DENC_FINISH(p); | |
342 | } | |
343 | ||
344 | friend bool operator==(const foo_t& l, const foo_t& r) { | |
345 | return l.a == r.a && l.b == r.b; | |
346 | } | |
347 | }; | |
348 | WRITE_CLASS_DENC_BOUNDED(foo_t) | |
349 | ||
350 | struct foo2_t { | |
351 | int32_t c = 0; | |
352 | uint64_t d = 123; | |
353 | ||
354 | DENC(foo2_t, v, p) { | |
355 | DENC_START(1, 1, p); | |
356 | ::denc(v.c, p); | |
357 | ::denc(v.d, p); | |
358 | DENC_FINISH(p); | |
359 | } | |
360 | ||
361 | friend bool operator==(const foo2_t& l, const foo2_t& r) { | |
362 | return l.c == r.c && l.d == r.d; | |
363 | } | |
364 | }; | |
365 | WRITE_CLASS_DENC_BOUNDED(foo2_t) | |
366 | ||
367 | ||
368 | struct bar_t { | |
369 | int32_t a = 0; | |
370 | uint64_t b = 123; | |
371 | ||
372 | DENC_FEATURED(bar_t, v, p, f) { | |
373 | ::denc(v.a, p, f); | |
374 | ::denc(v.b, p, f); | |
375 | } | |
376 | ||
377 | friend bool operator==(const bar_t& l, const bar_t& r) { | |
378 | return l.a == r.a && l.b == r.b; | |
379 | } | |
380 | }; | |
381 | WRITE_CLASS_DENC_FEATURED_BOUNDED(bar_t) | |
382 | ||
383 | TEST(denc, foo) | |
384 | { | |
385 | foo_t a; | |
386 | test_denc(a); | |
387 | bufferlist bl; | |
388 | ::encode(a, bl); | |
389 | bl.hexdump(cout); | |
390 | } | |
391 | ||
392 | TEST(denc, bar) | |
393 | { | |
394 | bar_t a; | |
395 | test_denc_featured(a); | |
396 | } | |
397 | ||
398 | ||
399 | ||
400 | TEST(denc, pair) | |
401 | { | |
402 | pair<int32_t,std::string> p; | |
403 | bufferlist bl; | |
404 | { | |
405 | auto a = bl.get_contiguous_appender(1000); | |
406 | denc(p, a); | |
407 | ::encode(p, bl); | |
408 | } | |
409 | ||
410 | pair<int32_t,legacy_t> lp; | |
411 | ::encode(lp, bl); | |
412 | } | |
413 | ||
414 | template<template<class, class> class C> | |
415 | void test_common_maplike(const char* c) { | |
416 | { | |
417 | cout << c << "<std::string, foo_t>" << std::endl; | |
418 | C<string, foo_t> s; | |
419 | s["foo"] = foo_t(); | |
420 | s["bar"] = foo_t(); | |
421 | s["baz"] = foo_t(); | |
422 | test_denc(s); | |
423 | } | |
424 | { | |
425 | cout << c << "<std::string, bar_t>" << std::endl; | |
426 | C<string, bar_t> s; | |
427 | s["foo"] = bar_t(); | |
428 | s["bar"] = bar_t(); | |
429 | s["baz"] = bar_t(); | |
430 | test_denc_featured(s); | |
431 | } | |
432 | { | |
433 | cout << c << "<std::string, legacy_t>" << std::endl; | |
434 | C<std::string, legacy_t> s; | |
435 | s["foo"] = legacy_t(1); | |
436 | s["bar"] = legacy_t(2); | |
437 | test_encode_decode(s); | |
438 | } | |
439 | } | |
440 | ||
441 | template<typename U, typename V> | |
442 | using default_map = std::map<U, V>; | |
443 | ||
444 | TEST(denc, map) | |
445 | { | |
446 | test_common_maplike<default_map>("std::map"); | |
447 | } | |
448 | ||
449 | template<typename U, typename V> | |
450 | using default_flat_map = boost::container::flat_map<U, V>; | |
451 | ||
452 | TEST(denc, flat_map) | |
453 | { | |
454 | test_common_maplike<default_flat_map>("boost::container::flat_map"); | |
455 | } | |
456 | ||
457 | TEST(denc, bufferptr_shallow_and_deep) { | |
458 | // shallow encode | |
459 | int32_t i = 1; | |
460 | bufferptr p1("foo", 3); | |
461 | bufferlist bl; | |
462 | { | |
463 | auto a = bl.get_contiguous_appender(100); | |
464 | denc(i, a); | |
465 | denc(p1, a); | |
466 | denc(i, a); | |
467 | } | |
468 | cout << "bl is " << bl << std::endl; | |
469 | bl.hexdump(cout); | |
470 | ASSERT_EQ(3u, bl.get_num_buffers()); | |
471 | ||
472 | bufferlist bl2 = bl; | |
473 | bl.rebuild(); | |
474 | bl2.rebuild(); | |
475 | ||
476 | // shallow decode | |
477 | { | |
478 | cout << "bl is " << bl << std::endl; | |
479 | bl.hexdump(cout); | |
480 | auto p = bl.front().begin(); | |
481 | bufferptr op; | |
482 | int32_t i; | |
483 | denc(i, p); | |
484 | denc(op, p); | |
485 | denc(i, p); | |
486 | ASSERT_EQ(3u, op.length()); | |
487 | ASSERT_EQ('f', op[0]); | |
488 | memset(bl.c_str(), 0, bl.length()); | |
489 | ASSERT_EQ(0, op[0]); | |
490 | } | |
491 | ||
492 | // deep decode | |
493 | { | |
494 | cout << "bl is " << bl2 << std::endl; | |
495 | bl2.hexdump(cout); | |
496 | auto p = bl2.front().begin_deep(); | |
497 | bufferptr op; | |
498 | int32_t i; | |
499 | denc(i, p); | |
500 | denc(op, p); | |
501 | denc(i, p); | |
502 | ASSERT_EQ('f', op[0]); | |
503 | memset(bl2.c_str(), 1, bl2.length()); | |
504 | ASSERT_EQ('f', op[0]); | |
505 | } | |
506 | } | |
507 | ||
508 | TEST(denc, array) | |
509 | { | |
510 | { | |
511 | cout << "std::array<std::string, 3>" << std::endl; | |
512 | std::array<std::string, 3> s = { "foo", "bar", "baz" }; | |
513 | counts.reset(); | |
514 | test_denc(s); | |
515 | } | |
516 | { | |
517 | cout << "std::array<uint32_t, 3>" << std::endl; | |
518 | std::array<uint32_t, 3> s = { 1UL, 2UL, 3UL }; | |
519 | test_denc(s); | |
520 | } | |
521 | } | |
522 | ||
523 | TEST(denc, tuple) | |
524 | { | |
525 | { | |
526 | cout << "std::tuple<uint64_t, uint32_t>" << std::endl; | |
527 | std::tuple<uint64_t, uint32_t> s(100ULL, 97UL); | |
528 | counts.reset(); | |
529 | test_denc(s); | |
530 | } | |
531 | { | |
532 | cout << "std::tuple<std::string, uint3_t>" << std::endl; | |
533 | std::tuple<std::string, uint32_t> s("foo", 97); | |
534 | test_denc(s); | |
535 | } | |
536 | { | |
537 | cout << "std::tuple<std::string, std::set<uint32_t>>" << std::endl; | |
538 | std::tuple<std::string, std::set<uint32_t>> s( | |
539 | "bar", std::set<uint32_t>{uint32_t(1), uint32_t(2), uint32_t(3)}); | |
540 | test_denc(s); | |
541 | } | |
542 | } | |
543 | ||
544 | TEST(denc, optional) | |
545 | { | |
546 | { | |
547 | cout << "boost::optional<uint64_t>" << std::endl; | |
548 | boost::optional<uint64_t> s = 97, t = boost::none; | |
549 | counts.reset(); | |
550 | test_denc(s); | |
551 | test_denc(t); | |
552 | } | |
553 | { | |
554 | cout << "boost::optional<std::string>" << std::endl; | |
555 | boost::optional<std::string> s = std::string("Meow"), t = boost::none; | |
556 | counts.reset(); | |
557 | test_denc(s); | |
558 | test_denc(t); | |
559 | } | |
560 | { | |
561 | size_t s = 0; | |
562 | denc(boost::none, s); | |
563 | ASSERT_NE(s, 0u); | |
564 | ||
565 | // encode | |
566 | bufferlist bl; | |
567 | { | |
568 | auto a = bl.get_contiguous_appender(s); | |
569 | denc(boost::none, a); | |
570 | } | |
571 | ASSERT_LE(bl.length(), s); | |
572 | ||
573 | bl.rebuild(); | |
574 | boost::optional<uint32_t> out = 5; | |
575 | auto bpi = bl.front().begin(); | |
576 | denc(out, bpi); | |
577 | ASSERT_FALSE(!!out); | |
578 | ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length()); | |
579 | } | |
580 | } |