1 /** 2 * Copyright © 2020 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "file_descriptor.hpp" 17 18 #include <errno.h> // for errno 19 #include <fcntl.h> // for open() and fcntl() 20 #include <sys/stat.h> // for open() 21 #include <sys/types.h> // for open() 22 #include <unistd.h> // for fcntl() 23 24 #include <utility> 25 26 #include <gtest/gtest.h> 27 28 using namespace phosphor::power::util; 29 30 /** 31 * Returns whether the specified file descriptor is valid/open. 32 * 33 * @param[in] fd - File descriptor 34 * @return true if descriptor is valid/open, false otherwise 35 */ 36 bool isValid(int fd) 37 { 38 return (fcntl(fd, F_GETFL) != -1) || (errno != EBADF); 39 } 40 41 /** 42 * Creates an open file descriptor. 43 * 44 * Verifies the file descriptor is valid. 45 * 46 * @return file descriptor 47 */ 48 int createOpenFileDescriptor() 49 { 50 int fd = open("/etc/hosts", O_RDONLY); 51 EXPECT_NE(fd, -1); 52 EXPECT_TRUE(isValid(fd)); 53 return fd; 54 } 55 56 TEST(FileDescriptorTests, DefaultConstructor) 57 { 58 FileDescriptor descriptor; 59 EXPECT_EQ(descriptor(), -1); 60 EXPECT_FALSE(descriptor.operator bool()); 61 } 62 63 TEST(FileDescriptorTests, IntConstructor) 64 { 65 int fd = createOpenFileDescriptor(); 66 FileDescriptor descriptor{fd}; 67 EXPECT_EQ(descriptor(), fd); 68 EXPECT_TRUE(descriptor.operator bool()); 69 EXPECT_TRUE(isValid(fd)); 70 } 71 72 TEST(FileDescriptorTests, MoveConstructor) 73 { 74 // Create first FileDescriptor object with open file descriptor 75 int fd = createOpenFileDescriptor(); 76 FileDescriptor descriptor1{fd}; 77 EXPECT_EQ(descriptor1(), fd); 78 EXPECT_TRUE(isValid(fd)); 79 80 // Create second FileDescriptor object, moving in the contents of the first 81 FileDescriptor descriptor2{std::move(descriptor1)}; 82 83 // Verify descriptor has been moved out of first object 84 EXPECT_EQ(descriptor1(), -1); 85 86 // Verify descriptor has been moved into second object 87 EXPECT_EQ(descriptor2(), fd); 88 89 // Verify descriptor is still valid/open 90 EXPECT_TRUE(isValid(fd)); 91 } 92 93 TEST(FileDescriptorTests, MoveAssignmentOperator) 94 { 95 // Test where move is valid 96 { 97 // Create first FileDescriptor object with open file descriptor 98 int fd1 = createOpenFileDescriptor(); 99 FileDescriptor descriptor1{fd1}; 100 EXPECT_EQ(descriptor1(), fd1); 101 EXPECT_TRUE(isValid(fd1)); 102 103 // Create second FileDescriptor object with open file descriptor 104 int fd2 = createOpenFileDescriptor(); 105 FileDescriptor descriptor2{fd2}; 106 EXPECT_EQ(descriptor2(), fd2); 107 EXPECT_TRUE(isValid(fd2)); 108 109 // Move second FileDescriptor object into the first 110 descriptor1 = std::move(descriptor2); 111 112 // Verify second file descriptor has been moved into first object 113 EXPECT_EQ(descriptor1(), fd2); 114 115 // Verify second file descriptor has been moved out of second object 116 EXPECT_EQ(descriptor2(), -1); 117 118 // Verify first file descriptor has been closed and is no longer valid 119 EXPECT_FALSE(isValid(fd1)); 120 121 // Verify second file descriptor is still valid 122 EXPECT_TRUE(isValid(fd2)); 123 } 124 125 // Test where move is invalid: Attempt to move object into itself 126 { 127 // Create FileDescriptor object with open file descriptor 128 int fd = createOpenFileDescriptor(); 129 FileDescriptor descriptor{fd}; 130 EXPECT_EQ(descriptor(), fd); 131 EXPECT_TRUE(isValid(fd)); 132 133 // This is undefined behavior in C++, but suppress the warning 134 // to observe how the class handles it. 135 #ifdef __clang__ 136 #pragma clang diagnostic push 137 #pragma clang diagnostic ignored "-Wself-move" 138 #endif 139 // Try to move object into itself 140 descriptor = static_cast<FileDescriptor&&>(descriptor); 141 #ifdef __clang__ 142 #pragma clang diagnostic pop 143 #endif 144 145 // Verify object still contains file descriptor 146 EXPECT_EQ(descriptor(), fd); 147 EXPECT_TRUE(isValid(fd)); 148 } 149 } 150 151 TEST(FileDescriptorTests, Destructor) 152 { 153 // Test where file descriptor was never set 154 { 155 FileDescriptor descriptor; 156 EXPECT_EQ(descriptor(), -1); 157 } 158 159 // Test where file descriptor was already closed 160 { 161 // Create FileDescriptor object with open file descriptor. Close the 162 // descriptor explicitly. 163 int fd = createOpenFileDescriptor(); 164 { 165 FileDescriptor descriptor{fd}; 166 EXPECT_EQ(descriptor(), fd); 167 EXPECT_TRUE(isValid(fd)); 168 169 EXPECT_EQ(descriptor.close(), 0); 170 EXPECT_EQ(descriptor(), -1); 171 EXPECT_FALSE(isValid(fd)); 172 } 173 EXPECT_FALSE(isValid(fd)); 174 } 175 176 // Test where file descriptor needs to be closed 177 { 178 // Create FileDescriptor object with open file descriptor. Destructor 179 // will close descriptor. 180 int fd = createOpenFileDescriptor(); 181 { 182 FileDescriptor descriptor{fd}; 183 EXPECT_EQ(descriptor(), fd); 184 EXPECT_TRUE(isValid(fd)); 185 } 186 EXPECT_FALSE(isValid(fd)); 187 } 188 } 189 190 TEST(FileDescriptorTests, FunctionCallOperator) 191 { 192 // Test where FileDescriptor object does not contain a valid file descriptor 193 FileDescriptor descriptor{}; 194 EXPECT_EQ(descriptor(), -1); 195 196 // Test where FileDescriptor object contains a valid file descriptor 197 int fd = createOpenFileDescriptor(); 198 descriptor.set(fd); 199 EXPECT_EQ(descriptor(), fd); 200 } 201 202 TEST(FileDescriptorTests, OperatorBool) 203 { 204 // Test where FileDescriptor object does not contain a valid file descriptor 205 FileDescriptor descriptor{}; 206 EXPECT_FALSE(descriptor.operator bool()); 207 if (descriptor) 208 { 209 ADD_FAILURE() << "Should not have reached this line."; 210 } 211 212 // Test where FileDescriptor object contains a valid file descriptor 213 int fd = createOpenFileDescriptor(); 214 descriptor.set(fd); 215 EXPECT_TRUE(descriptor.operator bool()); 216 if (!descriptor) 217 { 218 ADD_FAILURE() << "Should not have reached this line."; 219 } 220 221 // Test where file descriptor has been closed 222 EXPECT_EQ(descriptor.close(), 0); 223 EXPECT_FALSE(descriptor.operator bool()); 224 if (descriptor) 225 { 226 ADD_FAILURE() << "Should not have reached this line."; 227 } 228 } 229 230 TEST(FileDescriptorTests, Close) 231 { 232 // Test where object contains an open file descriptor 233 int fd = createOpenFileDescriptor(); 234 FileDescriptor descriptor{fd}; 235 EXPECT_EQ(descriptor(), fd); 236 EXPECT_TRUE(isValid(fd)); 237 EXPECT_EQ(descriptor.close(), 0); 238 EXPECT_EQ(descriptor(), -1); 239 EXPECT_FALSE(isValid(fd)); 240 241 // Test where object does not contain an open file descriptor 242 EXPECT_EQ(descriptor(), -1); 243 EXPECT_EQ(descriptor.close(), 0); 244 EXPECT_EQ(descriptor(), -1); 245 246 // Test where close() fails due to invalid file descriptor 247 descriptor.set(999999); 248 EXPECT_EQ(descriptor.close(), -1); 249 EXPECT_EQ(errno, EBADF); 250 EXPECT_EQ(descriptor(), -1); 251 } 252 253 TEST(FileDescriptorTests, Set) 254 { 255 // Test where object does not contain an open file descriptor 256 FileDescriptor descriptor{}; 257 EXPECT_EQ(descriptor(), -1); 258 int fd1 = createOpenFileDescriptor(); 259 descriptor.set(fd1); 260 EXPECT_EQ(descriptor(), fd1); 261 EXPECT_TRUE(isValid(fd1)); 262 263 // Test where object contains an open file descriptor. Should close 264 // previous descriptor. 265 EXPECT_EQ(descriptor(), fd1); 266 EXPECT_TRUE(isValid(fd1)); 267 int fd2 = createOpenFileDescriptor(); 268 descriptor.set(fd2); 269 EXPECT_EQ(descriptor(), fd2); 270 EXPECT_FALSE(isValid(fd1)); 271 EXPECT_TRUE(isValid(fd2)); 272 273 // Test where -1 is specified. Should close previous descriptor. 274 EXPECT_EQ(descriptor(), fd2); 275 EXPECT_TRUE(isValid(fd2)); 276 descriptor.set(-1); 277 EXPECT_EQ(descriptor(), -1); 278 EXPECT_FALSE(isValid(fd2)); 279 } 280