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

realm / realm-core / 2543

05 Aug 2024 04:04PM UTC coverage: 91.108%. Remained the same
2543

push

Evergreen

web-flow
Only track pending client resets done by the same core version (#7944)

If the previous attempt at performing a client reset was done with a different
core version then we should retry the client reset as the new version may have
fixed a bug that made the previous attempt fail (or may be a downgrade to a
version before when the bug was introduced). This also simplifies the tracking
as it means that we don't need to be able to read trackers created by different
versions.

This also means that we can freely change the schema of the table, which this
takes advantage of to drop the unused primary key and make the error required,
as we never actually stored null and the code reading it would have crashed if
it encountered a null error.

102732 of 181534 branches covered (56.59%)

138 of 153 new or added lines in 10 files covered. (90.2%)

60 existing lines in 12 files now uncovered.

216763 of 237919 relevant lines covered (91.11%)

5749223.09 hits per line

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

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

54
namespace {
55

56
struct EndOfFile {};
57

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

84
    COUNT
85
};
86

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

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

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

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

110
const char* get_encryption_key()
111
{
2✔
112
#if REALM_ENABLE_ENCRYPTION
2✔
113
    return "1234567890123456789012345678901123456789012345678901234567890123";
2✔
114
#else
115
    return nullptr;
116
#endif
117
}
2✔
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
{
5✔
131
    int32_t v = 0;
5✔
132
    for (size_t t = 0; t < 4; t++) {
25✔
133
        unsigned char c = get_next(s);
20✔
134
        *(reinterpret_cast<signed char*>(&v) + t) = c;
20✔
135
    }
20✔
136
    return v;
5✔
137
}
5✔
138

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

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

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

165
std::string create_column_name(DataType t)
166
{
39✔
167
    std::string str;
39✔
168
    switch (t) {
39✔
169
        case type_Int:
2✔
170
            str = "int_";
2✔
171
            break;
2✔
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:
3✔
182
            str = "string_";
3✔
183
            break;
3✔
184
        case type_Binary:
2✔
185
            str = "binary_";
2✔
186
            break;
2✔
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:
25✔
197
            str = "link_";
25✔
198
            break;
25✔
199
        case type_TypedLink:
✔
200
            str = "typed_link_";
×
201
            break;
×
202
        case type_UUID:
✔
203
            str = "uuid_";
×
204
            break;
×
205
        case type_Mixed:
✔
206
            str = "any_";
×
207
            break;
×
208
    }
39✔
209
    return str + util::to_string(column_index++);
39✔
210
}
39✔
211

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

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

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

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

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

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

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

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

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

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

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

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

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

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

283
    try {
6✔
284

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

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

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

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

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

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

701
                if (log) {
702
                    *log << "wt = db_w.start_write();\n";
703
                }
704
                wt = db_w.start_write();
705
                if (log) {
706
                    *log << "db_r.open(path, true, DBOptions(key));\n";
707
                }
708
                db_r.open(path, true, DBOptions(encryption_key));
709
                if (log) {
710
                    *log << "rt = db_r.start_read();\n";
711
                }
712
                rt = db_r.start_read();
713
                REALM_DO_IF_VERIFY(log, rt->verify());
714
                */
715
            }
38✔
716
            else if (instr == IS_NULL && rt->size() > 0) {
422✔
717
                TableKey table_key = rt->get_table_keys()[get_next(s) % rt->size()];
13✔
718
                TableRef t = rt->get_table(table_key);
13✔
719
                if (t->get_column_count() > 0 && t->size() > 0) {
13✔
720
                    auto all_col_keys = t->get_column_keys();
5✔
721
                    size_t ndx = get_next(s) % all_col_keys.size();
5✔
722
                    ColKey col = all_col_keys[ndx];
5✔
723
                    ObjKey key = t->get_object(get_int32(s) % t->size()).get_key();
5✔
724
                    if (log) {
5✔
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);
5✔
729
                    static_cast<void>(res);
5✔
730
                }
5✔
731
            }
13✔
732
        }
850✔
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