diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 7cbfffc0a..df62a2d57 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -12,8 +12,6 @@ #include #include -#include - #include #include #include @@ -237,13 +235,13 @@ namespace llarp if(idx != std::string::npos) { std::string data = v.substr(0, idx); - absl::StripAsciiWhitespace(&data); + TrimWhiteSpace(data); parsed_opts.emplace(std::move(data)); v = v.substr(idx + 1); } else { - absl::StripAsciiWhitespace(&v); + TrimWhiteSpace(v); parsed_opts.insert(std::move(v)); } } while(idx != std::string::npos); diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 3806c6da1..ef6a48578 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -18,8 +18,6 @@ #include -#include - namespace llarp { namespace handlers @@ -193,7 +191,7 @@ namespace llarp { routerStr = v; } - absl::StripAsciiWhitespace(&routerStr); + TrimWhiteSpace(routerStr); if(!(exitRouter.FromString(routerStr) || HexDecode(routerStr.c_str(), exitRouter.begin(), exitRouter.size()))) diff --git a/llarp/util/str.cpp b/llarp/util/str.cpp index 5a6c6e874..5676c222b 100644 --- a/llarp/util/str.cpp +++ b/llarp/util/str.cpp @@ -60,4 +60,24 @@ namespace llarp return false; } + + constexpr static char whitespace[] = " \t\n\r\f\v"; + void + TrimWhiteSpace(std::string& str) + { + size_t begin = str.find_first_not_of(whitespace); + if(begin == std::string::npos) + { + str.clear(); + return; + } + size_t end = str.find_last_not_of(whitespace) + 1; + if(begin == 0) + str.resize(end); + else + { + std::copy(str.begin() + begin, str.begin() + end, str.begin()); + str.resize(end - begin); + } + } } // namespace llarp diff --git a/llarp/util/str.hpp b/llarp/util/str.hpp index e09617287..fdb006233 100644 --- a/llarp/util/str.hpp +++ b/llarp/util/str.hpp @@ -20,6 +20,11 @@ namespace llarp bool IsTrueValue(string_view str); + /// Trim leading and trailing (ascii) whitespace from the given string; the + /// string is modified in-place. + void + TrimWhiteSpace(std::string &str); + } // namespace llarp #endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0c7a1bbc3..c234c0d83 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,7 +39,6 @@ list(APPEND TEST_SRC util/test_llarp_util_decaying_hashset.cpp util/test_llarp_util_encode.cpp util/test_llarp_util_printer.cpp - util/test_llarp_utils_str.cpp util/test_llarp_util_log_level.cpp util/thread/test_llarp_util_queue_manager.cpp util/thread/test_llarp_util_queue.cpp @@ -75,6 +74,7 @@ add_subdirectory(Catch2) add_executable(${CHECK_EXE} nodedb/test_nodedb.cpp path/test_path.cpp + util/test_llarp_util_str.cpp check_main.cpp) target_link_libraries(${CHECK_EXE} PUBLIC ${STATIC_LIB} Catch2::Catch2) diff --git a/test/util/test_llarp_util_str.cpp b/test/util/test_llarp_util_str.cpp new file mode 100644 index 000000000..d06ca3db1 --- /dev/null +++ b/test/util/test_llarp_util_str.cpp @@ -0,0 +1,89 @@ +#include +#include + +using namespace std::literals; + +TEST_CASE("TrimWhiteSpace -- positive tests", "[str][trim]") +{ + // Test that things that should be trimmed actually get trimmed + auto fee = " J a c k"s; + auto fi = "\ra\nd"s; + auto fo = "\fthe "s; + auto fum = " \t\r\n\v\f Beanstalk\n\n\n\t\r\f\v \n\n\r\f\f\f\f\v"s; + for (auto* s: {&fee, &fi, &fo, &fum}) + llarp::TrimWhiteSpace(*s); + + REQUIRE( fee == "J a c k" ); + REQUIRE( fi == "a\nd" ); + REQUIRE( fo == "the" ); + REQUIRE( fum == "Beanstalk" ); +} + +TEST_CASE("TrimWhitespace -- negative tests", "[str][trim]") +{ + // Test that things that shouldn't be trimmed don't get trimmed + auto c = GENERATE(range(std::numeric_limits::min(), std::numeric_limits::max())); + std::string plant = c + "bean"s + c; + llarp::TrimWhiteSpace(plant); + if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' || c == '\v') + REQUIRE( plant == "bean" ); + else + { + REQUIRE( plant.size() == 6 ); + REQUIRE( plant.substr(1, 4) == "bean" ); + } +} + +TEST_CASE("caseless comparison tests - less than", "[str][lt]") { + using namespace llarp; + CaselessLessThan lt; + auto expect_less_than = GENERATE(table({ + {"", "1"}, + {"1", "11"}, + {"abc", "abcd"}, + {"ABC", "abcd"}, + {"abc", "ABCD"}, + {"abc", "Abcd"}, + {"abc", "abcD"}, + {"abc", "abCd"}, + {"abc", "zz"}, + {"abc", "zzzz"}, + {"abc", "abd"}, + {"abc", "aBd"}, + {"abc", "abD"}, + {"ABC", "abd"}, + {"abC", "abd"}, + })); + REQUIRE( lt(std::get<0>(expect_less_than), std::get<1>(expect_less_than)) ); + REQUIRE( !lt(std::get<1>(expect_less_than), std::get<0>(expect_less_than)) ); +} + +TEST_CASE("caseless comparison tests - equality", "[str][eq]") { + using namespace llarp; + CaselessLessThan lt; + auto expect_less_than = GENERATE(table({ + {"1", "1"}, + {"a", "A"}, + {"abc", "ABC"}, + {"abc", "aBc"}, + {"ABC", "abc"}, + })); + REQUIRE( !lt(std::get<0>(expect_less_than), std::get<1>(expect_less_than)) ); + REQUIRE( !lt(std::get<1>(expect_less_than), std::get<0>(expect_less_than)) ); +} + +TEST_CASE("truthy string values", "[str][truthy]") { + auto val = GENERATE("true", "TruE", "yes", "yeS", "yES", "yes", "YES", "1", "on", "oN", "ON"); + REQUIRE( llarp::IsTrueValue(val) ); +} + +TEST_CASE("falsey string values", "[str][falsey]") { + auto val = GENERATE("false", "FalSe", "no", "NO", "No", "nO", "0", "off", "OFF"); + REQUIRE( llarp::IsFalseValue(val) ); +} + +TEST_CASE("neither true nor false string values", "[str][nottruefalse]") { + auto val = GENERATE("false y", "maybe", "not on", "2", "yesno", "YESNO", "-1", "default", "OMG"); + REQUIRE( !llarp::IsTrueValue(val) ); + REQUIRE( !llarp::IsFalseValue(val) ); +} diff --git a/test/util/test_llarp_utils_str.cpp b/test/util/test_llarp_utils_str.cpp deleted file mode 100644 index 7836c3132..000000000 --- a/test/util/test_llarp_utils_str.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include - -#include -#include - -using namespace llarp; -using namespace ::testing; - -struct CmpTestData -{ - bool lt; - std::string lhs; - std::string rhs; -}; - -class CaselessCmpTest : public ::testing::TestWithParam< CmpTestData > -{ -}; - -TEST_P(CaselessCmpTest, test) -{ - CaselessCmp cmp; - auto d = GetParam(); - ASSERT_EQ(d.lt, cmp(d.lhs, d.rhs)); -} - -std::vector< CmpTestData > CMPTESTDATA{ - {true, "", "1"}, {false, "1", ""}, {true, "abc", "abcd"}, - {true, "abc", "abd"}, {false, "11", "1"}, {false, "a", "A"}, - {false, "abc", "aBc"}, {false, "ABC", "abc"}}; - -INSTANTIATE_TEST_SUITE_P(TestStr, CaselessCmpTest, ValuesIn(CMPTESTDATA)); - -using TestData = std::pair< bool, std::string >; - -class TestIsFalseValue : public ::testing::TestWithParam< TestData > -{ -}; - -TEST_P(TestIsFalseValue, test) -{ - ASSERT_EQ(GetParam().first, IsFalseValue(GetParam().second)); -} - -std::vector< TestData > FALSE_DATA{ - {true, "false"}, {true, "FaLsE"}, {true, "no"}, {true, "nO"}, - {true, "No"}, {true, "NO"}, {true, "NO"}, {true, "0"}, - {true, "off"}, {true, "oFF"}, {false, "false y"}, {false, "true"}, - {false, "tRue"}, {false, "on"}}; - -INSTANTIATE_TEST_SUITE_P(TestStr, TestIsFalseValue, ValuesIn(FALSE_DATA)); - -class TestIsTrueValue : public ::testing::TestWithParam< TestData > -{ -}; - -TEST_P(TestIsTrueValue, test) -{ - ASSERT_EQ(GetParam().first, IsTrueValue(GetParam().second)); -} - -std::vector< TestData > TRUE_DATA{ - {true, "true"}, {true, "TruE"}, {true, "yes"}, {true, "yeS"}, - {true, "yES"}, {true, "YES"}, {true, "1"}, {false, "0"}, - {true, "on"}, {true, "oN"}, {false, "false y"}, {false, "truth"}, - {false, "false"}, {false, "off"}}; - -INSTANTIATE_TEST_SUITE_P(TestStr, TestIsTrueValue, ValuesIn(TRUE_DATA));