blob: bf5409259133d24e9f1ba8437f9bc1cbbcb78ef9 [file] [log] [blame]
// Copyright 2024 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/developer/forensics/exceptions/handler/wake_lease.h"
#include <fidl/fuchsia.power.broker/cpp/fidl.h>
#include <fidl/fuchsia.power.broker/cpp/test_base.h>
#include <fidl/fuchsia.power.system/cpp/fidl.h>
#include <fidl/fuchsia.power.system/cpp/test_base.h>
#include <lib/async/cpp/executor.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/wire/channel.h>
#include <lib/fpromise/promise.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/zx/time.h>
#include <zircon/errors.h>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <gtest/gtest.h>
#include "src/developer/forensics/exceptions/constants.h"
#include "src/developer/forensics/testing/gpretty_printers.h"
#include "src/developer/forensics/testing/stubs/power_broker_lessor.h"
#include "src/developer/forensics/testing/stubs/power_broker_topology.h"
#include "src/developer/forensics/testing/stubs/system_activity_governor.h"
#include "src/developer/forensics/testing/unit_test_fixture.h"
#include "src/developer/forensics/utils/errors.h"
namespace forensics::exceptions::handler {
namespace {
using ::fidl::testing::TestBase;
constexpr zx::duration kTimeout = zx::sec(5);
namespace fpb = fuchsia_power_broker;
namespace fps = fuchsia_power_system;
class WakeLeaseTest : public UnitTestFixture {
protected:
WakeLeaseTest() : executor_(dispatcher()) {}
async::Executor& GetExecutor() { return executor_; }
template <typename Impl>
static std::optional<std::tuple<fidl::ClientEnd<fps::ActivityGovernor>, std::unique_ptr<Impl>>>
CreateSag(async_dispatcher_t* dispatcher) {
static_assert(std::is_base_of_v<TestBase<fps::ActivityGovernor>, Impl>);
auto endpoints = fidl::CreateEndpoints<fps::ActivityGovernor>();
if (!endpoints.is_ok()) {
return std::nullopt;
}
auto stub = std::make_unique<Impl>(std::move(endpoints->server), dispatcher);
return std::make_tuple(std::move(endpoints->client), std::move(stub));
}
template <typename Impl>
static std::optional<std::tuple<fidl::ClientEnd<fpb::Topology>, std::unique_ptr<Impl>>>
CreateTopology(async_dispatcher_t* dispatcher,
typename Impl::ConstructLessorFn construct_lessor) {
static_assert(std::is_base_of_v<TestBase<fpb::Topology>, Impl>);
auto endpoints = fidl::CreateEndpoints<fpb::Topology>();
if (!endpoints.is_ok()) {
return std::nullopt;
}
auto stub = std::make_unique<Impl>(std::move(endpoints->server), dispatcher, construct_lessor);
return std::make_tuple(std::move(endpoints->client), std::move(stub));
}
private:
async::Executor executor_;
};
TEST_F(WakeLeaseTest, AcquiresLeaseSuccessfully) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernor>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopology>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessor>(std::move(server_end), dispatcher,
fpb::LeaseStatus::kSatisfied);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
std::optional<fidl::Client<fuchsia_power_broker::LeaseControl>> lease;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([&lease](fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
lease = std::move(acquired_lease);
})
.or_else([](const Error& error) {
FX_LOGS(FATAL) << "Unexpected error while acquiring lease: " << ToString(error);
}));
RunLoopUntilIdle();
ASSERT_TRUE(lease.has_value());
EXPECT_TRUE(lease->is_valid());
EXPECT_TRUE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_TRUE(topology->IsLeaseActive("exceptions-element-001"));
ASSERT_EQ(topology->Dependencies("exceptions-element-001").size(), 1u);
EXPECT_EQ(topology->Dependencies("exceptions-element-001").front().dependency_type(),
fpb::DependencyType::kPassive);
EXPECT_EQ(topology->Dependencies("exceptions-element-001").front().dependent_level(),
kPowerLevelActive);
EXPECT_EQ(topology->Dependencies("exceptions-element-001")
.front()
.requires_level_by_preference()
.front(),
fidl::ToUnderlying(fps::ExecutionStateLevel::kWakeHandling));
}
TEST_F(WakeLeaseTest, AddsElementOnlyOnce) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernor>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopology>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessor>(std::move(server_end), dispatcher,
fpb::LeaseStatus::kSatisfied);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
{
std::optional<fidl::Client<fuchsia_power_broker::LeaseControl>> lease;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([&lease](fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
lease = std::move(acquired_lease);
})
.or_else([](const Error& error) {
FX_LOGS(FATAL) << "Unexpected error while acquiring lease: " << ToString(error);
}));
RunLoopUntilIdle();
ASSERT_TRUE(lease.has_value());
EXPECT_TRUE(lease->is_valid());
EXPECT_TRUE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_TRUE(topology->IsLeaseActive("exceptions-element-001"));
}
// Lease fell out of scope.
RunLoopUntilIdle();
EXPECT_TRUE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_FALSE(topology->IsLeaseActive("exceptions-element-001"));
std::optional<fidl::Client<fuchsia_power_broker::LeaseControl>> lease;
// Acquiring a lease again would check-fail if the element was added to the topology twice.
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([&lease](fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
lease = std::move(acquired_lease);
})
.or_else([](const Error& error) {
FX_LOGS(FATAL) << "Unexpected error while acquiring lease: " << ToString(error);
}));
RunLoopUntilIdle();
ASSERT_TRUE(lease.has_value());
EXPECT_TRUE(lease->is_valid());
EXPECT_TRUE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_TRUE(topology->IsLeaseActive("exceptions-element-001"));
}
TEST_F(WakeLeaseTest, WaitsForAddElementToComplete) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernor>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopologyDelaysResponse>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessor>(std::move(server_end), dispatcher,
fpb::LeaseStatus::kSatisfied);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
std::optional<fidl::Client<fuchsia_power_broker::LeaseControl>> lease;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([&lease](fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
lease = std::move(acquired_lease);
})
.or_else([](const Error& error) {
FX_LOGS(FATAL) << "Unexpected error while acquiring lease: " << ToString(error);
}));
// The element is in the topology, but the topology hasn't returned a response to WakeLease yet
// because PopResponse hasn't been called.
RunLoopUntilIdle();
EXPECT_FALSE(lease.has_value());
EXPECT_TRUE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_FALSE(topology->IsLeaseActive("exceptions-element-001"));
std::optional<fidl::Client<fuchsia_power_broker::LeaseControl>> lease2;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([&lease2](fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
lease2 = std::move(acquired_lease);
})
.or_else([](const Error& error) {
FX_LOGS(FATAL) << "Unexpected error while acquiring lease: " << ToString(error);
}));
RunLoopUntilIdle();
EXPECT_FALSE(lease.has_value());
EXPECT_FALSE(lease2.has_value());
topology->PopResponse();
RunLoopUntilIdle();
ASSERT_TRUE(lease.has_value());
ASSERT_TRUE(lease2.has_value());
EXPECT_TRUE(lease->is_valid());
EXPECT_TRUE(lease2->is_valid());
EXPECT_TRUE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_TRUE(topology->IsLeaseActive("exceptions-element-001"));
}
TEST_F(WakeLeaseTest, WaitsUntilLeaseSatisfied) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernor>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopology>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessor>(std::move(server_end), dispatcher,
fpb::LeaseStatus::kPending);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
std::optional<fidl::Client<fuchsia_power_broker::LeaseControl>> lease;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([&lease](fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
lease = std::move(acquired_lease);
})
.or_else([](const Error& error) {
FX_LOGS(FATAL) << "Unexpected error while acquiring lease: " << ToString(error);
}));
RunLoopUntilIdle();
EXPECT_TRUE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_TRUE(topology->IsLeaseActive("exceptions-element-001"));
ASSERT_EQ(topology->Dependencies("exceptions-element-001").size(), 1u);
EXPECT_EQ(topology->Dependencies("exceptions-element-001").front().dependency_type(),
fpb::DependencyType::kPassive);
EXPECT_EQ(topology->Dependencies("exceptions-element-001").front().dependent_level(),
kPowerLevelActive);
EXPECT_EQ(topology->Dependencies("exceptions-element-001")
.front()
.requires_level_by_preference()
.front(),
fidl::ToUnderlying(fps::ExecutionStateLevel::kWakeHandling));
EXPECT_FALSE(lease.has_value());
topology->SetLeaseStatus("exceptions-element-001", fuchsia_power_broker::LeaseStatus::kSatisfied);
RunLoopUntilIdle();
ASSERT_TRUE(lease.has_value());
EXPECT_TRUE(lease->is_valid());
}
TEST_F(WakeLeaseTest, GetPowerElementsFails) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernorClosesConnection>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopology>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessor>(std::move(server_end), dispatcher,
fpb::LeaseStatus::kSatisfied);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
std::optional<Error> error;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([](const fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
FX_LOGS(FATAL) << "Unexpected success while acquiring lease";
})
.or_else([&error](const Error& result) { error = result; }));
RunLoopUntilIdle();
ASSERT_EQ(error, Error::kBadValue);
EXPECT_FALSE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_FALSE(topology->IsLeaseActive("exceptions-element-001"));
}
TEST_F(WakeLeaseTest, GracefulSubsequentFailuresAfterFailureToAddElement) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernorClosesConnection>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopology>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessor>(std::move(server_end), dispatcher,
fpb::LeaseStatus::kSatisfied);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
std::optional<Error> error;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([](const fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
FX_LOGS(FATAL) << "Unexpected success while acquiring lease";
})
.or_else([&error](const Error& result) { error = result; }));
RunLoopUntilIdle();
ASSERT_EQ(error, Error::kBadValue);
EXPECT_FALSE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_FALSE(topology->IsLeaseActive("exceptions-element-001"));
// Subsequent requests should also fail gracefully.
error = std::nullopt;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([](const fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
FX_LOGS(FATAL) << "Unexpected success while acquiring lease";
})
.or_else([&error](const Error& result) { error = result; }));
RunLoopUntilIdle();
ASSERT_EQ(error, Error::kBadValue);
EXPECT_FALSE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_FALSE(topology->IsLeaseActive("exceptions-element-001"));
}
TEST_F(WakeLeaseTest, GetPowerElementsNoSagPowerElements) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernorNoPowerElements>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopology>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessor>(std::move(server_end), dispatcher,
fpb::LeaseStatus::kSatisfied);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
std::optional<Error> error;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([](const fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
FX_LOGS(FATAL) << "Unexpected success while acquiring lease";
})
.or_else([&error](const Error& result) { error = result; }));
RunLoopUntilIdle();
ASSERT_EQ(error, Error::kBadValue);
EXPECT_FALSE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_FALSE(topology->IsLeaseActive("exceptions-element-001"));
}
TEST_F(WakeLeaseTest, GetPowerElementsNoTokens) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernorNoTokens>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopology>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessor>(std::move(server_end), dispatcher,
fpb::LeaseStatus::kSatisfied);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
std::optional<Error> error;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([](const fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
FX_LOGS(FATAL) << "Unexpected success while acquiring lease";
})
.or_else([&error](const Error& result) { error = result; }));
RunLoopUntilIdle();
ASSERT_EQ(error, Error::kBadValue);
EXPECT_FALSE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_FALSE(topology->IsLeaseActive("exceptions-element-001"));
}
TEST_F(WakeLeaseTest, AddElementFails) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernor>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_endpoints = fidl::CreateEndpoints<fpb::Topology>();
ASSERT_TRUE(topology_endpoints.is_ok());
auto topology = std::make_unique<stubs::PowerBrokerTopologyClosesConnection>(
std::move(topology_endpoints->server), dispatcher());
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_endpoints->client));
std::optional<Error> error;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([](const fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
FX_LOGS(FATAL) << "Unexpected success while acquiring lease";
})
.or_else([&error](const Error& result) { error = result; }));
RunLoopUntilIdle();
ASSERT_EQ(error, Error::kBadValue);
}
TEST_F(WakeLeaseTest, LeaseFails) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernor>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopology>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessorClosesConnection>(std::move(server_end),
dispatcher);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
std::optional<Error> error;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([](const fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
FX_LOGS(FATAL) << "Unexpected success while acquiring lease";
})
.or_else([&error](const Error& result) { error = result; }));
RunLoopUntilIdle();
ASSERT_EQ(error, Error::kBadValue);
EXPECT_TRUE(topology->ElementInTopology("exceptions-element-001"));
EXPECT_FALSE(topology->IsLeaseActive("exceptions-element-001"));
}
TEST_F(WakeLeaseTest, LeaseFailsOnTimeout) {
auto sag_client_and_stub = CreateSag<stubs::SystemActivityGovernor>(dispatcher());
ASSERT_TRUE(sag_client_and_stub.has_value());
auto& [sag_client, sag] = *sag_client_and_stub;
auto topology_client_and_stub = CreateTopology<stubs::PowerBrokerTopology>(
dispatcher(),
/*construct_lessor=*/[dispatcher = dispatcher()](
fidl::ServerEnd<fuchsia_power_broker::Lessor> server_end) {
return std::make_unique<stubs::PowerBrokerLessor>(std::move(server_end), dispatcher,
fpb::LeaseStatus::kPending);
});
ASSERT_TRUE(topology_client_and_stub.has_value());
auto& [topology_client, topology] = *topology_client_and_stub;
WakeLease wake_lease(dispatcher(), "exceptions-element-001", std::move(sag_client),
std::move(topology_client));
std::optional<Error> error;
GetExecutor().schedule_task(
wake_lease.Acquire(kTimeout)
.and_then([](const fidl::Client<fuchsia_power_broker::LeaseControl>& acquired_lease) {
FX_LOGS(FATAL) << "Unexpected success while acquiring lease";
})
.or_else([&error](const Error& result) { error = result; }));
RunLoopFor(kTimeout);
EXPECT_EQ(error, Error::kTimeout);
}
} // namespace
} // namespace forensics::exceptions::handler