blob: d3e18437ff115961c0995e12cca068becfa21363 [file]
// 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 "google3/host_commands.h"
#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 <cstdint>
#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, "spidev"))
{
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, ChipInfoH1d3p)
{
ec_response_chip_info response = {};
response.hardware_identity = 0x0123456789abcdef;
response.hardware_category = 0x4;
response.info_variant = 0xffffffff;
EXPECT_CALL(ecUtil, getHothChipInfo()).WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(hoth_state->chipSerialNumber(), "4-0123456789abcdef");
EXPECT_EQ(hoth_state->chipPartNumber(), "1100336");
EXPECT_EQ(hoth_state->chipRevision(), "H1D3P");
EXPECT_EQ(hoth_state->chipType(), "root_of_trust");
EXPECT_EQ(hoth_state->chipName(), "Titan");
EXPECT_EQ(hoth_state->chipManufacturer(), "Google");
}
TEST_F(HothStateTest, ChipInfoH1b3p)
{
ec_response_chip_info response = {};
response.hardware_identity = 0x0123456789abcdef;
response.hardware_category = 0x1;
response.info_variant = 0x1;
EXPECT_CALL(ecUtil, getHothChipInfo()).WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(hoth_state->chipSerialNumber(), "1-0123456789abcdef");
EXPECT_EQ(hoth_state->chipPartNumber(), "1059594");
EXPECT_EQ(hoth_state->chipRevision(), "H1B3P");
EXPECT_EQ(hoth_state->chipType(), "root_of_trust");
EXPECT_EQ(hoth_state->chipName(), "Titan");
EXPECT_EQ(hoth_state->chipManufacturer(), "Google");
}
TEST_F(HothStateTest, ChipInfoH1b2p)
{
ec_response_chip_info response = {};
response.hardware_identity = 0x0123456789abcdef;
response.hardware_category = 0x1;
response.info_variant = 0xffffffff;
EXPECT_CALL(ecUtil, getHothChipInfo()).WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(hoth_state->chipSerialNumber(), "1-0123456789abcdef");
EXPECT_EQ(hoth_state->chipPartNumber(), "07138244");
EXPECT_EQ(hoth_state->chipRevision(), "H1B2P");
EXPECT_EQ(hoth_state->chipType(), "root_of_trust");
EXPECT_EQ(hoth_state->chipName(), "Titan");
EXPECT_EQ(hoth_state->chipManufacturer(), "Google");
}
TEST_F(HothStateTest, ChipInfoTi03)
{
std::array<uint8_t, kOtDeviceIdLenInBytes> response{
0x00, 0x01, 0x02, 0x03, 'T', 'I', '0', '3', 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f};
EXPECT_CALL(ecUtil, getHothChipInfo()).WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(
hoth_state->chipSerialNumber(),
"000102035449303308090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
EXPECT_EQ(hoth_state->chipPartNumber(), "1229354");
EXPECT_EQ(hoth_state->chipRevision(), "TI03");
EXPECT_EQ(hoth_state->chipType(), "root_of_trust");
EXPECT_EQ(hoth_state->chipName(), "Titan");
EXPECT_EQ(hoth_state->chipManufacturer(), "Nuvoton");
}
TEST_F(HothStateTest, ChipInfoTi02)
{
std::array<uint8_t, kOtDeviceIdLenInBytes> response{
0xf0, 0xf1, 0xf2, 0xf3, 'T', 'I', '0', '2', 0xf8, 0xf9, 0xfa,
0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f};
EXPECT_CALL(ecUtil, getHothChipInfo()).WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(
hoth_state->chipSerialNumber(),
"f0f1f2f354493032f8f9fafbfcfdfeff707172737475767778797a7b7c7d7e7f");
EXPECT_EQ(hoth_state->chipPartNumber(), "1250620");
EXPECT_EQ(hoth_state->chipRevision(), "TI02");
EXPECT_EQ(hoth_state->chipType(), "root_of_trust");
EXPECT_EQ(hoth_state->chipName(), "Titan");
EXPECT_EQ(hoth_state->chipManufacturer(), "Nuvoton");
}
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());
}
TEST_F(HothStateTest, ValidAuthRecord)
{
ec_authz_record_get_response response = {};
response.valid = 1;
response.record.capabilities = 0x0102030405060708;
EXPECT_CALL(ecUtil, getHothAuthRecord()).WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_TRUE(hoth_state->hasValidAuthRecord());
EXPECT_EQ(hoth_state->authRecordCapabilities(), 0x0102030405060708);
}
TEST_F(HothStateTest, InvalidAuthRecord)
{
ec_authz_record_get_response response = {};
response.valid = 0;
EXPECT_CALL(ecUtil, getHothAuthRecord()).WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_FALSE(hoth_state->hasValidAuthRecord());
}
TEST_F(HothStateTest, KeyRotationStatus)
{
ec_response_key_rotation_status response = {};
response.version = 0x41414141;
response.image_family = 0x4242;
response.image_family_variant = 0x4343;
response.validation_method = 0x44444444;
response.validation_key_data = 0x45454545;
response.validation_hash_data = 0x46464646;
EXPECT_CALL(ecUtil, getHothKeyRotationStatus()).WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(hoth_state->keyRotationVersion(), 0x41414141);
EXPECT_EQ(hoth_state->keyRotationImageFamily(), 0x4242);
EXPECT_EQ(hoth_state->keyRotationImageFamilyVariant(), 0x4343);
EXPECT_EQ(hoth_state->keyRotationValidationMethod(), 0x44444444);
EXPECT_EQ(hoth_state->keyRotationValidationKeyData(), 0x45454545);
EXPECT_EQ(hoth_state->keyRotationValidationHashData(), 0x46464646);
}
TEST_F(HothStateTest, SecureBootEnforced)
{
secure_boot_enforcement_state response = {};
response.enabled = 1;
EXPECT_CALL(ecUtil, getSecureBootEnforcementState())
.WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_TRUE(hoth_state->secureBootEnforced());
response.enabled = 0;
EXPECT_CALL(ecUtil, getSecureBootEnforcementState())
.WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_FALSE(hoth_state->secureBootEnforced());
}
TEST_F(HothStateTest, SecureBootEnforcedUnsupported)
{
EXPECT_CALL(ecUtil, getSecureBootEnforcementState())
.WillOnce(testing::Throw(internal::CommandNotSupportedException(0)));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_FALSE(hoth_state->secureBootEnforced());
}
TEST_F(HothStateTest, SecureBootEnforcedException)
{
EXPECT_CALL(ecUtil, getSecureBootEnforcementState())
.WillOnce(testing::Throw(std::runtime_error("test")));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_FALSE(hoth_state->secureBootEnforced());
}
TEST_F(HothStateTest, FirmwareMauv)
{
haven_mauv response = {};
response.struct_version = 1;
response.mauv_version = 2;
response.minimum_acceptable_update_version.epoch = 0;
response.minimum_acceptable_update_version.major = 4;
response.minimum_acceptable_update_version.minor = 2023081710;
response.denylist_num_entries = 2;
response.denylist[0].epoch = 0;
response.denylist[0].major = 4;
response.denylist[0].minor = 2023081500;
response.denylist[1].epoch = 0;
response.denylist[1].major = 4;
response.denylist[1].minor = 2023081600;
EXPECT_CALL(ecUtil, getHothFirmwareMauv()).WillOnce(Return(response));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(hoth_state->firmwareMauvVersion(), 2u);
EXPECT_EQ(hoth_state->effectiveFirmwareMauv(), "0.4.2023081710");
}
TEST_F(HothStateTest, FirmwareMauvUnsupported)
{
EXPECT_CALL(ecUtil, getHothFirmwareMauv())
.WillOnce(testing::Throw(internal::CommandNotSupportedException(0)));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(hoth_state->firmwareMauvVersion(), 0u);
EXPECT_EQ(hoth_state->effectiveFirmwareMauv(), "");
}
TEST_F(HothStateTest, FirmwareMauvException)
{
EXPECT_CALL(ecUtil, getHothFirmwareMauv())
.WillOnce(testing::Throw(std::runtime_error("test")));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(hoth_state->firmwareMauvVersion(), 0u);
EXPECT_EQ(hoth_state->effectiveFirmwareMauv(), "");
}
TEST_F(HothStateTest, TransportType)
{
EXPECT_EQ(hoth_state->transportType(), "spidev");
}
TEST_F(HothStateTest, PayloadStatusAB)
{
payload_status_response payload_status = {
.header =
{
.version = 1,
.lockdown_state = 1,
.active_half = 1,
.region_count = 2,
},
.states = {
{
.validation_state = 0,
.failure_reason = 0,
.reserved_0 = 0,
.image_type = 4,
.key_index = 1,
.reserved_1 = 0,
.image_family = 12,
.version_major = 11,
.version_minor = 22,
.version_point = 33,
.version_subpoint = 44,
.descriptor_offset = 0,
},
{
.validation_state = 1,
.failure_reason = 2,
.reserved_0 = 0,
.image_type = 3,
.key_index = 1,
.reserved_1 = 0,
.image_family = 56,
.version_major = 55,
.version_minor = 66,
.version_point = 77,
.version_subpoint = 88,
.descriptor_offset = 0,
},
}};
EXPECT_CALL(ecUtil, getPayloadStatus()).WillOnce(Return(payload_status));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(hoth_state->payloadLockdownState(), 1);
EXPECT_EQ(hoth_state->payloadActivePartition(), 1);
EXPECT_EQ(hoth_state->payloadValidationStateA(), 0);
EXPECT_EQ(hoth_state->payloadValidationFailureReasonA(), 0);
EXPECT_EQ(hoth_state->payloadImageTypeA(), 4);
EXPECT_EQ(hoth_state->payloadKeyIndexA(), 1);
EXPECT_EQ(hoth_state->payloadImageFamilyA(), 12);
EXPECT_EQ(hoth_state->payloadVersionA(), "11.22.33.44");
EXPECT_EQ(hoth_state->payloadValidationStateB(), 1);
EXPECT_EQ(hoth_state->payloadValidationFailureReasonB(), 2);
EXPECT_EQ(hoth_state->payloadImageTypeB(), 3);
EXPECT_EQ(hoth_state->payloadKeyIndexB(), 1);
EXPECT_EQ(hoth_state->payloadImageFamilyB(), 56);
EXPECT_EQ(hoth_state->payloadVersionB(), "55.66.77.88");
}
TEST_F(HothStateTest, PayloadStatusAOnly)
{
payload_status_response payload_status = {
.header =
{
.version = 1,
.lockdown_state = 1,
.active_half = 0,
.region_count = 1,
},
.states = {
{
.validation_state = 0,
.failure_reason = 0,
.reserved_0 = 0,
.image_type = 4,
.key_index = 1,
.reserved_1 = 0,
.image_family = 12,
.version_major = 11,
.version_minor = 22,
.version_point = 33,
.version_subpoint = 44,
.descriptor_offset = 0,
},
{},
}};
EXPECT_CALL(ecUtil, getPayloadStatus()).WillOnce(Return(payload_status));
EXPECT_NO_THROW(hoth_state->updateMetrics());
EXPECT_EQ(hoth_state->payloadLockdownState(), 1);
EXPECT_EQ(hoth_state->payloadActivePartition(), 0);
EXPECT_EQ(hoth_state->payloadValidationStateA(), 0);
EXPECT_EQ(hoth_state->payloadValidationFailureReasonA(), 0);
EXPECT_EQ(hoth_state->payloadImageTypeA(), 4);
EXPECT_EQ(hoth_state->payloadKeyIndexA(), 1);
EXPECT_EQ(hoth_state->payloadImageFamilyA(), 12);
EXPECT_EQ(hoth_state->payloadVersionA(), "11.22.33.44");
EXPECT_EQ(hoth_state->payloadValidationStateB(), 0);
EXPECT_EQ(hoth_state->payloadValidationFailureReasonB(), 0);
EXPECT_EQ(hoth_state->payloadImageTypeB(), 0);
EXPECT_EQ(hoth_state->payloadKeyIndexB(), 0);
EXPECT_EQ(hoth_state->payloadImageFamilyB(), 0);
EXPECT_EQ(hoth_state->payloadVersionB(), "N/A");
}
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, "spidev"))
{
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