blob: 1f9d4652b813cb49de457fe12ca797069b4a234a [file] [log] [blame]
// Copyright 2022 The Fuchsia 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 "src/devices/bin/driver_manager/shutdown/node_removal_tracker.h"
#include <gtest/gtest.h>
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
struct NodeBank {
NodeBank(driver_manager::NodeRemovalTracker *tracker) : tracker_(tracker) {}
void AddNode(driver_manager::Collection collection, driver_manager::NodeState state) {
ids_.insert(tracker_->RegisterNode(driver_manager::NodeRemovalTracker::Node{
.name = "node",
.collection = collection,
.state = state,
}));
}
void NotifyRemovalComplete() {
for (driver_manager::NodeId id : ids_) {
tracker_->Notify(id, driver_manager::NodeState::kStopped);
}
}
std::set<driver_manager::NodeId> ids_;
driver_manager::NodeRemovalTracker *tracker_;
};
class NodeRemovalTrackerTest : public gtest::TestLoopFixture {};
TEST_F(NodeRemovalTrackerTest, RegisterOneNode) {
driver_manager::NodeRemovalTracker tracker(dispatcher());
driver_manager::NodeId id = tracker.RegisterNode(driver_manager::NodeRemovalTracker::Node{
.name = "node",
.collection = driver_manager::Collection::kBoot,
.state = driver_manager::NodeState::kRunning,
});
int package_callbacks = 0;
int all_callbacks = 0;
tracker.set_pkg_callback([&package_callbacks]() { package_callbacks++; });
tracker.set_all_callback([&all_callbacks]() { all_callbacks++; });
tracker.FinishEnumeration();
tracker.Notify(id, driver_manager::NodeState::kStopped);
EXPECT_EQ(package_callbacks, 1);
EXPECT_EQ(all_callbacks, 1);
}
TEST_F(NodeRemovalTrackerTest, RegisterManyNodes) {
driver_manager::NodeRemovalTracker tracker(dispatcher());
NodeBank node_bank(&tracker);
node_bank.AddNode(driver_manager::Collection::kBoot, driver_manager::NodeState::kRunning);
node_bank.AddNode(driver_manager::Collection::kBoot, driver_manager::NodeState::kRunning);
node_bank.AddNode(driver_manager::Collection::kPackage, driver_manager::NodeState::kRunning);
node_bank.AddNode(driver_manager::Collection::kPackage, driver_manager::NodeState::kRunning);
int package_callbacks = 0;
int all_callbacks = 0;
tracker.set_pkg_callback([&package_callbacks]() { package_callbacks++; });
tracker.set_all_callback([&all_callbacks]() { all_callbacks++; });
tracker.FinishEnumeration();
EXPECT_EQ(package_callbacks, 0);
EXPECT_EQ(all_callbacks, 0);
node_bank.NotifyRemovalComplete();
EXPECT_EQ(package_callbacks, 1);
EXPECT_EQ(all_callbacks, 1);
}
// Make sure package callback is only called when package drivers stop
// and all callback is only called when all drivers stop
TEST_F(NodeRemovalTrackerTest, CallbacksCallOrder) {
driver_manager::NodeRemovalTracker tracker(dispatcher());
NodeBank boot_node_bank(&tracker), package_node_bank(&tracker);
boot_node_bank.AddNode(driver_manager::Collection::kBoot, driver_manager::NodeState::kRunning);
boot_node_bank.AddNode(driver_manager::Collection::kBoot, driver_manager::NodeState::kRunning);
package_node_bank.AddNode(driver_manager::Collection::kPackage,
driver_manager::NodeState::kRunning);
package_node_bank.AddNode(driver_manager::Collection::kPackage,
driver_manager::NodeState::kRunning);
int package_callbacks = 0;
int all_callbacks = 0;
tracker.set_pkg_callback([&package_callbacks]() { package_callbacks++; });
tracker.set_all_callback([&all_callbacks]() { all_callbacks++; });
EXPECT_EQ(package_callbacks, 0);
EXPECT_EQ(all_callbacks, 0);
tracker.FinishEnumeration();
package_node_bank.NotifyRemovalComplete();
EXPECT_EQ(package_callbacks, 1);
EXPECT_EQ(all_callbacks, 0);
boot_node_bank.NotifyRemovalComplete();
EXPECT_EQ(package_callbacks, 1);
EXPECT_EQ(all_callbacks, 1);
}
// This tests verifies that set_all_callback can be called
// during the pkg_callback without causing a deadlock.
TEST_F(NodeRemovalTrackerTest, CallbackDeadlock) {
driver_manager::NodeRemovalTracker tracker(dispatcher());
driver_manager::NodeId id = tracker.RegisterNode(driver_manager::NodeRemovalTracker::Node{
.name = "node",
.collection = driver_manager::Collection::kBoot,
.state = driver_manager::NodeState::kRunning,
});
int package_callbacks = 0;
int all_callbacks = 0;
tracker.set_pkg_callback([&tracker, &package_callbacks, &all_callbacks]() {
package_callbacks++;
tracker.set_all_callback([&all_callbacks]() { all_callbacks++; });
});
tracker.FinishEnumeration();
tracker.Notify(id, driver_manager::NodeState::kStopped);
EXPECT_EQ(package_callbacks, 1);
EXPECT_EQ(all_callbacks, 1);
}
// Make sure callbacks are not called until FinishEnumeration is called
TEST_F(NodeRemovalTrackerTest, FinishEnumeration) {
driver_manager::NodeRemovalTracker tracker(dispatcher());
NodeBank node_bank(&tracker);
node_bank.AddNode(driver_manager::Collection::kBoot, driver_manager::NodeState::kRunning);
node_bank.AddNode(driver_manager::Collection::kBoot, driver_manager::NodeState::kRunning);
node_bank.AddNode(driver_manager::Collection::kPackage, driver_manager::NodeState::kRunning);
node_bank.AddNode(driver_manager::Collection::kPackage, driver_manager::NodeState::kRunning);
int package_callbacks = 0;
int all_callbacks = 0;
tracker.set_pkg_callback([&package_callbacks]() { package_callbacks++; });
tracker.set_all_callback([&all_callbacks]() { all_callbacks++; });
EXPECT_EQ(package_callbacks, 0);
EXPECT_EQ(all_callbacks, 0);
node_bank.NotifyRemovalComplete();
EXPECT_EQ(package_callbacks, 0);
EXPECT_EQ(all_callbacks, 0);
tracker.FinishEnumeration();
EXPECT_EQ(package_callbacks, 1);
EXPECT_EQ(all_callbacks, 1);
}