| /** @file |
| |
| Unit tests for BufferFormat and bwprint. |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #include "catch.hpp" |
| #include "../../../tests/include/catch.hpp" |
| #include <chrono> |
| #include <iostream> |
| #include <netinet/in.h> |
| #include "tscore/BufferWriter.h" |
| #include "tscore/bwf_std_format.h" |
| #include "tscpp/util/MemSpan.h" |
| #include "tscore/ink_config.h" |
| #if TS_ENABLE_FIPS == 0 |
| #include "tscore/INK_MD5.h" |
| #endif |
| #include "tscore/CryptoHash.h" |
| |
| using namespace std::literals; |
| |
| TEST_CASE("Buffer Writer << operator", "[bufferwriter][stream]") |
| { |
| ts::LocalBufferWriter<50> bw; |
| |
| bw << "The" << ' ' << "quick" << ' ' << "brown fox"; |
| |
| REQUIRE(bw.view() == "The quick brown fox"); |
| |
| bw.reduce(0); |
| bw << "x=" << bw.capacity(); |
| REQUIRE(bw.view() == "x=50"); |
| } |
| |
| TEST_CASE("bwprint basics", "[bwprint]") |
| { |
| ts::LocalBufferWriter<256> bw; |
| std::string_view fmt1{"Some text"sv}; |
| |
| bw.print(fmt1); |
| REQUIRE(bw.view() == fmt1); |
| bw.reduce(0); |
| bw.print("Arg {}", 1); |
| REQUIRE(bw.view() == "Arg 1"); |
| bw.reduce(0); |
| bw.print("arg 1 {1} and 2 {2} and 0 {0}", "zero", "one", "two"); |
| REQUIRE(bw.view() == "arg 1 one and 2 two and 0 zero"); |
| bw.reduce(0); |
| bw.print("args {2}{0}{1}", "zero", "one", "two"); |
| REQUIRE(bw.view() == "args twozeroone"); |
| bw.reduce(0); |
| bw.print("left |{:<10}|", "text"); |
| REQUIRE(bw.view() == "left |text |"); |
| bw.reduce(0); |
| bw.print("right |{:>10}|", "text"); |
| REQUIRE(bw.view() == "right | text|"); |
| bw.reduce(0); |
| bw.print("right |{:.>10}|", "text"); |
| REQUIRE(bw.view() == "right |......text|"); |
| bw.reduce(0); |
| bw.print("center |{:.^10}|", "text"); |
| REQUIRE(bw.view() == "center |...text...|"); |
| bw.reduce(0); |
| bw.print("center |{:.^11}|", "text"); |
| REQUIRE(bw.view() == "center |...text....|"); |
| bw.reduce(0); |
| bw.print("center |{:^^10}|", "text"); |
| REQUIRE(bw.view() == "center |^^^text^^^|"); |
| bw.reduce(0); |
| bw.print("center |{:%3A^10}|", "text"); |
| REQUIRE(bw.view() == "center |:::text:::|"); |
| bw.reduce(0); |
| bw.print("left >{0:<9}< right >{0:>9}< center >{0:^9}<", 956); |
| REQUIRE(bw.view() == "left >956 < right > 956< center > 956 <"); |
| |
| bw.reduce(0); |
| bw.print("Format |{:>#010x}|", -956); |
| REQUIRE(bw.view() == "Format |0000-0x3bc|"); |
| bw.reduce(0); |
| bw.print("Format |{:<#010x}|", -956); |
| REQUIRE(bw.view() == "Format |-0x3bc0000|"); |
| bw.reduce(0); |
| bw.print("Format |{:#010x}|", -956); |
| REQUIRE(bw.view() == "Format |-0x00003bc|"); |
| |
| bw.reduce(0); |
| bw.print("{{BAD_ARG_INDEX:{} of {}}}", 17, 23); |
| REQUIRE(bw.view() == "{BAD_ARG_INDEX:17 of 23}"); |
| |
| bw.reduce(0); |
| bw.print("Arg {0} Arg {3}", 1, 2); |
| REQUIRE(bw.view() == "Arg 1 Arg {BAD_ARG_INDEX:3 of 2}"); |
| |
| bw.reduce(0); |
| bw.print("{{stuff}} Arg {0} Arg {}", 1, 2); |
| REQUIRE(bw.view() == "{stuff} Arg 1 Arg 2"); |
| bw.reduce(0); |
| bw.print("Arg {0} Arg {} and {{stuff}}", 3, 4); |
| REQUIRE(bw.view() == "Arg 3 Arg 4 and {stuff}"); |
| bw.reduce(0); |
| bw.print("Arg {{{0}}} Arg {} and {{stuff}}", 5, 6); |
| REQUIRE(bw.view() == "Arg {5} Arg 6 and {stuff}"); |
| bw.reduce(0); |
| bw.print("Arg {0} Arg {{}}{{}} {} and {{stuff}}", 7, 8); |
| REQUIRE(bw.view() == "Arg 7 Arg {}{} 8 and {stuff}"); |
| bw.reduce(0); |
| bw.print("Arg {0} Arg {{{{}}}} {}", 9, 10); |
| REQUIRE(bw.view() == "Arg 9 Arg {{}} 10"); |
| |
| bw.reduce(0); |
| bw.print("Arg {0} Arg {{{{}}}} {}", 9, 10); |
| REQUIRE(bw.view() == "Arg 9 Arg {{}} 10"); |
| bw.reduce(0); |
| |
| bw.reset().print("{leif}"); |
| REQUIRE(bw.view() == "{~leif~}"); // expected to be missing. |
| |
| bw.reset().print("Thread: {thread-name} [{thread-id:#x}] - Tick: {tick} - Epoch: {now} - timestamp: {timestamp} {0}\n", 31267); |
| // std::cout << bw; |
| /* |
| std::cout << ts::LocalBufferWriter<256>().print( |
| "Thread: {thread-name} [{thread-id:#x}] - Tick: {tick} - Epoch: {now} - timestamp: {timestamp} {0}{}", 31267, '\n'); |
| */ |
| } |
| |
| TEST_CASE("BWFormat numerics", "[bwprint][bwformat]") |
| { |
| ts::LocalBufferWriter<256> bw; |
| ts::BWFormat fmt("left >{0:<9}< right >{0:>9}< center >{0:^9}<"); |
| std::string_view text{"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"}; |
| |
| bw.reduce(0); |
| static const ts::BWFormat bad_arg_fmt{"{{BAD_ARG_INDEX:{} of {}}}"}; |
| bw.print(bad_arg_fmt, 17, 23); |
| REQUIRE(bw.view() == "{BAD_ARG_INDEX:17 of 23}"); |
| |
| bw.reduce(0); |
| bw.print(fmt, 956); |
| REQUIRE(bw.view() == "left >956 < right > 956< center > 956 <"); |
| |
| bw.reduce(0); |
| bw.print("Text: _{0:.10,20}_", text); |
| REQUIRE(bw.view() == "Text: _abcdefghijklmnopqrst_"); |
| bw.reduce(0); |
| bw.print("Text: _{0:-<20.52,20}_", text); |
| REQUIRE(bw.view() == "Text: _QRSTUVWXYZ----------_"); |
| |
| void *ptr = reinterpret_cast<void *>(0XBADD0956); |
| bw.reduce(0); |
| bw.print("{}", ptr); |
| REQUIRE(bw.view() == "0xbadd0956"); |
| bw.reduce(0); |
| bw.print("{:X}", ptr); |
| REQUIRE(bw.view() == "0XBADD0956"); |
| int *int_ptr = static_cast<int *>(ptr); |
| bw.reduce(0); |
| bw.print("{}", int_ptr); |
| REQUIRE(bw.view() == "0xbadd0956"); |
| auto char_ptr = "good"; |
| bw.reduce(0); |
| bw.print("{:x}", static_cast<char *>(ptr)); |
| REQUIRE(bw.view() == "0xbadd0956"); |
| bw.reduce(0); |
| bw.print("{}", char_ptr); |
| REQUIRE(bw.view() == "good"); |
| |
| ts::MemSpan span{ptr, 0x200}; |
| bw.reduce(0); |
| bw.print("{}", span); |
| REQUIRE(bw.view() == "0x200@0xbadd0956"); |
| |
| bw.reduce(0); |
| bw.print("{:x}", ts::MemSpan(const_cast<char *>(char_ptr), 4)); |
| REQUIRE(bw.view() == "676f6f64"); |
| bw.reduce(0); |
| bw.print("{:#x}", ts::MemSpan(const_cast<char *>(char_ptr), 4)); |
| REQUIRE(bw.view() == "0x676f6f64"); |
| bw.reduce(0); |
| bw.print("{:x}", ts::MemSpan<void>(const_cast<char *>(char_ptr), 4)); |
| REQUIRE(bw.view() == "676f6f64"); |
| bw.reduce(0); |
| bw.print("{:#x}", ts::MemSpan<void>(const_cast<char *>(char_ptr), 4)); |
| REQUIRE(bw.view() == "0x676f6f64"); |
| |
| std::string_view sv{"abc123"}; |
| bw.reduce(0); |
| bw.print("{}", sv); |
| REQUIRE(bw.view() == sv); |
| bw.reduce(0); |
| bw.print("{:x}", sv); |
| REQUIRE(bw.view() == "616263313233"); |
| bw.reduce(0); |
| bw.print("{:#x}", sv); |
| REQUIRE(bw.view() == "0x616263313233"); |
| bw.reduce(0); |
| bw.print("|{:16x}|", sv); |
| REQUIRE(bw.view() == "|616263313233 |"); |
| bw.reduce(0); |
| bw.print("|{:>16x}|", sv); |
| REQUIRE(bw.view() == "| 616263313233|"); |
| bw.reduce(0); |
| bw.print("|{:^16x}|", sv); |
| REQUIRE(bw.view() == "| 616263313233 |"); |
| bw.reduce(0); |
| bw.print("|{:>16.2x}|", sv); |
| REQUIRE(bw.view() == "| 63313233|"); |
| bw.reduce(0); |
| bw.print("|{:<0.2,5x}|", sv); |
| REQUIRE(bw.view() == "|63313|"); |
| bw.reset().print("|{:<.2,5x}|", sv); |
| REQUIRE(bw.view() == "|63313|"); |
| |
| bw.reduce(0); |
| bw.print("|{}|", true); |
| REQUIRE(bw.view() == "|1|"); |
| bw.reduce(0); |
| bw.print("|{}|", false); |
| REQUIRE(bw.view() == "|0|"); |
| bw.reduce(0); |
| bw.print("|{:s}|", true); |
| REQUIRE(bw.view() == "|true|"); |
| bw.reduce(0); |
| bw.print("|{:S}|", false); |
| REQUIRE(bw.view() == "|FALSE|"); |
| bw.reduce(0); |
| bw.print("|{:>9s}|", false); |
| REQUIRE(bw.view() == "| false|"); |
| bw.reduce(0); |
| bw.print("|{:^10s}|", true); |
| REQUIRE(bw.view() == "| true |"); |
| |
| // Test clipping a bit. |
| ts::LocalBufferWriter<20> bw20; |
| bw20.print("0123456789abc|{:^10s}|", true); |
| REQUIRE(bw20.view() == "0123456789abc| tru"); |
| bw20.reduce(0); |
| bw20.print("012345|{:^10s}|6789abc", true); |
| REQUIRE(bw20.view() == "012345| true |67"); |
| |
| #if TS_ENABLE_FIPS == 0 |
| INK_MD5 md5; |
| bw.reduce(0); |
| bw.print("{}", md5); |
| REQUIRE(bw.view() == "00000000000000000000000000000000"); |
| CryptoContext().hash_immediate(md5, sv.data(), sv.size()); |
| bw.reduce(0); |
| bw.print("{}", md5); |
| REQUIRE(bw.view() == "e99a18c428cb38d5f260853678922e03"); |
| #endif |
| |
| bw.reset().print("Char '{}'", 'a'); |
| REQUIRE(bw.view() == "Char 'a'"); |
| bw.reset().print("Byte '{}'", uint8_t{'a'}); |
| REQUIRE(bw.view() == "Byte '97'"); |
| } |
| |
| TEST_CASE("bwstring", "[bwprint][bwstring]") |
| { |
| std::string s; |
| ts::TextView fmt("{} -- {}"); |
| std::string_view text{"e99a18c428cb38d5f260853678922e03"}; |
| |
| ts::bwprint(s, fmt, "string", 956); |
| REQUIRE(s.size() == 13); |
| REQUIRE(s == "string -- 956"); |
| |
| ts::bwprint(s, fmt, 99999, text); |
| REQUIRE(s == "99999 -- e99a18c428cb38d5f260853678922e03"); |
| |
| ts::bwprint(s, "{} .. |{:,20}|", 32767, text); |
| REQUIRE(s == "32767 .. |e99a18c428cb38d5f260|"); |
| |
| ts::LocalBufferWriter<128> bw; |
| char buff[128]; |
| snprintf(buff, sizeof(buff), "|%s|", bw.print("Deep Silent Complete by {}\0", "Nightwish"sv).data()); |
| REQUIRE(std::string_view(buff) == "|Deep Silent Complete by Nightwish|"); |
| snprintf(buff, sizeof(buff), "|%s|", bw.reset().print("Deep Silent Complete by {}\0elided junk", "Nightwish"sv).data()); |
| REQUIRE(std::string_view(buff) == "|Deep Silent Complete by Nightwish|"); |
| |
| // Special tests for clang analyzer failures - special asserts are needed to make it happy but |
| // those can break functionality. |
| fmt = "Did you know? {}{} is {}"sv; |
| s.resize(0); |
| ts::bwprint(s, fmt, "Lady "sv, "Persia"sv, "not mean"); |
| REQUIRE(s == "Did you know? Lady Persia is not mean"); |
| s.resize(0); |
| ts::bwprint(s, fmt, ""sv, "Phil", "correct"); |
| REQUIRE(s == "Did you know? Phil is correct"); |
| s.resize(0); |
| ts::bwprint(s, fmt, std::string_view(), "Leif", "confused"); |
| REQUIRE(s == "Did you know? Leif is confused"); |
| |
| { |
| std::string out; |
| ts::bwprint(out, fmt, ""sv, "Phil", "correct"); |
| REQUIRE(out == "Did you know? Phil is correct"); |
| } |
| { |
| std::string out; |
| ts::bwprint(out, fmt, std::string_view(), "Leif", "confused"); |
| REQUIRE(out == "Did you know? Leif is confused"); |
| } |
| |
| char const *null_string{nullptr}; |
| ts::bwprint(s, "Null {0:x}.{0}", null_string); |
| REQUIRE(s == "Null 0x0."); |
| ts::bwprint(s, "Null {0:X}.{0}", nullptr); |
| REQUIRE(s == "Null 0X0."); |
| ts::bwprint(s, "Null {0:p}.{0:P}.{0:s}.{0:S}", null_string); |
| REQUIRE(s == "Null 0x0.0X0.null.NULL"); |
| } |
| |
| TEST_CASE("BWFormat integral", "[bwprint][bwformat]") |
| { |
| ts::LocalBufferWriter<256> bw; |
| ts::BWFSpec spec; |
| uint32_t num = 30; |
| int num_neg = -30; |
| |
| // basic |
| bwformat(bw, spec, num); |
| REQUIRE(bw.view() == "30"); |
| bw.reduce(0); |
| bwformat(bw, spec, num_neg); |
| REQUIRE(bw.view() == "-30"); |
| bw.reduce(0); |
| |
| // radix |
| ts::BWFSpec spec_hex; |
| spec_hex._radix_lead_p = true; |
| spec_hex._type = 'x'; |
| bwformat(bw, spec_hex, num); |
| REQUIRE(bw.view() == "0x1e"); |
| bw.reduce(0); |
| |
| ts::BWFSpec spec_dec; |
| spec_dec._type = '0'; |
| bwformat(bw, spec_dec, num); |
| REQUIRE(bw.view() == "30"); |
| bw.reduce(0); |
| |
| ts::BWFSpec spec_bin; |
| spec_bin._radix_lead_p = true; |
| spec_bin._type = 'b'; |
| bwformat(bw, spec_bin, num); |
| REQUIRE(bw.view() == "0b11110"); |
| bw.reduce(0); |
| |
| int one = 1; |
| int two = 2; |
| int three_n = -3; |
| // alignment |
| ts::BWFSpec left; |
| left._align = ts::BWFSpec::Align::LEFT; |
| left._min = 5; |
| ts::BWFSpec right; |
| right._align = ts::BWFSpec::Align::RIGHT; |
| right._min = 5; |
| ts::BWFSpec center; |
| center._align = ts::BWFSpec::Align::CENTER; |
| center._min = 5; |
| |
| bwformat(bw, left, one); |
| bwformat(bw, right, two); |
| REQUIRE(bw.view() == "1 2"); |
| bwformat(bw, right, two); |
| REQUIRE(bw.view() == "1 2 2"); |
| bwformat(bw, center, three_n); |
| REQUIRE(bw.view() == "1 2 2 -3 "); |
| |
| std::atomic<int> ax{0}; |
| bw.reset().print("ax == {}", ax); |
| REQUIRE(bw.view() == "ax == 0"); |
| ++ax; |
| bw.reset().print("ax == {}", ax); |
| REQUIRE(bw.view() == "ax == 1"); |
| } |
| |
| TEST_CASE("BWFormat floating", "[bwprint][bwformat]") |
| { |
| ts::LocalBufferWriter<256> bw; |
| ts::BWFSpec spec; |
| |
| bw.reduce(0); |
| bw.print("{}", 3.14); |
| REQUIRE(bw.view() == "3.14"); |
| bw.reduce(0); |
| bw.print("{} {:.2} {:.0} ", 32.7, 32.7, 32.7); |
| REQUIRE(bw.view() == "32.70 32.70 32 "); |
| bw.reduce(0); |
| bw.print("{} neg {:.3}", -123.2, -123.2); |
| REQUIRE(bw.view() == "-123.20 neg -123.200"); |
| bw.reduce(0); |
| bw.print("zero {} quarter {} half {} 3/4 {}", 0, 0.25, 0.50, 0.75); |
| REQUIRE(bw.view() == "zero 0 quarter 0.25 half 0.50 3/4 0.75"); |
| bw.reduce(0); |
| bw.print("long {:.11}", 64.9); |
| REQUIRE(bw.view() == "long 64.90000000000"); |
| bw.reduce(0); |
| |
| double n = 180.278; |
| double neg = -238.47; |
| bwformat(bw, spec, n); |
| REQUIRE(bw.view() == "180.28"); |
| bw.reduce(0); |
| bwformat(bw, spec, neg); |
| REQUIRE(bw.view() == "-238.47"); |
| bw.reduce(0); |
| |
| spec._prec = 5; |
| bwformat(bw, spec, n); |
| REQUIRE(bw.view() == "180.27800"); |
| bw.reduce(0); |
| bwformat(bw, spec, neg); |
| REQUIRE(bw.view() == "-238.47000"); |
| bw.reduce(0); |
| |
| float f = 1234; |
| float fneg = -1; |
| bwformat(bw, spec, f); |
| REQUIRE(bw.view() == "1234"); |
| bw.reduce(0); |
| bwformat(bw, spec, fneg); |
| REQUIRE(bw.view() == "-1"); |
| bw.reduce(0); |
| f = 1234.5667; |
| spec._prec = 4; |
| bwformat(bw, spec, f); |
| REQUIRE(bw.view() == "1234.5667"); |
| bw.reduce(0); |
| |
| bw << 1234 << .567; |
| REQUIRE(bw.view() == "12340.57"); |
| bw.reduce(0); |
| bw << f; |
| REQUIRE(bw.view() == "1234.57"); |
| bw.reduce(0); |
| bw << n; |
| REQUIRE(bw.view() == "180.28"); |
| bw.reduce(0); |
| bw << f << n; |
| REQUIRE(bw.view() == "1234.57180.28"); |
| bw.reduce(0); |
| |
| double edge = 0.345; |
| spec._prec = 3; |
| bwformat(bw, spec, edge); |
| REQUIRE(bw.view() == "0.345"); |
| bw.reduce(0); |
| edge = .1234; |
| bwformat(bw, spec, edge); |
| REQUIRE(bw.view() == "0.123"); |
| bw.reduce(0); |
| edge = 1.0; |
| bwformat(bw, spec, edge); |
| REQUIRE(bw.view() == "1"); |
| bw.reduce(0); |
| |
| // alignment |
| double first = 1.23; |
| double second = 2.35; |
| double third = -3.5; |
| ts::BWFSpec left; |
| left._align = ts::BWFSpec::Align::LEFT; |
| left._min = 5; |
| ts::BWFSpec right; |
| right._align = ts::BWFSpec::Align::RIGHT; |
| right._min = 5; |
| ts::BWFSpec center; |
| center._align = ts::BWFSpec::Align::CENTER; |
| center._min = 5; |
| |
| bwformat(bw, left, first); |
| bwformat(bw, right, second); |
| REQUIRE(bw.view() == "1.23 2.35"); |
| bwformat(bw, right, second); |
| REQUIRE(bw.view() == "1.23 2.35 2.35"); |
| bwformat(bw, center, third); |
| REQUIRE(bw.view() == "1.23 2.35 2.35-3.50"); |
| bw.reduce(0); |
| |
| double over = 1.4444444; |
| ts::BWFSpec over_min; |
| over_min._prec = 7; |
| over_min._min = 5; |
| bwformat(bw, over_min, over); |
| REQUIRE(bw.view() == "1.4444444"); |
| bw.reduce(0); |
| |
| // Edge |
| bw.print("{}", (1.0 / 0.0)); |
| REQUIRE(bw.view() == "Inf"); |
| bw.reduce(0); |
| |
| double inf = std::numeric_limits<double>::infinity(); |
| bw.print(" {} ", inf); |
| REQUIRE(bw.view() == " Inf "); |
| bw.reduce(0); |
| |
| double nan_1 = std::nan("1"); |
| bw.print("{} {}", nan_1, nan_1); |
| REQUIRE(bw.view() == "NaN NaN"); |
| bw.reduce(0); |
| |
| double z = 0.0; |
| bw.print("{} ", z); |
| REQUIRE(bw.view() == "0 "); |
| bw.reduce(0); |
| } |
| |
| TEST_CASE("bwstring std formats", "[libts][bwprint]") |
| { |
| ts::LocalBufferWriter<120> w; |
| |
| w.print("{}", ts::bwf::Errno(13)); |
| REQUIRE(w.view() == "EACCES: Permission denied [13]"sv); |
| w.reset().print("{}", ts::bwf::Errno(134)); |
| REQUIRE(w.view().substr(0, 9) == "Unknown: "sv); |
| |
| time_t t = 1528484137; |
| // default is GMT |
| w.reset().print("{} is {}", t, ts::bwf::Date(t)); |
| REQUIRE(w.view() == "1528484137 is 2018 Jun 08 18:55:37"); |
| w.reset().print("{} is {}", t, ts::bwf::Date(t, "%a, %d %b %Y at %H.%M.%S")); |
| REQUIRE(w.view() == "1528484137 is Fri, 08 Jun 2018 at 18.55.37"); |
| // OK to be explicit |
| w.reset().print("{} is {::gmt}", t, ts::bwf::Date(t)); |
| REQUIRE(w.view() == "1528484137 is 2018 Jun 08 18:55:37"); |
| w.reset().print("{} is {::gmt}", t, ts::bwf::Date(t, "%a, %d %b %Y at %H.%M.%S")); |
| REQUIRE(w.view() == "1528484137 is Fri, 08 Jun 2018 at 18.55.37"); |
| // Local time - set it to something specific or the test will be geographically sensitive. |
| setenv("TZ", "CST6", 1); |
| tzset(); |
| w.reset().print("{} is {::local}", t, ts::bwf::Date(t)); |
| REQUIRE(w.view() == "1528484137 is 2018 Jun 08 12:55:37"); |
| w.reset().print("{} is {::local}", t, ts::bwf::Date(t, "%a, %d %b %Y at %H.%M.%S")); |
| REQUIRE(w.view() == "1528484137 is Fri, 08 Jun 2018 at 12.55.37"); |
| |
| // Verify these compile and run, not really much hope to check output. |
| w.reset().print("|{}| |{}|", ts::bwf::Date(), ts::bwf::Date("%a, %d %b %Y")); |
| |
| w.reset().print("name = {}", ts::bwf::FirstOf("Persia")); |
| REQUIRE(w.view() == "name = Persia"); |
| w.reset().print("name = {}", ts::bwf::FirstOf("Persia", "Evil Dave")); |
| REQUIRE(w.view() == "name = Persia"); |
| w.reset().print("name = {}", ts::bwf::FirstOf("", "Evil Dave")); |
| REQUIRE(w.view() == "name = Evil Dave"); |
| w.reset().print("name = {}", ts::bwf::FirstOf(nullptr, "Evil Dave")); |
| REQUIRE(w.view() == "name = Evil Dave"); |
| w.reset().print("name = {}", ts::bwf::FirstOf("Persia", "Evil Dave", "Leif")); |
| REQUIRE(w.view() == "name = Persia"); |
| w.reset().print("name = {}", ts::bwf::FirstOf("Persia", nullptr, "Leif")); |
| REQUIRE(w.view() == "name = Persia"); |
| w.reset().print("name = {}", ts::bwf::FirstOf("", nullptr, "Leif")); |
| REQUIRE(w.view() == "name = Leif"); |
| |
| const char *empty{nullptr}; |
| std::string s1{"Persia"}; |
| std::string_view s2{"Evil Dave"}; |
| ts::TextView s3{"Leif"}; |
| w.reset().print("name = {}", ts::bwf::FirstOf(empty, s3)); |
| REQUIRE(w.view() == "name = Leif"); |
| w.reset().print("name = {}", ts::bwf::FirstOf(s2, s3)); |
| REQUIRE(w.view() == "name = Evil Dave"); |
| w.reset().print("name = {}", ts::bwf::FirstOf(s1, empty, s2)); |
| REQUIRE(w.view() == "name = Persia"); |
| w.reset().print("name = {}", ts::bwf::FirstOf(empty, s2, s1, s3)); |
| REQUIRE(w.view() == "name = Evil Dave"); |
| w.reset().print("name = {}", ts::bwf::FirstOf(empty, empty, s3, empty, s2, s1)); |
| REQUIRE(w.view() == "name = Leif"); |
| |
| unsigned v = ntohl(0xdeadbeef); |
| w.reset().print("{}", ts::bwf::Hex_Dump(v)); |
| REQUIRE(w.view() == "deadbeef"); |
| w.reset().print("{:x}", ts::bwf::Hex_Dump(v)); |
| REQUIRE(w.view() == "deadbeef"); |
| w.reset().print("{:X}", ts::bwf::Hex_Dump(v)); |
| REQUIRE(w.view() == "DEADBEEF"); |
| w.reset().print("{:#X}", ts::bwf::Hex_Dump(v)); |
| REQUIRE(w.view() == "0XDEADBEEF"); |
| w.reset().print("{} bytes {} digits {}", sizeof(double), std::numeric_limits<double>::digits10, ts::bwf::Hex_Dump(2.718281828)); |
| REQUIRE(w.view() == "8 bytes 15 digits 9b91048b0abf0540"); |
| |
| #if TS_ENABLE_FIPS == 0 |
| INK_MD5 md5; |
| w.reset().print("{}", ts::bwf::Hex_Dump(md5)); |
| REQUIRE(w.view() == "00000000000000000000000000000000"); |
| CryptoContext().hash_immediate(md5, s2.data(), s2.size()); |
| w.reset().print("{}", ts::bwf::Hex_Dump(md5)); |
| REQUIRE(w.view() == "f240ccd7a95c7ec66d6c111e2925b23e"); |
| #endif |
| } |
| |
| // Normally there's no point in running the performance tests, but it's worth keeping the code |
| // for when additional testing needs to be done. |
| #if 0 |
| TEST_CASE("bwperf", "[bwprint][performance]") |
| { |
| // Force these so I can easily change the set of tests. |
| auto start = std::chrono::high_resolution_clock::now(); |
| auto delta = std::chrono::high_resolution_clock::now() - start; |
| constexpr int N_LOOPS = 1000000; |
| |
| static constexpr const char * FMT = "Format |{:#010x}| '{}'"; |
| static constexpr ts::TextView fmt{FMT, strlen(FMT)}; |
| static constexpr std::string_view text{"e99a18c428cb38d5f260853678922e03"sv}; |
| ts::LocalBufferWriter<256> bw; |
| |
| ts::BWFSpec spec; |
| |
| bw.reduce(0); |
| bw.print(fmt, -956, text); |
| REQUIRE(bw.view() == "Format |-0x00003bc| 'e99a18c428cb38d5f260853678922e03'"); |
| |
| start = std::chrono::high_resolution_clock::now(); |
| for (int i = 0; i < N_LOOPS; ++i) { |
| bw.reduce(0); |
| bw.print(fmt, -956, text); |
| } |
| delta = std::chrono::high_resolution_clock::now() - start; |
| std::cout << "bw.print() " << delta.count() << "ns or " << std::chrono::duration_cast<std::chrono::milliseconds>(delta).count() |
| << "ms" << std::endl; |
| |
| ts::BWFormat pre_fmt(fmt); |
| start = std::chrono::high_resolution_clock::now(); |
| for (int i = 0; i < N_LOOPS; ++i) { |
| bw.reduce(0); |
| bw.print(pre_fmt, -956, text); |
| } |
| delta = std::chrono::high_resolution_clock::now() - start; |
| std::cout << "Preformatted: " << delta.count() << "ns or " |
| << std::chrono::duration_cast<std::chrono::milliseconds>(delta).count() << "ms" << std::endl; |
| |
| char buff[256]; |
| start = std::chrono::high_resolution_clock::now(); |
| for (int i = 0; i < N_LOOPS; ++i) { |
| snprintf(buff, sizeof(buff), "Format |%#0x10| '%.*s'", -956, static_cast<int>(text.size()), text.data()); |
| } |
| delta = std::chrono::high_resolution_clock::now() - start; |
| std::cout << "snprint Timing is " << delta.count() << "ns or " |
| << std::chrono::duration_cast<std::chrono::milliseconds>(delta).count() << "ms" << std::endl; |
| } |
| #endif |