scan-test.cc 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Formatting library for C++ - scanning API test
  2. //
  3. // Copyright (c) 2019 - present, Victor Zverovich
  4. // All rights reserved.
  5. //
  6. // For the license information refer to format.h.
  7. #include "scan.h"
  8. #include <time.h>
  9. #include <climits>
  10. #include <thread>
  11. #include "fmt/os.h"
  12. #include "gmock/gmock.h"
  13. #include "gtest-extra.h"
  14. TEST(scan_test, read_text) {
  15. fmt::string_view s = "foo";
  16. auto end = fmt::scan_to(s, "foo");
  17. EXPECT_EQ(end, s.end());
  18. EXPECT_THROW_MSG(fmt::scan<int>("fob", "foo"), fmt::format_error,
  19. "invalid input");
  20. }
  21. TEST(scan_test, read_int) {
  22. EXPECT_EQ(fmt::scan<int>("42", "{}")->value(), 42);
  23. EXPECT_EQ(fmt::scan<int>("-42", "{}")->value(), -42);
  24. EXPECT_EQ(fmt::scan<int>("42", "{:}")->value(), 42);
  25. EXPECT_THROW_MSG(fmt::scan<int>(std::to_string(INT_MAX + 1u), "{}"),
  26. fmt::format_error, "number is too big");
  27. }
  28. TEST(scan_test, read_long_long) {
  29. EXPECT_EQ(fmt::scan<long long>("42", "{}")->value(), 42);
  30. EXPECT_EQ(fmt::scan<long long>("-42", "{}")->value(), -42);
  31. }
  32. TEST(scan_test, read_uint) {
  33. EXPECT_EQ(fmt::scan<unsigned>("42", "{}")->value(), 42);
  34. EXPECT_THROW_MSG(fmt::scan<unsigned>("-42", "{}"), fmt::format_error,
  35. "invalid input");
  36. }
  37. TEST(scan_test, read_ulong_long) {
  38. EXPECT_EQ(fmt::scan<unsigned long long>("42", "{}")->value(), 42);
  39. EXPECT_THROW_MSG(fmt::scan<unsigned long long>("-42", "{}")->value(),
  40. fmt::format_error, "invalid input");
  41. }
  42. TEST(scan_test, read_hex) {
  43. EXPECT_EQ(fmt::scan<unsigned>("2a", "{:x}")->value(), 42);
  44. auto num_digits = std::numeric_limits<unsigned>::digits / 4;
  45. EXPECT_THROW_MSG(
  46. fmt::scan<unsigned>(fmt::format("1{:0{}}", 0, num_digits), "{:x}")
  47. ->value(),
  48. fmt::format_error, "number is too big");
  49. }
  50. TEST(scan_test, read_string) {
  51. EXPECT_EQ(fmt::scan<std::string>("foo", "{}")->value(), "foo");
  52. }
  53. TEST(scan_test, read_string_view) {
  54. EXPECT_EQ(fmt::scan<fmt::string_view>("foo", "{}")->value(), "foo");
  55. }
  56. TEST(scan_test, separator) {
  57. int n1 = 0, n2 = 0;
  58. fmt::scan_to("10 20", "{} {}", n1, n2);
  59. EXPECT_EQ(n1, 10);
  60. EXPECT_EQ(n2, 20);
  61. }
  62. struct num {
  63. int value;
  64. };
  65. namespace fmt {
  66. template <> struct scanner<num> {
  67. bool hex = false;
  68. auto parse(scan_parse_context& ctx) -> scan_parse_context::iterator {
  69. auto it = ctx.begin(), end = ctx.end();
  70. if (it != end && *it == 'x') {
  71. hex = true;
  72. ++it;
  73. }
  74. if (it != end && *it != '}') report_error("invalid format");
  75. return it;
  76. }
  77. template <class ScanContext>
  78. auto scan(num& n, ScanContext& ctx) const -> typename ScanContext::iterator {
  79. return hex ? scan_to(ctx, "{:x}", n.value) : scan_to(ctx, "{}", n.value);
  80. }
  81. };
  82. } // namespace fmt
  83. TEST(scan_test, read_custom) {
  84. EXPECT_EQ(fmt::scan<num>("42", "{}")->value().value, 42);
  85. EXPECT_EQ(fmt::scan<num>("2a", "{:x}")->value().value, 42);
  86. }
  87. TEST(scan_test, invalid_format) {
  88. EXPECT_THROW_MSG(fmt::scan_to("", "{}"), fmt::format_error,
  89. "argument index out of range");
  90. EXPECT_THROW_MSG(fmt::scan_to("", "{"), fmt::format_error,
  91. "invalid format string");
  92. }
  93. namespace std {
  94. using fmt::scan;
  95. using fmt::scan_error;
  96. } // namespace std
  97. TEST(scan_test, example) {
  98. // Example from https://wg21.link/p1729r3.
  99. if (auto result = std::scan<std::string, int>("answer = 42", "{} = {}")) {
  100. auto range = result->range();
  101. EXPECT_EQ(range.begin(), range.end());
  102. EXPECT_EQ(result->begin(), result->end());
  103. #ifdef __cpp_structured_bindings
  104. const auto& [key, value] = result->values();
  105. EXPECT_EQ(key, "answer");
  106. EXPECT_EQ(value, 42);
  107. #endif
  108. } else {
  109. std::scan_error error = result.error();
  110. (void)error;
  111. FAIL();
  112. }
  113. }
  114. TEST(scan_test, end_of_input) { fmt::scan<int>("", "{}"); }
  115. #if FMT_USE_FCNTL
  116. TEST(scan_test, file) {
  117. auto pipe = fmt::pipe();
  118. fmt::string_view input = "10 20";
  119. pipe.write_end.write(input.data(), input.size());
  120. pipe.write_end.close();
  121. int n1 = 0, n2 = 0;
  122. fmt::buffered_file f = pipe.read_end.fdopen("r");
  123. fmt::scan_to(f.get(), "{} {}", n1, n2);
  124. EXPECT_EQ(n1, 10);
  125. EXPECT_EQ(n2, 20);
  126. }
  127. TEST(scan_test, lock) {
  128. auto pipe = fmt::pipe();
  129. std::thread producer([&]() {
  130. fmt::string_view input = "42 ";
  131. for (int i = 0; i < 1000; ++i)
  132. pipe.write_end.write(input.data(), input.size());
  133. pipe.write_end.close();
  134. });
  135. std::atomic<int> count(0);
  136. fmt::buffered_file f = pipe.read_end.fdopen("r");
  137. auto fun = [&]() {
  138. int value = 0;
  139. while (fmt::scan_to(f.get(), "{}", value)) {
  140. if (value != 42) {
  141. pipe.read_end.close();
  142. EXPECT_EQ(value, 42);
  143. break;
  144. }
  145. ++count;
  146. }
  147. };
  148. std::thread consumer1(fun);
  149. std::thread consumer2(fun);
  150. producer.join();
  151. consumer1.join();
  152. consumer2.join();
  153. EXPECT_EQ(count, 1000);
  154. }
  155. #endif // FMT_USE_FCNTL