#include "tap.hpp" #include "string.hpp" #include "types.hpp" #include "iterator/zip.hpp" #include /////////////////////////////////////////////////////////////////////////////// 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 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 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 (); }