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