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::experimental::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 
52     virtual ~MockLed()
53     {
54         chmod(root.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
55         fs::remove_all(root);
56     }
57 
58     MOCK_METHOD0(getBrightness, unsigned long());
59     MOCK_METHOD1(setBrightness, void(unsigned long value));
60     MOCK_METHOD0(getMaxBrightness, unsigned long());
61     MOCK_METHOD0(getTrigger, std::string());
62     MOCK_METHOD1(setTrigger, void(const std::string& trigger));
63     MOCK_METHOD0(getDelayOn, unsigned long());
64     MOCK_METHOD1(setDelayOn, void(unsigned long ms));
65     MOCK_METHOD0(getDelayOff, unsigned long());
66     MOCK_METHOD1(setDelayOff, void(unsigned long ms));
67 };
68 
69 using ::testing::InSequence;
70 using ::testing::NiceMock;
71 using ::testing::Return;
72 using ::testing::Throw;
73 
74 TEST(Physical, ctor_none_trigger)
75 {
76     sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
77     /* NiceMock ignores calls to methods with no expectations defined */
78     NiceMock<MockLed> led;
79     ON_CALL(led, getTrigger()).WillByDefault(Return("none"));
80     phosphor::led::Physical phy(bus, LED_OBJ, led);
81 }
82 
83 TEST(Physical, ctor_timer_trigger)
84 {
85     sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
86     NiceMock<MockLed> led;
87     EXPECT_CALL(led, getTrigger()).WillOnce(Return("timer"));
88     EXPECT_CALL(led, getDelayOn()).WillOnce(Return(500));
89     EXPECT_CALL(led, getDelayOff()).WillOnce(Return(500));
90     phosphor::led::Physical phy(bus, LED_OBJ, led);
91 }
92 
93 TEST(Physical, off)
94 {
95     sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
96     NiceMock<MockLed> led;
97     ON_CALL(led, getTrigger()).WillByDefault(Return("none"));
98     phosphor::led::Physical phy(bus, LED_OBJ, led);
99     phy.state(Action::Off);
100 }
101 
102 TEST(Physical, on)
103 {
104     sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
105     NiceMock<MockLed> led;
106     ON_CALL(led, getTrigger()).WillByDefault(Return("none"));
107     phosphor::led::Physical phy(bus, LED_OBJ, led);
108     phy.state(Action::On);
109 }
110 
111 TEST(Physical, blink)
112 {
113     sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
114     NiceMock<MockLed> led;
115     EXPECT_CALL(led, getTrigger()).WillOnce(Return("none"));
116     EXPECT_CALL(led, setTrigger("timer"));
117     EXPECT_CALL(led, setDelayOn(500));
118     EXPECT_CALL(led, setDelayOff(500));
119     phosphor::led::Physical phy(bus, LED_OBJ, led);
120     phy.state(Action::Blink);
121 }
122 
123 TEST(Physical, ctor_none_trigger_asserted_brightness)
124 {
125     sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
126     NiceMock<MockLed> led;
127     EXPECT_CALL(led, getTrigger()).WillRepeatedly(Return("none"));
128     constexpr auto val = phosphor::led::ASSERT;
129     EXPECT_CALL(led, getBrightness()).WillRepeatedly(Return(val));
130     phosphor::led::Physical phy(bus, LED_OBJ, led);
131 }
132 
133 TEST(Physical, on_to_off)
134 {
135     InSequence s;
136 
137     auto bus = sdbusplus::bus::new_default();
138     NiceMock<MockLed> led;
139     EXPECT_CALL(led, getTrigger()).Times(1).WillOnce(Return("none"));
140     constexpr auto deasserted = phosphor::led::DEASSERT;
141     EXPECT_CALL(led, getBrightness()).WillOnce(Return(deasserted));
142     constexpr auto asserted = phosphor::led::ASSERT;
143     EXPECT_CALL(led, setBrightness(asserted));
144     EXPECT_CALL(led, setBrightness(deasserted));
145     phosphor::led::Physical phy(bus, LED_OBJ, led);
146     phy.state(Action::On);
147     phy.state(Action::Off);
148 }
149