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