// Copyright (c) 2015 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 "net/third_party/quic/core/quic_epoll_alarm_factory.h"

#include "net/third_party/quic/platform/api/quic_test.h"
#include "net/third_party/quic/platform/impl/quic_epoll_clock.h"
#include "net/third_party/quic/test_tools/fake_epoll_server.h"

namespace quic {
namespace test {
namespace {

class TestDelegate : public QuicAlarm::Delegate {
 public:
  TestDelegate() : fired_(false) {}

  void OnAlarm() override { fired_ = true; }

  bool fired() const { return fired_; }

 private:
  bool fired_;
};

// The boolean parameter denotes whether or not to use an arena.
class QuicEpollAlarmFactoryTest : public QuicTestWithParam<bool> {
 protected:
  QuicEpollAlarmFactoryTest()
      : clock_(&epoll_server_), alarm_factory_(&epoll_server_) {}

  QuicConnectionArena* GetArenaParam() {
    return GetParam() ? &arena_ : nullptr;
  }

  const QuicEpollClock clock_;
  QuicEpollAlarmFactory alarm_factory_;
  test::FakeEpollServer epoll_server_;
  QuicConnectionArena arena_;
};

INSTANTIATE_TEST_CASE_P(UseArena,
                        QuicEpollAlarmFactoryTest,
                        ::testing::ValuesIn({true, false}));

TEST_P(QuicEpollAlarmFactoryTest, CreateAlarm) {
  QuicArenaScopedPtr<TestDelegate> delegate =
      QuicArenaScopedPtr<TestDelegate>(new TestDelegate());
  QuicArenaScopedPtr<QuicAlarm> alarm(
      alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam()));

  QuicTime start = clock_.Now();
  QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
  alarm->Set(start + delta);

  epoll_server_.AdvanceByAndWaitForEventsAndExecuteCallbacks(
      delta.ToMicroseconds());
  EXPECT_EQ(start + delta, clock_.Now());
}

TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndCancel) {
  QuicArenaScopedPtr<TestDelegate> delegate =
      QuicArenaScopedPtr<TestDelegate>(new TestDelegate());
  TestDelegate* unowned_delegate = delegate.get();
  QuicArenaScopedPtr<QuicAlarm> alarm(
      alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam()));

  QuicTime start = clock_.Now();
  QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
  alarm->Set(start + delta);
  alarm->Cancel();

  epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds());
  EXPECT_EQ(start + delta, clock_.Now());
  EXPECT_FALSE(unowned_delegate->fired());
}

TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndReset) {
  QuicArenaScopedPtr<TestDelegate> delegate =
      QuicArenaScopedPtr<TestDelegate>(new TestDelegate());
  TestDelegate* unowned_delegate = delegate.get();
  QuicArenaScopedPtr<QuicAlarm> alarm(
      alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam()));

  QuicTime start = clock_.Now();
  QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
  alarm->Set(clock_.Now() + delta);
  alarm->Cancel();
  QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3);
  alarm->Set(clock_.Now() + new_delta);

  epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds());
  EXPECT_EQ(start + delta, clock_.Now());
  EXPECT_FALSE(unowned_delegate->fired());

  epoll_server_.AdvanceByExactlyAndCallCallbacks(
      (new_delta - delta).ToMicroseconds());
  EXPECT_EQ(start + new_delta, clock_.Now());
  EXPECT_TRUE(unowned_delegate->fired());
}

TEST_P(QuicEpollAlarmFactoryTest, CreateAlarmAndUpdate) {
  QuicArenaScopedPtr<TestDelegate> delegate =
      QuicArenaScopedPtr<TestDelegate>(new TestDelegate());
  TestDelegate* unowned_delegate = delegate.get();
  QuicArenaScopedPtr<QuicAlarm> alarm(
      alarm_factory_.CreateAlarm(std::move(delegate), GetArenaParam()));

  QuicTime start = clock_.Now();
  QuicTime::Delta delta = QuicTime::Delta::FromMicroseconds(1);
  alarm->Set(clock_.Now() + delta);
  QuicTime::Delta new_delta = QuicTime::Delta::FromMicroseconds(3);
  alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(1));

  epoll_server_.AdvanceByExactlyAndCallCallbacks(delta.ToMicroseconds());
  EXPECT_EQ(start + delta, clock_.Now());
  EXPECT_FALSE(unowned_delegate->fired());

  // Move the alarm forward 1us and ensure it doesn't move forward.
  alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(2));

  epoll_server_.AdvanceByExactlyAndCallCallbacks(
      (new_delta - delta).ToMicroseconds());
  EXPECT_EQ(start + new_delta, clock_.Now());
  EXPECT_TRUE(unowned_delegate->fired());

  // Set the alarm via an update call.
  new_delta = QuicTime::Delta::FromMicroseconds(5);
  alarm->Update(clock_.Now() + new_delta, QuicTime::Delta::FromMicroseconds(1));
  EXPECT_TRUE(alarm->IsSet());

  // Update it with an uninitialized time and ensure it's cancelled.
  alarm->Update(QuicTime::Zero(), QuicTime::Delta::FromMicroseconds(1));
  EXPECT_FALSE(alarm->IsSet());
}

}  // namespace
}  // namespace test
}  // namespace quic
