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