|  | /* | 
|  | * Copyright (C) 2018 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "perfetto/ext/base/string_splitter.h" | 
|  |  | 
|  | #include <vector> | 
|  |  | 
|  | #include "test/gtest_and_gmock.h" | 
|  |  | 
|  | namespace perfetto { | 
|  | namespace base { | 
|  | namespace { | 
|  |  | 
|  | using testing::ElementsAreArray; | 
|  |  | 
|  | TEST(StringSplitterTest, StdString) { | 
|  | { | 
|  | StringSplitter ss("", 'x'); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | { | 
|  | StringSplitter ss(std::string(), 'x'); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | { | 
|  | StringSplitter ss("a", 'x'); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("a", ss.cur_token()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | { | 
|  | StringSplitter ss("abc", 'x'); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("abc", ss.cur_token()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | } | 
|  | { | 
|  | StringSplitter ss("ab,", ','); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("ab", ss.cur_token()); | 
|  | EXPECT_EQ(2u, ss.cur_token_size()); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | } | 
|  | { | 
|  | StringSplitter ss(",ab,", ','); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("ab", ss.cur_token()); | 
|  | EXPECT_EQ(2u, ss.cur_token_size()); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | } | 
|  | { | 
|  | StringSplitter ss("a,b,c", ','); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("a", ss.cur_token()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("b", ss.cur_token()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("c", ss.cur_token()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  |  | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | { | 
|  | StringSplitter ss("a,b,c,", ','); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("a", ss.cur_token()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("b", ss.cur_token()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("c", ss.cur_token()); | 
|  |  | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | { | 
|  | StringSplitter ss(",,a,,b,,,,c,,,", ','); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("a", ss.cur_token()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("b", ss.cur_token()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("c", ss.cur_token()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  |  | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | } | 
|  | { | 
|  | StringSplitter ss(",,", ','); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | } | 
|  | { | 
|  | StringSplitter ss(",,foo", ','); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("foo", ss.cur_token()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(StringSplitterTest, CString) { | 
|  | { | 
|  | char buf[] = "\0x\0"; | 
|  | StringSplitter ss(buf, sizeof(buf), ','); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | { | 
|  | char buf[] = "foo\nbar\n\nbaz\n"; | 
|  | StringSplitter ss(buf, sizeof(buf), '\n'); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("foo", ss.cur_token()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("bar", ss.cur_token()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("baz", ss.cur_token()); | 
|  |  | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | { | 
|  | char buf[] = ""; | 
|  | StringSplitter ss(buf, 0, ','); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | { | 
|  | char buf[] = "\0"; | 
|  | StringSplitter ss(buf, 1, ','); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | { | 
|  | char buf[] = ",,foo,bar\0,baz"; | 
|  | StringSplitter ss(buf, sizeof(buf), ','); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("foo", ss.cur_token()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("bar", ss.cur_token()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  |  | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | } | 
|  | { | 
|  | char buf[] = ",,a\0,b,"; | 
|  | StringSplitter ss(buf, sizeof(buf), ','); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("a", ss.cur_token()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | } | 
|  | } | 
|  | { | 
|  | char buf[] = ",a,\0b"; | 
|  | StringSplitter ss(buf, sizeof(buf), ','); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("a", ss.cur_token()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | } | 
|  | { | 
|  | char buf[] = ",a\0\0,x\0\0b"; | 
|  | StringSplitter ss(buf, sizeof(buf), ','); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("a", ss.cur_token()); | 
|  | EXPECT_EQ(1u, ss.cur_token_size()); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(StringSplitterTest, SplitOnNUL) { | 
|  | { | 
|  | StringSplitter ss(std::string(""), '\0'); | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | { | 
|  | std::string str; | 
|  | str.resize(48); | 
|  | memcpy(&str[0], "foo\0", 4); | 
|  | memcpy(&str[4], "bar\0", 4); | 
|  | memcpy(&str[20], "baz", 3); | 
|  | StringSplitter ss(std::move(str), '\0'); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("foo", ss.cur_token()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("bar", ss.cur_token()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_STREQ("baz", ss.cur_token()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  |  | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | } | 
|  | { | 
|  | char buf[] = "foo\0bar\0baz\0"; | 
|  | StringSplitter ss(buf, sizeof(buf), '\0'); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("foo", ss.cur_token()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("bar", ss.cur_token()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("baz", ss.cur_token()); | 
|  |  | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | } | 
|  | { | 
|  | char buf[] = "\0\0foo\0\0\0\0bar\0baz\0\0"; | 
|  | StringSplitter ss(buf, sizeof(buf), '\0'); | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("foo", ss.cur_token()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("bar", ss.cur_token()); | 
|  |  | 
|  | EXPECT_TRUE(ss.Next()); | 
|  | EXPECT_EQ(3u, ss.cur_token_size()); | 
|  | EXPECT_STREQ("baz", ss.cur_token()); | 
|  |  | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | } | 
|  | { | 
|  | char buf[] = ""; | 
|  | StringSplitter ss(buf, 0, '\0'); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | } | 
|  | { | 
|  | char buf[] = "\0"; | 
|  | StringSplitter ss(buf, 1, '\0'); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | } | 
|  | { | 
|  | char buf[] = "\0\0"; | 
|  | StringSplitter ss(buf, 2, '\0'); | 
|  | for (int i = 0; i < 3; i++) { | 
|  | EXPECT_FALSE(ss.Next()); | 
|  | EXPECT_EQ(0u, ss.cur_token_size()); | 
|  | EXPECT_EQ(nullptr, ss.cur_token()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST(StringSplitterTest, NestedUsage) { | 
|  | char text[] = R"( | 
|  | l1w1 l1w2 l1w3 | 
|  |  | 
|  | ,l,2,w,1   l,2,,w,,2,, | 
|  | )"; | 
|  | std::vector<std::string> all_lines; | 
|  | std::vector<std::string> all_words; | 
|  | std::vector<std::string> all_tokens; | 
|  | for (StringSplitter lines(text, sizeof(text), '\n'); lines.Next();) { | 
|  | all_lines.push_back(lines.cur_token()); | 
|  | for (StringSplitter words(&lines, ' '); words.Next();) { | 
|  | all_words.push_back(words.cur_token()); | 
|  | for (StringSplitter tokens(&words, ','); tokens.Next();) { | 
|  | all_tokens.push_back(tokens.cur_token()); | 
|  | } | 
|  | } | 
|  | } | 
|  | EXPECT_THAT(all_lines, | 
|  | ElementsAreArray({"l1w1 l1w2 l1w3", ",l,2,w,1   l,2,,w,,2,,"})); | 
|  | EXPECT_THAT(all_words, ElementsAreArray({"l1w1", "l1w2", "l1w3", ",l,2,w,1", | 
|  | "l,2,,w,,2,,"})); | 
|  | EXPECT_THAT(all_tokens, ElementsAreArray({"l1w1", "l1w2", "l1w3", "l", "2", | 
|  | "w", "1", "l", "2", "w", "2"})); | 
|  | }  // namespace | 
|  |  | 
|  | TEST(StringSplitterTest, EmptyTokens) { | 
|  | char text[] = "a,,b"; | 
|  | std::vector<std::string> tokens; | 
|  | for (StringSplitter lines(text, sizeof(text), ',', | 
|  | StringSplitter::EmptyTokenMode::ALLOW_EMPTY_TOKENS); | 
|  | lines.Next();) { | 
|  | tokens.push_back(lines.cur_token()); | 
|  | } | 
|  | EXPECT_THAT(tokens, testing::ElementsAre("a", "", "b")); | 
|  | }  // namespace | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace base | 
|  | }  // namespace perfetto |