From ac46512b05cc859b87634c8413e785fefd181263 Mon Sep 17 00:00:00 2001 From: qining Date: Wed, 27 Jul 2016 17:22:45 -0400 Subject: [PATCH] Add an utility: in-place string replacement --- test/opt/pass_utils.cpp | 26 ++++++++++++++++----- test/opt/pass_utils.h | 12 ++++++++++ test/opt/test_utils.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/test/opt/pass_utils.cpp b/test/opt/pass_utils.cpp index a6f2374..68bc1a4 100644 --- a/test/opt/pass_utils.cpp +++ b/test/opt/pass_utils.cpp @@ -43,7 +43,27 @@ const char* kDebugOpcodes[] = { // clang-format on }; -// Returns true if the given string contains any debug opcode substring. +} // anonymous namespace + +namespace spvtools { + +bool FindAndReplace(std::string* process_str, const std::string find_str, + const std::string replace_str) { + if (process_str->empty() || find_str.empty()) { + return false; + } + bool replaced = false; + // Note this algorithm has quadratic time complexity. It is OK for test cases + // with short strings, but might not fit in other contexts. + for (size_t pos = process_str->find(find_str, 0); pos != std::string::npos; + pos = process_str->find(find_str, pos)) { + process_str->replace(pos, find_str.length(), replace_str); + pos += replace_str.length(); + replaced = true; + } + return replaced; +} + bool ContainsDebugOpcode(const char* inst) { return std::any_of(std::begin(kDebugOpcodes), std::end(kDebugOpcodes), [inst](const char* op) { @@ -51,10 +71,6 @@ bool ContainsDebugOpcode(const char* inst) { }); } -} // anonymous namespace - -namespace spvtools { - std::string SelectiveJoin(const std::vector& strings, const std::function& skip_dictator, char delimiter) { diff --git a/test/opt/pass_utils.h b/test/opt/pass_utils.h index 6e047db..f504eb5 100644 --- a/test/opt/pass_utils.h +++ b/test/opt/pass_utils.h @@ -33,6 +33,18 @@ namespace spvtools { +// In-place substring replacement. Finds the |find_str| in the |process_str| +// and replaces the found substring with |replace_str|. Returns true if at +// least one replacement is done successfully, returns false otherwise. The +// replaced substring won't be processed again, which means: If the +// |replace_str| has |find_str| as its substring, that newly replaced part of +// |process_str| won't be processed again. +bool FindAndReplace(std::string* process_str, const std::string find_str, + const std::string replace_str); + +// Returns true if the given string contains any debug opcode substring. +bool ContainsDebugOpcode(const char* inst); + // Returns the concatenated string from a vector of |strings|, with postfixing // each string with the given |delimiter|. if the |skip_dictator| returns true // for an original string, that string will be omitted. diff --git a/test/opt/test_utils.cpp b/test/opt/test_utils.cpp index 4a96559..e9ebb31 100644 --- a/test/opt/test_utils.cpp +++ b/test/opt/test_utils.cpp @@ -55,4 +55,64 @@ TEST(JoinNonDebugInsts, Cases) { "the only remaining string"})); } +namespace { +struct SubstringReplacementTestCase { + const char* orig_str; + const char* find_substr; + const char* replace_substr; + const char* expected_str; + bool replace_should_succeed; +}; +} +using FindAndReplaceTest = + ::testing::TestWithParam; + +TEST_P(FindAndReplaceTest, SubstringReplacement) { + auto process = std::string(GetParam().orig_str); + EXPECT_EQ(GetParam().replace_should_succeed, + FindAndReplace(&process, GetParam().find_substr, + GetParam().replace_substr)) + << "Original string: " << GetParam().orig_str + << " replace: " << GetParam().find_substr + << " to: " << GetParam().replace_substr + << " should returns: " << GetParam().replace_should_succeed; + EXPECT_STREQ(GetParam().expected_str, process.c_str()) + << "Original string: " << GetParam().orig_str + << " replace: " << GetParam().find_substr + << " to: " << GetParam().replace_substr + << " expected string: " << GetParam().expected_str; +} + +INSTANTIATE_TEST_CASE_P( + SubstringReplacement, FindAndReplaceTest, + ::testing::ValuesIn(std::vector({ + // orig string, find substring, replace substring, expected string, + // replacement happened + {"", "", "", "", false}, + {"", "b", "", "", false}, + {"", "", "c", "", false}, + {"", "a", "b", "", false}, + + {"a", "", "c", "a", false}, + {"a", "b", "c", "a", false}, + {"a", "b", "", "a", false}, + {"a", "a", "", "", true}, + {"a", "a", "b", "b", true}, + + {"ab", "a", "b", "bb", true}, + {"ab", "a", "", "b", true}, + {"ab", "b", "", "a", true}, + {"ab", "ab", "", "", true}, + {"ab", "ab", "cd", "cd", true}, + {"bc", "abc", "efg", "bc", false}, + + {"abc", "ab", "bc", "bcc", true}, + {"abc", "ab", "", "c", true}, + {"abc", "bc", "", "a", true}, + {"abc", "bc", "d", "ad", true}, + {"abc", "a", "123", "123bc", true}, + {"abc", "ab", "a", "ac", true}, + {"abc", "a", "aab", "aabbc", true}, + {"abc", "abcd", "efg", "abc", false}, + }))); } // anonymous namespace -- 2.7.4