libcruft-util/test/string.cpp
Danny Robson b0ac349f70 string: fancy comparators should take only string_views
This reduces complications dealing with implicit casts from things like
'std::string' which are fairly common.
2020-10-21 10:23:04 +10:00

231 lines
6.1 KiB
C++

#include "tap.hpp"
#include "string.hpp"
#include "types.hpp"
#include "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, %!", msg);
tap.expect_eq (cruft::to_lower (src), lower, "to_lower, %!", 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
);
}
}
///////////////////////////////////////////////////////////////////////////////
void
test_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_comparator_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::compare::lower cmp;
for (auto const &t: TESTS) {
tap.expect_eq (cmp (t.a, t.b), t.equal, "compare::lower, pointers, %!", t.message);
}
}
///////////////////////////////////////////////////////////////////////////////
int
main (int, char**)
{
cruft::TAP::logger tap;
test_transforms (tap);
test_tokeniser (tap);
test_position (tap);
test_contains (tap);
test_comparator_less (tap);
test_comparator_lower (tap);
return tap.status ();
}