blob: a8c3d8687e6ffb89f4365209d0b03e17a1b6fa23 [file] [log] [blame]
#include "fakes/clock_fake.hpp"
#include "helpers.hpp"
#include "metric.hpp"
#include "mocks/sensor_mock.hpp"
#include "params/metric_params.hpp"
#include "utils/conv_container.hpp"
#include "utils/conversion.hpp"
#include "utils/tstring.hpp"
#include <gmock/gmock.h>
using namespace testing;
using namespace std::chrono_literals;
namespace tstring = utils::tstring;
using Timestamp = uint64_t;
class TestMetric : public Test
{
public:
static std::vector<std::shared_ptr<SensorMock>>
makeSensorMocks(size_t amount)
{
std::vector<std::shared_ptr<SensorMock>> result;
for (size_t i = 0; i < amount; ++i)
{
result.emplace_back(std::make_shared<NiceMock<SensorMock>>());
}
return result;
}
std::shared_ptr<Metric> makeSut(const MetricParams& p)
{
return std::make_shared<Metric>(
utils::convContainer<std::shared_ptr<interfaces::Sensor>>(
sensorMocks),
p.operationType(), p.id(), p.metadata(), p.collectionTimeScope(),
p.collectionDuration(), std::move(clockFakePtr));
}
MetricParams params = MetricParams()
.id("id")
.metadata("metadata")
.operationType(OperationType::avg)
.collectionTimeScope(CollectionTimeScope::point)
.collectionDuration(CollectionDuration(0ms));
std::vector<std::shared_ptr<SensorMock>> sensorMocks = makeSensorMocks(1u);
std::unique_ptr<ClockFake> clockFakePtr = std::make_unique<ClockFake>();
ClockFake& clockFake = *clockFakePtr;
std::shared_ptr<Metric> sut;
};
TEST_F(TestMetric, subscribesForSensorDuringInitialization)
{
sut = makeSut(params);
EXPECT_CALL(*sensorMocks.front(),
registerForUpdates(Truly([sut = sut.get()](const auto& a0) {
return a0.lock().get() == sut;
})));
sut->initialize();
}
TEST_F(TestMetric, unsubscribesForSensorDuringDeinitialization)
{
sut = makeSut(params);
EXPECT_CALL(*sensorMocks.front(),
unregisterFromUpdates(Truly([sut = sut.get()](const auto& a0) {
return a0.lock().get() == sut;
})));
sut->deinitialize();
}
TEST_F(TestMetric, containsEmptyReadingAfterCreated)
{
sut = makeSut(params);
ASSERT_THAT(sut->getReadings(),
ElementsAre(MetricValue({"id", "metadata", 0., 0u})));
}
TEST_F(TestMetric, parsesSensorMetadata)
{
using ReadingMetadata =
utils::LabeledTuple<std::tuple<std::string, std::string>,
utils::tstring::SensorDbusPath,
utils::tstring::SensorRedfishUri>;
nlohmann::json metadata;
metadata["MetricProperties"] = {"sensor1", "sensor2"};
sensorMocks = makeSensorMocks(2);
sut = makeSut(params.metadata(metadata.dump()));
EXPECT_THAT(
sut->getReadings(),
ElementsAre(
MetricValue{"id", ReadingMetadata("", "sensor1").dump(), 0., 0u},
MetricValue{"id", ReadingMetadata("", "sensor2").dump(), 0., 0u}));
}
TEST_F(TestMetric, parsesSensorMetadataWhenMoreMetadataThanSensors)
{
nlohmann::json metadata;
metadata["MetricProperties"] = {"sensor1", "sensor2"};
sensorMocks = makeSensorMocks(1);
sut = makeSut(params.metadata(metadata.dump()));
EXPECT_THAT(sut->getReadings(),
ElementsAre(MetricValue{"id", metadata.dump(), 0., 0u}));
}
TEST_F(TestMetric, parsesSensorMetadataWhenMoreSensorsThanMetadata)
{
nlohmann::json metadata;
metadata["MetricProperties"] = {"sensor1"};
sensorMocks = makeSensorMocks(2);
sut = makeSut(params.metadata(metadata.dump()));
EXPECT_THAT(sut->getReadings(),
ElementsAre(MetricValue{"id", metadata.dump(), 0., 0u},
MetricValue{"id", metadata.dump(), 0., 0u}));
}
class TestMetricAfterInitialization : public TestMetric
{
public:
void SetUp() override
{
sut = makeSut(params);
sut->initialize();
}
};
TEST_F(TestMetricAfterInitialization, containsEmptyReading)
{
ASSERT_THAT(sut->getReadings(),
ElementsAre(MetricValue({"id", "metadata", 0., 0u})));
}
TEST_F(TestMetricAfterInitialization, updatesMetricValuesOnSensorUpdate)
{
sut->sensorUpdated(*sensorMocks.front(), Timestamp{18}, 31.2);
ASSERT_THAT(sut->getReadings(),
ElementsAre(MetricValue{"id", "metadata", 31.2, 18u}));
}
TEST_F(TestMetricAfterInitialization,
throwsWhenUpdateIsPerformedOnUnknownSensor)
{
auto sensor = std::make_shared<StrictMock<SensorMock>>();
EXPECT_THROW(sut->sensorUpdated(*sensor, Timestamp{10}), std::out_of_range);
EXPECT_THROW(sut->sensorUpdated(*sensor, Timestamp{10}, 20.0),
std::out_of_range);
}
TEST_F(TestMetricAfterInitialization, dumpsConfiguration)
{
namespace ts = utils::tstring;
ON_CALL(*sensorMocks.front(), id())
.WillByDefault(Return(SensorMock::makeId("service1", "path1")));
const auto conf = sut->dumpConfiguration();
LabeledMetricParameters expected = {};
expected.at_label<ts::Id>() = params.id();
expected.at_label<ts::MetricMetadata>() = params.metadata();
expected.at_label<ts::OperationType>() = params.operationType();
expected.at_label<ts::CollectionTimeScope>() = params.collectionTimeScope();
expected.at_label<ts::CollectionDuration>() = params.collectionDuration();
expected.at_label<ts::SensorPath>() = {
LabeledSensorParameters("service1", "path1")};
EXPECT_THAT(conf, Eq(expected));
}
class TestMetricCalculationFunctions :
public TestMetric,
public WithParamInterface<MetricParams>
{
public:
void SetUp() override
{
clockFake.reset();
sut = makeSut(params.operationType(GetParam().operationType())
.collectionTimeScope(GetParam().collectionTimeScope())
.collectionDuration(GetParam().collectionDuration()));
}
static std::vector<std::pair<Milliseconds, double>> defaultReadings()
{
std::vector<std::pair<Milliseconds, double>> ret;
ret.emplace_back(0ms, std::numeric_limits<double>::quiet_NaN());
ret.emplace_back(10ms, 14.);
ret.emplace_back(1ms, 3.);
ret.emplace_back(5ms, 7.);
return ret;
}
};
MetricParams defaultSingleParams()
{
return MetricParams()
.operationType(OperationType::single)
.readings(TestMetricCalculationFunctions::defaultReadings())
.expectedReading(11ms, 7.0);
}
INSTANTIATE_TEST_SUITE_P(
OperationSingleReturnsLastReading, TestMetricCalculationFunctions,
Values(
defaultSingleParams().collectionTimeScope(CollectionTimeScope::point),
defaultSingleParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(100ms)),
defaultSingleParams().collectionTimeScope(
CollectionTimeScope::startup)));
MetricParams defaultPointParams()
{
return defaultSingleParams().collectionTimeScope(
CollectionTimeScope::point);
}
INSTANTIATE_TEST_SUITE_P(
TimeScopePointReturnsLastReading, TestMetricCalculationFunctions,
Values(defaultPointParams().operationType(OperationType::single),
defaultPointParams().operationType(OperationType::min),
defaultPointParams().operationType(OperationType::max),
defaultPointParams().operationType(OperationType::sum),
defaultPointParams().operationType(OperationType::avg)));
MetricParams defaultMinParams()
{
return defaultSingleParams().operationType(OperationType::min);
}
INSTANTIATE_TEST_SUITE_P(
ReturnsMinForGivenTimeScope, TestMetricCalculationFunctions,
Values(defaultMinParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(100ms))
.expectedReading(10ms, 3.0),
defaultMinParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(3ms))
.expectedReading(13ms, 7.0),
defaultMinParams()
.collectionTimeScope(CollectionTimeScope::startup)
.expectedReading(10ms, 3.0)));
MetricParams defaultMaxParams()
{
return defaultSingleParams().operationType(OperationType::max);
}
INSTANTIATE_TEST_SUITE_P(
ReturnsMaxForGivenTimeScope, TestMetricCalculationFunctions,
Values(defaultMaxParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(100ms))
.expectedReading(0ms, 14.0),
defaultMaxParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(6ms))
.expectedReading(10ms, 14.0),
defaultMaxParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(5ms))
.expectedReading(11ms, 7.0),
defaultMaxParams()
.collectionTimeScope(CollectionTimeScope::startup)
.expectedReading(0ms, 14.0)));
MetricParams defaultSumParams()
{
return defaultSingleParams().operationType(OperationType::sum);
}
INSTANTIATE_TEST_SUITE_P(
ReturnsSumForGivenTimeScope, TestMetricCalculationFunctions,
Values(defaultSumParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(100ms))
.expectedReading(16ms, 14. * 10 + 3. * 1 + 7 * 5),
defaultSumParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(8ms))
.expectedReading(16ms, 14. * 2 + 3. * 1 + 7 * 5),
defaultSumParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(6ms))
.expectedReading(16ms, 3. * 1 + 7 * 5),
defaultSumParams()
.collectionTimeScope(CollectionTimeScope::startup)
.expectedReading(16ms, 14. * 10 + 3. * 1 + 7 * 5)));
MetricParams defaultAvgParams()
{
return defaultSingleParams().operationType(OperationType::avg);
}
INSTANTIATE_TEST_SUITE_P(
ReturnsAvgForGivenTimeScope, TestMetricCalculationFunctions,
Values(defaultAvgParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(100ms))
.expectedReading(16ms, (14. * 10 + 3. * 1 + 7 * 5) / 16.),
defaultAvgParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(8ms))
.expectedReading(16ms, (14. * 2 + 3. * 1 + 7 * 5) / 8.),
defaultAvgParams()
.collectionTimeScope(CollectionTimeScope::interval)
.collectionDuration(CollectionDuration(6ms))
.expectedReading(16ms, (3. * 1 + 7 * 5) / 6.),
defaultAvgParams()
.collectionTimeScope(CollectionTimeScope::startup)
.expectedReading(16ms, (14. * 10 + 3. * 1 + 7 * 5) / 16.)));
TEST_P(TestMetricCalculationFunctions, calculatesReadingValue)
{
for (auto [timestamp, reading] : GetParam().readings())
{
sut->sensorUpdated(*sensorMocks.front(), clockFake.timestamp(),
reading);
clockFake.advance(timestamp);
}
const auto [expectedTimestamp, expectedReading] =
GetParam().expectedReading();
const auto readings = sut->getReadings();
EXPECT_THAT(readings, ElementsAre(MetricValue{
"id", "metadata", expectedReading,
ClockFake::toTimestamp(expectedTimestamp)}));
}
TEST_P(TestMetricCalculationFunctions,
calculatedReadingValueWithIntermediateCalculations)
{
for (auto [timestamp, reading] : GetParam().readings())
{
sut->sensorUpdated(*sensorMocks.front(), clockFake.timestamp(),
reading);
clockFake.advance(timestamp);
sut->getReadings();
}
const auto [expectedTimestamp, expectedReading] =
GetParam().expectedReading();
const auto readings = sut->getReadings();
EXPECT_THAT(readings, ElementsAre(MetricValue{
"id", "metadata", expectedReading,
ClockFake::toTimestamp(expectedTimestamp)}));
}