blob: 6578a0ca9a70a9998355ad871e277fb49d659c2e [file] [log] [blame]
Tomáš Pecka3649ae12023-04-14 10:40:56 +02001#include "trompeloeil_doctest.h"
2#include <fstream>
3#include "fs-helpers/utils.h"
Tomáš Pecka45b67f92024-01-11 14:13:05 +01004#include "ietf-hardware/FspYh.h"
Tomáš Pecka23536bf2023-04-27 18:30:55 +02005#include "ietf-hardware/IETFHardware.h"
Tomáš Pecka3649ae12023-04-14 10:40:56 +02006#include "pretty_printers.h"
7#include "test_log_setup.h"
8#include "test_sysrepo_helpers.h"
9#include "tests/configure.cmake.h"
10
11using namespace std::literals;
12
13class FakeI2C : public velia::ietf_hardware::TransientI2C {
14public:
15 FakeI2C(const std::string& fakeHwmonRoot)
16 : TransientI2C({}, {}, {})
17 , m_fakeHwmonRoot(fakeHwmonRoot)
18 {
19 }
20
21 MAKE_CONST_MOCK0(isPresent, bool(), override);
22 MAKE_CONST_MOCK0(bind_mock, void());
23 MAKE_CONST_MOCK0(unbind_mock, void());
24
25 void removeHwmonFile(const std::string& name) const
26 {
27 std::filesystem::remove(m_fakeHwmonRoot / ("hwmon" + std::to_string(m_hwmonNo)) / name);
28 }
29
30 void bind() const override
31 {
32 bind_mock();
33 removeDirectoryTreeIfExists(m_fakeHwmonRoot);
34 std::filesystem::create_directory(m_fakeHwmonRoot);
35 std::filesystem::create_directory(m_fakeHwmonRoot / ("hwmon" + std::to_string(m_hwmonNo)));
36
37 for (const auto& filename : {"name", "temp1_input", "temp2_input", "curr1_input", "curr2_input", "curr3_input", "in1_input", "in2_input", "in3_input", "power1_input", "power2_input", "fan1_input"}) {
38 std::ofstream ofs(m_fakeHwmonRoot / ("hwmon" + std::to_string(m_hwmonNo)) / filename);
39 // I don't really care about the values here, I just need the HWMon class to think that the files exist.
40 ofs << 0 << "\n";
41 }
42 }
43 void unbind() const override
44 {
45 unbind_mock();
46 removeDirectoryTreeIfExists(m_fakeHwmonRoot);
47 m_hwmonNo++;
48 }
49
50private:
51 std::filesystem::path m_fakeHwmonRoot;
52 mutable std::atomic<int> m_hwmonNo = 1;
53};
54
55TEST_CASE("FspYhPsu")
56{
57 TEST_INIT_LOGS;
58 std::atomic<int> counter = 0;
59 const auto fakeHwmonRoot = CMAKE_CURRENT_BINARY_DIR + "/tests/psu"s;
60 removeDirectoryTreeIfExists(fakeHwmonRoot);
61 auto fakeI2c = std::make_shared<FakeI2C>(fakeHwmonRoot);
62 trompeloeil::sequence seq1;
63 std::shared_ptr<velia::ietf_hardware::FspYhPsu> psu;
64 std::vector<std::unique_ptr<trompeloeil::expectation>> expectations;
65
66 auto i2cPresence = [&counter] {
67 switch (counter) {
68 case 0:
69 case 2:
70 case 4:
71 return false;
72 case 1:
73 case 3:
74 return true;
75 }
76
77 REQUIRE(false);
78 __builtin_unreachable();
79 };
80
81 ALLOW_CALL(*fakeI2c, isPresent()).LR_RETURN(i2cPresence());
82 REQUIRE_CALL(*fakeI2c, bind_mock()).LR_WITH(counter == 1).IN_SEQUENCE(seq1);
83 REQUIRE_CALL(*fakeI2c, unbind_mock()).LR_WITH(counter == 2).IN_SEQUENCE(seq1);
84 REQUIRE_CALL(*fakeI2c, bind_mock()).LR_WITH(counter == 3).IN_SEQUENCE(seq1);
85 REQUIRE_CALL(*fakeI2c, unbind_mock()).LR_WITH(counter == 4).IN_SEQUENCE(seq1);
86
87 psu = std::make_shared<velia::ietf_hardware::FspYhPsu>(fakeHwmonRoot, "psu", fakeI2c);
88
Tomáš Pecka23536bf2023-04-27 18:30:55 +020089 const velia::ietf_hardware::DataTree expectedDisabled = {
90 {"/ietf-hardware:hardware/component[name='ne:psu']/class", "iana-hardware:power-supply"},
91 {"/ietf-hardware:hardware/component[name='ne:psu']/parent", "ne"},
Tomáš Peckac0991ce2023-12-20 15:46:03 +010092 {"/ietf-hardware:hardware/component[name='ne:psu']/state/oper-state", "disabled"}};
93
94 std::set<std::string> expectedThresholdsKeys;
Tomáš Pecka23536bf2023-04-27 18:30:55 +020095
Tomáš Pecka3649ae12023-04-14 10:40:56 +020096 for (auto i : {0, 1, 2, 3, 4}) {
97 std::this_thread::sleep_for(std::chrono::seconds(4));
98 velia::ietf_hardware::DataTree expected;
99
100 switch (i) {
101 case 0:
Tomáš Pecka23536bf2023-04-27 18:30:55 +0200102 expected = expectedDisabled;
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100103 expectedThresholdsKeys.clear();
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200104 break;
105 case 1:
106 expected = {
107 {"/ietf-hardware:hardware/component[name='ne:psu']/class", "iana-hardware:power-supply"},
108 {"/ietf-hardware:hardware/component[name='ne:psu']/parent", "ne"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200109 {"/ietf-hardware:hardware/component[name='ne:psu']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200110 {"/ietf-hardware:hardware/component[name='ne:psu:current-12V']/class", "iana-hardware:sensor"},
111 {"/ietf-hardware:hardware/component[name='ne:psu:current-12V']/parent", "ne:psu"},
112 {"/ietf-hardware:hardware/component[name='ne:psu:current-12V']/sensor-data/oper-status", "ok"},
113 {"/ietf-hardware:hardware/component[name='ne:psu:current-12V']/sensor-data/value", "0"},
114 {"/ietf-hardware:hardware/component[name='ne:psu:current-12V']/sensor-data/value-precision", "0"},
115 {"/ietf-hardware:hardware/component[name='ne:psu:current-12V']/sensor-data/value-scale", "milli"},
116 {"/ietf-hardware:hardware/component[name='ne:psu:current-12V']/sensor-data/value-type", "amperes"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200117 {"/ietf-hardware:hardware/component[name='ne:psu:current-12V']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200118 {"/ietf-hardware:hardware/component[name='ne:psu:current-5Vsb']/class", "iana-hardware:sensor"},
119 {"/ietf-hardware:hardware/component[name='ne:psu:current-5Vsb']/parent", "ne:psu"},
120 {"/ietf-hardware:hardware/component[name='ne:psu:current-5Vsb']/sensor-data/oper-status", "ok"},
121 {"/ietf-hardware:hardware/component[name='ne:psu:current-5Vsb']/sensor-data/value", "0"},
122 {"/ietf-hardware:hardware/component[name='ne:psu:current-5Vsb']/sensor-data/value-precision", "0"},
123 {"/ietf-hardware:hardware/component[name='ne:psu:current-5Vsb']/sensor-data/value-scale", "milli"},
124 {"/ietf-hardware:hardware/component[name='ne:psu:current-5Vsb']/sensor-data/value-type", "amperes"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200125 {"/ietf-hardware:hardware/component[name='ne:psu:current-5Vsb']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200126 {"/ietf-hardware:hardware/component[name='ne:psu:current-in']/class", "iana-hardware:sensor"},
127 {"/ietf-hardware:hardware/component[name='ne:psu:current-in']/parent", "ne:psu"},
128 {"/ietf-hardware:hardware/component[name='ne:psu:current-in']/sensor-data/oper-status", "ok"},
129 {"/ietf-hardware:hardware/component[name='ne:psu:current-in']/sensor-data/value", "0"},
130 {"/ietf-hardware:hardware/component[name='ne:psu:current-in']/sensor-data/value-precision", "0"},
131 {"/ietf-hardware:hardware/component[name='ne:psu:current-in']/sensor-data/value-scale", "milli"},
132 {"/ietf-hardware:hardware/component[name='ne:psu:current-in']/sensor-data/value-type", "amperes"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200133 {"/ietf-hardware:hardware/component[name='ne:psu:current-in']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200134 {"/ietf-hardware:hardware/component[name='ne:psu:fan']/class", "iana-hardware:module"},
135 {"/ietf-hardware:hardware/component[name='ne:psu:fan']/parent", "ne:psu"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200136 {"/ietf-hardware:hardware/component[name='ne:psu:fan']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200137 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1']/class", "iana-hardware:fan"},
138 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1']/parent", "ne:psu:fan"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200139 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200140 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1:rpm']/class", "iana-hardware:sensor"},
141 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1:rpm']/parent", "ne:psu:fan:fan1"},
142 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1:rpm']/sensor-data/oper-status", "ok"},
143 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1:rpm']/sensor-data/value", "0"},
144 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1:rpm']/sensor-data/value-precision", "0"},
145 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1:rpm']/sensor-data/value-scale", "units"},
146 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1:rpm']/sensor-data/value-type", "rpm"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200147 {"/ietf-hardware:hardware/component[name='ne:psu:fan:fan1:rpm']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200148 {"/ietf-hardware:hardware/component[name='ne:psu:power-in']/class", "iana-hardware:sensor"},
149 {"/ietf-hardware:hardware/component[name='ne:psu:power-in']/parent", "ne:psu"},
150 {"/ietf-hardware:hardware/component[name='ne:psu:power-in']/sensor-data/oper-status", "ok"},
151 {"/ietf-hardware:hardware/component[name='ne:psu:power-in']/sensor-data/value", "0"},
152 {"/ietf-hardware:hardware/component[name='ne:psu:power-in']/sensor-data/value-precision", "0"},
153 {"/ietf-hardware:hardware/component[name='ne:psu:power-in']/sensor-data/value-scale", "micro"},
154 {"/ietf-hardware:hardware/component[name='ne:psu:power-in']/sensor-data/value-type", "watts"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200155 {"/ietf-hardware:hardware/component[name='ne:psu:power-in']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200156 {"/ietf-hardware:hardware/component[name='ne:psu:power-out']/class", "iana-hardware:sensor"},
157 {"/ietf-hardware:hardware/component[name='ne:psu:power-out']/parent", "ne:psu"},
158 {"/ietf-hardware:hardware/component[name='ne:psu:power-out']/sensor-data/oper-status", "ok"},
159 {"/ietf-hardware:hardware/component[name='ne:psu:power-out']/sensor-data/value", "0"},
160 {"/ietf-hardware:hardware/component[name='ne:psu:power-out']/sensor-data/value-precision", "0"},
161 {"/ietf-hardware:hardware/component[name='ne:psu:power-out']/sensor-data/value-scale", "micro"},
162 {"/ietf-hardware:hardware/component[name='ne:psu:power-out']/sensor-data/value-type", "watts"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200163 {"/ietf-hardware:hardware/component[name='ne:psu:power-out']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200164 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-1']/class", "iana-hardware:sensor"},
165 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-1']/parent", "ne:psu"},
166 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-1']/sensor-data/oper-status", "ok"},
167 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-1']/sensor-data/value", "0"},
168 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-1']/sensor-data/value-precision", "0"},
169 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-1']/sensor-data/value-scale", "milli"},
170 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-1']/sensor-data/value-type", "celsius"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200171 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-1']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200172 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-2']/class", "iana-hardware:sensor"},
173 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-2']/parent", "ne:psu"},
174 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-2']/sensor-data/oper-status", "ok"},
175 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-2']/sensor-data/value", "0"},
176 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-2']/sensor-data/value-precision", "0"},
177 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-2']/sensor-data/value-scale", "milli"},
178 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-2']/sensor-data/value-type", "celsius"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200179 {"/ietf-hardware:hardware/component[name='ne:psu:temperature-2']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200180 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-12V']/class", "iana-hardware:sensor"},
181 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-12V']/parent", "ne:psu"},
182 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-12V']/sensor-data/oper-status", "ok"},
183 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-12V']/sensor-data/value", "0"},
184 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-12V']/sensor-data/value-precision", "0"},
185 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-12V']/sensor-data/value-scale", "milli"},
186 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-12V']/sensor-data/value-type", "volts-DC"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200187 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-12V']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200188 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-5Vsb']/class", "iana-hardware:sensor"},
189 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-5Vsb']/parent", "ne:psu"},
190 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-5Vsb']/sensor-data/oper-status", "ok"},
191 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-5Vsb']/sensor-data/value", "0"},
192 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-5Vsb']/sensor-data/value-precision", "0"},
193 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-5Vsb']/sensor-data/value-scale", "milli"},
194 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-5Vsb']/sensor-data/value-type", "volts-DC"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200195 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-5Vsb']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200196 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-in']/class", "iana-hardware:sensor"},
197 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-in']/parent", "ne:psu"},
198 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-in']/sensor-data/oper-status", "ok"},
199 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-in']/sensor-data/value", "0"},
200 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-in']/sensor-data/value-precision", "0"},
201 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-in']/sensor-data/value-scale", "milli"},
202 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-in']/sensor-data/value-type", "volts-AC"},
Tomáš Pecka7eb0c422023-04-21 15:36:33 +0200203 {"/ietf-hardware:hardware/component[name='ne:psu:voltage-in']/state/oper-state", "enabled"},
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200204 };
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100205 expectedThresholdsKeys = {
206 "/ietf-hardware:hardware/component[name='ne:psu:current-12V']/sensor-data/value",
207 "/ietf-hardware:hardware/component[name='ne:psu:current-5Vsb']/sensor-data/value",
208 "/ietf-hardware:hardware/component[name='ne:psu:current-in']/sensor-data/value",
209 "/ietf-hardware:hardware/component[name='ne:psu:fan:fan1:rpm']/sensor-data/value",
210 "/ietf-hardware:hardware/component[name='ne:psu:power-in']/sensor-data/value",
211 "/ietf-hardware:hardware/component[name='ne:psu:power-out']/sensor-data/value",
212 "/ietf-hardware:hardware/component[name='ne:psu:temperature-1']/sensor-data/value",
213 "/ietf-hardware:hardware/component[name='ne:psu:temperature-2']/sensor-data/value",
214 "/ietf-hardware:hardware/component[name='ne:psu:voltage-12V']/sensor-data/value",
215 "/ietf-hardware:hardware/component[name='ne:psu:voltage-5Vsb']/sensor-data/value",
216 "/ietf-hardware:hardware/component[name='ne:psu:voltage-in']/sensor-data/value",
217 };
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200218 break;
219 case 2:
Tomáš Pecka23536bf2023-04-27 18:30:55 +0200220 expected = expectedDisabled;
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100221 expectedThresholdsKeys.clear();
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200222 break;
223 case 3:
224 // Here I simulate read failure by a file from the hwmon directory. This happens when the user wants data from
225 // a PSU that's no longer there and the watcher thread didn't unbind it yet.
226 fakeI2c->removeHwmonFile("temp1_input");
Tomáš Pecka23536bf2023-04-27 18:30:55 +0200227 expected = expectedDisabled;
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100228 expectedThresholdsKeys.clear();
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200229 break;
230 case 4:
Tomáš Pecka23536bf2023-04-27 18:30:55 +0200231 expected = expectedDisabled;
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100232 expectedThresholdsKeys.clear();
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200233 break;
234 }
235
Tomáš Peckac0991ce2023-12-20 15:46:03 +0100236 auto res = psu->readValues();
237
238 CAPTURE((int)counter);
239 REQUIRE(res.data == expected);
240
241 std::set<std::string> thresholdsKeys;
242 std::transform(res.thresholds.begin(), res.thresholds.end(), std::inserter(thresholdsKeys, thresholdsKeys.begin()), [](const auto& kv) { return kv.first; });
243 REQUIRE(thresholdsKeys == expectedThresholdsKeys);
Tomáš Pecka3649ae12023-04-14 10:40:56 +0200244
245 counter++;
246 }
247
248 waitForCompletionAndBitMore(seq1);
249}