1 #include "physical.hpp"
2 
3 #include <sys/param.h>
4 
5 #include <sdbusplus/bus.hpp>
6 
7 #include <gmock/gmock.h>
8 #include <gtest/gtest.h>
9 
10 constexpr auto ledObj = "/foo/bar/led";
11 
12 using Action = sdbusplus::xyz::openbmc_project::Led::server::Physical::Action;
13 namespace fs = std::filesystem;
14 
15 fs::path createSandbox()
16 {
17     /* If your tests need to touch the filesystem, always use mkdtemp() or
18      * mkstemp() for creating directories and files. Tests can be run in
19      * parallel with `make -j`, and if use the same path in multiple tests they
20      * will stomp on eachother and likely fail.
21      */
22     static constexpr auto tmplt = "/tmp/MockLed.XXXXXX";
23     std::array<char, MAXPATHLEN> buffer = {0};
24 
25     strncpy(buffer.data(), tmplt, buffer.size() - 1);
26     auto* dir = mkdtemp(buffer.data());
27     if (dir == nullptr)
28     {
29         throw std::system_error(errno, std::system_category());
30     }
31 
32     /* We want to limit behaviours to mocks, and if methods aren't mocked they
33      * may fall back to their base class implementation. Stop read/write to
34      * directory to prevent streams from creating files.
35      */
36     if (chmod(dir, S_IXUSR | S_IXGRP) == -1)
37     {
38         throw std::system_error(errno, std::system_category());
39     }
40 
41     return dir;
42 }
43 
44 class MockLed : public phosphor::led::SysfsLed
45 {
46   public:
47     /* Use a no-args ctor here to avoid headaches with {Nice,Strict}Mock */
48     MockLed() : SysfsLed(createSandbox())
49     {}
50     MockLed(MockLed& other) = delete;
51     MockLed(MockLed&& other) = delete;
52     MockLed& operator=(MockLed& other) = delete;
53     MockLed&& operator=(MockLed&& other) = delete;
54 
55     ~MockLed() override
56     {
57         chmod(root.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
58         fs::remove_all(root);
59     }
60 
61     MOCK_METHOD0(getBrightness, unsigned long());
62     MOCK_METHOD1(setBrightness, void(unsigned long value));
63     MOCK_METHOD0(getMaxBrightness, unsigned long());
64     MOCK_METHOD0(getTrigger, std::string());
65     MOCK_METHOD1(setTrigger, void(const std::string& trigger));
66     MOCK_METHOD0(getDelayOn, unsigned long());
67     MOCK_METHOD1(setDelayOn, void(unsigned long ms));
68     MOCK_METHOD0(getDelayOff, unsigned long());
69     MOCK_METHOD1(setDelayOff, void(unsigned long ms));
70 };
71 
72 using ::testing::InSequence;
73 using ::testing::NiceMock;
74 using ::testing::Return;
75 
76 TEST(Physical, ctor_none_trigger)
77 {
78     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
79     /* NiceMock ignores calls to methods with no expectations defined */
80     NiceMock<MockLed> led;
81     ON_CALL(led, getTrigger()).WillByDefault(Return("none"));
82     phosphor::led::Physical phy(bus, ledObj, led);
83     EXPECT_EQ(phy.state(), Action::Off);
84 }
85 
86 TEST(Physical, ctor_maxbrightness_and_brightness_read_127)
87 {
88     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
89     /* NiceMock ignores calls to methods with no expectations defined */
90     NiceMock<MockLed> led;
91     EXPECT_CALL(led, getTrigger()).WillRepeatedly(Return("none"));
92     EXPECT_CALL(led, getBrightness()).WillOnce(Return(127));
93     EXPECT_CALL(led, getMaxBrightness()).WillOnce(Return(127));
94     phosphor::led::Physical phy(bus, ledObj, led);
95     EXPECT_EQ(phy.state(), Action::On);
96 }
97 
98 TEST(Physical, ctor_maxbrightness_and_brightness_read_0)
99 {
100     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
101     /* NiceMock ignores calls to methods with no expectations defined */
102     NiceMock<MockLed> led;
103     EXPECT_CALL(led, getTrigger()).WillRepeatedly(Return("none"));
104     EXPECT_CALL(led, getBrightness()).WillOnce(Return(0));
105     EXPECT_CALL(led, getMaxBrightness()).WillOnce(Return(0));
106     phosphor::led::Physical phy(bus, ledObj, led);
107     EXPECT_EQ(phy.state(), Action::Off);
108 }
109 
110 TEST(Physical, ctor_only_maxbrightness_read_127)
111 {
112     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
113     /* NiceMock ignores calls to methods with no expectations defined */
114     NiceMock<MockLed> led;
115     EXPECT_CALL(led, getTrigger()).WillRepeatedly(Return("none"));
116     EXPECT_CALL(led, getBrightness()).WillOnce(Return(0));
117     EXPECT_CALL(led, getMaxBrightness()).WillOnce(Return(127));
118     phosphor::led::Physical phy(bus, ledObj, led);
119     EXPECT_EQ(phy.state(), Action::Off);
120 }
121 
122 TEST(Physical, ctor_only_brightness_read_127)
123 {
124     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
125     /* NiceMock ignores calls to methods with no expectations defined */
126     NiceMock<MockLed> led;
127     EXPECT_CALL(led, getTrigger()).WillRepeatedly(Return("none"));
128     EXPECT_CALL(led, getBrightness()).WillOnce(Return(127));
129     EXPECT_CALL(led, getMaxBrightness()).WillOnce(Return(0));
130     phosphor::led::Physical phy(bus, ledObj, led);
131     EXPECT_EQ(phy.state(), Action::Off);
132 }
133 
134 TEST(Physical, ctor_timer_trigger)
135 {
136     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
137     NiceMock<MockLed> led;
138     EXPECT_CALL(led, getTrigger()).WillOnce(Return("timer"));
139     EXPECT_CALL(led, getDelayOn()).WillOnce(Return(500));
140     EXPECT_CALL(led, getDelayOff()).WillOnce(Return(500));
141     phosphor::led::Physical phy(bus, ledObj, led);
142     EXPECT_EQ(phy.state(), Action::Off);
143 }
144 
145 TEST(Physical, off)
146 {
147     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
148     NiceMock<MockLed> led;
149     ON_CALL(led, getMaxBrightness()).WillByDefault(Return(127));
150     EXPECT_CALL(led, getTrigger()).WillOnce(Return("none"));
151     EXPECT_CALL(led, getBrightness())
152         .WillOnce(Return(phosphor::led::deasserted));
153     EXPECT_CALL(led, setBrightness(phosphor::led::deasserted)).Times(0);
154     phosphor::led::Physical phy(bus, ledObj, led);
155     phy.state(Action::Off);
156     EXPECT_EQ(phy.state(), Action::Off);
157 }
158 
159 TEST(Physical, on)
160 {
161     constexpr unsigned long asserted = 127;
162 
163     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
164     NiceMock<MockLed> led;
165     ON_CALL(led, getMaxBrightness()).WillByDefault(Return(asserted));
166     EXPECT_CALL(led, getTrigger()).WillOnce(Return("none"));
167     EXPECT_CALL(led, setTrigger("none"));
168     EXPECT_CALL(led, setBrightness(asserted));
169     phosphor::led::Physical phy(bus, ledObj, led);
170     phy.state(Action::On);
171     EXPECT_EQ(phy.state(), Action::On);
172 }
173 
174 TEST(Physical, blink)
175 {
176     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
177     NiceMock<MockLed> led;
178     EXPECT_CALL(led, getTrigger()).WillOnce(Return("none"));
179     EXPECT_CALL(led, setTrigger("timer"));
180     EXPECT_CALL(led, setDelayOn(500));
181     EXPECT_CALL(led, setDelayOff(500));
182     phosphor::led::Physical phy(bus, ledObj, led);
183     phy.state(Action::Blink);
184     EXPECT_EQ(phy.state(), Action::Blink);
185 }
186 
187 TEST(Physical, ctor_none_trigger_asserted_brightness)
188 {
189     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
190     NiceMock<MockLed> led;
191     EXPECT_CALL(led, getTrigger()).WillRepeatedly(Return("none"));
192     EXPECT_CALL(led, getBrightness()).WillRepeatedly(Return(127));
193     phosphor::led::Physical phy(bus, ledObj, led);
194     EXPECT_EQ(phy.state(), Action::Off);
195 }
196 
197 TEST(Physical, on_to_off)
198 {
199     InSequence s;
200     constexpr unsigned long asserted = 127;
201 
202     auto bus = sdbusplus::bus::new_default();
203     NiceMock<MockLed> led;
204     ON_CALL(led, getMaxBrightness()).WillByDefault(Return(asserted));
205     EXPECT_CALL(led, getTrigger()).Times(1).WillOnce(Return("none"));
206     constexpr auto deasserted = phosphor::led::deasserted;
207     EXPECT_CALL(led, getBrightness()).WillOnce(Return(deasserted));
208     EXPECT_CALL(led, setBrightness(asserted));
209     EXPECT_CALL(led, setBrightness(deasserted));
210     phosphor::led::Physical phy(bus, ledObj, led);
211     phy.state(Action::On);
212     EXPECT_EQ(phy.state(), Action::On);
213     phy.state(Action::Off);
214     EXPECT_EQ(phy.state(), Action::Off);
215 }
216