• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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