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

realm / realm-core / 1771

20 Oct 2023 08:58AM UTC coverage: 91.567% (-0.009%) from 91.576%
1771

push

Evergreen

web-flow
Fix blocked DB::open on multiprocess access on exFAT filesystem (#6959)

Fix double file lock and DB::open being blocked with multiple concurrent realm access on fat32/exfat file systems.

When file is truncated on fat32/exfat its uid is available for other files. With multiple processes opening and truncating the same set of files could lead to the situation when within one process get_unique_id will return the same value for different files in timing is right. This breaks proper initialization of static data for interprocess mutexes, so that subsequent locks will hang by trying to lock essentially the same file twice.

94304 of 173552 branches covered (0.0%)

59 of 82 new or added lines in 5 files covered. (71.95%)

53 existing lines in 13 files now uncovered.

230544 of 251776 relevant lines covered (91.57%)

6594884.0 hits per line

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

47.95
/test/fuzz_group.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 "fuzz_group.hpp"
20

21
#include <realm.hpp>
22
#include <realm/index_string.hpp>
23

24
#include <ctime>
25
#include <cstdio>
26
#include <fstream>
27
#include <iostream>
28

29
#include "util/test_path.hpp"
30

31
using namespace realm;
32
using namespace realm::util;
33

34
#define TEST_FUZZ
35
#ifdef TEST_FUZZ
36
// Determines whether or not to run the shared group verify function
37
// after each transaction. This will find errors earlier but is expensive.
38
#define REALM_VERIFY true
39

40
#if REALM_VERIFY
41
#define REALM_DO_IF_VERIFY(log, op)                                                                                  \
42
    do {                                                                                                             \
237✔
43
        if (log)                                                                                                     \
237✔
44
            *log << #op << ";\n";                                                                                    \
96✔
45
        op;                                                                                                          \
237✔
46
    } while (false)
237✔
47
#else
48
#define REALM_DO_IF_VERIFY(log, owner)                                                                               \
49
    do {                                                                                                             \
50
    } while (false)
51
#endif
52

53
namespace {
54

55
struct EndOfFile {
56
};
57

58
enum INS {
59
    ADD_TABLE,
60
    REMOVE_TABLE,
61
    CREATE_OBJECT,
62
    RENAME_COLUMN,
63
    ADD_COLUMN,
64
    REMOVE_COLUMN,
65
    SET,
66
    REMOVE_OBJECT,
67
    REMOVE_RECURSIVE,
68
    ADD_COLUMN_LINK,
69
    ADD_COLUMN_LINK_LIST,
70
    CLEAR_TABLE,
71
    ADD_SEARCH_INDEX,
72
    REMOVE_SEARCH_INDEX,
73
    COMMIT,
74
    ROLLBACK,
75
    ADVANCE,
76
    MOVE_LAST_OVER,
77
    CLOSE_AND_REOPEN,
78
    GET_ALL_COLUMN_NAMES,
79
    CREATE_TABLE_VIEW,
80
    COMPACT,
81
    IS_NULL,
82
    ENUMERATE_COLUMN,
83

84
    COUNT
85
};
86

87
DataType get_type(unsigned char c)
88
{
12✔
89
    DataType types[] = {type_Int, type_Bool, type_Float, type_Double, type_String, type_Binary, type_Timestamp};
12✔
90

10✔
91
    unsigned char mod = c % (sizeof(types) / sizeof(DataType));
12✔
92
    return types[mod];
12✔
93
}
12✔
94

95
struct State {
96
    std::string str;
97
    size_t pos;
98
};
99

100
unsigned char get_next(State& s)
101
{
1,206✔
102
    if (s.pos == s.str.size()) {
1,206✔
103
        throw EndOfFile{};
6✔
104
    }
6✔
105
    char byte = s.str[s.pos];
1,200✔
106
    s.pos++;
1,200✔
107
    return byte;
1,200✔
108
}
1,200✔
109

110
const char* get_encryption_key()
111
{
4✔
112
#if REALM_ENABLE_ENCRYPTION
4✔
113
    return "1234567890123456789012345678901123456789012345678901234567890123";
4✔
114
#else
115
    return nullptr;
116
#endif
117
}
4✔
118

119
int64_t get_int64(State& s)
120
{
×
121
    int64_t v = 0;
×
122
    for (size_t t = 0; t < 8; t++) {
×
123
        unsigned char c = get_next(s);
×
124
        *(reinterpret_cast<signed char*>(&v) + t) = c;
×
125
    }
×
126
    return v;
×
127
}
×
128

129
int32_t get_int32(State& s)
130
{
×
131
    int32_t v = 0;
×
132
    for (size_t t = 0; t < 4; t++) {
×
133
        unsigned char c = get_next(s);
×
134
        *(reinterpret_cast<signed char*>(&v) + t) = c;
×
135
    }
×
136
    return v;
×
137
}
×
138

139
std::string create_string(size_t length)
140
{
×
141
    REALM_ASSERT_3(length, <, 256);
×
142
    char buf[256] = {0};
×
143
    for (size_t i = 0; i < length; i++)
×
144
        buf[i] = 'a' + (rand() % 20);
×
145
    return std::string{buf, length};
×
146
}
×
147

148
std::pair<int64_t, int32_t> get_timestamp_values(State& s)
149
{
×
150
    int64_t seconds = get_int64(s);
×
151
    int32_t nanoseconds = get_int32(s) % 1000000000;
×
152
    // Make sure the values form a sensible Timestamp
153
    const bool both_non_negative = seconds >= 0 && nanoseconds >= 0;
×
154
    const bool both_non_positive = seconds <= 0 && nanoseconds <= 0;
×
155
    const bool correct_timestamp = both_non_negative || both_non_positive;
×
156
    if (!correct_timestamp) {
×
157
        nanoseconds = -nanoseconds;
×
158
    }
×
159
    return {seconds, nanoseconds};
×
160
}
×
161

162
int table_index = 0;
163
int column_index = 0;
164

165
std::string create_column_name(DataType t)
166
{
35✔
167
    std::string str;
35✔
168
    switch (t) {
35✔
169
        case type_Int:
1✔
170
            str = "int_";
1✔
171
            break;
1✔
172
        case type_Bool:
2✔
173
            str = "bool_";
2✔
174
            break;
2✔
UNCOV
175
        case type_Float:
✔
UNCOV
176
            str = "float_";
×
UNCOV
177
            break;
×
178
        case type_Double:
3✔
179
            str = "double_";
3✔
180
            break;
3✔
181
        case type_String:
3✔
182
            str = "string_";
3✔
183
            break;
3✔
184
        case type_Binary:
3✔
185
            str = "binary_";
3✔
186
            break;
3✔
187
        case type_Timestamp:
✔
188
            str = "date_";
×
189
            break;
×
190
        case type_Decimal:
✔
191
            str = "decimal_";
×
192
            break;
×
193
        case type_ObjectId:
✔
194
            str = "id_";
×
195
            break;
×
196
        case type_Link:
16✔
197
            str = "link_";
16✔
198
            break;
16✔
199
        case type_TypedLink:
✔
200
            str = "typed_link_";
×
201
            break;
×
202
        case type_LinkList:
7✔
203
            str = "link_list_";
7✔
204
            break;
7✔
205
        case type_UUID:
✔
206
            str = "uuid_";
×
207
            break;
×
208
        case type_Mixed:
✔
209
            str = "any_";
×
210
            break;
×
211
    }
35✔
212
    return str + util::to_string(column_index++);
35✔
213
}
35✔
214

215
std::string create_table_name()
216
{
41✔
217
    std::string str = "Table_";
41✔
218
    return str + util::to_string(table_index++);
41✔
219
}
41✔
220

221
std::string get_current_time_stamp()
222
{
×
223
    std::time_t t = std::time(nullptr);
×
224
    const int str_size = 100;
×
225
    char str_buffer[str_size] = {0};
×
226
    std::strftime(str_buffer, str_size, "%c", std::localtime(&t));
×
227
    return str_buffer;
×
228
}
×
229

230
// You can use this variable to make a conditional breakpoint if you know that
231
// a problem occurs after a certain amount of iterations.
232
int iteration = 0;
233
} // anonymous namespace
234

235
void parse_and_apply_instructions(std::string& in, const std::string& path, std::ostream* log)
236
{
6✔
237
    const size_t add_empty_row_max = REALM_MAX_BPNODE_SIZE * REALM_MAX_BPNODE_SIZE + 1000;
6✔
238
    const size_t max_tables = REALM_MAX_BPNODE_SIZE * 10;
6✔
239

3✔
240
    // Max number of rows in a table. Overridden only by create_object() and only in the case where
3✔
241
    // max_rows is not exceeded *prior* to executing add_empty_row.
3✔
242
    const size_t max_rows = 100000;
6✔
243
    column_index = table_index = 0;
6✔
244

3✔
245
    State s;
6✔
246
    s.str = in;
6✔
247
    s.pos = 0;
6✔
248

3✔
249
    // const bool use_encryption = false;
3✔
250
    const bool use_encryption = get_next(s) % 2 == 0;
6✔
251
    const char* encryption_key = use_encryption ? get_encryption_key() : nullptr;
5✔
252

3✔
253
    if (log) {
6✔
254
        *log << "// Test case generated in " REALM_VER_CHUNK " on " << get_current_time_stamp() << ".\n";
×
255
        *log << "// REALM_MAX_BPNODE_SIZE is " << REALM_MAX_BPNODE_SIZE << "\n";
×
256
        *log << "// ----------------------------------------------------------------------\n";
×
257
        std::string printable_key;
×
258
        if (encryption_key == nullptr) {
×
259
            printable_key = "nullptr";
×
260
        }
×
261
        else {
×
262
            printable_key = std::string("\"") + encryption_key + "\"";
×
263
        }
×
264

265
        *log << "SHARED_GROUP_TEST_PATH(path);\n";
×
266

267
        *log << "const char* key = " << printable_key << ";\n";
×
268
        *log << "std::unique_ptr<Replication> hist(make_in_realm_history());\n";
×
269

270
        *log << "DBRef db = DB::create(*hist, path, DBOptions(key));\n";
×
271
        *log << "auto wt = db->start_write();\n";
×
272
        *log << "auto rt = db->start_read();\n";
×
273
        *log << "std::vector<TableView> table_views;\n";
×
274

275
        *log << "\n";
×
276
    }
×
277

3✔
278
    std::unique_ptr<Replication> hist(make_in_realm_history());
6✔
279

3✔
280
    DBOptions options(encryption_key);
6✔
281
    DBRef db = DB::create(*hist, path, options);
6✔
282
    auto wt = db->start_write();
6✔
283
    auto rt = db->start_read();
6✔
284
    std::vector<TableView> table_views;
6✔
285

3✔
286
    try {
6✔
287

3✔
288
        for (;;) {
934✔
289
            char instr = get_next(s) % COUNT;
934✔
290
            iteration++;
934✔
291

413✔
292
            /* This can help when debugging
413✔
293
            if (log) {
413✔
294
                *log << iteration << " ";
413✔
295
            }
413✔
296
            */
413✔
297

413✔
298
            if (instr == ADD_TABLE && wt->size() < max_tables) {
934✔
299
                std::string name = create_table_name();
41✔
300
                if (log) {
41✔
301
                    *log << "wt->add_table(\"" << name << "\");\n";
×
302
                }
×
303
                wt->add_table(name);
41✔
304
            }
41✔
305
            else if (instr == REMOVE_TABLE && wt->size() > 0) {
893✔
306
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
17✔
307
                if (log) {
17✔
308
                    *log << "try { wt->remove_table(" << table_key
×
309
                         << "); }"
×
310
                            " catch (const CrossTableLinkTarget&) { }\n";
×
311
                }
×
312
                try {
17✔
313
                    wt->remove_table(table_key);
17✔
314
                }
17✔
315
                catch (const CrossTableLinkTarget&) {
16✔
316
                    if (log) {
4!
317
                        *log << "// Exception\n";
×
318
                    }
×
319
                }
4✔
320
            }
17✔
321
            else if (instr == CLEAR_TABLE && wt->size() > 0) {
876✔
322
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
9✔
323
                if (log) {
9✔
324
                    *log << "wt->get_table(" << table_key << ")->clear();\n";
×
325
                }
×
326
                wt->get_table(table_key)->clear();
9✔
327
            }
9✔
328
            else if (instr == CREATE_OBJECT && wt->size() > 0) {
867✔
329
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
19✔
330
                size_t num_rows = get_next(s);
19✔
331
                if (wt->get_table(table_key)->size() + num_rows < max_rows) {
19✔
332
                    if (log) {
19✔
333
                        *log << "{ std::vector<ObjKey> keys; wt->get_table(" << table_key << ")->create_objects("
×
334
                             << num_rows % add_empty_row_max << ", keys); }\n";
×
335
                    }
×
336
                    std::vector<ObjKey> keys;
19✔
337
                    wt->get_table(table_key)->create_objects(num_rows % add_empty_row_max, keys);
19✔
338
                }
19✔
339
            }
19✔
340
            else if (instr == ADD_COLUMN && wt->size() > 0) {
848✔
341
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
12✔
342
                DataType type = get_type(get_next(s));
12✔
343
                std::string name = create_column_name(type);
12✔
344
                // Mixed cannot be nullable. For other types, chose nullability randomly
10✔
345
                bool nullable = (get_next(s) % 2 == 0);
12✔
346
                if (log) {
12✔
347
                    *log << "wt->get_table(" << table_key << ")->add_column(DataType(" << int(type) << "), \"" << name
×
348
                         << "\", " << (nullable ? "true" : "false") << ");";
×
349
                }
×
350
                auto col = wt->get_table(table_key)->add_column(type, name, nullable);
12✔
351
                if (log) {
12✔
352
                    *log << " // -> " << col << "\n";
×
353
                }
×
354
            }
12✔
355
            else if (instr == REMOVE_COLUMN && wt->size() > 0) {
836✔
356
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
12✔
357
                TableRef t = wt->get_table(table_key);
12✔
358
                auto column_keys = t->get_column_keys();
12✔
359
                if (!column_keys.empty()) {
12✔
360
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
2✔
361
                    if (log) {
2✔
362
                        *log << "wt->get_table(" << table_key << ")->remove_column(" << col << ");\n";
×
363
                    }
×
364
                    t->remove_column(col);
2✔
365
                }
2✔
366
            }
12✔
367
            else if (instr == RENAME_COLUMN && wt->size() > 0) {
824✔
368
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
7✔
369
                TableRef t = wt->get_table(table_key);
7✔
370
                auto column_keys = t->get_column_keys();
7✔
371
                if (!column_keys.empty()) {
7✔
372
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
1✔
373
                    std::string name = create_column_name(t->get_column_type(col));
1✔
374
                    if (log) {
1✔
375
                        *log << "wt->get_table(" << table_key << ")->rename_column(" << col << ", \"" << name
×
376
                             << "\");\n";
×
377
                    }
×
378
                    t->rename_column(col, name);
1✔
379
                }
1✔
380
            }
7✔
381
            else if (instr == ADD_SEARCH_INDEX && wt->size() > 0) {
817✔
382
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
14✔
383
                TableRef t = wt->get_table(table_key);
14✔
384
                auto column_keys = t->get_column_keys();
14✔
385
                if (!column_keys.empty()) {
14✔
386
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
5✔
387
                    bool supports_search_index = StringIndex::type_supported(t->get_column_type(col));
5✔
388

4✔
389
                    if (supports_search_index) {
5✔
390
                        if (log) {
2✔
391
                            *log << "wt->get_table(" << table_key << ")->add_search_index(" << col << ");\n";
×
392
                        }
×
393
                        t->add_search_index(col);
2✔
394
                    }
2✔
395
                }
5✔
396
            }
14✔
397
            else if (instr == REMOVE_SEARCH_INDEX && wt->size() > 0) {
803✔
398
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
10✔
399
                TableRef t = wt->get_table(table_key);
10✔
400
                auto column_keys = t->get_column_keys();
10✔
401
                if (!column_keys.empty()) {
10✔
402
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
3✔
403
                    // We don't need to check if the column is of a type that is indexable or if it has index on or
2✔
404
                    // off
2✔
405
                    // because Realm will just do a no-op at worst (no exception or assert).
2✔
406
                    if (log) {
3✔
407
                        *log << "wt->get_table(" << table_key << ")->remove_search_index(" << col << ");\n";
×
408
                    }
×
409
                    t->remove_search_index(col);
3✔
410
                }
3✔
411
            }
10✔
412
            else if (instr == ADD_COLUMN_LINK && wt->size() >= 1) {
793✔
413
                TableKey table_key_1 = wt->get_table_keys()[get_next(s) % wt->size()];
16✔
414
                TableKey table_key_2 = wt->get_table_keys()[get_next(s) % wt->size()];
16✔
415
                TableRef t1 = wt->get_table(table_key_1);
16✔
416
                TableRef t2 = wt->get_table(table_key_2);
16✔
417
                std::string name = create_column_name(type_Link);
16✔
418
                if (log) {
16✔
419
                    *log << "wt->get_table(" << table_key_1 << ")->add_column_link(type_Link, \"" << name
×
420
                         << "\", *wt->get_table(" << table_key_2 << "));";
×
421
                }
×
422
                auto col = t1->add_column(*t2, name);
16✔
423
                if (log) {
16✔
424
                    *log << " // -> " << col << "\n";
×
425
                }
×
426
            }
16✔
427
            else if (instr == ADD_COLUMN_LINK_LIST && wt->size() >= 2) {
777✔
428
                TableKey table_key_1 = wt->get_table_keys()[get_next(s) % wt->size()];
8✔
429
                TableKey table_key_2 = wt->get_table_keys()[get_next(s) % wt->size()];
8✔
430
                TableRef t1 = wt->get_table(table_key_1);
8✔
431
                TableRef t2 = wt->get_table(table_key_2);
8✔
432
                std::string name = create_column_name(type_LinkList);
8✔
433
                if (log) {
8✔
434
                    *log << "wt->get_table(" << table_key_1 << ")->add_column_link(type_LinkList, \"" << name
×
435
                         << "\", *wt->get_table(" << table_key_2 << "));";
×
436
                }
×
437
                auto col = t1->add_column_list(*t2, name);
8✔
438
                if (log) {
8✔
439
                    *log << " // -> " << col << "\n";
×
440
                }
×
441
            }
8✔
442
            else if (instr == SET && wt->size() > 0) {
769✔
443
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
12✔
444
                TableRef t = wt->get_table(table_key);
12✔
445
                auto all_col_keys = t->get_column_keys();
12✔
446
                if (!all_col_keys.empty() && t->size() > 0) {
12✔
447
                    ColKey col = all_col_keys[get_next(s) % all_col_keys.size()];
×
448
                    size_t row = get_next(s) % t->size();
×
449
                    DataType type = t->get_column_type(col);
×
450
                    Obj obj = t->get_object(row);
×
451
                    if (log) {
×
452
                        *log << "{\nObj obj = wt->get_table(" << table_key << ")->get_object(" << row << ");\n";
×
453
                    }
×
454

455
                    // With equal probability, either set to null or to a value
456
                    if (get_next(s) % 2 == 0 && t->is_nullable(col)) {
×
457
                        if (type == type_Link) {
×
458
                            if (log) {
×
459
                                *log << "obj.set(" << col << ", null_key);\n";
×
460
                            }
×
461
                            obj.set(col, null_key);
×
462
                        }
×
463
                        else {
×
464
                            if (log) {
×
465
                                *log << "obj.set_null(" << col << ");\n";
×
466
                            }
×
467
                            obj.set_null(col);
×
468
                        }
×
469
                    }
×
470
                    else {
×
471
                        if (type == type_String) {
×
472
                            std::string str = create_string(get_next(s));
×
473
                            if (log) {
×
474
                                *log << "obj.set(" << col << ", \"" << str << "\");\n";
×
475
                            }
×
476
                            obj.set(col, StringData(str));
×
477
                        }
×
478
                        else if (type == type_Binary) {
×
479
                            std::string str = create_string(get_next(s));
×
480
                            if (log) {
×
481
                                *log << "obj.set<Binary>(" << col << ", BinaryData{\"" << str << "\", " << str.size()
×
482
                                     << "});\n";
×
483
                            }
×
484
                            obj.set<Binary>(col, BinaryData(str));
×
485
                        }
×
486
                        else if (type == type_Int) {
×
487
                            bool add_int = get_next(s) % 2 == 0;
×
488
                            int64_t value = get_int64(s);
×
489
                            if (add_int) {
×
490
                                if (log) {
×
491
                                    *log << "try { obj.add_int(" << col << ", " << value
×
492
                                         << "); } catch (const LogicError& le) { CHECK(le.kind() == "
×
493
                                            "LogicError::illegal_combination); }\n";
×
494
                                }
×
495
                                try {
×
496
                                    obj.add_int(col, value);
×
497
                                }
×
498
                                catch (const LogicError& le) {
×
499
                                    if (le.code() != ErrorCodes::IllegalOperation) {
×
500
                                        throw;
×
501
                                    }
×
502
                                }
×
503
                            }
×
504
                            else {
×
505
                                if (log) {
×
506
                                    *log << "obj.set<Int>(" << col << ", " << value << ");\n";
×
507
                                }
×
508
                                obj.set<Int>(col, value);
×
509
                            }
×
510
                        }
×
511
                        else if (type == type_Bool) {
×
512
                            bool value = get_next(s) % 2 == 0;
×
513
                            if (log) {
×
514
                                *log << "obj.set<Bool>(" << col << ", " << (value ? "true" : "false") << ");\n";
×
515
                            }
×
516
                            obj.set<Bool>(col, value);
×
517
                        }
×
518
                        else if (type == type_Float) {
×
519
                            float value = get_next(s);
×
520
                            if (log) {
×
521
                                *log << "obj.set<Float>(" << col << ", " << value << ");\n";
×
522
                            }
×
523
                            obj.set<Float>(col, value);
×
524
                        }
×
525
                        else if (type == type_Double) {
×
526
                            double value = get_next(s);
×
527
                            if (log) {
×
528
                                *log << "obj.set<double>(" << col << ", " << value << ");\n";
×
529
                            }
×
530
                            obj.set<double>(col, value);
×
531
                        }
×
532
                        else if (type == type_Link) {
×
533
                            TableRef target = t->get_link_target(col);
×
534
                            if (target->size() > 0) {
×
535
                                ObjKey target_key = target->get_object(get_next(s) % target->size()).get_key();
×
536
                                if (log) {
×
537
                                    *log << "obj.set<Key>(" << col << ", " << target_key << ");\n";
×
538
                                }
×
539
                                obj.set(col, target_key);
×
540
                            }
×
541
                        }
×
542
                        else if (type == type_LinkList) {
×
543
                            TableRef target = t->get_link_target(col);
×
544
                            if (target->size() > 0) {
×
545
                                LnkLst links = obj.get_linklist(col);
×
546
                                ObjKey target_key = target->get_object(get_next(s) % target->size()).get_key();
×
547
                                // either add or set, 50/50 probability
548
                                if (links.size() > 0 && get_next(s) > 128) {
×
549
                                    size_t linklist_row = get_next(s) % links.size();
×
550
                                    if (log) {
×
551
                                        *log << "obj.get_linklist(" << col << ")->set(" << linklist_row << ", "
×
552
                                             << target_key << ");\n";
×
553
                                    }
×
554
                                    links.set(linklist_row, target_key);
×
555
                                }
×
556
                                else {
×
557
                                    if (log) {
×
558
                                        *log << "obj.get_linklist(" << col << ")->add(" << target_key << ");\n";
×
559
                                    }
×
560
                                    links.add(target_key);
×
561
                                }
×
562
                            }
×
563
                        }
×
564
                        else if (type == type_Timestamp) {
×
565
                            std::pair<int64_t, int32_t> values = get_timestamp_values(s);
×
566
                            Timestamp value{values.first, values.second};
×
567
                            if (log) {
×
568
                                *log << "obj.set(" << col << ", " << value << ");\n";
×
569
                            }
×
570
                            obj.set(col, value);
×
571
                        }
×
572
                    }
×
573
                    if (log) {
×
574
                        *log << "}\n";
×
575
                    }
×
576
                }
×
577
            }
12✔
578
            else if (instr == REMOVE_OBJECT && wt->size() > 0) {
757✔
579
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
13✔
580
                TableRef t = wt->get_table(table_key);
13✔
581
                if (t->size() > 0) {
13✔
582
                    ObjKey key = t->get_object(get_next(s) % t->size()).get_key();
2✔
583
                    if (log) {
2✔
584
                        *log << "wt->get_table(" << table_key << ")->remove_object(" << key << ");\n";
×
585
                    }
×
586
                    t->remove_object(key);
2✔
587
                }
2✔
588
            }
13✔
589
            else if (instr == REMOVE_RECURSIVE && wt->size() > 0) {
744✔
590
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
14✔
591
                TableRef t = wt->get_table(table_key);
14✔
592
                if (t->size() > 0) {
14✔
593
                    ObjKey key = t->get_object(get_next(s) % t->size()).get_key();
1✔
594
                    if (log) {
1!
595
                        *log << "wt->get_table(" << table_key << ")->remove_object_recursive(" << key << ");\n";
×
596
                    }
×
597
                    t->remove_object_recursive(key);
1✔
598
                }
1✔
599
            }
14✔
600
            else if (instr == ENUMERATE_COLUMN && wt->size() > 0) {
730✔
601
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
7✔
602
                TableRef t = wt->get_table(table_key);
7✔
603
                auto all_col_keys = t->get_column_keys();
7✔
604
                if (!all_col_keys.empty()) {
7✔
605
                    size_t ndx = get_next(s) % all_col_keys.size();
2✔
606
                    ColKey col = all_col_keys[ndx];
2✔
607
                    if (log) {
2!
608
                        *log << "wt->get_table(" << table_key << ")->enumerate_string_column(" << col << ");\n";
×
609
                    }
×
610
                    wt->get_table(table_key)->enumerate_string_column(col);
2✔
611
                }
2✔
612
            }
7✔
613
            else if (instr == COMMIT) {
723✔
614
                if (log) {
42✔
615
                    *log << "wt->commit_and_continue_as_read();\n";
×
616
                }
×
617
                wt->commit_and_continue_as_read();
42✔
618
                REALM_DO_IF_VERIFY(log, wt->verify());
42✔
619
                if (log) {
42✔
620
                    *log << "wt->promote_to_write();\n";
×
621
                }
×
622
                wt->promote_to_write();
42✔
623
                REALM_DO_IF_VERIFY(log, wt->verify());
42✔
624
            }
42✔
625
            else if (instr == ROLLBACK) {
681✔
626
                if (log) {
42✔
627
                    *log << "wt->rollback_and_continue_as_read();\n";
×
628
                }
×
629
                wt->rollback_and_continue_as_read();
42✔
630
                REALM_DO_IF_VERIFY(log, wt->verify());
42✔
631
                if (log) {
42✔
632
                    *log << "wt->promote_to_write();\n";
×
633
                }
×
634
                wt->promote_to_write();
42✔
635
                REALM_DO_IF_VERIFY(log, wt->verify());
42✔
636
            }
42✔
637
            else if (instr == ADVANCE) {
639✔
638
                if (log) {
37✔
639
                    *log << "rt->advance_read();\n";
×
640
                }
×
641
                rt->advance_read();
37✔
642
                REALM_DO_IF_VERIFY(log, rt->verify());
37✔
643
            }
37✔
644
            else if (instr == CLOSE_AND_REOPEN) {
602✔
645
                if (log) {
32✔
646
                    *log << "wt = nullptr;\n";
×
647
                    *log << "rt = nullptr;\n";
×
648
                    *log << "db->close();\n";
×
649
                }
×
650
                wt = nullptr; // transactions must be done/closed before closing the DB.
32✔
651
                rt = nullptr;
32✔
652
                db->close();
32✔
653
                if (log) {
32✔
654
                    *log << "db = DB::create(*hist, path, DBOptions(key));\n";
×
655
                }
×
656
                db = DB::create(*hist, path, DBOptions(encryption_key));
32✔
657
                if (log) {
32✔
658
                    *log << "wt = db_w->start_write();\n";
×
659
                    *log << "rt = db->start_read();\n";
×
660
                }
×
661
                rt = db->start_read();
32✔
662
                wt = db->start_write();
32✔
663
                REALM_DO_IF_VERIFY(log, rt->verify());
32✔
664
            }
32✔
665
            else if (instr == GET_ALL_COLUMN_NAMES && wt->size() > 0) {
570✔
666
                // try to fuzz find this: https://github.com/realm/realm-core/issues/1769
10✔
667
                for (auto table_key : wt->get_table_keys()) {
54✔
668
                    TableRef t = wt->get_table(table_key);
54✔
669
                    auto all_col_keys = t->get_column_keys();
54✔
670
                    for (auto col : all_col_keys) {
43✔
671
                        StringData col_name = t->get_column_name(col);
31✔
672
                        static_cast<void>(col_name);
31✔
673
                    }
31✔
674
                }
54✔
675
            }
18✔
676
            else if (instr == CREATE_TABLE_VIEW && wt->size() > 0) {
552✔
677
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
9✔
678
                TableRef t = wt->get_table(table_key);
9✔
679
                if (log) {
9✔
680
                    *log << "table_views.push_back(wt->get_table(" << table_key << ")->where().find_all());\n";
×
681
                }
×
682
                TableView tv = t->where().find_all();
9✔
683
                table_views.push_back(tv);
9✔
684
            }
9✔
685
            else if (instr == COMPACT) {
543✔
686
                /*
8✔
687
                if (log) {
8✔
688
                    *log << "db_r.close();\n";
8✔
689
                }
8✔
690
                db_r.close();
8✔
691
                if (log) {
8✔
692
                    *log << "wt->commit();\n";
8✔
693
                }
8✔
694
                wt->commit();
8✔
695

8✔
696
                if (log) {
8✔
697
                    *log << "REALM_ASSERT_RELEASE(db_w.compact());\n";
8✔
698
                }
8✔
699
                REALM_ASSERT_RELEASE(db_w.compact());
8✔
700

8✔
701
                if (log) {
8✔
702
                    *log << "wt = db_w.start_write();\n";
8✔
703
                }
8✔
704
                wt = db_w.start_write();
8✔
705
                if (log) {
8✔
706
                    *log << "db_r.open(path, true, DBOptions(key));\n";
8✔
707
                }
8✔
708
                db_r.open(path, true, DBOptions(encryption_key));
8✔
709
                if (log) {
8✔
710
                    *log << "rt = db_r.start_read();\n";
8✔
711
                }
8✔
712
                rt = db_r.start_read();
8✔
713
                REALM_DO_IF_VERIFY(log, rt->verify());
8✔
714
                */
8✔
715
            }
25✔
716
            else if (instr == IS_NULL && rt->size() > 0) {
518✔
717
                TableKey table_key = rt->get_table_keys()[get_next(s) % rt->size()];
4✔
718
                TableRef t = rt->get_table(table_key);
4✔
719
                if (t->get_column_count() > 0 && t->size() > 0) {
4✔
720
                    auto all_col_keys = t->get_column_keys();
×
721
                    size_t ndx = get_next(s) % all_col_keys.size();
×
722
                    ColKey col = all_col_keys[ndx];
×
723
                    ObjKey key = t->get_object(get_int32(s) % t->size()).get_key();
×
724
                    if (log) {
×
725
                        *log << "wt->get_table(" << table_key << ")->get_object(" << key << ").is_null(" << col
×
726
                             << ");\n";
×
727
                    }
×
728
                    bool res = t->get_object(key).is_null(col);
×
729
                    static_cast<void>(res);
×
730
                }
×
731
            }
4✔
732
        }
934✔
733
    }
6✔
734
    catch (const EndOfFile&) {
6✔
735
    }
6✔
736
}
6✔
737

738

739
static void usage(const char* argv[])
740
{
×
741
    fprintf(stderr,
×
742
            "Usage: %s {FILE | --} [--log] [--name NAME] [--prefix PATH]\n"
×
743
            "Where FILE is a instruction file that will be replayed.\n"
×
744
            "Pass -- without argument to read filenames from stdin\n"
×
745
            "Pass --log to have code printed to stdout producing the same instructions.\n"
×
746
            "Pass --name NAME with distinct values when running on multiple threads,\n"
×
747
            "                 to make sure the test don't use the same Realm file\n"
×
748
            "Pass --prefix PATH to supply a path that should be prepended to all filenames\n"
×
749
            "                 read from stdin.\n",
×
750
            argv[0]);
×
751
    exit(1);
×
752
}
×
753

754
int run_fuzzy(int argc, const char* argv[])
755
{
×
756
    std::ostream* log = nullptr;
×
757
    std::string name = "fuzz-test";
×
758
    std::string prefix = "./";
×
759
    bool file_names_from_stdin = false;
×
760

761
    size_t file_arg = size_t(-1);
×
762
    for (size_t i = 1; i < size_t(argc); ++i) {
×
763
        std::string arg = argv[i];
×
764
        if (arg == "--log") {
×
765
            log = &std::cout;
×
766
        }
×
767
        else if (arg == "--") {
×
768
            file_names_from_stdin = true;
×
769
        }
×
770
        else if (arg == "--prefix") {
×
771
            prefix = argv[++i];
×
772
        }
×
773
        else if (arg == "--name") {
×
774
            name = argv[++i];
×
775
        }
×
776
        else {
×
777
            file_arg = i;
×
778
        }
×
779
    }
×
780

781
    if (!file_names_from_stdin && file_arg == size_t(-1)) {
×
782
        usage(argv);
×
783
    }
×
784

785
    disable_sync_to_disk();
×
786

787
    if (file_names_from_stdin) {
×
788
        std::string file_name;
×
789

790
        std::cin >> file_name;
×
791
        while (std::cin) {
×
792
            std::ifstream in(prefix + file_name, std::ios::in | std::ios::binary);
×
793
            if (!in.is_open()) {
×
794
                std::cerr << "Could not open file for reading: " << (prefix + file_name) << std::endl;
×
795
            }
×
796
            else {
×
797
                std::cout << file_name << std::endl;
×
798
                realm::test_util::RealmPathInfo test_context{name};
×
799
                SHARED_GROUP_TEST_PATH(path);
×
800

801
                std::string contents((std::istreambuf_iterator<char>(in)), (std::istreambuf_iterator<char>()));
×
802
                parse_and_apply_instructions(contents, path, log);
×
803
            }
×
804

805
            std::cin >> file_name;
×
806
        }
×
807
    }
×
808
    else {
×
809
        std::ifstream in(argv[file_arg], std::ios::in | std::ios::binary);
×
810
        if (!in.is_open()) {
×
811
            std::cerr << "Could not open file for reading: " << argv[file_arg] << "\n";
×
812
            exit(1);
×
813
        }
×
814

815
        realm::test_util::RealmPathInfo test_context{name};
×
816
        SHARED_GROUP_TEST_PATH(path);
×
817

818
        std::string contents((std::istreambuf_iterator<char>(in)), (std::istreambuf_iterator<char>()));
×
819
        parse_and_apply_instructions(contents, path, log);
×
820
    }
×
821

822
    return 0;
×
823
}
×
824
#else
825
int run_fuzzy(int, const char*[])
826
{
827
    return 0;
828
}
829
#endif
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

© 2025 Coveralls, Inc