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