blob: 1702fd66b3a344e6c230e57059f6040b32726d92 [file] [log] [blame] [edit]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quiche/quic/core/quic_connection_context.h"
#include <memory>
#include <string>
#include <vector>
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/platform/api/quic_thread.h"
using testing::ElementsAre;
namespace quic::test {
namespace {
class TraceCollector : public QuicConnectionTracer {
public:
~TraceCollector() override = default;
void PrintLiteral(const char* literal) override { trace_.push_back(literal); }
void PrintString(absl::string_view s) override {
trace_.push_back(std::string(s));
}
const std::vector<std::string>& trace() const { return trace_; }
private:
std::vector<std::string> trace_;
};
struct FakeConnection {
FakeConnection() { context.tracer = std::make_unique<TraceCollector>(); }
const std::vector<std::string>& trace() const {
return static_cast<const TraceCollector*>(context.tracer.get())->trace();
}
QuicConnectionContext context;
};
void SimpleSwitch() {
FakeConnection connection;
// These should be ignored since current context is nullptr.
EXPECT_EQ(QuicConnectionContext::Current(), nullptr);
QUIC_TRACELITERAL("before switch: literal");
QUIC_TRACESTRING(std::string("before switch: string"));
QUIC_TRACEPRINTF("%s: %s", "before switch", "printf");
{
QuicConnectionContextSwitcher switcher(&connection.context);
QUIC_TRACELITERAL("literal");
QUIC_TRACESTRING(std::string("string"));
QUIC_TRACEPRINTF("%s", "printf");
}
EXPECT_EQ(QuicConnectionContext::Current(), nullptr);
QUIC_TRACELITERAL("after switch: literal");
QUIC_TRACESTRING(std::string("after switch: string"));
QUIC_TRACEPRINTF("%s: %s", "after switch", "printf");
EXPECT_THAT(connection.trace(), ElementsAre("literal", "string", "printf"));
}
void NestedSwitch() {
FakeConnection outer, inner;
{
QuicConnectionContextSwitcher switcher(&outer.context);
QUIC_TRACELITERAL("outer literal 0");
QUIC_TRACESTRING(std::string("outer string 0"));
QUIC_TRACEPRINTF("%s %s %d", "outer", "printf", 0);
{
QuicConnectionContextSwitcher nested_switcher(&inner.context);
QUIC_TRACELITERAL("inner literal");
QUIC_TRACESTRING(std::string("inner string"));
QUIC_TRACEPRINTF("%s %s", "inner", "printf");
}
QUIC_TRACELITERAL("outer literal 1");
QUIC_TRACESTRING(std::string("outer string 1"));
QUIC_TRACEPRINTF("%s %s %d", "outer", "printf", 1);
}
EXPECT_THAT(outer.trace(), ElementsAre("outer literal 0", "outer string 0",
"outer printf 0", "outer literal 1",
"outer string 1", "outer printf 1"));
EXPECT_THAT(inner.trace(),
ElementsAre("inner literal", "inner string", "inner printf"));
}
void AlternatingSwitch() {
FakeConnection zero, one, two;
for (int i = 0; i < 15; ++i) {
FakeConnection* connection =
((i % 3) == 0) ? &zero : (((i % 3) == 1) ? &one : &two);
QuicConnectionContextSwitcher switcher(&connection->context);
QUIC_TRACEPRINTF("%d", i);
}
EXPECT_THAT(zero.trace(), ElementsAre("0", "3", "6", "9", "12"));
EXPECT_THAT(one.trace(), ElementsAre("1", "4", "7", "10", "13"));
EXPECT_THAT(two.trace(), ElementsAre("2", "5", "8", "11", "14"));
}
typedef void (*ThreadFunction)();
template <ThreadFunction func>
class TestThread : public QuicThread {
public:
TestThread() : QuicThread("TestThread") {}
~TestThread() override = default;
protected:
void Run() override { func(); }
};
template <ThreadFunction func>
void RunInThreads(size_t n_threads) {
using ThreadType = TestThread<func>;
std::vector<ThreadType> threads(n_threads);
for (ThreadType& t : threads) {
t.Start();
}
for (ThreadType& t : threads) {
t.Join();
}
}
class QuicConnectionContextTest : public QuicTest {
protected:
};
TEST_F(QuicConnectionContextTest, NullTracerOK) {
FakeConnection connection;
std::unique_ptr<QuicConnectionTracer> tracer;
{
QuicConnectionContextSwitcher switcher(&connection.context);
QUIC_TRACELITERAL("msg 1 recorded");
}
connection.context.tracer.swap(tracer);
{
QuicConnectionContextSwitcher switcher(&connection.context);
// Should be a no-op since connection.context.tracer is nullptr.
QUIC_TRACELITERAL("msg 2 ignored");
}
EXPECT_THAT(static_cast<TraceCollector*>(tracer.get())->trace(),
ElementsAre("msg 1 recorded"));
}
TEST_F(QuicConnectionContextTest, TestSimpleSwitch) {
RunInThreads<SimpleSwitch>(10);
}
TEST_F(QuicConnectionContextTest, TestNestedSwitch) {
RunInThreads<NestedSwitch>(10);
}
TEST_F(QuicConnectionContextTest, TestAlternatingSwitch) {
RunInThreads<AlternatingSwitch>(10);
}
} // namespace
} // namespace quic::test