| /* |
| * 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 |