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