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

realm / realm-core / jorgen.edelbo_402

21 Aug 2024 11:10AM UTC coverage: 91.054% (-0.03%) from 91.085%
jorgen.edelbo_402

Pull #7803

Evergreen

jedelbo
Small fix to Table::typed_write

When writing the realm to a new file from a write transaction,
the Table may be COW so that the top ref is changed. So don't
use the ref that is present in the group when the operation starts.
Pull Request #7803: Feature/string compression

103494 of 181580 branches covered (57.0%)

1929 of 1999 new or added lines in 46 files covered. (96.5%)

695 existing lines in 51 files now uncovered.

220142 of 241772 relevant lines covered (91.05%)

7344461.76 hits per line

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

42.35
/test/fuzz_group.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2016 Realm Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 * http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 **************************************************************************/
18

19
#include "fuzz_group.hpp"
20

21
#include <realm.hpp>
22
#include <realm/disable_sync_to_disk.hpp>
23
#include <realm/index_string.hpp>
24

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

30
#include "util/test_path.hpp"
31

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

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

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

54
namespace {
55

56
struct EndOfFile {};
57

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

83
    COUNT
84
};
85

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

90
    unsigned char mod = c % (sizeof(types) / sizeof(DataType));
15✔
91
    return types[mod];
15✔
92
}
15✔
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
{
37✔
166
    std::string str;
37✔
167
    switch (t) {
37✔
168
        case type_Int:
1✔
169
            str = "int_";
1✔
170
            break;
1✔
171
        case type_Bool:
1✔
172
            str = "bool_";
1✔
173
            break;
1✔
174
        case type_Float:
7✔
175
            str = "float_";
7✔
176
            break;
7✔
177
        case type_Double:
3✔
178
            str = "double_";
3✔
179
            break;
3✔
180
        case type_String:
4✔
181
            str = "string_";
4✔
182
            break;
4✔
UNCOV
183
        case type_Binary:
✔
UNCOV
184
            str = "binary_";
×
UNCOV
185
            break;
×
186
        case type_Timestamp:
2✔
187
            str = "date_";
2✔
188
            break;
2✔
189
        case type_Decimal:
✔
190
            str = "decimal_";
×
191
            break;
×
192
        case type_ObjectId:
✔
193
            str = "id_";
×
194
            break;
×
195
        case type_Link:
19✔
196
            str = "link_";
19✔
197
            break;
19✔
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
    }
37✔
208
    return str + util::to_string(column_index++);
37✔
209
}
37✔
210

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

385
                    if (supports_search_index) {
4✔
386
                        if (log) {
2✔
387
                            *log << "wt->get_table(" << table_key << ")->add_search_index(" << col << ");\n";
×
388
                        }
×
389
                        t->add_search_index(col);
2✔
390
                    }
2✔
391
                }
4✔
392
            }
11✔
393
            else if (instr == REMOVE_SEARCH_INDEX && wt->size() > 0) {
667✔
394
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
18✔
395
                TableRef t = wt->get_table(table_key);
18✔
396
                auto column_keys = t->get_column_keys();
18✔
397
                if (!column_keys.empty()) {
18✔
398
                    ColKey col = column_keys[get_next(s) % column_keys.size()];
8✔
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) {
8✔
403
                        *log << "wt->get_table(" << table_key << ")->remove_search_index(" << col << ");\n";
×
404
                    }
×
405
                    t->remove_search_index(col);
8✔
406
                }
8✔
407
            }
18✔
408
            else if (instr == ADD_COLUMN_LINK && wt->size() >= 1) {
649✔
409
                TableKey table_key_1 = wt->get_table_keys()[get_next(s) % wt->size()];
15✔
410
                TableKey table_key_2 = wt->get_table_keys()[get_next(s) % wt->size()];
15✔
411
                TableRef t1 = wt->get_table(table_key_1);
15✔
412
                TableRef t2 = wt->get_table(table_key_2);
15✔
413
                std::string name = create_column_name(type_Link);
15✔
414
                if (log) {
15✔
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);
15✔
419
                if (log) {
15✔
420
                    *log << " // -> " << col << "\n";
×
421
                }
×
422
            }
15✔
423
            else if (instr == ADD_COLUMN_LINK_LIST && wt->size() >= 2) {
634✔
424
                TableKey table_key_1 = wt->get_table_keys()[get_next(s) % wt->size()];
4✔
425
                TableKey table_key_2 = wt->get_table_keys()[get_next(s) % wt->size()];
4✔
426
                TableRef t1 = wt->get_table(table_key_1);
4✔
427
                TableRef t2 = wt->get_table(table_key_2);
4✔
428
                std::string name = create_column_name(type_Link);
4✔
429
                if (log) {
4✔
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);
4✔
434
                if (log) {
4✔
435
                    *log << " // -> " << col << "\n";
×
436
                }
×
437
            }
4✔
438
            else if (instr == SET && wt->size() > 0) {
630✔
439
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
20✔
440
                TableRef t = wt->get_table(table_key);
20✔
441
                auto all_col_keys = t->get_column_keys();
20✔
442
                if (!all_col_keys.empty() && t->size() > 0) {
20✔
443
                    ColKey col = all_col_keys[get_next(s) % all_col_keys.size()];
×
444
                    size_t row = get_next(s) % t->size();
×
445
                    DataType type = t->get_column_type(col);
×
446
                    Obj obj = t->get_object(row);
×
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
452
                    if (get_next(s) % 2 == 0 && t->is_nullable(col)) {
×
453
                        if (type == type_Link) {
×
454
                            if (log) {
×
455
                                *log << "obj.set(" << col << ", null_key);\n";
×
456
                            }
×
457
                            obj.set(col, null_key);
×
458
                        }
×
459
                        else {
×
460
                            if (log) {
×
461
                                *log << "obj.set_null(" << col << ");\n";
×
462
                            }
×
463
                            obj.set_null(col);
×
464
                        }
×
465
                    }
×
466
                    else {
×
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
                        }
×
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
                        }
×
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
                        }
×
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
                        }
×
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
                        }
×
521
                        else if (type == type_Double) {
×
522
                            double value = get_next(s);
×
523
                            if (log) {
×
524
                                *log << "obj.set<double>(" << col << ", " << value << ");\n";
×
525
                            }
×
526
                            obj.set<double>(col, value);
×
527
                        }
×
528
                        else if (type == type_Link) {
×
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
                            }
×
551
                            else {
×
552
                                TableRef target = t->get_link_target(col);
×
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
                                }
×
560
                            }
×
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
                        }
×
571
                    }
×
572
                    if (log) {
×
573
                        *log << "}\n";
×
574
                    }
×
575
                }
×
576
            }
20✔
577
            else if (instr == REMOVE_OBJECT && wt->size() > 0) {
610✔
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();
3✔
582
                    if (log) {
3✔
583
                        *log << "wt->get_table(" << table_key << ")->remove_object(" << key << ");\n";
×
584
                    }
×
585
                    t->remove_object(key);
3✔
586
                }
3✔
587
            }
17✔
588
            else if (instr == REMOVE_RECURSIVE && wt->size() > 0) {
593✔
589
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
15✔
590
                TableRef t = wt->get_table(table_key);
15✔
591
                if (t->size() > 0) {
15✔
592
                    ObjKey key = t->get_object(get_next(s) % t->size()).get_key();
1✔
593
                    if (log) {
1✔
594
                        *log << "wt->get_table(" << table_key << ")->remove_object_recursive(" << key << ");\n";
×
595
                    }
×
596
                    t->remove_object_recursive(key);
1✔
597
                }
1✔
598
            }
15✔
599
            else if (instr == COMMIT) {
578✔
600
                if (log) {
31✔
601
                    *log << "wt->commit_and_continue_as_read();\n";
×
602
                }
×
603
                wt->commit_and_continue_as_read();
31✔
604
                REALM_DO_IF_VERIFY(log, wt->verify());
31✔
605
                if (log) {
31✔
606
                    *log << "wt->promote_to_write();\n";
×
607
                }
×
608
                wt->promote_to_write();
31✔
609
                REALM_DO_IF_VERIFY(log, wt->verify());
31✔
610
            }
31✔
611
            else if (instr == ROLLBACK) {
547✔
612
                if (log) {
31✔
613
                    *log << "wt->rollback_and_continue_as_read();\n";
×
614
                }
×
615
                wt->rollback_and_continue_as_read();
31✔
616
                REALM_DO_IF_VERIFY(log, wt->verify());
31✔
617
                if (log) {
31✔
618
                    *log << "wt->promote_to_write();\n";
×
619
                }
×
620
                wt->promote_to_write();
31✔
621
                REALM_DO_IF_VERIFY(log, wt->verify());
31✔
622
            }
31✔
623
            else if (instr == ADVANCE) {
516✔
624
                if (log) {
33✔
625
                    *log << "rt->advance_read();\n";
×
626
                }
×
627
                rt->advance_read();
33✔
628
                REALM_DO_IF_VERIFY(log, rt->verify());
33✔
629
            }
33✔
630
            else if (instr == CLOSE_AND_REOPEN) {
483✔
631
                if (log) {
48✔
632
                    *log << "wt = nullptr;\n";
×
633
                    *log << "rt = nullptr;\n";
×
634
                    *log << "db->close();\n";
×
635
                }
×
636
                wt = nullptr; // transactions must be done/closed before closing the DB.
48✔
637
                rt = nullptr;
48✔
638
                db->close();
48✔
639
                if (log) {
48✔
640
                    *log << "db = DB::create(*hist, path, DBOptions(key));\n";
×
641
                }
×
642
                db = DB::create(*hist, path, DBOptions(encryption_key));
48✔
643
                if (log) {
48✔
644
                    *log << "wt = db_w->start_write();\n";
×
645
                    *log << "rt = db->start_read();\n";
×
646
                }
×
647
                rt = db->start_read();
48✔
648
                wt = db->start_write();
48✔
649
                REALM_DO_IF_VERIFY(log, rt->verify());
48✔
650
            }
48✔
651
            else if (instr == GET_ALL_COLUMN_NAMES && wt->size() > 0) {
435✔
652
                // try to fuzz find this: https://github.com/realm/realm-core/issues/1769
653
                for (auto table_key : wt->get_table_keys()) {
24✔
654
                    TableRef t = wt->get_table(table_key);
24✔
655
                    auto all_col_keys = t->get_column_keys();
24✔
656
                    for (auto col : all_col_keys) {
24✔
657
                        StringData col_name = t->get_column_name(col);
8✔
658
                        static_cast<void>(col_name);
8✔
659
                    }
8✔
660
                }
24✔
661
            }
14✔
662
            else if (instr == CREATE_TABLE_VIEW && wt->size() > 0) {
421✔
663
                TableKey table_key = wt->get_table_keys()[get_next(s) % wt->size()];
15✔
664
                TableRef t = wt->get_table(table_key);
15✔
665
                if (log) {
15✔
666
                    *log << "table_views.push_back(wt->get_table(" << table_key << ")->where().find_all());\n";
×
667
                }
×
668
                TableView tv = t->where().find_all();
15✔
669
                table_views.push_back(tv);
15✔
670
            }
15✔
671
            else if (instr == COMPACT) {
406✔
672
                /*
673
                if (log) {
674
                    *log << "db_r.close();\n";
675
                }
676
                db_r.close();
677
                if (log) {
678
                    *log << "wt->commit();\n";
679
                }
680
                wt->commit();
681

682
                if (log) {
683
                    *log << "REALM_ASSERT_RELEASE(db_w.compact());\n";
684
                }
685
                REALM_ASSERT_RELEASE(db_w.compact());
686

687
                if (log) {
688
                    *log << "wt = db_w.start_write();\n";
689
                }
690
                wt = db_w.start_write();
691
                if (log) {
692
                    *log << "db_r.open(path, true, DBOptions(key));\n";
693
                }
694
                db_r.open(path, true, DBOptions(encryption_key));
695
                if (log) {
696
                    *log << "rt = db_r.start_read();\n";
697
                }
698
                rt = db_r.start_read();
699
                REALM_DO_IF_VERIFY(log, rt->verify());
700
                */
701
            }
27✔
702
            else if (instr == IS_NULL && rt->size() > 0) {
379✔
703
                TableKey table_key = rt->get_table_keys()[get_next(s) % rt->size()];
11✔
704
                TableRef t = rt->get_table(table_key);
11✔
705
                if (t->get_column_count() > 0 && t->size() > 0) {
11✔
706
                    auto all_col_keys = t->get_column_keys();
×
707
                    size_t ndx = get_next(s) % all_col_keys.size();
×
708
                    ColKey col = all_col_keys[ndx];
×
709
                    ObjKey key = t->get_object(get_int32(s) % t->size()).get_key();
×
710
                    if (log) {
×
711
                        *log << "wt->get_table(" << table_key << ")->get_object(" << key << ").is_null(" << col
×
712
                             << ");\n";
×
713
                    }
×
714
                    bool res = t->get_object(key).is_null(col);
×
715
                    static_cast<void>(res);
×
716
                }
×
717
            }
11✔
718
        }
851✔
719
    }
6✔
720
    catch (const EndOfFile&) {
6✔
721
    }
6✔
722
}
6✔
723

724

725
static void usage(const char* argv[])
726
{
×
727
    fprintf(stderr,
×
728
            "Usage: %s {FILE | --} [--log] [--name NAME] [--prefix PATH]\n"
×
729
            "Where FILE is a instruction file that will be replayed.\n"
×
730
            "Pass -- without argument to read filenames from stdin\n"
×
731
            "Pass --log to have code printed to stdout producing the same instructions.\n"
×
732
            "Pass --name NAME with distinct values when running on multiple threads,\n"
×
733
            "                 to make sure the test don't use the same Realm file\n"
×
734
            "Pass --prefix PATH to supply a path that should be prepended to all filenames\n"
×
735
            "                 read from stdin.\n",
×
736
            argv[0]);
×
737
    exit(1);
×
738
}
×
739

740
int run_fuzzy(int argc, const char* argv[])
741
{
×
742
    std::ostream* log = nullptr;
×
743
    std::string name = "fuzz-test";
×
744
    std::string prefix = "./";
×
745
    bool file_names_from_stdin = false;
×
746

747
    size_t file_arg = size_t(-1);
×
748
    for (size_t i = 1; i < size_t(argc); ++i) {
×
749
        std::string arg = argv[i];
×
750
        if (arg == "--log") {
×
751
            log = &std::cout;
×
752
        }
×
753
        else if (arg == "--") {
×
754
            file_names_from_stdin = true;
×
755
        }
×
756
        else if (arg == "--prefix") {
×
757
            prefix = argv[++i];
×
758
        }
×
759
        else if (arg == "--name") {
×
760
            name = argv[++i];
×
761
        }
×
762
        else {
×
763
            file_arg = i;
×
764
        }
×
765
    }
×
766

767
    if (!file_names_from_stdin && file_arg == size_t(-1)) {
×
768
        usage(argv);
×
769
    }
×
770

771
    disable_sync_to_disk();
×
772

773
    if (file_names_from_stdin) {
×
774
        std::string file_name;
×
775

776
        std::cin >> file_name;
×
777
        while (std::cin) {
×
778
            std::ifstream in(prefix + file_name, std::ios::in | std::ios::binary);
×
779
            if (!in.is_open()) {
×
780
                std::cerr << "Could not open file for reading: " << (prefix + file_name) << std::endl;
×
781
            }
×
782
            else {
×
783
                std::cout << file_name << std::endl;
×
784
                realm::test_util::RealmPathInfo test_context{name};
×
785
                SHARED_GROUP_TEST_PATH(path);
×
786

787
                std::string contents((std::istreambuf_iterator<char>(in)), (std::istreambuf_iterator<char>()));
×
788
                parse_and_apply_instructions(contents, path, log);
×
789
            }
×
790

791
            std::cin >> file_name;
×
792
        }
×
793
    }
×
794
    else {
×
795
        std::ifstream in(argv[file_arg], std::ios::in | std::ios::binary);
×
796
        if (!in.is_open()) {
×
797
            std::cerr << "Could not open file for reading: " << argv[file_arg] << "\n";
×
798
            exit(1);
×
799
        }
×
800

801
        realm::test_util::RealmPathInfo test_context{name};
×
802
        SHARED_GROUP_TEST_PATH(path);
×
803

804
        std::string contents((std::istreambuf_iterator<char>(in)), (std::istreambuf_iterator<char>()));
×
805
        parse_and_apply_instructions(contents, path, log);
×
806
    }
×
807

808
    return 0;
×
809
}
×
810
#else
811
int run_fuzzy(int, const char*[])
812
{
813
    return 0;
814
}
815
#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