Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / test_denc.cc
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 #include <numeric>
19
20 #include "global/global_init.h"
21 #include "common/ceph_argparse.h"
22 #include "global/global_context.h"
23 #include "gtest/gtest.h"
24
25 #include "include/denc.h"
26
27 // test helpers
28
29 template<typename T>
30 void test_encode_decode(T v) {
31   bufferlist bl;
32   ::encode(v, bl);
33   bufferlist::iterator p = bl.begin();
34   T out;
35   ::decode(out, p);
36   ASSERT_EQ(v, out);
37 }
38
39 template<typename T>
40 void test_denc(T v) {
41   // estimate
42   size_t s = 0;
43   denc(v, s);
44   ASSERT_NE(s, 0u);
45
46   // encode
47   bufferlist bl;
48   {
49     auto a = bl.get_contiguous_appender(s);
50     denc(v, a);
51   }
52   ASSERT_LE(bl.length(), s);
53
54   // decode
55   bl.rebuild();
56   T out;
57   auto bpi = bl.front().begin();
58   denc(out, bpi);
59   ASSERT_EQ(v, out);
60   ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
61
62   // test glue
63   test_encode_decode(v);
64 }
65
66 template<typename T>
67 void test_encode_decode_featured(T v) {
68   bufferlist bl;
69   ::encode(v, bl, 123);
70   bufferlist::iterator p = bl.begin();
71   T out;
72   ::decode(out, p);
73   ASSERT_EQ(v, out);
74 }
75
76 template<typename T>
77 void test_denc_featured(T v) {
78   // estimate
79   size_t s = 0;
80   denc(v, s, 0);
81   ASSERT_GT(s, 0u);
82
83   // encode
84   bufferlist bl;
85   {
86     auto a = bl.get_contiguous_appender(s);
87     denc(v, a, 1);
88   }
89   ASSERT_LE(bl.length(), s);
90
91   // decode
92   bl.rebuild();
93   T out;
94   auto bpi = bl.front().begin();
95   denc(out, bpi, 1);
96   ASSERT_EQ(v, out);
97   ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
98
99   // test glue
100   test_encode_decode_featured(v);
101 }
102
103
104 // hooks to count bound calls
105
106 struct counts_t {
107   int num_bound_encode = 0;
108   int num_encode = 0;
109   int num_decode = 0;
110   void reset() {
111     num_bound_encode = 0;
112     num_encode = 0;
113     num_decode = 0;
114   }
115 } counts;
116
117 struct denc_counter_t {
118   void bound_encode(size_t& p) const {
119     ++counts.num_bound_encode;
120     ++p;  // denc.h does not like 0-length objects
121   }
122   void encode(buffer::list::contiguous_appender& p) const {
123     p.append("a", 1);
124     ++counts.num_encode;
125   }
126   void decode(buffer::ptr::iterator &p) {
127     p.advance(1);
128     ++counts.num_decode;
129   }
130 };
131 WRITE_CLASS_DENC(denc_counter_t)
132
133 struct denc_counter_bounded_t {
134   void bound_encode(size_t& p) const {
135     ++counts.num_bound_encode;
136     ++p;  // denc.h does not like 0-length objects
137   }
138   void encode(buffer::list::contiguous_appender& p) const {
139     p.append("a", 1);
140     ++counts.num_encode;
141   }
142   void decode(buffer::ptr::iterator &p) {
143     p.advance(1);
144     ++counts.num_decode;
145   }
146 };
147 WRITE_CLASS_DENC_BOUNDED(denc_counter_bounded_t)
148
149 TEST(denc, denc_counter)
150 {
151   denc_counter_t single, single2;
152   {
153     bufferlist bl;
154     ::encode(single, bl);
155     ::decode(single2, bl);
156   }
157   ASSERT_EQ(counts.num_bound_encode, 1);
158   ASSERT_EQ(counts.num_encode, 1);
159   ASSERT_EQ(counts.num_decode, 1);
160   counts.reset();
161 }
162
163 TEST(denc, simple)
164 {
165   test_denc((uint8_t)4);
166   test_denc((int8_t)-5);
167   test_denc((uint16_t)6);
168   test_denc((int16_t)-7);
169   test_denc((uint32_t)8);
170   test_denc((int32_t)-9);
171   test_denc((uint64_t)10);
172   test_denc((int64_t)-11);
173 }
174
175 TEST(denc, string)
176 {
177   string a, b("hi"), c("multi\nline\n");
178   test_denc(a);
179   test_denc(b);
180   test_denc(c);
181 }
182
183 struct legacy_t {
184   int32_t a = 1;
185   void encode(bufferlist& bl) const {
186     ::encode(a, bl);
187   }
188   void decode(bufferlist::iterator& p) {
189     ::decode(a, p);
190   }
191   legacy_t() {}
192   legacy_t(int32_t i) : a(i) {}
193   friend bool operator<(const legacy_t& l, const legacy_t& r) {
194     return l.a < r.a;
195   }
196   friend bool operator==(const legacy_t& l, const legacy_t& r) {
197     return l.a == r.a;
198   }
199 };
200 WRITE_CLASS_ENCODER(legacy_t)
201
202 template<template<class> class C>
203 void test_common_veclist(const char* c) {
204   {
205     cout << c << "<std::string>" << std::endl;
206     C<std::string> s;
207     s.push_back("foo");
208     s.push_back("bar");
209     s.push_back("baz");
210     counts.reset();
211     test_denc(s);
212   }
213   {
214     cout << c << "<int32_t>" << std::endl;
215     C<int32_t> s;
216     s.push_back(1);
217     s.push_back(2);
218     s.push_back(3);
219     test_denc(s);
220   }
221   {
222     cout << c << "<legacy_t>" << std::endl;
223     C<legacy_t> s;
224     s.push_back(legacy_t(1));
225     s.push_back(legacy_t(2));
226     test_encode_decode(s);
227   }
228 }
229
230 // We only care about specializing the type, all other template
231 // parameters should have the default values. (Like first-class
232 // functions, first-class templates do not bring their defaults.)
233
234 template<typename T>
235 using default_vector = std::vector<T>;
236
237 TEST(denc, vector)
238 {
239   test_common_veclist<default_vector>("std::vector");
240   {
241     counts.reset();
242     vector<denc_counter_t> v, v2;
243     v.resize(100);
244     {
245       bufferlist bl;
246       ::encode(v, bl);
247       ::decode(v2, bl);
248     }
249     ASSERT_EQ(counts.num_bound_encode, 100);
250     ASSERT_EQ(counts.num_encode, 100);
251     ASSERT_EQ(counts.num_decode, 100);
252   }
253   {
254     counts.reset();
255     vector<denc_counter_bounded_t> v, v2;
256     v.resize(100);
257     {
258       bufferlist bl;
259       ::encode(v, bl);
260       ::decode(v2, bl);
261     }
262     ASSERT_EQ(counts.num_bound_encode, 1);
263     ASSERT_EQ(counts.num_encode, 100);
264     ASSERT_EQ(counts.num_decode, 100);
265   }
266 }
267
268 template<typename T>
269 using default_list = std::list<T>;
270
271 TEST(denc, list)
272 {
273   test_common_veclist<default_list>("std::list");
274   {
275     counts.reset();
276     list<denc_counter_bounded_t> l, l2;
277     for (unsigned i=0; i<100; ++i) {
278       l.emplace_back(denc_counter_bounded_t());
279     }
280     {
281       bufferlist bl;
282       ::encode(l, bl);
283       ::decode(l2, bl);
284     }
285     ASSERT_EQ(counts.num_bound_encode, 1);
286     ASSERT_EQ(counts.num_encode, 100);
287     ASSERT_EQ(counts.num_decode, 100);
288   }
289 }
290
291 template<template<class> class C>
292 void test_setlike(const char* c) {
293   {
294     cout << c << "<std::string>" << std::endl;
295     C<std::string> s;
296     s.insert("foo");
297     s.insert("bar");
298     s.insert("baz");
299     test_denc(s);
300   }
301   {
302     cout << c << "<int32_t>" << std::endl;
303     C<int32_t> s;
304     s.insert(1);
305     s.insert(2);
306     s.insert(3);
307     test_denc(s);
308   }
309   {
310     cout << c << "<legacy_t>" << std::endl;
311     C<legacy_t> s;
312     s.insert(legacy_t(1));
313     s.insert(legacy_t(2));
314     test_encode_decode(s);
315   }
316 }
317
318 template<typename T>
319 using default_set = std::set<T>;
320
321 TEST(denc, set)
322 {
323   test_setlike<default_set>("std::set");
324 }
325
326 template<typename T>
327 using default_flat_set= boost::container::flat_set<T>;
328
329 TEST(denc, flat_set)
330 {
331   test_setlike<default_flat_set>("std::set");
332 }
333
334 struct foo_t {
335   int32_t a = 0;
336   uint64_t b = 123;
337
338   DENC(foo_t, v, p) {
339     DENC_START(1, 1, p);
340     ::denc(v.a, p);
341     ::denc(v.b, p);
342     DENC_FINISH(p);
343   }
344
345   friend bool operator==(const foo_t& l, const foo_t& r) {
346     return l.a == r.a && l.b == r.b;
347   }
348 };
349 WRITE_CLASS_DENC_BOUNDED(foo_t)
350
351 struct foo2_t {
352   int32_t c = 0;
353   uint64_t d = 123;
354
355   DENC(foo2_t, v, p) {
356     DENC_START(1, 1, p);
357     ::denc(v.c, p);
358     ::denc(v.d, p);
359     DENC_FINISH(p);
360   }
361
362   friend bool operator==(const foo2_t& l, const foo2_t& r) {
363     return l.c == r.c && l.d == r.d;
364   }
365 };
366 WRITE_CLASS_DENC_BOUNDED(foo2_t)
367
368
369 struct bar_t {
370   int32_t a = 0;
371   uint64_t b = 123;
372
373   DENC_FEATURED(bar_t, v, p, f) {
374     ::denc(v.a, p, f);
375     ::denc(v.b, p, f);
376   }
377
378   friend bool operator==(const bar_t& l, const bar_t& r) {
379     return l.a == r.a && l.b == r.b;
380   }
381 };
382 WRITE_CLASS_DENC_FEATURED_BOUNDED(bar_t)
383
384 TEST(denc, foo)
385 {
386   foo_t a;
387   test_denc(a);
388   bufferlist bl;
389   ::encode(a, bl);
390   bl.hexdump(cout);
391 }
392
393 TEST(denc, bar)
394 {
395   bar_t a;
396   test_denc_featured(a);
397 }
398
399
400
401 TEST(denc, pair)
402 {
403   pair<int32_t,std::string> p;
404   bufferlist bl;
405   {
406     auto a = bl.get_contiguous_appender(1000);
407     denc(p, a);
408     ::encode(p, bl);
409   }
410
411   pair<int32_t,legacy_t> lp;
412   ::encode(lp, bl);
413 }
414
415 template<template<class, class> class C>
416 void test_common_maplike(const char* c) {
417   {
418     cout << c << "<std::string, foo_t>" << std::endl;
419     C<string, foo_t> s;
420     s["foo"] = foo_t();
421     s["bar"] = foo_t();
422     s["baz"] = foo_t();
423     test_denc(s);
424   }
425   {
426     cout << c << "<std::string, bar_t>" << std::endl;
427     C<string, bar_t> s;
428     s["foo"] = bar_t();
429     s["bar"] = bar_t();
430     s["baz"] = bar_t();
431     test_denc_featured(s);
432   }
433   {
434     cout << c << "<std::string, legacy_t>" << std::endl;
435     C<std::string, legacy_t> s;
436     s["foo"] = legacy_t(1);
437     s["bar"] = legacy_t(2);
438     test_encode_decode(s);
439   }
440 }
441
442 template<typename U, typename V>
443 using default_map = std::map<U, V>;
444
445 TEST(denc, map)
446 {
447   test_common_maplike<default_map>("std::map");
448 }
449
450 template<typename U, typename V>
451 using default_flat_map = boost::container::flat_map<U, V>;
452
453 TEST(denc, flat_map)
454 {
455   test_common_maplike<default_flat_map>("boost::container::flat_map");
456 }
457
458 TEST(denc, bufferptr_shallow_and_deep) {
459   // shallow encode
460   int32_t i = 1;
461   bufferptr p1("foo", 3);
462   bufferlist bl;
463   {
464     auto a = bl.get_contiguous_appender(100);
465     denc(i, a);
466     denc(p1, a);
467     denc(i, a);
468   }
469   cout << "bl is " << bl << std::endl;
470   bl.hexdump(cout);
471   ASSERT_EQ(3u, bl.get_num_buffers());
472
473   bufferlist bl2 = bl;
474   bl.rebuild();
475   bl2.rebuild();
476
477   // shallow decode
478   {
479     cout << "bl is " << bl << std::endl;
480     bl.hexdump(cout);
481     auto p = bl.front().begin();
482     bufferptr op;
483     int32_t i;
484     denc(i, p);
485     denc(op, p);
486     denc(i, p);
487     ASSERT_EQ(3u, op.length());
488     ASSERT_EQ('f', op[0]);
489     memset(bl.c_str(), 0, bl.length());
490     ASSERT_EQ(0, op[0]);
491   }
492
493   // deep decode
494   {
495     cout << "bl is " << bl2 << std::endl;
496     bl2.hexdump(cout);
497     auto p = bl2.front().begin_deep();
498     bufferptr op;
499     int32_t i;
500     denc(i, p);
501     denc(op, p);
502     denc(i, p);
503     ASSERT_EQ('f', op[0]);
504     memset(bl2.c_str(), 1, bl2.length());
505     ASSERT_EQ('f', op[0]);
506   }
507 }
508
509 TEST(denc, array)
510 {
511   {
512     cout << "std::array<std::string, 3>" << std::endl;
513     std::array<std::string, 3> s = { "foo", "bar", "baz" };
514     counts.reset();
515     test_denc(s);
516   }
517   {
518     cout << "std::array<uint32_t, 3>" << std::endl;
519     std::array<uint32_t, 3> s = { 1UL, 2UL, 3UL };
520     test_denc(s);
521   }
522 }
523
524 TEST(denc, tuple)
525 {
526   {
527     cout << "std::tuple<uint64_t, uint32_t>" << std::endl;
528     std::tuple<uint64_t, uint32_t> s(100ULL, 97UL);
529     counts.reset();
530     test_denc(s);
531   }
532   {
533     cout << "std::tuple<std::string, uint3_t>" << std::endl;
534     std::tuple<std::string, uint32_t> s("foo", 97);
535     test_denc(s);
536   }
537   {
538     cout << "std::tuple<std::string, std::set<uint32_t>>" << std::endl;
539     std::tuple<std::string, std::set<uint32_t>> s(
540       "bar", std::set<uint32_t>{uint32_t(1), uint32_t(2), uint32_t(3)});
541     test_denc(s);
542   }
543 }
544
545 TEST(denc, optional)
546 {
547   {
548     cout << "boost::optional<uint64_t>" << std::endl;
549     boost::optional<uint64_t> s = 97, t = boost::none;
550     counts.reset();
551     test_denc(s);
552     test_denc(t);
553   }
554   {
555     cout << "boost::optional<std::string>" << std::endl;
556     boost::optional<std::string> s = std::string("Meow"), t = boost::none;
557     counts.reset();
558     test_denc(s);
559     test_denc(t);
560   }
561   {
562     size_t s = 0;
563     denc(boost::none, s);
564     ASSERT_NE(s, 0u);
565
566     // encode
567     bufferlist bl;
568     {
569       auto a = bl.get_contiguous_appender(s);
570       denc(boost::none, a);
571     }
572     ASSERT_LE(bl.length(), s);
573
574     bl.rebuild();
575     boost::optional<uint32_t> out = 5;
576     auto bpi = bl.front().begin();
577     denc(out, bpi);
578     ASSERT_FALSE(!!out);
579     ASSERT_EQ(bpi.get_pos(), bl.c_str() + bl.length());
580   }
581 }
582
583 // unlike legacy_t, Legacy supports denc() also.
584 struct Legacy {
585   static unsigned n_denc;
586   static unsigned n_decode;
587   uint8_t value = 0;
588   DENC(Legacy, v, p) {
589     n_denc++;
590     denc(v.value, p);
591   }
592   void decode(buffer::list::iterator& p) {
593     n_decode++;
594     ::decode(value, p);
595   }
596   static void reset() {
597     n_denc = n_decode = 0;
598   }
599   static bufferlist encode_n(unsigned n, const vector<unsigned>& segments);
600 };
601 WRITE_CLASS_DENC(Legacy)
602 unsigned Legacy::n_denc = 0;
603 unsigned Legacy::n_decode = 0;
604
605 bufferlist Legacy::encode_n(unsigned n, const vector<unsigned>& segments) {
606   vector<Legacy> v;
607   for (unsigned i = 0; i < n; i++) {
608     v.push_back(Legacy());
609   }
610   bufferlist bl(n * sizeof(uint8_t));
611   ::encode(v, bl);
612   bufferlist segmented;
613   auto p = bl.begin();
614
615   auto sum = std::accumulate(segments.begin(), segments.end(), 0u);
616   assert(sum != 0u);
617   for (auto i : segments) {
618     buffer::ptr seg;
619     p.copy_deep(bl.length() * i / sum, seg);
620     segmented.push_back(seg);
621   }
622   p.copy_all(segmented);
623   return segmented;
624 }
625
626 TEST(denc, no_copy_if_segmented_and_lengthy)
627 {
628   static_assert(_denc::has_legacy_denc<Legacy>::value,
629                 "Legacy do have legacy denc");
630   {
631     // use denc() which shallow_copy() if the buffer is small
632     constexpr unsigned N_COPIES = 42;
633     const vector<unsigned> segs{50, 50}; // half-half
634     bufferlist segmented = Legacy::encode_n(N_COPIES, segs);
635     ASSERT_GT(segmented.get_num_buffers(), 1u);
636     ASSERT_LT(segmented.length(), CEPH_PAGE_SIZE);
637     auto p = segmented.begin();
638     vector<Legacy> v;
639     // denc() is shared by encode() and decode(), so reset() right before
640     // decode()
641     Legacy::reset();
642     ::decode(v, p);
643     ASSERT_EQ(N_COPIES, v.size());
644     ASSERT_EQ(N_COPIES, Legacy::n_denc);
645     ASSERT_EQ(0u, Legacy::n_decode);
646   }
647   {
648     // use denc() which shallow_copy() if the buffer is not segmented and large
649     const unsigned N_COPIES = CEPH_PAGE_SIZE * 2;
650     const vector<unsigned> segs{100};
651     bufferlist segmented = Legacy::encode_n(N_COPIES, segs);
652     ASSERT_EQ(segmented.get_num_buffers(), 1u);
653     ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
654     auto p = segmented.begin();
655     vector<Legacy> v;
656     Legacy::reset();
657     ::decode(v, p);
658     ASSERT_EQ(N_COPIES, v.size());
659     ASSERT_EQ(N_COPIES, Legacy::n_denc);
660     ASSERT_EQ(0u, Legacy::n_decode);
661   }
662   {
663     // use denc() which shallow_copy() if the buffer is segmented and large,
664     // but the total size of the chunks to be decoded is smallish.
665     bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50});
666     bufferlist small_bl = Legacy::encode_n(100, {50, 50});
667     bufferlist segmented;
668     segmented.append(large_bl);
669     segmented.append(small_bl);
670     ASSERT_GT(segmented.get_num_buffers(), 1u);
671     ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
672     auto p = segmented.begin();
673     p.advance(large_bl.length());
674     ASSERT_LT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE);
675     vector<Legacy> v;
676     Legacy::reset();
677     ::decode(v, p);
678     ASSERT_EQ(Legacy::n_denc, 100u);
679     ASSERT_EQ(0u, Legacy::n_decode);
680   }
681   {
682     // use decode() which avoids deep copy if the buffer is segmented and large
683     bufferlist small_bl = Legacy::encode_n(100, {50, 50});
684     bufferlist large_bl = Legacy::encode_n(CEPH_PAGE_SIZE * 2, {50, 50});
685     bufferlist segmented;
686     segmented.append(small_bl);
687     segmented.append(large_bl);
688     ASSERT_GT(segmented.get_num_buffers(), 1u);
689     ASSERT_GT(segmented.length(), CEPH_PAGE_SIZE);
690     auto p = segmented.begin();
691     p.advance(small_bl.length());
692     ASSERT_GT(segmented.length() - p.get_off(), CEPH_PAGE_SIZE);
693     vector<Legacy> v;
694     Legacy::reset();
695     ::decode(v, p);
696     ASSERT_EQ(0u, Legacy::n_denc);
697     ASSERT_EQ(CEPH_PAGE_SIZE * 2, Legacy::n_decode);
698   }
699 }