blob: 07ade6b04aeb8af9458763367ef49c51b069129b [file] [log] [blame]
// 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