misterg | c2e7548 | 2017-09-19 16:54:40 -0400 | [diff] [blame] | 1 | // Copyright 2017 The Abseil Authors. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
nik7273 | 38b7043 | 2019-03-08 10:27:53 -0500 | [diff] [blame] | 7 | // https://www.apache.org/licenses/LICENSE-2.0 |
misterg | c2e7548 | 2017-09-19 16:54:40 -0400 | [diff] [blame] | 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include "absl/strings/str_replace.h" |
| 16 | |
| 17 | #include <list> |
Abseil Team | cf6ab6b | 2017-09-24 08:20:48 -0700 | [diff] [blame] | 18 | #include <map> |
misterg | c2e7548 | 2017-09-19 16:54:40 -0400 | [diff] [blame] | 19 | #include <tuple> |
| 20 | |
| 21 | #include "gtest/gtest.h" |
misterg | c2e7548 | 2017-09-19 16:54:40 -0400 | [diff] [blame] | 22 | #include "absl/strings/str_cat.h" |
Abseil Team | cf6ab6b | 2017-09-24 08:20:48 -0700 | [diff] [blame] | 23 | #include "absl/strings/str_split.h" |
misterg | c2e7548 | 2017-09-19 16:54:40 -0400 | [diff] [blame] | 24 | |
| 25 | TEST(StrReplaceAll, OneReplacement) { |
| 26 | std::string s; |
| 27 | |
| 28 | // Empty std::string. |
| 29 | s = absl::StrReplaceAll(s, {{"", ""}}); |
| 30 | EXPECT_EQ(s, ""); |
| 31 | s = absl::StrReplaceAll(s, {{"x", ""}}); |
| 32 | EXPECT_EQ(s, ""); |
| 33 | s = absl::StrReplaceAll(s, {{"", "y"}}); |
| 34 | EXPECT_EQ(s, ""); |
| 35 | s = absl::StrReplaceAll(s, {{"x", "y"}}); |
| 36 | EXPECT_EQ(s, ""); |
| 37 | |
| 38 | // Empty substring. |
| 39 | s = absl::StrReplaceAll("abc", {{"", ""}}); |
| 40 | EXPECT_EQ(s, "abc"); |
| 41 | s = absl::StrReplaceAll("abc", {{"", "y"}}); |
| 42 | EXPECT_EQ(s, "abc"); |
| 43 | s = absl::StrReplaceAll("abc", {{"x", ""}}); |
| 44 | EXPECT_EQ(s, "abc"); |
| 45 | |
| 46 | // Substring not found. |
| 47 | s = absl::StrReplaceAll("abc", {{"xyz", "123"}}); |
| 48 | EXPECT_EQ(s, "abc"); |
| 49 | |
| 50 | // Replace entire std::string. |
| 51 | s = absl::StrReplaceAll("abc", {{"abc", "xyz"}}); |
| 52 | EXPECT_EQ(s, "xyz"); |
| 53 | |
| 54 | // Replace once at the start. |
| 55 | s = absl::StrReplaceAll("abc", {{"a", "x"}}); |
| 56 | EXPECT_EQ(s, "xbc"); |
| 57 | |
| 58 | // Replace once in the middle. |
| 59 | s = absl::StrReplaceAll("abc", {{"b", "x"}}); |
| 60 | EXPECT_EQ(s, "axc"); |
| 61 | |
| 62 | // Replace once at the end. |
| 63 | s = absl::StrReplaceAll("abc", {{"c", "x"}}); |
| 64 | EXPECT_EQ(s, "abx"); |
| 65 | |
| 66 | // Replace multiple times with varying lengths of original/replacement. |
| 67 | s = absl::StrReplaceAll("ababa", {{"a", "xxx"}}); |
| 68 | EXPECT_EQ(s, "xxxbxxxbxxx"); |
| 69 | |
| 70 | s = absl::StrReplaceAll("ababa", {{"b", "xxx"}}); |
| 71 | EXPECT_EQ(s, "axxxaxxxa"); |
| 72 | |
| 73 | s = absl::StrReplaceAll("aaabaaabaaa", {{"aaa", "x"}}); |
| 74 | EXPECT_EQ(s, "xbxbx"); |
| 75 | |
| 76 | s = absl::StrReplaceAll("abbbabbba", {{"bbb", "x"}}); |
| 77 | EXPECT_EQ(s, "axaxa"); |
| 78 | |
| 79 | // Overlapping matches are replaced greedily. |
| 80 | s = absl::StrReplaceAll("aaa", {{"aa", "x"}}); |
| 81 | EXPECT_EQ(s, "xa"); |
| 82 | |
| 83 | // The replacements are not recursive. |
| 84 | s = absl::StrReplaceAll("aaa", {{"aa", "a"}}); |
| 85 | EXPECT_EQ(s, "aa"); |
| 86 | } |
| 87 | |
| 88 | TEST(StrReplaceAll, ManyReplacements) { |
| 89 | std::string s; |
| 90 | |
| 91 | // Empty std::string. |
| 92 | s = absl::StrReplaceAll("", {{"", ""}, {"x", ""}, {"", "y"}, {"x", "y"}}); |
| 93 | EXPECT_EQ(s, ""); |
| 94 | |
| 95 | // Empty substring. |
| 96 | s = absl::StrReplaceAll("abc", {{"", ""}, {"", "y"}, {"x", ""}}); |
| 97 | EXPECT_EQ(s, "abc"); |
| 98 | |
| 99 | // Replace entire std::string, one char at a time |
| 100 | s = absl::StrReplaceAll("abc", {{"a", "x"}, {"b", "y"}, {"c", "z"}}); |
| 101 | EXPECT_EQ(s, "xyz"); |
| 102 | s = absl::StrReplaceAll("zxy", {{"z", "x"}, {"x", "y"}, {"y", "z"}}); |
| 103 | EXPECT_EQ(s, "xyz"); |
| 104 | |
| 105 | // Replace once at the start (longer matches take precedence) |
| 106 | s = absl::StrReplaceAll("abc", {{"a", "x"}, {"ab", "xy"}, {"abc", "xyz"}}); |
| 107 | EXPECT_EQ(s, "xyz"); |
| 108 | |
| 109 | // Replace once in the middle. |
| 110 | s = absl::StrReplaceAll( |
| 111 | "Abc!", {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc", "yz"}, {"c", "z"}}); |
| 112 | EXPECT_EQ(s, "Ayz!"); |
| 113 | |
| 114 | // Replace once at the end. |
| 115 | s = absl::StrReplaceAll( |
| 116 | "Abc!", |
| 117 | {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc!", "yz?"}, {"c!", "z;"}}); |
| 118 | EXPECT_EQ(s, "Ayz?"); |
| 119 | |
| 120 | // Replace multiple times with varying lengths of original/replacement. |
| 121 | s = absl::StrReplaceAll("ababa", {{"a", "xxx"}, {"b", "XXXX"}}); |
| 122 | EXPECT_EQ(s, "xxxXXXXxxxXXXXxxx"); |
| 123 | |
| 124 | // Overlapping matches are replaced greedily. |
| 125 | s = absl::StrReplaceAll("aaa", {{"aa", "x"}, {"a", "X"}}); |
| 126 | EXPECT_EQ(s, "xX"); |
| 127 | s = absl::StrReplaceAll("aaa", {{"a", "X"}, {"aa", "x"}}); |
| 128 | EXPECT_EQ(s, "xX"); |
| 129 | |
| 130 | // Two well-known sentences |
| 131 | s = absl::StrReplaceAll("the quick brown fox jumped over the lazy dogs", |
| 132 | { |
| 133 | {"brown", "box"}, |
| 134 | {"dogs", "jugs"}, |
| 135 | {"fox", "with"}, |
| 136 | {"jumped", "five"}, |
| 137 | {"over", "dozen"}, |
| 138 | {"quick", "my"}, |
| 139 | {"the", "pack"}, |
| 140 | {"the lazy", "liquor"}, |
| 141 | }); |
| 142 | EXPECT_EQ(s, "pack my box with five dozen liquor jugs"); |
| 143 | } |
| 144 | |
| 145 | TEST(StrReplaceAll, ManyReplacementsInMap) { |
| 146 | std::map<const char *, const char *> replacements; |
| 147 | replacements["$who"] = "Bob"; |
| 148 | replacements["$count"] = "5"; |
| 149 | replacements["#Noun"] = "Apples"; |
| 150 | std::string s = absl::StrReplaceAll("$who bought $count #Noun. Thanks $who!", |
Abseil Team | febc5ee | 2019-03-06 11:36:55 -0800 | [diff] [blame] | 151 | replacements); |
misterg | c2e7548 | 2017-09-19 16:54:40 -0400 | [diff] [blame] | 152 | EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s); |
| 153 | } |
| 154 | |
| 155 | TEST(StrReplaceAll, ReplacementsInPlace) { |
| 156 | std::string s = std::string("$who bought $count #Noun. Thanks $who!"); |
| 157 | int count; |
| 158 | count = absl::StrReplaceAll({{"$count", absl::StrCat(5)}, |
| 159 | {"$who", "Bob"}, |
| 160 | {"#Noun", "Apples"}}, &s); |
| 161 | EXPECT_EQ(count, 4); |
| 162 | EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s); |
| 163 | } |
| 164 | |
| 165 | TEST(StrReplaceAll, ReplacementsInPlaceInMap) { |
| 166 | std::string s = std::string("$who bought $count #Noun. Thanks $who!"); |
| 167 | std::map<absl::string_view, absl::string_view> replacements; |
| 168 | replacements["$who"] = "Bob"; |
| 169 | replacements["$count"] = "5"; |
| 170 | replacements["#Noun"] = "Apples"; |
| 171 | int count; |
| 172 | count = absl::StrReplaceAll(replacements, &s); |
| 173 | EXPECT_EQ(count, 4); |
| 174 | EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s); |
| 175 | } |
| 176 | |
| 177 | struct Cont { |
| 178 | Cont() {} |
| 179 | explicit Cont(absl::string_view src) : data(src) {} |
| 180 | |
| 181 | absl::string_view data; |
| 182 | }; |
| 183 | |
| 184 | template <int index> |
| 185 | absl::string_view get(const Cont& c) { |
| 186 | auto splitter = absl::StrSplit(c.data, ':'); |
| 187 | auto it = splitter.begin(); |
| 188 | for (int i = 0; i < index; ++i) ++it; |
| 189 | |
| 190 | return *it; |
| 191 | } |
| 192 | |
| 193 | TEST(StrReplaceAll, VariableNumber) { |
| 194 | std::string s; |
| 195 | { |
| 196 | std::vector<std::pair<std::string, std::string>> replacements; |
| 197 | |
| 198 | s = "abc"; |
| 199 | EXPECT_EQ(0, absl::StrReplaceAll(replacements, &s)); |
| 200 | EXPECT_EQ("abc", s); |
| 201 | |
| 202 | s = "abc"; |
| 203 | replacements.push_back({"a", "A"}); |
| 204 | EXPECT_EQ(1, absl::StrReplaceAll(replacements, &s)); |
| 205 | EXPECT_EQ("Abc", s); |
| 206 | |
| 207 | s = "abc"; |
| 208 | replacements.push_back({"b", "B"}); |
| 209 | EXPECT_EQ(2, absl::StrReplaceAll(replacements, &s)); |
| 210 | EXPECT_EQ("ABc", s); |
| 211 | |
| 212 | s = "abc"; |
| 213 | replacements.push_back({"d", "D"}); |
| 214 | EXPECT_EQ(2, absl::StrReplaceAll(replacements, &s)); |
| 215 | EXPECT_EQ("ABc", s); |
| 216 | |
| 217 | EXPECT_EQ("ABcABc", absl::StrReplaceAll("abcabc", replacements)); |
| 218 | } |
| 219 | |
| 220 | { |
| 221 | std::map<const char*, const char*> replacements; |
| 222 | replacements["aa"] = "x"; |
| 223 | replacements["a"] = "X"; |
| 224 | s = "aaa"; |
| 225 | EXPECT_EQ(2, absl::StrReplaceAll(replacements, &s)); |
| 226 | EXPECT_EQ("xX", s); |
| 227 | |
| 228 | EXPECT_EQ("xxX", absl::StrReplaceAll("aaaaa", replacements)); |
| 229 | } |
| 230 | |
| 231 | { |
| 232 | std::list<std::pair<absl::string_view, absl::string_view>> replacements = { |
| 233 | {"a", "x"}, {"b", "y"}, {"c", "z"}}; |
| 234 | |
| 235 | std::string s = absl::StrReplaceAll("abc", replacements); |
| 236 | EXPECT_EQ(s, "xyz"); |
| 237 | } |
| 238 | |
| 239 | { |
| 240 | using X = std::tuple<absl::string_view, std::string, int>; |
| 241 | std::vector<X> replacements(3); |
| 242 | replacements[0] = X{"a", "x", 1}; |
| 243 | replacements[1] = X{"b", "y", 0}; |
| 244 | replacements[2] = X{"c", "z", -1}; |
| 245 | |
| 246 | std::string s = absl::StrReplaceAll("abc", replacements); |
| 247 | EXPECT_EQ(s, "xyz"); |
| 248 | } |
| 249 | |
| 250 | { |
| 251 | std::vector<Cont> replacements(3); |
| 252 | replacements[0] = Cont{"a:x"}; |
| 253 | replacements[1] = Cont{"b:y"}; |
| 254 | replacements[2] = Cont{"c:z"}; |
| 255 | |
| 256 | std::string s = absl::StrReplaceAll("abc", replacements); |
| 257 | EXPECT_EQ(s, "xyz"); |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | // Same as above, but using the in-place variant of absl::StrReplaceAll, |
| 262 | // that returns the # of replacements performed. |
| 263 | TEST(StrReplaceAll, Inplace) { |
| 264 | std::string s; |
| 265 | int reps; |
| 266 | |
| 267 | // Empty std::string. |
| 268 | s = ""; |
| 269 | reps = absl::StrReplaceAll({{"", ""}, {"x", ""}, {"", "y"}, {"x", "y"}}, &s); |
| 270 | EXPECT_EQ(reps, 0); |
| 271 | EXPECT_EQ(s, ""); |
| 272 | |
| 273 | // Empty substring. |
| 274 | s = "abc"; |
| 275 | reps = absl::StrReplaceAll({{"", ""}, {"", "y"}, {"x", ""}}, &s); |
| 276 | EXPECT_EQ(reps, 0); |
| 277 | EXPECT_EQ(s, "abc"); |
| 278 | |
| 279 | // Replace entire std::string, one char at a time |
| 280 | s = "abc"; |
| 281 | reps = absl::StrReplaceAll({{"a", "x"}, {"b", "y"}, {"c", "z"}}, &s); |
| 282 | EXPECT_EQ(reps, 3); |
| 283 | EXPECT_EQ(s, "xyz"); |
| 284 | s = "zxy"; |
| 285 | reps = absl::StrReplaceAll({{"z", "x"}, {"x", "y"}, {"y", "z"}}, &s); |
| 286 | EXPECT_EQ(reps, 3); |
| 287 | EXPECT_EQ(s, "xyz"); |
| 288 | |
| 289 | // Replace once at the start (longer matches take precedence) |
| 290 | s = "abc"; |
| 291 | reps = absl::StrReplaceAll({{"a", "x"}, {"ab", "xy"}, {"abc", "xyz"}}, &s); |
| 292 | EXPECT_EQ(reps, 1); |
| 293 | EXPECT_EQ(s, "xyz"); |
| 294 | |
| 295 | // Replace once in the middle. |
| 296 | s = "Abc!"; |
| 297 | reps = absl::StrReplaceAll( |
| 298 | {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc", "yz"}, {"c", "z"}}, &s); |
| 299 | EXPECT_EQ(reps, 1); |
| 300 | EXPECT_EQ(s, "Ayz!"); |
| 301 | |
| 302 | // Replace once at the end. |
| 303 | s = "Abc!"; |
| 304 | reps = absl::StrReplaceAll( |
| 305 | {{"a", "x"}, {"ab", "xy"}, {"b", "y"}, {"bc!", "yz?"}, {"c!", "z;"}}, &s); |
| 306 | EXPECT_EQ(reps, 1); |
| 307 | EXPECT_EQ(s, "Ayz?"); |
| 308 | |
| 309 | // Replace multiple times with varying lengths of original/replacement. |
| 310 | s = "ababa"; |
| 311 | reps = absl::StrReplaceAll({{"a", "xxx"}, {"b", "XXXX"}}, &s); |
| 312 | EXPECT_EQ(reps, 5); |
| 313 | EXPECT_EQ(s, "xxxXXXXxxxXXXXxxx"); |
| 314 | |
| 315 | // Overlapping matches are replaced greedily. |
| 316 | s = "aaa"; |
| 317 | reps = absl::StrReplaceAll({{"aa", "x"}, {"a", "X"}}, &s); |
| 318 | EXPECT_EQ(reps, 2); |
| 319 | EXPECT_EQ(s, "xX"); |
| 320 | s = "aaa"; |
| 321 | reps = absl::StrReplaceAll({{"a", "X"}, {"aa", "x"}}, &s); |
| 322 | EXPECT_EQ(reps, 2); |
| 323 | EXPECT_EQ(s, "xX"); |
| 324 | |
| 325 | // Two well-known sentences |
| 326 | s = "the quick brown fox jumped over the lazy dogs"; |
| 327 | reps = absl::StrReplaceAll( |
| 328 | { |
| 329 | {"brown", "box"}, |
| 330 | {"dogs", "jugs"}, |
| 331 | {"fox", "with"}, |
| 332 | {"jumped", "five"}, |
| 333 | {"over", "dozen"}, |
| 334 | {"quick", "my"}, |
| 335 | {"the", "pack"}, |
| 336 | {"the lazy", "liquor"}, |
| 337 | }, |
| 338 | &s); |
| 339 | EXPECT_EQ(reps, 8); |
| 340 | EXPECT_EQ(s, "pack my box with five dozen liquor jugs"); |
| 341 | } |