recordio_unittest.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // Licensed to the Apache Software Foundation (ASF) under one
  2. // or more contributor license agreements. See the NOTICE file
  3. // distributed with this work for additional information
  4. // regarding copyright ownership. The ASF licenses this file
  5. // to you under the Apache License, Version 2.0 (the
  6. // "License"); you may not use this file except in compliance
  7. // with the License. You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing,
  12. // software distributed under the License is distributed on an
  13. // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. // KIND, either express or implied. See the License for the
  15. // specific language governing permissions and limitations
  16. // under the License.
  17. #include <map>
  18. #include <gtest/gtest.h>
  19. #include "butil/recordio.h"
  20. #include "butil/fast_rand.h"
  21. #include "butil/string_printf.h"
  22. #include "butil/file_util.h"
  23. namespace {
  24. class StringReader : public butil::IReader {
  25. public:
  26. StringReader(const std::string& str,
  27. bool report_eagain_on_end = false)
  28. : _str(str)
  29. , _offset(0)
  30. , _report_eagain_on_end(report_eagain_on_end) {}
  31. ssize_t ReadV(const iovec* iov, int iovcnt) override {
  32. size_t total_nc = 0;
  33. for (int i = 0; i < iovcnt; ++i) {
  34. void* dst = iov[i].iov_base;
  35. size_t len = iov[i].iov_len;
  36. size_t remain = _str.size() - _offset;
  37. size_t nc = std::min(len, remain);
  38. memcpy(dst, _str.data() + _offset, nc);
  39. _offset += nc;
  40. total_nc += nc;
  41. if (_offset == _str.size()) {
  42. break;
  43. }
  44. }
  45. if (_report_eagain_on_end && total_nc == 0) {
  46. errno = EAGAIN;
  47. return -1;
  48. }
  49. return total_nc;
  50. }
  51. private:
  52. std::string _str;
  53. size_t _offset;
  54. bool _report_eagain_on_end;
  55. };
  56. class StringWriter : public butil::IWriter {
  57. public:
  58. ssize_t WriteV(const iovec* iov, int iovcnt) override {
  59. const size_t old_size = _str.size();
  60. for (int i = 0; i < iovcnt; ++i) {
  61. _str.append((char*)iov[i].iov_base, iov[i].iov_len);
  62. }
  63. return _str.size() - old_size;
  64. }
  65. const std::string& str() const { return _str; }
  66. private:
  67. std::string _str;
  68. };
  69. TEST(RecordIOTest, empty_record) {
  70. butil::Record r;
  71. ASSERT_EQ((size_t)0, r.MetaCount());
  72. ASSERT_TRUE(r.Meta("foo") == NULL);
  73. ASSERT_FALSE(r.RemoveMeta("foo"));
  74. ASSERT_TRUE(r.Payload().empty());
  75. ASSERT_TRUE(r.MutablePayload()->empty());
  76. }
  77. TEST(RecordIOTest, manipulate_record) {
  78. butil::Record r1;
  79. ASSERT_EQ((size_t)0, r1.MetaCount());
  80. butil::IOBuf* foo_val = r1.MutableMeta("foo");
  81. ASSERT_EQ((size_t)1, r1.MetaCount());
  82. ASSERT_TRUE(foo_val->empty());
  83. foo_val->append("foo_data");
  84. ASSERT_EQ(foo_val, r1.MutableMeta("foo"));
  85. ASSERT_EQ((size_t)1, r1.MetaCount());
  86. ASSERT_EQ("foo_data", *foo_val);
  87. ASSERT_EQ(foo_val, r1.Meta("foo"));
  88. butil::IOBuf* bar_val = r1.MutableMeta("bar");
  89. ASSERT_EQ((size_t)2, r1.MetaCount());
  90. ASSERT_TRUE(bar_val->empty());
  91. bar_val->append("bar_data");
  92. ASSERT_EQ(bar_val, r1.MutableMeta("bar"));
  93. ASSERT_EQ((size_t)2, r1.MetaCount());
  94. ASSERT_EQ("bar_data", *bar_val);
  95. ASSERT_EQ(bar_val, r1.Meta("bar"));
  96. butil::Record r2 = r1;
  97. ASSERT_TRUE(r1.RemoveMeta("foo"));
  98. ASSERT_EQ((size_t)1, r1.MetaCount());
  99. ASSERT_TRUE(r1.Meta("foo") == NULL);
  100. ASSERT_EQ(foo_val, r2.Meta("foo"));
  101. ASSERT_EQ("foo_data", *foo_val);
  102. }
  103. TEST(RecordIOTest, invalid_name) {
  104. char name[258];
  105. for (size_t i = 0; i < sizeof(name); ++i) {
  106. name[i] = 'a';
  107. }
  108. name[sizeof(name) - 1] = 0;
  109. butil::Record r;
  110. ASSERT_EQ(NULL, r.MutableMeta(name));
  111. }
  112. TEST(RecordIOTest, write_read_basic) {
  113. StringWriter sw;
  114. butil::RecordWriter rw(&sw);
  115. butil::Record src;
  116. ASSERT_EQ(0, rw.Write(src));
  117. butil::IOBuf* foo_val = src.MutableMeta("foo");
  118. foo_val->append("foo_data");
  119. ASSERT_EQ(0, rw.Write(src));
  120. butil::IOBuf* bar_val = src.MutableMeta("bar");
  121. bar_val->append("bar_data");
  122. ASSERT_EQ(0, rw.Write(src));
  123. src.MutablePayload()->append("payload_data");
  124. ASSERT_EQ(0, rw.Write(src));
  125. ASSERT_EQ(0, rw.Flush());
  126. std::cout << "len=" << sw.str().size()
  127. << " content=" << butil::PrintedAsBinary(sw.str(), 256) << std::endl;
  128. StringReader sr(sw.str());
  129. butil::RecordReader rr(&sr);
  130. butil::Record r1;
  131. ASSERT_TRUE(rr.ReadNext(&r1));
  132. ASSERT_EQ(0, rr.last_error());
  133. ASSERT_EQ((size_t)0, r1.MetaCount());
  134. ASSERT_TRUE(r1.Payload().empty());
  135. butil::Record r2;
  136. ASSERT_TRUE(rr.ReadNext(&r2));
  137. ASSERT_EQ(0, rr.last_error());
  138. ASSERT_EQ((size_t)1, r2.MetaCount());
  139. ASSERT_EQ("foo", r2.MetaAt(0).name);
  140. ASSERT_EQ("foo_data", *r2.MetaAt(0).data);
  141. ASSERT_TRUE(r2.Payload().empty());
  142. butil::Record r3;
  143. ASSERT_TRUE(rr.ReadNext(&r3));
  144. ASSERT_EQ(0, rr.last_error());
  145. ASSERT_EQ((size_t)2, r3.MetaCount());
  146. ASSERT_EQ("foo", r3.MetaAt(0).name);
  147. ASSERT_EQ("foo_data", *r3.MetaAt(0).data);
  148. ASSERT_EQ("bar", r3.MetaAt(1).name);
  149. ASSERT_EQ("bar_data", *r3.MetaAt(1).data);
  150. ASSERT_TRUE(r3.Payload().empty());
  151. butil::Record r4;
  152. ASSERT_TRUE(rr.ReadNext(&r4));
  153. ASSERT_EQ(0, rr.last_error());
  154. ASSERT_EQ((size_t)2, r4.MetaCount());
  155. ASSERT_EQ("foo", r4.MetaAt(0).name);
  156. ASSERT_EQ("foo_data", *r4.MetaAt(0).data);
  157. ASSERT_EQ("bar", r4.MetaAt(1).name);
  158. ASSERT_EQ("bar_data", *r4.MetaAt(1).data);
  159. ASSERT_EQ("payload_data", r4.Payload());
  160. ASSERT_FALSE(rr.ReadNext(NULL));
  161. ASSERT_EQ((int)butil::RecordReader::END_OF_READER, rr.last_error());
  162. ASSERT_EQ(sw.str().size(), rr.offset());
  163. }
  164. TEST(RecordIOTest, incomplete_reader) {
  165. StringWriter sw;
  166. butil::RecordWriter rw(&sw);
  167. butil::Record src;
  168. butil::IOBuf* foo_val = src.MutableMeta("foo");
  169. foo_val->append("foo_data");
  170. ASSERT_EQ(0, rw.Write(src));
  171. butil::IOBuf* bar_val = src.MutableMeta("bar");
  172. bar_val->append("bar_data");
  173. ASSERT_EQ(0, rw.Write(src));
  174. ASSERT_EQ(0, rw.Flush());
  175. std::string data = sw.str();
  176. std::cout << "len=" << data.size()
  177. << " content=" << butil::PrintedAsBinary(data, 256) << std::endl;
  178. StringReader sr(data, true);
  179. butil::RecordReader rr(&sr);
  180. butil::Record r2;
  181. ASSERT_TRUE(rr.ReadNext(&r2));
  182. ASSERT_EQ(0, rr.last_error());
  183. ASSERT_EQ((size_t)1, r2.MetaCount());
  184. ASSERT_EQ("foo", r2.MetaAt(0).name);
  185. ASSERT_EQ("foo_data", *r2.MetaAt(0).data);
  186. ASSERT_TRUE(r2.Payload().empty());
  187. butil::Record r3;
  188. ASSERT_TRUE(rr.ReadNext(&r3));
  189. ASSERT_EQ(0, rr.last_error());
  190. ASSERT_EQ((size_t)2, r3.MetaCount());
  191. ASSERT_EQ("foo", r3.MetaAt(0).name);
  192. ASSERT_EQ("foo_data", *r3.MetaAt(0).data);
  193. ASSERT_EQ("bar", r3.MetaAt(1).name);
  194. ASSERT_EQ("bar_data", *r3.MetaAt(1).data);
  195. ASSERT_TRUE(r3.Payload().empty());
  196. ASSERT_FALSE(rr.ReadNext(NULL));
  197. ASSERT_EQ(EAGAIN, rr.last_error());
  198. ASSERT_EQ(sw.str().size(), rr.offset());
  199. }
  200. static std::string rand_string(int min_len, int max_len) {
  201. const int len = butil::fast_rand_in(min_len, max_len);
  202. std::string str;
  203. str.reserve(len);
  204. for (int i = 0; i < len; ++i) {
  205. str.push_back(butil::fast_rand_in('a', 'Z'));
  206. }
  207. return str;
  208. }
  209. TEST(RecordIOTest, write_read_random) {
  210. StringWriter sw;
  211. butil::RecordWriter rw(&sw);
  212. const int N = 1024;
  213. std::vector<std::pair<std::string, std::string>> name_value_list;
  214. size_t nbytes = 0;
  215. std::map<int, size_t> breaking_offsets;
  216. for (int i = 0; i < N; ++i) {
  217. butil::Record src;
  218. std::string value = rand_string(10, 20);
  219. std::string name = butil::string_printf("name_%d_", i) + value;
  220. src.MutableMeta(name)->append(value);
  221. ASSERT_EQ(0, rw.Write(src));
  222. if (butil::fast_rand_less_than(70) == 0) {
  223. breaking_offsets[i] = nbytes;
  224. } else {
  225. name_value_list.push_back(std::make_pair(name, value));
  226. }
  227. nbytes += src.ByteSize();
  228. }
  229. ASSERT_EQ(0, rw.Flush());
  230. std::string str = sw.str();
  231. ASSERT_EQ(nbytes, str.size());
  232. // break some records
  233. int break_idx = 0;
  234. for (auto it = breaking_offsets.begin(); it != breaking_offsets.end(); ++it) {
  235. switch (break_idx++ % 10) {
  236. case 0:
  237. str[it->second] = 'r';
  238. break;
  239. case 1:
  240. str[it->second + 1] = 'd';
  241. break;
  242. case 2:
  243. str[it->second + 2] = 'i';
  244. break;
  245. case 3:
  246. str[it->second + 3] = 'o';
  247. break;
  248. case 4:
  249. ++str[it->second + 4];
  250. break;
  251. case 5:
  252. str[it->second + 4] = 8;
  253. break;
  254. case 6:
  255. ++str[it->second + 5];
  256. break;
  257. case 7:
  258. ++str[it->second + 6];
  259. break;
  260. case 8:
  261. ++str[it->second + 7];
  262. break;
  263. case 9:
  264. ++str[it->second + 8];
  265. break;
  266. default:
  267. ASSERT_TRUE(false) << "never";
  268. }
  269. }
  270. ASSERT_EQ((size_t)N - breaking_offsets.size(), name_value_list.size());
  271. std::cout << "sw.size=" << str.size()
  272. << " nbreak=" << breaking_offsets.size() << std::endl;
  273. StringReader sr(str);
  274. ASSERT_LT(0, butil::WriteFile(butil::FilePath("recordio_ref.io"), str.data(), str.size()));
  275. butil::RecordReader rr(&sr);
  276. size_t j = 0;
  277. butil::Record r;
  278. for (; rr.ReadNext(&r); ++j) {
  279. ASSERT_LT(j, name_value_list.size());
  280. ASSERT_EQ((size_t)1, r.MetaCount());
  281. ASSERT_EQ(name_value_list[j].first, r.MetaAt(0).name) << j;
  282. ASSERT_EQ(name_value_list[j].second, *r.MetaAt(0).data);
  283. }
  284. ASSERT_EQ((int)butil::RecordReader::END_OF_READER, rr.last_error());
  285. ASSERT_EQ(j, name_value_list.size());
  286. ASSERT_LE(str.size() - rr.offset(), 3u);
  287. }
  288. } // namespace