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

realm / realm-core / 2294

02 May 2024 08:09PM UTC coverage: 90.761% (+0.01%) from 90.747%
2294

push

Evergreen

web-flow
Fix a deadlock when accessing current user from inside an App listener (#7671)

App::switch_user() emitted changes without first releasing the lock on
m_user_mutex, leading to a deadlock if anyone inside the listener tried to
acquire the mutex. The rest of the places where we emitted changes were
correct.

The newly added wrapper catches this error when building with clang.

101982 of 180246 branches covered (56.58%)

14 of 17 new or added lines in 2 files covered. (82.35%)

66 existing lines in 15 files now uncovered.

212573 of 234212 relevant lines covered (90.76%)

5645071.85 hits per line

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

53.11
/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 {                                                                                                             \
211✔
43
        if (log)                                                                                                     \
211✔
44
            *log << #op << ";\n";                                                                                    \
211✔
45
        op;                                                                                                          \
211✔
46
    } while (false)
211✔
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
enum INS {
58
    ADD_TABLE,
59
    REMOVE_TABLE,
60
    CREATE_OBJECT,
61
    RENAME_COLUMN,
62
    ADD_COLUMN,
63
    REMOVE_COLUMN,
64
    SET,
65
    REMOVE_OBJECT,
66
    REMOVE_RECURSIVE,
67
    ADD_COLUMN_LINK,
68
    ADD_COLUMN_LINK_LIST,
69
    CLEAR_TABLE,
70
    ADD_SEARCH_INDEX,
71
    REMOVE_SEARCH_INDEX,
72
    COMMIT,
73
    ROLLBACK,
74
    ADVANCE,
75
    MOVE_LAST_OVER,
76
    CLOSE_AND_REOPEN,
77
    GET_ALL_COLUMN_NAMES,
78
    CREATE_TABLE_VIEW,
79
    COMPACT,
80
    IS_NULL,
81
    ENUMERATE_COLUMN,
82

83
    COUNT
84
};
85

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

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

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

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

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

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

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

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

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

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

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

211
std::string create_table_name()
212
{
37✔
213
    std::string str = "Table_";
37✔
214
    return str + util::to_string(table_index++);
37✔
215
}
37✔
216

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

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

231
void parse_and_apply_instructions(std::string& in, const std::string& path, std::ostream* log)
232
{
6✔
233
    const size_t add_empty_row_max = REALM_MAX_BPNODE_SIZE * REALM_MAX_BPNODE_SIZE + 1000;
6✔
234
    const size_t max_tables = REALM_MAX_BPNODE_SIZE * 10;
6✔
235

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

241
    State s;
6✔
242
    s.str = in;
6✔
243
    s.pos = 0;
6✔
244

245
    // const bool use_encryption = false;
246
    const bool use_encryption = get_next(s) % 2 == 0;
6✔
247
    const char* encryption_key = use_encryption ? get_encryption_key() : nullptr;
6✔
248

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

261
        *log << "SHARED_GROUP_TEST_PATH(path);\n";
×
262

263
        *log << "const char* key = " << printable_key << ";\n";
×
264
        *log << "std::unique_ptr<Replication> hist(make_in_realm_history());\n";
×
265

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

271
        *log << "\n";
×
272
    }
×
273

274
    std::unique_ptr<Replication> hist(make_in_realm_history());
6✔
275

276
    DBOptions options(encryption_key);
6✔
277
    DBRef db = DB::create(*hist, path, options);
6✔
278
    auto wt = db->start_write();
6✔
279
    auto rt = db->start_read();
6✔
280
    std::vector<TableView> table_views;
6✔
281

282
    try {
6✔
283

284
        for (;;) {
840✔
285
            char instr = get_next(s) % COUNT;
840✔
286
            iteration++;
840✔
287

288
            /* This can help when debugging
289
            if (log) {
290
                *log << iteration << " ";
291
            }
292
            */
293

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

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

451
                    // With equal probability, either set to null or to a value
452
                    if (get_next(s) % 2 == 0 && t->is_nullable(col)) {
4✔
453
                        if (type == type_Link) {
1✔
454
                            if (log) {
1✔
455
                                *log << "obj.set(" << col << ", null_key);\n";
×
456
                            }
×
457
                            obj.set(col, null_key);
1✔
458
                        }
1✔
459
                        else {
×
460
                            if (log) {
×
461
                                *log << "obj.set_null(" << col << ");\n";
×
462
                            }
×
463
                            obj.set_null(col);
×
464
                        }
×
465
                    }
1✔
466
                    else {
3✔
467
                        if (type == type_String) {
3✔
468
                            std::string str = create_string(get_next(s));
×
469
                            if (log) {
×
470
                                *log << "obj.set(" << col << ", \"" << str << "\");\n";
×
471
                            }
×
472
                            obj.set(col, StringData(str));
×
473
                        }
×
474
                        else if (type == type_Binary) {
3✔
475
                            std::string str = create_string(get_next(s));
×
476
                            if (log) {
×
477
                                *log << "obj.set<Binary>(" << col << ", BinaryData{\"" << str << "\", " << str.size()
×
478
                                     << "});\n";
×
479
                            }
×
480
                            obj.set<Binary>(col, BinaryData(str));
×
481
                        }
×
482
                        else if (type == type_Int) {
3✔
483
                            bool add_int = get_next(s) % 2 == 0;
×
484
                            int64_t value = get_int64(s);
×
485
                            if (add_int) {
×
486
                                if (log) {
×
487
                                    *log << "try { obj.add_int(" << col << ", " << value
×
488
                                         << "); } catch (const LogicError& le) { CHECK(le.kind() == "
×
489
                                            "LogicError::illegal_combination); }\n";
×
490
                                }
×
491
                                try {
×
492
                                    obj.add_int(col, value);
×
493
                                }
×
494
                                catch (const LogicError& le) {
×
495
                                    if (le.code() != ErrorCodes::IllegalOperation) {
×
496
                                        throw;
×
497
                                    }
×
498
                                }
×
499
                            }
×
500
                            else {
×
501
                                if (log) {
×
502
                                    *log << "obj.set<Int>(" << col << ", " << value << ");\n";
×
503
                                }
×
504
                                obj.set<Int>(col, value);
×
505
                            }
×
506
                        }
×
507
                        else if (type == type_Bool) {
3✔
508
                            bool value = get_next(s) % 2 == 0;
×
509
                            if (log) {
×
510
                                *log << "obj.set<Bool>(" << col << ", " << (value ? "true" : "false") << ");\n";
×
511
                            }
×
512
                            obj.set<Bool>(col, value);
×
513
                        }
×
514
                        else if (type == type_Float) {
3✔
515
                            float value = get_next(s);
×
516
                            if (log) {
×
517
                                *log << "obj.set<Float>(" << col << ", " << value << ");\n";
×
518
                            }
×
519
                            obj.set<Float>(col, value);
×
520
                        }
×
521
                        else if (type == type_Double) {
3✔
522
                            double value = get_next(s);
×
523
                            if (log) {
×
524
                                *log << "obj.set<double>(" << col << ", " << value << ");\n";
×
525
                            }
×
526
                            obj.set<double>(col, value);
×
527
                        }
×
528
                        else if (type == type_Link) {
3✔
UNCOV
529
                            if (col.is_list()) {
×
530
                                TableRef target = t->get_link_target(col);
×
531
                                if (target->size() > 0) {
×
532
                                    LnkLst links = obj.get_linklist(col);
×
533
                                    ObjKey target_key = target->get_object(get_next(s) % target->size()).get_key();
×
534
                                    // either add or set, 50/50 probability
535
                                    if (links.size() > 0 && get_next(s) > 128) {
×
536
                                        size_t linklist_row = get_next(s) % links.size();
×
537
                                        if (log) {
×
538
                                            *log << "obj.get_linklist(" << col << ")->set(" << linklist_row << ", "
×
539
                                                 << target_key << ");\n";
×
540
                                        }
×
541
                                        links.set(linklist_row, target_key);
×
542
                                    }
×
543
                                    else {
×
544
                                        if (log) {
×
545
                                            *log << "obj.get_linklist(" << col << ")->add(" << target_key << ");\n";
×
546
                                        }
×
547
                                        links.add(target_key);
×
548
                                    }
×
549
                                }
×
550
                            }
×
UNCOV
551
                            else {
×
UNCOV
552
                                TableRef target = t->get_link_target(col);
×
UNCOV
553
                                if (target->size() > 0) {
×
UNCOV
554
                                    ObjKey target_key = target->get_object(get_next(s) % target->size()).get_key();
×
UNCOV
555
                                    if (log) {
×
556
                                        *log << "obj.set<Key>(" << col << ", " << target_key << ");\n";
×
557
                                    }
×
UNCOV
558
                                    obj.set(col, target_key);
×
UNCOV
559
                                }
×
UNCOV
560
                            }
×
UNCOV
561
                        }
×
562

563
                        else if (type == type_Timestamp) {
3✔
564
                            std::pair<int64_t, int32_t> values = get_timestamp_values(s);
3✔
565
                            Timestamp value{values.first, values.second};
3✔
566
                            if (log) {
3✔
567
                                *log << "obj.set(" << col << ", " << value << ");\n";
×
568
                            }
×
569
                            obj.set(col, value);
3✔
570
                        }
3✔
571
                    }
3✔
572
                    if (log) {
4✔
573
                        *log << "}\n";
×
574
                    }
×
575
                }
4✔
576
            }
9✔
577
            else if (instr == REMOVE_OBJECT && wt->size() > 0) {
661✔
578
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
17✔
579
                TableRef t = wt->get_table(table_key);
17✔
580
                if (t->size() > 0) {
17✔
581
                    ObjKey key = t->get_object(get_next(s) % t->size()).get_key();
5✔
582
                    if (log) {
5✔
583
                        *log << "wt->get_table(" << table_key << ")->remove_object(" << key << ");\n";
×
584
                    }
×
585
                    t->remove_object(key);
5✔
586
                }
5✔
587
            }
17✔
588
            else if (instr == REMOVE_RECURSIVE && wt->size() > 0) {
644✔
589
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
18✔
590
                TableRef t = wt->get_table(table_key);
18✔
591
                if (t->size() > 0) {
18✔
592
                    ObjKey key = t->get_object(get_next(s) % t->size()).get_key();
6✔
593
                    if (log) {
6✔
594
                        *log << "wt->get_table(" << table_key << ")->remove_object_recursive(" << key << ");\n";
×
595
                    }
×
596
                    t->remove_object_recursive(key);
6✔
597
                }
6✔
598
            }
18✔
599
            else if (instr == ENUMERATE_COLUMN && wt->size() > 0) {
626✔
600
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
11✔
601
                TableRef t = wt->get_table(table_key);
11✔
602
                auto all_col_keys = t->get_column_keys();
11✔
603
                if (!all_col_keys.empty()) {
11✔
604
                    size_t ndx = get_next(s) % all_col_keys.size();
5✔
605
                    ColKey col = all_col_keys[ndx];
5✔
606
                    if (log) {
5✔
607
                        *log << "wt->get_table(" << table_key << ")->enumerate_string_column(" << col << ");\n";
×
608
                    }
×
609
                    wt->get_table(table_key)->enumerate_string_column(col);
5✔
610
                }
5✔
611
            }
11✔
612
            else if (instr == COMMIT) {
615✔
613
                if (log) {
30✔
614
                    *log << "wt->commit_and_continue_as_read();\n";
×
615
                }
×
616
                wt->commit_and_continue_as_read();
30✔
617
                REALM_DO_IF_VERIFY(log, wt->verify());
30✔
618
                if (log) {
30✔
619
                    *log << "wt->promote_to_write();\n";
×
620
                }
×
621
                wt->promote_to_write();
30✔
622
                REALM_DO_IF_VERIFY(log, wt->verify());
30✔
623
            }
30✔
624
            else if (instr == ROLLBACK) {
585✔
625
                if (log) {
38✔
626
                    *log << "wt->rollback_and_continue_as_read();\n";
×
627
                }
×
628
                wt->rollback_and_continue_as_read();
38✔
629
                REALM_DO_IF_VERIFY(log, wt->verify());
38✔
630
                if (log) {
38✔
631
                    *log << "wt->promote_to_write();\n";
×
632
                }
×
633
                wt->promote_to_write();
38✔
634
                REALM_DO_IF_VERIFY(log, wt->verify());
38✔
635
            }
38✔
636
            else if (instr == ADVANCE) {
547✔
637
                if (log) {
38✔
638
                    *log << "rt->advance_read();\n";
×
639
                }
×
640
                rt->advance_read();
38✔
641
                REALM_DO_IF_VERIFY(log, rt->verify());
38✔
642
            }
38✔
643
            else if (instr == CLOSE_AND_REOPEN) {
509✔
644
                if (log) {
37✔
645
                    *log << "wt = nullptr;\n";
×
646
                    *log << "rt = nullptr;\n";
×
647
                    *log << "db->close();\n";
×
648
                }
×
649
                wt = nullptr; // transactions must be done/closed before closing the DB.
37✔
650
                rt = nullptr;
37✔
651
                db->close();
37✔
652
                if (log) {
37✔
653
                    *log << "db = DB::create(*hist, path, DBOptions(key));\n";
×
654
                }
×
655
                db = DB::create(*hist, path, DBOptions(encryption_key));
37✔
656
                if (log) {
37✔
657
                    *log << "wt = db_w->start_write();\n";
×
658
                    *log << "rt = db->start_read();\n";
×
659
                }
×
660
                rt = db->start_read();
37✔
661
                wt = db->start_write();
37✔
662
                REALM_DO_IF_VERIFY(log, rt->verify());
37✔
663
            }
37✔
664
            else if (instr == GET_ALL_COLUMN_NAMES && wt->size() > 0) {
472✔
665
                // try to fuzz find this: https://github.com/realm/realm-core/issues/1769
666
                for (auto table_key : wt->get_table_keys()) {
16✔
667
                    TableRef t = wt->get_table(table_key);
16✔
668
                    auto all_col_keys = t->get_column_keys();
16✔
669
                    for (auto col : all_col_keys) {
16✔
670
                        StringData col_name = t->get_column_name(col);
9✔
671
                        static_cast<void>(col_name);
9✔
672
                    }
9✔
673
                }
16✔
674
            }
12✔
675
            else if (instr == CREATE_TABLE_VIEW && wt->size() > 0) {
460✔
676
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
13✔
677
                TableRef t = wt->get_table(table_key);
13✔
678
                if (log) {
13✔
679
                    *log << "table_views.push_back(wt->get_table(" << table_key << ")->where().find_all());\n";
×
680
                }
×
681
                TableView tv = t->where().find_all();
13✔
682
                table_views.push_back(tv);
13✔
683
            }
13✔
684
            else if (instr == COMPACT) {
447✔
685
                /*
686
                if (log) {
687
                    *log << "db_r.close();\n";
688
                }
689
                db_r.close();
690
                if (log) {
691
                    *log << "wt->commit();\n";
692
                }
693
                wt->commit();
694

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

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

737

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

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

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

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

784
    disable_sync_to_disk();
×
785

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

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

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

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

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

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

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