// 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_to("fob", "foo"), fmt::format_error, "invalid input"); } TEST(scan_test, read_int) { int n = 0; fmt::scan_to("42", "{}", n); EXPECT_EQ(n, 42); fmt::scan_to("-42", "{}", n); EXPECT_EQ(n, -42); fmt::scan_to("42", "{:}", n); EXPECT_EQ(n, 42); EXPECT_THROW_MSG(fmt::scan_to(std::to_string(INT_MAX + 1u), "{}", n), fmt::format_error, "number is too big"); } TEST(scan_test, read_longlong) { long long n = 0; fmt::scan_to("42", "{}", n); EXPECT_EQ(n, 42); fmt::scan_to("-42", "{}", n); EXPECT_EQ(n, -42); } TEST(scan_test, read_uint) { unsigned n = 0; fmt::scan_to("42", "{}", n); EXPECT_EQ(n, 42); EXPECT_THROW_MSG(fmt::scan_to("-42", "{}", n), fmt::format_error, "invalid input"); } TEST(scan_test, read_ulonglong) { unsigned long long n = 0; fmt::scan_to("42", "{}", n); EXPECT_EQ(n, 42); EXPECT_THROW_MSG(fmt::scan_to("-42", "{}", n), fmt::format_error, "invalid input"); } TEST(scan_test, read_hex) { unsigned n = 0; fmt::scan_to("2a", "{:x}", n); EXPECT_EQ(n, 42); auto num_digits = std::numeric_limits::digits / 4; EXPECT_THROW_MSG( fmt::scan_to(fmt::format("1{:0{}}", 0, num_digits), "{:x}", n), fmt::format_error, "number is too big"); } TEST(scan_test, read_string) { std::string s; fmt::scan_to("foo", "{}", s); EXPECT_EQ(s, "foo"); } TEST(scan_test, read_string_view) { fmt::string_view s; fmt::scan_to("foo", "{}", s); EXPECT_EQ(s, "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 != '}') throw_format_error("invalid format"); return it; } template auto scan(num& n, ScanContext& ctx) const -> typename ScanContext::iterator { // TODO: handle specifier return fmt::scan(ctx, "{}", n.value); } }; } // namespace fmt TEST(scan_test, read_custom) { auto n = num(); fmt::scan_to("42", "{}", n); EXPECT_EQ(n.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) { std::string key; int value = 0; fmt::scan_to("answer = 42", "{} = {}", key, value); EXPECT_EQ(key, "answer"); EXPECT_EQ(value, 42); } TEST(scan_test, end_of_input) { int value = 0; fmt::scan_to("", "{}", value); } #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(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(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