loop_ut.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. #include <library/cpp/yson_pull/input.h>
  2. #include <library/cpp/yson_pull/output.h>
  3. #include <library/cpp/yson_pull/reader.h>
  4. #include <library/cpp/yson_pull/writer.h>
  5. #include <library/cpp/testing/unittest/registar.h>
  6. #include <cerrno>
  7. #include <cmath>
  8. #ifdef _unix_
  9. #include <unistd.h>
  10. #include <sys/wait.h>
  11. #endif
  12. namespace {
  13. constexpr const char* alphabet =
  14. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  15. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  16. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  17. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  18. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  19. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  20. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  21. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  22. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  23. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  24. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  25. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  26. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  27. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  28. "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  29. void generate(NYsonPull::TWriter& writer, size_t count) {
  30. writer.BeginStream();
  31. for (size_t i = 0; i < count; ++i) {
  32. writer.BeginMap()
  33. .Key("ints")
  34. .BeginList()
  35. .Int64(0)
  36. .Int64(-1)
  37. .Int64(1000)
  38. .Int64(-1000)
  39. .EndList()
  40. .Key("uints")
  41. .BeginList()
  42. .UInt64(0)
  43. .UInt64(1000)
  44. .UInt64(10000000)
  45. .EndList()
  46. .Key("entities")
  47. .BeginList()
  48. .Entity()
  49. .BeginAttributes()
  50. .Key("color")
  51. .String("blue")
  52. .Key("size")
  53. .Int64(100)
  54. .EndAttributes()
  55. .Entity()
  56. .Entity()
  57. .EndList()
  58. .Key("booleans")
  59. .BeginList()
  60. .Boolean(true)
  61. .Boolean(false)
  62. .Boolean(true)
  63. .EndList()
  64. .Key("floats")
  65. .BeginList()
  66. .Float64(0.0)
  67. .Float64(13.0e30)
  68. .Float64(M_PI)
  69. .EndList()
  70. .Key("strings")
  71. .BeginList()
  72. .String("hello")
  73. .String("")
  74. .String("foo \"-bar-\" baz")
  75. .String("oh\nwow")
  76. .String(alphabet)
  77. .EndList()
  78. .EndMap();
  79. }
  80. writer.EndStream();
  81. }
  82. #ifdef __clang__
  83. // XXX: With all the macros below (esp. UNIT_ASSERT_VALUES_EQUAL) unfolded,
  84. // the time it takes clang to optimize generated code becomes abysmal.
  85. // Locally disabling optimization brings it back to normal.
  86. __attribute__((optnone))
  87. #endif // __clang__
  88. void
  89. verify(NYsonPull::TReader& reader, size_t count) {
  90. #define NEXT(name__) \
  91. { \
  92. auto& name__ = reader.NextEvent(); // SCOPED_TRACE(e);
  93. #define END_NEXT }
  94. #define NEXT_TYPE(type__) \
  95. NEXT(e) { \
  96. UNIT_ASSERT_VALUES_EQUAL(NYsonPull::EEventType::type__, e.Type()); \
  97. } \
  98. END_NEXT
  99. #define NEXT_KEY(key__) \
  100. NEXT(e) { \
  101. UNIT_ASSERT_VALUES_EQUAL(NYsonPull::EEventType::Key, e.Type()); \
  102. UNIT_ASSERT_VALUES_EQUAL(key__, e.AsString()); \
  103. } \
  104. END_NEXT
  105. #define NEXT_SCALAR(type__, value__) \
  106. NEXT(e) { \
  107. UNIT_ASSERT_VALUES_EQUAL(NYsonPull::EEventType::Scalar, e.Type()); \
  108. UNIT_ASSERT_VALUES_EQUAL(NYsonPull::EScalarType::type__, e.AsScalar().Type()); \
  109. UNIT_ASSERT_VALUES_EQUAL(value__, e.AsScalar().As##type__()); \
  110. } \
  111. END_NEXT
  112. #define NEXT_ENTITY() \
  113. NEXT(e) { \
  114. UNIT_ASSERT_VALUES_EQUAL(NYsonPull::EEventType::Scalar, e.Type()); \
  115. UNIT_ASSERT_VALUES_EQUAL(NYsonPull::EScalarType::Entity, e.AsScalar().Type()); \
  116. } \
  117. END_NEXT
  118. #define NEXT_FLOAT64(value__) \
  119. NEXT(e) { \
  120. UNIT_ASSERT_VALUES_EQUAL(NYsonPull::EEventType::Scalar, e.Type()); \
  121. UNIT_ASSERT_VALUES_EQUAL(NYsonPull::EScalarType::Float64, e.AsScalar().Type()); \
  122. UNIT_ASSERT_DOUBLES_EQUAL(value__, e.AsScalar().AsFloat64(), 1e-5); \
  123. } \
  124. END_NEXT
  125. constexpr auto true_ = true;
  126. constexpr auto false_ = false;
  127. NEXT_TYPE(BeginStream);
  128. for (size_t i = 0; i < count; ++i) {
  129. NEXT_TYPE(BeginMap);
  130. NEXT_KEY("ints") {
  131. NEXT_TYPE(BeginList);
  132. NEXT_SCALAR(Int64, 0);
  133. NEXT_SCALAR(Int64, -1);
  134. NEXT_SCALAR(Int64, 1000);
  135. NEXT_SCALAR(Int64, -1000);
  136. NEXT_TYPE(EndList);
  137. }
  138. NEXT_KEY("uints") {
  139. NEXT_TYPE(BeginList);
  140. NEXT_SCALAR(UInt64, 0U);
  141. NEXT_SCALAR(UInt64, 1000U);
  142. NEXT_SCALAR(UInt64, 10000000U);
  143. NEXT_TYPE(EndList);
  144. }
  145. NEXT_KEY("entities") {
  146. NEXT_TYPE(BeginList);
  147. NEXT_ENTITY();
  148. NEXT_TYPE(BeginAttributes) {
  149. NEXT_KEY("color") {
  150. NEXT_SCALAR(String, "blue");
  151. }
  152. NEXT_KEY("size") {
  153. NEXT_SCALAR(Int64, 100);
  154. }
  155. }
  156. NEXT_TYPE(EndAttributes);
  157. NEXT_ENTITY();
  158. NEXT_ENTITY();
  159. NEXT_TYPE(EndList);
  160. }
  161. NEXT_KEY("booleans") {
  162. NEXT_TYPE(BeginList);
  163. NEXT_SCALAR(Boolean, true_);
  164. NEXT_SCALAR(Boolean, false_);
  165. NEXT_SCALAR(Boolean, true_);
  166. NEXT_TYPE(EndList);
  167. }
  168. NEXT_KEY("floats") {
  169. NEXT_TYPE(BeginList);
  170. NEXT_FLOAT64(0.0);
  171. NEXT_FLOAT64(13.0e30);
  172. NEXT_FLOAT64(M_PI);
  173. NEXT_TYPE(EndList);
  174. }
  175. NEXT_KEY("strings") {
  176. NEXT_TYPE(BeginList);
  177. NEXT_SCALAR(String, "hello");
  178. NEXT_SCALAR(String, "");
  179. NEXT_SCALAR(String, "foo \"-bar-\" baz");
  180. NEXT_SCALAR(String, "oh\nwow");
  181. NEXT_SCALAR(String, alphabet);
  182. NEXT_TYPE(EndList);
  183. }
  184. NEXT_TYPE(EndMap);
  185. }
  186. NEXT_TYPE(EndStream);
  187. #undef NEXT
  188. #undef END_NEXT
  189. #undef NEXT_TYPE
  190. #undef NEXT_KEY
  191. #undef NEXT_SCALAR
  192. }
  193. class sys_error {};
  194. IOutputStream& operator<<(IOutputStream& stream, const sys_error&) {
  195. stream << strerror(errno);
  196. return stream;
  197. }
  198. NYsonPull::TReader make_reader(THolder<NYsonPull::NInput::IStream> stream) {
  199. return NYsonPull::TReader(
  200. std::move(stream),
  201. NYsonPull::EStreamType::ListFragment);
  202. }
  203. template <typename Function>
  204. void test_memory(Function make_writer, size_t nrepeat) {
  205. TString text;
  206. {
  207. auto writer = make_writer(NYsonPull::NOutput::FromString(&text));
  208. generate(writer, nrepeat);
  209. }
  210. {
  211. auto reader = make_reader(NYsonPull::NInput::FromMemory(text));
  212. verify(reader, nrepeat);
  213. }
  214. {
  215. TStringInput input(text);
  216. auto reader = make_reader(NYsonPull::NInput::FromInputStream(&input, /* buffer_size = */ 1));
  217. verify(reader, nrepeat);
  218. }
  219. }
  220. #ifdef _unix_
  221. template <typename Here, typename There>
  222. void pipe(Here&& reader, There&& writer) {
  223. int fildes[2];
  224. UNIT_ASSERT_VALUES_EQUAL_C(0, ::pipe(fildes), sys_error());
  225. auto read_fd = fildes[0];
  226. auto write_fd = fildes[1];
  227. auto pid = ::fork();
  228. UNIT_ASSERT_C(pid >= 0, sys_error());
  229. if (pid > 0) {
  230. // parent
  231. UNIT_ASSERT_VALUES_EQUAL_C(0, ::close(write_fd), sys_error());
  232. reader(read_fd);
  233. UNIT_ASSERT_VALUES_EQUAL_C(0, ::close(read_fd), sys_error());
  234. } else {
  235. // child
  236. UNIT_ASSERT_VALUES_EQUAL_C(0, ::close(read_fd), sys_error());
  237. UNIT_ASSERT_NO_EXCEPTION(writer(write_fd));
  238. UNIT_ASSERT_VALUES_EQUAL_C(0, ::close(write_fd), sys_error());
  239. ::exit(0);
  240. }
  241. int stat_loc;
  242. UNIT_ASSERT_VALUES_EQUAL_C(pid, ::waitpid(pid, &stat_loc, 0), sys_error());
  243. }
  244. template <typename Function>
  245. void test_posix_fd(
  246. Function make_writer,
  247. size_t nrepeat,
  248. size_t read_buffer_size,
  249. size_t write_buffer_size) {
  250. pipe(
  251. [&](int fd) {
  252. auto reader = make_reader(NYsonPull::NInput::FromPosixFd(fd, read_buffer_size));
  253. verify(reader, nrepeat);
  254. },
  255. [&](int fd) {
  256. auto writer = make_writer(NYsonPull::NOutput::FromPosixFd(fd, write_buffer_size));
  257. generate(writer, nrepeat);
  258. });
  259. }
  260. template <typename Function>
  261. void test_stdio_file(
  262. Function make_writer,
  263. size_t nrepeat,
  264. size_t read_buffer_size,
  265. size_t write_buffer_size) {
  266. pipe(
  267. [&](int fd) {
  268. auto file = ::fdopen(fd, "rb");
  269. UNIT_ASSERT_C(file != nullptr, sys_error());
  270. auto reader = make_reader(NYsonPull::NInput::FromStdioFile(file, read_buffer_size));
  271. verify(reader, nrepeat);
  272. },
  273. [&](int fd) {
  274. auto file = ::fdopen(fd, "wb");
  275. Y_UNUSED(write_buffer_size);
  276. auto writer = make_writer(NYsonPull::NOutput::FromStdioFile(file, write_buffer_size));
  277. generate(writer, nrepeat);
  278. fflush(file);
  279. });
  280. }
  281. #endif
  282. NYsonPull::TWriter text(THolder<NYsonPull::NOutput::IStream> stream) {
  283. return NYsonPull::MakeTextWriter(
  284. std::move(stream),
  285. NYsonPull::EStreamType::ListFragment);
  286. }
  287. NYsonPull::TWriter pretty_text(THolder<NYsonPull::NOutput::IStream> stream) {
  288. return NYsonPull::MakePrettyTextWriter(
  289. std::move(stream),
  290. NYsonPull::EStreamType::ListFragment);
  291. }
  292. NYsonPull::TWriter binary(THolder<NYsonPull::NOutput::IStream> stream) {
  293. return NYsonPull::MakeBinaryWriter(
  294. std::move(stream),
  295. NYsonPull::EStreamType::ListFragment);
  296. }
  297. } // anonymous namespace
  298. Y_UNIT_TEST_SUITE(Loop) {
  299. Y_UNIT_TEST(memory_pretty_text) {
  300. test_memory(pretty_text, 100);
  301. }
  302. Y_UNIT_TEST(memory_text) {
  303. test_memory(text, 100);
  304. }
  305. Y_UNIT_TEST(memory_binary) {
  306. test_memory(binary, 100);
  307. }
  308. #ifdef _unix_
  309. Y_UNIT_TEST(posix_fd_pretty_text_buffered) {
  310. test_posix_fd(pretty_text, 100, 1024, 1024);
  311. }
  312. Y_UNIT_TEST(posix_fd_pretty_text_unbuffered) {
  313. test_posix_fd(pretty_text, 100, 1, 0);
  314. }
  315. Y_UNIT_TEST(posix_fd_text_buffered) {
  316. test_posix_fd(text, 100, 1024, 1024);
  317. }
  318. Y_UNIT_TEST(posix_fd_text_unbuffered) {
  319. test_posix_fd(text, 100, 1, 0);
  320. }
  321. Y_UNIT_TEST(posix_fd_binary_buffered) {
  322. test_posix_fd(binary, 100, 1024, 1024);
  323. }
  324. Y_UNIT_TEST(posix_fd_binary_unbuffered) {
  325. test_posix_fd(binary, 100, 1, 0);
  326. }
  327. Y_UNIT_TEST(stdio_file_pretty_text_buffered) {
  328. test_stdio_file(pretty_text, 100, 1024, 1024);
  329. }
  330. Y_UNIT_TEST(stdio_file_pretty_text_unbuffered) {
  331. test_stdio_file(pretty_text, 100, 1, 0);
  332. }
  333. Y_UNIT_TEST(stdio_file_text_buffered) {
  334. test_stdio_file(text, 100, 1024, 1024);
  335. }
  336. Y_UNIT_TEST(stdio_file_text_unbuffered) {
  337. test_stdio_file(text, 100, 1, 0);
  338. }
  339. Y_UNIT_TEST(stdio_file_binary_buffered) {
  340. test_stdio_file(binary, 100, 1024, 1024);
  341. }
  342. Y_UNIT_TEST(stdio_file_binary_unbuffered) {
  343. test_stdio_file(binary, 100, 1, 0);
  344. }
  345. #endif
  346. } // Y_UNIT_TEST_SUITE(Loop)