blob: b3a7d4d403a4f5a5a16bd01a71a44695b06cc63e [file] [log] [blame]
/*
* 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 <vector>
#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<base::StringView> all_lines;
std::vector<base::StringView> all_words;
std::vector<base::StringView> 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<std::string> 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