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

realm / realm-core / github_pull_request_279261

11 Oct 2023 02:16PM UTC coverage: 91.624% (+0.06%) from 91.563%
github_pull_request_279261

Pull #6763

Evergreen

finnschiermer
Merge branch 'master' of github.com:realm/realm-core into fsa/enhance-freelist-check
Pull Request #6763: add freelist verification at more points during commit

94332 of 173512 branches covered (0.0%)

124 of 124 new or added lines in 2 files covered. (100.0%)

29 existing lines in 10 files now uncovered.

230660 of 251746 relevant lines covered (91.62%)

6863226.4 hits per line

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

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

6✔
91
    unsigned char mod = c % (sizeof(types) / sizeof(DataType));
17✔
92
    return types[mod];
17✔
93
}
17✔
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
{
3✔
112
#if REALM_ENABLE_ENCRYPTION
3✔
113
    return "1234567890123456789012345678901123456789012345678901234567890123";
3✔
114
#else
115
    return nullptr;
116
#endif
117
}
3✔
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
{
1✔
141
    REALM_ASSERT_3(length, <, 256);
1✔
142
    char buf[256] = {0};
1✔
143
    for (size_t i = 0; i < length; i++)
220✔
144
        buf[i] = 'a' + (rand() % 20);
219✔
145
    return std::string{buf, length};
1✔
146
}
1✔
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
{
43✔
167
    std::string str;
43✔
168
    switch (t) {
43✔
169
        case type_Int:
1✔
170
            str = "int_";
1✔
171
            break;
1✔
172
        case type_Bool:
4✔
173
            str = "bool_";
4✔
174
            break;
4✔
175
        case type_Float:
2✔
176
            str = "float_";
2✔
177
            break;
2✔
178
        case type_Double:
1✔
179
            str = "double_";
1✔
180
            break;
1✔
181
        case type_String:
4✔
182
            str = "string_";
4✔
183
            break;
4✔
184
        case type_Binary:
9✔
185
            str = "binary_";
9✔
186
            break;
9✔
UNCOV
187
        case type_Timestamp:
✔
UNCOV
188
            str = "date_";
×
UNCOV
189
            break;
×
190
        case type_Decimal:
✔
191
            str = "decimal_";
×
192
            break;
×
193
        case type_ObjectId:
✔
194
            str = "id_";
×
195
            break;
×
196
        case type_Link:
18✔
197
            str = "link_";
18✔
198
            break;
18✔
199
        case type_TypedLink:
✔
200
            str = "typed_link_";
×
201
            break;
×
202
        case type_LinkList:
4✔
203
            str = "link_list_";
4✔
204
            break;
4✔
205
        case type_UUID:
✔
206
            str = "uuid_";
×
207
            break;
×
208
        case type_Mixed:
✔
209
            str = "any_";
×
210
            break;
×
211
    }
43✔
212
    return str + util::to_string(column_index++);
43✔
213
}
43✔
214

215
std::string create_table_name()
216
{
32✔
217
    std::string str = "Table_";
32✔
218
    return str + util::to_string(table_index++);
32✔
219
}
32✔
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 (;;) {
886✔
289
            char instr = get_next(s) % COUNT;
886✔
290
            iteration++;
886✔
291

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

437✔
298
            if (instr == ADD_TABLE && wt->size() < max_tables) {
886✔
299
                std::string name = create_table_name();
32✔
300
                if (log) {
32✔
301
                    *log << "wt->add_table(\"" << name << "\");\n";
×
302
                }
×
303
                wt->add_table(name);
32✔
304
            }
32✔
305
            else if (instr == REMOVE_TABLE && wt->size() > 0) {
854✔
306
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
7✔
307
                if (log) {
7✔
308
                    *log << "try { wt->remove_table(" << table_key
×
309
                         << "); }"
×
310
                            " catch (const CrossTableLinkTarget&) { }\n";
×
311
                }
×
312
                try {
7✔
313
                    wt->remove_table(table_key);
7✔
314
                }
7✔
315
                catch (const CrossTableLinkTarget&) {
4✔
316
                    if (log) {
1!
317
                        *log << "// Exception\n";
×
318
                    }
×
319
                }
1✔
320
            }
7✔
321
            else if (instr == CLEAR_TABLE && wt->size() > 0) {
847✔
322
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
8✔
323
                if (log) {
8✔
324
                    *log << "wt->get_table(" << table_key << ")->clear();\n";
×
325
                }
×
326
                wt->get_table(table_key)->clear();
8✔
327
            }
8✔
328
            else if (instr == CREATE_OBJECT && wt->size() > 0) {
839✔
329
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
17✔
330
                size_t num_rows = get_next(s);
17✔
331
                if (wt->get_table(table_key)->size() + num_rows < max_rows) {
17✔
332
                    if (log) {
17✔
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;
17✔
337
                    wt->get_table(table_key)->create_objects(num_rows % add_empty_row_max, keys);
17✔
338
                }
17✔
339
            }
17✔
340
            else if (instr == ADD_COLUMN && wt->size() > 0) {
822✔
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
6✔
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) {
805✔
356
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
17✔
357
                TableRef t = wt->get_table(table_key);
17✔
358
                auto column_keys = t->get_column_keys();
17✔
359
                if (!column_keys.empty()) {
17✔
360
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
5✔
361
                    if (log) {
5✔
362
                        *log << "wt->get_table(" << table_key << ")->remove_column(" << col << ");\n";
×
363
                    }
×
364
                    t->remove_column(col);
5✔
365
                }
5✔
366
            }
17✔
367
            else if (instr == RENAME_COLUMN && wt->size() > 0) {
788✔
368
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
13✔
369
                TableRef t = wt->get_table(table_key);
13✔
370
                auto column_keys = t->get_column_keys();
13✔
371
                if (!column_keys.empty()) {
13✔
372
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
6✔
373
                    std::string name = create_column_name(t->get_column_type(col));
6✔
374
                    if (log) {
6✔
375
                        *log << "wt->get_table(" << table_key << ")->rename_column(" << col << ", \"" << name
×
376
                             << "\");\n";
×
377
                    }
×
378
                    t->rename_column(col, name);
6✔
379
                }
6✔
380
            }
13✔
381
            else if (instr == ADD_SEARCH_INDEX && wt->size() > 0) {
775✔
382
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
6✔
383
                TableRef t = wt->get_table(table_key);
6✔
384
                auto column_keys = t->get_column_keys();
6✔
385
                if (!column_keys.empty()) {
6✔
386
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
1✔
387
                    bool supports_search_index = StringIndex::type_supported(t->get_column_type(col));
1✔
388

1✔
389
                    if (supports_search_index) {
1!
390
                        if (log) {
1!
391
                            *log << "wt->get_table(" << table_key << ")->add_search_index(" << col << ");\n";
×
392
                        }
×
393
                        t->add_search_index(col);
1✔
394
                    }
1✔
395
                }
1✔
396
            }
6✔
397
            else if (instr == REMOVE_SEARCH_INDEX && wt->size() > 0) {
769✔
398
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
12✔
399
                TableRef t = wt->get_table(table_key);
12✔
400
                auto column_keys = t->get_column_keys();
12✔
401
                if (!column_keys.empty()) {
12✔
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
6✔
404
                    // off
6✔
405
                    // because Realm will just do a no-op at worst (no exception or assert).
6✔
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
            }
12✔
412
            else if (instr == ADD_COLUMN_LINK && wt->size() >= 1) {
757✔
413
                TableKey table_key_1 = wt->get_table_keys()[get_next(s) % wt->size()];
16✔
414
                TableKey table_key_2 = wt->get_table_keys()[get_next(s) % wt->size()];
16✔
415
                TableRef t1 = wt->get_table(table_key_1);
16✔
416
                TableRef t2 = wt->get_table(table_key_2);
16✔
417
                std::string name = create_column_name(type_Link);
16✔
418
                if (log) {
16✔
419
                    *log << "wt->get_table(" << table_key_1 << ")->add_column_link(type_Link, \"" << name
×
420
                         << "\", *wt->get_table(" << table_key_2 << "));";
×
421
                }
×
422
                auto col = t1->add_column(*t2, name);
16✔
423
                if (log) {
16✔
424
                    *log << " // -> " << col << "\n";
×
425
                }
×
426
            }
16✔
427
            else if (instr == ADD_COLUMN_LINK_LIST && wt->size() >= 2) {
741✔
428
                TableKey table_key_1 = wt->get_table_keys()[get_next(s) % wt->size()];
4✔
429
                TableKey table_key_2 = wt->get_table_keys()[get_next(s) % wt->size()];
4✔
430
                TableRef t1 = wt->get_table(table_key_1);
4✔
431
                TableRef t2 = wt->get_table(table_key_2);
4✔
432
                std::string name = create_column_name(type_LinkList);
4✔
433
                if (log) {
4!
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);
4✔
438
                if (log) {
4!
439
                    *log << " // -> " << col << "\n";
×
440
                }
×
441
            }
4✔
442
            else if (instr == SET && wt->size() > 0) {
737✔
443
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
17✔
444
                TableRef t = wt->get_table(table_key);
17✔
445
                auto all_col_keys = t->get_column_keys();
17✔
446
                if (!all_col_keys.empty() && t->size() > 0) {
17✔
447
                    ColKey col = all_col_keys[get_next(s) % all_col_keys.size()];
1✔
448
                    size_t row = get_next(s) % t->size();
1✔
449
                    DataType type = t->get_column_type(col);
1✔
450
                    Obj obj = t->get_object(row);
1✔
451
                    if (log) {
1✔
452
                        *log << "{\nObj obj = wt->get_table(" << table_key << ")->get_object(" << row << ");\n";
×
453
                    }
×
454

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

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

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