1 #include "health_metric_collection.hpp"
2 
3 #include <sdbusplus/test/sdbus_mock.hpp>
4 #include <xyz/openbmc_project/Metric/Value/server.hpp>
5 
6 #include <gtest/gtest.h>
7 
8 namespace ConfigIntf = phosphor::health::metric::config;
9 namespace MetricIntf = phosphor::health::metric;
10 namespace CollectionIntf = phosphor::health::metric::collection;
11 
12 using PathInterface =
13     sdbusplus::common::xyz::openbmc_project::metric::Value::namespace_path;
14 using ThresholdIntf =
15     sdbusplus::server::xyz::openbmc_project::common::Threshold;
16 using ::testing::Invoke;
17 using ::testing::IsNull;
18 using ::testing::NotNull;
19 using ::testing::StrEq;
20 
21 class HealthMetricCollectionTest : public ::testing::Test
22 {
23   public:
24     sdbusplus::SdBusMock sdbusMock;
25     sdbusplus::bus_t bus = sdbusplus::get_mocked_new(&sdbusMock);
26 
27     static constexpr auto busName = "xyz.openbmc_project.test.HealthMon";
28     static constexpr auto objPath = "/xyz/openbmc_project/sdbusplus/test";
29     const std::string valueInterface =
30         sdbusplus::common::xyz::openbmc_project::metric::Value::interface;
31     const std::string thresholdInterface =
32         sdbusplus::common::xyz::openbmc_project::common::Threshold::interface;
33     ConfigIntf::HealthMetric::map_t configs;
34 
35     void SetUp() override
36     {
37         sdbusplus::server::manager_t objManager(bus, objPath);
38         bus.request_name(busName);
39 
40         configs = ConfigIntf::getHealthMetricConfigs();
41         EXPECT_THAT(configs.size(), testing::Ge(1));
42         // Update the health metric window size to 1 and path for test purposes
43         for (auto& [key, values] : configs)
44         {
45             for (auto& config : values)
46             {
47                 config.windowSize = 1;
48                 if (key == MetricIntf::Type::storage &&
49                     config.subType == MetricIntf::SubType::storageReadWrite)
50                 {
51                     config.path = "/tmp";
52                 }
53             }
54         }
55     }
56 
57     void updateThreshold(ThresholdIntf::Bound bound, double value)
58     {
59         for (auto& [key, values] : configs)
60         {
61             for (auto& config : values)
62             {
63                 for (auto& threshold : config.thresholds)
64                 {
65                     if (get<ThresholdIntf::Bound>(threshold.first) == bound)
66                     {
67                         threshold.second.value = value;
68                     }
69                 }
70             }
71         }
72     }
73 
74     void createCollection()
75     {
76         std::map<MetricIntf::Type,
77                  std::unique_ptr<CollectionIntf::HealthMetricCollection>>
78             collections;
79         MetricIntf::paths_t bmcPaths = {};
80         for (const auto& [type, collectionConfig] : configs)
81         {
82             collections[type] =
83                 std::make_unique<CollectionIntf::HealthMetricCollection>(
84                     bus, type, collectionConfig, bmcPaths);
85             collections[type]->read();
86         }
87     }
88 };
89 
90 TEST_F(HealthMetricCollectionTest, TestCreation)
91 {
92     // Change threshold values to avoid threshold assertion
93     updateThreshold(ThresholdIntf::Bound::Upper, 100);
94     updateThreshold(ThresholdIntf::Bound::Lower, 0);
95 
96     EXPECT_CALL(sdbusMock,
97                 sd_bus_emit_properties_changed_strv(
98                     IsNull(), NotNull(), StrEq(valueInterface), NotNull()))
99         .WillRepeatedly(Invoke(
100             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
101                 [[maybe_unused]] const char* interface, const char** names) {
102         // Test no signal generation for metric init properties
103         const std::set<std::string> metricInitProperties = {"MaxValue",
104                                                             "MinValue", "Unit"};
105         EXPECT_THAT(metricInitProperties,
106                     testing::Not(testing::Contains(names[0])));
107         // Test signal generated for Value property set
108         const std::set<std::string> metricSetProperties = {"Value"};
109         EXPECT_THAT(metricSetProperties, testing::Contains(names[0]));
110         return 0;
111     }));
112 
113     EXPECT_CALL(sdbusMock,
114                 sd_bus_emit_properties_changed_strv(
115                     IsNull(), NotNull(), StrEq(thresholdInterface), NotNull()))
116         .WillRepeatedly(Invoke(
117             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
118                 [[maybe_unused]] const char* interface, const char** names) {
119         // Test no signal generation for threshold init properties
120         const std::set<std::string> thresholdProperties = {"Value", "Asserted"};
121         EXPECT_THAT(thresholdProperties,
122                     testing::Not(testing::Contains(names[0])));
123         return 0;
124     }));
125 
126     createCollection();
127 }
128 
129 TEST_F(HealthMetricCollectionTest, TestThresholdAsserted)
130 {
131     // Change threshold values to trigger threshold assertion
132     updateThreshold(ThresholdIntf::Bound::Upper, 0);
133     updateThreshold(ThresholdIntf::Bound::Lower, 100);
134 
135     // Test metric value property change
136     EXPECT_CALL(sdbusMock,
137                 sd_bus_emit_properties_changed_strv(
138                     IsNull(), NotNull(), StrEq(valueInterface), NotNull()))
139         .WillRepeatedly(Invoke(
140             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
141                 [[maybe_unused]] const char* interface, const char** names) {
142         EXPECT_THAT("Value", StrEq(names[0]));
143         return 0;
144     }));
145 
146     // Test threshold asserted property change
147     EXPECT_CALL(sdbusMock,
148                 sd_bus_emit_properties_changed_strv(
149                     IsNull(), NotNull(), StrEq(thresholdInterface), NotNull()))
150         .WillRepeatedly(Invoke(
151             [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path,
152                 [[maybe_unused]] const char* interface, const char** names) {
153         EXPECT_THAT("Asserted", StrEq(names[0]));
154         return 0;
155     }));
156 
157     // Test AssertionChanged signal generation
158     EXPECT_CALL(sdbusMock,
159                 sd_bus_message_new_signal(IsNull(), NotNull(), NotNull(),
160                                           StrEq(thresholdInterface),
161                                           StrEq("AssertionChanged")))
162         .Times(8);
163 
164     createCollection();
165 }
166