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
39 using fmt::BufferedFile;
40 using fmt::ErrorCode;
41 using fmt::File;
42
43 using testing::internal::scoped_ptr;
44
45 // Checks if the file is open by reading one character from it.
isopen(int fd)46 bool isopen(int fd) {
47 char buffer;
48 return FMT_POSIX(read(fd, &buffer, 1)) == 1;
49 }
50
isclosed(int fd)51 bool 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.
open_file()59 File 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.
write(File & f,fmt::StringRef s)68 void 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
TEST(BufferedFileTest,DefaultCtor)80 TEST(BufferedFileTest, DefaultCtor) {
81 BufferedFile f;
82 EXPECT_TRUE(f.get() == 0);
83 }
84
TEST(BufferedFileTest,MoveCtor)85 TEST(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
TEST(BufferedFileTest,MoveAssignment)94 TEST(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
TEST(BufferedFileTest,MoveAssignmentClosesFile)104 TEST(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
TEST(BufferedFileTest,MoveFromTemporaryInCtor)112 TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
113 FILE *fp = 0;
114 BufferedFile f(open_buffered_file(&fp));
115 EXPECT_EQ(fp, f.get());
116 }
117
TEST(BufferedFileTest,MoveFromTemporaryInAssignment)118 TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
119 FILE *fp = 0;
120 BufferedFile f;
121 f = open_buffered_file(&fp);
122 EXPECT_EQ(fp, f.get());
123 }
124
TEST(BufferedFileTest,MoveFromTemporaryInAssignmentClosesFile)125 TEST(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
TEST(BufferedFileTest,CloseFileInDtor)132 TEST(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
TEST(BufferedFileTest,CloseErrorInDtor)141 TEST(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
TEST(BufferedFileTest,Close)153 TEST(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
TEST(BufferedFileTest,CloseError)161 TEST(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
TEST(BufferedFileTest,Fileno)168 TEST(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
TEST(FileTest,DefaultCtor)187 TEST(FileTest, DefaultCtor) {
188 File f;
189 EXPECT_EQ(-1, f.descriptor());
190 }
191
TEST(FileTest,OpenBufferedFileInCtor)192 TEST(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
TEST(FileTest,OpenBufferedFileError)200 TEST(FileTest, OpenBufferedFileError) {
201 EXPECT_SYSTEM_ERROR(File("nonexistent", File::RDONLY),
202 ENOENT, "cannot open file nonexistent");
203 }
204
TEST(FileTest,MoveCtor)205 TEST(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
TEST(FileTest,MoveAssignment)214 TEST(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
TEST(FileTest,MoveAssignmentClosesFile)224 TEST(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
OpenBufferedFile(int & fd)232 File OpenBufferedFile(int &fd) {
233 File f = open_file();
234 fd = f.descriptor();
235 return f;
236 }
237
TEST(FileTest,MoveFromTemporaryInCtor)238 TEST(FileTest, MoveFromTemporaryInCtor) {
239 int fd = 0xdead;
240 File f(OpenBufferedFile(fd));
241 EXPECT_EQ(fd, f.descriptor());
242 }
243
TEST(FileTest,MoveFromTemporaryInAssignment)244 TEST(FileTest, MoveFromTemporaryInAssignment) {
245 int fd = 0xdead;
246 File f;
247 f = OpenBufferedFile(fd);
248 EXPECT_EQ(fd, f.descriptor());
249 }
250
TEST(FileTest,MoveFromTemporaryInAssignmentClosesFile)251 TEST(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
TEST(FileTest,CloseFileInDtor)259 TEST(FileTest, CloseFileInDtor) {
260 int fd = 0;
261 {
262 File f = open_file();
263 fd = f.descriptor();
264 }
265 EXPECT_TRUE(isclosed(fd));
266 }
267
TEST(FileTest,CloseErrorInDtor)268 TEST(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
TEST(FileTest,Close)280 TEST(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
TEST(FileTest,CloseError)288 TEST(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
TEST(FileTest,Read)295 TEST(FileTest, Read) {
296 File f = open_file();
297 EXPECT_READ(f, FILE_CONTENT);
298 }
299
TEST(FileTest,ReadError)300 TEST(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
TEST(FileTest,Write)308 TEST(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
TEST(FileTest,WriteError)316 TEST(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
TEST(FileTest,Dup)323 TEST(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__
TEST(FileTest,DupError)331 TEST(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
TEST(FileTest,Dup2)338 TEST(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
TEST(FileTest,Dup2Error)346 TEST(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
TEST(FileTest,Dup2NoExcept)352 TEST(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
TEST(FileTest,Dup2NoExceptError)362 TEST(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
TEST(FileTest,Pipe)369 TEST(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
TEST(FileTest,Fdopen)378 TEST(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
TEST(FileTest,FdopenError)385 TEST(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
TEST(LocaleTest,Strtod)392 TEST(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