]> git.proxmox.com Git - ceph.git/blame - ceph/src/arrow/cpp/src/arrow/util/io_util_test.cc
import quincy 17.2.0
[ceph.git] / ceph / src / arrow / cpp / src / arrow / util / io_util_test.cc
CommitLineData
1d09f67e
TL
1// Licensed to the Apache Software Foundation (ASF) under one
2// std::unique_ptr<TemporaryDir> temp_dir;
3// or more contributor license agreements. See the NOTICE file
4// distributed with this work for additional information
5// regarding copyright ownership. The ASF licenses this file
6// to you under the Apache License, Version 2.0 (the
7// "License"); you may not use this file except in compliance
8// with the License. You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing,
13// software distributed under the License is distributed on an
14// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15// KIND, either express or implied. See the License for the
16// specific language governing permissions and limitations
17// under the License.
18
19#include <algorithm>
20#include <atomic>
21#include <cerrno>
22#include <limits>
23#include <sstream>
24#include <vector>
25
26#include <signal.h>
27
28#ifndef _WIN32
29#include <pthread.h>
30#endif
31
32#include <gmock/gmock-matchers.h>
33#include <gtest/gtest.h>
34
35#include "arrow/testing/gtest_util.h"
36#include "arrow/util/bit_util.h"
37#include "arrow/util/io_util.h"
38#include "arrow/util/logging.h"
39#include "arrow/util/windows_compatibility.h"
40#include "arrow/util/windows_fixup.h"
41
42namespace arrow {
43namespace internal {
44
45void AssertExists(const PlatformFilename& path) {
46 bool exists = false;
47 ASSERT_OK_AND_ASSIGN(exists, FileExists(path));
48 ASSERT_TRUE(exists) << "Path '" << path.ToString() << "' doesn't exist";
49}
50
51void AssertNotExists(const PlatformFilename& path) {
52 bool exists = true;
53 ASSERT_OK_AND_ASSIGN(exists, FileExists(path));
54 ASSERT_FALSE(exists) << "Path '" << path.ToString() << "' exists";
55}
56
57void TouchFile(const PlatformFilename& path) {
58 int fd = -1;
59 ASSERT_OK_AND_ASSIGN(fd, FileOpenWritable(path));
60 ASSERT_OK(FileClose(fd));
61}
62
63TEST(ErrnoFromStatus, Basics) {
64 Status st;
65 st = Status::OK();
66 ASSERT_EQ(ErrnoFromStatus(st), 0);
67 st = Status::KeyError("foo");
68 ASSERT_EQ(ErrnoFromStatus(st), 0);
69 st = Status::IOError("foo");
70 ASSERT_EQ(ErrnoFromStatus(st), 0);
71 st = StatusFromErrno(EINVAL, StatusCode::KeyError, "foo");
72 ASSERT_EQ(ErrnoFromStatus(st), EINVAL);
73 st = IOErrorFromErrno(EPERM, "foo");
74 ASSERT_EQ(ErrnoFromStatus(st), EPERM);
75 st = IOErrorFromErrno(6789, "foo");
76 ASSERT_EQ(ErrnoFromStatus(st), 6789);
77
78 st = CancelledFromSignal(SIGINT, "foo");
79 ASSERT_EQ(ErrnoFromStatus(st), 0);
80}
81
82TEST(SignalFromStatus, Basics) {
83 Status st;
84 st = Status::OK();
85 ASSERT_EQ(SignalFromStatus(st), 0);
86 st = Status::KeyError("foo");
87 ASSERT_EQ(SignalFromStatus(st), 0);
88 st = Status::Cancelled("foo");
89 ASSERT_EQ(SignalFromStatus(st), 0);
90 st = StatusFromSignal(SIGINT, StatusCode::KeyError, "foo");
91 ASSERT_EQ(SignalFromStatus(st), SIGINT);
92 ASSERT_EQ(st.ToString(),
93 "Key error: foo. Detail: received signal " + std::to_string(SIGINT));
94 st = CancelledFromSignal(SIGINT, "bar");
95 ASSERT_EQ(SignalFromStatus(st), SIGINT);
96 ASSERT_EQ(st.ToString(),
97 "Cancelled: bar. Detail: received signal " + std::to_string(SIGINT));
98
99 st = IOErrorFromErrno(EINVAL, "foo");
100 ASSERT_EQ(SignalFromStatus(st), 0);
101}
102
103TEST(GetPageSize, Basics) {
104 const auto page_size = GetPageSize();
105 ASSERT_GE(page_size, 4096);
106 // It's a power of 2
107 ASSERT_EQ((page_size - 1) & page_size, 0);
108}
109
110TEST(MemoryAdviseWillNeed, Basics) {
111 ASSERT_OK_AND_ASSIGN(auto buf1, AllocateBuffer(8192));
112 ASSERT_OK_AND_ASSIGN(auto buf2, AllocateBuffer(1024 * 1024));
113
114 const auto addr1 = buf1->mutable_data();
115 const auto size1 = static_cast<size_t>(buf1->size());
116 const auto addr2 = buf2->mutable_data();
117 const auto size2 = static_cast<size_t>(buf2->size());
118
119 ASSERT_OK(MemoryAdviseWillNeed({}));
120 ASSERT_OK(MemoryAdviseWillNeed({{addr1, size1}, {addr2, size2}}));
121 ASSERT_OK(MemoryAdviseWillNeed({{addr1 + 1, size1 - 1}, {addr2 + 4095, size2 - 4095}}));
122 ASSERT_OK(MemoryAdviseWillNeed({{addr1, 13}, {addr2, 1}}));
123 ASSERT_OK(MemoryAdviseWillNeed({{addr1, 0}, {addr2 + 1, 0}}));
124
125 // Should probably fail
126 // (but on Windows, MemoryAdviseWillNeed can be a no-op)
127#ifndef _WIN32
128 ASSERT_RAISES(IOError,
129 MemoryAdviseWillNeed({{nullptr, std::numeric_limits<size_t>::max()}}));
130#endif
131}
132
133#if _WIN32
134TEST(WinErrorFromStatus, Basics) {
135 Status st;
136 st = Status::OK();
137 ASSERT_EQ(WinErrorFromStatus(st), 0);
138 st = Status::KeyError("foo");
139 ASSERT_EQ(WinErrorFromStatus(st), 0);
140 st = Status::IOError("foo");
141 ASSERT_EQ(WinErrorFromStatus(st), 0);
142 st = StatusFromWinError(ERROR_FILE_NOT_FOUND, StatusCode::KeyError, "foo");
143 ASSERT_EQ(WinErrorFromStatus(st), ERROR_FILE_NOT_FOUND);
144 st = IOErrorFromWinError(ERROR_ACCESS_DENIED, "foo");
145 ASSERT_EQ(WinErrorFromStatus(st), ERROR_ACCESS_DENIED);
146 st = IOErrorFromWinError(6789, "foo");
147 ASSERT_EQ(WinErrorFromStatus(st), 6789);
148}
149#endif
150
151TEST(PlatformFilename, RoundtripAscii) {
152 PlatformFilename fn;
153 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("a/b"));
154 ASSERT_EQ(fn.ToString(), "a/b");
155#if _WIN32
156 ASSERT_EQ(fn.ToNative(), L"a\\b");
157#else
158 ASSERT_EQ(fn.ToNative(), "a/b");
159#endif
160}
161
162TEST(PlatformFilename, RoundtripUtf8) {
163 PlatformFilename fn;
164 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("h\xc3\xa9h\xc3\xa9"));
165 ASSERT_EQ(fn.ToString(), "h\xc3\xa9h\xc3\xa9");
166#if _WIN32
167 ASSERT_EQ(fn.ToNative(), L"h\u00e9h\u00e9");
168#else
169 ASSERT_EQ(fn.ToNative(), "h\xc3\xa9h\xc3\xa9");
170#endif
171}
172
173#if _WIN32
174TEST(PlatformFilename, Separators) {
175 PlatformFilename fn;
176 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("C:/foo/bar"));
177 ASSERT_EQ(fn.ToString(), "C:/foo/bar");
178 ASSERT_EQ(fn.ToNative(), L"C:\\foo\\bar");
179
180 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("C:\\foo\\bar"));
181 ASSERT_EQ(fn.ToString(), "C:/foo/bar");
182 ASSERT_EQ(fn.ToNative(), L"C:\\foo\\bar");
183}
184#endif
185
186TEST(PlatformFilename, Invalid) {
187 std::string s = "foo";
188 s += '\x00';
189 ASSERT_RAISES(Invalid, PlatformFilename::FromString(s));
190}
191
192TEST(PlatformFilename, Join) {
193 PlatformFilename fn, joined;
194 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("a/b"));
195 ASSERT_OK_AND_ASSIGN(joined, fn.Join("c/d"));
196 ASSERT_EQ(joined.ToString(), "a/b/c/d");
197#if _WIN32
198 ASSERT_EQ(joined.ToNative(), L"a\\b\\c\\d");
199#else
200 ASSERT_EQ(joined.ToNative(), "a/b/c/d");
201#endif
202
203 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("a/b/"));
204 ASSERT_OK_AND_ASSIGN(joined, fn.Join("c/d"));
205 ASSERT_EQ(joined.ToString(), "a/b/c/d");
206#if _WIN32
207 ASSERT_EQ(joined.ToNative(), L"a\\b\\c\\d");
208#else
209 ASSERT_EQ(joined.ToNative(), "a/b/c/d");
210#endif
211
212 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString(""));
213 ASSERT_OK_AND_ASSIGN(joined, fn.Join("c/d"));
214 ASSERT_EQ(joined.ToString(), "c/d");
215#if _WIN32
216 ASSERT_EQ(joined.ToNative(), L"c\\d");
217#else
218 ASSERT_EQ(joined.ToNative(), "c/d");
219#endif
220
221#if _WIN32
222 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("a\\b"));
223 ASSERT_OK_AND_ASSIGN(joined, fn.Join("c\\d"));
224 ASSERT_EQ(joined.ToString(), "a/b/c/d");
225 ASSERT_EQ(joined.ToNative(), L"a\\b\\c\\d");
226
227 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("a\\b\\"));
228 ASSERT_OK_AND_ASSIGN(joined, fn.Join("c\\d"));
229 ASSERT_EQ(joined.ToString(), "a/b/c/d");
230 ASSERT_EQ(joined.ToNative(), L"a\\b\\c\\d");
231#endif
232}
233
234TEST(PlatformFilename, JoinInvalid) {
235 PlatformFilename fn;
236 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("a/b"));
237 std::string s = "foo";
238 s += '\x00';
239 ASSERT_RAISES(Invalid, fn.Join(s));
240}
241
242TEST(PlatformFilename, Parent) {
243 PlatformFilename fn;
244
245 // Relative
246 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("ab/cd"));
247 ASSERT_EQ(fn.ToString(), "ab/cd");
248 fn = fn.Parent();
249 ASSERT_EQ(fn.ToString(), "ab");
250 fn = fn.Parent();
251 ASSERT_EQ(fn.ToString(), "ab");
252#if _WIN32
253 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("ab/cd\\ef"));
254 ASSERT_EQ(fn.ToString(), "ab/cd/ef");
255 fn = fn.Parent();
256 ASSERT_EQ(fn.ToString(), "ab/cd");
257 fn = fn.Parent();
258 ASSERT_EQ(fn.ToString(), "ab");
259 fn = fn.Parent();
260 ASSERT_EQ(fn.ToString(), "ab");
261#endif
262
263 // Absolute
264 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("/ab/cd/ef"));
265 ASSERT_EQ(fn.ToString(), "/ab/cd/ef");
266 fn = fn.Parent();
267 ASSERT_EQ(fn.ToString(), "/ab/cd");
268 fn = fn.Parent();
269 ASSERT_EQ(fn.ToString(), "/ab");
270 fn = fn.Parent();
271 ASSERT_EQ(fn.ToString(), "/");
272 fn = fn.Parent();
273 ASSERT_EQ(fn.ToString(), "/");
274#if _WIN32
275 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("\\ab\\cd/ef"));
276 ASSERT_EQ(fn.ToString(), "/ab/cd/ef");
277 fn = fn.Parent();
278 ASSERT_EQ(fn.ToString(), "/ab/cd");
279 fn = fn.Parent();
280 ASSERT_EQ(fn.ToString(), "/ab");
281 fn = fn.Parent();
282 ASSERT_EQ(fn.ToString(), "/");
283 fn = fn.Parent();
284 ASSERT_EQ(fn.ToString(), "/");
285#endif
286
287 // Empty
288 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString(""));
289 ASSERT_EQ(fn.ToString(), "");
290 fn = fn.Parent();
291 ASSERT_EQ(fn.ToString(), "");
292
293 // Multiple separators, relative
294 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("ab//cd///ef"));
295 ASSERT_EQ(fn.ToString(), "ab//cd///ef");
296 fn = fn.Parent();
297 ASSERT_EQ(fn.ToString(), "ab//cd");
298 fn = fn.Parent();
299 ASSERT_EQ(fn.ToString(), "ab");
300 fn = fn.Parent();
301 ASSERT_EQ(fn.ToString(), "ab");
302#if _WIN32
303 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("ab\\\\cd\\\\\\ef"));
304 ASSERT_EQ(fn.ToString(), "ab//cd///ef");
305 fn = fn.Parent();
306 ASSERT_EQ(fn.ToString(), "ab//cd");
307 fn = fn.Parent();
308 ASSERT_EQ(fn.ToString(), "ab");
309 fn = fn.Parent();
310 ASSERT_EQ(fn.ToString(), "ab");
311#endif
312
313 // Multiple separators, absolute
314 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("//ab//cd///ef"));
315 ASSERT_EQ(fn.ToString(), "//ab//cd///ef");
316 fn = fn.Parent();
317 ASSERT_EQ(fn.ToString(), "//ab//cd");
318 fn = fn.Parent();
319 ASSERT_EQ(fn.ToString(), "//ab");
320 fn = fn.Parent();
321 ASSERT_EQ(fn.ToString(), "//");
322 fn = fn.Parent();
323 ASSERT_EQ(fn.ToString(), "//");
324#if _WIN32
325 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("\\\\ab\\cd\\ef"));
326 ASSERT_EQ(fn.ToString(), "//ab/cd/ef");
327 fn = fn.Parent();
328 ASSERT_EQ(fn.ToString(), "//ab/cd");
329 fn = fn.Parent();
330 ASSERT_EQ(fn.ToString(), "//ab");
331 fn = fn.Parent();
332 ASSERT_EQ(fn.ToString(), "//");
333 fn = fn.Parent();
334 ASSERT_EQ(fn.ToString(), "//");
335#endif
336
337 // Trailing slashes
338 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("/ab/cd/ef/"));
339 ASSERT_EQ(fn.ToString(), "/ab/cd/ef/");
340 fn = fn.Parent();
341 ASSERT_EQ(fn.ToString(), "/ab/cd");
342 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("/ab/cd/ef//"));
343 ASSERT_EQ(fn.ToString(), "/ab/cd/ef//");
344 fn = fn.Parent();
345 ASSERT_EQ(fn.ToString(), "/ab/cd");
346 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("ab/"));
347 ASSERT_EQ(fn.ToString(), "ab/");
348 fn = fn.Parent();
349 ASSERT_EQ(fn.ToString(), "ab/");
350 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("ab//"));
351 ASSERT_EQ(fn.ToString(), "ab//");
352 fn = fn.Parent();
353 ASSERT_EQ(fn.ToString(), "ab//");
354#if _WIN32
355 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("\\ab\\cd\\ef\\"));
356 ASSERT_EQ(fn.ToString(), "/ab/cd/ef/");
357 fn = fn.Parent();
358 ASSERT_EQ(fn.ToString(), "/ab/cd");
359 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("\\ab\\cd\\ef\\\\"));
360 ASSERT_EQ(fn.ToString(), "/ab/cd/ef//");
361 fn = fn.Parent();
362 ASSERT_EQ(fn.ToString(), "/ab/cd");
363 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("ab\\"));
364 ASSERT_EQ(fn.ToString(), "ab/");
365 fn = fn.Parent();
366 ASSERT_EQ(fn.ToString(), "ab/");
367 ASSERT_OK_AND_ASSIGN(fn, PlatformFilename::FromString("ab\\\\"));
368 ASSERT_EQ(fn.ToString(), "ab//");
369 fn = fn.Parent();
370 ASSERT_EQ(fn.ToString(), "ab//");
371#endif
372}
373
374TEST(CreateDirDeleteDir, Basics) {
375 std::unique_ptr<TemporaryDir> temp_dir;
376 ASSERT_OK_AND_ASSIGN(temp_dir, TemporaryDir::Make("deletedirtest-"));
377 const std::string BASE =
378 temp_dir->path().Join("xxx-io-util-test-dir2").ValueOrDie().ToString();
379 bool created, deleted;
380 PlatformFilename parent, child, child_file;
381
382 ASSERT_OK_AND_ASSIGN(parent, PlatformFilename::FromString(BASE));
383 ASSERT_EQ(parent.ToString(), BASE);
384
385 // Make sure the directory doesn't exist already
386 ARROW_UNUSED(DeleteDirTree(parent));
387
388 AssertNotExists(parent);
389
390 ASSERT_OK_AND_ASSIGN(created, CreateDir(parent));
391 ASSERT_TRUE(created);
392 AssertExists(parent);
393 ASSERT_OK_AND_ASSIGN(created, CreateDir(parent));
394 ASSERT_FALSE(created); // already exists
395 AssertExists(parent);
396
397 ASSERT_OK_AND_ASSIGN(child, PlatformFilename::FromString(BASE + "/some-child"));
398 ASSERT_OK_AND_ASSIGN(created, CreateDir(child));
399 ASSERT_TRUE(created);
400 AssertExists(child);
401
402 ASSERT_OK_AND_ASSIGN(child_file, PlatformFilename::FromString(BASE + "/some-file"));
403 TouchFile(child_file);
404 EXPECT_RAISES_WITH_MESSAGE_THAT(
405 IOError, ::testing::HasSubstr("non-directory entry exists"), CreateDir(child_file));
406
407 ASSERT_OK_AND_ASSIGN(deleted, DeleteDirTree(parent));
408 ASSERT_TRUE(deleted);
409 AssertNotExists(parent);
410 AssertNotExists(child);
411
412 // Parent is deleted, cannot create child again
413 ASSERT_RAISES(IOError, CreateDir(child));
414
415 // It's not an error to call DeleteDirTree on a nonexistent path.
416 ASSERT_OK_AND_ASSIGN(deleted, DeleteDirTree(parent));
417 ASSERT_FALSE(deleted);
418 // ... unless asked so
419 auto status = DeleteDirTree(parent, /*allow_not_found=*/false).status();
420 ASSERT_RAISES(IOError, status);
421#ifdef _WIN32
422 ASSERT_EQ(WinErrorFromStatus(status), ERROR_FILE_NOT_FOUND);
423#else
424 ASSERT_EQ(ErrnoFromStatus(status), ENOENT);
425#endif
426}
427
428TEST(DeleteDirContents, Basics) {
429 std::unique_ptr<TemporaryDir> temp_dir;
430 ASSERT_OK_AND_ASSIGN(temp_dir, TemporaryDir::Make("deletedirtest-"));
431 const std::string BASE =
432 temp_dir->path().Join("xxx-io-util-test-dir2").ValueOrDie().ToString();
433 bool created, deleted;
434 PlatformFilename parent, child1, child2;
435
436 ASSERT_OK_AND_ASSIGN(parent, PlatformFilename::FromString(BASE));
437 ASSERT_EQ(parent.ToString(), BASE);
438
439 // Make sure the directory doesn't exist already
440 ARROW_UNUSED(DeleteDirTree(parent));
441
442 AssertNotExists(parent);
443
444 // Create the parent, a child dir and a child file
445 ASSERT_OK_AND_ASSIGN(created, CreateDir(parent));
446 ASSERT_TRUE(created);
447 ASSERT_OK_AND_ASSIGN(child1, PlatformFilename::FromString(BASE + "/child-dir"));
448 ASSERT_OK_AND_ASSIGN(child2, PlatformFilename::FromString(BASE + "/child-file"));
449 ASSERT_OK_AND_ASSIGN(created, CreateDir(child1));
450 ASSERT_TRUE(created);
451 TouchFile(child2);
452 AssertExists(child1);
453 AssertExists(child2);
454
455 // Cannot call DeleteDirContents on a file
456 ASSERT_RAISES(IOError, DeleteDirContents(child2));
457 AssertExists(child2);
458
459 ASSERT_OK_AND_ASSIGN(deleted, DeleteDirContents(parent));
460 ASSERT_TRUE(deleted);
461 AssertExists(parent);
462 AssertNotExists(child1);
463 AssertNotExists(child2);
464 ASSERT_OK_AND_ASSIGN(deleted, DeleteDirContents(parent));
465 ASSERT_TRUE(deleted);
466 AssertExists(parent);
467
468 // It's not an error to call DeleteDirContents on a nonexistent path.
469 ASSERT_OK_AND_ASSIGN(deleted, DeleteDirContents(child1));
470 ASSERT_FALSE(deleted);
471 // ... unless asked so
472 auto status = DeleteDirContents(child1, /*allow_not_found=*/false).status();
473 ASSERT_RAISES(IOError, status);
474#ifdef _WIN32
475 ASSERT_EQ(WinErrorFromStatus(status), ERROR_FILE_NOT_FOUND);
476#else
477 ASSERT_EQ(ErrnoFromStatus(status), ENOENT);
478#endif
479
480 // Now actually delete the test directory
481 ASSERT_OK_AND_ASSIGN(deleted, DeleteDirTree(parent));
482 ASSERT_TRUE(deleted);
483}
484
485TEST(TemporaryDir, Basics) {
486 std::unique_ptr<TemporaryDir> temp_dir;
487 PlatformFilename fn;
488
489 ASSERT_OK_AND_ASSIGN(temp_dir, TemporaryDir::Make("some-prefix-"));
490 fn = temp_dir->path();
491 // Path has a trailing separator, for convenience
492 ASSERT_EQ(fn.ToString().back(), '/');
493#if defined(_WIN32)
494 ASSERT_EQ(fn.ToNative().back(), L'\\');
495#else
496 ASSERT_EQ(fn.ToNative().back(), '/');
497#endif
498 AssertExists(fn);
499 ASSERT_NE(fn.ToString().find("some-prefix-"), std::string::npos);
500
501 // Create child contents to check that they're cleaned up at the end
502#if defined(_WIN32)
503 PlatformFilename child(fn.ToNative() + L"some-child");
504#else
505 PlatformFilename child(fn.ToNative() + "some-child");
506#endif
507 ASSERT_OK(CreateDir(child));
508 AssertExists(child);
509
510 temp_dir.reset();
511 AssertNotExists(fn);
512 AssertNotExists(child);
513}
514
515TEST(CreateDirTree, Basics) {
516 std::unique_ptr<TemporaryDir> temp_dir;
517 PlatformFilename fn;
518 bool created;
519
520 ASSERT_OK_AND_ASSIGN(temp_dir, TemporaryDir::Make("io-util-test-"));
521
522 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB/CD"));
523 ASSERT_OK_AND_ASSIGN(created, CreateDirTree(fn));
524 ASSERT_TRUE(created);
525 ASSERT_OK_AND_ASSIGN(created, CreateDirTree(fn));
526 ASSERT_FALSE(created);
527
528 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB"));
529 ASSERT_OK_AND_ASSIGN(created, CreateDirTree(fn));
530 ASSERT_FALSE(created);
531
532 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("EF"));
533 ASSERT_OK_AND_ASSIGN(created, CreateDirTree(fn));
534 ASSERT_TRUE(created);
535
536 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB/file"));
537 TouchFile(fn);
538 EXPECT_RAISES_WITH_MESSAGE_THAT(
539 IOError, ::testing::HasSubstr("non-directory entry exists"), CreateDirTree(fn));
540
541 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB/file/sub"));
542 ASSERT_RAISES(IOError, CreateDirTree(fn));
543}
544
545TEST(ListDir, Basics) {
546 std::unique_ptr<TemporaryDir> temp_dir;
547 PlatformFilename fn;
548 std::vector<PlatformFilename> entries;
549
550 auto check_entries = [](const std::vector<PlatformFilename>& entries,
551 std::vector<std::string> expected) -> void {
552 std::vector<std::string> actual(entries.size());
553 std::transform(entries.begin(), entries.end(), actual.begin(),
554 [](const PlatformFilename& fn) { return fn.ToString(); });
555 // Sort results for deterministic testing
556 std::sort(actual.begin(), actual.end());
557 ASSERT_EQ(actual, expected);
558 };
559
560 ASSERT_OK_AND_ASSIGN(temp_dir, TemporaryDir::Make("io-util-test-"));
561
562 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB/CD"));
563 ASSERT_OK(CreateDirTree(fn));
564 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB/EF/GH"));
565 ASSERT_OK(CreateDirTree(fn));
566 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB/ghi.txt"));
567 TouchFile(fn);
568
569 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB"));
570 ASSERT_OK_AND_ASSIGN(entries, ListDir(fn));
571 ASSERT_EQ(entries.size(), 3);
572 check_entries(entries, {"CD", "EF", "ghi.txt"});
573 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB/EF/GH"));
574 ASSERT_OK_AND_ASSIGN(entries, ListDir(fn));
575 check_entries(entries, {});
576
577 // Errors
578 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("nonexistent"));
579 ASSERT_RAISES(IOError, ListDir(fn));
580 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("AB/ghi.txt"));
581 ASSERT_RAISES(IOError, ListDir(fn));
582}
583
584TEST(DeleteFile, Basics) {
585 std::unique_ptr<TemporaryDir> temp_dir;
586 PlatformFilename fn;
587 bool deleted;
588
589 ASSERT_OK_AND_ASSIGN(temp_dir, TemporaryDir::Make("io-util-test-"));
590 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("test-file"));
591
592 AssertNotExists(fn);
593 TouchFile(fn);
594 AssertExists(fn);
595 ASSERT_OK_AND_ASSIGN(deleted, DeleteFile(fn));
596 ASSERT_TRUE(deleted);
597 AssertNotExists(fn);
598 ASSERT_OK_AND_ASSIGN(deleted, DeleteFile(fn));
599 ASSERT_FALSE(deleted);
600 AssertNotExists(fn);
601 auto status = DeleteFile(fn, /*allow_not_found=*/false).status();
602 ASSERT_RAISES(IOError, status);
603#ifdef _WIN32
604 ASSERT_EQ(WinErrorFromStatus(status), ERROR_FILE_NOT_FOUND);
605#else
606 ASSERT_EQ(ErrnoFromStatus(status), ENOENT);
607#endif
608
609 // Cannot call DeleteFile on directory
610 ASSERT_OK_AND_ASSIGN(fn, temp_dir->path().Join("test-temp_dir"));
611 ASSERT_OK(CreateDir(fn));
612 AssertExists(fn);
613 ASSERT_RAISES(IOError, DeleteFile(fn));
614}
615
616#ifndef __APPLE__
617TEST(FileUtils, LongPaths) {
618 // ARROW-8477: check using long file paths under Windows (> 260 characters).
619 bool created, deleted;
620#ifdef _WIN32
621 const char* kRegKeyName = R"(SYSTEM\CurrentControlSet\Control\FileSystem)";
622 const char* kRegValueName = "LongPathsEnabled";
623 DWORD value = 0;
624 DWORD size = sizeof(value);
625 LSTATUS status = RegGetValueA(HKEY_LOCAL_MACHINE, kRegKeyName, kRegValueName,
626 RRF_RT_REG_DWORD, NULL, &value, &size);
627 bool test_long_paths = (status == ERROR_SUCCESS && value == 1);
628 if (!test_long_paths) {
629 ARROW_LOG(WARNING)
630 << "Tests for accessing files with long path names have been disabled. "
631 << "To enable these tests, set the value of " << kRegValueName
632 << " in registry key \\HKEY_LOCAL_MACHINE\\" << kRegKeyName
633 << " to 1 on the test host.";
634 return;
635 }
636#endif
637
638 const std::string BASE = "xxx-io-util-test-dir-long";
639 PlatformFilename base_path, long_path, long_filename;
640 int fd = -1;
641 std::stringstream fs;
642 fs << BASE;
643 for (int i = 0; i < 64; ++i) {
644 fs << "/123456789ABCDEF";
645 }
646 ASSERT_OK_AND_ASSIGN(base_path,
647 PlatformFilename::FromString(BASE)); // long_path length > 1024
648 ASSERT_OK_AND_ASSIGN(
649 long_path, PlatformFilename::FromString(fs.str())); // long_path length > 1024
650 ASSERT_OK_AND_ASSIGN(created, CreateDirTree(long_path));
651 ASSERT_TRUE(created);
652 AssertExists(long_path);
653 ASSERT_OK_AND_ASSIGN(long_filename,
654 PlatformFilename::FromString(fs.str() + "/file.txt"));
655 TouchFile(long_filename);
656 AssertExists(long_filename);
657 fd = -1;
658 ASSERT_OK_AND_ASSIGN(fd, FileOpenReadable(long_filename));
659 ASSERT_OK(FileClose(fd));
660 ASSERT_OK_AND_ASSIGN(deleted, DeleteDirContents(long_path));
661 ASSERT_TRUE(deleted);
662 ASSERT_OK_AND_ASSIGN(deleted, DeleteDirTree(long_path));
663 ASSERT_TRUE(deleted);
664
665 // Now delete the whole test directory tree
666 ASSERT_OK_AND_ASSIGN(deleted, DeleteDirTree(base_path));
667 ASSERT_TRUE(deleted);
668}
669#endif
670
671static std::atomic<int> signal_received;
672
673static void handle_signal(int signum) {
674 ReinstateSignalHandler(signum, &handle_signal);
675 signal_received.store(signum);
676}
677
678TEST(SendSignal, Generic) {
679 signal_received.store(0);
680 SignalHandlerGuard guard(SIGINT, &handle_signal);
681
682 ASSERT_EQ(signal_received.load(), 0);
683 ASSERT_OK(SendSignal(SIGINT));
684 BusyWait(1.0, [&]() { return signal_received.load() != 0; });
685 ASSERT_EQ(signal_received.load(), SIGINT);
686
687 // Re-try (exercise ReinstateSignalHandler)
688 signal_received.store(0);
689 ASSERT_OK(SendSignal(SIGINT));
690 BusyWait(1.0, [&]() { return signal_received.load() != 0; });
691 ASSERT_EQ(signal_received.load(), SIGINT);
692}
693
694TEST(SendSignal, ToThread) {
695#ifdef _WIN32
696 uint64_t dummy_thread_id = 42;
697 ASSERT_RAISES(NotImplemented, SendSignalToThread(SIGINT, dummy_thread_id));
698#else
699 // Have to use a C-style cast because pthread_t can be a pointer *or* integer type
700 uint64_t thread_id = (uint64_t)(pthread_self()); // NOLINT readability-casting
701 signal_received.store(0);
702 SignalHandlerGuard guard(SIGINT, &handle_signal);
703
704 ASSERT_EQ(signal_received.load(), 0);
705 ASSERT_OK(SendSignalToThread(SIGINT, thread_id));
706 BusyWait(1.0, [&]() { return signal_received.load() != 0; });
707
708 ASSERT_EQ(signal_received.load(), SIGINT);
709#endif
710}
711
712} // namespace internal
713} // namespace arrow