/* * Copyright (C) 2024 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_view_splitter.h" #include #include "test/gtest_and_gmock.h" namespace perfetto { namespace base { namespace { using testing::ElementsAreArray; TEST(StringViewSplitterTest, StdString) { { StringViewSplitter ss("", 'x'); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } { StringViewSplitter ss("", 'x'); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } { StringViewSplitter ss("a", 'x'); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); } { StringViewSplitter ss("abc", 'x'); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("abc", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); } { StringViewSplitter ss("ab,", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("ab", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); } { StringViewSplitter ss(",ab,", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("ab", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); } { StringViewSplitter ss("a,b,c", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("b", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("c", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } { StringViewSplitter ss("a,b,c,", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("b", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("c", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } { StringViewSplitter ss(",,a,,b,,,,c,,,", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("b", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("c", ss.cur_token().ToStdString().c_str()); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); } } { StringViewSplitter ss(",,", ','); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } { StringViewSplitter ss(",,foo", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); } } TEST(StringViewSplitterTest, CString) { { StringViewSplitter ss("foo\nbar\n\nbaz\n", '\n'); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("baz", ss.cur_token().ToStdString().c_str()); EXPECT_FALSE(ss.Next()); } { StringViewSplitter ss("", ','); EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } { StringViewSplitter ss(",,foo,bar\0,baz", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str()); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); } } { StringViewSplitter ss(",,a\0,b,", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str()); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } { StringViewSplitter ss(",a,\0b", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str()); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } { StringViewSplitter ss(",a\0\0,x\0\0b", ','); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("a", ss.cur_token().ToStdString().c_str()); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } } TEST(StringViewSplitterTest, SplitOnNUL) { { StringViewSplitter ss("", '\0'); EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } { std::string str; str.resize(48); memcpy(&str[0], "foo\0", 4); memcpy(&str[4], "bar\0", 4); memcpy(&str[20], "baz", 3); StringViewSplitter ss(base::StringView(std::move(str)), '\0'); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("baz", ss.cur_token().ToStdString().c_str()); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } { char buf[] = "foo\0bar\0baz\0"; StringViewSplitter ss(base::StringView("foo\0bar\0baz\0", sizeof(buf)), '\0'); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("baz", ss.cur_token().ToStdString().c_str()); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } { char buf[] = "\0\0foo\0\0\0\0bar\0baz\0\0"; StringViewSplitter ss(base::StringView(buf, sizeof(buf)), '\0'); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("foo", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("bar", ss.cur_token().ToStdString().c_str()); EXPECT_TRUE(ss.Next()); EXPECT_STREQ("baz", ss.cur_token().ToStdString().c_str()); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } { StringViewSplitter ss("", '\0'); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } { StringViewSplitter ss("\0", '\0'); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } { StringViewSplitter ss("\0\0", '\0'); for (int i = 0; i < 3; i++) { EXPECT_FALSE(ss.Next()); EXPECT_STREQ("", ss.cur_token().ToStdString().c_str()); } } } TEST(StringViewSplitterTest, NestedUsage) { char text[] = R"( l1w1 l1w2 l1w3 ,l,2,w,1 l,2,,w,,2,, )"; std::vector all_lines; std::vector all_words; std::vector all_tokens; for (StringViewSplitter lines(base::StringView(text), '\n'); lines.Next();) { all_lines.push_back(lines.cur_token()); for (StringViewSplitter words(&lines, ' '); words.Next();) { all_words.push_back(words.cur_token()); for (StringViewSplitter 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(StringViewSplitterTest, EmptyTokens) { std::vector tokens; for (StringViewSplitter lines( "a,,b", ',', StringViewSplitter::EmptyTokenMode::ALLOW_EMPTY_TOKENS); lines.Next();) { tokens.push_back(lines.cur_token().ToStdString()); } EXPECT_THAT(tokens, testing::ElementsAre("a", "", "b")); } // namespace } // namespace } // namespace base } // namespace perfetto