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