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

realm / realm-core / github_pull_request_319175

19 May 2025 08:16PM UTC coverage: 91.119% (-0.02%) from 91.143%
github_pull_request_319175

Pull #8082

Evergreen

web-flow
Bump setuptools from 70.0.0 to 78.1.1 in /evergreen/hang_analyzer

Bumps [setuptools](https://github.com/pypa/setuptools) from 70.0.0 to 78.1.1.
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v70.0.0...v78.1.1)

---
updated-dependencies:
- dependency-name: setuptools
  dependency-version: 78.1.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #8082: Bump setuptools from 70.0.0 to 78.1.1 in /evergreen/hang_analyzer

102788 of 181548 branches covered (56.62%)

217441 of 238634 relevant lines covered (91.12%)

5497200.53 hits per line

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

57.73
/test/fuzz_tester.hpp
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
#ifndef REALM_TEST_FUZZ_TESTER_HPP
20
#define REALM_TEST_FUZZ_TESTER_HPP
21

22
#include <realm/util/features.h>
23
#include <realm/list.hpp>
24
#include <realm/sync/transform.hpp>
25

26
#include "util/unit_test.hpp"
27
#include "util/quote.hpp"
28
#include "util/compare_groups.hpp"
29
#include "util/dump_changesets.hpp"
30
#include "peer.hpp"
31

32
#include <iostream>
33

34
namespace realm {
35
namespace test_util {
36

37
template <class L>
38
struct StreamableLambda {
39
    StreamableLambda(L&& l)
40
        : m_lambda(std::move(l))
41
    {
×
42
    }
×
43
    L m_lambda;
44
};
45

46
template <class L>
47
StreamableLambda<L> make_streamable_lambda(L&& lambda)
48
{
×
49
    return StreamableLambda<L>(std::move(lambda));
×
50
}
×
51

52
template <class OS, class L>
53
OS& operator<<(OS& os, StreamableLambda<L>&& sl)
54
{
×
55
    sl.m_lambda(os);
×
56
    return os;
×
57
}
×
58

59
template <class Source>
60
class FuzzTester {
61
public:
62
    FuzzTester(Source& source, bool trace)
63
        : m_source(source)
1✔
64
        , m_trace(trace)
1✔
65
    {
2✔
66
    }
2✔
67

68
    // FIXME: Eliminate the dependency on the unit_test namespace.
69
    void round(unit_test::TestContext&, std::string path_add_on = "");
70

71
private:
72
    static const int num_modifications_per_round = 256;
73
    static const int num_clients = 4;
74

75
    static const int modify_weight = 100;
76
    static const int upload_weight = 100;
77
    static const int download_weight = 100;
78

79

80
    static constexpr double group_to_table_level_transition_chance()
81
    {
2,024✔
82
        return 7.0 / 8;
2,024✔
83
    }
2,024✔
84

85
    static constexpr double table_to_array_level_transition_chance()
86
    {
451✔
87
        return 7.0 / 8;
451✔
88
    }
451✔
89

90
    static const int rename_table_weight = 0; // Rename table is destructive; not supported.
91
    static const int add_table_weight = 100;
92
    static const int erase_table_weight = 10;
93

94
    static const int insert_column_weight = 10;
95
    static const int insert_link_column_weight = 5;
96
    static const int insert_array_column_weight = 5;
97
    static const int erase_column_weight = 1;
98

99
    static const int update_row_weight = 80;
100
    static const int insert_row_weight = 100;
101
    static const int erase_row_weight = 80;
102

103
    static const int set_link_weight = 80;
104
    static const int insert_link_weight = 100;
105
    static const int remove_link_weight = 70;
106
    static const int move_link_weight = 50;
107
    static const int swap_links_weight = 50;
108
    static const int clear_link_list_weight = 1;
109

110
    static const int array_set_weight = 80;
111
    static const int array_insert_weight = 100;
112
    static const int array_remove_weight = 70;
113
    static const int array_move_weight = 50;
114
    static const int array_swap_weight = 0;
115
    static const int array_clear_weight = 1;
116

117

118
    template <class T>
119
    T draw_int(T min, T max)
120
    {
5,103✔
121
        return m_source.template draw_int<T>(min, max);
5,103✔
122
    }
5,103✔
123

124
    template <class T>
125
    T draw_int_mod(T mod)
126
    {
26,080✔
127
        return m_source.template draw_int_mod<T>(mod);
26,080✔
128
    }
26,080✔
129

130
    template <class T>
131
    T draw_int_max(T max)
132
    {
1,231✔
133
        return m_source.template draw_int_max<T>(max);
1,231✔
134
    }
1,231✔
135

136
    template <class T>
137
    T draw_float()
138
    {
2,475✔
139
        return m_source.template draw_float<T>();
2,475✔
140
    }
2,475✔
141

142
    bool draw_bool()
143
    {
144
        return m_source.draw_bool();
145
    }
146

147
    void rename_table(Peer& client)
148
    {
×
149
        static_cast<void>(client);
×
150
        REALM_ASSERT(false);
×
151
    }
×
152

153
    auto trace_client(Peer& client)
154
    {
×
155
        return make_streamable_lambda([&](std::ostream& os) {
×
156
            os << "client_" << client.local_file_ident;
×
157
        });
×
158
    }
×
159

160
    auto trace_selected_table(Peer& client)
161
    {
×
162
        return make_streamable_lambda([&](std::ostream& os) {
×
163
            os << trace_client(client) << "->selected_table";
×
164
        });
×
165
    }
×
166

167
    auto trace_selected_link_list(Peer& client)
168
    {
×
169
        return make_streamable_lambda([&](std::ostream& os) {
×
170
            os << trace_client(client) << "->selected_link_list";
×
171
        });
×
172
    }
×
173

174
    auto trace_selected_array(Peer& client)
175
    {
×
176
        return make_streamable_lambda([&](std::ostream& os) {
×
177
            os << trace_client(client) << "->selected_array";
×
178
        });
×
179
    }
×
180

181
    auto trace_selected_int_array(Peer& client)
182
    {
×
183
        return make_streamable_lambda([&](std::ostream& os) {
×
184
            os << "static_cast<Lst<int64_t>*>(" << trace_client(client) << "->selected_array.get())";
×
185
        });
×
186
    }
×
187

188
    auto trace_selected_string_array(Peer& client)
189
    {
×
190
        return make_streamable_lambda([&](std::ostream& os) {
×
191
            os << "static_cast<Lst<StringData>*>(" << trace_client(client) << "->selected_array.get())";
×
192
        });
×
193
    }
×
194

195
    void add_table(Peer& client)
196
    {
235✔
197
        char table_name[] = {'c', 'l', 'a', 's', 's', '_', 0, 0};
235✔
198
        table_name[6] = 'A' + draw_int_mod(6); // pick a random letter A-F
235✔
199

200
        if (client.group->get_table(table_name))
235✔
201
            return;
139✔
202

203
        bool is_string_pk = (table_name[6] == 'B');
96✔
204
        if (m_trace) {
96✔
205
            std::cerr << "sync::create_table_with_primary_key(*" << trace_client(client)
×
206
                      << "->"
×
207
                         "group, \""
×
208
                      << table_name << "\",";
×
209
            if (is_string_pk)
×
210
                std::cerr << "type_String";
×
211
            else
×
212
                std::cerr << "type_Int";
×
213
            std::cerr << ", \"pk\");\n";
×
214
        }
×
215
        client.group->add_table_with_primary_key(table_name, is_string_pk ? type_String : type_Int, "pk");
96✔
216
    }
96✔
217

218
    void erase_table(Peer& client)
219
    {
32✔
220
        size_t num_tables = count_classes(client);
32✔
221
        size_t table_ndx = draw_int_mod(num_tables);
32✔
222
        TableRef table = get_class(client, table_ndx);
32✔
223
        if (m_trace) {
32✔
224
            std::cerr << "sync::erase_table(*" << trace_client(client)
×
225
                      << "->"
×
226
                         "group, \""
×
227
                      << table->get_name() << "\");\n";
×
228
        }
×
229
        client.group->remove_table(table->get_name());
32✔
230
    }
32✔
231

232
    void clear_group(Peer& client)
233
    {
234
        if (m_trace) {
235
            std::cerr << trace_client(client)
236
                      << "->"
237
                         "group->clear();\n";
238
        }
239
        //        client.group->clear();
240
    }
241

242
    void insert_column(Peer& client)
243
    {
69✔
244
        // It is currently an error to request multiple columns with the same name
245
        // but with different types / nullability (there is no non-destructive way
246
        // to merge them).
247
        const char* column_names[] = {"a", "b", "c", "d"};
69✔
248
        const DataType column_types[] = {type_Int, type_Int, type_String, type_String};
69✔
249
        const bool column_nullable[] = {false, true, false, true};
69✔
250

251
        size_t which = draw_int_mod(4);
69✔
252
        const char* name = column_names[which];
69✔
253
        DataType type = column_types[which];
69✔
254
        bool nullable = column_nullable[which];
69✔
255

256
        TableRef table = client.selected_table;
69✔
257
        if (table->get_column_key(name))
69✔
258
            return;
2✔
259

260
        if (m_trace) {
67✔
261
            const char* type_name;
×
262
            if (type == type_Int) {
×
263
                type_name = "type_Int";
×
264
            }
×
265
            else if (type == type_String) {
×
266
                type_name = "type_String";
×
267
            }
×
268
            else {
×
269
                REALM_TERMINATE("Missing trace support for column type.");
270
            }
×
271

272
            std::cerr << trace_selected_table(client) << "->add_column(" << type_name << ", \"" << name << "\", "
×
273
                      << nullable << ");\n";
×
274
        }
×
275

276
        ColKey col_key = table->add_column(type, name, nullable);
67✔
277
        m_unstructured_columns.push_back(col_key);
67✔
278
    }
67✔
279

280
    void insert_link_column(Peer& client)
281
    {
31✔
282
        REALM_ASSERT(count_classes(client) > 1);
31✔
283

284
        const char* column_names[] = {"e", "f"};
31✔
285

286
        size_t which = draw_int_max(1);
31✔
287
        const char* name = column_names[which];
31✔
288
        bool is_list = bool(which);
31✔
289

290
        TableRef table = client.selected_table;
31✔
291
        if (table->get_column_key(name))
31✔
292
            return;
×
293

294
        // Avoid divergent schemas by always creating links to table "A"
295
        TableKey link_target_table_key = client.group->find_table("A");
31✔
296
        if (!link_target_table_key)
31✔
297
            return;
31✔
298

299
        TableRef link_target_table = client.group->get_table(link_target_table_key);
×
300

301
        if (m_trace) {
×
302
            const char* type_name;
×
303
            if (is_list) {
×
304
                type_name = "type_LinkList";
×
305
            }
×
306
            else {
×
307
                type_name = "type_Link";
×
308
            }
×
309
            std::cerr << trace_selected_table(client) << "->add_column_link(" << type_name << ", \"" << name
×
310
                      << "\", *client_" << client.local_file_ident << "->group->get_table(\"A\"));\n";
×
311
        }
×
312

313
        if (is_list) {
×
314
            ColKey col_key = table->add_column_list(*link_target_table, name);
×
315
            m_link_list_columns.push_back(col_key);
×
316
        }
×
317
        else {
×
318
            ColKey col_key = table->add_column(*link_target_table, name);
×
319
            m_unstructured_columns.push_back(col_key);
×
320
        }
×
321
    }
×
322

323
    void insert_array_column(Peer& client)
324
    {
38✔
325
        REALM_ASSERT(count_classes(client) >= 1);
38✔
326

327
        const char* column_names[] = {"g", "h"};
38✔
328
        const DataType column_types[] = {type_Int, type_String};
38✔
329

330
        size_t which = draw_int_max(1);
38✔
331
        const char* name = column_names[which];
38✔
332
        DataType type = column_types[which];
38✔
333
        bool nullable = false;
38✔
334

335
        TableRef table = client.selected_table;
38✔
336
        if (table->get_column_key(name))
38✔
337
            return;
×
338

339
        if (m_trace) {
38✔
340
            const char* type_name;
×
341
            if (type == type_Int) {
×
342
                type_name = "type_Int";
×
343
            }
×
344
            else if (type == type_String) {
×
345
                type_name = "type_String";
×
346
            }
×
347
            else {
×
348
                REALM_TERMINATE("Missing trace support for column type.");
349
            }
×
350
            std::cerr << trace_selected_table(client) << "->add_column_list(" << type_name << ", \"" << name << "\", "
×
351
                      << nullable << ");\n";
×
352
        }
×
353

354
        ColKey col_key = table->add_column_list(type, name, nullable);
38✔
355
        m_array_columns.push_back(col_key);
38✔
356
    }
38✔
357

358
    void update_row(Peer& client)
359
    {
119✔
360
        REALM_ASSERT(!m_unstructured_columns.empty());
119✔
361
        size_t i = draw_int_mod(m_unstructured_columns.size());
119✔
362
        ColKey col_key = m_unstructured_columns[i];
119✔
363
        size_t num_rows = client.selected_table->size();
119✔
364
        size_t row_ndx = draw_int_mod(num_rows);
119✔
365
        ObjKey row_key = (client.selected_table->begin() + row_ndx)->get_key();
119✔
366
        DataType type = client.selected_table->get_column_type(col_key);
119✔
367
        bool nullable = client.selected_table->is_nullable(col_key);
119✔
368

369
        Obj obj = client.selected_table->get_object(row_key);
119✔
370

371
        if (type == type_Int) {
119✔
372
            int_fast64_t value = next_value();
60✔
373
            if (nullable && value % 7 == 0) {
60✔
374
                bool is_default = (value % 21 == 0);
4✔
375
                if (m_trace) {
4✔
376
                    std::cerr << trace_selected_table(client) << "->get_object(" << row_key << ").set_null("
×
377
                              << col_key << ", " << is_default << ");\n";
×
378
                }
×
379
                client.selected_table->get_object(row_key).set_null(col_key, is_default);
4✔
380
                return;
4✔
381
            }
4✔
382
            else {
56✔
383
                if (value % 3 == 0 && (!nullable || !obj.is_null(col_key))) {
56✔
384
                    if (m_trace) {
12✔
385
                        std::cerr << trace_selected_table(client) << "->get_object(" << row_key << ").add_int("
×
386
                                  << col_key << ", " << value << ");\n";
×
387
                    }
×
388
                    obj.add_int(col_key, value);
12✔
389
                }
12✔
390
                else {
44✔
391
                    bool is_default = (value % 13 == 0);
44✔
392
                    if (m_trace) {
44✔
393
                        std::cerr << trace_selected_table(client) << "->get_object(" << row_key << ").set(" << col_key
×
394
                                  << ", " << value << ", " << is_default << ");\n";
×
395
                    }
×
396
                    obj.set(col_key, value, is_default);
44✔
397
                }
44✔
398
                return;
56✔
399
            }
56✔
400
        }
60✔
401

402
        if (type == type_String) {
59✔
403
            int_fast64_t ival = next_value();
59✔
404

405
            if (nullable && ival % 7 == 0) {
59✔
406
                bool is_default = (ival % 21 == 0);
4✔
407
                if (m_trace) {
4✔
408
                    std::cerr << trace_selected_table(client) << "->get_object(" << row_key << ").set_null("
×
409
                              << col_key << ", " << is_default << ");\n";
×
410
                }
×
411
                client.selected_table->get_object(row_key).set_null(col_key, is_default);
4✔
412
                return;
4✔
413
            }
4✔
414
            else {
55✔
415
                std::stringstream ss;
55✔
416
                ss << ival;
55✔
417
                std::string value = ss.str();
55✔
418

419
                bool is_default = (ival % 13 == 0);
55✔
420
                if (m_trace) {
55✔
421
                    std::cerr << trace_selected_table(client) << "->get_object(" << row_key << ").set(" << col_key
×
422
                              << ", \"" << value << "\", " << is_default << ");\n";
×
423
                }
×
424
                obj.set(col_key, value, is_default);
55✔
425
                return;
55✔
426
            }
55✔
427
        }
59✔
428

429
        if (type == type_Link) {
×
430
            TableRef target_table = client.selected_table->get_link_target(col_key);
×
431
            size_t value = draw_int_mod(target_table->size() + 1);
×
432
            if (value == target_table->size()) {
×
433
                if (m_trace) {
×
434
                    std::cerr << trace_selected_table(client) << "->get_object(" << row_key << ").set_null("
×
435
                              << col_key << ";\n";
×
436
                }
×
437
                obj.set_null(col_key);
×
438
            }
×
439
            else {
×
440
                ObjKey target_key = (target_table->begin() + value)->get_key();
×
441
                if (m_trace) {
×
442
                    std::cerr << trace_selected_table(client) << "->get_object(" << row_key << ").set(" << col_key
×
443
                              << ", " << target_key << ");\n";
×
444
                }
×
445
                obj.set(col_key, target_key);
×
446
            }
×
447
            return;
×
448
        }
×
449
        REALM_ASSERT(false);
×
450
    }
×
451

452
    void insert_row(Peer& client)
453
    {
735✔
454
        ColKey pk_col_key = client.selected_table->get_column_key("pk");
735✔
455

456
        char string_buffer[2] = {0};
735✔
457
        bool is_string_pk = (client.selected_table->get_column_type(pk_col_key) == type_String);
735✔
458
        int_fast64_t pk_int = 0;
735✔
459
        StringData pk_string;
735✔
460
        if (is_string_pk) {
735✔
461
            string_buffer[0] = 'a' + draw_int_max(25); // "a" to "z"
104✔
462
            pk_string = StringData{string_buffer, 1};
104✔
463
        }
104✔
464
        else {
631✔
465
            pk_int = draw_int_max(10); // Low number to ensure some collisions
631✔
466
        }
631✔
467
        if (m_trace) {
735✔
468
            std::cerr << trace_selected_table(client) << "->create_object_with_primary_key(";
×
469
            if (is_string_pk)
×
470
                std::cerr << "\"" << pk_string << "\"";
×
471
            else
×
472
                std::cerr << pk_int;
×
473
            std::cerr << ");\n";
×
474
        }
×
475
        if (is_string_pk)
735✔
476
            client.selected_table->create_object_with_primary_key(pk_string);
104✔
477
        else
631✔
478
            client.selected_table->create_object_with_primary_key(pk_int);
631✔
479
    }
735✔
480

481
    void move_last_row_over(Peer& client)
482
    {
381✔
483
        size_t num_rows = client.selected_table->size();
381✔
484
        size_t row_ndx = draw_int_mod(num_rows);
381✔
485
        ObjKey row_key = (client.selected_table->begin() + row_ndx)->get_key();
381✔
486
        if (m_trace) {
381✔
487
            std::cerr << trace_selected_table(client) << "->remove_object(" << row_key << ");\n";
×
488
        }
×
489
        client.selected_table->remove_object(row_key);
381✔
490
    }
381✔
491

492
    void set_link(Peer& client)
493
    {
×
494
        size_t num_links = client.selected_link_list->size();
×
495
        size_t link_ndx = draw_int_max(num_links - 1);
×
496
        auto target_table = client.selected_link_list->get_target_table();
×
497
        size_t num_target_rows = target_table->size();
×
498
        REALM_ASSERT(num_target_rows > 0);
×
499
        size_t target_row_ndx = draw_int_mod(num_target_rows);
×
500
        ObjKey target_row_key = (target_table->begin() + target_row_ndx)->get_key();
×
501
        if (m_trace) {
×
502
            std::cerr << trace_selected_link_list(client) << "->set(" << link_ndx << ", " << target_row_key << ");\n";
×
503
        }
×
504
        client.selected_link_list->set(link_ndx, target_row_key);
×
505
    }
×
506

507
    void insert_link(Peer& client)
508
    {
×
509
        size_t num_links = client.selected_link_list->size();
×
510
        size_t link_ndx = draw_int_max(num_links);
×
511
        auto target_table = client.selected_link_list->get_target_table();
×
512
        size_t num_target_rows = target_table->size();
×
513
        REALM_ASSERT(num_target_rows > 0);
×
514
        size_t target_row_ndx = draw_int_mod(num_target_rows);
×
515
        ObjKey target_row_key = (target_table->begin() + target_row_ndx)->get_key();
×
516
        if (m_trace) {
×
517
            std::cerr << trace_selected_link_list(client) << "->insert(" << link_ndx << ", " << target_row_key
×
518
                      << ");\n";
×
519
        }
×
520
        client.selected_link_list->insert(link_ndx, target_row_key);
×
521
    }
×
522

523
    void remove_link(Peer& client)
524
    {
×
525
        size_t num_links = client.selected_link_list->size();
×
526
        size_t link_ndx = draw_int_mod(num_links);
×
527
        if (m_trace) {
×
528
            std::cerr << trace_selected_link_list(client) << "->remove(" << link_ndx << ");\n";
×
529
        }
×
530
        client.selected_link_list->remove(link_ndx);
×
531
    }
×
532

533
    void move_link(Peer& client)
534
    {
×
535
        size_t num_links = client.selected_link_list->size();
×
536
        size_t from_link_ndx, to_link_ndx;
×
537
        for (;;) {
×
538
            from_link_ndx = draw_int_mod(num_links);
×
539
            to_link_ndx = draw_int_mod(num_links);
×
540
            if (from_link_ndx != to_link_ndx)
×
541
                break;
×
542
        }
×
543

544
        if (m_trace) {
×
545
            std::cerr << trace_selected_link_list(client) << "->move(" << from_link_ndx << ", " << to_link_ndx
×
546
                      << ");\n";
×
547
        }
×
548
        client.selected_link_list->move(from_link_ndx, to_link_ndx);
×
549
    }
×
550

551
    void clear_link_list(Peer& client)
552
    {
×
553
        if (m_trace) {
×
554
            std::cerr << trace_selected_link_list(client) << "->clear();\n";
×
555
        }
×
556
        client.selected_link_list->clear();
×
557
    }
×
558

559
    void array_set(Peer& client)
560
    {
72✔
561
        size_t num_elements = client.selected_array->size();
72✔
562
        DataType type = client.selected_array->get_table()->get_column_type(client.selected_array->get_col_key());
72✔
563
        size_t ndx = draw_int_max(num_elements - 1);
72✔
564
        if (type == type_Int) {
72✔
565
            int_fast64_t value = draw_int_max(1000);
23✔
566
            if (m_trace) {
23✔
567
                std::cerr << trace_selected_int_array(client) << "->set(" << ndx << ", " << value << ");\n";
×
568
            }
×
569
            static_cast<Lst<int64_t>*>(client.selected_array.get())->set(ndx, value);
23✔
570
        }
23✔
571
        else {
49✔
572
            StringData value = "abc";
49✔
573
            if (m_trace) {
49✔
574
                std::cerr << trace_selected_string_array(client) << "->set(" << ndx << ", \"" << value << "\");\n";
×
575
            }
×
576
            static_cast<Lst<StringData>*>(client.selected_array.get())->set(ndx, value);
49✔
577
        }
49✔
578
    }
72✔
579

580
    void array_insert(Peer& client)
581
    {
275✔
582
        size_t num_elements = client.selected_array->size();
275✔
583
        DataType type = client.selected_array->get_table()->get_column_type(client.selected_array->get_col_key());
275✔
584
        size_t ndx = draw_int_max(num_elements);
275✔
585
        if (type == type_Int) {
275✔
586
            if (m_trace) {
95✔
587
                std::cerr << trace_selected_int_array(client) << "->insert(" << ndx << ", 0);\n";
×
588
            }
×
589
            static_cast<Lst<int64_t>*>(client.selected_array.get())->insert(ndx, 0);
95✔
590
        }
95✔
591
        else if (type == type_String) {
180✔
592
            if (m_trace) {
180✔
593
                std::cerr << trace_selected_string_array(client) << "->insert(" << ndx << ", \"\");\n";
×
594
            }
×
595
            static_cast<Lst<StringData>*>(client.selected_array.get())->insert(ndx, "");
180✔
596
        }
180✔
597
    }
275✔
598

599
    void array_remove(Peer& client)
600
    {
57✔
601
        size_t num_elements = client.selected_array->size();
57✔
602
        size_t ndx = draw_int_max(num_elements - 1);
57✔
603
        if (m_trace) {
57✔
604
            std::cerr << "client_" << client.local_file_ident
×
605
                      << "->"
×
606
                         "selected_array->remove("
×
607
                      << ndx << ", " << ndx + 1 << ");\n";
×
608
        }
×
609
        client.selected_array->remove(ndx, ndx + 1);
57✔
610
    }
57✔
611

612
    void array_move(Peer& client)
613
    {
614
        size_t num_elements = client.selected_array->size();
615
        size_t from_ndx, to_ndx;
616
        for (;;) {
617
            from_ndx = draw_int_mod(num_elements);
618
            to_ndx = draw_int_mod(num_elements);
619
            if (from_ndx != to_ndx)
620
                break;
621
        }
622

623
        if (m_trace) {
624
            std::cerr << trace_selected_array(client) << "->move_row(" << from_ndx << ", " << to_ndx << ");\n";
625
        }
626
        client.selected_array->move(from_ndx, to_ndx);
627
    }
628

629
    void array_clear(Peer& client)
630
    {
4✔
631
        if (m_trace) {
4✔
632
            std::cerr << trace_selected_array(client) << "->clear();\n";
×
633
        }
×
634
        client.selected_array->clear();
4✔
635
    }
4✔
636

637
    using action_func_type = void (FuzzTester<Source>::*)(Peer&);
638
    using action_type = std::pair<int, action_func_type>; // First componenet is 'weight'
639

640
    Source& m_source;
641
    const bool m_trace;
642
    int_fast64_t m_current_value;
643
    std::vector<ColKey> m_unstructured_columns;
644
    std::vector<ColKey> m_link_list_columns;
645
    std::vector<ColKey> m_array_columns;
646

647
    int_fast64_t next_value()
648
    {
119✔
649
        return ++m_current_value;
119✔
650
    }
119✔
651

652
    void get_group_level_modify_actions(size_t num_classes, std::vector<action_type>& actions)
653
    {
267✔
654
        if (num_classes >= 1)
267✔
655
            actions.push_back(std::make_pair(rename_table_weight + 0, &FuzzTester<Source>::rename_table));
243✔
656
        if (true)
267✔
657
            actions.push_back(std::make_pair(add_table_weight + 0, &FuzzTester<Source>::add_table));
267✔
658
        if (num_classes >= 1)
267✔
659
            actions.push_back(std::make_pair(erase_table_weight + 0, &FuzzTester<Source>::erase_table));
243✔
660
    }
267✔
661

662
    void get_table_level_modify_actions(size_t num_classes, size_t num_cols, size_t num_rows,
663
                                        std::vector<action_type>& actions)
664
    {
1,373✔
665
        if (true)
1,373✔
666
            actions.push_back(std::make_pair(insert_column_weight + 0, &FuzzTester<Source>::insert_column));
1,373✔
667
        if (num_classes > 1)
1,373✔
668
            actions.push_back(std::make_pair(insert_link_column_weight + 0, &FuzzTester<Source>::insert_link_column));
1,291✔
669
        if (num_classes >= 1)
1,373✔
670
            actions.push_back(
1,373✔
671
                std::make_pair(insert_array_column_weight + 0, &FuzzTester<Source>::insert_array_column));
1,373✔
672
        if (num_rows >= 1 && !m_unstructured_columns.empty())
1,373✔
673
            actions.push_back(std::make_pair(update_row_weight + 0, &FuzzTester<Source>::update_row));
415✔
674
        if (num_cols >= 1)
1,373✔
675
            actions.push_back(std::make_pair(insert_row_weight + 0, &FuzzTester<Source>::insert_row));
1,373✔
676
        if (num_rows >= 1)
1,373✔
677
            actions.push_back(std::make_pair(erase_row_weight + 0, &FuzzTester<Source>::move_last_row_over));
1,076✔
678
    }
1,373✔
679

680
    void get_link_list_level_modify_actions(size_t num_links, std::vector<action_type>& actions)
681
    {
×
682
        if (num_links >= 1)
×
683
            actions.push_back(std::make_pair(set_link_weight + 0, &FuzzTester<Source>::set_link));
×
684
        if (true)
×
685
            actions.push_back(std::make_pair(insert_link_weight + 0, &FuzzTester<Source>::insert_link));
×
686
        if (num_links >= 1)
×
687
            actions.push_back(std::make_pair(remove_link_weight + 0, &FuzzTester<Source>::remove_link));
×
688
        if (num_links >= 2)
×
689
            actions.push_back(std::make_pair(move_link_weight + 0, &FuzzTester<Source>::move_link));
×
690
        if (true)
×
691
            actions.push_back(std::make_pair(clear_link_list_weight + 0, &FuzzTester<Source>::clear_link_list));
×
692
    }
×
693

694
    void get_array_level_modify_actions(size_t num_elements, std::vector<action_type>& actions)
695
    {
408✔
696
        if (num_elements >= 1)
408✔
697
            actions.push_back(std::make_pair(array_set_weight + 0, &FuzzTester<Source>::array_set));
210✔
698
        if (true)
408✔
699
            actions.push_back(std::make_pair(array_insert_weight + 0, &FuzzTester<Source>::array_insert));
408✔
700
        if (num_elements >= 1)
408✔
701
            actions.push_back(std::make_pair(array_remove_weight + 0, &FuzzTester<Source>::array_remove));
210✔
702
        // if (num_elements >= 2)
703
        //     actions.push_back(std::make_pair(array_move_weight+0, &FuzzTester<Source>::array_move));
704
        if (true)
408✔
705
            actions.push_back(std::make_pair(array_clear_weight + 0, &FuzzTester<Source>::array_clear));
408✔
706
    }
408✔
707

708
    size_t count_classes(Peer& client)
709
    {
2,149✔
710
        size_t count = 0;
2,149✔
711
        for (TableKey key : client.group->get_table_keys()) {
8,112✔
712
            if (client.group->get_table_name(key).begins_with("class_"))
8,112✔
713
                ++count;
8,112✔
714
        }
8,112✔
715
        return count;
2,149✔
716
    }
2,149✔
717

718
    TableRef get_class(Peer& client, size_t ndx)
719
    {
1,813✔
720
        size_t x = 0;
1,813✔
721
        for (TableKey key : client.group->get_table_keys()) {
4,332✔
722
            if (client.group->get_table_name(key).begins_with("class_")) {
4,332✔
723
                if (x == ndx)
4,332✔
724
                    return client.group->get_table(key);
1,813✔
725
                else
2,519✔
726
                    ++x;
2,519✔
727
            }
4,332✔
728
        }
4,332✔
729
        return TableRef{};
×
730
    }
1,813✔
731
};
732

733
template <class S>
734
void FuzzTester<S>::round(unit_test::TestContext& test_context, std::string path_add_on)
735
{
8✔
736
    m_current_value = 0;
8✔
737

738
    if (m_trace) {
8✔
739
        std::cerr << "auto changeset_dump_dir_gen = get_changeset_dump_dir_generator(test_context);\n"
×
740
                  << "auto server = Peer::create_server(test_context, changeset_dump_dir_gen.get());\n";
×
741
    }
×
742
    auto changeset_dump_dir_gen = get_changeset_dump_dir_generator(test_context);
8✔
743
    auto server = Peer::create_server(test_context, changeset_dump_dir_gen.get(), path_add_on);
8✔
744
    std::vector<std::unique_ptr<Peer>> clients(num_clients);
8✔
745
    for (int i = 0; i < num_clients; ++i) {
40✔
746
        using file_ident_type = Peer::file_ident_type;
32✔
747
        file_ident_type client_file_ident = 2 + i;
32✔
748
        if (m_trace) {
32✔
749
            std::cerr << "auto client_" << client_file_ident << " = Peer::create_client(test_context, "
×
750
                      << client_file_ident << ", changeset_dump_dir_gen.get());\n";
×
751
        }
×
752
        clients[i] = Peer::create_client(test_context, client_file_ident, changeset_dump_dir_gen.get(), path_add_on);
32✔
753
    }
32✔
754
    int pending_modifications = num_modifications_per_round;
8✔
755
    std::vector<int> pending_uploads(num_clients);   // One entry per client
8✔
756
    std::vector<int> pending_downloads(num_clients); // One entry per client
8✔
757
    std::vector<int> client_indexes;
8✔
758
    std::vector<action_type> actions;
8✔
759
    for (;;) {
10,248✔
760
        int client_index;
10,248✔
761
        bool can_modify = pending_modifications > 0;
10,248✔
762
        if (can_modify) {
10,248✔
763
            client_index = draw_int_mod(num_clients);
5,833✔
764
        }
5,833✔
765
        else {
4,415✔
766
            client_indexes.clear();
4,415✔
767
            for (int i = 0; i < num_clients; ++i) {
22,075✔
768
                if (pending_uploads[i] > 0 || pending_downloads[i] > 0)
17,660✔
769
                    client_indexes.push_back(i);
16,606✔
770
            }
17,660✔
771
            if (client_indexes.empty())
4,415✔
772
                break;
8✔
773
            client_index = client_indexes[draw_int_mod(client_indexes.size())];
4,407✔
774
        }
4,407✔
775
        Peer& client = *clients[client_index];
10,240✔
776
        if (m_source.chance(1, 2)) {
10,240✔
777
            int time;
5,103✔
778
            if (m_source.chance(1, 16)) {
5,103✔
779
                time = draw_int(-16, -1);
321✔
780
            }
321✔
781
            else {
4,782✔
782
                time = draw_int(1, 5);
4,782✔
783
            }
4,782✔
784
            if (m_trace) {
5,103✔
785
                std::cerr << "client_" << client.local_file_ident
×
786
                          << "->"
×
787
                             "history.advance_time("
×
788
                          << time << ");\n";
×
789
            }
×
790
            client.history.advance_time(time);
5,103✔
791
        }
5,103✔
792
        bool can_upload = pending_uploads[client_index] > 0;
10,240✔
793
        bool can_download = pending_downloads[client_index] > 0;
10,240✔
794
        long long accum_weights = 0;
10,240✔
795
        if (can_modify)
10,240✔
796
            accum_weights += modify_weight;
5,833✔
797
        if (can_upload)
10,240✔
798
            accum_weights += upload_weight;
5,759✔
799
        if (can_download)
10,240✔
800
            accum_weights += download_weight;
10,153✔
801
        REALM_ASSERT(accum_weights > 0);
10,240✔
802
        long long rest_weight = draw_int_mod(accum_weights);
10,240✔
803
        if (can_modify) {
10,240✔
804
            if (rest_weight < modify_weight) {
5,833✔
805
                actions.clear();
2,048✔
806
                if (m_trace) {
2,048✔
807
                    std::cerr << "client_" << client.local_file_ident
×
808
                              << "->"
×
809
                                 "start_transaction();\n";
×
810
                }
×
811
                client.start_transaction();
2,048✔
812
                size_t num_classes = count_classes(client);
2,048✔
813
                bool group_level =
2,048✔
814
                    num_classes == 0 || draw_float<double>() >= group_to_table_level_transition_chance();
2,048✔
815
                if (group_level) {
2,048✔
816
                    get_group_level_modify_actions(num_classes, actions);
267✔
817
                }
267✔
818
                else {
1,781✔
819
                    // Draw a table, but not the special "pk" table.
820
                    TableRef table = get_class(client, draw_int_mod<size_t>(num_classes));
1,781✔
821

822
                    if (m_trace && table != client.selected_table) {
1,781!
823
                        std::cerr << trace_selected_table(client) << " = " << trace_client(client)
×
824
                                  << "->"
×
825
                                     "group->get_table(\""
×
826
                                  << table->get_name() << "\");\n";
×
827
                    }
×
828
                    client.selected_table = table;
1,781✔
829
                    m_unstructured_columns.clear();
1,781✔
830
                    m_link_list_columns.clear();
1,781✔
831
                    m_array_columns.clear();
1,781✔
832
                    size_t n = table->get_column_count();
1,781✔
833
                    for (ColKey key : table->get_column_keys()) {
2,987✔
834
                        if (table->get_primary_key_column() == key)
2,987✔
835
                            continue; // don't make normal modifications to primary keys
1,781✔
836
                        DataType type = table->get_column_type(key);
1,206✔
837
                        if (key.is_list() && type == type_Link) {
1,206✔
838
                            // Only consider LinkList columns that target tables
839
                            // with rows in them.
840
                            if (table->get_link_target(key)->size() != 0) {
×
841
                                m_link_list_columns.push_back(key);
×
842
                            }
×
843
                        }
×
844
                        else if (table->is_list(key)) {
1,206✔
845
                            m_array_columns.push_back(key);
477✔
846
                        }
477✔
847
                        else {
729✔
848
                            m_unstructured_columns.push_back(key);
729✔
849
                        }
729✔
850
                    }
1,206✔
851
                    size_t num_cols = table->get_column_count();
1,781✔
852
                    size_t num_rows = table->size();
1,781✔
853
                    bool table_level = num_rows == 0 || (m_link_list_columns.empty() && m_array_columns.empty()) ||
1,781✔
854
                                       draw_float<double>() >= table_to_array_level_transition_chance();
1,781✔
855
                    if (table_level) {
1,781✔
856
                        get_table_level_modify_actions(num_classes, num_cols, num_rows, actions);
1,373✔
857
                    }
1,373✔
858
                    else {
408✔
859
                        REALM_ASSERT(n > 0); // No columns implies no rows
408✔
860
                        size_t i = draw_int_mod<size_t>(m_link_list_columns.size() + m_array_columns.size());
408✔
861
                        ColKey col_key;
408✔
862
                        bool is_array;
408✔
863
                        if (i >= m_link_list_columns.size()) {
408✔
864
                            col_key = m_array_columns[i - m_link_list_columns.size()];
408✔
865
                            is_array = true;
408✔
866
                        }
408✔
867
                        else {
×
868
                            col_key = m_link_list_columns[i];
×
869
                            is_array = false;
×
870
                        }
×
871

872
                        size_t row_ndx = draw_int_mod<size_t>(num_rows);
408✔
873
                        ObjKey row_key = (table->begin() + row_ndx)->get_key();
408✔
874

875
                        if (is_array) {
408✔
876
                            DataType type = table->get_column_type(col_key);
408✔
877
                            if (type == type_Int) {
408✔
878
                                LstPtr<int64_t> array = table->get_object(row_key).get_list_ptr<int64_t>(col_key);
138✔
879
                                if (m_trace) {
138✔
880
                                    std::cerr << trace_selected_array(client) << " = " << trace_selected_table(client)
×
881
                                              << "->get_object(" << row_key << ").get_list_ptr<int64_t>(" << col_key
×
882
                                              << ");\n";
×
883
                                }
×
884
                                client.selected_array = std::move(array);
138✔
885
                            }
138✔
886
                            else if (type == type_String) {
270✔
887
                                LstPtr<StringData> array =
270✔
888
                                    table->get_object(row_key).get_list_ptr<StringData>(col_key);
270✔
889
                                if (m_trace) {
270✔
890
                                    std::cerr << trace_selected_array(client) << " = " << trace_selected_table(client)
×
891
                                              << "->get_object(" << row_key << ").get_list_ptr<StringData>("
×
892
                                              << col_key << ");\n";
×
893
                                }
×
894
                                client.selected_array = std::move(array);
270✔
895
                            }
270✔
896
                            else {
×
897
                                REALM_TERMINATE("Unsupported list type.");
898
                            }
×
899
                            size_t num_elements = client.selected_array->size();
408✔
900
                            get_array_level_modify_actions(num_elements, actions);
408✔
901
                        }
408✔
902
                        else {
×
903
                            LnkLstPtr link_list = table->get_object(row_key).get_linklist_ptr(col_key);
×
904
                            if (m_trace) {
×
905
                                std::cerr << trace_selected_link_list(client) << " = " << trace_selected_table(client)
×
906
                                          << "->get_object(" << row_key << ").get_linklist_ptr(" << col_key << ");\n";
×
907
                            }
×
908
                            size_t num_links = link_list->size();
×
909
                            client.selected_link_list = std::move(link_list);
×
910
                            get_link_list_level_modify_actions(num_links, actions);
×
911
                        }
×
912
                    }
408✔
913
                }
1,781✔
914
                long long accum_weights_2 = 0;
2,048✔
915
                for (int i = 0; i < int(actions.size()); ++i)
10,938✔
916
                    accum_weights_2 += actions[i].first;
8,890✔
917
                long long rest_weight_2 = draw_int_mod(accum_weights_2);
2,048✔
918
                action_func_type action_func = 0;
2,048✔
919
                for (int i = 0; i < int(actions.size()); ++i) {
6,911✔
920
                    int action_weight = actions[i].first;
6,911✔
921
                    if (rest_weight_2 < action_weight) {
6,911✔
922
                        action_func = actions[i].second;
2,048✔
923
                        break;
2,048✔
924
                    }
2,048✔
925
                    rest_weight_2 -= action_weight;
4,863✔
926
                }
4,863✔
927
                REALM_ASSERT(action_func);
2,048✔
928
                (this->*action_func)(client);
2,048✔
929
                if (m_trace) {
2,048✔
930
                    std::cerr << "client_" << client.local_file_ident
×
931
                              << "->"
×
932
                                 "commit();";
×
933
                }
×
934
                auto produced_version = client.commit();
2,048✔
935
                if (m_trace) {
2,048✔
936
                    std::cerr << " // changeset " << produced_version << '\n';
×
937
                }
×
938
                ++pending_uploads[client_index];
2,048✔
939
                --pending_modifications;
2,048✔
940
                continue;
2,048✔
941
            }
2,048✔
942
            rest_weight -= modify_weight;
3,785✔
943
        }
3,785✔
944
        if (can_upload) {
8,192✔
945
            if (rest_weight < upload_weight) {
4,029✔
946
                if (m_trace) {
2,048✔
947
                    std::cerr << "server->integrate_next_changeset_from"
×
948
                                 "(*client_"
×
949
                              << client.local_file_ident << ");\n";
×
950
                }
×
951
                bool identical_initial_schema_creating_transaction = server->integrate_next_changeset_from(client);
2,048✔
952
                --pending_uploads[client_index];
2,048✔
953
                for (int i = 0; i < num_clients; ++i) {
10,240✔
954
                    if (i != client_index)
8,192✔
955
                        ++pending_downloads[i];
6,144✔
956
                }
8,192✔
957
                if (m_trace && identical_initial_schema_creating_transaction) {
2,048!
958
                    std::cerr << "// Special handling of identical initial "
×
959
                                 "schema-creating transaction occurred\n";
×
960
                }
×
961
                continue;
2,048✔
962
            }
2,048✔
963
            rest_weight -= upload_weight;
1,981✔
964
        }
1,981✔
965
        if (can_download) {
6,144✔
966
            if (rest_weight < download_weight) {
6,144✔
967
                if (m_trace) {
6,144✔
968
                    std::cerr << "client_" << client.local_file_ident
×
969
                              << "->"
×
970
                                 "integrate_next_changeset_from(*server);\n";
×
971
                }
×
972
                client.integrate_next_changeset_from(*server);
6,144✔
973
                --pending_downloads[client_index];
6,144✔
974
                continue;
6,144✔
975
            }
6,144✔
976
            rest_weight -= download_weight;
×
977
        }
×
978
        REALM_ASSERT(false);
×
979
    }
×
980

981
    ReadTransaction rt_0(server->shared_group);
8✔
982
    for (int i = 0; i < num_clients; ++i) {
40✔
983
        ReadTransaction rt_1(clients[i]->shared_group);
32✔
984
        bool same = CHECK(compare_groups(rt_0, rt_1));
32✔
985
        if (!same) {
32✔
986
            std::cout << "Server" << std::endl;
×
987
            rt_0.get_group().to_json(std::cout);
×
988
            std::cout << "Client_" << clients[i]->local_file_ident << std::endl;
×
989
            rt_1.get_group().to_json(std::cout);
×
990
        }
×
991
        CHECK(same);
32✔
992
    }
32✔
993
}
8✔
994

995

996
} // namespace test_util
997
} // namespace realm
998

999
#endif // REALM_TEST_FUZZ_TESTER_HPP
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