/* * Copyright (C) 2022 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 "src/trace_processor/util/streaming_line_reader.h" #include #include #include #include #include #include "perfetto/ext/base/string_utils.h" #include "perfetto/ext/base/sys_types.h" // For ssize_t on Windows. #include "test/gtest_and_gmock.h" namespace perfetto { namespace trace_processor { namespace util { namespace { using ::testing::ElementsAreArray; struct LineSink { StreamingLineReader::LinesCallback AppendLinesCallback() { return [&](const std::vector& lines_parsed) { for (const auto& sv : lines_parsed) { lines_.emplace_back(sv.ToStdString()); } }; } // Returns the lines received so far. Not idempotent, clears the vector. std::vector GetLines() { auto lines = std::move(lines_); lines_.clear(); return lines; } std::vector lines_; }; TEST(StreamingLineReaderTest, Tokenize) { LineSink sink; StreamingLineReader slr(sink.AppendLinesCallback()); slr.Tokenize("a12\nb3456\nc\nd78\n\ne12\nf3456\n"); ASSERT_THAT(sink.GetLines(), ElementsAreArray({"a12", "b3456", "c", "d78", "", "e12", "f3456"})); } TEST(StreamingLineReaderTest, BeginEndWrite) { LineSink sink; StreamingLineReader slr(sink.AppendLinesCallback()); char* w = slr.BeginWrite(9); slr.EndWrite(static_cast(base::SprintfTrunc(w, 9, "a12\nb345"))); ASSERT_THAT(sink.GetLines(), ElementsAreArray({"a12"})); w = slr.BeginWrite(9); slr.EndWrite(static_cast(base::SprintfTrunc(w, 9, "6\nc\nd78\n"))); ASSERT_THAT(sink.GetLines(), ElementsAreArray({"b3456", "c", "d78"})); w = slr.BeginWrite(4); // Deliberately over-sizing the `reserve_size`. slr.EndWrite(static_cast(base::SprintfTrunc(w, 4, "\n"))); ASSERT_THAT(sink.GetLines(), ElementsAreArray({""})); w = slr.BeginWrite(128); // Deliberately over-sizing the `reserve_size`. slr.EndWrite(static_cast(base::SprintfTrunc(w, 128, "e12\nf3456\n"))); ASSERT_THAT(sink.GetLines(), ElementsAreArray({"e12", "f3456"})); } // Creates a random text of 10000 chars which looks like the one below. Then // feeds it into the SLR pushing chunks of random size. Checks that all the // lines received match the original text. TEST(StreamingLineReaderTest, RandomWrite) { LineSink sink; StreamingLineReader slr(sink.AppendLinesCallback()); std::minstd_rand0 rnd(0); // Builds a random string with 10k char that looks like this: // geoefss1hmwgp9r6i3hlmpejjv6c4u2tsgbrwp30arkyb8b13ntek09f\n // t4q\n // \n // vr135li3m3330gy\n // ... std::string expected_txt(10000, '\0'); static const char kRandChars[] = "\n0123456789abcdefghijklmnopqrstuvwxyz"; for (size_t i = 0; i < expected_txt.size(); i++) expected_txt[i] = kRandChars[rnd() % strlen(kRandChars)]; expected_txt[expected_txt.size() - 1] = '\n'; // Push it in random chunks of max 1Kb. for (auto it = expected_txt.begin(); it < expected_txt.end();) { size_t wr_size = static_cast(rnd()) % 1000ul; auto avail = static_cast(std::distance(it, expected_txt.end())); wr_size = std::min(wr_size, avail); memcpy(slr.BeginWrite(wr_size), &*it, wr_size); slr.EndWrite(wr_size); it += static_cast(wr_size); } // Merge the lines received and check they match the original text. std::string actual_txt; actual_txt.reserve(expected_txt.size()); std::vector lines = sink.GetLines(); for (const std::string& line : lines) { actual_txt.append(line); actual_txt.append("\n"); } ASSERT_EQ(actual_txt, expected_txt); } } // namespace } // namespace util } // namespace trace_processor } // namespace perfetto