posix-mock-test.cc revision d02a5031f0b550605d49fc9b65f70afe57d3a782
1/* 2 Tests of the C++ interface to POSIX functions that require mocks 3 4 Copyright (c) 2012-2015, Victor Zverovich 5 All rights reserved. 6 7 Redistribution and use in source and binary forms, with or without 8 modification, are permitted provided that the following conditions are met: 9 10 1. Redistributions of source code must retain the above copyright notice, this 11 list of conditions and the following disclaimer. 12 2. Redistributions in binary form must reproduce the above copyright notice, 13 this list of conditions and the following disclaimer in the documentation 14 and/or other materials provided with the distribution. 15 16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28// Disable bogus MSVC warnings. 29#define _CRT_SECURE_NO_WARNINGS 30 31#include "posix-mock.h" 32#include "posix.cc" 33 34#include <errno.h> 35#include <fcntl.h> 36#include <climits> 37 38#ifdef _WIN32 39# include <io.h> 40# undef max 41# undef ERROR 42#endif 43 44#include "gtest-extra.h" 45#include "util.h" 46 47using fmt::BufferedFile; 48using fmt::ErrorCode; 49using fmt::File; 50 51namespace { 52int open_count; 53int close_count; 54int dup_count; 55int dup2_count; 56int fdopen_count; 57int read_count; 58int write_count; 59int pipe_count; 60int fopen_count; 61int fclose_count; 62int fileno_count; 63std::size_t read_nbyte; 64std::size_t write_nbyte; 65bool sysconf_error; 66 67enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim; 68} 69 70#define EMULATE_EINTR(func, error_result) \ 71 if (func##_count != 0) { \ 72 if (func##_count++ != 3) { \ 73 errno = EINTR; \ 74 return error_result; \ 75 } \ 76 } 77 78#ifndef _MSC_VER 79int test::open(const char *path, int oflag, int mode) { 80 EMULATE_EINTR(open, -1); 81 return ::open(path, oflag, mode); 82} 83#else 84errno_t test::sopen_s( 85 int* pfh, const char *filename, int oflag, int shflag, int pmode) { 86 EMULATE_EINTR(open, EINTR); 87 return _sopen_s(pfh, filename, oflag, shflag, pmode); 88} 89#endif 90 91#ifndef _WIN32 92 93long test::sysconf(int name) { 94 long result = ::sysconf(name); 95 if (!sysconf_error) 96 return result; 97 // Simulate an error. 98 errno = EINVAL; 99 return -1; 100} 101 102static off_t max_file_size() { return std::numeric_limits<off_t>::max(); } 103 104int test::fstat(int fd, struct stat *buf) { 105 int result = ::fstat(fd, buf); 106 if (fstat_sim == MAX_SIZE) 107 buf->st_size = max_file_size(); 108 return result; 109} 110 111#else 112 113static LONGLONG max_file_size() { return std::numeric_limits<LONGLONG>::max(); } 114 115DWORD test::GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) { 116 if (fstat_sim == ERROR) { 117 SetLastError(ERROR_ACCESS_DENIED); 118 return INVALID_FILE_SIZE; 119 } 120 if (fstat_sim == MAX_SIZE) { 121 DWORD max = std::numeric_limits<DWORD>::max(); 122 *lpFileSizeHigh = max >> 1; 123 return max; 124 } 125 return ::GetFileSize(hFile, lpFileSizeHigh); 126} 127 128#endif 129 130int test::close(int fildes) { 131 // Close the file first because close shouldn't be retried. 132 int result = ::FMT_POSIX(close(fildes)); 133 EMULATE_EINTR(close, -1); 134 return result; 135} 136 137int test::dup(int fildes) { 138 EMULATE_EINTR(dup, -1); 139 return ::FMT_POSIX(dup(fildes)); 140} 141 142int test::dup2(int fildes, int fildes2) { 143 EMULATE_EINTR(dup2, -1); 144 return ::FMT_POSIX(dup2(fildes, fildes2)); 145} 146 147FILE *test::fdopen(int fildes, const char *mode) { 148 EMULATE_EINTR(fdopen, 0); 149 return ::FMT_POSIX(fdopen(fildes, mode)); 150} 151 152test::ssize_t test::read(int fildes, void *buf, test::size_t nbyte) { 153 read_nbyte = nbyte; 154 EMULATE_EINTR(read, -1); 155 return ::FMT_POSIX(read(fildes, buf, nbyte)); 156} 157 158test::ssize_t test::write(int fildes, const void *buf, test::size_t nbyte) { 159 write_nbyte = nbyte; 160 EMULATE_EINTR(write, -1); 161 return ::FMT_POSIX(write(fildes, buf, nbyte)); 162} 163 164#ifndef _WIN32 165int test::pipe(int fildes[2]) { 166 EMULATE_EINTR(pipe, -1); 167 return ::pipe(fildes); 168} 169#else 170int test::pipe(int *pfds, unsigned psize, int textmode) { 171 EMULATE_EINTR(pipe, -1); 172 return _pipe(pfds, psize, textmode); 173} 174#endif 175 176FILE *test::fopen(const char *filename, const char *mode) { 177 EMULATE_EINTR(fopen, 0); 178 return ::fopen(filename, mode); 179} 180 181int test::fclose(FILE *stream) { 182 EMULATE_EINTR(fclose, EOF); 183 return ::fclose(stream); 184} 185 186int (test::fileno)(FILE *stream) { 187 EMULATE_EINTR(fileno, -1); 188#ifdef fileno 189 return FMT_POSIX(fileno(stream)); 190#else 191 return ::FMT_POSIX(fileno(stream)); 192#endif 193} 194 195#ifndef _WIN32 196# define EXPECT_RETRY(statement, func, message) \ 197 func##_count = 1; \ 198 statement; \ 199 EXPECT_EQ(4, func##_count); \ 200 func##_count = 0; 201# define EXPECT_EQ_POSIX(expected, actual) EXPECT_EQ(expected, actual) 202#else 203# define EXPECT_RETRY(statement, func, message) \ 204 func##_count = 1; \ 205 EXPECT_SYSTEM_ERROR(statement, EINTR, message); \ 206 func##_count = 0; 207# define EXPECT_EQ_POSIX(expected, actual) 208#endif 209 210void write_file(fmt::CStringRef filename, fmt::StringRef content) { 211 fmt::BufferedFile f(filename, "w"); 212 f.print("{}", content); 213} 214 215TEST(UtilTest, StaticAssert) { 216 FMT_STATIC_ASSERT(true, "success"); 217 // Static assertion failure is tested in compile-test because it causes 218 // a compile-time error. 219} 220 221TEST(UtilTest, GetPageSize) { 222#ifdef _WIN32 223 SYSTEM_INFO si = {}; 224 GetSystemInfo(&si); 225 EXPECT_EQ(si.dwPageSize, fmt::getpagesize()); 226#else 227 EXPECT_EQ(sysconf(_SC_PAGESIZE), fmt::getpagesize()); 228 sysconf_error = true; 229 EXPECT_SYSTEM_ERROR( 230 fmt::getpagesize(), EINVAL, "cannot get memory page size"); 231 sysconf_error = false; 232#endif 233} 234 235TEST(FileTest, OpenRetry) { 236 write_file("test", "there must be something here"); 237 File *f = 0; 238 EXPECT_RETRY(f = new File("test", File::RDONLY), 239 open, "cannot open file test"); 240#ifndef _WIN32 241 char c = 0; 242 f->read(&c, 1); 243#endif 244 delete f; 245} 246 247TEST(FileTest, CloseNoRetryInDtor) { 248 File read_end, write_end; 249 File::pipe(read_end, write_end); 250 testing::internal::scoped_ptr<File> f(new File(std::move(read_end))); 251 int saved_close_count = 0; 252 EXPECT_WRITE(stderr, { 253 close_count = 1; 254 f.reset(); 255 saved_close_count = close_count; 256 close_count = 0; 257 }, format_system_error(EINTR, "cannot close file") + "\n"); 258 EXPECT_EQ(2, saved_close_count); 259} 260 261TEST(FileTest, CloseNoRetry) { 262 File read_end, write_end; 263 File::pipe(read_end, write_end); 264 close_count = 1; 265 EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file"); 266 EXPECT_EQ(2, close_count); 267 close_count = 0; 268} 269 270TEST(FileTest, Size) { 271 std::string content = "top secret, destroy before reading"; 272 write_file("test", content); 273 File f("test", File::RDONLY); 274 EXPECT_GE(f.size(), 0); 275 fmt::ULongLong file_size = f.size(); 276 EXPECT_EQ(content.size(), file_size); 277#ifdef _WIN32 278 fmt::MemoryWriter message; 279 fmt::internal::format_windows_error( 280 message, ERROR_ACCESS_DENIED, "cannot get file size"); 281 fstat_sim = ERROR; 282 EXPECT_THROW_MSG(f.size(), fmt::WindowsError, message.str()); 283 fstat_sim = NONE; 284#else 285 f.close(); 286 EXPECT_SYSTEM_ERROR(f.size(), EBADF, "cannot get file attributes"); 287#endif 288} 289 290TEST(FileTest, MaxSize) { 291 write_file("test", ""); 292 File f("test", File::RDONLY); 293 fstat_sim = MAX_SIZE; 294 EXPECT_GE(f.size(), 0); 295 EXPECT_EQ(max_file_size(), f.size()); 296 fstat_sim = NONE; 297} 298 299TEST(FileTest, ReadRetry) { 300 File read_end, write_end; 301 File::pipe(read_end, write_end); 302 enum { SIZE = 4 }; 303 write_end.write("test", SIZE); 304 write_end.close(); 305 char buffer[SIZE]; 306 std::streamsize count = 0; 307 EXPECT_RETRY(count = read_end.read(buffer, SIZE), 308 read, "cannot read from file"); 309 EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count); 310} 311 312TEST(FileTest, WriteRetry) { 313 File read_end, write_end; 314 File::pipe(read_end, write_end); 315 enum { SIZE = 4 }; 316 std::streamsize count = 0; 317 EXPECT_RETRY(count = write_end.write("test", SIZE), 318 write, "cannot write to file"); 319 write_end.close(); 320#ifndef _WIN32 321 EXPECT_EQ(static_cast<std::streamsize>(SIZE), count); 322 char buffer[SIZE + 1]; 323 read_end.read(buffer, SIZE); 324 buffer[SIZE] = '\0'; 325 EXPECT_STREQ("test", buffer); 326#endif 327} 328 329#ifdef _WIN32 330TEST(FileTest, ConvertReadCount) { 331 File read_end, write_end; 332 File::pipe(read_end, write_end); 333 char c; 334 std::size_t size = UINT_MAX; 335 if (sizeof(unsigned) != sizeof(std::size_t)) 336 ++size; 337 read_count = 1; 338 read_nbyte = 0; 339 EXPECT_THROW(read_end.read(&c, size), fmt::SystemError); 340 read_count = 0; 341 EXPECT_EQ(UINT_MAX, read_nbyte); 342} 343 344TEST(FileTest, ConvertWriteCount) { 345 File read_end, write_end; 346 File::pipe(read_end, write_end); 347 char c; 348 std::size_t size = UINT_MAX; 349 if (sizeof(unsigned) != sizeof(std::size_t)) 350 ++size; 351 write_count = 1; 352 write_nbyte = 0; 353 EXPECT_THROW(write_end.write(&c, size), fmt::SystemError); 354 write_count = 0; 355 EXPECT_EQ(UINT_MAX, write_nbyte); 356} 357#endif 358 359TEST(FileTest, DupNoRetry) { 360 int stdout_fd = FMT_POSIX(fileno(stdout)); 361 dup_count = 1; 362 EXPECT_SYSTEM_ERROR(File::dup(stdout_fd), EINTR, 363 fmt::format("cannot duplicate file descriptor {}", stdout_fd)); 364 dup_count = 0; 365} 366 367TEST(FileTest, Dup2Retry) { 368 int stdout_fd = FMT_POSIX(fileno(stdout)); 369 File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd); 370 EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2, 371 fmt::format("cannot duplicate file descriptor {} to {}", 372 f1.descriptor(), f2.descriptor())); 373} 374 375TEST(FileTest, Dup2NoExceptRetry) { 376 int stdout_fd = FMT_POSIX(fileno(stdout)); 377 File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd); 378 ErrorCode ec; 379 dup2_count = 1; 380 f1.dup2(f2.descriptor(), ec); 381#ifndef _WIN32 382 EXPECT_EQ(4, dup2_count); 383#else 384 EXPECT_EQ(EINTR, ec.get()); 385#endif 386 dup2_count = 0; 387} 388 389TEST(FileTest, PipeNoRetry) { 390 File read_end, write_end; 391 pipe_count = 1; 392 EXPECT_SYSTEM_ERROR( 393 File::pipe(read_end, write_end), EINTR, "cannot create pipe"); 394 pipe_count = 0; 395} 396 397TEST(FileTest, FdopenNoRetry) { 398 File read_end, write_end; 399 File::pipe(read_end, write_end); 400 fdopen_count = 1; 401 EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), 402 EINTR, "cannot associate stream with file descriptor"); 403 fdopen_count = 0; 404} 405 406TEST(BufferedFileTest, OpenRetry) { 407 write_file("test", "there must be something here"); 408 BufferedFile *f = 0; 409 EXPECT_RETRY(f = new BufferedFile("test", "r"), 410 fopen, "cannot open file test"); 411#ifndef _WIN32 412 char c = 0; 413 if (fread(&c, 1, 1, f->get()) < 1) 414 throw fmt::SystemError(errno, "fread failed"); 415#endif 416 delete f; 417} 418 419TEST(BufferedFileTest, CloseNoRetryInDtor) { 420 File read_end, write_end; 421 File::pipe(read_end, write_end); 422 BufferedFile *f = new BufferedFile(read_end.fdopen("r")); 423 int saved_fclose_count = 0; 424 EXPECT_WRITE(stderr, { 425 fclose_count = 1; 426 delete f; 427 saved_fclose_count = fclose_count; 428 fclose_count = 0; 429 }, format_system_error(EINTR, "cannot close file") + "\n"); 430 EXPECT_EQ(2, saved_fclose_count); 431} 432 433TEST(BufferedFileTest, CloseNoRetry) { 434 File read_end, write_end; 435 File::pipe(read_end, write_end); 436 BufferedFile f = read_end.fdopen("r"); 437 fclose_count = 1; 438 EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file"); 439 EXPECT_EQ(2, fclose_count); 440 fclose_count = 0; 441} 442 443TEST(BufferedFileTest, FilenoNoRetry) { 444 File read_end, write_end; 445 File::pipe(read_end, write_end); 446 BufferedFile f = read_end.fdopen("r"); 447 fileno_count = 1; 448 EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor"); 449 EXPECT_EQ(2, fileno_count); 450 fileno_count = 0; 451} 452