1/* 2 Tests of the C++ interface to POSIX functions 3 4 Copyright (c) 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#include <cstdlib> // std::exit 29#include <cstring> 30 31#include "fmt/posix.h" 32#include "gtest-extra.h" 33#include "util.h" 34 35#ifdef fileno 36# undef fileno 37#endif 38 39using fmt::BufferedFile; 40using fmt::ErrorCode; 41using fmt::File; 42 43using testing::internal::scoped_ptr; 44 45// Checks if the file is open by reading one character from it. 46bool isopen(int fd) { 47 char buffer; 48 return FMT_POSIX(read(fd, &buffer, 1)) == 1; 49} 50 51bool isclosed(int fd) { 52 char buffer; 53 std::streamsize result = 0; 54 SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1))); 55 return result == -1 && errno == EBADF; 56} 57 58// Opens a file for reading. 59File open_file() { 60 File read_end, write_end; 61 File::pipe(read_end, write_end); 62 write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT)); 63 write_end.close(); 64 return read_end; 65} 66 67// Attempts to write a string to a file. 68void write(File &f, fmt::StringRef s) { 69 std::size_t num_chars_left = s.size(); 70 const char *ptr = s.data(); 71 do { 72 std::size_t count = f.write(ptr, num_chars_left); 73 ptr += count; 74 // We can't write more than size_t bytes since num_chars_left 75 // has type size_t. 76 num_chars_left -= static_cast<std::size_t>(count); 77 } while (num_chars_left != 0); 78} 79 80TEST(BufferedFileTest, DefaultCtor) { 81 BufferedFile f; 82 EXPECT_TRUE(f.get() == 0); 83} 84 85TEST(BufferedFileTest, MoveCtor) { 86 BufferedFile bf = open_buffered_file(); 87 FILE *fp = bf.get(); 88 EXPECT_TRUE(fp != 0); 89 BufferedFile bf2(std::move(bf)); 90 EXPECT_EQ(fp, bf2.get()); 91 EXPECT_TRUE(bf.get() == 0); 92} 93 94TEST(BufferedFileTest, MoveAssignment) { 95 BufferedFile bf = open_buffered_file(); 96 FILE *fp = bf.get(); 97 EXPECT_TRUE(fp != 0); 98 BufferedFile bf2; 99 bf2 = std::move(bf); 100 EXPECT_EQ(fp, bf2.get()); 101 EXPECT_TRUE(bf.get() == 0); 102} 103 104TEST(BufferedFileTest, MoveAssignmentClosesFile) { 105 BufferedFile bf = open_buffered_file(); 106 BufferedFile bf2 = open_buffered_file(); 107 int old_fd = bf2.fileno(); 108 bf2 = std::move(bf); 109 EXPECT_TRUE(isclosed(old_fd)); 110} 111 112TEST(BufferedFileTest, MoveFromTemporaryInCtor) { 113 FILE *fp = 0; 114 BufferedFile f(open_buffered_file(&fp)); 115 EXPECT_EQ(fp, f.get()); 116} 117 118TEST(BufferedFileTest, MoveFromTemporaryInAssignment) { 119 FILE *fp = 0; 120 BufferedFile f; 121 f = open_buffered_file(&fp); 122 EXPECT_EQ(fp, f.get()); 123} 124 125TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) { 126 BufferedFile f = open_buffered_file(); 127 int old_fd = f.fileno(); 128 f = open_buffered_file(); 129 EXPECT_TRUE(isclosed(old_fd)); 130} 131 132TEST(BufferedFileTest, CloseFileInDtor) { 133 int fd = 0; 134 { 135 BufferedFile f = open_buffered_file(); 136 fd = f.fileno(); 137 } 138 EXPECT_TRUE(isclosed(fd)); 139} 140 141TEST(BufferedFileTest, CloseErrorInDtor) { 142 scoped_ptr<BufferedFile> f(new BufferedFile(open_buffered_file())); 143 EXPECT_WRITE(stderr, { 144 // The close function must be called inside EXPECT_WRITE, otherwise 145 // the system may recycle closed file descriptor when redirecting the 146 // output in EXPECT_STDERR and the second close will break output 147 // redirection. 148 FMT_POSIX(close(f->fileno())); 149 SUPPRESS_ASSERT(f.reset()); 150 }, format_system_error(EBADF, "cannot close file") + "\n"); 151} 152 153TEST(BufferedFileTest, Close) { 154 BufferedFile f = open_buffered_file(); 155 int fd = f.fileno(); 156 f.close(); 157 EXPECT_TRUE(f.get() == 0); 158 EXPECT_TRUE(isclosed(fd)); 159} 160 161TEST(BufferedFileTest, CloseError) { 162 BufferedFile f = open_buffered_file(); 163 FMT_POSIX(close(f.fileno())); 164 EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); 165 EXPECT_TRUE(f.get() == 0); 166} 167 168TEST(BufferedFileTest, Fileno) { 169 BufferedFile f; 170#ifndef __COVERITY__ 171 // fileno on a null FILE pointer either crashes or returns an error. 172 // Disable Coverity because this is intentional. 173 EXPECT_DEATH_IF_SUPPORTED({ 174 try { 175 f.fileno(); 176 } catch (fmt::SystemError) { 177 std::exit(1); 178 } 179 }, ""); 180#endif 181 f = open_buffered_file(); 182 EXPECT_TRUE(f.fileno() != -1); 183 File copy = File::dup(f.fileno()); 184 EXPECT_READ(copy, FILE_CONTENT); 185} 186 187TEST(FileTest, DefaultCtor) { 188 File f; 189 EXPECT_EQ(-1, f.descriptor()); 190} 191 192TEST(FileTest, OpenBufferedFileInCtor) { 193 FILE *fp = safe_fopen("test-file", "w"); 194 std::fputs(FILE_CONTENT, fp); 195 std::fclose(fp); 196 File f("test-file", File::RDONLY); 197 ASSERT_TRUE(isopen(f.descriptor())); 198} 199 200TEST(FileTest, OpenBufferedFileError) { 201 EXPECT_SYSTEM_ERROR(File("nonexistent", File::RDONLY), 202 ENOENT, "cannot open file nonexistent"); 203} 204 205TEST(FileTest, MoveCtor) { 206 File f = open_file(); 207 int fd = f.descriptor(); 208 EXPECT_NE(-1, fd); 209 File f2(std::move(f)); 210 EXPECT_EQ(fd, f2.descriptor()); 211 EXPECT_EQ(-1, f.descriptor()); 212} 213 214TEST(FileTest, MoveAssignment) { 215 File f = open_file(); 216 int fd = f.descriptor(); 217 EXPECT_NE(-1, fd); 218 File f2; 219 f2 = std::move(f); 220 EXPECT_EQ(fd, f2.descriptor()); 221 EXPECT_EQ(-1, f.descriptor()); 222} 223 224TEST(FileTest, MoveAssignmentClosesFile) { 225 File f = open_file(); 226 File f2 = open_file(); 227 int old_fd = f2.descriptor(); 228 f2 = std::move(f); 229 EXPECT_TRUE(isclosed(old_fd)); 230} 231 232File OpenBufferedFile(int &fd) { 233 File f = open_file(); 234 fd = f.descriptor(); 235 return f; 236} 237 238TEST(FileTest, MoveFromTemporaryInCtor) { 239 int fd = 0xdead; 240 File f(OpenBufferedFile(fd)); 241 EXPECT_EQ(fd, f.descriptor()); 242} 243 244TEST(FileTest, MoveFromTemporaryInAssignment) { 245 int fd = 0xdead; 246 File f; 247 f = OpenBufferedFile(fd); 248 EXPECT_EQ(fd, f.descriptor()); 249} 250 251TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) { 252 int fd = 0xdead; 253 File f = open_file(); 254 int old_fd = f.descriptor(); 255 f = OpenBufferedFile(fd); 256 EXPECT_TRUE(isclosed(old_fd)); 257} 258 259TEST(FileTest, CloseFileInDtor) { 260 int fd = 0; 261 { 262 File f = open_file(); 263 fd = f.descriptor(); 264 } 265 EXPECT_TRUE(isclosed(fd)); 266} 267 268TEST(FileTest, CloseErrorInDtor) { 269 scoped_ptr<File> f(new File(open_file())); 270 EXPECT_WRITE(stderr, { 271 // The close function must be called inside EXPECT_WRITE, otherwise 272 // the system may recycle closed file descriptor when redirecting the 273 // output in EXPECT_STDERR and the second close will break output 274 // redirection. 275 FMT_POSIX(close(f->descriptor())); 276 SUPPRESS_ASSERT(f.reset()); 277 }, format_system_error(EBADF, "cannot close file") + "\n"); 278} 279 280TEST(FileTest, Close) { 281 File f = open_file(); 282 int fd = f.descriptor(); 283 f.close(); 284 EXPECT_EQ(-1, f.descriptor()); 285 EXPECT_TRUE(isclosed(fd)); 286} 287 288TEST(FileTest, CloseError) { 289 File f = open_file(); 290 FMT_POSIX(close(f.descriptor())); 291 EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); 292 EXPECT_EQ(-1, f.descriptor()); 293} 294 295TEST(FileTest, Read) { 296 File f = open_file(); 297 EXPECT_READ(f, FILE_CONTENT); 298} 299 300TEST(FileTest, ReadError) { 301 File f("test-file", File::WRONLY); 302 char buf; 303 // We intentionally read from a file opened in the write-only mode to 304 // cause error. 305 EXPECT_SYSTEM_ERROR(f.read(&buf, 1), EBADF, "cannot read from file"); 306} 307 308TEST(FileTest, Write) { 309 File read_end, write_end; 310 File::pipe(read_end, write_end); 311 write(write_end, "test"); 312 write_end.close(); 313 EXPECT_READ(read_end, "test"); 314} 315 316TEST(FileTest, WriteError) { 317 File f("test-file", File::RDONLY); 318 // We intentionally write to a file opened in the read-only mode to 319 // cause error. 320 EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file"); 321} 322 323TEST(FileTest, Dup) { 324 File f = open_file(); 325 File copy = File::dup(f.descriptor()); 326 EXPECT_NE(f.descriptor(), copy.descriptor()); 327 EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT))); 328} 329 330#ifndef __COVERITY__ 331TEST(FileTest, DupError) { 332 int value = -1; 333 EXPECT_SYSTEM_ERROR_NOASSERT(File::dup(value), 334 EBADF, "cannot duplicate file descriptor -1"); 335} 336#endif 337 338TEST(FileTest, Dup2) { 339 File f = open_file(); 340 File copy = open_file(); 341 f.dup2(copy.descriptor()); 342 EXPECT_NE(f.descriptor(), copy.descriptor()); 343 EXPECT_READ(copy, FILE_CONTENT); 344} 345 346TEST(FileTest, Dup2Error) { 347 File f = open_file(); 348 EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF, 349 fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor())); 350} 351 352TEST(FileTest, Dup2NoExcept) { 353 File f = open_file(); 354 File copy = open_file(); 355 ErrorCode ec; 356 f.dup2(copy.descriptor(), ec); 357 EXPECT_EQ(0, ec.get()); 358 EXPECT_NE(f.descriptor(), copy.descriptor()); 359 EXPECT_READ(copy, FILE_CONTENT); 360} 361 362TEST(FileTest, Dup2NoExceptError) { 363 File f = open_file(); 364 ErrorCode ec; 365 SUPPRESS_ASSERT(f.dup2(-1, ec)); 366 EXPECT_EQ(EBADF, ec.get()); 367} 368 369TEST(FileTest, Pipe) { 370 File read_end, write_end; 371 File::pipe(read_end, write_end); 372 EXPECT_NE(-1, read_end.descriptor()); 373 EXPECT_NE(-1, write_end.descriptor()); 374 write(write_end, "test"); 375 EXPECT_READ(read_end, "test"); 376} 377 378TEST(FileTest, Fdopen) { 379 File read_end, write_end; 380 File::pipe(read_end, write_end); 381 int read_fd = read_end.descriptor(); 382 EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get()))); 383} 384 385TEST(FileTest, FdopenError) { 386 File f; 387 EXPECT_SYSTEM_ERROR_NOASSERT( 388 f.fdopen("r"), EBADF, "cannot associate stream with file descriptor"); 389} 390 391#ifdef FMT_LOCALE 392TEST(LocaleTest, Strtod) { 393 fmt::Locale locale; 394 const char *start = "4.2", *ptr = start; 395 EXPECT_EQ(4.2, locale.strtod(ptr)); 396 EXPECT_EQ(start + 3, ptr); 397} 398#endif 399