blob: ffb1feddb9193f7e96a44fa8811b36426c5e8ff4 [file] [log] [blame]
/*
* 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 <algorithm>
#include <functional>
#include <random>
#include <string>
#include <vector>
#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<base::StringView>& 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<std::string> GetLines() {
auto lines = std::move(lines_);
lines_.clear();
return lines;
}
std::vector<std::string> 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<size_t>(base::SprintfTrunc(w, 9, "a12\nb345")));
ASSERT_THAT(sink.GetLines(), ElementsAreArray({"a12"}));
w = slr.BeginWrite(9);
slr.EndWrite(static_cast<size_t>(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<size_t>(base::SprintfTrunc(w, 4, "\n")));
ASSERT_THAT(sink.GetLines(), ElementsAreArray({""}));
w = slr.BeginWrite(128); // Deliberately over-sizing the `reserve_size`.
slr.EndWrite(static_cast<size_t>(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<size_t>(rnd()) % 1000ul;
auto avail = static_cast<size_t>(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<ssize_t>(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<std::string> 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