blob: 7c7d7c0bb2628c7b45bb80c4717a0f465dbebf97 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
/** @file
Unit tests for BufferFormat and bwprint.
*/
#include <chrono>
#include <iostream>
#include <variant>
#include <netinet/in.h>
#include "swoc/MemSpan.h"
#include "swoc/BufferWriter.h"
#include "swoc/bwf_std.h"
#include "swoc/bwf_ex.h"
#include "catch.hpp"
using namespace std::literals;
using namespace swoc::literals;
using swoc::TextView;
using swoc::bwprint, swoc::bwappend;
TEST_CASE("Buffer Writer << operator", "[bufferwriter][stream]") {
swoc::LocalBufferWriter<50> bw;
bw << "The" << ' ' << "quick" << ' ' << "brown fox";
REQUIRE(bw.view() == "The quick brown fox");
bw.clear();
bw << "x=" << bw.capacity();
REQUIRE(bw.view() == "x=50");
}
TEST_CASE("bwprint basics", "[bwprint]") {
swoc::LocalBufferWriter<256> bw;
std::string_view fmt1{"Some text"sv};
swoc::bwf::Format fmt2("left >{0:<9}< right >{0:>9}< center >{0:^9}<");
std::string_view text{"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"};
static const swoc::bwf::Format bad_arg_fmt{"{{BAD_ARG_INDEX:{} of {}}}"};
bw.print(fmt1);
REQUIRE(bw.view() == fmt1);
bw.clear().print("Some text"); // check that a literal string works as expected.
REQUIRE(bw.view() == fmt1);
bw.clear().print("Some text"_tv); // check that a literal TextView works.
REQUIRE(bw.view() == fmt1);
bw.clear().print("Arg {}", 1);
REQUIRE(bw.view() == "Arg 1");
bw.clear().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.clear().print("args {2}{0}{1}", "zero", "one", "two");
REQUIRE(bw.view() == "args twozeroone");
bw.clear().print("left |{:<10}|", "text");
REQUIRE(bw.view() == "left |text |");
bw.clear().print("right |{:>10}|", "text");
REQUIRE(bw.view() == "right | text|");
bw.clear().print("right |{:.>10}|", "text");
REQUIRE(bw.view() == "right |......text|");
bw.clear().print("center |{:.^10}|", "text");
REQUIRE(bw.view() == "center |...text...|");
bw.clear().print("center |{:.^11}|", "text");
REQUIRE(bw.view() == "center |...text....|");
bw.clear().print("center |{:^^10}|", "text");
REQUIRE(bw.view() == "center |^^^text^^^|");
bw.clear().print("center |{:%3A^10}|", "text");
REQUIRE(bw.view() == "center |:::text:::|");
bw.clear().print("left >{0:<9}< right >{0:>9}< center >{0:^9}<", 956);
REQUIRE(bw.view() == "left >956 < right > 956< center > 956 <");
bw.clear().print("Format |{:>#010x}|", -956);
REQUIRE(bw.view() == "Format |0000-0x3bc|");
bw.clear().print("Format |{:<#010x}|", -956);
REQUIRE(bw.view() == "Format |-0x3bc0000|");
bw.clear().print("Format |{:#010x}|", -956);
REQUIRE(bw.view() == "Format |-0x00003bc|");
bw.clear().print("{{BAD_ARG_INDEX:{} of {}}}", 17, 23);
REQUIRE(bw.view() == "{BAD_ARG_INDEX:17 of 23}");
bw.clear().print("Arg {0} Arg {3}", 0, 1);
REQUIRE(bw.view() == "Arg 0 Arg {BAD_ARG_INDEX:3 of 2}");
bw.clear().print("{{stuff}} Arg {0} Arg {}", 0, 1, 2);
REQUIRE(bw.view() == "{stuff} Arg 0 Arg 0");
bw.clear().print("{{stuff}} Arg {0} Arg {} {}", 0, 1, 2);
REQUIRE(bw.view() == "{stuff} Arg 0 Arg 0 1");
bw.clear();
bw.print("Arg {0} Arg {} and {{stuff}}", 3, 4);
REQUIRE(bw.view() == "Arg 3 Arg 3 and {stuff}");
bw.clear().print("Arg {{{0}}} Arg {} and {{stuff}}", 5, 6);
REQUIRE(bw.view() == "Arg {5} Arg 5 and {stuff}");
bw.clear().print("Arg {{{0}}} Arg {} {1} {} {0} and {{stuff}}", 5, 6);
REQUIRE(bw.view() == "Arg {5} Arg 5 6 6 5 and {stuff}");
bw.clear();
bw.print("Arg {0} Arg {{}}{{}} {} and {} {{stuff}}", 7, 8);
REQUIRE(bw.view() == "Arg 7 Arg {}{} 7 and 8 {stuff}");
bw.clear();
bw.print("Arg {} Arg {{{{}}}} {} {1} {0}", 9, 10);
REQUIRE(bw.view() == "Arg 9 Arg {{}} 10 10 9");
bw.clear();
bw.print("Arg {} Arg {{{{}}}} {}", 9, 10);
REQUIRE(bw.view() == "Arg 9 Arg {{}} 10");
bw.clear().print(bad_arg_fmt, 17, 23);
REQUIRE(bw.view() == "{BAD_ARG_INDEX:17 of 23}");
bw.clear().print("{leif}");
REQUIRE(bw.view() == "{~leif~}"); // expected to be missing.
bw.clear().print(fmt2, 956);
REQUIRE(bw.view() == "left >956 < right > 956< center > 956 <");
// Check leading space printing.
bw.clear().print(" {}", fmt1);
REQUIRE(bw.view() == " Some text");
}
TEST_CASE("BWFormat numerics", "[bwprint][bwformat]") {
swoc::LocalBufferWriter<256> bw;
void *ptr = reinterpret_cast<void *>(0XBADD0956);
bw.clear();
bw.print("{}", ptr);
REQUIRE(bw.view() == "0xbadd0956");
bw.clear();
bw.print("{:X}", ptr);
REQUIRE(bw.view() == "0XBADD0956");
int *int_ptr = static_cast<int *>(ptr);
bw.clear();
bw.print("{}", int_ptr);
REQUIRE(bw.view() == "0xbadd0956");
char char_ptr[] = "delain";
bw.clear();
bw.print("{:x}", static_cast<char *>(ptr));
REQUIRE(bw.view() == "0xbadd0956");
bw.clear();
bw.print("{}", char_ptr);
REQUIRE(bw.view() == "delain");
swoc::MemSpan span{ptr, 0x200};
bw.clear().print("{}", span);
REQUIRE(bw.view() == "0x200@0xbadd0956");
swoc::MemSpan cspan{char_ptr, 6};
bw.clear().print("{:x}", cspan);
REQUIRE(bw.view() == "64 65 6c 61 69 6e");
bw.clear().print("{:#x}", cspan);
REQUIRE(bw.view() == "0x64 0x65 0x6c 0x61 0x69 0x6e");
bw.clear().print("{:#.2x}", cspan);
REQUIRE(bw.view() == "0x6465 0x6c61 0x696e");
bw.clear().print("{:x}", cspan.rebind());
REQUIRE(bw.view() == "64656c61696e");
TextView sv{"abc123"};
bw.clear();
bw.print("{}", sv);
REQUIRE(bw.view() == sv);
bw.clear();
bw.print("{:x}", sv);
REQUIRE(bw.view() == "616263313233");
bw.clear();
bw.print("{:#x}", sv);
REQUIRE(bw.view() == "0x616263313233");
bw.clear();
bw.print("|{:16x}|", sv);
REQUIRE(bw.view() == "|616263313233 |");
bw.clear();
bw.print("|{:>16x}|", sv);
REQUIRE(bw.view() == "| 616263313233|");
bw.clear().print("|{:^16x}|", sv);
REQUIRE(bw.view() == "| 616263313233 |");
bw.clear().print("|{:>16.2x}|", sv);
REQUIRE(bw.view() == "| 6162|");
// Substrings by argument adjustment.
bw.clear().print("|{:<0,7x}|", sv.prefix(4));
REQUIRE(bw.view() == "|6162633|");
bw.clear().print("|{:<5,7x}|", sv.prefix(2));
REQUIRE(bw.view() == "|6162 |");
bw.clear().print("|{:<5,7x}|", sv.prefix(3));
REQUIRE(bw.view() == "|616263|");
bw.clear().print("|{:<7x}|", sv.prefix(3));
REQUIRE(bw.view() == "|616263 |");
// Substrings by precision - should be same output.
bw.clear().print("|{:<0.4,7x}|", sv);
REQUIRE(bw.view() == "|6162633|");
bw.clear().print("|{:<5.2,7x}|", sv);
REQUIRE(bw.view() == "|6162 |");
bw.clear().print("|{:<5.3,7x}|", sv);
REQUIRE(bw.view() == "|616263|");
bw.clear().print("|{:<7.3x}|", sv);
REQUIRE(bw.view() == "|616263 |");
bw.clear();
bw.print("|{}|", true);
REQUIRE(bw.view() == "|1|");
bw.clear();
bw.print("|{}|", false);
REQUIRE(bw.view() == "|0|");
bw.clear();
bw.print("|{:s}|", true);
REQUIRE(bw.view() == "|true|");
bw.clear();
bw.print("|{:S}|", false);
REQUIRE(bw.view() == "|FALSE|");
bw.clear();
bw.print("|{:>9s}|", false);
REQUIRE(bw.view() == "| false|");
bw.clear();
bw.print("|{:^10s}|", true);
REQUIRE(bw.view() == "| true |");
// Test clipping a bit.
swoc::LocalBufferWriter<20> bw20;
bw20.print("0123456789abc|{:^10s}|", true);
REQUIRE(bw20.view() == "0123456789abc| tru");
bw20.clear();
bw20.print("012345|{:^10s}|6789abc", true);
REQUIRE(bw20.view() == "012345| true |67");
bw.clear().print("Char '{}'", 'a');
REQUIRE(bw.view() == "Char 'a'");
bw.clear().print("Byte '{}'", uint8_t{'a'});
REQUIRE(bw.view() == "Byte '97'");
}
TEST_CASE("bwstring", "[bwprint][bwappend][bwstring]") {
std::string s;
swoc::TextView fmt("{} -- {}");
std::string_view text{"e99a18c428cb38d5f260853678922e03"};
bwprint(s, fmt, "string", 956);
REQUIRE(s.size() == 13);
REQUIRE(s == "string -- 956");
bwprint(s, fmt, 99999, text);
REQUIRE(s == "99999 -- e99a18c428cb38d5f260853678922e03");
bwprint(s, "{} .. |{:,20}|", 32767, text);
REQUIRE(s == "32767 .. |e99a18c428cb38d5f260|");
swoc::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.clear().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);
bwprint(s, fmt, "Lady "sv, "Persia"sv, "not mean");
REQUIRE(s == "Did you know? Lady Persia is not mean");
s.resize(0);
bwprint(s, fmt, ""sv, "Phil", "correct");
REQUIRE(s == "Did you know? Phil is correct");
s.resize(0);
bwprint(s, fmt, std::string_view(), "Leif", "confused");
REQUIRE(s == "Did you know? Leif is confused");
{
std::string out;
bwprint(out, fmt, ""sv, "Phil", "correct");
REQUIRE(out == "Did you know? Phil is correct");
}
{
std::string out;
bwprint(out, fmt, std::string_view(), "Leif", "confused");
REQUIRE(out == "Did you know? Leif is confused");
}
char const *null_string{nullptr};
bwprint(s, "Null {0:x}.{0}", null_string);
REQUIRE(s == "Null 0x0.");
bwprint(s, "Null {0:X}.{0}", nullptr);
REQUIRE(s == "Null 0X0.");
bwprint(s, "Null {0:p}.{0:P}.{0:s}.{0:S}", null_string);
REQUIRE(s == "Null 0x0.0X0.null.NULL");
{
std::string x;
bwappend(x, "Phil");
REQUIRE(x == "Phil");
bwappend(x, " is {} most of the time", "correct"_tv);
REQUIRE(x == "Phil is correct most of the time");
x.resize(0); // try it with already sufficient capacity.
bwappend(x, "Dave");
REQUIRE(x == "Dave");
bwappend(x, " is {} some of the time", "correct"_tv);
REQUIRE(x == "Dave is correct some of the time");
}
}
TEST_CASE("BWFormat integral", "[bwprint][bwformat]") {
swoc::LocalBufferWriter<256> bw;
swoc::bwf::Spec spec;
uint32_t num = 30;
int num_neg = -30;
// basic
bwformat(bw, spec, num);
REQUIRE(bw.view() == "30");
bw.clear();
bwformat(bw, spec, num_neg);
REQUIRE(bw.view() == "-30");
bw.clear();
// radix
swoc::bwf::Spec spec_hex;
spec_hex._radix_lead_p = true;
spec_hex._type = 'x';
bwformat(bw, spec_hex, num);
REQUIRE(bw.view() == "0x1e");
bw.clear();
swoc::bwf::Spec spec_dec;
spec_dec._type = '0';
bwformat(bw, spec_dec, num);
REQUIRE(bw.view() == "30");
bw.clear();
swoc::bwf::Spec spec_bin;
spec_bin._radix_lead_p = true;
spec_bin._type = 'b';
bwformat(bw, spec_bin, num);
REQUIRE(bw.view() == "0b11110");
bw.clear();
int one = 1;
int two = 2;
int three_n = -3;
// alignment
swoc::bwf::Spec left;
left._align = swoc::bwf::Spec::Align::LEFT;
left._min = 5;
swoc::bwf::Spec right;
right._align = swoc::bwf::Spec::Align::RIGHT;
right._min = 5;
swoc::bwf::Spec center;
center._align = swoc::bwf::Spec::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.clear().print("ax == {}", ax);
REQUIRE(bw.view() == "ax == 0");
++ax;
bw.clear().print("ax == {}", ax);
REQUIRE(bw.view() == "ax == 1");
}
TEST_CASE("BWFormat floating", "[bwprint][bwformat]") {
swoc::LocalBufferWriter<256> bw;
swoc::bwf::Spec spec;
bw.clear();
bw.print("{}", 3.14);
REQUIRE(bw.view() == "3.14");
bw.clear();
bw.print("{} {:.2} {:.0} ", 32.7, 32.7, 32.7);
REQUIRE(bw.view() == "32.70 32.70 32 ");
bw.clear();
bw.print("{} neg {:.3}", -123.2, -123.2);
REQUIRE(bw.view() == "-123.20 neg -123.200");
bw.clear();
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.clear();
bw.print("long {:.11}", 64.9);
REQUIRE(bw.view() == "long 64.90000000000");
bw.clear();
double n = 180.278;
double neg = -238.47;
bwformat(bw, spec, n);
REQUIRE(bw.view() == "180.28");
bw.clear();
bwformat(bw, spec, neg);
REQUIRE(bw.view() == "-238.47");
bw.clear();
spec._prec = 5;
bwformat(bw, spec, n);
REQUIRE(bw.view() == "180.27800");
bw.clear();
bwformat(bw, spec, neg);
REQUIRE(bw.view() == "-238.47000");
bw.clear();
float f = 1234;
float fneg = -1;
bwformat(bw, spec, f);
REQUIRE(bw.view() == "1234");
bw.clear();
bwformat(bw, spec, fneg);
REQUIRE(bw.view() == "-1");
bw.clear();
f = 1234.5667;
spec._prec = 4;
bwformat(bw, spec, f);
REQUIRE(bw.view() == "1234.5667");
bw.clear();
bw << 1234 << .567;
REQUIRE(bw.view() == "12340.57");
bw.clear();
bw << f;
REQUIRE(bw.view() == "1234.57");
bw.clear();
bw << n;
REQUIRE(bw.view() == "180.28");
bw.clear();
bw << f << n;
REQUIRE(bw.view() == "1234.57180.28");
bw.clear();
double edge = 0.345;
spec._prec = 3;
bwformat(bw, spec, edge);
REQUIRE(bw.view() == "0.345");
bw.clear();
edge = .1234;
bwformat(bw, spec, edge);
REQUIRE(bw.view() == "0.123");
bw.clear();
edge = 1.0;
bwformat(bw, spec, edge);
REQUIRE(bw.view() == "1");
bw.clear();
// alignment
double first = 1.23;
double second = 2.35;
double third = -3.5;
swoc::bwf::Spec left;
left._align = swoc::bwf::Spec::Align::LEFT;
left._min = 5;
swoc::bwf::Spec right;
right._align = swoc::bwf::Spec::Align::RIGHT;
right._min = 5;
swoc::bwf::Spec center;
center._align = swoc::bwf::Spec::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.clear();
double over = 1.4444444;
swoc::bwf::Spec over_min;
over_min._prec = 7;
over_min._min = 5;
bwformat(bw, over_min, over);
REQUIRE(bw.view() == "1.4444444");
bw.clear();
// Edge
bw.print("{}", (1.0 / 0.0));
REQUIRE(bw.view() == "Inf");
bw.clear();
double inf = std::numeric_limits<double>::infinity();
bw.print(" {} ", inf);
REQUIRE(bw.view() == " Inf ");
bw.clear();
double nan_1 = std::nan("1");
bw.print("{} {}", nan_1, nan_1);
REQUIRE(bw.view() == "NaN NaN");
bw.clear();
double z = 0.0;
bw.print("{} ", z);
REQUIRE(bw.view() == "0 ");
bw.clear();
}
TEST_CASE("bwstring std formats", "[libswoc][bwprint]") {
std::string_view text{"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"};
swoc::LocalBufferWriter<120> w;
w.print("{}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "EACCES: Permission denied [13]"sv);
w.clear().print("{}", swoc::bwf::Errno(134));
REQUIRE(w.view().substr(0, 22) == "Unknown: Unknown error"sv);
w.clear().print("{:s}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "EACCES: Permission denied"sv);
w.clear().print("{:S}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "EACCES: Permission denied"sv);
w.clear().print("{:s:s}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "EACCES"sv);
w.clear().print("{:s:l}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "Permission denied"sv);
w.clear().print("{:s:sl}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "EACCES: Permission denied"sv);
w.clear().print("{:d}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "[13]"sv);
w.clear().print("{:g}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "EACCES: Permission denied [13]"sv);
w.clear().print("{:g:s}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "EACCES [13]"sv);
w.clear().print("{::s}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "EACCES [13]"sv);
w.clear().print("{::l}", swoc::bwf::Errno(13));
REQUIRE(w.view() == "Permission denied [13]"sv);
time_t t = 1528484137;
// default is GMT
w.clear().print("{} is {}", t, swoc::bwf::Date(t));
REQUIRE(w.view() == "1528484137 is 2018 Jun 08 18:55:37");
w.clear().print("{} is {}", t, swoc::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.clear().print("{} is {::gmt}", t, swoc::bwf::Date(t));
REQUIRE(w.view() == "1528484137 is 2018 Jun 08 18:55:37");
w.clear().print("{} is {::gmt}", t, swoc::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.clear().print("{} is {::local}", t, swoc::bwf::Date(t));
REQUIRE(w.view() == "1528484137 is 2018 Jun 08 12:55:37");
w.clear().print("{} is {::local}", t, swoc::bwf::Date(t, "%a, %d %b %Y at %H.%M.%S"));
REQUIRE(w.view() == "1528484137 is Fri, 08 Jun 2018 at 12.55.37");
unsigned v = htonl(0xdeadbeef);
w.clear().print("{}", swoc::bwf::As_Hex(v));
REQUIRE(w.view() == "deadbeef");
w.clear().print("{:x}", swoc::bwf::As_Hex(v));
REQUIRE(w.view() == "deadbeef");
w.clear().print("{:X}", swoc::bwf::As_Hex(v));
REQUIRE(w.view() == "DEADBEEF");
w.clear().print("{:#X}", swoc::bwf::As_Hex(v));
REQUIRE(w.view() == "0XDEADBEEF");
w.clear().print("{} bytes {} digits {}", sizeof(double), std::numeric_limits<double>::digits10, swoc::bwf::As_Hex(2.718281828));
REQUIRE(w.view() == "8 bytes 15 digits 9b91048b0abf0540");
// Verify these compile and run, not really much hope to check output.
w.clear().print("|{}| |{}|", swoc::bwf::Date(), swoc::bwf::Date("%a, %d %b %Y"));
w.clear().print("name = {}", swoc::bwf::FirstOf("Persia"));
REQUIRE(w.view() == "name = Persia");
w.clear().print("name = {}", swoc::bwf::FirstOf("Persia", "Evil Dave"));
REQUIRE(w.view() == "name = Persia");
w.clear().print("name = {}", swoc::bwf::FirstOf("", "Evil Dave"));
REQUIRE(w.view() == "name = Evil Dave");
w.clear().print("name = {}", swoc::bwf::FirstOf(nullptr, "Evil Dave"));
REQUIRE(w.view() == "name = Evil Dave");
w.clear().print("name = {}", swoc::bwf::FirstOf("Persia", "Evil Dave", "Leif"));
REQUIRE(w.view() == "name = Persia");
w.clear().print("name = {}", swoc::bwf::FirstOf("Persia", nullptr, "Leif"));
REQUIRE(w.view() == "name = Persia");
w.clear().print("name = {}", swoc::bwf::FirstOf("", nullptr, "Leif"));
REQUIRE(w.view() == "name = Leif");
const char *empty{nullptr};
std::string s1{"Persia"};
std::string_view s2{"Evil Dave"};
swoc::TextView s3{"Leif"};
w.clear().print("name = {}", swoc::bwf::FirstOf(empty, s3));
REQUIRE(w.view() == "name = Leif");
w.clear().print("name = {}", swoc::bwf::FirstOf(s2, s3));
REQUIRE(w.view() == "name = Evil Dave");
w.clear().print("name = {}", swoc::bwf::FirstOf(s1, empty, s2));
REQUIRE(w.view() == "name = Persia");
w.clear().print("name = {}", swoc::bwf::FirstOf(empty, s2, s1, s3));
REQUIRE(w.view() == "name = Evil Dave");
w.clear().print("name = {}", swoc::bwf::FirstOf(empty, empty, s3, empty, s2, s1));
REQUIRE(w.view() == "name = Leif");
w.clear().print("Lower - |{:s}|", text);
REQUIRE(w.view() == "Lower - |0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz|");
w.clear().print("Upper - |{:S}|", text);
REQUIRE(w.view() == "Upper - |0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ|");
w.clear().print("Leading{}{}{}.", swoc::bwf::Optional(" | {} |", s1), swoc::bwf::Optional(" <{}>", empty), swoc::bwf::If(!s3.empty(), " [{}]", s3));
REQUIRE(w.view() == "Leading | Persia | [Leif].");
// Do it again, but this time as C strings (char * variants).
w.clear().print("Leading{}{}{}.", swoc::bwf::Optional(" | {} |", s3.data()), swoc::bwf::Optional(" <{}>", empty), swoc::bwf::If(!s3.empty(), " [{}]", s1.c_str()));
REQUIRE(w.view() == "Leading | Leif | [Persia].");
// Play with string_view
w.clear().print("Clone?{}{}.", swoc::bwf::Optional(" #. {}", s2), swoc::bwf::Optional(" #. {}", s2.data()));
REQUIRE(w.view() == "Clone? #. Evil Dave #. Evil Dave.");
s2 = "";
w.clear().print("Leading{}{}{}", swoc::bwf::If(true, " true"), swoc::bwf::If(false, " false"), swoc::bwf::If(true, " Persia"));
REQUIRE(w.view() == "Leading true Persia");
// Differentiate because the C string variant will generate output, as it's not nullptr,
// but is a pointer to an empty string.
w.clear().print("Clone?{}{}.", swoc::bwf::Optional(" 1. {}", s2), swoc::bwf::Optional(" 2. {}", s2.data()));
REQUIRE(w.view() == "Clone? 2. .");
s2 = std::string_view{};
w.clear().print("Clone?{}{}.", swoc::bwf::Optional(" #. {}", s2), swoc::bwf::Optional(" #. {}", s2.data()));
REQUIRE(w.view() == "Clone?.");
};
// 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 swoc::TextView fmt{FMT, strlen(FMT)};
static constexpr std::string_view text{"e99a18c428cb38d5f260853678922e03"sv};
swoc::LocalBufferWriter<256> bw;
swoc::bwf::Spec spec;
bw.clear();
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.clear();
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;
swoc::bwf::Format pre_fmt(fmt);
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < N_LOOPS; ++i) {
bw.clear();
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