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 //
7 // https://www.apache.org/licenses/LICENSE-2.0
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>
18 #include <map>
19 #include <tuple>
20
21 #include "gtest/gtest.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/strings/str_split.h"
24
TEST(StrReplaceAll,OneReplacement)25 TEST(StrReplaceAll, OneReplacement) {
26 std::string s;
27
28 // Empty 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 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
TEST(StrReplaceAll,ManyReplacements)88 TEST(StrReplaceAll, ManyReplacements) {
89 std::string s;
90
91 // Empty 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 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
TEST(StrReplaceAll,ManyReplacementsInMap)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!",
151 replacements);
152 EXPECT_EQ("Bob bought 5 Apples. Thanks Bob!", s);
153 }
154
TEST(StrReplaceAll,ReplacementsInPlace)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
TEST(StrReplaceAll,ReplacementsInPlaceInMap)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 {
ContCont178 Cont() {}
ContCont179 explicit Cont(absl::string_view src) : data(src) {}
180
181 absl::string_view data;
182 };
183
184 template <int index>
get(const Cont & c)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
TEST(StrReplaceAll,VariableNumber)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.
TEST(StrReplaceAll,Inplace)263 TEST(StrReplaceAll, Inplace) {
264 std::string s;
265 int reps;
266
267 // Empty 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 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 }
342