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

realm / realm-core / 2657

03 Mar 2025 05:12PM UTC coverage: 91.122% (+0.003%) from 91.119%
2657

push

Evergreen

web-flow
Enable automatic client reset handling for audit Realms (#8072)

Audit Realms typically don't get client resets due to being very short lived, but restarting sync while one is partially uploaded can result in a bad client file ident error which also blocks uploading any other unuploaded files. Standard automatic client reset is pretty inefficient for these files, but it works fine and the optimal thing would be fairly complicated.

102750 of 181548 branches covered (56.6%)

77 of 79 new or added lines in 2 files covered. (97.47%)

61 existing lines in 11 files now uncovered.

217450 of 238635 relevant lines covered (91.12%)

5986068.35 hits per line

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

42.9
/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/disable_sync_to_disk.hpp>
23
#include <realm/index_string.hpp>
24

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

30
#include "util/test_path.hpp"
31

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

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

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

54
namespace {
55

56
struct EndOfFile {};
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
{
9✔
89
    DataType types[] = {type_Int, type_Bool, type_Float, type_Double, type_String, type_Binary, type_Timestamp};
9✔
90

91
    unsigned char mod = c % (sizeof(types) / sizeof(DataType));
9✔
92
    return types[mod];
9✔
93
}
9✔
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,206✔
109

110
const char* get_encryption_key()
111
{
2✔
112
#if REALM_ENABLE_ENCRYPTION
2✔
113
    return "1234567890123456789012345678901123456789012345678901234567890123";
2✔
114
#else
115
    return nullptr;
116
#endif
117
}
2✔
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
{
30✔
167
    std::string str;
30✔
168
    switch (t) {
30✔
169
        case type_Int:
1✔
170
            str = "int_";
1✔
171
            break;
1✔
UNCOV
172
        case type_Bool:
✔
UNCOV
173
            str = "bool_";
×
UNCOV
174
            break;
×
175
        case type_Float:
1✔
176
            str = "float_";
1✔
177
            break;
1✔
178
        case type_Double:
1✔
179
            str = "double_";
1✔
180
            break;
1✔
181
        case type_String:
1✔
182
            str = "string_";
1✔
183
            break;
1✔
184
        case type_Binary:
1✔
185
            str = "binary_";
1✔
186
            break;
1✔
187
        case type_Timestamp:
4✔
188
            str = "date_";
4✔
189
            break;
4✔
190
        case type_Decimal:
✔
191
            str = "decimal_";
×
192
            break;
×
193
        case type_ObjectId:
✔
194
            str = "id_";
×
195
            break;
×
196
        case type_Link:
21✔
197
            str = "link_";
21✔
198
            break;
21✔
199
        case type_TypedLink:
✔
200
            str = "typed_link_";
×
201
            break;
×
202
        case type_UUID:
✔
203
            str = "uuid_";
×
204
            break;
×
205
        case type_Mixed:
✔
206
            str = "any_";
×
207
            break;
×
208
    }
30✔
209
    return str + util::to_string(column_index++);
30✔
210
}
30✔
211

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

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

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

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

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

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

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

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

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

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

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

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

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

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

283
    try {
6✔
284

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

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

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

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

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

UNCOV
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
                        }
×
UNCOV
572
                    }
×
UNCOV
573
                    if (log) {
×
574
                        *log << "}\n";
×
575
                    }
×
UNCOV
576
                }
×
577
            }
13✔
578
            else if (instr == REMOVE_OBJECT && wt->size() > 0) {
802✔
579
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
12✔
580
                TableRef t = wt->get_table(table_key);
12✔
581
                if (t->size() > 0) {
12✔
582
                    ObjKey key = t->get_object(get_next(s) % t->size()).get_key();
1✔
583
                    if (log) {
1✔
584
                        *log << "wt->get_table(" << table_key << ")->remove_object(" << key << ");\n";
×
585
                    }
×
586
                    t->remove_object(key);
1✔
587
                }
1✔
588
            }
12✔
589
            else if (instr == REMOVE_RECURSIVE && wt->size() > 0) {
790✔
590
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
12✔
591
                TableRef t = wt->get_table(table_key);
12✔
592
                if (t->size() > 0) {
12✔
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
            }
12✔
600
            else if (instr == ENUMERATE_COLUMN && wt->size() > 0) {
778✔
601
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
12✔
602
                TableRef t = wt->get_table(table_key);
12✔
603
                auto all_col_keys = t->get_column_keys();
12✔
604
                if (!all_col_keys.empty()) {
12✔
605
                    size_t ndx = get_next(s) % all_col_keys.size();
4✔
606
                    ColKey col = all_col_keys[ndx];
4✔
607
                    if (log) {
4✔
608
                        *log << "wt->get_table(" << table_key << ")->enumerate_string_column(" << col << ");\n";
×
609
                    }
×
610
                    wt->get_table(table_key)->enumerate_string_column(col);
4✔
611
                }
4✔
612
            }
12✔
613
            else if (instr == COMMIT) {
766✔
614
                if (log) {
33✔
615
                    *log << "wt->commit_and_continue_as_read();\n";
×
616
                }
×
617
                wt->commit_and_continue_as_read();
33✔
618
                REALM_DO_IF_VERIFY(log, wt->verify());
33✔
619
                if (log) {
33✔
620
                    *log << "wt->promote_to_write();\n";
×
621
                }
×
622
                wt->promote_to_write();
33✔
623
                REALM_DO_IF_VERIFY(log, wt->verify());
33✔
624
            }
33✔
625
            else if (instr == ROLLBACK) {
733✔
626
                if (log) {
54✔
627
                    *log << "wt->rollback_and_continue_as_read();\n";
×
628
                }
×
629
                wt->rollback_and_continue_as_read();
54✔
630
                REALM_DO_IF_VERIFY(log, wt->verify());
54✔
631
                if (log) {
54✔
632
                    *log << "wt->promote_to_write();\n";
×
633
                }
×
634
                wt->promote_to_write();
54✔
635
                REALM_DO_IF_VERIFY(log, wt->verify());
54✔
636
            }
54✔
637
            else if (instr == ADVANCE) {
679✔
638
                if (log) {
41✔
639
                    *log << "rt->advance_read();\n";
×
640
                }
×
641
                rt->advance_read();
41✔
642
                REALM_DO_IF_VERIFY(log, rt->verify());
41✔
643
            }
41✔
644
            else if (instr == CLOSE_AND_REOPEN) {
638✔
645
                if (log) {
44✔
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.
44✔
651
                rt = nullptr;
44✔
652
                db->close();
44✔
653
                if (log) {
44✔
654
                    *log << "db = DB::create(*hist, path, DBOptions(key));\n";
×
655
                }
×
656
                db = DB::create(*hist, path, DBOptions(encryption_key));
44✔
657
                if (log) {
44✔
658
                    *log << "wt = db_w->start_write();\n";
×
659
                    *log << "rt = db->start_read();\n";
×
660
                }
×
661
                rt = db->start_read();
44✔
662
                wt = db->start_write();
44✔
663
                REALM_DO_IF_VERIFY(log, rt->verify());
44✔
664
            }
44✔
665
            else if (instr == GET_ALL_COLUMN_NAMES && wt->size() > 0) {
594✔
666
                // try to fuzz find this: https://github.com/realm/realm-core/issues/1769
667
                for (auto table_key : wt->get_table_keys()) {
19✔
668
                    TableRef t = wt->get_table(table_key);
19✔
669
                    auto all_col_keys = t->get_column_keys();
19✔
670
                    for (auto col : all_col_keys) {
19✔
671
                        StringData col_name = t->get_column_name(col);
8✔
672
                        static_cast<void>(col_name);
8✔
673
                    }
8✔
674
                }
19✔
675
            }
11✔
676
            else if (instr == CREATE_TABLE_VIEW && wt->size() > 0) {
583✔
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) {
574✔
686
                /*
687
                if (log) {
688
                    *log << "db_r.close();\n";
689
                }
690
                db_r.close();
691
                if (log) {
692
                    *log << "wt->commit();\n";
693
                }
694
                wt->commit();
695

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

701
                if (log) {
702
                    *log << "wt = db_w.start_write();\n";
703
                }
704
                wt = db_w.start_write();
705
                if (log) {
706
                    *log << "db_r.open(path, true, DBOptions(key));\n";
707
                }
708
                db_r.open(path, true, DBOptions(encryption_key));
709
                if (log) {
710
                    *log << "rt = db_r.start_read();\n";
711
                }
712
                rt = db_r.start_read();
713
                REALM_DO_IF_VERIFY(log, rt->verify());
714
                */
715
            }
31✔
716
            else if (instr == IS_NULL && rt->size() > 0) {
543✔
717
                TableKey table_key = rt->get_table_keys()[get_next(s) % rt->size()];
5✔
718
                TableRef t = rt->get_table(table_key);
5✔
719
                if (t->get_column_count() > 0 && t->size() > 0) {
5✔
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
            }
5✔
732
        }
969✔
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