// Formatting library for C++ - scanning API test // // Copyright (c) 2019 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #include "scan.h" #include #include #include #include "fmt/os.h" #include "gmock/gmock.h" #include "gtest-extra.h" TEST(scan_test, read_text) { fmt::string_view s = "foo"; auto end = fmt::scan_to(s, "foo"); EXPECT_EQ(end, s.end()); EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input"); } TEST(scan_test, read_int) { EXPECT_EQ(fmt::scan("42", "{}")->value(), 42); EXPECT_EQ(fmt::scan("-42", "{}")->value(), -42); EXPECT_EQ(fmt::scan("42", "{:}")->value(), 42); EXPECT_THROW_MSG(fmt::scan(std::to_string(INT_MAX + 1u), "{}"), fmt::format_error, "number is too big"); } TEST(scan_test, read_long_long) { EXPECT_EQ(fmt::scan("42", "{}")->value(), 42); EXPECT_EQ(fmt::scan("-42", "{}")->value(), -42); } TEST(scan_test, read_uint) { EXPECT_EQ(fmt::scan("42", "{}")->value(), 42); EXPECT_THROW_MSG(fmt::scan("-42", "{}"), fmt::format_error, "invalid input"); } TEST(scan_test, read_ulong_long) { EXPECT_EQ(fmt::scan("42", "{}")->value(), 42); EXPECT_THROW_MSG(fmt::scan("-42", "{}")->value(), fmt::format_error, "invalid input"); } TEST(scan_test, read_hex) { EXPECT_EQ(fmt::scan("2a", "{:x}")->value(), 42); auto num_digits = std::numeric_limits::digits / 4; EXPECT_THROW_MSG( fmt::scan(fmt::format("1{:0{}}", 0, num_digits), "{:x}") ->value(), fmt::format_error, "number is too big"); } TEST(scan_test, read_string) { EXPECT_EQ(fmt::scan("foo", "{}")->value(), "foo"); } TEST(scan_test, read_string_view) { EXPECT_EQ(fmt::scan("foo", "{}")->value(), "foo"); } TEST(scan_test, separator) { int n1 = 0, n2 = 0; fmt::scan_to("10 20", "{} {}", n1, n2); EXPECT_EQ(n1, 10); EXPECT_EQ(n2, 20); } struct num { int value; }; namespace fmt { template <> struct scanner { bool hex = false; auto parse(scan_parse_context& ctx) -> scan_parse_context::iterator { auto it = ctx.begin(), end = ctx.end(); if (it != end && *it == 'x') { hex = true; ++it; } if (it != end && *it != '}') report_error("invalid format"); return it; } template auto scan(num& n, ScanContext& ctx) const -> typename ScanContext::iterator { return hex ? scan_to(ctx, "{:x}", n.value) : scan_to(ctx, "{}", n.value); } }; } // namespace fmt TEST(scan_test, read_custom) { EXPECT_EQ(fmt::scan("42", "{}")->value().value, 42); EXPECT_EQ(fmt::scan("2a", "{:x}")->value().value, 42); } TEST(scan_test, invalid_format) { EXPECT_THROW_MSG(fmt::scan_to("", "{}"), fmt::format_error, "argument index out of range"); EXPECT_THROW_MSG(fmt::scan_to("", "{"), fmt::format_error, "invalid format string"); } TEST(scan_test, example) { // Example from https://wg21.link/p1729r3. if (auto result = fmt::scan("answer = 42", "{} = {}")) { #ifdef __cpp_structured_bindings const auto& [key, value] = result->values(); EXPECT_EQ(key, "answer"); EXPECT_EQ(value, 42); #endif } else { FAIL(); } } TEST(scan_test, end_of_input) { fmt::scan("", "{}"); } #if FMT_USE_FCNTL TEST(scan_test, file) { auto pipe = fmt::pipe(); fmt::string_view input = "10 20"; pipe.write_end.write(input.data(), input.size()); pipe.write_end.close(); int n1 = 0, n2 = 0; fmt::buffered_file f = pipe.read_end.fdopen("r"); fmt::scan_to(f.get(), "{} {}", n1, n2); EXPECT_EQ(n1, 10); EXPECT_EQ(n2, 20); } TEST(scan_test, lock) { auto pipe = fmt::pipe(); std::thread producer([&]() { fmt::string_view input = "42 "; for (int i = 0; i < 1000; ++i) pipe.write_end.write(input.data(), input.size()); pipe.write_end.close(); }); std::atomic count(0); fmt::buffered_file f = pipe.read_end.fdopen("r"); auto fun = [&]() { int value = 0; while (fmt::scan_to(f.get(), "{}", value)) { if (value != 42) { pipe.read_end.close(); EXPECT_EQ(value, 42); break; } ++count; } }; std::thread consumer1(fun); std::thread consumer2(fun); producer.join(); consumer1.join(); consumer2.join(); EXPECT_EQ(count, 1000); } #endif // FMT_USE_FCNTL