1 /*
2 * Copyright (C) 2022 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 "src/trace_processor/util/streaming_line_reader.h"
18
19 #include <algorithm>
20 #include <functional>
21 #include <random>
22 #include <string>
23 #include <vector>
24
25 #include "perfetto/ext/base/string_utils.h"
26 #include "perfetto/ext/base/sys_types.h" // For ssize_t on Windows.
27 #include "test/gtest_and_gmock.h"
28
29 namespace perfetto {
30 namespace trace_processor {
31 namespace util {
32
33 namespace {
34
35 using ::testing::ElementsAreArray;
36
37 struct LineSink {
AppendLinesCallbackperfetto::trace_processor::util::__anonbcab78d60111::LineSink38 StreamingLineReader::LinesCallback AppendLinesCallback() {
39 return [&](const std::vector<base::StringView>& lines_parsed) {
40 for (const auto& sv : lines_parsed) {
41 lines_.emplace_back(sv.ToStdString());
42 }
43 };
44 }
45
46 // Returns the lines received so far. Not idempotent, clears the vector.
GetLinesperfetto::trace_processor::util::__anonbcab78d60111::LineSink47 std::vector<std::string> GetLines() {
48 auto lines = std::move(lines_);
49 lines_.clear();
50 return lines;
51 }
52
53 std::vector<std::string> lines_;
54 };
55
TEST(StreamingLineReaderTest,Tokenize)56 TEST(StreamingLineReaderTest, Tokenize) {
57 LineSink sink;
58 StreamingLineReader slr(sink.AppendLinesCallback());
59
60 slr.Tokenize("a12\nb3456\nc\nd78\n\ne12\nf3456\n");
61 ASSERT_THAT(sink.GetLines(), ElementsAreArray({"a12", "b3456", "c", "d78", "",
62 "e12", "f3456"}));
63 }
64
TEST(StreamingLineReaderTest,BeginEndWrite)65 TEST(StreamingLineReaderTest, BeginEndWrite) {
66 LineSink sink;
67 StreamingLineReader slr(sink.AppendLinesCallback());
68
69 char* w = slr.BeginWrite(9);
70 slr.EndWrite(static_cast<size_t>(base::SprintfTrunc(w, 9, "a12\nb345")));
71 ASSERT_THAT(sink.GetLines(), ElementsAreArray({"a12"}));
72
73 w = slr.BeginWrite(9);
74 slr.EndWrite(static_cast<size_t>(base::SprintfTrunc(w, 9, "6\nc\nd78\n")));
75 ASSERT_THAT(sink.GetLines(), ElementsAreArray({"b3456", "c", "d78"}));
76
77 w = slr.BeginWrite(4); // Deliberately over-sizing the `reserve_size`.
78 slr.EndWrite(static_cast<size_t>(base::SprintfTrunc(w, 4, "\n")));
79 ASSERT_THAT(sink.GetLines(), ElementsAreArray({""}));
80
81 w = slr.BeginWrite(128); // Deliberately over-sizing the `reserve_size`.
82 slr.EndWrite(static_cast<size_t>(base::SprintfTrunc(w, 128, "e12\nf3456\n")));
83 ASSERT_THAT(sink.GetLines(), ElementsAreArray({"e12", "f3456"}));
84 }
85
86 // Creates a random text of 10000 chars which looks like the one below. Then
87 // feeds it into the SLR pushing chunks of random size. Checks that all the
88 // lines received match the original text.
TEST(StreamingLineReaderTest,RandomWrite)89 TEST(StreamingLineReaderTest, RandomWrite) {
90 LineSink sink;
91 StreamingLineReader slr(sink.AppendLinesCallback());
92 std::minstd_rand0 rnd(0);
93
94 // Builds a random string with 10k char that looks like this:
95 // geoefss1hmwgp9r6i3hlmpejjv6c4u2tsgbrwp30arkyb8b13ntek09f\n
96 // t4q\n
97 // \n
98 // vr135li3m3330gy\n
99 // ...
100 std::string expected_txt(10000, '\0');
101 static const char kRandChars[] = "\n0123456789abcdefghijklmnopqrstuvwxyz";
102 for (size_t i = 0; i < expected_txt.size(); i++)
103 expected_txt[i] = kRandChars[rnd() % strlen(kRandChars)];
104 expected_txt[expected_txt.size() - 1] = '\n';
105
106 // Push it in random chunks of max 1Kb.
107 for (auto it = expected_txt.begin(); it < expected_txt.end();) {
108 size_t wr_size = static_cast<size_t>(rnd()) % 1000ul;
109 auto avail = static_cast<size_t>(std::distance(it, expected_txt.end()));
110 wr_size = std::min(wr_size, avail);
111 memcpy(slr.BeginWrite(wr_size), &*it, wr_size);
112 slr.EndWrite(wr_size);
113 it += static_cast<ssize_t>(wr_size);
114 }
115
116 // Merge the lines received and check they match the original text.
117 std::string actual_txt;
118 actual_txt.reserve(expected_txt.size());
119 std::vector<std::string> lines = sink.GetLines();
120 for (const std::string& line : lines) {
121 actual_txt.append(line);
122 actual_txt.append("\n");
123 }
124
125 ASSERT_EQ(actual_txt, expected_txt);
126 }
127
128 } // namespace
129 } // namespace util
130 } // namespace trace_processor
131 } // namespace perfetto
132