--- nono/lib/test_mystring.cpp 2026/04/29 17:04:54 1.1 +++ nono/lib/test_mystring.cpp 2026/04/29 17:05:08 1.1.1.3 @@ -4,7 +4,6 @@ // Licensed under nono-license.txt // - #include #include #include @@ -38,12 +37,31 @@ ToDebugString(const std::string& x) return rv; } -static std::string +static std::string __unused ToDebugString(const char *x) { return ToDebugString(std::string { x }); } +// デバッグ文字列を返す +// std::vector +static std::string +ToDebugString(const std::vector& x) +{ + std::string rv; + + for (const auto& item : x) { + if (rv.empty()) { + rv += "{"; + } else { + rv += ","; + } + rv += ToDebugString(item); + } + rv += "}"; + return rv; +} + // Tuple with ToDebugString // 前方宣言 @@ -145,7 +163,7 @@ class TestClass } protected: - // 継承して、TESTDEF マクロを使って testlist を作ってください。 + // 継承して、TESTDEF マクロを使って testlist を作ること。 virtual void Init() = 0; // テスト関数のリスト @@ -175,28 +193,130 @@ class Test_MyString : public TestClass { protected: void Init() override { - // TESTDEF マクロに、テスト実行関数の識別子を渡します。 - TESTDEF(startwith_ignorecase_1); + // TESTDEF マクロに、テスト実行関数の識別子を渡すこと。 TESTDEF(string_ltrim_1); + TESTDEF(string_rtrim_1); + TESTDEF(rtrim_1); + TESTDEF(string_trim_1); + TESTDEF(startwith_ignorecase_1); + TESTDEF(string_split_1); + TESTDEF(string_split_2); + TESTDEF(format_number_1); + } + + void string_ltrim_1() { + TestTable table = { + { "", "" }, + { "a", "a" }, + { "abc", "abc" }, + { "a ", "a " }, + { " a", "a" }, + { "a \t", "a \t" }, + { "\t a", "a" }, + { " a ", "a " }, + { " abc ", "abc " }, + { "\r\na\r\n", "a\r\n" }, // 行頭の改行も空白文字になる + { " ", "" }, + }; + + for (const auto& t : table) { + const auto& src = t.get<0>(); + const auto& exp = t.get<1>(); + + xp_eq(exp, string_ltrim(src), t); + } + } + + void string_rtrim_1() { + TestTable table = { + { "", "" }, + { "a", "a" }, + { "abc", "abc" }, + { "a ", "a" }, + { " a", " a" }, + { "a \t", "a" }, + { "\t a", "\t a" }, + { " a ", " a" }, + { " abc ", " abc" }, + { "\r\na\r\n", "\r\na" }, + { " ", "" }, + }; + + for (const auto& t : table) { + auto src = t.get<0>(); + const auto& exp = t.get<1>(); + + string_rtrim(src); + xp_eq(exp, src, t); + } + } + + void rtrim_1() { + TestTable table = { + { "", "" }, + { "a", "a" }, + { "abc", "abc" }, + { "a ", "a" }, + { " a", " a" }, + { "a \t", "a" }, + { "\t a", "\t a" }, + { " a ", " a" }, + { " abc ", " abc" }, + { "\r\na\r\n", "\r\na" }, + { " ", "" }, + }; + + for (const auto& t : table) { + const auto& src = t.get<0>(); + const auto& exp = t.get<1>(); + + std::vector buf(src.size() + 1); + memcpy(buf.data(), src.c_str(), buf.size()); + rtrim(buf.data()); + xp_eq(exp, std::string(buf.data()), t); + } + } + + void string_trim_1() { + TestTable table = { + { "", "" }, + { "a", "a" }, + { "abc", "abc" }, + { "a ", "a" }, + { " a", "a" }, + { "a \t", "a" }, + { "\t a", "a" }, + { " a ", "a" }, + { " abc ", "abc" }, + { "\r\na\r\n", "a" }, + { " ", "" }, + }; + + for (const auto& t : table) { + const auto& src = t.get<0>(); + const auto& exp = t.get<1>(); + + xp_eq(exp, string_trim(src), t); + } } void startwith_ignorecase_1() { TestTable table = { - { "ABCDEF", "A", true }, - { "ABCDEF", "B", false }, - { "ABCDEF", "AB", true }, - { "ABCDEF", "BC", false }, - { "ABCDEF", "a", true }, - { "ABCDEF", "b", false }, - { "ABCDEF", "ab", true }, - { "ABCDEF", "bc", false }, - { "aBCDEF", "a", true }, - { "aBCDEF", "b", false }, - { "aBCDEF", "ab", true }, - { "aBCDEF", "ABcd", true }, - { "", "A", false }, - { "", "", true }, // ??? これはまだわかる - { "A", "", true }, // ??? 概念的には true だが果たして? + { "ABCDEF", "A", true }, + { "ABCDEF", "B", false }, + { "ABCDEF", "AB", true }, + { "ABCDEF", "BC", false }, + { "ABCDEF", "a", true }, + { "ABCDEF", "b", false }, + { "ABCDEF", "ab", true }, + { "ABCDEF", "bc", false }, + { "aBCDEF", "a", true }, + { "aBCDEF", "b", false }, + { "aBCDEF", "ab", true }, + { "aBCDEF", "ABcd", true }, + { "", "A", false }, + { "", "", true }, // ??? これはまだわかる + { "A", "", true }, // ??? 概念的には true だが果たして? }; for (auto t : table) { @@ -208,9 +328,78 @@ class Test_MyString : public TestClass } } - void string_ltrim_1() - { - Fail("NOT IMPL", ""); + void string_split_1() { + TestTable> table = { + { "", { } }, + { "ab", { "ab" } }, + { "ab,c", { "ab", "c" } }, + { "ab,", { "ab", "" } }, // separator が末尾 + { ",a,,", { "", "a", "", "" } }, // separator は連続しても独立 + { ",", { "", "" } }, // separator のみ + }; + + for (const auto& t : table) { + const auto& src = t.get<0>(); + const auto& exp = t.get<1>(); + + xp_eq(exp, string_split(src, ','), t); + } + } + + void string_split_2() { + TestTable> table = { + // 念のため等価のはず + { "", 0, { } }, + { "ab", 0, { "ab" } }, + { "ab,c", 0, { "ab", "c" } }, + { "ab,", 0, { "ab", "" } }, + { ",a,,", 0, { "", "a", "", "" } }, + { ",", 0, { "", "" } }, + + { "", 1, { } }, + { "ab", 1, { "ab" } }, + { "ab,c", 1, { "ab,c" } }, + { "ab,", 1, { "ab," } }, + { ",a,,", 1, { ",a,," } }, + { ",", 1, { "," } }, + + { "", 2, { } }, + { "ab", 2, { "ab" } }, + { "ab,c", 2, { "ab", "c" } }, + { "ab,", 2, { "ab", "" } }, + { "a,b,c", 2, { "a", "b,c" } }, + { ",a,,", 2, { "", "a,," } }, + { ",", 2, { "", "" } }, + }; + + for (const auto& t : table) { + const auto& src = t.get<0>(); + auto num = t.get<1>(); + const auto& exp = t.get<2>(); + + xp_eq(exp, string_split(src, ',', num), t); + } + } + + void format_number_1() { + TestTable table = { + { 0, "0" }, + { 1, "1" }, + { 999, "999" }, + { 1000, "1,000" }, + { 12345, "12,345" }, + { 123456, "123,456" }, + { 9876543, "9,876,543" }, + { 987654321, "987,654,321" }, + { 1000000000, "1,000,000,000" }, + }; + + for (const auto& t : table) { + auto val = t.get<0>(); + const auto& exp = t.get<1>(); + + xp_eq(exp, format_number(val), t); + } } };