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

realm / realm-core / 2451

29 Jun 2024 12:42AM UTC coverage: 90.99% (+0.03%) from 90.961%
2451

push

Evergreen

web-flow
RCORE-2175 RCORE-2176 RCORE-2182 RCORE-2083 Fix several problems with object removal and links (#7830)

* fix change of mode from Strong to All when deleting embedded objects that link to a tombstone

* fix remove recursive to follow mixed links

* fix removal of backlinks from mixed in large clusters

* add more test coverage to verify single link removal

* Replace ObjKey with RowKey in selected places

* fix a few more type casts

* touchup changelog

---------

Co-authored-by: Jørgen Edelbo <jorgen.edelbo@mongodb.com>

102368 of 180506 branches covered (56.71%)

371 of 382 new or added lines in 8 files covered. (97.12%)

50 existing lines in 11 files now uncovered.

215226 of 236537 relevant lines covered (90.99%)

5659844.03 hits per line

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

51.04
/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 {                                                                                                             \
213✔
44
        if (log)                                                                                                     \
213✔
45
            *log << #op << ";\n";                                                                                    \
213✔
46
        op;                                                                                                          \
213✔
47
    } while (false)
213✔
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
{
15✔
89
    DataType types[] = {type_Int, type_Bool, type_Float, type_Double, type_String, type_Binary, type_Timestamp};
15✔
90

91
    unsigned char mod = c % (sizeof(types) / sizeof(DataType));
15✔
92
    return types[mod];
15✔
93
}
15✔
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
{
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)
UNCOV
120
{
×
UNCOV
121
    int64_t v = 0;
×
UNCOV
122
    for (size_t t = 0; t < 8; t++) {
×
UNCOV
123
        unsigned char c = get_next(s);
×
UNCOV
124
        *(reinterpret_cast<signed char*>(&v) + t) = c;
×
UNCOV
125
    }
×
UNCOV
126
    return v;
×
UNCOV
127
}
×
128

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

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

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

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

165
std::string create_column_name(DataType t)
166
{
46✔
167
    std::string str;
46✔
168
    switch (t) {
46✔
169
        case type_Int:
2✔
170
            str = "int_";
2✔
171
            break;
2✔
172
        case type_Bool:
1✔
173
            str = "bool_";
1✔
174
            break;
1✔
175
        case type_Float:
1✔
176
            str = "float_";
1✔
177
            break;
1✔
178
        case type_Double:
2✔
179
            str = "double_";
2✔
180
            break;
2✔
181
        case type_String:
1✔
182
            str = "string_";
1✔
183
            break;
1✔
184
        case type_Binary:
6✔
185
            str = "binary_";
6✔
186
            break;
6✔
187
        case type_Timestamp:
3✔
188
            str = "date_";
3✔
189
            break;
3✔
190
        case type_Decimal:
✔
191
            str = "decimal_";
×
192
            break;
×
193
        case type_ObjectId:
✔
194
            str = "id_";
×
195
            break;
×
196
        case type_Link:
30✔
197
            str = "link_";
30✔
198
            break;
30✔
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
    }
46✔
209
    return str + util::to_string(column_index++);
46✔
210
}
46✔
211

212
std::string create_table_name()
213
{
48✔
214
    std::string str = "Table_";
48✔
215
    return str + util::to_string(table_index++);
48✔
216
}
48✔
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 (;;) {
827✔
286
            char instr = get_next(s) % COUNT;
827✔
287
            iteration++;
827✔
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) {
827✔
296
                std::string name = create_table_name();
48✔
297
                if (log) {
48✔
298
                    *log << "wt->add_table(\"" << name << "\");\n";
×
299
                }
×
300
                wt->add_table(name);
48✔
301
            }
48✔
302
            else if (instr == REMOVE_TABLE && wt->size() > 0) {
779✔
303
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
20✔
304
                if (log) {
20✔
305
                    *log << "try { wt->remove_table(" << table_key
×
306
                         << "); }"
×
307
                            " catch (const CrossTableLinkTarget&) { }\n";
×
308
                }
×
309
                try {
20✔
310
                    wt->remove_table(table_key);
20✔
311
                }
20✔
312
                catch (const CrossTableLinkTarget&) {
20✔
313
                    if (log) {
2✔
314
                        *log << "// Exception\n";
×
315
                    }
×
316
                }
2✔
317
            }
20✔
318
            else if (instr == CLEAR_TABLE && wt->size() > 0) {
759✔
319
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
18✔
320
                if (log) {
18✔
321
                    *log << "wt->get_table(" << table_key << ")->clear();\n";
×
322
                }
×
323
                wt->get_table(table_key)->clear();
18✔
324
            }
18✔
325
            else if (instr == CREATE_OBJECT && wt->size() > 0) {
741✔
326
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
18✔
327
                size_t num_rows = get_next(s);
18✔
328
                if (wt->get_table(table_key)->size() + num_rows < max_rows) {
18✔
329
                    if (log) {
18✔
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;
18✔
334
                    wt->get_table(table_key)->create_objects(num_rows % add_empty_row_max, keys);
18✔
335
                }
18✔
336
            }
18✔
337
            else if (instr == ADD_COLUMN && wt->size() > 0) {
723✔
338
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
15✔
339
                DataType type = get_type(get_next(s));
15✔
340
                std::string name = create_column_name(type);
15✔
341
                // Mixed cannot be nullable. For other types, chose nullability randomly
342
                bool nullable = (get_next(s) % 2 == 0);
15✔
343
                if (log) {
15✔
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);
15✔
348
                if (log) {
15✔
349
                    *log << " // -> " << col << "\n";
×
350
                }
×
351
            }
15✔
352
            else if (instr == REMOVE_COLUMN && wt->size() > 0) {
708✔
353
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
22✔
354
                TableRef t = wt->get_table(table_key);
22✔
355
                auto column_keys = t->get_column_keys();
22✔
356
                if (!column_keys.empty()) {
22✔
357
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
5✔
358
                    if (log) {
5✔
359
                        *log << "wt->get_table(" << table_key << ")->remove_column(" << col << ");\n";
×
360
                    }
×
361
                    t->remove_column(col);
5✔
362
                }
5✔
363
            }
22✔
364
            else if (instr == RENAME_COLUMN && wt->size() > 0) {
686✔
365
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
12✔
366
                TableRef t = wt->get_table(table_key);
12✔
367
                auto column_keys = t->get_column_keys();
12✔
368
                if (!column_keys.empty()) {
12✔
369
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
4✔
370
                    std::string name = create_column_name(t->get_column_type(col));
4✔
371
                    if (log) {
4✔
372
                        *log << "wt->get_table(" << table_key << ")->rename_column(" << col << ", \"" << name
×
373
                             << "\");\n";
×
374
                    }
×
375
                    t->rename_column(col, name);
4✔
376
                }
4✔
377
            }
12✔
378
            else if (instr == ADD_SEARCH_INDEX && wt->size() > 0) {
674✔
379
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
12✔
380
                TableRef t = wt->get_table(table_key);
12✔
381
                auto column_keys = t->get_column_keys();
12✔
382
                if (!column_keys.empty()) {
12✔
383
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
6✔
384
                    bool supports_search_index = StringIndex::type_supported(t->get_column_type(col));
6✔
385

386
                    if (supports_search_index) {
6✔
387
                        if (log) {
1✔
388
                            *log << "wt->get_table(" << table_key << ")->add_search_index(" << col << ");\n";
×
389
                        }
×
390
                        t->add_search_index(col);
1✔
391
                    }
1✔
392
                }
6✔
393
            }
12✔
394
            else if (instr == REMOVE_SEARCH_INDEX && wt->size() > 0) {
662✔
395
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
20✔
396
                TableRef t = wt->get_table(table_key);
20✔
397
                auto column_keys = t->get_column_keys();
20✔
398
                if (!column_keys.empty()) {
20✔
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
            }
20✔
409
            else if (instr == ADD_COLUMN_LINK && wt->size() >= 1) {
642✔
410
                TableKey table_key_1 = wt->get_table_keys()[get_next(s) % wt->size()];
19✔
411
                TableKey table_key_2 = wt->get_table_keys()[get_next(s) % wt->size()];
19✔
412
                TableRef t1 = wt->get_table(table_key_1);
19✔
413
                TableRef t2 = wt->get_table(table_key_2);
19✔
414
                std::string name = create_column_name(type_Link);
19✔
415
                if (log) {
19✔
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);
19✔
420
                if (log) {
19✔
421
                    *log << " // -> " << col << "\n";
×
422
                }
×
423
            }
19✔
424
            else if (instr == ADD_COLUMN_LINK_LIST && wt->size() >= 2) {
623✔
425
                TableKey table_key_1 = wt->get_table_keys()[get_next(s) % wt->size()];
9✔
426
                TableKey table_key_2 = wt->get_table_keys()[get_next(s) % wt->size()];
9✔
427
                TableRef t1 = wt->get_table(table_key_1);
9✔
428
                TableRef t2 = wt->get_table(table_key_2);
9✔
429
                std::string name = create_column_name(type_Link);
9✔
430
                if (log) {
9✔
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);
9✔
435
                if (log) {
9✔
436
                    *log << " // -> " << col << "\n";
×
437
                }
×
438
            }
9✔
439
            else if (instr == SET && wt->size() > 0) {
614✔
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✔
444
                    ColKey col = all_col_keys[get_next(s) % all_col_keys.size()];
3✔
445
                    size_t row = get_next(s) % t->size();
3✔
446
                    DataType type = t->get_column_type(col);
3✔
447
                    Obj obj = t->get_object(row);
3✔
448
                    if (log) {
3✔
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
453
                    if (get_next(s) % 2 == 0 && t->is_nullable(col)) {
3✔
454
                        if (type == type_Link) {
2✔
455
                            if (log) {
2✔
456
                                *log << "obj.set(" << col << ", null_key);\n";
×
457
                            }
×
458
                            obj.set(col, null_key);
2✔
459
                        }
2✔
460
                        else {
×
461
                            if (log) {
×
462
                                *log << "obj.set_null(" << col << ");\n";
×
463
                            }
×
464
                            obj.set_null(col);
×
465
                        }
×
466
                    }
2✔
467
                    else {
1✔
468
                        if (type == type_String) {
1✔
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
                        }
×
475
                        else if (type == type_Binary) {
1✔
476
                            std::string str = create_string(get_next(s));
1✔
477
                            if (log) {
1✔
478
                                *log << "obj.set<Binary>(" << col << ", BinaryData{\"" << str << "\", " << str.size()
×
479
                                     << "});\n";
×
480
                            }
×
481
                            obj.set<Binary>(col, BinaryData(str));
1✔
482
                        }
1✔
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) {
×
UNCOV
565
                            std::pair<int64_t, int32_t> values = get_timestamp_values(s);
×
UNCOV
566
                            Timestamp value{values.first, values.second};
×
UNCOV
567
                            if (log) {
×
568
                                *log << "obj.set(" << col << ", " << value << ");\n";
×
569
                            }
×
UNCOV
570
                            obj.set(col, value);
×
UNCOV
571
                        }
×
572
                    }
1✔
573
                    if (log) {
3✔
574
                        *log << "}\n";
×
575
                    }
×
576
                }
3✔
577
            }
13✔
578
            else if (instr == REMOVE_OBJECT && wt->size() > 0) {
601✔
579
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
15✔
580
                TableRef t = wt->get_table(table_key);
15✔
581
                if (t->size() > 0) {
15✔
582
                    ObjKey key = t->get_object(get_next(s) % t->size()).get_key();
6✔
583
                    if (log) {
6✔
584
                        *log << "wt->get_table(" << table_key << ")->remove_object(" << key << ");\n";
×
585
                    }
×
586
                    t->remove_object(key);
6✔
587
                }
6✔
588
            }
15✔
589
            else if (instr == REMOVE_RECURSIVE && wt->size() > 0) {
586✔
590
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
11✔
591
                TableRef t = wt->get_table(table_key);
11✔
592
                if (t->size() > 0) {
11✔
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
            }
11✔
600
            else if (instr == ENUMERATE_COLUMN && wt->size() > 0) {
575✔
601
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
20✔
602
                TableRef t = wt->get_table(table_key);
20✔
603
                auto all_col_keys = t->get_column_keys();
20✔
604
                if (!all_col_keys.empty()) {
20✔
605
                    size_t ndx = get_next(s) % all_col_keys.size();
10✔
606
                    ColKey col = all_col_keys[ndx];
10✔
607
                    if (log) {
10✔
608
                        *log << "wt->get_table(" << table_key << ")->enumerate_string_column(" << col << ");\n";
×
609
                    }
×
610
                    wt->get_table(table_key)->enumerate_string_column(col);
10✔
611
                }
10✔
612
            }
20✔
613
            else if (instr == COMMIT) {
555✔
614
                if (log) {
39✔
615
                    *log << "wt->commit_and_continue_as_read();\n";
×
616
                }
×
617
                wt->commit_and_continue_as_read();
39✔
618
                REALM_DO_IF_VERIFY(log, wt->verify());
39✔
619
                if (log) {
39✔
620
                    *log << "wt->promote_to_write();\n";
×
621
                }
×
622
                wt->promote_to_write();
39✔
623
                REALM_DO_IF_VERIFY(log, wt->verify());
39✔
624
            }
39✔
625
            else if (instr == ROLLBACK) {
516✔
626
                if (log) {
43✔
627
                    *log << "wt->rollback_and_continue_as_read();\n";
×
628
                }
×
629
                wt->rollback_and_continue_as_read();
43✔
630
                REALM_DO_IF_VERIFY(log, wt->verify());
43✔
631
                if (log) {
43✔
632
                    *log << "wt->promote_to_write();\n";
×
633
                }
×
634
                wt->promote_to_write();
43✔
635
                REALM_DO_IF_VERIFY(log, wt->verify());
43✔
636
            }
43✔
637
            else if (instr == ADVANCE) {
473✔
638
                if (log) {
28✔
639
                    *log << "rt->advance_read();\n";
×
640
                }
×
641
                rt->advance_read();
28✔
642
                REALM_DO_IF_VERIFY(log, rt->verify());
28✔
643
            }
28✔
644
            else if (instr == CLOSE_AND_REOPEN) {
445✔
645
                if (log) {
21✔
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.
21✔
651
                rt = nullptr;
21✔
652
                db->close();
21✔
653
                if (log) {
21✔
654
                    *log << "db = DB::create(*hist, path, DBOptions(key));\n";
×
655
                }
×
656
                db = DB::create(*hist, path, DBOptions(encryption_key));
21✔
657
                if (log) {
21✔
658
                    *log << "wt = db_w->start_write();\n";
×
659
                    *log << "rt = db->start_read();\n";
×
660
                }
×
661
                rt = db->start_read();
21✔
662
                wt = db->start_write();
21✔
663
                REALM_DO_IF_VERIFY(log, rt->verify());
21✔
664
            }
21✔
665
            else if (instr == GET_ALL_COLUMN_NAMES && wt->size() > 0) {
424✔
666
                // try to fuzz find this: https://github.com/realm/realm-core/issues/1769
667
                for (auto table_key : wt->get_table_keys()) {
35✔
668
                    TableRef t = wt->get_table(table_key);
35✔
669
                    auto all_col_keys = t->get_column_keys();
35✔
670
                    for (auto col : all_col_keys) {
35✔
671
                        StringData col_name = t->get_column_name(col);
14✔
672
                        static_cast<void>(col_name);
14✔
673
                    }
14✔
674
                }
35✔
675
            }
17✔
676
            else if (instr == CREATE_TABLE_VIEW && wt->size() > 0) {
407✔
677
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
16✔
678
                TableRef t = wt->get_table(table_key);
16✔
679
                if (log) {
16✔
680
                    *log << "table_views.push_back(wt->get_table(" << table_key << ")->where().find_all());\n";
×
681
                }
×
682
                TableView tv = t->where().find_all();
16✔
683
                table_views.push_back(tv);
16✔
684
            }
16✔
685
            else if (instr == COMPACT) {
391✔
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
            }
23✔
716
            else if (instr == IS_NULL && rt->size() > 0) {
368✔
717
                TableKey table_key = rt->get_table_keys()[get_next(s) % rt->size()];
3✔
718
                TableRef t = rt->get_table(table_key);
3✔
719
                if (t->get_column_count() > 0 && t->size() > 0) {
3✔
720
                    auto all_col_keys = t->get_column_keys();
2✔
721
                    size_t ndx = get_next(s) % all_col_keys.size();
2✔
722
                    ColKey col = all_col_keys[ndx];
2✔
723
                    ObjKey key = t->get_object(get_int32(s) % t->size()).get_key();
2✔
724
                    if (log) {
2✔
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);
2✔
729
                    static_cast<void>(res);
2✔
730
                }
2✔
731
            }
3✔
732
        }
827✔
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