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 each other 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     MockLed(MockLed& other) = delete;
50     MockLed(MockLed&& other) = delete;
51     MockLed& operator=(MockLed& other) = delete;
52     MockLed&& operator=(MockLed&& other) = delete;
53 
54     ~MockLed() override
55     {
56         chmod(root.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
57         fs::remove_all(root);
58     }
59 
60     MOCK_METHOD0(getBrightness, unsigned long());
61     MOCK_METHOD1(setBrightness, void(unsigned long value));
62     MOCK_METHOD0(getMaxBrightness, unsigned long());
63     MOCK_METHOD0(getTrigger, std::string());
64     MOCK_METHOD1(setTrigger, void(const std::string& trigger));
65     MOCK_METHOD0(getDelayOn, unsigned long());
66     MOCK_METHOD1(setDelayOn, void(unsigned long ms));
67     MOCK_METHOD0(getDelayOff, unsigned long());
68     MOCK_METHOD1(setDelayOff, void(unsigned long ms));
69 };
70 
71 using ::testing::InSequence;
72 using ::testing::NiceMock;
73 using ::testing::Return;
74 
75 TEST(Physical, ctor_none_trigger)
76 {
77     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
78     /* NiceMock ignores calls to methods with no expectations defined */
79     auto led = std::make_unique<NiceMock<MockLed>>();
80     ON_CALL(*led, getTrigger()).WillByDefault(Return("none"));
81     phosphor::led::Physical phy(bus, ledObj, std::move(led));
82     EXPECT_EQ(phy.state(), Action::Off);
83 }
84 
85 TEST(Physical, ctor_maxbrightness_and_brightness_read_127)
86 {
87     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
88     /* NiceMock ignores calls to methods with no expectations defined */
89     auto led = std::make_unique<NiceMock<MockLed>>();
90     EXPECT_CALL(*led, getTrigger()).WillRepeatedly(Return("none"));
91     EXPECT_CALL(*led, getBrightness()).WillOnce(Return(127));
92     EXPECT_CALL(*led, getMaxBrightness()).WillOnce(Return(127));
93     phosphor::led::Physical phy(bus, ledObj, std::move(led));
94     EXPECT_EQ(phy.state(), Action::On);
95 }
96 
97 TEST(Physical, ctor_maxbrightness_and_brightness_read_0)
98 {
99     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
100     /* NiceMock ignores calls to methods with no expectations defined */
101     auto led = std::make_unique<NiceMock<MockLed>>();
102     EXPECT_CALL(*led, getTrigger()).WillRepeatedly(Return("none"));
103     EXPECT_CALL(*led, getBrightness()).WillOnce(Return(0));
104     EXPECT_CALL(*led, getMaxBrightness()).WillOnce(Return(0));
105     phosphor::led::Physical phy(bus, ledObj, std::move(led));
106     EXPECT_EQ(phy.state(), Action::Off);
107 }
108 
109 TEST(Physical, ctor_only_maxbrightness_read_127)
110 {
111     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
112     /* NiceMock ignores calls to methods with no expectations defined */
113     auto led = std::make_unique<NiceMock<MockLed>>();
114     EXPECT_CALL(*led, getTrigger()).WillRepeatedly(Return("none"));
115     EXPECT_CALL(*led, getBrightness()).WillOnce(Return(0));
116     EXPECT_CALL(*led, getMaxBrightness()).WillOnce(Return(127));
117     phosphor::led::Physical phy(bus, ledObj, std::move(led));
118     EXPECT_EQ(phy.state(), Action::Off);
119 }
120 
121 TEST(Physical, ctor_only_brightness_read_127)
122 {
123     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
124     /* NiceMock ignores calls to methods with no expectations defined */
125     auto led = std::make_unique<NiceMock<MockLed>>();
126     EXPECT_CALL(*led, getTrigger()).WillRepeatedly(Return("none"));
127     EXPECT_CALL(*led, getBrightness()).WillOnce(Return(127));
128     EXPECT_CALL(*led, getMaxBrightness()).WillOnce(Return(0));
129     phosphor::led::Physical phy(bus, ledObj, std::move(led));
130     EXPECT_EQ(phy.state(), Action::Off);
131 }
132 
133 TEST(Physical, ctor_timer_trigger)
134 {
135     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
136     auto led = std::make_unique<NiceMock<MockLed>>();
137     EXPECT_CALL(*led, getTrigger()).WillOnce(Return("timer"));
138     EXPECT_CALL(*led, getDelayOn()).WillOnce(Return(500));
139     EXPECT_CALL(*led, getDelayOff()).WillOnce(Return(500));
140     phosphor::led::Physical phy(bus, ledObj, std::move(led));
141     EXPECT_EQ(phy.state(), Action::Blink);
142 }
143 
144 TEST(Physical, off)
145 {
146     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
147     auto led = std::make_unique<NiceMock<MockLed>>();
148     ON_CALL(*led, getMaxBrightness()).WillByDefault(Return(127));
149     EXPECT_CALL(*led, getTrigger()).WillOnce(Return("none"));
150     EXPECT_CALL(*led, getBrightness())
151         .WillOnce(Return(phosphor::led::deasserted));
152     EXPECT_CALL(*led, setBrightness(phosphor::led::deasserted)).Times(0);
153     phosphor::led::Physical phy(bus, ledObj, std::move(led));
154     phy.state(Action::Off);
155     EXPECT_EQ(phy.state(), Action::Off);
156 }
157 
158 TEST(Physical, on)
159 {
160     constexpr unsigned long asserted = 127;
161 
162     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
163     auto led = std::make_unique<NiceMock<MockLed>>();
164     ON_CALL(*led, getMaxBrightness()).WillByDefault(Return(asserted));
165     EXPECT_CALL(*led, getTrigger()).WillOnce(Return("none"));
166     EXPECT_CALL(*led, setTrigger("none"));
167     EXPECT_CALL(*led, setBrightness(asserted));
168     phosphor::led::Physical phy(bus, ledObj, std::move(led));
169     phy.state(Action::On);
170     EXPECT_EQ(phy.state(), Action::On);
171 }
172 
173 TEST(Physical, blink)
174 {
175     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
176     auto led = std::make_unique<NiceMock<MockLed>>();
177     EXPECT_CALL(*led, getTrigger()).WillOnce(Return("none"));
178     EXPECT_CALL(*led, setTrigger("timer"));
179     EXPECT_CALL(*led, setDelayOn(500));
180     EXPECT_CALL(*led, setDelayOff(500));
181     phosphor::led::Physical phy(bus, ledObj, std::move(led));
182     phy.state(Action::Blink);
183     EXPECT_EQ(phy.state(), Action::Blink);
184 }
185 
186 TEST(Physical, ctor_none_trigger_asserted_brightness)
187 {
188     sdbusplus::bus_t bus = sdbusplus::bus::new_default();
189     auto led = std::make_unique<NiceMock<MockLed>>();
190     EXPECT_CALL(*led, getTrigger()).WillRepeatedly(Return("none"));
191     EXPECT_CALL(*led, getBrightness()).WillRepeatedly(Return(127));
192     phosphor::led::Physical phy(bus, ledObj, std::move(led));
193     EXPECT_EQ(phy.state(), Action::Off);
194 }
195 
196 TEST(Physical, on_to_off)
197 {
198     InSequence s;
199     constexpr unsigned long asserted = 127;
200 
201     auto bus = sdbusplus::bus::new_default();
202     auto led = std::make_unique<NiceMock<MockLed>>();
203     ON_CALL(*led, getMaxBrightness()).WillByDefault(Return(asserted));
204     EXPECT_CALL(*led, getTrigger()).Times(1).WillOnce(Return("none"));
205     EXPECT_CALL(*led, getBrightness())
206         .WillOnce(Return(phosphor::led::deasserted));
207     EXPECT_CALL(*led, setBrightness(asserted));
208     EXPECT_CALL(*led, setBrightness(phosphor::led::deasserted));
209     phosphor::led::Physical phy(bus, ledObj, std::move(led));
210     phy.state(Action::On);
211     EXPECT_EQ(phy.state(), Action::On);
212     phy.state(Action::Off);
213     EXPECT_EQ(phy.state(), Action::Off);
214 }
215