blob: c5e0fefc95aa1388cf9342fd8825b678351b244b [file] [log] [blame]
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/soc-reset-me.hpp"
#include <sdbusplus/test/sdbus_mock.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using ::testing::_;
using ::testing::StrEq;
namespace pldm_oem_google
{
namespace soc_reset_me
{
// PeerSoCId tests
class PeerSoCIdTest : public ::testing::Test
{
void SetUp() override
{
// reset the internal optional
PeerSoCId::socId.reset();
}
};
// current implemetation in Athena
// valid instance value is "0", "1"
// instance 0 => CN_1, instance 1 => CN_2
// this unit test to capture this arbitary mapping policy/assumption
// accidently changed in the future
TEST_F(PeerSoCIdTest, SetWithPldmdInstanceParam)
{
// Test setting with invalid instance (e.g., non-integer).
EXPECT_FALSE(PeerSoCId::setWithPldmdInstanceParam("invalid"));
EXPECT_EQ(PeerSoCId::value(), SocId::cnDefault);
EXPECT_TRUE(PeerSoCId::isDefault());
// Test setting with an unsupported SoC ID.
EXPECT_FALSE(PeerSoCId::setWithPldmdInstanceParam("3"));
EXPECT_EQ(PeerSoCId::value(), SocId::cnDefault);
EXPECT_TRUE(PeerSoCId::isDefault());
// Test setting with valid instance: "0"
EXPECT_TRUE(PeerSoCId::setWithPldmdInstanceParam("0"));
EXPECT_EQ(PeerSoCId::value(), SocId::cn1);
EXPECT_FALSE(PeerSoCId::isDefault());
}
TEST_F(PeerSoCIdTest, SetWithPldmdInstanceParam_cn1)
{
// Test setting with valid instance: "0"
EXPECT_TRUE(PeerSoCId::setWithPldmdInstanceParam("0"));
EXPECT_EQ(PeerSoCId::value(), SocId::cn1);
EXPECT_FALSE(PeerSoCId::isDefault());
// Test setting with valid instance: "1", immutable after first set
EXPECT_FALSE(PeerSoCId::setWithPldmdInstanceParam("1"));
EXPECT_EQ(PeerSoCId::value(), SocId::cn1);
EXPECT_FALSE(PeerSoCId::isDefault());
}
TEST_F(PeerSoCIdTest, SetWithPldmdInstanceParam_cn2)
{
// Test setting with valid instance: "1"
EXPECT_TRUE(PeerSoCId::setWithPldmdInstanceParam("1"));
EXPECT_EQ(PeerSoCId::value(), SocId::cn2);
EXPECT_FALSE(PeerSoCId::isDefault());
// Test setting with valid instance: "0", immutable after first set
EXPECT_FALSE(PeerSoCId::setWithPldmdInstanceParam("0"));
EXPECT_EQ(PeerSoCId::value(), SocId::cn2);
EXPECT_FALSE(PeerSoCId::isDefault());
}
// Test fixture for SocResetMeHandler tests
class SocResetMeHandlerTest : public testing::Test
{
protected:
static constexpr auto hostStateReboot =
"xyz.openbmc_project.State.Host.Transition.Reboot";
static constexpr auto dbusPropIntf = "org.freedesktop.DBus.Properties";
::testing::NiceMock<sdbusplus::SdBusMock> mock;
static auto dbusParams(SocResetMeHandler& h)
{
return h.dbusParams;
}
static void resetSoC(SocResetMeHandler& h)
{
h.resetSoC(SOC_RESET_ME_AUTO);
}
static auto quicentEnd(SocResetMeHandler& h)
{
return h.quiescentEnd;
}
static void
mockQuiecentEnd(SocResetMeHandler& h,
decltype(SocResetMeHandler::quiescentEnd) mocktime)
{
h.quiescentEnd = mocktime;
}
void expectNewSetPropMethod(const struct ResetDBusParams* params)
{
EXPECT_CALL(mock, sd_bus_message_new_method_call(
_, _, StrEq(params->conn), StrEq(params->path),
StrEq(dbusPropIntf), StrEq("Set")))
.WillOnce(::testing::Return(0));
}
void expectMessageAppend(const char* val)
{
using testing::MatcherCast;
using testing::Pointee;
using testing::SafeMatcherCast;
using ::testing::StrEq;
EXPECT_CALL(mock, sd_bus_message_append_basic(
_, SD_BUS_TYPE_STRING,
MatcherCast<const void*>(
SafeMatcherCast<const char*>(StrEq(val)))))
.WillOnce(::testing::Return(0));
}
void expectSetHostTransactionProp(const struct ResetDBusParams* params)
{
expectNewSetPropMethod(params);
expectMessageAppend(params->intf);
expectMessageAppend(params->prop);
expectMessageAppend(hostStateReboot);
// sync call without timeout
EXPECT_CALL(mock, sd_bus_call(_, _, 0, _, _)).Times(1);
}
auto resetMeRequestMsg(uint32_t quiescentPeriod, const char* str)
{
std::string_view resetReason{str};
memset(&data, 0, sizeof(data));
memcpy(data + sizeof(struct pldm_event_oem_google_soc_reset_me),
resetReason.data(), resetReason.length());
auto* msg =
reinterpret_cast<struct pldm_event_oem_google_soc_reset_me*>(data);
msg->descLen = resetReason.length();
msg->resetQuiesent = quiescentPeriod;
return msg;
}
uint8_t data[sizeof(struct pldm_event_oem_google_soc_reset_me) + 128];
};
// Test SocResetMeHandler constructor
TEST_F(SocResetMeHandlerTest, Constructor_InitializeDBusParams)
{
SocResetMeHandler mockCn1Handler(SocId::cn1,
sdbusplus::get_mocked_new(&mock));
EXPECT_EQ(dbusParams(mockCn1Handler), cn1DbusParams());
SocResetMeHandler mockCn2Handler(SocId::cn2,
sdbusplus::get_mocked_new(&mock));
EXPECT_EQ(dbusParams(mockCn2Handler), cn2DbusParams());
EXPECT_THROW(SocResetMeHandler mockInvalidHander(
SocId::cnDefault, sdbusplus::get_mocked_new(&mock)),
InvalidSoCIdException);
EXPECT_THROW(SocResetMeHandler mockInvalidHander(
static_cast<SocId>(-1), sdbusplus::get_mocked_new(&mock)),
InvalidSoCIdException);
}
class MockSocResetMeHandler : public SocResetMeHandler
{
public:
// Constructor taking the same parameters as SocResetMeHandler
MockSocResetMeHandler(const SocId id, sdbusplus::bus_t&& b) :
SocResetMeHandler(id, std::move(b))
{}
MOCK_METHOD(void, resetSoC, (const uint16_t resetType), (override));
MOCK_METHOD(void, logRequest,
(uint64_t internalInfo, uint16_t resetType,
uint32_t quiescentPeriod, const std::string& resetReason),
(const, override));
MOCK_METHOD(void, logRequestIgnored, (const ResetTimeStamp& requestTime),
(const, override));
MOCK_METHOD(void, logRequestIssued, (const ResetTimeStamp& requestTime),
(const, override));
};
// First time handling shall not in any reset quiecent period
// so shall reset and update the reset quiecentEnd to
// handleTime + requestQuiesent
TEST_F(SocResetMeHandlerTest, HandleSocResetMeFirstTime)
{
using std::chrono::seconds;
using std::chrono::_V2::steady_clock;
const auto* reason = "HandleSocResetMeFirstTime";
auto* msg = resetMeRequestMsg(0, reason);
MockSocResetMeHandler mockHandler(SocId::cn1,
sdbusplus::get_mocked_new(&mock));
EXPECT_CALL(mockHandler, resetSoC(msg->resetType)).Times(1);
EXPECT_CALL(mockHandler, logRequest(msg->internalInfo, msg->resetType,
msg->resetQuiesent, StrEq(reason)))
.Times(1);
EXPECT_CALL(mockHandler, logRequestIssued(_)).Times(1);
EXPECT_CALL(mockHandler, logRequestIgnored(_)).Times(0);
mockHandler.handle(0, msg, msg->descLen + sizeof(*msg));
EXPECT_EQ(
duration_cast<seconds>(quicentEnd(mockHandler).time_since_epoch()),
duration_cast<seconds>(
(steady_clock::now() + seconds(msg->resetQuiesent))
.time_since_epoch()));
}
// Following on handling request out of quiencent
// reset SoC and update quiecentEnd to handletTime + requestQuiesent
TEST_F(SocResetMeHandlerTest, HandleSocResetMeOutQuiesent)
{
using std::chrono::seconds;
using std::chrono::_V2::steady_clock;
const auto* reason = "HandleSocResetMeOutQuiesent";
auto* msg = resetMeRequestMsg(10, reason);
MockSocResetMeHandler mockHandler(SocId::cn1,
sdbusplus::get_mocked_new(&mock));
auto mocktime = steady_clock::now() - seconds(30);
mockQuiecentEnd(mockHandler, mocktime);
EXPECT_CALL(mockHandler, resetSoC(msg->resetType)).Times(1);
EXPECT_CALL(mockHandler, logRequest(msg->internalInfo, msg->resetType,
msg->resetQuiesent, StrEq(reason)))
.Times(1);
EXPECT_CALL(mockHandler, logRequestIssued(_)).Times(1);
EXPECT_CALL(mockHandler, logRequestIgnored(_)).Times(0);
mockHandler.handle(0, msg, msg->descLen + sizeof(*msg));
EXPECT_EQ(
duration_cast<seconds>(quicentEnd(mockHandler).time_since_epoch()),
duration_cast<seconds>(
(steady_clock::now() + seconds(msg->resetQuiesent))
.time_since_epoch()));
}
// Following on handling request within quiencent period
// ingore this request and w/o resetting SoC nor updating quiecentEnd
TEST_F(SocResetMeHandlerTest, HandleSocResetMeInQuiesent)
{
using std::chrono::seconds;
using std::chrono::_V2::steady_clock;
const auto* reason = "HandleSocResetMeInQuiesent";
auto* msg = resetMeRequestMsg(10, reason);
MockSocResetMeHandler mockHandler(SocId::cn1,
sdbusplus::get_mocked_new(&mock));
auto mocktime = steady_clock::now() + seconds(30);
mockQuiecentEnd(mockHandler, mocktime);
EXPECT_CALL(mockHandler, resetSoC(msg->resetType)).Times(0);
EXPECT_CALL(mockHandler, logRequest(msg->internalInfo, msg->resetType,
msg->resetQuiesent, StrEq(reason)))
.Times(1);
EXPECT_CALL(mockHandler, logRequestIssued(_)).Times(0);
EXPECT_CALL(mockHandler, logRequestIgnored(_)).Times(1);
mockHandler.handle(0, msg, sizeof(*msg) + msg->descLen);
EXPECT_EQ(quicentEnd(mockHandler), mocktime);
}
// Test the expected behavior: set the corresponding state transaction property
TEST_F(SocResetMeHandlerTest, SocResetCn1)
{
expectSetHostTransactionProp(cn1DbusParams());
SocResetMeHandler mockHandler(SocId::cn1, sdbusplus::get_mocked_new(&mock));
resetSoC(mockHandler);
}
TEST_F(SocResetMeHandlerTest, SocResetCn2)
{
expectSetHostTransactionProp(cn2DbusParams());
SocResetMeHandler mockHandler(SocId::cn2, sdbusplus::get_mocked_new(&mock));
resetSoC(mockHandler);
}
} // namespace soc_reset_me
} // namespace pldm_oem_google