1 #include <systemd/sd-bus.h>
2 
3 #include <sdbusplus/exception.hpp>
4 #include <sdbusplus/test/sdbus_mock.hpp>
5 
6 #include <cstdlib>
7 #include <stdexcept>
8 #include <string>
9 #include <utility>
10 
11 #include <gtest/gtest.h>
12 
13 #ifdef __clang__
14 #pragma clang diagnostic push
15 #pragma clang diagnostic ignored "-Wc99-extensions"
16 #endif
17 
18 // Needed for constructor error testing
19 extern sdbusplus::SdBusImpl sdbus_impl;
20 
21 namespace
22 {
23 
24 using sdbusplus::exception::SdBusError;
25 using testing::_;
26 using testing::Return;
27 
28 TEST(SdBusError, BasicErrno)
29 {
30     const int errorVal = EBUSY;
31     const std::string prefix = "BasicErrno";
32 
33     // Build the reference sd_bus_error
34     sd_bus_error error = SD_BUS_ERROR_NULL;
35     EXPECT_EQ(-errorVal, sd_bus_error_set_errno(&error, errorVal));
36     EXPECT_TRUE(sd_bus_error_is_set(&error));
37 
38     // Build the SdBusError
39     SdBusError err(errorVal, prefix.c_str());
40 
41     // Make sure inheritance is defined correctly
42     sdbusplus::exception::exception& sdbusErr = err;
43     SdBusError& errNew = err;
44     EXPECT_EQ(errorVal, errNew.get_errno());
45     EXPECT_EQ(std::string{error.name}, sdbusErr.name());
46     EXPECT_EQ(std::string{error.message}, sdbusErr.description());
47     std::exception& stdErr = sdbusErr;
48     EXPECT_EQ(prefix + ": " + error.name + ": " + error.message, stdErr.what());
49 
50     sd_bus_error_free(&error);
51 }
52 
53 TEST(SdBusError, EnomemErrno)
54 {
55     // Make sure no exception is thrown on construction
56     SdBusError err(ENOMEM, "EnomemErrno");
57 }
58 
59 TEST(SdBusError, NotSetErrno)
60 {
61     const int errorVal = EBUSY;
62 
63     sdbusplus::SdBusMock sdbus;
64     EXPECT_CALL(sdbus, sd_bus_error_set_errno(_, errorVal))
65         .Times(1)
66         .WillOnce(Return(errorVal));
67     EXPECT_CALL(sdbus, sd_bus_error_is_set(_)).Times(1).WillOnce(Return(false));
68     EXPECT_THROW(SdBusError(errorVal, "NotSetErrno", &sdbus),
69                  std::runtime_error);
70 }
71 
72 TEST(SdBusError, Move)
73 {
74     const int errorVal = EIO;
75     const std::string prefix = "Move";
76 
77     // Build the reference sd_bus_error
78     sd_bus_error error = SD_BUS_ERROR_NULL;
79     EXPECT_EQ(-errorVal, sd_bus_error_set_errno(&error, errorVal));
80     EXPECT_TRUE(sd_bus_error_is_set(&error));
81     const std::string name{error.name};
82     const std::string message{error.message};
83     const std::string what = prefix + ": " + error.name + ": " + error.message;
84 
85     SdBusError errFinal(EBUSY, "Move2");
86     // Nest to make sure RAII works for moves
87     {
88         // Build our first SdBusError
89         SdBusError err(errorVal, prefix.c_str());
90 
91         EXPECT_EQ(errorVal, err.get_errno());
92         EXPECT_EQ(name, err.name());
93         EXPECT_EQ(message, err.description());
94         EXPECT_EQ(what, err.what());
95 
96         // Move our SdBusError to a new one
97         SdBusError errNew(std::move(err));
98 
99         // We are purposefully calling functions on a moved-from object,
100         // so we need to suppress the clang warnings.
101 #ifndef __clang_analyzer__
102         // Ensure the old object was cleaned up
103         EXPECT_EQ(0, err.get_errno());
104         EXPECT_EQ(nullptr, err.name());
105         EXPECT_EQ(nullptr, err.description());
106 #endif
107 
108         // Ensure our new object has the same data but moved
109         EXPECT_EQ(errorVal, errNew.get_errno());
110         EXPECT_EQ(name, errNew.name());
111         EXPECT_EQ(message, errNew.description());
112         EXPECT_EQ(what, errNew.what());
113 
114         // Move our SdBusError using the operator=()
115         errFinal = std::move(errNew);
116 
117         // We are purposefully calling functions on a moved-from object,
118         // so we need to suppress the clang warnings.
119 #ifndef __clang_analyzer__
120         // Ensure the old object was cleaned up
121         EXPECT_EQ(0, errNew.get_errno());
122         EXPECT_EQ(nullptr, errNew.name());
123         EXPECT_EQ(nullptr, errNew.description());
124 #endif
125     }
126 
127     // Ensure our new object has the same data but moved
128     EXPECT_EQ(errorVal, errFinal.get_errno());
129     EXPECT_EQ(name, errFinal.name());
130     EXPECT_EQ(message, errFinal.description());
131     EXPECT_EQ(what, errFinal.what());
132 
133     sd_bus_error_free(&error);
134 }
135 
136 TEST(SdBusError, BasicError)
137 {
138     const std::string name = "org.freedesktop.DBus.Error.Failed";
139     const std::string description = "TestCase";
140     const std::string prefix = "BasicError";
141 
142     sd_bus_error error = SD_BUS_ERROR_NULL;
143     sd_bus_error_set(&error, name.c_str(), description.c_str());
144     EXPECT_TRUE(sd_bus_error_is_set(&error));
145     const char* nameBeforeMove = error.name;
146     const int errorVal = sd_bus_error_get_errno(&error);
147     SdBusError err(&error, prefix.c_str());
148 
149     // We expect a move not copy
150     EXPECT_EQ(nameBeforeMove, err.name());
151 
152     // The SdBusError should have moved our error so it should be freeable
153     EXPECT_FALSE(sd_bus_error_is_set(&error));
154     sd_bus_error_free(&error);
155     sd_bus_error_free(&error);
156 
157     EXPECT_EQ(errorVal, err.get_errno());
158     EXPECT_EQ(name, err.name());
159     EXPECT_EQ(description, err.description());
160     EXPECT_EQ(prefix + ": " + name + ": " + description, err.what());
161 }
162 
163 TEST(SdBusError, CatchBaseClassExceptions)
164 {
165     /* test each class in the chain:
166      * std::exception
167      *   -> sdbusplus::exception::exception
168      *     -> sdbusplus::exception::internal_exception
169      *       -> sdbusplus::exception::SdBusError
170      */
171     EXPECT_THROW({ throw SdBusError(-EINVAL, "SdBusError"); }, SdBusError);
172     EXPECT_THROW(
173         { throw SdBusError(-EINVAL, "internal_exception"); },
174         sdbusplus::exception::internal_exception);
175     EXPECT_THROW(
176         { throw SdBusError(-EINVAL, "exception"); },
177         sdbusplus::exception::exception);
178     EXPECT_THROW(
179         { throw SdBusError(-EINVAL, "std::exception"); }, std::exception);
180 }
181 
182 } // namespace
183 
184 #ifdef __clang__
185 #pragma clang diagnostic pop
186 #endif
187