• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / 1810

02 Nov 2023 05:09PM UTC coverage: 91.68% (+0.02%) from 91.661%
1810

push

Evergreen

web-flow
Loosen checks for uids on exfat fs (#7105)

92128 of 168856 branches covered (0.0%)

0 of 5 new or added lines in 1 file covered. (0.0%)

51 existing lines in 9 files now uncovered.

230753 of 251694 relevant lines covered (91.68%)

6261176.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.53
/test/test_file.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include "testsettings.hpp"
20
#ifdef TEST_FILE
21

22
#include <map>
23
#include <ostream>
24
#include <sstream>
25

26
#include <realm/util/file.hpp>
27
#include <realm/util/file_mapper.hpp>
28

29
#include "test.hpp"
30

31
#if REALM_PLATFORM_APPLE
32
#include <fcntl.h>
33
#include <sys/stat.h>
34
#include <unistd.h>
35
#endif
36

37
using namespace realm;
38
using namespace realm::util;
39
using namespace realm::test_util;
40

41

42
// Test independence and thread-safety
43
// -----------------------------------
44
//
45
// All tests must be thread safe and independent of each other. This
46
// is required because it allows for both shuffling of the execution
47
// order and for parallelized testing.
48
//
49
// In particular, avoid using std::rand() since it is not guaranteed
50
// to be thread safe. Instead use the API offered in
51
// `test/util/random.hpp`.
52
//
53
// All files created in tests must use the TEST_PATH macro (or one of
54
// its friends) to obtain a suitable file system path. See
55
// `test/util/test_path.hpp`.
56
//
57
//
58
// Debugging and the ONLY() macro
59
// ------------------------------
60
//
61
// A simple way of disabling all tests except one called `Foo`, is to
62
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
63
// test suite. Note that you can also use filtering by setting the
64
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
65
// this.
66
//
67
// Another way to debug a particular test, is to copy that test into
68
// `experiments/testcase.cpp` and then run `sh build.sh
69
// check-testcase` (or one of its friends) from the command line.
70

71

72
TEST(File_ExistsAndRemove)
73
{
2✔
74
    TEST_PATH(path);
2✔
75
    File(path, File::mode_Write);
2✔
76
    CHECK(File::exists(path));
2✔
77
    CHECK(File::try_remove(path));
2✔
78
    CHECK(!File::exists(path));
2✔
79
    CHECK(!File::try_remove(path));
2✔
80
}
2✔
81

82
TEST(File_IsSame)
83
{
2✔
84
    TEST_PATH(path_1);
2✔
85
    TEST_PATH(path_2);
2✔
86

1✔
87
    // exFAT does not allocate inode numbers until the file is first non-empty,
1✔
88
    // so all never-written-to files appear to be the same file
1✔
89
    File(path_1, File::mode_Write).resize(1);
2✔
90
    File(path_2, File::mode_Write).resize(1);
2✔
91

1✔
92
    File f1(path_1, File::mode_Append);
2✔
93
    File f2(path_1, File::mode_Read);
2✔
94
    File f3(path_2, File::mode_Append);
2✔
95

1✔
96
    CHECK(f1.is_same_file(f1));
2✔
97
    CHECK(f1.is_same_file(f2));
2✔
98
    CHECK(!f1.is_same_file(f3));
2✔
99
    CHECK(!f2.is_same_file(f3));
2✔
100
}
2✔
101

102

103
TEST(File_Streambuf)
104
{
2✔
105
    TEST_PATH(path);
2✔
106
    {
2✔
107
        File f(path, File::mode_Write);
2✔
108
        File::Streambuf b(&f);
2✔
109
        std::ostream out(&b);
2✔
110
        out << "Line " << 1 << std::endl;
2✔
111
        out << "Line " << 2 << std::endl;
2✔
112
    }
2✔
113
    {
2✔
114
        File f(path, File::mode_Read);
2✔
115
        char buffer[256];
2✔
116
        size_t n = f.read(buffer);
2✔
117
        std::string s_1(buffer, buffer + n);
2✔
118
        std::ostringstream out;
2✔
119
        out << "Line " << 1 << std::endl;
2✔
120
        out << "Line " << 2 << std::endl;
2✔
121
        std::string s_2 = out.str();
2✔
122
        CHECK(s_1 == s_2);
2✔
123
    }
2✔
124
}
2✔
125

126

127
TEST(File_Map)
128
{
2✔
129
    TEST_PATH(path);
2✔
130
    const char data[4096] = "12345678901234567890";
2✔
131
    size_t len = strlen(data);
2✔
132
    {
2✔
133
        File f(path, File::mode_Write);
2✔
134
        f.set_encryption_key(crypt_key());
2✔
135
        f.resize(len);
2✔
136

1✔
137
        File::Map<char> map(f, File::access_ReadWrite, len);
2✔
138
        realm::util::encryption_read_barrier(map, 0, len);
2✔
139
        memcpy(map.get_addr(), data, len);
2✔
140
        realm::util::encryption_write_barrier(map, 0, len);
2✔
141
    }
2✔
142
    {
2✔
143
        File f(path, File::mode_Read);
2✔
144
        f.set_encryption_key(crypt_key());
2✔
145
        File::Map<char> map(f, File::access_ReadOnly, len);
2✔
146
        realm::util::encryption_read_barrier(map, 0, len);
2✔
147
        CHECK(memcmp(map.get_addr(), data, len) == 0);
2✔
148
    }
2✔
149
}
2✔
150

151

152
TEST(File_MapMultiplePages)
153
{
2✔
154
    // two blocks of IV tables
1✔
155
    const size_t count = 4096 / sizeof(size_t) * 256 * 2;
2✔
156

1✔
157
    TEST_PATH(path);
2✔
158
    {
2✔
159
        File f(path, File::mode_Write);
2✔
160
        f.set_encryption_key(crypt_key());
2✔
161
        f.resize(count * sizeof(size_t));
2✔
162

1✔
163
        File::Map<size_t> map(f, File::access_ReadWrite, count * sizeof(size_t));
2✔
164
        realm::util::encryption_read_barrier(map, 0, count);
2✔
165
        for (size_t i = 0; i < count; ++i)
524,290✔
166
            map.get_addr()[i] = i;
524,288✔
167
        realm::util::encryption_write_barrier(map, 0, count);
2✔
168
    }
2✔
169
    {
2✔
170
        File f(path, File::mode_Read);
2✔
171
        f.set_encryption_key(crypt_key());
2✔
172
        File::Map<size_t> map(f, File::access_ReadOnly, count * sizeof(size_t));
2✔
173
        realm::util::encryption_read_barrier(map, 0, count);
2✔
174
        for (size_t i = 0; i < count; ++i) {
524,290✔
175
            CHECK_EQUAL(map.get_addr()[i], i);
524,288✔
176
            if (map.get_addr()[i] != i)
524,288✔
177
                return;
×
178
        }
524,288✔
179
    }
2✔
180
}
2✔
181

182
TEST(File_ReaderAndWriter)
183
{
2✔
184
    const size_t count = 4096 / sizeof(size_t) * 256 * 2;
2✔
185

1✔
186
    TEST_PATH(path);
2✔
187

1✔
188
    File writer(path, File::mode_Write);
2✔
189
    writer.set_encryption_key(crypt_key());
2✔
190
    writer.resize(count * sizeof(size_t));
2✔
191

1✔
192
    File reader(path, File::mode_Read);
2✔
193
    reader.set_encryption_key(crypt_key());
2✔
194
    CHECK_EQUAL(writer.get_size(), reader.get_size());
2✔
195

1✔
196
    File::Map<size_t> write(writer, File::access_ReadWrite, count * sizeof(size_t));
2✔
197
    File::Map<size_t> read(reader, File::access_ReadOnly, count * sizeof(size_t));
2✔
198

1✔
199
    for (size_t i = 0; i < count; i += 100) {
5,246✔
200
        realm::util::encryption_read_barrier(write, i, 1);
5,244✔
201
        write.get_addr()[i] = i;
5,244✔
202
        realm::util::encryption_write_barrier(write, i);
5,244✔
203
        realm::util::encryption_read_barrier(read, i);
5,244✔
204
        CHECK_EQUAL(read.get_addr()[i], i);
5,244✔
205
        if (read.get_addr()[i] != i)
5,244✔
206
            return;
×
207
    }
5,244✔
208
}
2✔
209

210
TEST(File_Offset)
211
{
2✔
212
    const size_t size = page_size();
2✔
213
    const size_t count_per_page = size / sizeof(size_t);
2✔
214
    // two blocks of IV tables
1✔
215
    const size_t page_count = 256 * 2 / (size / 4096);
2✔
216

1✔
217
    TEST_PATH(path);
2✔
218
    {
2✔
219
        File f(path, File::mode_Write);
2✔
220
        f.set_encryption_key(crypt_key());
2✔
221
        f.resize(page_count * size);
2✔
222

1✔
223
        for (size_t i = 0; i < page_count; ++i) {
642✔
224
            File::Map<size_t> map(f, i * size, File::access_ReadWrite, size);
640✔
225
            for (size_t j = 0; j < count_per_page; ++j) {
524,928✔
226
                realm::util::encryption_read_barrier(map, j, 1);
524,288✔
227
                map.get_addr()[j] = i * size + j;
524,288✔
228
                realm::util::encryption_write_barrier(map, j);
524,288✔
229
            }
524,288✔
230
        }
640✔
231
    }
2✔
232
    {
2✔
233
        File f(path, File::mode_Read);
2✔
234
        f.set_encryption_key(crypt_key());
2✔
235
        for (size_t i = 0; i < page_count; ++i) {
642✔
236
            File::Map<size_t> map(f, i * size, File::access_ReadOnly, size);
640✔
237
            for (size_t j = 0; j < count_per_page; ++j) {
524,928✔
238
                realm::util::encryption_read_barrier(map, j);
524,288✔
239
                CHECK_EQUAL(map.get_addr()[j], i * size + j);
524,288✔
240
                if (map.get_addr()[j] != i * size + j)
524,288✔
241
                    return;
×
242
            }
524,288✔
243
        }
640✔
244
    }
2✔
245
}
2✔
246

247

248
TEST(File_MultipleWriters)
249
{
2✔
250
    const size_t count = 4096 / sizeof(size_t) * 256 * 2;
2✔
251
#if defined(_WIN32) && defined(REALM_ENABLE_ENCRYPTION)
252
    // This test runs really slow on Windows with encryption
253
    const size_t increments = 3000;
254
#else
255
    const size_t increments = 100;
2✔
256
#endif
2✔
257
    TEST_PATH(path);
2✔
258

1✔
259
    {
2✔
260
        File w1(path, File::mode_Write);
2✔
261
        w1.set_encryption_key(crypt_key());
2✔
262
        w1.resize(count * sizeof(size_t));
2✔
263

1✔
264
        File w2(path, File::mode_Write);
2✔
265
        w2.set_encryption_key(crypt_key());
2✔
266
        w2.resize(count * sizeof(size_t));
2✔
267

1✔
268
        File::Map<size_t> map1(w1, File::access_ReadWrite, count * sizeof(size_t));
2✔
269
        File::Map<size_t> map2(w2, File::access_ReadWrite, count * sizeof(size_t));
2✔
270

1✔
271
        // Place zeroes in selected places
1✔
272
        for (size_t i = 0; i < count; i += increments) {
5,246✔
273
            realm::util::encryption_read_barrier(map1, i);
5,244✔
274
            map1.get_addr()[i] = 0;
5,244✔
275
            realm::util::encryption_write_barrier(map1, i);
5,244✔
276
        }
5,244✔
277

1✔
278
        for (size_t i = 0; i < count; i += increments) {
5,246✔
279
            realm::util::encryption_read_barrier(map1, i, 1);
5,244✔
280
            ++map1.get_addr()[i];
5,244✔
281
            realm::util::encryption_write_barrier(map1, i);
5,244✔
282
            realm::util::encryption_read_barrier(map2, i, 1);
5,244✔
283
            ++map2.get_addr()[i];
5,244✔
284
            realm::util::encryption_write_barrier(map2, i);
5,244✔
285
        }
5,244✔
286
    }
2✔
287

1✔
288
    File reader(path, File::mode_Read);
2✔
289
    reader.set_encryption_key(crypt_key());
2✔
290

1✔
291
    File::Map<size_t> read(reader, File::access_ReadOnly, count * sizeof(size_t));
2✔
292
    realm::util::encryption_read_barrier(read, 0, count);
2✔
293
    for (size_t i = 0; i < count; i += increments) {
5,246✔
294
        CHECK_EQUAL(read.get_addr()[i], 2);
5,244✔
295
        if (read.get_addr()[i] != 2)
5,244✔
296
            return;
×
297
    }
5,244✔
298
}
2✔
299

300

301
TEST(File_SetEncryptionKey)
302
{
2✔
303
    TEST_PATH(path);
2✔
304
    File f(path, File::mode_Write);
2✔
305
    const char key[64] = {0};
2✔
306

1✔
307
#if REALM_ENABLE_ENCRYPTION
2✔
308
    f.set_encryption_key(key); // should not throw
2✔
309
#else
310
    CHECK_THROW_EX(f.set_encryption_key(key), Exception, (e.code() == ErrorCodes::NotSupported));
311
#endif
312
}
2✔
313

314

315
TEST(File_ReadWrite)
316
{
2✔
317
    TEST_PATH(path);
2✔
318
    File f(path, File::mode_Write);
2✔
319
    f.set_encryption_key(crypt_key());
2✔
320
    f.resize(100);
2✔
321

1✔
322
    for (char i = 0; i < 100; ++i)
202✔
323
        f.write(&i, 1);
200✔
324
    f.seek(0);
2✔
325
    for (char i = 0; i < 100; ++i) {
202✔
326
        char read;
200✔
327
        f.read(&read, 1);
200✔
328
        CHECK_EQUAL(i, read);
200✔
329
    }
200✔
330
}
2✔
331

332

333
TEST(File_Resize)
334
{
2✔
335
    TEST_PATH(path);
2✔
336
    File f(path, File::mode_Write);
2✔
337
    f.set_encryption_key(crypt_key());
2✔
338

1✔
339
    f.resize(page_size() * 2);
2✔
340
    CHECK_EQUAL(page_size() * 2, f.get_size());
2✔
341
    {
2✔
342
        File::Map<unsigned char> m(f, File::access_ReadWrite, page_size() * 2);
2✔
343
        for (unsigned int i = 0; i < page_size() * 2; ++i) {
40,962✔
344
            realm::util::encryption_read_barrier(m, i, 1);
40,960✔
345
            m.get_addr()[i] = static_cast<unsigned char>(i);
40,960✔
346
            realm::util::encryption_write_barrier(m, i);
40,960✔
347
        }
40,960✔
348

1✔
349
        // Resizing away the first write is indistinguishable in encrypted files
1✔
350
        // from the process being interrupted before it does the first write,
1✔
351
        // but with subsequent writes it can tell that there was once valid
1✔
352
        // encrypted data there, so flush and write a second time
1✔
353
        m.sync();
2✔
354
        for (unsigned int i = 0; i < page_size() * 2; ++i) {
40,962✔
355
            realm::util::encryption_read_barrier(m, i, 1);
40,960✔
356
            m.get_addr()[i] = static_cast<unsigned char>(i);
40,960✔
357
            realm::util::encryption_write_barrier(m, i);
40,960✔
358
        }
40,960✔
359
    }
2✔
360

1✔
361
    f.resize(page_size());
2✔
362
    CHECK_EQUAL(page_size(), f.get_size());
2✔
363
    {
2✔
364
        File::Map<unsigned char> m(f, File::access_ReadOnly, page_size());
2✔
365
        for (unsigned int i = 0; i < page_size(); ++i) {
20,482✔
366
            realm::util::encryption_read_barrier(m, i);
20,480✔
367
            CHECK_EQUAL(static_cast<unsigned char>(i), m.get_addr()[i]);
20,480✔
368
            if (static_cast<unsigned char>(i) != m.get_addr()[i])
20,480✔
369
                return;
×
370
        }
20,480✔
371
    }
2✔
372

1✔
373
    f.resize(page_size() * 2);
2✔
374
    CHECK_EQUAL(page_size() * 2, f.get_size());
2✔
375
    {
2✔
376
        File::Map<unsigned char> m(f, File::access_ReadWrite, page_size() * 2);
2✔
377
        for (unsigned int i = 0; i < page_size() * 2; ++i) {
40,962✔
378
            realm::util::encryption_read_barrier(m, i, 1);
40,960✔
379
            m.get_addr()[i] = static_cast<unsigned char>(i);
40,960✔
380
            realm::util::encryption_write_barrier(m, i);
40,960✔
381
        }
40,960✔
382
    }
2✔
383
    {
2✔
384
        File::Map<unsigned char> m(f, File::access_ReadOnly, page_size() * 2);
2✔
385
        for (unsigned int i = 0; i < page_size() * 2; ++i) {
40,962✔
386
            realm::util::encryption_read_barrier(m, i);
40,960✔
387
            CHECK_EQUAL(static_cast<unsigned char>(i), m.get_addr()[i]);
40,960✔
388
            if (static_cast<unsigned char>(i) != m.get_addr()[i])
40,960✔
389
                return;
×
390
        }
40,960✔
391
    }
2✔
392
}
2✔
393

394

395
TEST(File_NotFound)
396
{
2✔
397
    TEST_PATH(path);
2✔
398
    File file;
2✔
399
    CHECK_THROW_EX(file.open(path), FileAccessError, e.get_path() == std::string(path));
2✔
400
}
2✔
401

402

403
TEST(File_PathNotFound)
404
{
2✔
405
    File file;
2✔
406
    CHECK_THROW_EX(file.open(""), FileAccessError, e.code() == ErrorCodes::FileNotFound);
2✔
407
}
2✔
408

409

410
TEST(File_Exists)
411
{
2✔
412
    TEST_PATH(path);
2✔
413
    File file;
2✔
414
    file.open(path, File::mode_Write); // Create the file
2✔
415
    file.close();
2✔
416
    CHECK_THROW_EX(file.open(path, File::access_ReadWrite, File::create_Must, File::flag_Trunc), FileAccessError,
2✔
417
                   e.get_path() == std::string(path) && e.code() == ErrorCodes::FileAlreadyExists);
2✔
418
}
2✔
419

420

421
TEST(File_Move)
422
{
2✔
423
    TEST_PATH(path);
2✔
424
    File file_1(path, File::mode_Write);
2✔
425
    CHECK(file_1.is_attached());
2✔
426
    File file_2(std::move(file_1));
2✔
427
    CHECK_NOT(file_1.is_attached());
2✔
428
    CHECK(file_2.is_attached());
2✔
429
    file_1 = std::move(file_2);
2✔
430
    CHECK(file_1.is_attached());
2✔
431
    CHECK_NOT(file_2.is_attached());
2✔
432
}
2✔
433

434
#if 0
435
TEST(File_PreallocResizing)
436
{
437
    TEST_PATH(path);
438
    File file(path, File::mode_Write);
439
    CHECK(file.is_attached());
440
    // we cannot test this with encryption...prealloc always allocates a full page
441
    file.prealloc(0); // this is allowed
442
    CHECK_EQUAL(file.get_size(), 0);
443
    file.prealloc(100);
444
    CHECK_EQUAL(file.get_size(), 100);
445
    file.prealloc(50);
446
    CHECK_EQUAL(file.get_size(), 100); // prealloc does not reduce size
447

448
    // To expose the preallocation bug, we need to iterate over a large numbers, less than 4096.
449
    // If the bug is present, we will allocate additional space to the file on every call, but if it is
450
    // not present, the OS will preallocate 4096 only on the first call.
451
    constexpr size_t init_size = 2048;
452
    constexpr size_t dest_size = 3000;
453
    for (size_t prealloc_space = init_size; prealloc_space <= dest_size; ++prealloc_space) {
454
        file.prealloc(prealloc_space);
455
        CHECK_EQUAL(file.get_size(), prealloc_space);
456
    }
457

458
#if REALM_PLATFORM_APPLE
459
    int fd = ::open(path.c_str(), O_RDONLY);
460
    CHECK(fd >= 0);
461
    struct stat statbuf;
462
    CHECK(fstat(fd, &statbuf) == 0);
463
    size_t allocated_size = statbuf.st_blocks;
464
    CHECK_EQUAL(statbuf.st_size, dest_size);
465
    CHECK(!int_multiply_with_overflow_detect(allocated_size, S_BLKSIZE));
466

467
    // When performing prealloc, the OS has the option to preallocate more than the requeted space
468
    // but we need to check that the preallocated space is within a reasonable bound.
469
    // If space is being incorrectly preallocated (growing on each call) then we will have more than 3000KB
470
    // of preallocated space, but if it is being allocated correctly (only when we need to expand) then we'll have
471
    // a multiple of the optimal file system I/O operation (`stat -f %k .`) which is 4096 on HSF+.
472
    // To give flexibility for file system prealloc implementations we check that the preallocated space is within
473
    // at least 16 times the nominal requested size.
474
    CHECK_LESS(allocated_size, 4096 * 16);
475

476
    CHECK(::close(fd) == 0);
477
#endif
478
}
479
#endif
480

481
TEST(File_PreallocResizingAPFSBug)
482
{
2✔
483
    TEST_PATH(path);
2✔
484
    File file(path, File::mode_Write);
2✔
485
    CHECK(file.is_attached());
2✔
486
    file.write("aaaaaaaaaaaaaaaaaaaa"); // 20 a's
2✔
487
    // calling prealloc on a newly created file would sometimes fail on APFS with EINVAL via fcntl(F_PREALLOCATE)
1✔
488
    // this may not be the only way to trigger the error, but it does seem to be timing dependant.
1✔
489
    file.prealloc(100);
2✔
490
    CHECK_EQUAL(file.get_size(), 100);
2✔
491

1✔
492
    // let's write past the first prealloc block (@ 4096) and verify it reads correctly too.
1✔
493
    file.write("aaaaa");
2✔
494
    // this will change the file size, but likely won't preallocate more space since the first call to prealloc
1✔
495
    // will probably have allocated a whole 4096 block.
1✔
496
    file.prealloc(200);
2✔
497
    CHECK_EQUAL(file.get_size(), 200);
2✔
498
    file.write("aa");
2✔
499
    file.prealloc(5020); // expands to another 4096 block
2✔
500
    constexpr size_t insert_pos = 5000;
2✔
501
    const char* insert_str = "hello";
2✔
502
    file.seek(insert_pos);
2✔
503
    file.write(insert_str);
2✔
504
    file.seek(insert_pos);
2✔
505
    CHECK_EQUAL(file.get_size(), 5020);
2✔
506
    constexpr size_t input_size = 6;
2✔
507
    char input[input_size];
2✔
508
    file.read(input, input_size);
2✔
509
    CHECK_EQUAL(strncmp(input, insert_str, input_size), 0);
2✔
510
}
2✔
511

512
TEST(File_parent_dir)
513
{
2✔
514
    std::map<std::string, std::string> mappings = {{"Unicorn🦄/file.cpp", "Unicorn🦄"},
2✔
515
                                                   {"", ""},
2✔
516
                                                   {"asdf", ""},
2✔
517
                                                   {"file.cpp", ""},
2✔
518
                                                   {"Unicorn🦄", ""},
2✔
519
                                                   {"parent/file.cpp", "parent"},
2✔
520
                                                   {"parent//file.cpp", "parent"},
2✔
521
                                                   {"parent///file.cpp", "parent"},
2✔
522
                                                   {"parent////file.cpp", "parent"},
2✔
523
                                                   {"1/2/3/4.cpp", "1/2/3"},
2✔
524
                                                   {"/1/2/3/4", "/1/2/3"}};
2✔
525
    for (auto [input, expected] : mappings) {
22✔
526
        std::string actual = File::parent_dir(input);
22✔
527
        CHECK_EQUAL(actual, expected);
22✔
528
        if (actual != expected) {
22✔
529
            realm::util::format(std::cout, "unexpected result '%1' for input '%2'", actual, input);
×
530
        }
×
531
    }
22✔
532
}
2✔
533

534
TEST(File_GetUniqueID)
535
{
2✔
536
    TEST_PATH(path_1);
2✔
537
    TEST_PATH(path_2);
2✔
538
    TEST_PATH(path_3);
2✔
539

1✔
540
    File file1_1;
2✔
541
    File file1_2;
2✔
542
    File file2_1;
2✔
543
    file1_1.open(path_1, File::mode_Write);
2✔
544
    file1_2.open(path_1, File::mode_Read);
2✔
545
    file2_1.open(path_2, File::mode_Write);
2✔
546

1✔
547
    // exFAT does not allocate inode numbers until the file is first non-empty
1✔
548
    file1_1.resize(1);
2✔
549
    file2_1.resize(1);
2✔
550

1✔
551
    File::UniqueID uid1_1 = file1_1.get_unique_id();
2✔
552
    File::UniqueID uid1_2 = file1_2.get_unique_id();
2✔
553
    File::UniqueID uid2_1 = file2_1.get_unique_id();
2✔
554
    std::optional<File::UniqueID> uid2_2;
2✔
555
    CHECK(uid2_2 = File::get_unique_id(path_2));
2✔
556

1✔
557
    CHECK(uid1_1 == uid1_2);
2✔
558
    CHECK(uid2_1 == *uid2_2);
2✔
559
    CHECK(uid1_1 != uid2_1);
2✔
560

1✔
561
    // Path doesn't exist
1✔
562
    CHECK_NOT(File::get_unique_id(path_3));
2✔
563

1✔
564
    // Test operator<
1✔
565
    File::UniqueID uid4_1{0, 5};
2✔
566
    File::UniqueID uid4_2{1, 42};
2✔
567
    CHECK(uid4_1 < uid4_2);
2✔
568
    CHECK_NOT(uid4_2 < uid4_1);
2✔
569

1✔
570
    uid4_1 = {0, 1};
2✔
571
    uid4_2 = {0, 2};
2✔
572
    CHECK(uid4_1 < uid4_2);
2✔
573
    CHECK_NOT(uid4_2 < uid4_1);
2✔
574

1✔
575
    uid4_1 = uid4_2;
2✔
576
    CHECK_NOT(uid4_1 < uid4_2);
2✔
577
    CHECK_NOT(uid4_2 < uid4_1);
2✔
578

1✔
579
    file1_1.resize(0);
2✔
580
    file2_1.resize(0);
2✔
581
    file2_1.resize(1);
2✔
582
    file1_1.resize(1);
2✔
583
    if (!test_util::test_dir_is_exfat()) {
2✔
584
        CHECK(uid1_1 == file1_1.get_unique_id());
2✔
585
        CHECK(uid2_1 == file2_1.get_unique_id());
2✔
586
    }
2✔
587
    else {
×
588
        // fat32/exfat could reuse or reassign uid after truncate
589
        // there is not much to guarantee about the values of uids
NEW
590
        auto u1 = file1_1.get_unique_id();
×
NEW
591
        auto u2 = file2_1.get_unique_id();
×
NEW
592
        CHECK(u1 != u2);
×
NEW
593
        auto u1_2 = file1_2.get_unique_id();
×
NEW
594
        CHECK(u1 == u1_2);
×
UNCOV
595
    }
×
596
}
2✔
597

598
TEST(File_Temp)
599
{
2✔
600
    auto tmp_file_name = make_temp_file("foo");
2✔
601
    {
2✔
602
        File file1;
2✔
603
        file1.open(tmp_file_name, File::mode_Write);
2✔
604
        CHECK(file1.is_attached());
2✔
605
    }
2✔
606
    remove(tmp_file_name.c_str());
2✔
607
}
2✔
608

609
#endif // TEST_FILE
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc