| // Copyright 2024 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 "ec_util_mock.hpp" |
| #include "host_command_mock.hpp" |
| |
| #include <boost/asio/io_context.hpp> |
| #include <hoth_state.hpp> |
| #include <sdbusplus/bus.hpp> |
| #include <sdbusplus/test/sdbus_mock.hpp> |
| #include <xyz/openbmc_project/Control/Hoth/State/error.hpp> |
| |
| #include <chrono> |
| #include <condition_variable> |
| #include <functional> |
| #include <mutex> |
| #include <stdexcept> |
| #include <string> |
| #include <thread> |
| #include <vector> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| using ::testing::_; |
| using ::testing::IsNull; |
| using ::testing::Return; |
| using ::testing::StrEq; |
| |
| namespace google |
| { |
| namespace hoth |
| { |
| namespace |
| { |
| |
| class HothStateTest : public ::testing::Test |
| { |
| public: |
| static constexpr char kTestPath[] = "/test/path"; |
| static constexpr char kHothInterface[] = |
| "xyz.openbmc_project.Control.Hoth.State"; |
| |
| protected: |
| HothStateTest() : |
| bus(sdbusplus::get_mocked_new(&sdbus_mock)), |
| hoth_state( |
| std::make_unique<HothState>(bus, io, kTestPath, &hostCmd, &ecUtil)) |
| { |
| EXPECT_CALL(sdbus_mock, |
| sd_bus_emit_object_added(IsNull(), StrEq(kTestPath))) |
| .WillRepeatedly(Return(0)); |
| EXPECT_CALL(sdbus_mock, |
| sd_bus_emit_object_removed(IsNull(), StrEq(kTestPath))) |
| .WillRepeatedly(Return(0)); |
| EXPECT_CALL(sdbus_mock, |
| sd_bus_add_object_vtable(IsNull(), _, StrEq(kTestPath), |
| StrEq(kHothInterface), _, _)) |
| .WillRepeatedly(Return(0)); |
| } |
| |
| // io_context |
| boost::asio::io_context io; |
| |
| // sdbus mock object |
| sdbusplus::SdBusMock sdbus_mock; |
| |
| // sdbusplus handle |
| sdbusplus::bus::bus bus; |
| |
| // Host Command interface handle |
| internal::HostCommandMock hostCmd; |
| |
| // EC Utility interface handle |
| internal::EcUtilMock ecUtil; |
| |
| // Hoth object |
| std::unique_ptr<HothState> hoth_state = nullptr; |
| }; |
| |
| TEST_F(HothStateTest, HasPersistentPanicInfo) |
| { |
| EXPECT_CALL(ecUtil, checkHothPersistentPanicInfo()).WillOnce(Return(true)); |
| EXPECT_NO_THROW(hoth_state->updateMetrics()); |
| EXPECT_TRUE(hoth_state->hasPersistentPanicInfo()); |
| |
| EXPECT_CALL(ecUtil, checkHothPersistentPanicInfo()).WillOnce(Return(false)); |
| EXPECT_NO_THROW(hoth_state->updateMetrics()); |
| EXPECT_FALSE(hoth_state->hasPersistentPanicInfo()); |
| } |
| |
| ec_response_statistics getMockStatistics() |
| { |
| ec_response_statistics ret = { |
| .valid_words = 22, |
| .hoth_reset_flags = 0x1, |
| .time_since_hoth_boot_us = 0xfeedbeefbeaddeab, |
| .hoth_temperature = 0x3, |
| .ro_info_strikes = 0x4, |
| .rw_info_strikes = 0x5, |
| .scratch_value = 0x6, |
| .payload_update_failure_reason = 0x7, |
| .firmware_update_failure_reason = 0x8, |
| .failed_firmware_minor_version = 0x9, |
| .boot_timing_total = |
| { |
| .start_us = 0x10, |
| .end_us = 0x11, |
| }, |
| .boot_timing_firmware_update = |
| { |
| .start_us = 0x12, |
| .end_us = 0x14, |
| }, |
| .boot_timing_firmware_mirroring = |
| { |
| .start_us = 0x15, |
| .end_us = 0x18, |
| }, |
| .boot_timing_payload_validation = |
| { |
| .start_us = 0x19, |
| .end_us = 0x1d, |
| }, |
| .payload_update_confirmation_cookie_failure_reason = 0x1e, |
| .payload_update_confirmation_cookie = 0xdeadbeeffeedfeed, |
| .bootloader_update_error = 0x1f, |
| .reserved = {}, |
| }; |
| return ret; |
| } |
| |
| struct HothStatisticsTestParam |
| { |
| char name[40]; |
| uint32_t valid_words_required = 0; |
| // Upgrade all metrics to uint64_t for compatibility. |
| uint64_t default_value = 0; |
| uint64_t expected_initial_value = 0; |
| uint64_t expected_new_value = 0; |
| uint64_t (*get_metric)(const HothState*) = nullptr; |
| }; |
| |
| constexpr HothStatisticsTestParam kHothStatisticsTests[] = { |
| { |
| .name = "ResetFlags", |
| .valid_words_required = 2, |
| .default_value = 0, |
| .expected_initial_value = 0x1, |
| .expected_new_value = 0x11, |
| .get_metric = [](const HothState* s) -> uint64_t { |
| return s->resetFlags(); |
| }, |
| }, |
| { |
| .name = "UpTime", |
| .valid_words_required = 4, |
| .default_value = 0, |
| .expected_initial_value = 0xfeedbeefbeaddeab, |
| .expected_new_value = 0xfeedbeefbeaddebb, |
| .get_metric = [](const HothState* s) -> uint64_t { |
| return s->upTime(); |
| }, |
| }, |
| { |
| .name = "RoInfoStrikes", |
| .valid_words_required = 6, |
| .default_value = 0xffffffff, |
| .expected_initial_value = 0x4, |
| .expected_new_value = 0x14, |
| .get_metric = [](const HothState* s) -> uint64_t { |
| return s->roInfoStrikes(); |
| }, |
| }, |
| { |
| .name = "RwInfoStrikes", |
| .valid_words_required = 7, |
| .default_value = 0xffffffff, |
| .expected_initial_value = 0x5, |
| .expected_new_value = 0x15, |
| .get_metric = [](const HothState* s) -> uint64_t { |
| return s->rwInfoStrikes(); |
| }, |
| }, |
| { |
| .name = "PayloadUpdateFailureCode", |
| .valid_words_required = 9, |
| .default_value = 0xffff, |
| .expected_initial_value = 0x7, |
| .expected_new_value = 0x17, |
| .get_metric = [](const HothState* s) -> uint64_t { |
| return s->payloadUpdateFailureCode(); |
| }, |
| }, |
| { |
| .name = "FirmwareUpdateFailureCode", |
| .valid_words_required = 9, |
| .default_value = 0xffff, |
| .expected_initial_value = 0x8, |
| .expected_new_value = 0x18, |
| .get_metric = [](const HothState* s) -> uint64_t { |
| return s->firmwareUpdateFailureCode(); |
| }, |
| }, |
| { |
| .name = "FirmwareUpdateFailedMinor", |
| .valid_words_required = 10, |
| .default_value = 0xffffffff, |
| .expected_initial_value = 0x9, |
| .expected_new_value = 0x19, |
| .get_metric = [](const HothState* s) -> uint64_t { |
| return s->firmwareUpdateFailedMinor(); |
| }, |
| }, |
| { |
| .name = "PayloadConfirmFailureCode", |
| .valid_words_required = 19, |
| .default_value = 0xffffffff, |
| .expected_initial_value = 0x1e, |
| .expected_new_value = 0x2e, |
| .get_metric = [](const HothState* s) -> uint64_t { |
| return s->payloadConfirmFailureCode(); |
| }, |
| }, |
| { |
| .name = "BootloaderUpdateFailureCode", |
| .valid_words_required = 22, |
| .default_value = 0xffffffff, |
| .expected_initial_value = 0x1f, |
| .expected_new_value = 0x2f, |
| .get_metric = [](const HothState* s) -> uint64_t { |
| return s->bootloaderUpdateFailureCode(); |
| }, |
| }, |
| }; |
| |
| class HothStatisticsTest : |
| public ::testing::TestWithParam<HothStatisticsTestParam> |
| { |
| public: |
| static constexpr char kTestPath[] = "/test/path"; |
| static constexpr char kHothInterface[] = |
| "xyz.openbmc_project.Control.Hoth.State"; |
| |
| protected: |
| HothStatisticsTest() : |
| bus(sdbusplus::get_mocked_new(&sdbus_mock)), |
| hoth_state( |
| std::make_unique<HothState>(bus, io, kTestPath, &hostCmd, &ecUtil)) |
| { |
| EXPECT_CALL(sdbus_mock, |
| sd_bus_emit_object_added(IsNull(), StrEq(kTestPath))) |
| .WillRepeatedly(Return(0)); |
| EXPECT_CALL(sdbus_mock, |
| sd_bus_emit_object_removed(IsNull(), StrEq(kTestPath))) |
| .WillRepeatedly(Return(0)); |
| EXPECT_CALL(sdbus_mock, |
| sd_bus_add_object_vtable(IsNull(), _, StrEq(kTestPath), |
| StrEq(kHothInterface), _, _)) |
| .WillRepeatedly(Return(0)); |
| } |
| |
| static void updateStatistics(ec_response_statistics *s) |
| { |
| s->hoth_reset_flags += 0x10; |
| s->time_since_hoth_boot_us += 0x10; |
| s->hoth_temperature += 0x10; |
| s->ro_info_strikes += 0x10; |
| s->rw_info_strikes += 0x10; |
| s->scratch_value += 0x10; |
| s->payload_update_failure_reason += 0x10; |
| s->firmware_update_failure_reason += 0x10; |
| s->failed_firmware_minor_version += 0x10; |
| s->boot_timing_total.end_us += 0x10; |
| s->boot_timing_firmware_update.end_us += 0x10; |
| s->boot_timing_firmware_mirroring.end_us += 0x10; |
| s->boot_timing_payload_validation.end_us += 0x10; |
| s->payload_update_confirmation_cookie_failure_reason += 0x10; |
| s->payload_update_confirmation_cookie += 0x10; |
| s->bootloader_update_error += 0x10; |
| } |
| |
| // io_context |
| boost::asio::io_context io; |
| |
| // sdbus mock object |
| sdbusplus::SdBusMock sdbus_mock; |
| |
| // sdbusplus handle |
| sdbusplus::bus::bus bus; |
| |
| // Host Command interface handle |
| internal::HostCommandMock hostCmd; |
| |
| // EC Utility interface handle |
| internal::EcUtilMock ecUtil; |
| |
| // Hoth object |
| std::unique_ptr<HothState> hoth_state = nullptr; |
| }; |
| |
| // Tests that statistics property queries return the correct result before and |
| // after a value change. |
| TEST_P(HothStatisticsTest, Update) |
| { |
| ec_response_statistics test_statistics = getMockStatistics(); |
| EXPECT_CALL(ecUtil, getHothStatistics()).WillOnce(Return(test_statistics)); |
| EXPECT_NO_THROW(hoth_state->updateMetrics()); |
| |
| EXPECT_EQ(GetParam().get_metric(hoth_state.get()), |
| GetParam().expected_initial_value); |
| |
| updateStatistics(&test_statistics); |
| |
| EXPECT_CALL(ecUtil, getHothStatistics()).WillOnce(Return(test_statistics)); |
| EXPECT_NO_THROW(hoth_state->updateMetrics()); |
| |
| EXPECT_EQ(GetParam().get_metric(hoth_state.get()), |
| GetParam().expected_new_value); |
| } |
| |
| // Tests that not enough valid words in statistics response returns default |
| // value when querying the property. |
| TEST_P(HothStatisticsTest, NotEnoughValidWords) |
| { |
| ec_response_statistics test_statistics = getMockStatistics(); |
| test_statistics.valid_words = GetParam().valid_words_required; |
| EXPECT_CALL(ecUtil, getHothStatistics()).WillOnce(Return(test_statistics)); |
| EXPECT_NO_THROW(hoth_state->updateMetrics()); |
| EXPECT_NO_THROW(GetParam().get_metric(hoth_state.get())); |
| |
| test_statistics.valid_words--; |
| EXPECT_CALL(ecUtil, getHothStatistics()).WillOnce(Return(test_statistics)); |
| EXPECT_NO_THROW(hoth_state->updateMetrics()); |
| EXPECT_EQ(GetParam().get_metric(hoth_state.get()), |
| GetParam().default_value); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| HothStateTest, HothStatisticsTest, testing::ValuesIn(kHothStatisticsTests), |
| [](const testing::TestParamInfo<HothStatisticsTest::ParamType>& info) { |
| return info.param.name; |
| }); |
| |
| } // namespace |
| } // namespace hoth |
| } // namespace google |