blob: 543900492278260b2b905a65388be3fafac8d02d [file] [log] [blame] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "heartd/daemon/heartbeat_tracker.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/functional/callback_forward.h"
#include <base/notreached.h>
#include <base/test/task_environment.h>
#include <base/test/test_future.h>
#include <base/time/time.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <mojo/public/cpp/bindings/remote.h>
#include "heartd/mojom/heartd.mojom.h"
namespace heartd {
namespace {
namespace mojom = ::ash::heartd::mojom;
class HeartbeatTrackerTest : public testing::Test {
public:
HeartbeatTrackerTest() {
heartbeat_tracker_ = std::make_unique<HeartbeatTracker>(
mojom::ServiceName::kKiosk, pacemaker_.BindNewPipeAndPassReceiver());
}
~HeartbeatTrackerTest() override = default;
mojom::HeartbeatResponse SendHeartbeatSync() {
base::test::TestFuture<mojom::HeartbeatResponse> test_future;
pacemaker_->SendHeartbeat(test_future.GetCallback());
if (!test_future.Wait()) {
NOTREACHED_NORETURN();
}
return test_future.Get();
}
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
mojo::Remote<mojom::Pacemaker> pacemaker_;
std::unique_ptr<HeartbeatTracker> heartbeat_tracker_ = nullptr;
};
TEST_F(HeartbeatTrackerTest, DefaultValueAfterCreation) {
EXPECT_TRUE(heartbeat_tracker_->IsPacemakerBound());
EXPECT_FALSE(heartbeat_tracker_->IsStopMonitor());
EXPECT_EQ(heartbeat_tracker_->GetFailureCount(), 0);
}
TEST_F(HeartbeatTrackerTest, PacemakerStopMonitor) {
base::test::TestFuture<void> test_future;
pacemaker_->StopMonitor(test_future.GetCallback());
if (!test_future.Wait()) {
NOTREACHED_NORETURN();
}
EXPECT_TRUE(heartbeat_tracker_->IsStopMonitor());
}
TEST_F(HeartbeatTrackerTest, PacemakerDisconnect) {
pacemaker_.reset();
task_environment_.RunUntilIdle();
EXPECT_FALSE(heartbeat_tracker_->IsPacemakerBound());
}
TEST_F(HeartbeatTrackerTest, RebindPacemaker) {
EXPECT_TRUE(heartbeat_tracker_->IsPacemakerBound());
pacemaker_.reset();
task_environment_.RunUntilIdle();
EXPECT_FALSE(heartbeat_tracker_->IsPacemakerBound());
heartbeat_tracker_->RebindPacemaker(pacemaker_.BindNewPipeAndPassReceiver());
EXPECT_TRUE(heartbeat_tracker_->IsPacemakerBound());
}
TEST_F(HeartbeatTrackerTest, VerifyTimeGap) {
mojom::HeartbeatResponse resp = SendHeartbeatSync();
EXPECT_EQ(resp, mojom::HeartbeatResponse::kSuccess);
task_environment_.FastForwardBy(base::Seconds(10));
EXPECT_TRUE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
// Without sending heartbeat and move on kMinVerificationWindow seconds.
task_environment_.FastForwardBy(kMinVerificationWindow);
EXPECT_FALSE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
}
TEST_F(HeartbeatTrackerTest, SetVerificationWindowArgument) {
auto argument = mojom::HeartbeatServiceArgument::New();
auto verification_window_seconds = kMinVerificationWindow.InSeconds() + 10;
argument->verification_window_seconds = verification_window_seconds;
heartbeat_tracker_->SetupArgument(std::move(argument));
mojom::HeartbeatResponse resp = SendHeartbeatSync();
EXPECT_EQ(resp, mojom::HeartbeatResponse::kSuccess);
task_environment_.FastForwardBy(base::Seconds(verification_window_seconds));
EXPECT_TRUE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
task_environment_.FastForwardBy(base::Seconds(1));
EXPECT_FALSE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
}
TEST_F(HeartbeatTrackerTest, SetFailureCountActionArgument) {
auto action_noop = mojom::Action::New(
/*failure_count = */ 2, mojom::ActionType::kNoOperation);
auto action_reboot = mojom::Action::New(
/*failure_count = */ 3, mojom::ActionType::kNormalReboot);
auto argument = mojom::HeartbeatServiceArgument::New();
argument->actions.push_back(action_noop->Clone());
argument->actions.push_back(std::move(action_noop));
argument->actions.push_back(std::move(action_reboot));
heartbeat_tracker_->SetupArgument(std::move(argument));
mojom::HeartbeatResponse resp = SendHeartbeatSync();
EXPECT_EQ(resp, mojom::HeartbeatResponse::kSuccess);
task_environment_.FastForwardBy(kMinVerificationWindow + base::Seconds(1));
// VerifyTimeGap increases the failure count to 1.
EXPECT_FALSE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
auto actions = heartbeat_tracker_->GetFailureCountActions();
std::vector<mojom::ActionType> expected_actions;
EXPECT_EQ(actions, expected_actions);
// VerifyTimeGap increases the failure count to 2.
EXPECT_FALSE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
actions = heartbeat_tracker_->GetFailureCountActions();
expected_actions = {mojom::ActionType::kNoOperation,
mojom::ActionType::kNoOperation};
EXPECT_EQ(actions, expected_actions);
// VerifyTimeGap increases the failure count to 3.
EXPECT_FALSE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
actions = heartbeat_tracker_->GetFailureCountActions();
expected_actions = {mojom::ActionType::kNormalReboot};
EXPECT_EQ(actions, expected_actions);
// VerifyTimeGap increases the failure count to 4.
// We should still get the reboot action.
EXPECT_FALSE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
actions = heartbeat_tracker_->GetFailureCountActions();
expected_actions = {mojom::ActionType::kNormalReboot};
EXPECT_EQ(actions, expected_actions);
}
TEST_F(HeartbeatTrackerTest, ResetFailureCount) {
mojom::HeartbeatResponse resp = SendHeartbeatSync();
EXPECT_EQ(resp, mojom::HeartbeatResponse::kSuccess);
task_environment_.FastForwardBy(kMinVerificationWindow + base::Seconds(1));
EXPECT_FALSE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
EXPECT_EQ(static_cast<int>(heartbeat_tracker_->GetFailureCount()), 1);
resp = SendHeartbeatSync();
EXPECT_EQ(resp, mojom::HeartbeatResponse::kSuccess);
EXPECT_TRUE(heartbeat_tracker_->VerifyTimeGap(base::Time().Now()));
EXPECT_EQ(static_cast<int>(heartbeat_tracker_->GetFailureCount()), 0);
}
} // namespace
} // namespace heartd