332 lines
9.1 KiB
C++
332 lines
9.1 KiB
C++
#include <cruft/util/tap.hpp>
|
|
#include <cruft/util/string.hpp>
|
|
#include <cruft/util/types.hpp>
|
|
#include <cruft/util/iterator/zip.hpp>
|
|
|
|
#include <vector>
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
test_transforms (cruft::TAP::logger &tap)
|
|
{
|
|
static struct {
|
|
std::string initial;
|
|
std::string upper;
|
|
std::string lower;
|
|
|
|
char const *message;
|
|
} const TESTS[] = {
|
|
{ "a", "A", "a", "single lower character" },
|
|
{ "A", "A", "a", "single upper character" },
|
|
{ "asdf", "ASDF", "asdf", "four lower characters" },
|
|
{ "ASDF", "ASDF", "asdf", "four upper characters" },
|
|
|
|
{ "Don't.", "DON'T.", "don't.", "upper and lower and symbols" },
|
|
};
|
|
|
|
for (auto const &[src, upper, lower, msg]: TESTS) {
|
|
tap.expect_eq (cruft::to_upper (src), upper, "to_upper, {:s}", msg);
|
|
tap.expect_eq (cruft::to_lower (src), lower, "to_lower, {:s}", msg);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
test_position (cruft::TAP::logger &tap)
|
|
{
|
|
char const *string = "a\nb\nc\n";
|
|
|
|
struct {
|
|
int offset;
|
|
|
|
int line;
|
|
int column;
|
|
} TESTS[] = {
|
|
{ 0, 0, 0 },
|
|
{ 1, 1, -1 },
|
|
{ 2, 1, 0 },
|
|
{ 3, 2, -1 },
|
|
{ 4, 2, 0 },
|
|
{ 5, 3, -1 },
|
|
};
|
|
|
|
for (auto const &t: TESTS) {
|
|
auto const pos = cruft::character_position ({string, strlen(string) }, string + t.offset);
|
|
|
|
tap.expect (
|
|
pos.line == t.line && pos.column == t.column,
|
|
"character_position {}:{}",
|
|
t.line,
|
|
t.column
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
static void
|
|
test_short_tokeniser (cruft::TAP::logger &tap)
|
|
{
|
|
struct {
|
|
char const *src;
|
|
std::vector<std::string_view> parts;
|
|
char const *message;
|
|
} TESTS[] = {
|
|
{ "foo", { "foo" }, "no separator" },
|
|
{ "foo bar", { "foo", "bar" }, "one separator" },
|
|
{ " foo", { "", "foo" }, "leading separator" },
|
|
{ "foo ", { "foo", "" }, "trailing separator" },
|
|
{ "", { }, "no separators" },
|
|
{ " ", { "", "" }, "only separators, single" },
|
|
{ " ", { "", "", "" }, "only separators, double" },
|
|
};
|
|
|
|
for (auto const &[src, expected, message]: TESTS) {
|
|
std::vector<std::string_view> computed;
|
|
cruft::tokeniser const tok (src, ' ');
|
|
std::transform (
|
|
std::begin (tok),
|
|
std::end (tok),
|
|
std::back_inserter (computed),
|
|
[] (auto const i)
|
|
{
|
|
return std::string_view (i.begin (), i.size ());
|
|
});
|
|
|
|
tap.expect_eq (expected, computed, "tokeniser: {}", message);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static void
|
|
test_long_tokeniser (cruft::TAP::logger &tap)
|
|
{
|
|
// the string_literal prefix is required to (easily) construct a string
|
|
// with an internal null character.
|
|
using namespace std::literals::string_literals;
|
|
const std::string csv = "\0,a,123,,this is a test,"s;
|
|
|
|
// expected test data must be a std::string so we can check embedded
|
|
// nulls (which are ambiguous when using a cstr).
|
|
struct foo {
|
|
const std::string value;
|
|
const char *message;
|
|
} TESTS[] = {
|
|
{ "\0"s, "null" },
|
|
{ "a", "single letter" },
|
|
{ "123", "three digits" },
|
|
{ "", "empty string" },
|
|
{ "this is a test", "string with spaces" },
|
|
{ "", "trailing empty" }
|
|
};
|
|
|
|
cruft::view src { csv.c_str (), csv.size () };
|
|
for (auto [tok, expected]: cruft::iterator::zip (cruft::tokeniser (src, ','), TESTS)) {
|
|
tap.expect (equal (tok, expected.value), "{:s}", expected.message);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void
|
|
test_contains (cruft::TAP::logger &tap)
|
|
{
|
|
struct {
|
|
char const *haystack;
|
|
char separator;
|
|
char const *needle;
|
|
bool expected;
|
|
char const *message;
|
|
} const TESTS[] = {
|
|
{
|
|
.haystack = "foo",
|
|
.separator = ',',
|
|
.needle = "foo",
|
|
.expected = true,
|
|
.message = "needle is haystack"
|
|
},
|
|
|
|
{
|
|
.haystack = "",
|
|
.separator = ',',
|
|
.needle = "foo",
|
|
.expected = false,
|
|
.message = "haystack is empty",
|
|
},
|
|
|
|
{
|
|
.haystack = "foo",
|
|
.separator = ',',
|
|
.needle = "",
|
|
.expected = false,
|
|
.message = "needle is empty",
|
|
},
|
|
|
|
{
|
|
.haystack = "a,b,foo",
|
|
.separator = ',',
|
|
.needle = "foo",
|
|
.expected = true,
|
|
.message = "needle is last",
|
|
},
|
|
|
|
{
|
|
.haystack = "foo,b,c",
|
|
.separator = ',',
|
|
.needle = "foo",
|
|
.expected = true,
|
|
.message = "needle is first",
|
|
},
|
|
{
|
|
.haystack = "foo,b,c",
|
|
.separator = ';',
|
|
.needle = "foo",
|
|
.expected = false,
|
|
.message = "separator is mismatched",
|
|
},
|
|
{
|
|
.haystack = "a;b;foo",
|
|
.separator = ';',
|
|
.needle = "foo",
|
|
.expected = true,
|
|
.message = "separator isn't default"
|
|
}
|
|
};
|
|
|
|
for (auto const &t: TESTS) {
|
|
cruft::tokeniser tok (t.haystack, t.separator);
|
|
auto const found = tok.contains (t.needle);
|
|
tap.expect_eq (found, t.expected, "{}", t.message);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void test_comparator_less (cruft::TAP::logger &tap)
|
|
{
|
|
cruft::string_less cmp;
|
|
|
|
struct {
|
|
char const *a;
|
|
char const *b;
|
|
bool less;
|
|
char const *message;
|
|
} const TESTS[] = {
|
|
{ "a", "b", true, "single char less-than" },
|
|
{ "b", "a", false, "single char not-less-than" },
|
|
{ "a", "aa", true, "single/double char less-than" },
|
|
};
|
|
|
|
for (auto const &t: TESTS)
|
|
tap.expect_eq (cmp (t.a, t.b), t.less, "string_less, {}", t.message);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void test_less_lower (cruft::TAP::logger &tap)
|
|
{
|
|
struct {
|
|
char const *a;
|
|
char const *b;
|
|
bool expected;
|
|
char const *message;
|
|
} const TESTS[] = {
|
|
{ "a", "b", true, "single letter lhs" },
|
|
{ "b", "a", false, "single letter rhs" },
|
|
|
|
{ "a", "a", false, "single lower equal" },
|
|
{ "a", "A", false, "lower, upper singles" },
|
|
{ "A", "a", false, "upper, lower singles" },
|
|
|
|
{ "a", "aa", true, "shorter lhs" },
|
|
{ "aa", "a", false, "shorter hhs" },
|
|
|
|
{ "date", "User-Agent", true, "lower lhs, upper rhs" },
|
|
};
|
|
|
|
cruft::string::less_lower cmp;
|
|
|
|
for (auto const &t: TESTS) {
|
|
tap.expect_eq (cmp (t.a, t.b), t.expected, "less::lower, pointers, {}", t.message);
|
|
tap.expect_eq (
|
|
cmp (
|
|
std::string_view {t.a},
|
|
std::string_view {t.b}
|
|
),
|
|
t.expected,
|
|
"less::lower, string_view, {}",
|
|
t.message
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void test_equality_lower (cruft::TAP::logger &tap)
|
|
{
|
|
struct {
|
|
char const *a;
|
|
char const *b;
|
|
bool equal;
|
|
char const *message;
|
|
} const TESTS[] = {
|
|
{ "a", "a", true, "both single lower" },
|
|
{ "a", "A", true, "lower, upper singles" },
|
|
{ "A", "a", true, "upper, lower singles" },
|
|
{ "AbcDEf", "aBcdEF", true, "mixed upper lower" },
|
|
{ "A", "aa", false, "mixed case, different lengths" },
|
|
};
|
|
|
|
cruft::string::equality::lower cmp;
|
|
|
|
for (auto const &t: TESTS)
|
|
tap.expect_eq (cmp (t.a, t.b), t.equal, "equality::lower, pointers, {}", t.message);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
static void
|
|
test_strip (cruft::TAP::logger &tap)
|
|
{
|
|
static constexpr struct {
|
|
char const *val;
|
|
char const *expected_l;
|
|
char const *expected_r;
|
|
char const *msg;
|
|
} TESTS[] = {
|
|
{ "asdf", "asdf", "asdf", "no stripping" },
|
|
{ "", "", "", "empty string" },
|
|
{ " ", "", "", "only space" },
|
|
{ " \nasdf", "asdf", " \nasdf", "left space" },
|
|
{ "asdf \n", "asdf \n", "asdf", "right space" },
|
|
{ " asdf ", "asdf ", " asdf", "both space" },
|
|
};
|
|
|
|
for (auto const &t: TESTS) {
|
|
tap.expect_eq (t.expected_l, cruft::lstrip (t.val), "lstrip: {}", t.msg);
|
|
tap.expect_eq (t.expected_r, cruft::rstrip (t.val), "rstrip: {}", t.msg);
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
int
|
|
main (int, char**)
|
|
{
|
|
cruft::TAP::logger tap;
|
|
|
|
test_transforms (tap);
|
|
test_short_tokeniser (tap);
|
|
test_long_tokeniser (tap);
|
|
test_position (tap);
|
|
test_contains (tap);
|
|
test_comparator_less (tap);
|
|
test_less_lower (tap);
|
|
test_equality_lower (tap);
|
|
test_strip (tap);
|
|
|
|
return tap.status ();
|
|
} |