1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "perfetto/ext/base/string_view_splitter.h"
18
19 #include <vector>
20
21 #include "test/gtest_and_gmock.h"
22
23 namespace perfetto {
24 namespace base {
25 namespace {
26
27 using testing::ElementsAreArray;
28
TEST(StringViewSplitterTest,StdString)29 TEST(StringViewSplitterTest, StdString) {
30 {
31 StringViewSplitter ss("", 'x');
32 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
33 EXPECT_FALSE(ss.Next());
34 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
35 }
36 {
37 StringViewSplitter ss("", 'x');
38 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
39 EXPECT_FALSE(ss.Next());
40 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
41 }
42 {
43 StringViewSplitter ss("a", 'x');
44 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
45 EXPECT_TRUE(ss.Next());
46 EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str());
47 EXPECT_FALSE(ss.Next());
48 }
49 {
50 StringViewSplitter ss("abc", 'x');
51 EXPECT_TRUE(ss.Next());
52 EXPECT_STREQ("abc", ss.cur_token().ToStdString().c_str());
53 EXPECT_FALSE(ss.Next());
54 }
55 {
56 StringViewSplitter ss("ab,", ',');
57 EXPECT_TRUE(ss.Next());
58 EXPECT_STREQ("ab", ss.cur_token().ToStdString().c_str());
59 EXPECT_FALSE(ss.Next());
60 }
61 {
62 StringViewSplitter ss(",ab,", ',');
63 EXPECT_TRUE(ss.Next());
64 EXPECT_STREQ("ab", ss.cur_token().ToStdString().c_str());
65 EXPECT_FALSE(ss.Next());
66 }
67 {
68 StringViewSplitter ss("a,b,c", ',');
69 EXPECT_TRUE(ss.Next());
70 EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str());
71
72 EXPECT_TRUE(ss.Next());
73 EXPECT_STREQ("b", ss.cur_token().ToStdString().c_str());
74
75 EXPECT_TRUE(ss.Next());
76 EXPECT_STREQ("c", ss.cur_token().ToStdString().c_str());
77
78 EXPECT_FALSE(ss.Next());
79 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
80 }
81 {
82 StringViewSplitter ss("a,b,c,", ',');
83 EXPECT_TRUE(ss.Next());
84 EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str());
85
86 EXPECT_TRUE(ss.Next());
87 EXPECT_STREQ("b", ss.cur_token().ToStdString().c_str());
88
89 EXPECT_TRUE(ss.Next());
90 EXPECT_STREQ("c", ss.cur_token().ToStdString().c_str());
91
92 EXPECT_FALSE(ss.Next());
93 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
94 }
95 {
96 StringViewSplitter ss(",,a,,b,,,,c,,,", ',');
97 EXPECT_TRUE(ss.Next());
98 EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str());
99
100 EXPECT_TRUE(ss.Next());
101 EXPECT_STREQ("b", ss.cur_token().ToStdString().c_str());
102
103 EXPECT_TRUE(ss.Next());
104 EXPECT_STREQ("c", ss.cur_token().ToStdString().c_str());
105
106 for (int i = 0; i < 3; i++) {
107 EXPECT_FALSE(ss.Next());
108 }
109 }
110 {
111 StringViewSplitter ss(",,", ',');
112 for (int i = 0; i < 3; i++) {
113 EXPECT_FALSE(ss.Next());
114 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
115 }
116 }
117 {
118 StringViewSplitter ss(",,foo", ',');
119 EXPECT_TRUE(ss.Next());
120 EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str());
121
122 EXPECT_FALSE(ss.Next());
123 }
124 }
125
TEST(StringViewSplitterTest,CString)126 TEST(StringViewSplitterTest, CString) {
127 {
128 StringViewSplitter ss("foo\nbar\n\nbaz\n", '\n');
129 EXPECT_TRUE(ss.Next());
130
131 EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str());
132
133 EXPECT_TRUE(ss.Next());
134
135 EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str());
136
137 EXPECT_TRUE(ss.Next());
138
139 EXPECT_STREQ("baz", ss.cur_token().ToStdString().c_str());
140
141 EXPECT_FALSE(ss.Next());
142 }
143 {
144 StringViewSplitter ss("", ',');
145 EXPECT_FALSE(ss.Next());
146 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
147 }
148 {
149 StringViewSplitter ss(",,foo,bar\0,baz", ',');
150
151 EXPECT_TRUE(ss.Next());
152 EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str());
153
154 EXPECT_TRUE(ss.Next());
155 EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str());
156
157 for (int i = 0; i < 3; i++) {
158 EXPECT_FALSE(ss.Next());
159 }
160 }
161 {
162 StringViewSplitter ss(",,a\0,b,", ',');
163 EXPECT_TRUE(ss.Next());
164 EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str());
165
166 for (int i = 0; i < 3; i++) {
167 EXPECT_FALSE(ss.Next());
168 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
169 }
170 }
171 {
172 StringViewSplitter ss(",a,\0b", ',');
173 EXPECT_TRUE(ss.Next());
174 EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str());
175
176 for (int i = 0; i < 3; i++) {
177 EXPECT_FALSE(ss.Next());
178
179 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
180 }
181 }
182 {
183 StringViewSplitter ss(",a\0\0,x\0\0b", ',');
184 EXPECT_TRUE(ss.Next());
185 EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str());
186
187 for (int i = 0; i < 3; i++) {
188 EXPECT_FALSE(ss.Next());
189
190 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
191 }
192 }
193 }
194
TEST(StringViewSplitterTest,SplitOnNUL)195 TEST(StringViewSplitterTest, SplitOnNUL) {
196 {
197 StringViewSplitter ss("", '\0');
198 EXPECT_FALSE(ss.Next());
199 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
200 }
201 {
202 std::string str;
203 str.resize(48);
204 memcpy(&str[0], "foo\0", 4);
205 memcpy(&str[4], "bar\0", 4);
206 memcpy(&str[20], "baz", 3);
207 StringViewSplitter ss(base::StringView(std::move(str)), '\0');
208 EXPECT_TRUE(ss.Next());
209 EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str());
210
211 EXPECT_TRUE(ss.Next());
212 EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str());
213
214 EXPECT_TRUE(ss.Next());
215 EXPECT_STREQ("baz", ss.cur_token().ToStdString().c_str());
216
217 for (int i = 0; i < 3; i++) {
218 EXPECT_FALSE(ss.Next());
219
220 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
221 }
222 }
223 {
224 char buf[] = "foo\0bar\0baz\0";
225 StringViewSplitter ss(base::StringView("foo\0bar\0baz\0", sizeof(buf)),
226 '\0');
227 EXPECT_TRUE(ss.Next());
228
229 EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str());
230
231 EXPECT_TRUE(ss.Next());
232
233 EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str());
234
235 EXPECT_TRUE(ss.Next());
236
237 EXPECT_STREQ("baz", ss.cur_token().ToStdString().c_str());
238
239 for (int i = 0; i < 3; i++) {
240 EXPECT_FALSE(ss.Next());
241
242 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
243 }
244 }
245 {
246 char buf[] = "\0\0foo\0\0\0\0bar\0baz\0\0";
247 StringViewSplitter ss(base::StringView(buf, sizeof(buf)), '\0');
248 EXPECT_TRUE(ss.Next());
249
250 EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str());
251
252 EXPECT_TRUE(ss.Next());
253
254 EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str());
255
256 EXPECT_TRUE(ss.Next());
257
258 EXPECT_STREQ("baz", ss.cur_token().ToStdString().c_str());
259
260 for (int i = 0; i < 3; i++) {
261 EXPECT_FALSE(ss.Next());
262
263 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
264 }
265 }
266 {
267 StringViewSplitter ss("", '\0');
268 for (int i = 0; i < 3; i++) {
269 EXPECT_FALSE(ss.Next());
270
271 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
272 }
273 }
274 {
275 StringViewSplitter ss("\0", '\0');
276 for (int i = 0; i < 3; i++) {
277 EXPECT_FALSE(ss.Next());
278
279 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
280 }
281 }
282 {
283 StringViewSplitter ss("\0\0", '\0');
284 for (int i = 0; i < 3; i++) {
285 EXPECT_FALSE(ss.Next());
286
287 EXPECT_STREQ("", ss.cur_token().ToStdString().c_str());
288 }
289 }
290 }
291
TEST(StringViewSplitterTest,NestedUsage)292 TEST(StringViewSplitterTest, NestedUsage) {
293 char text[] = R"(
294 l1w1 l1w2 l1w3
295
296 ,l,2,w,1 l,2,,w,,2,,
297 )";
298 std::vector<base::StringView> all_lines;
299 std::vector<base::StringView> all_words;
300 std::vector<base::StringView> all_tokens;
301 for (StringViewSplitter lines(base::StringView(text), '\n'); lines.Next();) {
302 all_lines.push_back(lines.cur_token());
303 for (StringViewSplitter words(&lines, ' '); words.Next();) {
304 all_words.push_back(words.cur_token());
305 for (StringViewSplitter tokens(&words, ','); tokens.Next();) {
306 all_tokens.push_back(tokens.cur_token());
307 }
308 }
309 }
310 EXPECT_THAT(all_lines,
311 ElementsAreArray({"l1w1 l1w2 l1w3", ",l,2,w,1 l,2,,w,,2,,"}));
312 EXPECT_THAT(all_words, ElementsAreArray({"l1w1", "l1w2", "l1w3", ",l,2,w,1",
313 "l,2,,w,,2,,"}));
314 EXPECT_THAT(all_tokens, ElementsAreArray({"l1w1", "l1w2", "l1w3", "l", "2",
315 "w", "1", "l", "2", "w", "2"}));
316 } // namespace
317
TEST(StringViewSplitterTest,EmptyTokens)318 TEST(StringViewSplitterTest, EmptyTokens) {
319 std::vector<std::string> tokens;
320 for (StringViewSplitter lines(
321 "a,,b", ',', StringViewSplitter::EmptyTokenMode::ALLOW_EMPTY_TOKENS);
322 lines.Next();) {
323 tokens.push_back(lines.cur_token().ToStdString());
324 }
325 EXPECT_THAT(tokens, testing::ElementsAre("a", "", "b"));
326 } // namespace
327
328 } // namespace
329 } // namespace base
330 } // namespace perfetto
331