Mercurial > minori
comparison dep/fmt/test/os-test.cc @ 343:1faa72660932
*: transfer back to cmake from autotools
autotools just made lots of things more complicated than
they should have and many things broke (i.e. translations)
author | Paper <paper@paper.us.eu.org> |
---|---|
date | Thu, 20 Jun 2024 05:56:06 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
342:adb79bdde329 | 343:1faa72660932 |
---|---|
1 // Formatting library for C++ - tests of the OS-specific functionality | |
2 // | |
3 // Copyright (c) 2012 - present, Victor Zverovich | |
4 // All rights reserved. | |
5 // | |
6 // For the license information refer to format.h. | |
7 | |
8 #include "fmt/os.h" | |
9 | |
10 #include <cstdlib> // std::exit | |
11 #include <cstring> | |
12 #include <memory> | |
13 | |
14 #include "gtest-extra.h" | |
15 #include "util.h" | |
16 | |
17 using fmt::buffered_file; | |
18 using testing::HasSubstr; | |
19 using wstring_view = fmt::basic_string_view<wchar_t>; | |
20 | |
21 #ifdef _WIN32 | |
22 | |
23 # include <windows.h> | |
24 | |
25 TEST(os_test, format_windows_error) { | |
26 LPWSTR message = nullptr; | |
27 auto result = FormatMessageW( | |
28 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | | |
29 FORMAT_MESSAGE_IGNORE_INSERTS, | |
30 nullptr, ERROR_FILE_EXISTS, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
31 reinterpret_cast<LPWSTR>(&message), 0, nullptr); | |
32 auto utf8_message = | |
33 fmt::detail::to_utf8<wchar_t>(wstring_view(message, result - 2)); | |
34 LocalFree(message); | |
35 fmt::memory_buffer actual_message; | |
36 fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test"); | |
37 EXPECT_EQ(fmt::format("test: {}", utf8_message.str()), | |
38 fmt::to_string(actual_message)); | |
39 actual_message.resize(0); | |
40 } | |
41 | |
42 TEST(os_test, format_long_windows_error) { | |
43 LPWSTR message = nullptr; | |
44 // this error code is not available on all Windows platforms and | |
45 // Windows SDKs, so do not fail the test if the error string cannot | |
46 // be retrieved. | |
47 int provisioning_not_allowed = 0x80284013L; // TBS_E_PROVISIONING_NOT_ALLOWED | |
48 auto result = FormatMessageW( | |
49 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | | |
50 FORMAT_MESSAGE_IGNORE_INSERTS, | |
51 nullptr, static_cast<DWORD>(provisioning_not_allowed), | |
52 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
53 reinterpret_cast<LPWSTR>(&message), 0, nullptr); | |
54 if (result == 0) { | |
55 LocalFree(message); | |
56 return; | |
57 } | |
58 auto utf8_message = | |
59 fmt::detail::to_utf8<wchar_t>(wstring_view(message, result - 2)); | |
60 LocalFree(message); | |
61 fmt::memory_buffer actual_message; | |
62 fmt::detail::format_windows_error(actual_message, provisioning_not_allowed, | |
63 "test"); | |
64 EXPECT_EQ(fmt::format("test: {}", utf8_message.str()), | |
65 fmt::to_string(actual_message)); | |
66 } | |
67 | |
68 TEST(os_test, windows_error) { | |
69 auto error = std::system_error(std::error_code()); | |
70 try { | |
71 throw fmt::windows_error(ERROR_FILE_EXISTS, "test {}", "error"); | |
72 } catch (const std::system_error& e) { | |
73 error = e; | |
74 } | |
75 fmt::memory_buffer message; | |
76 fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error"); | |
77 EXPECT_THAT(error.what(), HasSubstr(to_string(message))); | |
78 EXPECT_EQ(ERROR_FILE_EXISTS, error.code().value()); | |
79 } | |
80 | |
81 TEST(os_test, report_windows_error) { | |
82 fmt::memory_buffer out; | |
83 fmt::detail::format_windows_error(out, ERROR_FILE_EXISTS, "test error"); | |
84 out.push_back('\n'); | |
85 EXPECT_WRITE(stderr, | |
86 fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"), | |
87 fmt::to_string(out)); | |
88 } | |
89 | |
90 # if FMT_USE_FCNTL && !defined(__MINGW32__) | |
91 TEST(file_test, open_windows_file) { | |
92 using fmt::file; | |
93 file out = file::open_windows_file(L"test-file", | |
94 file::WRONLY | file::CREATE | file::TRUNC); | |
95 out.write("x", 1); | |
96 file in = file::open_windows_file(L"test-file", file::RDONLY); | |
97 EXPECT_READ(in, "x"); | |
98 } | |
99 # endif // FMT_USE_FCNTL && !defined(__MINGW32__) | |
100 | |
101 #endif // _WIN32 | |
102 | |
103 #if FMT_USE_FCNTL | |
104 | |
105 using fmt::file; | |
106 | |
107 bool isclosed(int fd) { | |
108 char buffer; | |
109 auto result = std::streamsize(); | |
110 SUPPRESS_ASSERT(result = FMT_POSIX(read(fd, &buffer, 1))); | |
111 return result == -1 && errno == EBADF; | |
112 } | |
113 | |
114 // Opens a file for reading. | |
115 file open_file() { | |
116 file read_end, write_end; | |
117 file::pipe(read_end, write_end); | |
118 write_end.write(file_content, std::strlen(file_content)); | |
119 write_end.close(); | |
120 return read_end; | |
121 } | |
122 | |
123 // Attempts to write a string to a file. | |
124 void write(file& f, fmt::string_view s) { | |
125 size_t num_chars_left = s.size(); | |
126 const char* ptr = s.data(); | |
127 do { | |
128 size_t count = f.write(ptr, num_chars_left); | |
129 ptr += count; | |
130 // We can't write more than size_t bytes since num_chars_left | |
131 // has type size_t. | |
132 num_chars_left -= count; | |
133 } while (num_chars_left != 0); | |
134 } | |
135 | |
136 TEST(buffered_file_test, default_ctor) { | |
137 auto f = buffered_file(); | |
138 EXPECT_TRUE(f.get() == nullptr); | |
139 } | |
140 | |
141 TEST(buffered_file_test, move_ctor) { | |
142 buffered_file bf = open_buffered_file(); | |
143 FILE* fp = bf.get(); | |
144 EXPECT_TRUE(fp != nullptr); | |
145 buffered_file bf2(std::move(bf)); | |
146 EXPECT_EQ(fp, bf2.get()); | |
147 EXPECT_TRUE(bf.get() == nullptr); | |
148 } | |
149 | |
150 TEST(buffered_file_test, move_assignment) { | |
151 buffered_file bf = open_buffered_file(); | |
152 FILE* fp = bf.get(); | |
153 EXPECT_TRUE(fp != nullptr); | |
154 buffered_file bf2; | |
155 bf2 = std::move(bf); | |
156 EXPECT_EQ(fp, bf2.get()); | |
157 EXPECT_TRUE(bf.get() == nullptr); | |
158 } | |
159 | |
160 TEST(buffered_file_test, move_assignment_closes_file) { | |
161 buffered_file bf = open_buffered_file(); | |
162 buffered_file bf2 = open_buffered_file(); | |
163 int old_fd = bf2.descriptor(); | |
164 bf2 = std::move(bf); | |
165 EXPECT_TRUE(isclosed(old_fd)); | |
166 } | |
167 | |
168 TEST(buffered_file_test, move_from_temporary_in_ctor) { | |
169 FILE* fp = nullptr; | |
170 buffered_file f = open_buffered_file(&fp); | |
171 EXPECT_EQ(fp, f.get()); | |
172 } | |
173 | |
174 TEST(buffered_file_test, move_from_temporary_in_assignment) { | |
175 FILE* fp = nullptr; | |
176 auto f = buffered_file(); | |
177 f = open_buffered_file(&fp); | |
178 EXPECT_EQ(fp, f.get()); | |
179 } | |
180 | |
181 TEST(buffered_file_test, move_from_temporary_in_assignment_closes_file) { | |
182 buffered_file f = open_buffered_file(); | |
183 int old_fd = f.descriptor(); | |
184 f = open_buffered_file(); | |
185 EXPECT_TRUE(isclosed(old_fd)); | |
186 } | |
187 | |
188 TEST(buffered_file_test, close_file_in_dtor) { | |
189 int fd = 0; | |
190 { | |
191 buffered_file f = open_buffered_file(); | |
192 fd = f.descriptor(); | |
193 } | |
194 EXPECT_TRUE(isclosed(fd)); | |
195 } | |
196 | |
197 TEST(buffered_file_test, close_error_in_dtor) { | |
198 auto f = | |
199 std::unique_ptr<buffered_file>(new buffered_file(open_buffered_file())); | |
200 EXPECT_WRITE( | |
201 stderr, | |
202 { | |
203 // The close function must be called inside EXPECT_WRITE, | |
204 // otherwise the system may recycle closed file descriptor when | |
205 // redirecting the output in EXPECT_STDERR and the second close | |
206 // will break output redirection. | |
207 FMT_POSIX(close(f->descriptor())); | |
208 SUPPRESS_ASSERT(f.reset(nullptr)); | |
209 }, | |
210 system_error_message(EBADF, "cannot close file") + "\n"); | |
211 } | |
212 | |
213 TEST(buffered_file_test, close) { | |
214 buffered_file f = open_buffered_file(); | |
215 int fd = f.descriptor(); | |
216 f.close(); | |
217 EXPECT_TRUE(f.get() == nullptr); | |
218 EXPECT_TRUE(isclosed(fd)); | |
219 } | |
220 | |
221 TEST(buffered_file_test, close_error) { | |
222 buffered_file f = open_buffered_file(); | |
223 FMT_POSIX(close(f.descriptor())); | |
224 EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); | |
225 EXPECT_TRUE(f.get() == nullptr); | |
226 } | |
227 | |
228 TEST(buffered_file_test, descriptor) { | |
229 auto f = open_buffered_file(); | |
230 EXPECT_TRUE(f.descriptor() != -1); | |
231 file copy = file::dup(f.descriptor()); | |
232 EXPECT_READ(copy, file_content); | |
233 } | |
234 | |
235 TEST(ostream_test, move) { | |
236 fmt::ostream out = fmt::output_file("test-file"); | |
237 fmt::ostream moved(std::move(out)); | |
238 moved.print("hello"); | |
239 } | |
240 | |
241 TEST(ostream_test, move_while_holding_data) { | |
242 { | |
243 fmt::ostream out = fmt::output_file("test-file"); | |
244 out.print("Hello, "); | |
245 fmt::ostream moved(std::move(out)); | |
246 moved.print("world!\n"); | |
247 } | |
248 { | |
249 file in("test-file", file::RDONLY); | |
250 EXPECT_READ(in, "Hello, world!\n"); | |
251 } | |
252 } | |
253 | |
254 TEST(ostream_test, print) { | |
255 fmt::ostream out = fmt::output_file("test-file"); | |
256 out.print("The answer is {}.\n", | |
257 fmt::join(std::initializer_list<int>{42}, ", ")); | |
258 out.close(); | |
259 file in("test-file", file::RDONLY); | |
260 EXPECT_READ(in, "The answer is 42.\n"); | |
261 } | |
262 | |
263 TEST(ostream_test, buffer_boundary) { | |
264 auto str = std::string(4096, 'x'); | |
265 fmt::ostream out = fmt::output_file("test-file"); | |
266 out.print("{}", str); | |
267 out.print("{}", str); | |
268 out.close(); | |
269 file in("test-file", file::RDONLY); | |
270 EXPECT_READ(in, str + str); | |
271 } | |
272 | |
273 TEST(ostream_test, buffer_size) { | |
274 fmt::ostream out = fmt::output_file("test-file", fmt::buffer_size = 1); | |
275 out.print("{}", "foo"); | |
276 out.close(); | |
277 file in("test-file", file::RDONLY); | |
278 EXPECT_READ(in, "foo"); | |
279 } | |
280 | |
281 TEST(ostream_test, truncate) { | |
282 { | |
283 fmt::ostream out = fmt::output_file("test-file"); | |
284 out.print("0123456789"); | |
285 } | |
286 { | |
287 fmt::ostream out = fmt::output_file("test-file"); | |
288 out.print("foo"); | |
289 } | |
290 file in("test-file", file::RDONLY); | |
291 EXPECT_EQ("foo", read(in, 4)); | |
292 } | |
293 | |
294 TEST(ostream_test, flush) { | |
295 auto out = fmt::output_file("test-file"); | |
296 out.print("x"); | |
297 out.flush(); | |
298 auto in = fmt::file("test-file", file::RDONLY); | |
299 EXPECT_READ(in, "x"); | |
300 } | |
301 | |
302 TEST(file_test, default_ctor) { | |
303 file f; | |
304 EXPECT_EQ(-1, f.descriptor()); | |
305 } | |
306 | |
307 TEST(file_test, open_buffered_file_in_ctor) { | |
308 FILE* fp = safe_fopen("test-file", "w"); | |
309 std::fputs(file_content, fp); | |
310 std::fclose(fp); | |
311 file f("test-file", file::RDONLY); | |
312 // Check if the file is open by reading one character from it. | |
313 char buffer; | |
314 bool isopen = FMT_POSIX(read(f.descriptor(), &buffer, 1)) == 1; | |
315 ASSERT_TRUE(isopen); | |
316 } | |
317 | |
318 TEST(file_test, open_buffered_file_error) { | |
319 EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY), ENOENT, | |
320 "cannot open file nonexistent"); | |
321 } | |
322 | |
323 TEST(file_test, move_ctor) { | |
324 file f = open_file(); | |
325 int fd = f.descriptor(); | |
326 EXPECT_NE(-1, fd); | |
327 file f2(std::move(f)); | |
328 EXPECT_EQ(fd, f2.descriptor()); | |
329 EXPECT_EQ(-1, f.descriptor()); | |
330 } | |
331 | |
332 TEST(file_test, move_assignment) { | |
333 file f = open_file(); | |
334 int fd = f.descriptor(); | |
335 EXPECT_NE(-1, fd); | |
336 file f2; | |
337 f2 = std::move(f); | |
338 EXPECT_EQ(fd, f2.descriptor()); | |
339 EXPECT_EQ(-1, f.descriptor()); | |
340 } | |
341 | |
342 TEST(file_test, move_assignment_closes_file) { | |
343 file f = open_file(); | |
344 file f2 = open_file(); | |
345 int old_fd = f2.descriptor(); | |
346 f2 = std::move(f); | |
347 EXPECT_TRUE(isclosed(old_fd)); | |
348 } | |
349 | |
350 file open_buffered_file(int& fd) { | |
351 file f = open_file(); | |
352 fd = f.descriptor(); | |
353 return f; | |
354 } | |
355 | |
356 TEST(file_test, move_from_temporary_in_ctor) { | |
357 int fd = 0xdead; | |
358 file f(open_buffered_file(fd)); | |
359 EXPECT_EQ(fd, f.descriptor()); | |
360 } | |
361 | |
362 TEST(file_test, move_from_temporary_in_assignment) { | |
363 int fd = 0xdead; | |
364 file f; | |
365 f = open_buffered_file(fd); | |
366 EXPECT_EQ(fd, f.descriptor()); | |
367 } | |
368 | |
369 TEST(file_test, move_from_temporary_in_assignment_closes_file) { | |
370 int fd = 0xdead; | |
371 file f = open_file(); | |
372 int old_fd = f.descriptor(); | |
373 f = open_buffered_file(fd); | |
374 EXPECT_TRUE(isclosed(old_fd)); | |
375 } | |
376 | |
377 TEST(file_test, close_file_in_dtor) { | |
378 int fd = 0; | |
379 { | |
380 file f = open_file(); | |
381 fd = f.descriptor(); | |
382 } | |
383 EXPECT_TRUE(isclosed(fd)); | |
384 } | |
385 | |
386 TEST(file_test, close_error_in_dtor) { | |
387 std::unique_ptr<file> f(new file(open_file())); | |
388 EXPECT_WRITE( | |
389 stderr, | |
390 { | |
391 // The close function must be called inside EXPECT_WRITE, | |
392 // otherwise the system may recycle closed file descriptor when | |
393 // redirecting the output in EXPECT_STDERR and the second close | |
394 // will break output redirection. | |
395 FMT_POSIX(close(f->descriptor())); | |
396 SUPPRESS_ASSERT(f.reset(nullptr)); | |
397 }, | |
398 system_error_message(EBADF, "cannot close file") + "\n"); | |
399 } | |
400 | |
401 TEST(file_test, close) { | |
402 file f = open_file(); | |
403 int fd = f.descriptor(); | |
404 f.close(); | |
405 EXPECT_EQ(-1, f.descriptor()); | |
406 EXPECT_TRUE(isclosed(fd)); | |
407 } | |
408 | |
409 TEST(file_test, close_error) { | |
410 file f = open_file(); | |
411 FMT_POSIX(close(f.descriptor())); | |
412 EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); | |
413 EXPECT_EQ(-1, f.descriptor()); | |
414 } | |
415 | |
416 TEST(file_test, read) { | |
417 file f = open_file(); | |
418 EXPECT_READ(f, file_content); | |
419 } | |
420 | |
421 TEST(file_test, read_error) { | |
422 file f("test-file", file::WRONLY); | |
423 char buf; | |
424 // We intentionally read from a file opened in the write-only mode to | |
425 // cause error. | |
426 EXPECT_SYSTEM_ERROR(f.read(&buf, 1), EBADF, "cannot read from file"); | |
427 } | |
428 | |
429 TEST(file_test, write) { | |
430 file read_end, write_end; | |
431 file::pipe(read_end, write_end); | |
432 write(write_end, "test"); | |
433 write_end.close(); | |
434 EXPECT_READ(read_end, "test"); | |
435 } | |
436 | |
437 TEST(file_test, write_error) { | |
438 file f("test-file", file::RDONLY); | |
439 // We intentionally write to a file opened in the read-only mode to | |
440 // cause error. | |
441 EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file"); | |
442 } | |
443 | |
444 TEST(file_test, dup) { | |
445 file f = open_file(); | |
446 file copy = file::dup(f.descriptor()); | |
447 EXPECT_NE(f.descriptor(), copy.descriptor()); | |
448 EXPECT_EQ(file_content, read(copy, std::strlen(file_content))); | |
449 } | |
450 | |
451 # ifndef __COVERITY__ | |
452 TEST(file_test, dup_error) { | |
453 int value = -1; | |
454 EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value), EBADF, | |
455 "cannot duplicate file descriptor -1"); | |
456 } | |
457 # endif | |
458 | |
459 TEST(file_test, dup2) { | |
460 file f = open_file(); | |
461 file copy = open_file(); | |
462 f.dup2(copy.descriptor()); | |
463 EXPECT_NE(f.descriptor(), copy.descriptor()); | |
464 EXPECT_READ(copy, file_content); | |
465 } | |
466 | |
467 TEST(file_test, dup2_error) { | |
468 file f = open_file(); | |
469 EXPECT_SYSTEM_ERROR_NOASSERT( | |
470 f.dup2(-1), EBADF, | |
471 fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor())); | |
472 } | |
473 | |
474 TEST(file_test, dup2_noexcept) { | |
475 file f = open_file(); | |
476 file copy = open_file(); | |
477 std::error_code ec; | |
478 f.dup2(copy.descriptor(), ec); | |
479 EXPECT_EQ(ec.value(), 0); | |
480 EXPECT_NE(f.descriptor(), copy.descriptor()); | |
481 EXPECT_READ(copy, file_content); | |
482 } | |
483 | |
484 TEST(file_test, dup2_noexcept_error) { | |
485 file f = open_file(); | |
486 std::error_code ec; | |
487 SUPPRESS_ASSERT(f.dup2(-1, ec)); | |
488 EXPECT_EQ(EBADF, ec.value()); | |
489 } | |
490 | |
491 TEST(file_test, pipe) { | |
492 file read_end, write_end; | |
493 file::pipe(read_end, write_end); | |
494 EXPECT_NE(-1, read_end.descriptor()); | |
495 EXPECT_NE(-1, write_end.descriptor()); | |
496 write(write_end, "test"); | |
497 EXPECT_READ(read_end, "test"); | |
498 } | |
499 | |
500 TEST(file_test, fdopen) { | |
501 file read_end, write_end; | |
502 file::pipe(read_end, write_end); | |
503 int read_fd = read_end.descriptor(); | |
504 EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get()))); | |
505 } | |
506 #endif // FMT_USE_FCNTL |