blob: 709efdd24bf6847ea7ba3bc2580b5b97ac79cb42 [file] [log] [blame]
#include "dbus_environment.hpp"
#include "fakes/clock_fake.hpp"
#include "helpers.hpp"
#include "messages/collect_trigger_id.hpp"
#include "messages/trigger_presence_changed_ind.hpp"
#include "messages/update_report_ind.hpp"
#include "mocks/json_storage_mock.hpp"
#include "mocks/metric_mock.hpp"
#include "mocks/report_factory_mock.hpp"
#include "mocks/report_manager_mock.hpp"
#include "params/report_params.hpp"
#include "report.hpp"
#include "report_manager.hpp"
#include "utils/clock.hpp"
#include "utils/contains.hpp"
#include "utils/conv_container.hpp"
#include "utils/dbus_path_utils.hpp"
#include "utils/messanger.hpp"
#include "utils/string_utils.hpp"
#include "utils/transform.hpp"
#include "utils/tstring.hpp"
#include <sdbusplus/exception.hpp>
#include <ranges>
using namespace testing;
using namespace std::literals::string_literals;
using namespace std::chrono_literals;
using sdbusplus::message::object_path;
namespace tstring = utils::tstring;
using ErrorMessageDbusType = std::tuple<std::string, std::string>;
using ErrorMessagesDbusType = std::vector<ErrorMessageDbusType>;
constexpr Milliseconds systemTimestamp = 55ms;
namespace
{
ReportParams defaultParams()
{
return ReportParams();
}
ReportParams defaultOnChangeParams()
{
return defaultParams().reportingType(ReportingType::onChange);
}
} // namespace
class TestReport : public Test
{
public:
std::unique_ptr<ReportManagerMock> reportManagerMock =
std::make_unique<NiceMock<ReportManagerMock>>();
std::unique_ptr<ReportFactoryMock> reportFactoryMock =
std::make_unique<NiceMock<ReportFactoryMock>>();
nlohmann::json storedConfiguration;
NiceMock<StorageMock> storageMock;
std::vector<std::shared_ptr<MetricMock>> metricMocks;
std::unique_ptr<ClockFake> clockFakePtr = std::make_unique<ClockFake>();
ClockFake& clockFake = *clockFakePtr;
std::unique_ptr<Report> sut;
utils::Messanger messanger;
MockFunction<void()> checkPoint;
TestReport() : messanger(DbusEnvironment::getIoc())
{
clockFake.system.set(systemTimestamp);
ON_CALL(storageMock, store(to_file_path(ReportParams().reportId()), _))
.WillByDefault(SaveArg<1>(&storedConfiguration));
}
void initMetricMocks(
const std::vector<LabeledMetricParameters>& metricParameters)
{
for (auto i = metricMocks.size(); i < metricParameters.size(); ++i)
{
metricMocks.emplace_back(std::make_shared<NiceMock<MetricMock>>());
}
metricMocks.resize(metricParameters.size());
std::vector<MetricValue> readings{
{MetricValue{"b", 17.1, 114}, MetricValue{"bb", 42.0, 74}}};
ASSERT_THAT(readings.size(), Ge(metricParameters.size()));
for (size_t i = 0; i < metricParameters.size(); ++i)
{
ON_CALL(*metricMocks[i], getUpdatedReadings())
.WillByDefault(ReturnRefOfCopy(std::vector({readings[i]})));
ON_CALL(*metricMocks[i], dumpConfiguration())
.WillByDefault(Return(metricParameters[i]));
}
}
std::vector<std::shared_ptr<interfaces::Metric>>
getMetricsFromReadingParams(const ReadingParameters& params)
{
const auto metricParameters =
reportFactoryMock->convertMetricParams(params);
std::vector<std::shared_ptr<MetricMock>> metricMocks;
for (size_t i = 0; i < metricParameters.size(); ++i)
{
metricMocks.emplace_back(std::make_shared<NiceMock<MetricMock>>());
ON_CALL(*metricMocks[i], dumpConfiguration())
.WillByDefault(Return(metricParameters[i]));
ON_CALL(*metricMocks[i], metricCount())
.WillByDefault(Return(metricParameters[i]
.at_label<tstring::SensorPath>()
.size()));
}
return utils::convContainer<std::shared_ptr<interfaces::Metric>>(
metricMocks);
}
void SetUp() override
{
sut = makeReport(defaultParams());
}
static interfaces::JsonStorage::FilePath to_file_path(std::string id)
{
return interfaces::JsonStorage::FilePath(
std::to_string(std::hash<std::string>{}(id)));
}
std::unique_ptr<Report> makeReport(const ReportParams& params)
{
initMetricMocks(params.metricParameters());
return std::make_unique<Report>(
DbusEnvironment::getIoc(), DbusEnvironment::getObjServer(),
params.reportId(), params.reportName(), params.reportingType(),
params.reportActions(), params.interval(), params.appendLimit(),
params.reportUpdates(), *reportManagerMock, storageMock,
utils::convContainer<std::shared_ptr<interfaces::Metric>>(
metricMocks),
*reportFactoryMock, params.enabled(), std::move(clockFakePtr),
params.readings());
}
template <class T>
static T getProperty(const std::string& path, const std::string& property)
{
return DbusEnvironment::getProperty<T>(path, Report::reportIfaceName,
property);
}
template <class T>
static boost::system::error_code setProperty(const std::string& path,
const std::string& property,
const T& newValue)
{
return DbusEnvironment::setProperty<T>(path, Report::reportIfaceName,
property, newValue);
}
template <class... Args>
static boost::system::error_code callMethod(const std::string& path,
const std::string& method,
Args&&... args)
{
return DbusEnvironment::callMethod(path, Report::reportIfaceName,
"SetReportingProperties",
std::forward<Args>(args)...);
}
template <class T>
struct ChangePropertyParams
{
Matcher<T> valueBefore = _;
T newValue;
Matcher<boost::system::error_code> ec =
Eq(boost::system::errc::success);
Matcher<T> valueAfter = Eq(newValue);
};
template <class T>
static void changeProperty(const std::string& path,
const std::string& property,
ChangePropertyParams<T> p)
{
ASSERT_THAT(getProperty<T>(path, property), p.valueBefore);
ASSERT_THAT(setProperty<T>(path, property, p.newValue), p.ec);
EXPECT_THAT(getProperty<T>(path, property), p.valueAfter);
}
boost::system::error_code call(const std::string& path,
const std::string& interface,
const std::string& method)
{
std::promise<boost::system::error_code> methodPromise;
DbusEnvironment::getBus()->async_method_call(
[&methodPromise](boost::system::error_code ec) {
methodPromise.set_value(ec);
}, DbusEnvironment::serviceName(), path, interface, method);
return DbusEnvironment::waitForFuture(methodPromise.get_future());
}
boost::system::error_code update(const std::string& path)
{
return call(path, Report::reportIfaceName, "Update");
}
boost::system::error_code deleteReport(const std::string& path)
{
return call(path, Report::deleteIfaceName, "Delete");
}
static std::pair<std::string, std::vector<std::string>>
makeStateDetail(const std::string& detailType,
std::vector<std::string> detailArgs)
{
return make_pair(detailType, detailArgs);
}
};
TEST_F(TestReport, returnsId)
{
EXPECT_THAT(sut->getId(), Eq(defaultParams().reportId()));
}
TEST_F(TestReport, verifyIfPropertiesHaveValidValue)
{
EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"),
Eq(defaultParams().enabled()));
EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"),
Eq(defaultParams().interval().count()));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistency"), Eq(true));
EXPECT_THAT(
getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"),
Eq(utils::transform(defaultParams().reportActions(), [](const auto v) {
return utils::enumToString(v);
})));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "EmitsReadingsUpdate"),
Eq(utils::contains(defaultParams().reportActions(),
ReportAction::emitsReadingsUpdate)));
EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "AppendLimit"),
Eq(defaultParams().appendLimit()));
EXPECT_THAT(getProperty<std::string>(sut->getPath(), "ReportingType"),
Eq(utils::enumToString(defaultParams().reportingType())));
EXPECT_THAT(getProperty<std::string>(sut->getPath(), "ReportUpdates"),
Eq(utils::enumToString(defaultParams().reportUpdates())));
EXPECT_THAT(
getProperty<bool>(sut->getPath(), "LogToMetricReportsCollection"),
Eq(utils::contains(defaultParams().reportActions(),
ReportAction::logToMetricReportsCollection)));
EXPECT_THAT(
getProperty<ReadingParameters>(sut->getPath(), "ReadingParameters"),
Eq(toReadingParameters(defaultParams().metricParameters())));
EXPECT_THAT(getProperty<std::string>(sut->getPath(), "Name"),
Eq(defaultParams().reportName()));
EXPECT_THAT(
getProperty<std::vector<object_path>>(sut->getPath(), "Triggers"),
IsEmpty());
}
TEST_F(TestReport, readingsAreInitialyEmpty)
{
EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"),
Eq(Readings{}));
}
TEST_F(TestReport, setReadingParametersWithNewParams)
{
ReadingParameters newParams = toReadingParameters(
std::vector<LabeledMetricParameters>{{LabeledMetricParameters{
{LabeledSensorInfo{"Service",
"/xyz/openbmc_project/sensors/power/psu",
"NewMetadata123"}},
OperationType::avg,
CollectionTimeScope::startup,
CollectionDuration(250ms)}}});
auto metrics = getMetricsFromReadingParams(newParams);
EXPECT_CALL(*reportFactoryMock, updateMetrics(_, _, _))
.WillOnce(SetArgReferee<0>(metrics));
EXPECT_THAT(
setProperty(sut->getPath(), "ReadingParameters", newParams).value(),
Eq(boost::system::errc::success));
EXPECT_THAT(
getProperty<ReadingParameters>(sut->getPath(), "ReadingParameters"),
Eq(newParams));
}
TEST_F(TestReport, setReadingParametersFailsWhenMetricCountExceedsAllowedValue)
{
std::vector<LabeledMetricParameters> readingParams{{LabeledMetricParameters{
{LabeledSensorInfo{"Service", "/xyz/openbmc_project/sensors/power/psu",
"NewMetadata123"}},
OperationType::avg,
CollectionTimeScope::startup,
CollectionDuration(250ms)}}};
auto& metricParamsVec =
readingParams[0].at_label<utils::tstring::SensorPath>();
for (size_t i = 0; i < ReportManager::maxNumberMetrics; i++)
{
metricParamsVec.emplace_back(LabeledSensorInfo{
"Service", "/xyz/openbmc_project/sensors/power/psu",
"NewMetadata123"});
}
ReadingParameters newParams = toReadingParameters(readingParams);
EXPECT_THAT(
setProperty(sut->getPath(), "ReadingParameters", newParams).value(),
Eq(boost::system::errc::invalid_argument));
}
TEST_F(TestReport, setReportActionsWithValidNewActions)
{
std::vector<std::string> newActions = {
utils::enumToString(ReportAction::emitsReadingsUpdate)};
std::vector<std::string> currActions =
utils::transform(defaultParams().reportActions(),
[](const auto v) { return utils::enumToString(v); });
EXPECT_THAT(newActions, Ne(currActions));
EXPECT_THAT(
setProperty(sut->getPath(), "ReportActions", newActions).value(),
Eq(boost::system::errc::success));
EXPECT_THAT(
getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"),
UnorderedElementsAre(
utils::enumToString(ReportAction::emitsReadingsUpdate),
utils::enumToString(ReportAction::logToMetricReportsCollection)));
}
TEST_F(TestReport, setReportActionsWithValidUnsortedActions)
{
std::vector<std::string> newActions = {
utils::enumToString(ReportAction::logToMetricReportsCollection),
utils::enumToString(ReportAction::emitsReadingsUpdate)};
std::vector<std::string> expectedActions = {
utils::enumToString(ReportAction::emitsReadingsUpdate),
utils::enumToString(ReportAction::logToMetricReportsCollection)};
std::vector<std::string> currActions =
utils::transform(defaultParams().reportActions(),
[](const auto v) { return utils::enumToString(v); });
EXPECT_THAT(newActions, Ne(currActions));
EXPECT_THAT(
setProperty(sut->getPath(), "ReportActions", newActions).value(),
Eq(boost::system::errc::success));
EXPECT_THAT(
getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"),
Eq(expectedActions));
}
TEST_F(TestReport, setReportActionsWithEmptyActions)
{
std::vector<std::string> newActions = {};
std::vector<std::string> expectedActions = {
utils::enumToString(ReportAction::logToMetricReportsCollection)};
std::vector<std::string> currActions =
utils::transform(defaultParams().reportActions(),
[](const auto v) { return utils::enumToString(v); });
EXPECT_THAT(newActions, Ne(currActions));
EXPECT_THAT(
setProperty(sut->getPath(), "ReportActions", newActions).value(),
Eq(boost::system::errc::success));
EXPECT_THAT(
getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"),
Eq(expectedActions));
}
TEST_F(TestReport, setReportActionsWithInvalidActions)
{
std::vector<std::string> invalidActions = {"EmitsReadingsUpdate_1"};
EXPECT_THAT(
setProperty(sut->getPath(), "ReportActions", invalidActions).value(),
Eq(boost::system::errc::invalid_argument));
EXPECT_THAT(
getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"),
Eq(utils::transform(defaultParams().reportActions(), [](const auto v) {
return utils::enumToString(v);
})));
}
TEST_F(TestReport, createReportWithEmptyActions)
{
std::vector<std::string> expectedActions = {
utils::enumToString(ReportAction::logToMetricReportsCollection)};
sut = makeReport(ReportParams().reportId("TestId_1").reportActions({}));
EXPECT_THAT(
getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"),
Eq(expectedActions));
}
TEST_F(TestReport, createReportWithValidUnsortedActions)
{
std::vector<std::string> newActions = {
utils::enumToString(ReportAction::logToMetricReportsCollection),
utils::enumToString(ReportAction::emitsReadingsUpdate)};
std::vector<std::string> expectedActions = {
utils::enumToString(ReportAction::emitsReadingsUpdate),
utils::enumToString(ReportAction::logToMetricReportsCollection)};
sut = makeReport(
ReportParams()
.reportId("TestId_1")
.reportActions(utils::transform(newActions, [](const auto& action) {
return utils::toReportAction(action);
})));
EXPECT_THAT(
getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"),
Eq(expectedActions));
}
TEST_F(TestReport, setEnabledWithNewValue)
{
bool newValue = !defaultParams().enabled();
EXPECT_THAT(setProperty(sut->getPath(), "Enabled", newValue).value(),
Eq(boost::system::errc::success));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), Eq(newValue));
}
TEST_F(TestReport, setReportingPropertiesWithValidValues)
{
uint64_t newValue = ReportManager::minInterval.count() * 42;
EXPECT_THAT(callMethod(sut->getPath(), "SetReportingProperties",
utils::enumToString(ReportingType::periodic),
newValue),
Eq(boost::system::errc::success));
EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"),
Eq(newValue));
}
TEST_F(TestReport, failsToSetInvalidInterval)
{
uint64_t newValue = ReportManager::minInterval.count() - 1;
EXPECT_THAT(
callMethod(sut->getPath(), "SetReportingProperties", "", newValue),
Eq(boost::system::errc::invalid_argument));
EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"),
Eq(defaultParams().interval().count()));
}
TEST_F(TestReport, failsToSetIncompatibleInterval)
{
auto report = makeReport(defaultParams()
.reportId("report2")
.reportingType(ReportingType::onRequest)
.interval(Milliseconds{0}));
uint64_t newValue = ReportManager::minInterval.count();
EXPECT_THAT(
callMethod(report->getPath(), "SetReportingProperties", "", newValue),
Eq(boost::system::errc::invalid_argument));
EXPECT_THAT(getProperty<uint64_t>(report->getPath(), "Interval"),
Eq(defaultParams().interval().count()));
}
TEST_F(TestReport, failsToSetInvalidReportingType)
{
auto report = makeReport(defaultParams()
.reportId("report2")
.reportingType(ReportingType::onRequest)
.interval(Milliseconds{0}));
EXPECT_THAT(callMethod(sut->getPath(), "SetReportingProperties", "XYZ",
std::numeric_limits<uint64_t>::max()),
Eq(boost::system::errc::invalid_argument));
EXPECT_THAT(getProperty<std::string>(report->getPath(), "ReportingType"),
Eq(utils::enumToString(ReportingType::onRequest)));
}
TEST_F(TestReport, failsToSetIncompatibleReportingType)
{
auto report = makeReport(defaultParams()
.reportId("report2")
.reportingType(ReportingType::onRequest)
.interval(Milliseconds{0}));
EXPECT_THAT(callMethod(sut->getPath(), "SetReportingProperties",
utils::enumToString(ReportingType::periodic),
std::numeric_limits<uint64_t>::max()),
Eq(boost::system::errc::invalid_argument));
EXPECT_THAT(getProperty<std::string>(report->getPath(), "ReportingType"),
Eq(utils::enumToString(ReportingType::onRequest)));
}
TEST_F(TestReport, settingEmitsReadingsUpdateHaveNoEffect)
{
EXPECT_THAT(
setProperty(sut->getPath(), "EmitsReadingsUpdate", true).value(),
Eq(boost::system::errc::read_only_file_system));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "EmitsReadingsUpdate"),
Eq(utils::contains(defaultParams().reportActions(),
ReportAction::emitsReadingsUpdate)));
}
TEST_F(TestReport, settingLogToMetricReportCollectionHaveNoEffect)
{
EXPECT_THAT(
setProperty(sut->getPath(), "LogToMetricReportsCollection", true)
.value(),
Eq(boost::system::errc::read_only_file_system));
EXPECT_THAT(
getProperty<bool>(sut->getPath(), "LogToMetricReportsCollection"),
Eq(utils::contains(defaultParams().reportActions(),
ReportAction::logToMetricReportsCollection)));
}
TEST_F(TestReport, settingPersistencyToFalseRemovesReportFromStorage)
{
EXPECT_CALL(storageMock, store(_, _)).Times(0);
EXPECT_CALL(storageMock, remove(to_file_path(sut->getId())))
.Times(AtLeast(1));
bool persistency = false;
EXPECT_THAT(setProperty(sut->getPath(), "Persistency", persistency).value(),
Eq(boost::system::errc::success));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistency"),
Eq(persistency));
}
TEST_F(TestReport, deleteReport)
{
EXPECT_CALL(*reportManagerMock, removeReport(sut.get()));
auto ec = deleteReport(sut->getPath());
EXPECT_THAT(ec, Eq(boost::system::errc::success));
}
TEST_F(TestReport, deletingNonExistingReportReturnInvalidRequestDescriptor)
{
auto ec =
deleteReport(utils::constants::reportDirPath.str + "NonExisting"s);
EXPECT_THAT(ec.value(), Eq(EBADR));
}
TEST_F(TestReport, deleteReportExpectThatFileIsRemoveFromStorage)
{
EXPECT_CALL(storageMock, store(_, _)).Times(0);
EXPECT_CALL(storageMock, remove(to_file_path(sut->getId())))
.Times(AtLeast(1));
auto ec = deleteReport(sut->getPath());
EXPECT_THAT(ec, Eq(boost::system::errc::success));
}
TEST_F(TestReport, updatesTriggerIdWhenTriggerIsAdded)
{
utils::Messanger messanger(DbusEnvironment::getIoc());
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger1", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger1", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger2", {"someOtherReport"}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist,
"trigger3",
{"someOtherReport", defaultParams().reportId()}});
EXPECT_THAT(
getProperty<std::vector<object_path>>(sut->getPath(), "Triggers"),
UnorderedElementsAre(utils::constants::triggerDirPath / "trigger1",
utils::constants::triggerDirPath / "trigger3"));
}
TEST_F(TestReport, updatesTriggerIdWhenTriggerIsRemoved)
{
utils::Messanger messanger(DbusEnvironment::getIoc());
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger1", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger2", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger3", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Removed, "trigger1", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Removed, "trigger2", {}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Removed, "trigger1", {defaultParams().reportId()}});
EXPECT_THAT(
getProperty<std::vector<object_path>>(sut->getPath(), "Triggers"),
UnorderedElementsAre(utils::constants::triggerDirPath / "trigger3"));
}
TEST_F(TestReport, updatesTriggerIdWhenTriggerIsModified)
{
utils::Messanger messanger(DbusEnvironment::getIoc());
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger1", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger2", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger3", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger1", {defaultParams().reportId()}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger2", {}});
messanger.send(messages::TriggerPresenceChangedInd{
messages::Presence::Exist, "trigger3", {defaultParams().reportId()}});
EXPECT_THAT(
getProperty<std::vector<object_path>>(sut->getPath(), "Triggers"),
UnorderedElementsAre(utils::constants::triggerDirPath / "trigger1",
utils::constants::triggerDirPath / "trigger3"));
}
class TestReportStore :
public TestReport,
public WithParamInterface<std::pair<std::string, nlohmann::json>>
{
void SetUp() override {}
};
INSTANTIATE_TEST_SUITE_P(
_, TestReportStore,
Values(
std::make_pair("Enabled"s, nlohmann::json(defaultParams().enabled())),
std::make_pair("Version"s, nlohmann::json(7)),
std::make_pair("Id"s, nlohmann::json(defaultParams().reportId())),
std::make_pair("Name"s, nlohmann::json(defaultParams().reportName())),
std::make_pair("ReportingType",
nlohmann::json(defaultParams().reportingType())),
std::make_pair("ReportActions", nlohmann::json(utils::transform(
defaultParams().reportActions(),
[](const auto v) {
return utils::toUnderlying(v);
}))),
std::make_pair("Interval",
nlohmann::json(defaultParams().interval().count())),
std::make_pair("AppendLimit",
nlohmann::json(ReportParams().appendLimit())),
std::make_pair(
"ReadingParameters",
nlohmann::json(
{{{tstring::SensorPath::str(),
{{{tstring::Service::str(), "Service"},
{tstring::Path::str(),
"/xyz/openbmc_project/sensors/power/p1"},
{tstring::Metadata::str(), "metadata1"}}}},
{tstring::OperationType::str(), OperationType::avg},
{tstring::CollectionTimeScope::str(),
CollectionTimeScope::point},
{tstring::CollectionDuration::str(), 0}},
{{tstring::SensorPath::str(),
{{{tstring::Service::str(), "Service"},
{tstring::Path::str(),
"/xyz/openbmc_project/sensors/power/p2"},
{tstring::Metadata::str(), "metadata2"}}}},
{tstring::OperationType::str(), OperationType::avg},
{tstring::CollectionTimeScope::str(),
CollectionTimeScope::point},
{tstring::CollectionDuration::str(), 0}}}))));
TEST_P(TestReportStore, settingPersistencyToTrueStoresReport)
{
sut = makeReport(defaultParams());
{
InSequence seq;
EXPECT_CALL(storageMock, remove(to_file_path(sut->getId())));
EXPECT_CALL(checkPoint, Call());
EXPECT_CALL(storageMock, store(to_file_path(sut->getId()), _));
}
setProperty(sut->getPath(), "Persistency", false);
checkPoint.Call();
setProperty(sut->getPath(), "Persistency", true);
const auto& [key, value] = GetParam();
ASSERT_THAT(storedConfiguration.at(key), Eq(value));
}
TEST_P(TestReportStore, reportIsSavedToStorageAfterCreated)
{
EXPECT_CALL(storageMock,
store(to_file_path(defaultParams().reportId()), _));
sut = makeReport(defaultParams());
const auto& [key, value] = GetParam();
ASSERT_THAT(storedConfiguration.at(key), Eq(value));
}
class TestReportValidNames :
public TestReport,
public WithParamInterface<ReportParams>
{
public:
void SetUp() override {}
};
INSTANTIATE_TEST_SUITE_P(
ValidNames, TestReportValidNames,
Values(defaultParams().reportName("Valid_1"),
defaultParams().reportName("Valid_1/Valid_2"),
defaultParams().reportName("Valid_1/Valid_2/Valid_3")));
TEST_P(TestReportValidNames, reportCtorDoesNotThrowOnValidName)
{
EXPECT_NO_THROW(makeReport(GetParam()));
}
class TestReportInvalidIds :
public TestReport,
public WithParamInterface<ReportParams>
{
public:
void SetUp() override {}
};
INSTANTIATE_TEST_SUITE_P(InvalidNames, TestReportInvalidIds,
Values(defaultParams().reportId("/"),
defaultParams().reportId("/Invalid"),
defaultParams().reportId("Invalid/"),
defaultParams().reportId("Invalid/Invalid/"),
defaultParams().reportId("Invalid?")));
TEST_P(TestReportInvalidIds, failsToCreateReportWithInvalidName)
{
EXPECT_CALL(storageMock, store).Times(0);
EXPECT_THROW(makeReport(GetParam()), sdbusplus::exception::SdBusError);
}
class TestReportAllReportTypes :
public TestReport,
public WithParamInterface<ReportParams>
{
public:
void SetUp() override
{
sut = makeReport(GetParam());
}
};
INSTANTIATE_TEST_SUITE_P(
_, TestReportAllReportTypes,
Values(defaultParams().reportingType(ReportingType::onRequest),
defaultParams().reportingType(ReportingType::onChange),
defaultParams()
.reportingType(ReportingType::periodic)
.interval(ReportManager::minInterval)));
TEST_P(TestReportAllReportTypes, returnPropertValueOfReportType)
{
EXPECT_THAT(utils::toReportingType(
getProperty<std::string>(sut->getPath(), "ReportingType")),
Eq(GetParam().reportingType()));
}
TEST_P(TestReportAllReportTypes, readingsAreUpdated)
{
clockFake.system.advance(10ms);
messanger.send(messages::UpdateReportInd{{sut->getId()}});
const auto [timestamp, readings] = getProperty<Readings>(sut->getPath(),
"Readings");
EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms));
}
TEST_P(TestReportAllReportTypes, readingsAreNotUpdatedWhenReportIsDisabled)
{
clockFake.system.advance(10ms);
setProperty(sut->getPath(), "Enabled", false);
messanger.send(messages::UpdateReportInd{{sut->getId()}});
const auto [timestamp, readings] = getProperty<Readings>(sut->getPath(),
"Readings");
EXPECT_THAT(Milliseconds{timestamp}, Eq(0ms));
}
TEST_P(TestReportAllReportTypes, readingsAreNotUpdatedWhenReportIdDiffers)
{
clockFake.system.advance(10ms);
messanger.send(messages::UpdateReportInd{{sut->getId() + "x"s}});
const auto [timestamp, readings] = getProperty<Readings>(sut->getPath(),
"Readings");
EXPECT_THAT(Milliseconds{timestamp}, Eq(0ms));
}
class TestReportOnRequestType : public TestReport
{
void SetUp() override
{
sut =
makeReport(defaultParams().reportingType(ReportingType::onRequest));
}
};
TEST_F(TestReportOnRequestType, updatesReadingTimestamp)
{
clockFake.system.advance(10ms);
ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success));
const auto [timestamp, readings] = getProperty<Readings>(sut->getPath(),
"Readings");
EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms));
}
TEST_F(TestReportOnRequestType, updatesReadingWhenUpdateIsCalled)
{
ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success));
const auto [timestamp, readings] = getProperty<Readings>(sut->getPath(),
"Readings");
EXPECT_THAT(readings, ElementsAre(std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u)));
}
class TestReportNonOnRequestType :
public TestReport,
public WithParamInterface<ReportParams>
{
void SetUp() override
{
sut = makeReport(GetParam());
}
};
INSTANTIATE_TEST_SUITE_P(_, TestReportNonOnRequestType,
Values(defaultParams()
.reportingType(ReportingType::periodic)
.interval(ReportManager::minInterval * 10),
defaultParams()
.reportingType(ReportingType::onChange)
.interval(Milliseconds(0))));
TEST_P(TestReportNonOnRequestType, readingsAreNotUpdateOnUpdateCall)
{
ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success));
EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"),
Eq(Readings{}));
}
class TestReportNonPeriodicReport :
public TestReport,
public WithParamInterface<ReportParams>
{
public:
void SetUp() override
{
sut = makeReport(GetParam());
}
};
INSTANTIATE_TEST_SUITE_P(
_, TestReportNonPeriodicReport,
Values(defaultParams().reportingType(ReportingType::onRequest),
defaultParams().reportingType(ReportingType::onChange)));
TEST_P(TestReportNonPeriodicReport, readingsAreNotUpdatedAfterIntervalExpires)
{
DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms);
EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"),
Eq(Readings{}));
}
class TestReportPeriodicReport : public TestReport
{
void SetUp() override
{
sut = makeReport(defaultParams()
.appendLimit(2u)
.reportingType(ReportingType::periodic)
.interval(ReportManager::minInterval));
}
};
TEST_F(TestReportPeriodicReport, readingTimestampIsUpdatedAfterIntervalExpires)
{
clockFake.system.advance(10ms);
DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms);
const auto [timestamp, readings] = getProperty<Readings>(sut->getPath(),
"Readings");
EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms));
}
TEST_F(TestReportPeriodicReport, readingsAreUpdatedAfterIntervalExpires)
{
DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms);
const auto [timestamp, readings] = getProperty<Readings>(sut->getPath(),
"Readings");
EXPECT_THAT(readings, ElementsAre(std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u)));
}
struct ReportUpdatesReportParams
{
ReportParams reportParams;
std::vector<ReadingData> expectedReadings;
bool expectedEnabled;
friend void PrintTo(const ReportUpdatesReportParams& params,
std::ostream* os)
{
*os << "{ ReportParams: ";
PrintTo(params.reportParams, os);
*os << ", ExpectedReadings: ";
PrintTo(params.expectedReadings, os);
*os << ", ExpectedEnabled: " << params.expectedEnabled << " }";
}
};
class TestReportWithReportUpdatesAndLimit :
public TestReport,
public WithParamInterface<ReportUpdatesReportParams>
{
public:
void SetUp() override {}
void changeReport(ReportingType rt, Milliseconds interval)
{
callMethod(sut->getPath(), "SetReportingProperties",
utils::enumToString(rt), interval.count());
}
auto readings()
{
auto [timestamp, readings] = getProperty<Readings>(sut->getPath(),
"Readings");
return readings;
}
void updateReportFourTimes()
{
for (int i = 0; i < 4; i++)
{
messanger.send(messages::UpdateReportInd{{sut->getId()}});
}
}
};
INSTANTIATE_TEST_SUITE_P(
_, TestReportWithReportUpdatesAndLimit,
Values(
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::appendWrapsWhenFull)
.appendLimit(5),
std::vector<ReadingData>{{std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("b"s, 17.1, 114u)}},
true},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::appendWrapsWhenFull)
.appendLimit(4),
std::vector<ReadingData>{{std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u)}},
true},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::appendWrapsWhenFull)
.appendLimit(0),
std::vector<ReadingData>{}, true},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::appendStopsWhenFull)
.appendLimit(10),
std::vector<ReadingData>{{std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u)}},
true},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::appendStopsWhenFull)
.appendLimit(5),
std::vector<ReadingData>{{std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("b"s, 17.1, 114u)}},
false},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::appendStopsWhenFull)
.appendLimit(4),
std::vector<ReadingData>{{std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u),
std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u)}},
false},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::appendStopsWhenFull)
.appendLimit(0),
std::vector<ReadingData>{}, false},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::overwrite)
.appendLimit(500),
std::vector<ReadingData>{{std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u)}},
true},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::overwrite)
.appendLimit(1),
std::vector<ReadingData>{{std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u)}},
true},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::overwrite)
.appendLimit(0),
std::vector<ReadingData>{{std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u)}},
true},
ReportUpdatesReportParams{
defaultParams()
.reportUpdates(ReportUpdates::appendStopsWhenFull)
.appendLimit(2u),
std::vector<ReadingData>{{std::make_tuple("b"s, 17.1, 114u),
std::make_tuple("bb"s, 42.0, 74u)}},
false}));
TEST_P(TestReportWithReportUpdatesAndLimit,
readingsAreUpdatedAfterIntervalExpires)
{
sut = makeReport(ReportParams(GetParam().reportParams)
.reportingType(ReportingType::periodic)
.interval(std::chrono::hours(1000)));
updateReportFourTimes();
EXPECT_THAT(readings(), ElementsAreArray(GetParam().expectedReadings));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"),
Eq(GetParam().expectedEnabled));
}
TEST_P(TestReportWithReportUpdatesAndLimit,
appendLimitIsRespectedAfterChangingToPeriodic)
{
sut = makeReport(ReportParams(GetParam().reportParams)
.appendLimit(GetParam().expectedReadings.size())
.reportingType(ReportingType::onRequest)
.interval(std::chrono::hours(0)));
changeReport(ReportingType::periodic, std::chrono::hours(1000));
updateReportFourTimes();
EXPECT_THAT(readings(), ElementsAreArray(GetParam().expectedReadings));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"),
Eq(GetParam().expectedEnabled));
}
TEST_P(TestReportWithReportUpdatesAndLimit,
appendLimitIsIgnoredAfterChangingToOnRequest)
{
sut = makeReport(ReportParams(GetParam().reportParams)
.reportingType(ReportingType::periodic)
.interval(std::chrono::hours(1000)));
changeReport(ReportingType::onRequest, Milliseconds{0});
updateReportFourTimes();
EXPECT_THAT(readings(), SizeIs(2u));
EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), Eq(true));
}
class TestReportInitialization : public TestReport
{
public:
void SetUp() override
{
initMetricMocks(defaultParams().metricParameters());
}
void monitorProc(sdbusplus::message_t& msg)
{
std::string iface;
std::vector<std::pair<std::string, std::variant<Readings>>>
changed_properties;
std::vector<std::string> invalidated_properties;
msg.read(iface, changed_properties, invalidated_properties);
if (iface == Report::reportIfaceName)
{
for (const auto& [name, value] : changed_properties)
{
if (name == "Readings")
{
readingsUpdated.Call();
}
}
}
}
void makeMonitor()
{
monitor = std::make_unique<sdbusplus::bus::match_t>(
*DbusEnvironment::getBus(),
sdbusplus::bus::match::rules::propertiesChanged(
sut->getPath(), Report::reportIfaceName),
[this](auto& msg) { monitorProc(msg); });
}
std::unique_ptr<sdbusplus::bus::match_t> monitor;
MockFunction<void()> readingsUpdated;
};
TEST_F(TestReportInitialization,
registersForMetricUpdatesWhenOnChangeReportCreated)
{
std::vector<const interfaces::MetricListener*> args;
for (auto& metric : metricMocks)
{
EXPECT_CALL(*metric, registerForUpdates(_))
.WillOnce(Invoke([&args](const interfaces::MetricListener& report) {
args.emplace_back(&report);
}));
;
}
sut = makeReport(defaultParams().reportingType(ReportingType::onChange));
EXPECT_THAT(args, SizeIs(metricMocks.size()));
for (const auto* reportPtr : args)
{
EXPECT_THAT(reportPtr, Eq(sut.get()));
}
}
TEST_F(TestReportInitialization,
deregistersForMetricUpdatesWhenOnChangeReportDestroyed)
{
sut = makeReport(defaultParams().reportingType(ReportingType::onChange));
for (auto& metric : metricMocks)
{
EXPECT_CALL(*metric,
unregisterFromUpdates(Ref(
static_cast<interfaces::MetricListener&>(*sut.get()))));
}
sut = nullptr;
}
TEST_F(TestReportInitialization,
metricsAreInitializedWhenEnabledReportConstructed)
{
for (auto& metric : metricMocks)
{
EXPECT_CALL(*metric, initialize());
}
sut = makeReport(defaultParams().enabled(true));
}
TEST_F(TestReportInitialization,
metricsAreNotInitializedWhenDisabledReportConstructed)
{
for (auto& metric : metricMocks)
{
EXPECT_CALL(*metric, initialize()).Times(0);
}
sut = makeReport(defaultParams().enabled(false));
}
TEST_F(TestReportInitialization,
emitReadingsUpdateIsTrueReadingsPropertiesChangedSingalEmits)
{
EXPECT_CALL(readingsUpdated, Call())
.WillOnce(
InvokeWithoutArgs(DbusEnvironment::setPromise("readingsUpdated")));
const auto elapsed = DbusEnvironment::measureTime([this] {
sut = makeReport(defaultParams()
.reportingType(ReportingType::periodic)
.reportActions({ReportAction::emitsReadingsUpdate})
.interval(ReportManager::minInterval));
makeMonitor();
EXPECT_TRUE(DbusEnvironment::waitForFuture("readingsUpdated"));
});
EXPECT_THAT(elapsed, AllOf(Ge(ReportManager::minInterval),
Lt(ReportManager::minInterval * 2)));
}
TEST_F(TestReportInitialization,
emitReadingsUpdateIsFalseReadingsPropertiesChangesSigalDoesNotEmits)
{
EXPECT_CALL(readingsUpdated, Call()).Times(0);
sut = makeReport(defaultParams()
.reportingType(ReportingType::periodic)
.reportActions({})
.interval(Milliseconds(1000)));
makeMonitor();
DbusEnvironment::sleepFor(defaultParams().interval() * 2);
}
TEST_F(TestReportInitialization, triggerIdsPropertyIsInitialzed)
{
for (const auto& triggerId : {"trigger1", "trigger2"})
{
messanger.on_receive<messages::CollectTriggerIdReq>(
[this, triggerId](const auto& msg) {
messanger.send(messages::CollectTriggerIdResp{triggerId});
});
}
sut = makeReport(ReportParams());
EXPECT_THAT(
getProperty<std::vector<object_path>>(sut->getPath(), "Triggers"),
UnorderedElementsAre(utils::constants::triggerDirPath / "trigger1",
utils::constants::triggerDirPath / "trigger2"));
}
TEST_F(TestReportInitialization,
metricValuesAreNotStoredForReportUpdatesDifferentThanAppendStopsWhenFull)
{
sut = makeReport(ReportParams()
.reportingType(ReportingType::periodic)
.interval(1h)
.reportUpdates(ReportUpdates::appendWrapsWhenFull)
.readings(Readings{{}, {{}}}));
ASSERT_THAT(storedConfiguration.find("MetricValues"),
Eq(storedConfiguration.end()));
}
TEST_F(TestReportInitialization, metricValuesAreNotStoredForOnRequestReport)
{
sut = makeReport(ReportParams()
.reportingType(ReportingType::onRequest)
.reportUpdates(ReportUpdates::appendStopsWhenFull)
.readings(Readings{{}, {{}}}));
ASSERT_THAT(storedConfiguration.find("MetricValues"),
Eq(storedConfiguration.end()));
}
TEST_F(TestReportInitialization,
metricValuesAreStoredForNonOnRequestReportWithAppendStopsWhenFull)
{
const auto readings = Readings{{}, {{}}};
sut = makeReport(ReportParams()
.reportingType(ReportingType::periodic)
.interval(1h)
.reportUpdates(ReportUpdates::appendStopsWhenFull)
.readings(readings));
ASSERT_THAT(storedConfiguration.at("MetricValues").get<LabeledReadings>(),
Eq(utils::toLabeledReadings(readings)));
}
class TestReportInitializationOnChangeReport : public TestReportInitialization
{
public:
void SetUp() override
{
initMetricMocks(params.metricParameters());
}
ReportParams params = defaultOnChangeParams();
};
TEST_F(TestReportInitializationOnChangeReport,
doesntUpdateReadingsWhenNotRequired)
{
EXPECT_CALL(*metricMocks[0], updateReadings(_)).Times(0);
ON_CALL(*metricMocks[0], isTimerRequired()).WillByDefault(Return(false));
sut = makeReport(params);
DbusEnvironment::sleepFor(500ms);
}
TEST_F(TestReportInitializationOnChangeReport, updatesReadingsWhenRequired)
{
EXPECT_CALL(*metricMocks[0], updateReadings(_))
.WillOnce(Return())
.WillOnce(
InvokeWithoutArgs(DbusEnvironment::setPromise("readingsUpdated")))
.WillRepeatedly(Return());
ON_CALL(*metricMocks[0], isTimerRequired()).WillByDefault(Return(true));
sut = makeReport(params);
DbusEnvironment::waitForFuture("readingsUpdated");
}