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

realm / realm-core / github_pull_request_281922

31 Oct 2023 09:13AM UTC coverage: 90.445% (-0.08%) from 90.528%
github_pull_request_281922

Pull #7039

Evergreen

jedelbo
Merge branch 'next-major' into je/global-key
Pull Request #7039: Remove ability to synchronize objects without primary key

95324 of 175822 branches covered (0.0%)

101 of 105 new or added lines in 13 files covered. (96.19%)

238 existing lines in 19 files now uncovered.

232657 of 257235 relevant lines covered (90.45%)

6351359.67 hits per line

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

47.8
/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 {                                                                                                             \
219✔
43
        if (log)                                                                                                     \
219✔
44
            *log << #op << ";\n";                                                                                    \
103✔
45
        op;                                                                                                          \
219✔
46
    } while (false)
219✔
47
#else
48
#define REALM_DO_IF_VERIFY(log, owner)                                                                               \
49
    do {                                                                                                             \
50
    } while (false)
51
#endif
52

53
namespace {
54

55
struct EndOfFile {
56
};
57

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

84
    COUNT
85
};
86

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

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

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

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

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

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

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

139
std::string create_string(size_t length)
UNCOV
140
{
×
UNCOV
141
    REALM_ASSERT_3(length, <, 256);
×
UNCOV
142
    char buf[256] = {0};
×
UNCOV
143
    for (size_t i = 0; i < length; i++)
×
UNCOV
144
        buf[i] = 'a' + (rand() % 20);
×
UNCOV
145
    return std::string{buf, length};
×
UNCOV
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
{
36✔
167
    std::string str;
36✔
168
    switch (t) {
36✔
169
        case type_Int:
3✔
170
            str = "int_";
3✔
171
            break;
3✔
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:
2✔
182
            str = "string_";
2✔
183
            break;
2✔
184
        case type_Binary:
4✔
185
            str = "binary_";
4✔
186
            break;
4✔
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:
12✔
197
            str = "link_";
12✔
198
            break;
12✔
199
        case type_TypedLink:
✔
200
            str = "typed_link_";
×
201
            break;
×
202
        case type_LinkList:
7✔
203
            str = "link_list_";
7✔
204
            break;
7✔
205
        case type_UUID:
✔
206
            str = "uuid_";
×
207
            break;
×
208
        case type_Mixed:
✔
209
            str = "any_";
×
210
            break;
×
211
    }
36✔
212
    return str + util::to_string(column_index++);
36✔
213
}
36✔
214

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3✔
286
    try {
6✔
287

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

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

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

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

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

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

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