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

realm / realm-core / finn.schiermer-andersen_89

04 Jun 2024 02:04PM UTC coverage: 90.651% (-0.03%) from 90.685%
finn.schiermer-andersen_89

Pull #7654

Evergreen

finnschiermer
optimized string cache gc
Pull Request #7654: Fsa/string interning

102644 of 180648 branches covered (56.82%)

1005 of 1125 new or added lines in 15 files covered. (89.33%)

154 existing lines in 21 files now uncovered.

217953 of 240431 relevant lines covered (90.65%)

7671710.15 hits per line

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

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

53
namespace {
54

55
struct EndOfFile {};
56

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

83
    COUNT
84
};
85

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

282
    try {
6✔
283

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

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

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

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

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

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

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

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

737

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

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

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

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

784
    disable_sync_to_disk();
×
785

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

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

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

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

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

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

821
    return 0;
×
822
}
×
823
#else
824
int run_fuzzy(int, const char*[])
825
{
826
    return 0;
827
}
828
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc