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