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

realm / realm-core / github_pull_request_314032

07 Mar 2025 05:25PM UTC coverage: 91.11% (-0.03%) from 91.143%
github_pull_request_314032

Pull #8075

Evergreen

web-flow
Bump axios and cmake-js

Bumps [axios](https://github.com/axios/axios) to 1.8.2 and updates ancestor dependency [cmake-js](https://github.com/cmake-js/cmake-js). These dependencies need to be updated together.


Updates `axios` from 0.21.4 to 1.8.2
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.21.4...v1.8.2)

Updates `cmake-js` from 6.3.2 to 7.3.0
- [Release notes](https://github.com/cmake-js/cmake-js/releases)
- [Changelog](https://github.com/cmake-js/cmake-js/blob/master/changelog.md)
- [Commits](https://github.com/cmake-js/cmake-js/compare/v6.3.2...v7.3.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
- dependency-name: cmake-js
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #8075: Bump axios and cmake-js

102712 of 181548 branches covered (56.58%)

217414 of 238627 relevant lines covered (91.11%)

5992215.16 hits per line

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

50.85
/test/util/compare_groups.cpp
1
#include "compare_groups.hpp"
2

3
#include <algorithm>
4
#include <vector>
5
#include <set>
6
#include <sstream>
7
#include <iostream>
8

9
#include <realm/table.hpp>
10
#include <realm/sync/object_id.hpp>
11
#include <realm/list.hpp>
12
#include <realm/dictionary.hpp>
13
#include <realm/set.hpp>
14

15
using namespace realm;
16

17
namespace {
18

19

20
class TableCompareLogger : public util::Logger {
21
public:
22
    TableCompareLogger(StringData table_name, util::Logger& base_logger) noexcept
23
        : util::Logger()
576✔
24
        , m_table_name{table_name}
576✔
25
        , m_base_logger{base_logger}
576✔
26
    {
1,140✔
27
        set_level_threshold(base_logger.get_level_threshold());
1,140✔
28
    }
1,140✔
29
    void do_log(const util::LogCategory& category, Level level, const std::string& message) override final
30
    {
×
31
        ensure_prefix();                                                    // Throws
×
32
        Logger::do_log(m_base_logger, category, level, m_prefix + message); // Throws
×
33
    }
×
34

35
private:
36
    const StringData m_table_name;
37
    util::Logger& m_base_logger;
38
    std::string m_prefix;
39
    void ensure_prefix()
40
    {
×
41
        if (REALM_LIKELY(!m_prefix.empty()))
×
42
            return;
×
43
        std::ostringstream out;
×
44
        out << "Table[" << m_table_name << "]: "; // Throws
×
45
        m_prefix = out.str();                     // Throws
×
46
    }
×
47
};
48

49

50
class ObjectCompareLogger : public util::Logger {
51
public:
52
    ObjectCompareLogger(Mixed pk, util::Logger& base_logger) noexcept
53
        : util::Logger()
3,618✔
54
        , m_pk{pk}
3,618✔
55
        , m_base_logger{base_logger}
3,618✔
56
    {
7,300✔
57
        set_level_threshold(base_logger.get_level_threshold());
7,300✔
58
    }
7,300✔
59
    void do_log(const util::LogCategory& category, Level level, const std::string& message) override final
60
    {
×
61
        ensure_prefix();                                                    // Throws
×
62
        Logger::do_log(m_base_logger, category, level, m_prefix + message); // Throws
×
63
    }
×
64

65
private:
66
    const Mixed m_pk;
67
    util::Logger& m_base_logger;
68
    std::string m_prefix;
69
    void ensure_prefix()
70
    {
×
71
        if (REALM_LIKELY(!m_prefix.empty()))
×
72
            return;
×
73
        std::ostringstream out;
×
74
        out << m_pk << ": ";  // Throws
×
75
        m_prefix = out.str(); // Throws
×
76
    }
×
77
};
78

79

80
template <class T, class Cmp = std::equal_to<>>
81
bool compare_arrays(T& a, T& b, Cmp equals = Cmp{})
82
{
384✔
83
    auto a_it = a.begin();
384✔
84
    auto b_it = b.begin();
384✔
85
    if (a.size() != b.size()) {
384!
86
#if REALM_DEBUG
×
87
        std::cerr << "LEFT size: " << a.size() << std::endl;
×
88
        std::cerr << "RIGHT size: " << b.size() << std::endl;
×
89
#endif // REALM_DEBUG
×
90
        return false;
×
91
    }
×
92

93
    // Compare entries
94
    for (; a_it != a.end(); ++a_it, ++b_it) {
750!
95
        if (!equals(*a_it, *b_it))
366!
96
            goto different;
×
97
    }
366✔
98

99
    return true;
384✔
100
different:
×
101
#if REALM_DEBUG
×
102
    std::cerr << "LEFT: " << *a_it << std::endl;
×
103
    std::cerr << "RIGHT: " << *b_it << std::endl;
×
104
#endif // REALM_DEBUG
×
105
    return false;
×
106
}
384✔
107

108
template <class T>
109
bool compare_set_values(const Set<T>& a, const Set<T>& b)
110
{
26✔
111
    return compare_arrays(a, b);
26✔
112
}
26✔
113

114
bool compare_dictionaries(const Dictionary& a, const Dictionary& b)
115
{
34✔
116
    auto a_it = a.begin();
34✔
117
    auto b_it = b.begin();
34✔
118
    if (a.size() != b.size()) {
34✔
119
#if REALM_DEBUG
×
120
        std::cerr << "LEFT size: " << a.size() << std::endl;
×
121
        std::cerr << "RIGHT size: " << b.size() << std::endl;
×
122
#endif
×
123
        return false;
×
124
    }
×
125

126
    // Compare entries
127
    for (; a_it != a.end(); ++a_it, ++b_it) {
186✔
128
        if (*a_it != *b_it)
152✔
129
            goto different;
×
130
    }
152✔
131

132
    return true;
34✔
133

134
different:
×
135
#if REALM_DEBUG
×
136
    std::cerr << "LEFT: " << (*a_it).first << " => " << (*a_it).second << std::endl;
×
137
    std::cerr << "RIGHT: " << (*b_it).first << " => " << (*b_it).second << std::endl;
×
138
#endif
×
139
    return false;
×
140
}
34✔
141

142
struct Column {
143
    StringData name;
144
    ColKey key_1, key_2;
145

146
    DataType get_type() const noexcept
147
    {
23,100✔
148
        return DataType(key_1.get_type());
23,100✔
149
    }
23,100✔
150

151
    bool is_list() const noexcept
152
    {
23,070✔
153
        return key_1.is_list();
23,070✔
154
    }
23,070✔
155

156
    bool is_dictionary() const noexcept
157
    {
23,134✔
158
        return key_1.is_dictionary();
23,134✔
159
    }
23,134✔
160

161
    bool is_set() const noexcept
162
    {
23,100✔
163
        return key_1.is_set();
23,100✔
164
    }
23,100✔
165

166
    bool is_nullable() const noexcept
167
    {
23,680✔
168
        return key_1.is_nullable();
23,680✔
169
    }
23,680✔
170
};
171

172
ObjKey row_for_primary_key(const Table& table, Mixed pk)
173
{
14,600✔
174
    ColKey pk_col = table.get_primary_key_column();
14,600✔
175
    if (pk_col) {
14,600✔
176
        return table.find_primary_key(pk); // will assert on type mismatch
14,600✔
177
    }
14,600✔
178

179
    if (pk.is_type(type_Link, type_TypedLink)) {
×
180
        return pk.get<ObjKey>();
×
181
    }
×
182
    else {
×
183
        REALM_TERMINATE("row_for_primary_key() with primary key, expected GlobalKey");
184
    }
×
185
    return {};
×
186
}
×
187

188
bool compare_objects(const Obj& obj_1, const Obj& obj_2, const std::vector<Column>& columns, util::Logger& logger);
189
bool compare_objects(Mixed& oid, const Table& table_1, const Table& table_2, const std::vector<Column>& columns,
190
                     util::Logger& logger);
191

192
bool compare_schemas(const Table& table_1, const Table& table_2, util::Logger& logger,
193
                     std::vector<Column>* out_columns = nullptr)
194
{
1,827✔
195
    bool equal = true;
1,827✔
196

197
    // Compare column names
198
    {
1,827✔
199
        auto col_keys = table_1.get_column_keys();
1,827✔
200
        for (auto key : col_keys) {
3,934✔
201
            StringData name = table_1.get_column_name(key);
3,934✔
202
            if (!table_2.get_column_key(name)) {
3,934✔
203
                logger.error("Column '%1' not found in right-hand side table", name);
×
204
                equal = false;
×
205
            }
×
206
        }
3,934✔
207
    }
1,827✔
208
    {
1,827✔
209
        auto col_keys = table_2.get_column_keys();
1,827✔
210
        for (auto key : col_keys) {
3,934✔
211
            StringData name = table_2.get_column_name(key);
3,934✔
212
            if (!table_1.get_column_key(name)) {
3,934✔
213
                logger.error("Column '%1' not found in left-hand side table", name);
×
214
                equal = false;
×
215
            }
×
216
        }
3,934✔
217
    }
1,827✔
218

219
    // Compare column signatures
220
    {
1,827✔
221
        auto keys_1 = table_1.get_column_keys();
1,827✔
222
        for (auto key_1 : keys_1) {
3,934✔
223
            StringData name = table_1.get_column_name(key_1);
3,934✔
224
            ColKey key_2 = table_2.get_column_key(name);
3,934✔
225
            if (!key_2)
3,934✔
226
                continue;
×
227
            DataType type_1 = table_1.get_column_type(key_1);
3,934✔
228
            DataType type_2 = table_2.get_column_type(key_2);
3,934✔
229
            if (type_1 != type_2) {
3,934✔
230
                logger.error("Type mismatch on column '%1'", name);
×
231
                equal = false;
×
232
                continue;
×
233
            }
×
234
            bool nullable_1 = table_1.is_nullable(key_1);
3,934✔
235
            bool nullable_2 = table_2.is_nullable(key_2);
3,934✔
236
            if (nullable_1 != nullable_2) {
3,934✔
237
                logger.error("Nullability mismatch on column '%1'", name);
×
238
                equal = false;
×
239
                continue;
×
240
            }
×
241
            bool is_list_1 = table_1.is_list(key_1);
3,934✔
242
            bool is_list_2 = table_2.is_list(key_2);
3,934✔
243
            if (is_list_1 != is_list_2) {
3,934✔
244
                logger.error("List type mismatch on column '%1'", name);
×
245
                equal = false;
×
246
                continue;
×
247
            }
×
248
            bool is_dictionary_1 = key_1.is_dictionary();
3,934✔
249
            bool is_dictionary_2 = key_2.is_dictionary();
3,934✔
250
            if (is_dictionary_1 != is_dictionary_2) {
3,934✔
251
                logger.error("Dictionary type mismatch on column '%1'", name);
×
252
                equal = false;
×
253
                continue;
×
254
            }
×
255
            bool is_set_1 = key_1.is_set();
3,934✔
256
            bool is_set_2 = key_2.is_set();
3,934✔
257
            if (is_set_1 != is_set_2) {
3,934✔
258
                logger.error("Set type mismatch on column '%1'", name);
×
259
                equal = false;
×
260
                continue;
×
261
            }
×
262
            if (type_1 == type_Link) {
3,934✔
263
                ConstTableRef target_1 = table_1.get_link_target(key_1);
838✔
264
                ConstTableRef target_2 = table_2.get_link_target(key_2);
838✔
265
                if (target_1->get_name() != target_2->get_name()) {
838✔
266
                    logger.error("Link target mismatch on column '%1'", name);
×
267
                    equal = false;
×
268
                    continue;
×
269
                }
×
270
            }
838✔
271
            if (out_columns)
3,934✔
272
                out_columns->push_back(Column{name, key_1, key_2});
3,934✔
273
        }
3,934✔
274
    }
1,827✔
275

276
    return equal;
1,827✔
277
}
1,827✔
278

279
bool compare_lists(const Column& col, const Obj& obj_1, const Obj& obj_2, util::Logger& logger)
280
{
1,050✔
281
    switch (col.get_type()) {
1,050✔
282
        case type_Int: {
184✔
283
            if (col.is_nullable()) {
184✔
284
                auto a = obj_1.get_list<util::Optional<int64_t>>(col.key_1);
×
285
                auto b = obj_2.get_list<util::Optional<int64_t>>(col.key_2);
×
286
                if (!compare_arrays(a, b)) {
×
287
                    logger.error("List mismatch in column '%1'", col.name);
×
288
                    return false;
×
289
                }
×
290
            }
×
291
            else {
184✔
292
                auto a = obj_1.get_list<int64_t>(col.key_1);
184✔
293
                auto b = obj_2.get_list<int64_t>(col.key_2);
184✔
294
                if (!compare_arrays(a, b)) {
184✔
295
                    logger.error("List mismatch in column '%1'", col.name);
×
296
                    return false;
×
297
                }
×
298
            }
184✔
299
            break;
184✔
300
        }
184✔
301
        case type_Bool: {
184✔
302
            auto a = obj_1.get_list<bool>(col.key_1);
×
303
            auto b = obj_2.get_list<bool>(col.key_2);
×
304
            if (!compare_arrays(a, b)) {
×
305
                logger.error("List mismatch in column '%1'", col.name);
×
306
                return false;
×
307
            }
×
308
            break;
×
309
        }
×
310
        case type_String: {
166✔
311
            auto a = obj_1.get_list<String>(col.key_1);
166✔
312
            auto b = obj_2.get_list<String>(col.key_2);
166✔
313
            if (!compare_arrays(a, b)) {
166✔
314
                logger.error("List mismatch in column '%1'", col.name);
×
315
                return false;
×
316
            }
×
317
            break;
166✔
318
        }
166✔
319
        case type_Binary: {
166✔
320
            auto a = obj_1.get_list<Binary>(col.key_1);
×
321
            auto b = obj_2.get_list<Binary>(col.key_2);
×
322
            if (!compare_arrays(a, b)) {
×
323
                logger.error("List mismatch in column '%1'", col.name);
×
324
                return false;
×
325
            }
×
326
            break;
×
327
        }
×
328
        case type_Float: {
✔
329
            auto a = obj_1.get_list<float>(col.key_1);
×
330
            auto b = obj_2.get_list<float>(col.key_2);
×
331
            if (!compare_arrays(a, b)) {
×
332
                logger.error("List mismatch in column '%1'", col.name);
×
333
                return false;
×
334
            }
×
335
            break;
×
336
        }
×
337
        case type_Double: {
✔
338
            auto a = obj_1.get_list<double>(col.key_1);
×
339
            auto b = obj_2.get_list<double>(col.key_2);
×
340
            if (!compare_arrays(a, b)) {
×
341
                logger.error("List mismatch in column '%1'", col.name);
×
342
                return false;
×
343
            }
×
344
            break;
×
345
        }
×
346
        case type_Timestamp: {
✔
347
            auto a = obj_1.get_list<Timestamp>(col.key_1);
×
348
            auto b = obj_2.get_list<Timestamp>(col.key_2);
×
349
            if (!compare_arrays(a, b)) {
×
350
                logger.error("List mismatch in column '%1'", col.name);
×
351
                return false;
×
352
            }
×
353
            break;
×
354
        }
×
355
        case type_ObjectId: {
✔
356
            auto a = obj_1.get_list<ObjectId>(col.key_1);
×
357
            auto b = obj_2.get_list<ObjectId>(col.key_2);
×
358
            if (!compare_arrays(a, b)) {
×
359
                logger.error("List mismatch in column '%1'", col.name);
×
360
                return false;
×
361
            }
×
362
            break;
×
363
        }
×
364
        case type_UUID: {
✔
365
            auto a = obj_1.get_list<UUID>(col.key_1);
×
366
            auto b = obj_2.get_list<UUID>(col.key_2);
×
367
            if (!compare_arrays(a, b)) {
×
368
                logger.error("List mismatch in column '%1'", col.name);
×
369
                return false;
×
370
            }
×
371
            break;
×
372
        }
×
373
        case type_Decimal: {
✔
374
            auto a = obj_1.get_list<Decimal128>(col.key_1);
×
375
            auto b = obj_2.get_list<Decimal128>(col.key_2);
×
376
            if (!compare_arrays(a, b)) {
×
377
                logger.error("List mismatch in column '%1'", col.name);
×
378
                return false;
×
379
            }
×
380
            break;
×
381
        }
×
382
        case type_Mixed: {
8✔
383
            auto a = obj_1.get_list<Mixed>(col.key_1);
8✔
384
            auto b = obj_2.get_list<Mixed>(col.key_2);
8✔
385
            if (!compare_arrays(a, b)) {
8✔
386
                logger.error("List mismatch in column '%1'", col.name);
×
387
                return false;
×
388
            }
×
389
            break;
8✔
390
        }
8✔
391
        case type_TypedLink:
8✔
392
            // FIXME: Implement
393
            break;
×
394
        case type_Link: {
692✔
395
            auto a = obj_1.get_list<ObjKey>(col.key_1);
692✔
396
            auto b = obj_2.get_list<ObjKey>(col.key_2);
692✔
397
            if (a.size() != b.size()) {
692✔
398
                logger.error("Link list size mismatch in column '%1'", col.name);
×
399
                return false;
×
400
                break;
×
401
            }
×
402
            auto table_1 = obj_1.get_table();
692✔
403
            auto table_2 = obj_2.get_table();
692✔
404
            ConstTableRef target_table_1 = table_1->get_link_target(col.key_1);
692✔
405
            ConstTableRef target_table_2 = table_2->get_link_target(col.key_2);
692✔
406

407
            bool is_embedded = target_table_1->is_embedded();
692✔
408
            std::vector<Column> embedded_columns;
692✔
409
            if (is_embedded) {
692✔
410
                // FIXME: This does the schema comparison for
411
                // embedded tables for every object with embedded
412
                // objects, just because we want to get the Column
413
                // info. Instead compare just the objects
414
                // themselves.
415
                bool schemas_equal = compare_schemas(*target_table_1, *target_table_2, logger, &embedded_columns);
640✔
416
                REALM_ASSERT(schemas_equal);
640✔
417
            }
640✔
418

419
            std::size_t n = a.size();
692✔
420
            for (std::size_t i = 0; i < n; ++i) {
1,448✔
421
                ObjKey link_1 = a.get(i);
756✔
422
                ObjKey link_2 = b.get(i);
756✔
423

424
                if (link_1.is_unresolved() || link_2.is_unresolved()) {
756✔
425
                    // if one link is unresolved, the other should also be unresolved
426
                    if (!link_1.is_unresolved() || !link_2.is_unresolved()) {
22✔
427
                        logger.error("Value mismatch in column '%1' at index %2 of the link "
×
428
                                     "list (%3 vs %4)",
×
429
                                     col.name, i, link_1, link_2);
×
430
                        return false;
×
431
                    }
×
432
                }
22✔
433
                else {
734✔
434
                    if (is_embedded) {
734✔
435
                        const Obj embedded_1 = target_table_1->get_object(link_1);
674✔
436
                        const Obj embedded_2 = target_table_2->get_object(link_2);
674✔
437
                        // Skip ID comparison for embedded objects, because
438
                        // they are only identified by their position in the
439
                        // database.
440
                        if (!compare_objects(embedded_1, embedded_2, embedded_columns, logger)) {
674✔
441
                            logger.error("Embedded object contents mismatch in column '%1'", col.name);
×
442
                            return false;
×
443
                        }
×
444
                    }
674✔
445
                    else {
60✔
446
                        Mixed target_oid_1 = target_table_1->get_primary_key(link_1);
60✔
447
                        Mixed target_oid_2 = target_table_2->get_primary_key(link_2);
60✔
448
                        if (target_oid_1 != target_oid_2) {
60✔
449
                            logger.error("Value mismatch in column '%1' at index %2 of the link "
×
450
                                         "list (%3 vs %4)",
×
451
                                         col.name, i, link_1, link_2);
×
452
                            return false;
×
453
                        }
×
454
                    }
60✔
455
                }
734✔
456
            }
756✔
457
            break;
692✔
458
        }
692✔
459
    }
1,050✔
460

461
    return true;
1,050✔
462
}
1,050✔
463

464
bool compare_sets(const Column& col, const Obj& obj_1, const Obj& obj_2, util::Logger& logger)
465
{
30✔
466
    switch (col.get_type()) {
30✔
467
        case type_Int: {
8✔
468
            if (col.is_nullable()) {
8✔
469
                auto a = obj_1.get_set<util::Optional<int64_t>>(col.key_1);
×
470
                auto b = obj_2.get_set<util::Optional<int64_t>>(col.key_2);
×
471
                if (!compare_set_values(a, b)) {
×
472
                    logger.error("Set mismatch in column '%1'", col.name);
×
473
                    return false;
×
474
                }
×
475
            }
×
476
            else {
8✔
477
                auto a = obj_1.get_set<int64_t>(col.key_1);
8✔
478
                auto b = obj_2.get_set<int64_t>(col.key_2);
8✔
479
                if (!compare_set_values(a, b)) {
8✔
480
                    logger.error("Set mismatch in column '%1'", col.name);
×
481
                    return false;
×
482
                }
×
483
            }
8✔
484
            break;
8✔
485
        }
8✔
486
        case type_Bool: {
8✔
487
            auto a = obj_1.get_set<bool>(col.key_1);
×
488
            auto b = obj_2.get_set<bool>(col.key_2);
×
489
            if (!compare_set_values(a, b)) {
×
490
                logger.error("Set mismatch in column '%1'", col.name);
×
491
                return false;
×
492
            }
×
493
            break;
×
494
        }
×
495
        case type_String: {
4✔
496
            auto a = obj_1.get_set<String>(col.key_1);
4✔
497
            auto b = obj_2.get_set<String>(col.key_2);
4✔
498
            if (!compare_set_values(a, b)) {
4✔
499
                logger.error("Set mismatch in column '%1'", col.name);
×
500
                return false;
×
501
            }
×
502
            break;
4✔
503
        }
4✔
504
        case type_Binary: {
4✔
505
            auto a = obj_1.get_set<Binary>(col.key_1);
×
506
            auto b = obj_2.get_set<Binary>(col.key_2);
×
507
            if (!compare_set_values(a, b)) {
×
508
                logger.error("Set mismatch in column '%1'", col.name);
×
509
                return false;
×
510
            }
×
511
            break;
×
512
        }
×
513
        case type_Float: {
✔
514
            auto a = obj_1.get_set<float>(col.key_1);
×
515
            auto b = obj_2.get_set<float>(col.key_2);
×
516
            if (!compare_set_values(a, b)) {
×
517
                logger.error("Set mismatch in column '%1'", col.name);
×
518
                return false;
×
519
            }
×
520
            break;
×
521
        }
×
522
        case type_Double: {
✔
523
            auto a = obj_1.get_set<double>(col.key_1);
×
524
            auto b = obj_2.get_set<double>(col.key_2);
×
525
            if (!compare_set_values(a, b)) {
×
526
                logger.error("Set mismatch in column '%1'", col.name);
×
527
                return false;
×
528
            }
×
529
            break;
×
530
        }
×
531
        case type_Timestamp: {
✔
532
            auto a = obj_1.get_set<Timestamp>(col.key_1);
×
533
            auto b = obj_2.get_set<Timestamp>(col.key_2);
×
534
            if (!compare_set_values(a, b)) {
×
535
                logger.error("Set mismatch in column '%1'", col.name);
×
536
                return false;
×
537
            }
×
538
            break;
×
539
        }
×
540
        case type_ObjectId: {
✔
541
            auto a = obj_1.get_set<ObjectId>(col.key_1);
×
542
            auto b = obj_2.get_set<ObjectId>(col.key_2);
×
543
            if (!compare_set_values(a, b)) {
×
544
                logger.error("Set mismatch in column '%1'", col.name);
×
545
                return false;
×
546
            }
×
547
            break;
×
548
        }
×
549
        case type_UUID: {
✔
550
            auto a = obj_1.get_set<UUID>(col.key_1);
×
551
            auto b = obj_2.get_set<UUID>(col.key_2);
×
552
            if (!compare_set_values(a, b)) {
×
553
                logger.error("Set mismatch in column '%1'", col.name);
×
554
                return false;
×
555
            }
×
556
            break;
×
557
        }
×
558
        case type_Decimal: {
✔
559
            auto a = obj_1.get_set<Decimal128>(col.key_1);
×
560
            auto b = obj_2.get_set<Decimal128>(col.key_2);
×
561
            if (!compare_set_values(a, b)) {
×
562
                logger.error("Set mismatch in column '%1'", col.name);
×
563
                return false;
×
564
            }
×
565
            break;
×
566
        }
×
567
        case type_Mixed: {
14✔
568
            auto a = obj_1.get_set<Mixed>(col.key_1);
14✔
569
            auto b = obj_2.get_set<Mixed>(col.key_2);
14✔
570
            if (!compare_set_values(a, b)) {
14✔
571
                logger.error("Set mismatch in column '%1'", col.name);
×
572
                return false;
×
573
            }
×
574
            break;
14✔
575
        }
14✔
576
        case type_Link: {
14✔
577
            auto a = obj_1.get_linkset(col.key_1);
4✔
578
            auto b = obj_2.get_linkset(col.key_2);
4✔
579
            if (a.size() != b.size()) {
4✔
580
                logger.error("Link set size mismatch in column '%1'", col.name);
×
581
                return false;
×
582
                break;
×
583
            }
×
584

585
            auto target_table_1 = a.get_target_table();
4✔
586
            auto target_table_2 = b.get_target_table();
4✔
587

588
            std::size_t n = a.size();
4✔
589
            for (std::size_t i = 0; i < n; ++i) {
8✔
590
                ObjKey link_1 = a.get(i);
4✔
591
                ObjKey link_2 = b.get(i);
4✔
592

593
                if (link_1.is_unresolved() || link_2.is_unresolved()) {
4✔
594
                    // if one link is unresolved, the other should also be unresolved
595
                    if (!link_1.is_unresolved() || !link_2.is_unresolved()) {
×
596
                        logger.error("Value mismatch in column '%1' at index %2 of the link "
×
597
                                     "set (%3 vs %4)",
×
598
                                     col.name, i, link_1, link_2);
×
599
                        return false;
×
600
                    }
×
601
                }
×
602
                else {
4✔
603
                    Mixed target_oid_1 = target_table_1->get_primary_key(link_1);
4✔
604
                    Mixed target_oid_2 = target_table_2->get_primary_key(link_2);
4✔
605
                    if (target_oid_1 != target_oid_2) {
4✔
606
                        logger.error("Value mismatch in column '%1' at index %2 of the link "
×
607
                                     "set (%3 vs %4)",
×
608
                                     col.name, i, link_1, link_2);
×
609
                        return false;
×
610
                    }
×
611
                }
4✔
612
            }
4✔
613
            break;
4✔
614
        }
4✔
615
        case type_TypedLink:
4✔
616
            // FIXME: Implement
617
            break;
×
618
    }
30✔
619

620
    return true;
30✔
621
}
30✔
622

623
bool compare_objects(const Obj& obj_1, const Obj& obj_2, const std::vector<Column>& columns, util::Logger& logger)
624
{
8,022✔
625
    bool equal = true;
8,022✔
626
    auto ptable_1 = obj_1.get_table();
8,022✔
627
    auto ptable_2 = obj_2.get_table();
8,022✔
628
    auto& table_1 = *ptable_1;
8,022✔
629
    auto& table_2 = *ptable_2;
8,022✔
630

631
    for (const Column& col : columns) {
23,488✔
632
        if (col.is_nullable()) {
23,488✔
633
            bool a = obj_1.is_null(col.key_1);
858✔
634
            bool b = obj_2.is_null(col.key_2);
858✔
635
            if (a && b)
858✔
636
                continue;
354✔
637
            if (a || b) {
504✔
638
                logger.error("Null/nonnull disagreement in column '%1' (%2 vs %3)", col.name, a, b);
×
639
                equal = false;
×
640
                continue;
×
641
            }
×
642
        }
504✔
643

644
        if (col.is_dictionary()) {
23,134✔
645
            auto a = obj_1.get_dictionary(col.key_1);
34✔
646
            auto b = obj_2.get_dictionary(col.key_2);
34✔
647
            if (!compare_dictionaries(a, b)) {
34✔
648
                logger.error("Dictionary mismatch in column '%1'", col.name);
×
649
                equal = false;
×
650
            }
×
651
            continue;
34✔
652
        }
34✔
653

654
        if (col.is_set()) {
23,100✔
655
            if (!compare_sets(col, obj_1, obj_2, logger)) {
30✔
656
                logger.error("Set mismatch in column '%1'", col.name);
×
657
                equal = false;
×
658
            }
×
659
            continue;
30✔
660
        }
30✔
661

662
        if (col.is_list()) {
23,070✔
663
            if (!compare_lists(col, obj_1, obj_2, logger)) {
1,050✔
664
                equal = false;
×
665
            }
×
666
            continue;
1,050✔
667
        }
1,050✔
668

669
        const bool nullable = table_1.is_nullable(col.key_1);
22,020✔
670
        REALM_ASSERT(table_2.is_nullable(col.key_2) == nullable);
22,020✔
671
        switch (col.get_type()) {
22,020✔
672
            case type_Int: {
20,260✔
673
                if (nullable) {
20,260✔
674
                    auto a = obj_1.get<util::Optional<int64_t>>(col.key_1);
70✔
675
                    auto b = obj_2.get<util::Optional<int64_t>>(col.key_2);
70✔
676
                    if (a != b) {
70✔
677
                        logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
678
                        equal = false;
×
679
                    }
×
680
                }
70✔
681
                else {
20,190✔
682
                    auto a = obj_1.get<int64_t>(col.key_1);
20,190✔
683
                    auto b = obj_2.get<int64_t>(col.key_2);
20,190✔
684
                    if (a != b) {
20,190✔
685
                        logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
686
                        equal = false;
×
687
                    }
×
688
                }
20,190✔
689
                continue;
20,260✔
690
            }
×
691
            case type_Bool: {
12✔
692
                if (nullable) {
12✔
693
                    auto a = obj_1.get<util::Optional<bool>>(col.key_1);
×
694
                    auto b = obj_2.get<util::Optional<bool>>(col.key_2);
×
695
                    if (a != b) {
×
696
                        logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
697
                        equal = false;
×
698
                    }
×
699
                }
×
700
                else {
12✔
701
                    auto a = obj_1.get<bool>(col.key_1);
12✔
702
                    auto b = obj_2.get<bool>(col.key_2);
12✔
703
                    if (a != b) {
12✔
704
                        logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
705
                        equal = false;
×
706
                    }
×
707
                }
12✔
708

709
                continue;
12✔
710
            }
×
711
            case type_Float: {
12✔
712
                auto a = obj_1.get<float>(col.key_1);
12✔
713
                auto b = obj_2.get<float>(col.key_2);
12✔
714
                if (a != b) {
12✔
715
                    logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
716
                    equal = false;
×
717
                }
×
718
                continue;
12✔
719
            }
×
720
            case type_Double: {
16✔
721
                auto a = obj_1.get<double>(col.key_1);
16✔
722
                auto b = obj_2.get<double>(col.key_2);
16✔
723
                if (a != b) {
16✔
724
                    logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
725
                    equal = false;
×
726
                }
×
727
                continue;
16✔
728
            }
×
729
            case type_String: {
522✔
730
                auto a = obj_1.get<StringData>(col.key_1);
522✔
731
                auto b = obj_2.get<StringData>(col.key_2);
522✔
732
                if (a != b) {
522✔
733
                    logger.error("Value mismatch in column '%1'", col.name);
×
734
                    equal = false;
×
735
                }
×
736
                continue;
522✔
737
            }
×
738
            case type_Binary: {
832✔
739
                // FIXME: This looks like an incorrect way of comparing BLOBs (Table::get_binary_iterator()).
740
                auto a = obj_1.get<BinaryData>(col.key_1);
832✔
741
                auto b = obj_2.get<BinaryData>(col.key_2);
832✔
742
                if (a != b) {
832✔
743
                    logger.error("Value mismatch in column '%1'", col.name);
×
744
                    equal = false;
×
745
                }
×
746
                continue;
832✔
747
            }
×
748
            case type_Timestamp: {
16✔
749
                auto a = obj_1.get<Timestamp>(col.key_1);
16✔
750
                auto b = obj_2.get<Timestamp>(col.key_2);
16✔
751
                if (a != b) {
16✔
752
                    logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
753
                    equal = false;
×
754
                }
×
755
                continue;
16✔
756
            }
×
757
            case type_ObjectId: {
8✔
758
                auto a = obj_1.get<ObjectId>(col.key_1);
8✔
759
                auto b = obj_2.get<ObjectId>(col.key_2);
8✔
760
                if (a != b) {
8✔
761
                    logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
762
                    equal = false;
×
763
                }
×
764
                continue;
8✔
765
            }
×
766
            case type_Decimal: {
8✔
767
                auto a = obj_1.get<Decimal128>(col.key_1);
8✔
768
                auto b = obj_2.get<Decimal128>(col.key_2);
8✔
769
                if (a != b) {
8✔
770
                    logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
771
                    equal = false;
×
772
                }
×
773
                continue;
8✔
774
            }
×
775
            case type_Mixed: {
274✔
776
                auto a = obj_1.get<Mixed>(col.key_1);
274✔
777
                auto b = obj_2.get<Mixed>(col.key_2);
274✔
778
                if (a != b) {
274✔
779
                    logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
780
                    equal = false;
×
781
                }
×
782
                continue;
274✔
783
            }
×
784
            case type_UUID: {
✔
785
                auto a = obj_1.get<UUID>(col.key_1);
×
786
                auto b = obj_2.get<UUID>(col.key_2);
×
787
                if (a != b) {
×
788
                    logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, a, b);
×
789
                    equal = false;
×
790
                }
×
791
                continue;
×
792
            }
×
793
            case type_TypedLink:
✔
794
                // FIXME: Implement
795
                continue;
×
796
            case type_Link: {
60✔
797
                auto link_1 = obj_1.get<ObjKey>(col.key_1);
60✔
798
                auto link_2 = obj_2.get<ObjKey>(col.key_2);
60✔
799
                ConstTableRef target_table_1 = table_1.get_link_target(col.key_1);
60✔
800
                ConstTableRef target_table_2 = table_2.get_link_target(col.key_2);
60✔
801

802
                if (!link_1 || !link_2) {
60✔
803
                    // If one link is null the other should also be null
804
                    if (link_1 != link_2) {
×
805
                        equal = false;
×
806
                        logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, link_1, link_2);
×
807
                    }
×
808
                }
×
809
                else {
60✔
810
                    bool is_embedded = target_table_1->is_embedded();
60✔
811
                    std::vector<Column> embedded_columns;
60✔
812
                    if (is_embedded) {
60✔
813
                        // FIXME: This does the schema comparison for
814
                        // embedded tables for every object with embedded
815
                        // objects, just because we want to get the Column
816
                        // info. Instead compare just the objects
817
                        // themselves.
818
                        bool schemas_equal =
48✔
819
                            compare_schemas(*target_table_1, *target_table_2, logger, &embedded_columns);
48✔
820
                        REALM_ASSERT(schemas_equal);
48✔
821
                    }
48✔
822

823
                    if (is_embedded) {
60✔
824
                        const Obj embedded_1 = target_table_1->get_object(link_1);
48✔
825
                        const Obj embedded_2 = target_table_2->get_object(link_2);
48✔
826
                        // Skip ID comparison for embedded objects, because
827
                        // they are only identified by their position in the
828
                        // database.
829
                        if (!compare_objects(embedded_1, embedded_2, embedded_columns, logger)) {
48✔
830
                            logger.error("Embedded object contents mismatch in column '%1'", col.name);
×
831
                            equal = false;
×
832
                        }
×
833
                    }
48✔
834
                    else {
12✔
835
                        Mixed target_oid_1 = target_table_1->get_primary_key(link_1);
12✔
836
                        Mixed target_oid_2 = target_table_2->get_primary_key(link_2);
12✔
837
                        if (target_oid_1 != target_oid_2) {
12✔
838
                            logger.error("Value mismatch in column '%1' (%2 vs %3)", col.name, target_oid_1,
×
839
                                         target_oid_2);
×
840
                            equal = false;
×
841
                        }
×
842
                    }
12✔
843
                }
60✔
844

845
                continue;
60✔
846
            }
×
847
        }
22,020✔
848
        REALM_TERMINATE("Unsupported column type.");
849
    }
×
850
    return equal;
8,022✔
851
}
8,022✔
852

853
bool compare_objects(Mixed& pk, const Table& table_1, const Table& table_2, const std::vector<Column>& columns,
854
                     util::Logger& logger)
855
{
7,300✔
856
    ObjKey oid_1 = row_for_primary_key(table_1, pk);
7,300✔
857
    ObjKey oid_2 = row_for_primary_key(table_2, pk);
7,300✔
858

859
    // Note: This is ensured by the inventory handling in compare_tables().
860
    REALM_ASSERT(oid_1);
7,300✔
861
    REALM_ASSERT(oid_2);
7,300✔
862
    const Obj obj_1 = table_1.get_object(oid_1);
7,300✔
863
    const Obj obj_2 = table_2.get_object(oid_2);
7,300✔
864
    return compare_objects(obj_1, obj_2, columns, logger);
7,300✔
865
}
7,300✔
866

867
} // anonymous namespace
868

869
namespace realm::test_util {
870

871
bool compare_tables(const Table& table_1, const Table& table_2, util::Logger& logger)
872
{
1,139✔
873
    bool equal = true;
1,139✔
874

875
    std::vector<Column> columns;
1,139✔
876
    equal = compare_schemas(table_1, table_2, logger, &columns);
1,139✔
877

878
    if (table_1.is_embedded() != table_2.is_embedded()) {
1,139✔
879
        logger.error("Table embeddedness mismatch");
×
880
        equal = false;
×
881
    }
×
882

883
    if (table_1.is_embedded() || table_2.is_embedded()) {
1,139✔
884
        if (table_1.size() != table_2.size()) {
90✔
885
            logger.error("Embedded table size mismatch (%1 vs %2): %3", table_1.size(), table_2.size(),
×
886
                         table_1.get_name());
×
887
            equal = false;
×
888
        }
×
889
        // Do not attempt to compare by row on embedded tables.
890
        return equal;
90✔
891
    }
90✔
892

893
    // Compare row sets
894
    using Objects = std::set<Mixed>;
1,049✔
895
    auto make_inventory = [](const Table& table, Objects& objects) {
2,100✔
896
        for (const Obj& obj : table) {
14,600✔
897
            objects.insert(obj.get_primary_key());
14,600✔
898
        }
14,600✔
899
    };
2,100✔
900
    Objects objects_1, objects_2;
1,049✔
901
    make_inventory(table_1, objects_1);
1,049✔
902
    make_inventory(table_2, objects_2);
1,049✔
903
    auto report_missing = [&](const char* hand_2, Objects& objects_1, Objects& objects_2) {
2,100✔
904
        std::vector<Mixed> missing;
2,100✔
905
        for (auto oid : objects_1) {
14,600✔
906
            if (objects_2.find(oid) == objects_2.end())
14,600✔
907
                missing.push_back(oid);
×
908
        }
14,600✔
909
        if (missing.empty())
2,100✔
910
            return;
2,100✔
911
        std::size_t n = missing.size();
×
912
        if (n == 1) {
×
913
            logger.error("One object missing in %1 side table: %2", hand_2, missing[0]);
×
914
            equal = false;
×
915
            return;
×
916
        }
×
917
        std::ostringstream out;
×
918
        out << missing[0];
×
919
        std::size_t m = std::min<std::size_t>(4, n);
×
920
        for (std::size_t i = 1; i < m; ++i)
×
921
            out << ", " << missing[i];
×
922
        if (m < n)
×
923
            out << ", ...";
×
924
        logger.error("%1 objects missing in %2 side table: %3", n, hand_2, out.str());
×
925
        equal = false;
×
926
    };
×
927
    report_missing("right-hand", objects_1, objects_2);
1,049✔
928
    report_missing("left-hand", objects_2, objects_1);
1,049✔
929

930
    // Compare individual rows
931
    for (auto pk : objects_1) {
7,300✔
932
        if (objects_2.find(pk) != objects_2.end()) {
7,300✔
933
            ObjectCompareLogger sublogger{pk, logger};
7,300✔
934
            if (!compare_objects(pk, table_1, table_2, columns, sublogger)) {
7,300✔
935
                equal = false;
×
936
            }
×
937
        }
7,300✔
938
    }
7,300✔
939

940
    return equal;
1,049✔
941
}
1,139✔
942

943

944
bool compare_groups(const Transaction& group_1, const Transaction& group_2)
945
{
306✔
946
    util::StderrLogger logger(util::Logger::Level::off);
306✔
947
    return compare_groups(group_1, group_2, logger);
306✔
948
}
306✔
949

950

951
bool compare_groups(const Transaction& group_1, const Transaction& group_2,
952
                    util::FunctionRef<bool(StringData)> filter_func, util::Logger& logger)
953
{
846✔
954
    std::vector<std::string_view> ignored_tables = {"pk", "metadata", "client_reset_metadata", "flx_metadata",
846✔
955
                                                    "sync_internal_schemas"};
846✔
956

957
    auto filter = [&](const Group& group, std::vector<StringData>& tables) {
1,692✔
958
        auto table_keys = group.get_table_keys();
1,692✔
959
        for (auto i : table_keys) {
2,304✔
960
            ConstTableRef table = group.get_table(i);
2,304✔
961
            StringData name = table->get_name();
2,304✔
962
            if (std::find(ignored_tables.begin(), ignored_tables.end(), name) == ignored_tables.end() &&
2,304✔
963
                filter_func(name))
2,304✔
964
                tables.push_back(name);
2,284✔
965
        }
2,304✔
966
    };
1,692✔
967

968
    std::vector<StringData> tables_1, tables_2;
846✔
969
    filter(group_1, tables_1);
846✔
970
    filter(group_2, tables_2);
846✔
971

972
    bool equal = true;
846✔
973
    for (StringData table_name : tables_1) {
1,142✔
974
        if (!group_2.has_table(table_name)) {
1,142✔
975
            logger.error("Table '%1' not found in right-hand side group", table_name);
2✔
976
            equal = false;
2✔
977
        }
2✔
978
    }
1,142✔
979
    for (StringData table_name : tables_2) {
1,142✔
980
        if (!group_1.has_table(table_name)) {
1,142✔
981
            logger.error("Table '%1' not found in left-hand side group", table_name);
2✔
982
            equal = false;
2✔
983
        }
2✔
984
    }
1,142✔
985

986
    for (StringData table_name : tables_1) {
1,142✔
987
        ConstTableRef table_1 = group_1.get_table(table_name);
1,142✔
988
        ConstTableRef table_2 = group_2.get_table(table_name);
1,142✔
989
        if (table_2) {
1,142✔
990
            TableCompareLogger sublogger{table_name, logger};
1,140✔
991
            if (!compare_tables(*table_1, *table_2, sublogger))
1,140✔
992
                equal = false;
×
993
        }
1,140✔
994
    }
1,142✔
995

996
    return equal;
846✔
997
}
846✔
998

999
} // namespace realm::test_util
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