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         // Ensure the old object was cleaned up
95         EXPECT_EQ(0, err.get_errno());
96         EXPECT_EQ(nullptr, err.name());
97         EXPECT_EQ(nullptr, err.description());
98         EXPECT_EQ(std::string{}, err.what());
99 
100         // Ensure our new object has the same data but moved
101         EXPECT_EQ(errorVal, errNew.get_errno());
102         EXPECT_EQ(name, errNew.name());
103         EXPECT_EQ(message, errNew.description());
104         EXPECT_EQ(what, errNew.what());
105 
106         // Move our SdBusError using the operator=()
107         errFinal = std::move(errNew);
108 
109         // Ensure the old object was cleaned up
110         EXPECT_EQ(0, errNew.get_errno());
111         EXPECT_EQ(nullptr, errNew.name());
112         EXPECT_EQ(nullptr, errNew.description());
113         EXPECT_EQ(std::string{}, errNew.what());
114     }
115 
116     // Ensure our new object has the same data but moved
117     EXPECT_EQ(errorVal, errFinal.get_errno());
118     EXPECT_EQ(name, errFinal.name());
119     EXPECT_EQ(message, errFinal.description());
120     EXPECT_EQ(what, errFinal.what());
121 
122     sd_bus_error_free(&error);
123 }
124 
125 TEST(SdBusError, BasicError)
126 {
127     const std::string name = "org.freedesktop.DBus.Error.Failed";
128     const std::string description = "TestCase";
129     const std::string prefix = "BasicError";
130 
131     sd_bus_error error = SD_BUS_ERROR_NULL;
132     sd_bus_error_set(&error, name.c_str(), description.c_str());
133     EXPECT_TRUE(sd_bus_error_is_set(&error));
134     const char* nameBeforeMove = error.name;
135     const int errorVal = sd_bus_error_get_errno(&error);
136     SdBusError err(&error, prefix.c_str());
137 
138     // We expect a move not copy
139     EXPECT_EQ(nameBeforeMove, err.name());
140 
141     // The SdBusError should have moved our error so it should be freeable
142     EXPECT_FALSE(sd_bus_error_is_set(&error));
143     sd_bus_error_free(&error);
144     sd_bus_error_free(&error);
145 
146     EXPECT_EQ(errorVal, err.get_errno());
147     EXPECT_EQ(name, err.name());
148     EXPECT_EQ(description, err.description());
149     EXPECT_EQ(prefix + ": " + name + ": " + description, err.what());
150 }
151 
152 TEST(SdBusError, CatchBaseClassExceptions)
153 {
154     /* test each class in the chain:
155      * std::exception
156      *   -> sdbusplus::exception::exception
157      *     -> sdbusplus::exception::internal_exception
158      *       -> sdbusplus::exception::SdBusError
159      */
160     EXPECT_THROW({ throw SdBusError(-EINVAL, "SdBusError"); }, SdBusError);
161     EXPECT_THROW({ throw SdBusError(-EINVAL, "internal_exception"); },
162                  sdbusplus::exception::internal_exception);
163     EXPECT_THROW({ throw SdBusError(-EINVAL, "exception"); },
164                  sdbusplus::exception::exception);
165     EXPECT_THROW({ throw SdBusError(-EINVAL, "std::exception"); },
166                  std::exception);
167 }
168 
169 } // namespace
170