blob: c5f0327d9c617ba3c35d2cfd9d2bc639f42c64a8 [file] [log] [blame]
// Copyright 2013 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 "quic/core/quic_alarm.h"
#include "quic/core/quic_connection_context.h"
#include "quic/platform/api/quic_expect_bug.h"
#include "quic/platform/api/quic_test.h"
using testing::ElementsAre;
using testing::Invoke;
using testing::Return;
namespace quic {
namespace 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_;
};
class MockDelegate : public QuicAlarm::Delegate {
public:
MOCK_METHOD(QuicConnectionContext*, GetConnectionContext, (), (override));
MOCK_METHOD(void, OnAlarm, (), (override));
};
class DestructiveDelegate : public QuicAlarm::DelegateWithoutContext {
public:
DestructiveDelegate() : alarm_(nullptr) {}
void set_alarm(QuicAlarm* alarm) { alarm_ = alarm; }
void OnAlarm() override {
QUICHE_DCHECK(alarm_);
delete alarm_;
}
private:
QuicAlarm* alarm_;
};
class TestAlarm : public QuicAlarm {
public:
explicit TestAlarm(QuicAlarm::Delegate* delegate)
: QuicAlarm(QuicArenaScopedPtr<QuicAlarm::Delegate>(delegate)) {}
bool scheduled() const { return scheduled_; }
void FireAlarm() {
scheduled_ = false;
Fire();
}
protected:
void SetImpl() override {
QUICHE_DCHECK(deadline().IsInitialized());
scheduled_ = true;
}
void CancelImpl() override {
QUICHE_DCHECK(!deadline().IsInitialized());
scheduled_ = false;
}
private:
bool scheduled_;
};
class DestructiveAlarm : public QuicAlarm {
public:
explicit DestructiveAlarm(DestructiveDelegate* delegate)
: QuicAlarm(QuicArenaScopedPtr<DestructiveDelegate>(delegate)) {}
void FireAlarm() { Fire(); }
protected:
void SetImpl() override {}
void CancelImpl() override {}
};
class QuicAlarmTest : public QuicTest {
public:
QuicAlarmTest()
: delegate_(new MockDelegate()),
alarm_(delegate_),
deadline_(QuicTime::Zero() + QuicTime::Delta::FromSeconds(7)),
deadline2_(QuicTime::Zero() + QuicTime::Delta::FromSeconds(14)),
new_deadline_(QuicTime::Zero()) {}
void ResetAlarm() { alarm_.Set(new_deadline_); }
MockDelegate* delegate_; // not owned
TestAlarm alarm_;
QuicTime deadline_;
QuicTime deadline2_;
QuicTime new_deadline_;
};
TEST_F(QuicAlarmTest, IsSet) {
EXPECT_FALSE(alarm_.IsSet());
}
TEST_F(QuicAlarmTest, Set) {
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
EXPECT_TRUE(alarm_.IsSet());
EXPECT_TRUE(alarm_.scheduled());
EXPECT_EQ(deadline, alarm_.deadline());
}
TEST_F(QuicAlarmTest, Cancel) {
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
alarm_.Cancel();
EXPECT_FALSE(alarm_.IsSet());
EXPECT_FALSE(alarm_.scheduled());
EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
}
TEST_F(QuicAlarmTest, PermanentCancel) {
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
alarm_.PermanentCancel();
EXPECT_FALSE(alarm_.IsSet());
EXPECT_FALSE(alarm_.scheduled());
EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
EXPECT_QUIC_BUG(alarm_.Set(deadline),
"Set called after alarm is permanently cancelled");
EXPECT_TRUE(alarm_.IsPermanentlyCancelled());
EXPECT_FALSE(alarm_.IsSet());
EXPECT_FALSE(alarm_.scheduled());
EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
EXPECT_QUIC_BUG(alarm_.Update(deadline, QuicTime::Delta::Zero()),
"Update called after alarm is permanently cancelled");
EXPECT_TRUE(alarm_.IsPermanentlyCancelled());
EXPECT_FALSE(alarm_.IsSet());
EXPECT_FALSE(alarm_.scheduled());
EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
}
TEST_F(QuicAlarmTest, Update) {
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
QuicTime new_deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(8);
alarm_.Update(new_deadline, QuicTime::Delta::Zero());
EXPECT_TRUE(alarm_.IsSet());
EXPECT_TRUE(alarm_.scheduled());
EXPECT_EQ(new_deadline, alarm_.deadline());
}
TEST_F(QuicAlarmTest, UpdateWithZero) {
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
alarm_.Update(QuicTime::Zero(), QuicTime::Delta::Zero());
EXPECT_FALSE(alarm_.IsSet());
EXPECT_FALSE(alarm_.scheduled());
EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
}
TEST_F(QuicAlarmTest, Fire) {
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
EXPECT_CALL(*delegate_, OnAlarm());
alarm_.FireAlarm();
EXPECT_FALSE(alarm_.IsSet());
EXPECT_FALSE(alarm_.scheduled());
EXPECT_EQ(QuicTime::Zero(), alarm_.deadline());
}
TEST_F(QuicAlarmTest, FireAndResetViaSet) {
alarm_.Set(deadline_);
new_deadline_ = deadline2_;
EXPECT_CALL(*delegate_, OnAlarm())
.WillOnce(Invoke(this, &QuicAlarmTest::ResetAlarm));
alarm_.FireAlarm();
EXPECT_TRUE(alarm_.IsSet());
EXPECT_TRUE(alarm_.scheduled());
EXPECT_EQ(deadline2_, alarm_.deadline());
}
TEST_F(QuicAlarmTest, FireDestroysAlarm) {
DestructiveDelegate* delegate(new DestructiveDelegate);
DestructiveAlarm* alarm = new DestructiveAlarm(delegate);
delegate->set_alarm(alarm);
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm->Set(deadline);
// This should not crash, even though it will destroy alarm.
alarm->FireAlarm();
}
TEST_F(QuicAlarmTest, NullAlarmContext) {
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
if (GetQuicReloadableFlag(quic_restore_connection_context_in_alarms)) {
EXPECT_CALL(*delegate_, GetConnectionContext()).WillOnce(Return(nullptr));
}
EXPECT_CALL(*delegate_, OnAlarm()).WillOnce(Invoke([] {
QUIC_TRACELITERAL("Alarm fired.");
}));
alarm_.FireAlarm();
}
TEST_F(QuicAlarmTest, AlarmContextWithNullTracer) {
QuicConnectionContext context;
ASSERT_EQ(context.tracer, nullptr);
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
if (GetQuicReloadableFlag(quic_restore_connection_context_in_alarms)) {
EXPECT_CALL(*delegate_, GetConnectionContext()).WillOnce(Return(&context));
}
EXPECT_CALL(*delegate_, OnAlarm()).WillOnce(Invoke([] {
QUIC_TRACELITERAL("Alarm fired.");
}));
alarm_.FireAlarm();
}
TEST_F(QuicAlarmTest, AlarmContextWithTracer) {
QuicConnectionContext context;
std::unique_ptr<TraceCollector> tracer = std::make_unique<TraceCollector>();
const TraceCollector& tracer_ref = *tracer;
context.tracer = std::move(tracer);
QuicTime deadline = QuicTime::Zero() + QuicTime::Delta::FromSeconds(7);
alarm_.Set(deadline);
if (GetQuicReloadableFlag(quic_restore_connection_context_in_alarms)) {
EXPECT_CALL(*delegate_, GetConnectionContext()).WillOnce(Return(&context));
}
EXPECT_CALL(*delegate_, OnAlarm()).WillOnce(Invoke([] {
QUIC_TRACELITERAL("Alarm fired.");
}));
// Since |context| is not installed in the current thread, the messages before
// and after FireAlarm() should not be collected by |tracer|.
QUIC_TRACELITERAL("Should not be collected before alarm.");
alarm_.FireAlarm();
QUIC_TRACELITERAL("Should not be collected after alarm.");
if (GetQuicReloadableFlag(quic_restore_connection_context_in_alarms)) {
EXPECT_THAT(tracer_ref.trace(), ElementsAre("Alarm fired."));
} else {
EXPECT_TRUE(tracer_ref.trace().empty());
}
}
} // namespace
} // namespace test
} // namespace quic