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

realm / realm-core / thomas.goyne_275

09 Apr 2024 03:33AM CUT coverage: 92.608% (+0.5%) from 92.088%
thomas.goyne_275

Pull #7300

Evergreen

tgoyne
Extract some duplicated code in PushClient
Pull Request #7300: Rework sync user handling and metadata storage

102672 of 194970 branches covered (52.66%)

3165 of 3247 new or added lines in 46 files covered. (97.47%)

34 existing lines in 9 files now uncovered.

249420 of 269329 relevant lines covered (92.61%)

45087511.34 hits per line

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

99.79
/test/object-store/c_api/c_api.cpp
1
////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2019 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 "../util/test_file.hpp"
20
#include "../util/event_loop.hpp"
21

22
#include <realm.h>
23

24
#include <realm/object-store/c_api/conversion.hpp>
25
#include <realm/object-store/c_api/realm.hpp>
26
#include <realm/object-store/c_api/types.hpp>
27
#include <realm/object-store/impl/object_accessor_impl.hpp>
28
#include <realm/object-store/object.hpp>
29
#include <realm/object-store/sync/generic_network_transport.hpp>
30
#include <realm/sync/binding_callback_thread_observer.hpp>
31
#include <realm/util/base64.hpp>
32
#include <realm/util/logger.hpp>
33

34
#include <catch2/catch_all.hpp>
35

36
#include <cstring>
37
#include <numeric>
38
#include <thread>
39
#include <fstream>
40

41
#if REALM_ENABLE_SYNC
42
#include <util/sync/flx_sync_harness.hpp>
43
#include <util/sync/sync_test_utils.hpp>
44
#include "../util/test_path.hpp"
45
#include "../util/unit_test_transport.hpp"
46

47
#include <realm/object-store/sync/app_utils.hpp>
48
#include <realm/object-store/sync/sync_user.hpp>
49
#include <realm/sync/client_base.hpp>
50
#include <realm/sync/network/websocket.hpp>
51
#include <realm/sync/protocol.hpp>
52
#include <realm/util/misc_errors.hpp>
53

54
#include <external/json/json.hpp>
55
#endif
56

57
#if REALM_ENABLE_AUTH_TESTS
58
#include "util/sync/baas_admin_api.hpp"
59
#endif
60

61
using namespace realm;
62

63
extern "C" int realm_c_api_tests(const char* file);
64

65
namespace {
66
template <class T>
67
T checked(T x)
68
{
62,240✔
69
    if (!x) {
62,240✔
70
        realm_error_t err_info;
16✔
71
        if (realm_get_last_error(&err_info)) {
16!
72
            FAIL(err_info.message);
×
73
        }
×
74
    }
16✔
75
    return x;
62,240✔
76
}
62,240✔
77

78
realm_value_t rlm_str_val(const char* str)
79
{
4,544✔
80
    realm_value_t val;
4,544✔
81
    val.type = RLM_TYPE_STRING;
4,544✔
82
    val.string = realm_string_t{str, std::strlen(str)};
4,544✔
83
    return val;
4,544✔
84
}
4,544✔
85

86
realm_value_t rlm_int_val(int64_t n)
87
{
6,720✔
88
    realm_value_t val;
6,720✔
89
    val.type = RLM_TYPE_INT;
6,720✔
90
    val.integer = n;
6,720✔
91
    return val;
6,720✔
92
}
6,720✔
93

94
realm_value_t rlm_float_val(float fnum)
95
{
80✔
96
    realm_value_t val;
80✔
97
    val.type = RLM_TYPE_FLOAT;
80✔
98
    val.fnum = fnum;
80✔
99
    return val;
80✔
100
}
80✔
101

102
realm_value_t rlm_double_val(double dnum)
103
{
112✔
104
    realm_value_t val;
112✔
105
    val.type = RLM_TYPE_DOUBLE;
112✔
106
    val.dnum = dnum;
112✔
107
    return val;
112✔
108
}
112✔
109

110
realm_value_t rlm_object_id_val(const char* hex_digits)
111
{
80✔
112
    size_t len = strlen(hex_digits);
80✔
113
    REALM_ASSERT(len == 12);
80✔
114
    realm_value_t val;
80✔
115
    val.type = RLM_TYPE_OBJECT_ID;
80✔
116
    for (size_t i = 0; i < 12; ++i) {
1,040✔
117
        val.object_id.bytes[i] = uint8_t(hex_digits[i]);
960✔
118
    }
960✔
119
    return val;
80✔
120
}
80✔
121

122
realm_value_t rlm_timestamp_val(int64_t seconds, int32_t nanoseconds)
123
{
80✔
124
    realm_value_t val;
80✔
125
    val.type = RLM_TYPE_TIMESTAMP;
80✔
126
    val.timestamp.seconds = seconds;
80✔
127
    val.timestamp.nanoseconds = nanoseconds;
80✔
128
    return val;
80✔
129
}
80✔
130

131
realm_value_t rlm_bool_val(bool b)
132
{
80✔
133
    realm_value_t val;
80✔
134
    val.type = RLM_TYPE_BOOL;
80✔
135
    val.boolean = b;
80✔
136
    return val;
80✔
137
}
80✔
138

139
realm_value_t rlm_decimal_val(double d)
140
{
80✔
141
    realm_value_t val;
80✔
142
    val.type = RLM_TYPE_DECIMAL128;
80✔
143

40✔
144
    realm::Decimal128 dec{d};
80✔
145
    val.decimal128.w[0] = dec.raw()->w[0];
80✔
146
    val.decimal128.w[1] = dec.raw()->w[1];
80✔
147

40✔
148
    return val;
80✔
149
}
80✔
150

151
realm_value_t rlm_decimal_nan()
152
{
16✔
153
    realm_value_t val;
16✔
154
    val.type = RLM_TYPE_DECIMAL128;
16✔
155

8✔
156
    realm::Decimal128 dec = realm::Decimal128::nan("0");
16✔
157
    val.decimal128.w[0] = dec.raw()->w[0];
16✔
158
    val.decimal128.w[1] = dec.raw()->w[1];
16✔
159

8✔
160
    return val;
16✔
161
}
16✔
162

163
realm_value_t rlm_uuid_val(const char* str)
164
{
80✔
165
    realm_value_t val;
80✔
166
    val.type = RLM_TYPE_UUID;
80✔
167
    realm::UUID uuid{realm::StringData{str}};
80✔
168
    auto bytes = uuid.to_bytes();
80✔
169
    for (size_t i = 0; i < 16; ++i) {
1,360✔
170
        val.uuid.bytes[i] = bytes[i];
1,280✔
171
    }
1,280✔
172
    return val;
80✔
173
}
80✔
174

175
realm_value_t rlm_binary_val(const uint8_t* bytes, size_t len)
176
{
80✔
177
    realm_value_t val;
80✔
178
    val.type = RLM_TYPE_BINARY;
80✔
179
    val.binary.size = len;
80✔
180
    val.binary.data = bytes;
80✔
181
    return val;
80✔
182
}
80✔
183

184
realm_value_t rlm_link_val(realm_class_key_t cls, realm_object_key_t obj)
185
{
48✔
186
    realm_value_t val;
48✔
187
    val.type = RLM_TYPE_LINK;
48✔
188
    val.link.target_table = cls;
48✔
189
    val.link.target = obj;
48✔
190
    return val;
48✔
191
}
48✔
192

193
realm_value_t rlm_null()
194
{
768✔
195
    realm_value_t null;
768✔
196
    null.type = RLM_TYPE_NULL;
768✔
197
    return null;
768✔
198
}
768✔
199

200
std::string rlm_stdstr(realm_value_t val)
201
{
96✔
202
    CHECK(val.type == RLM_TYPE_STRING);
96!
203
    return std::string(val.string.data, 0, val.string.size);
96✔
204
}
96✔
205

206
bool rlm_val_eq(realm_value_t lhs, realm_value_t rhs)
207
{
3,216✔
208
    if (lhs.type != rhs.type)
3,216✔
209
        return false;
×
210

1,608✔
211
    switch (lhs.type) {
3,216✔
212
        case RLM_TYPE_NULL:
720✔
213
        case RLM_TYPE_LIST:
720✔
214
        case RLM_TYPE_DICTIONARY:
720✔
215
            return true;
720✔
216
        case RLM_TYPE_INT:
704✔
217
            return lhs.integer == rhs.integer;
688✔
218
        case RLM_TYPE_BOOL:
440✔
219
            return lhs.boolean == rhs.boolean;
160✔
220
        case RLM_TYPE_STRING:
608✔
221
            return strncmp(lhs.string.data, rhs.string.data, lhs.string.size) == 0;
496✔
222
        case RLM_TYPE_BINARY:
440✔
223
            return memcmp(lhs.binary.data, rhs.binary.data, lhs.binary.size) == 0;
160✔
224
        case RLM_TYPE_TIMESTAMP:
440✔
225
            return lhs.timestamp.seconds == rhs.timestamp.seconds &&
160✔
226
                   lhs.timestamp.nanoseconds == rhs.timestamp.nanoseconds;
160✔
227
        case RLM_TYPE_FLOAT:
440✔
228
            return lhs.fnum == rhs.fnum;
160✔
229
        case RLM_TYPE_DOUBLE:
440✔
230
            return lhs.dnum == rhs.dnum;
160✔
231
        case RLM_TYPE_DECIMAL128:
440✔
232
            return lhs.decimal128.w[0] == rhs.decimal128.w[0] && lhs.decimal128.w[1] == rhs.decimal128.w[1];
160✔
233
        case RLM_TYPE_OBJECT_ID:
440✔
234
            return memcmp(lhs.object_id.bytes, rhs.object_id.bytes, 12) == 0;
160✔
235
        case RLM_TYPE_LINK:
376✔
236
            return lhs.link.target_table == rhs.link.target_table && lhs.link.target == rhs.link.target;
32✔
237
        case RLM_TYPE_UUID:
440✔
238
            return memcmp(lhs.uuid.bytes, rhs.uuid.bytes, 16) == 0;
160✔
239
    }
×
240
    REALM_TERMINATE("");
241
}
×
242

243
struct RealmReleaseDeleter {
244
    void operator()(void* ptr)
245
    {
23,086✔
246
        realm_release(ptr);
23,086✔
247
    }
23,086✔
248
};
249

250
template <class T>
251
using CPtr = std::unique_ptr<T, RealmReleaseDeleter>;
252

253
template <class T>
254
CPtr<T> cptr(T* ptr)
255
{
22,638✔
256
    return CPtr<T>{ptr};
22,638✔
257
}
22,638✔
258

259
template <class T>
260
CPtr<T> cptr_checked(T* ptr)
261
{
13,648✔
262
    return cptr(checked(ptr));
13,648✔
263
}
13,648✔
264

265
template <class T>
266
CPtr<T> clone_cptr(const CPtr<T>& ptr)
267
{
32✔
268
    void* clone = realm_clone(ptr.get());
32✔
269
    return CPtr<T>{static_cast<T*>(clone)};
32✔
270
}
32✔
271

272
template <class T>
273
CPtr<T> clone_cptr(const T* ptr)
274
{
496✔
275
    void* clone = realm_clone(ptr);
496✔
276
    return CPtr<T>{static_cast<T*>(clone)};
496✔
277
}
496✔
278
} // anonymous namespace
279

280
#define CHECK_ERR(err)                                                                                               \
281
    do {                                                                                                             \
1,312✔
282
        realm_error_t _err;                                                                                          \
1,312✔
283
        _err.message = "";                                                                                           \
1,312✔
284
        _err.error = RLM_ERR_NONE;                                                                                   \
1,312✔
285
        CHECK(realm_get_last_error(&_err));                                                                          \
1,312✔
286
        if (_err.error != err) {                                                                                     \
1,312✔
287
            CHECK(_err.error == err);                                                                                \
×
288
            CHECK(std::string{_err.message} == "");                                                                  \
×
289
        }                                                                                                            \
×
290
        else {                                                                                                       \
1,312✔
291
            realm_clear_last_error();                                                                                \
1,312✔
292
        }                                                                                                            \
1,312✔
293
    } while (false);
1,312✔
294

295
#define CHECK_ERR_CAT(err, category)                                                                                 \
296
    do {                                                                                                             \
64✔
297
        realm_error_t _err;                                                                                          \
64✔
298
        _err.message = "";                                                                                           \
64✔
299
        _err.categories = 0;                                                                                         \
64✔
300
        _err.error = RLM_ERR_NONE;                                                                                   \
64✔
301
        CHECK(realm_get_last_error(&_err));                                                                          \
64✔
302
        CHECK((_err.categories ^ category) == 0);                                                                    \
64✔
303
        if (_err.error != err) {                                                                                     \
64✔
304
            CHECK(_err.error == err);                                                                                \
×
305
            CHECK(std::string{_err.message} == "");                                                                  \
×
306
        }                                                                                                            \
×
307
        else {                                                                                                       \
64✔
308
            realm_clear_last_error();                                                                                \
64✔
309
        }                                                                                                            \
64✔
310
    } while (false);
64✔
311

312
TEST_CASE("C API (C)", "[c_api]") {
16✔
313
    TestFile file;
16✔
314
    CHECK(realm_c_api_tests(file.path.c_str()) == 0);
16!
315
}
16✔
316

317
TEST_CASE("C API (non-database)", "[c_api]") {
416✔
318
    SECTION("realm_get_library_version_numbers()") {
416✔
319
        int major, minor, patch;
16✔
320
        const char* extra;
16✔
321
        realm_get_library_version_numbers(&major, &minor, &patch, &extra);
16✔
322

8✔
323
        CHECK(major == REALM_VERSION_MAJOR);
16!
324
        CHECK(minor == REALM_VERSION_MINOR);
16!
325
        CHECK(patch == REALM_VERSION_PATCH);
16!
326
        CHECK(std::string{extra} == REALM_VERSION_EXTRA);
16!
327
    }
16✔
328

208✔
329
    SECTION("realm_get_library_version()") {
416✔
330
        const char* version = realm_get_library_version();
16✔
331
        CHECK(std::string{version} == REALM_VERSION_STRING);
16!
332
    }
16✔
333

208✔
334
    SECTION("realm_release(NULL)") {
416✔
335
        // Just check that it doesn't crash.
8✔
336
        realm_release(nullptr);
16✔
337
    }
16✔
338

208✔
339
    SECTION("realm_get_last_error()") {
416✔
340
        CHECK(!realm_get_last_error(nullptr));
16!
341
        CHECK(!realm_clear_last_error());
16!
342

8✔
343
        auto synthetic = []() {
16✔
344
            throw std::runtime_error("Synthetic error");
16✔
345
        };
16✔
346
        CHECK(!realm_wrap_exceptions(synthetic));
16!
347

8✔
348
        realm_error_t err;
16✔
349
        CHECK(realm_get_last_error(&err));
16!
350
        CHECK(err.error == RLM_ERR_RUNTIME);
16!
351
        CHECK(std::string{err.message} == "Synthetic error");
16!
352
        realm_clear_last_error();
16✔
353
    }
16✔
354

208✔
355
    SECTION("realm_get_last_error_as_async_error()") {
416✔
356
        CHECK(!realm_get_last_error_as_async_error());
32!
357

16✔
358
        auto synthetic = []() {
32✔
359
            throw std::runtime_error("Synthetic error");
32✔
360
        };
32✔
361
        CHECK(!realm_wrap_exceptions(synthetic));
32!
362

16✔
363
        realm_async_error_t* async_err = realm_get_last_error_as_async_error();
32✔
364
        CHECK(async_err);
32!
365

16✔
366
        realm_error_t err;
32✔
367
        CHECK(realm_get_async_error(async_err, &err));
32!
368
        CHECK_FALSE(realm_get_async_error(nullptr, &err));
32!
369

16✔
370
        CHECK(err.error == RLM_ERR_RUNTIME);
32!
371
        CHECK(std::string{err.message} == "Synthetic error");
32!
372

16✔
373
        SECTION("realm_clone()") {
32✔
374
            auto cloned = clone_cptr(async_err);
16✔
375
            CHECK(realm_equals(async_err, cloned.get()));
16!
376
            realm_error_t err2;
16✔
377
            CHECK(realm_get_async_error(cloned.get(), &err2));
16!
378
            CHECK(err2.error == RLM_ERR_RUNTIME);
16!
379
            CHECK(std::string{err2.message} == "Synthetic error");
16!
380
        }
16✔
381

16✔
382
        SECTION("realm_equals()") {
32✔
383
            auto config = cptr(realm_config_new());
16✔
384
            CHECK(!realm_equals(config.get(), async_err));
16!
385
            CHECK(!realm_equals(async_err, config.get()));
16!
386
        }
16✔
387

16✔
388
        realm_release(async_err);
32✔
389
        realm_clear_last_error();
32✔
390
    }
32✔
391

208✔
392
    SECTION("realm_clear_last_error()") {
416✔
393
        auto synthetic = []() {
16✔
394
            throw std::runtime_error("Synthetic error");
16✔
395
        };
16✔
396
        CHECK(!realm_wrap_exceptions(synthetic));
16!
397

8✔
398
        CHECK(realm_clear_last_error());
16!
399
        CHECK(!realm_get_last_error(nullptr));
16!
400
    }
16✔
401

208✔
402
    SECTION("realm_clone() error") {
416✔
403
        // realm_config_t is not clonable
8✔
404
        auto config = cptr(realm_config_new());
16✔
405
        CHECK(!realm_clone(config.get()));
16!
406
        CHECK_ERR(RLM_ERR_NOT_CLONABLE);
16!
407
    }
16✔
408

208✔
409
    SECTION("realm_create_thread_safe_reference() error") {
416✔
410
        // realm_config_t is not sendable between threads
8✔
411
        auto config = cptr(realm_config_new());
16✔
412
        CHECK(!realm_create_thread_safe_reference(config.get()));
16!
413
        CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
414
    }
16✔
415

208✔
416
    SECTION("realm_is_frozen() false by default") {
416✔
417
        // realm_config_t cannot be frozen, so is never frozen
8✔
418
        auto config = cptr(realm_config_new());
16✔
419
        CHECK(!realm_is_frozen(config.get()));
16!
420
    }
16✔
421

208✔
422
    SECTION("realm_equals() with different object types returns false") {
416✔
423
        auto config = cptr(realm_config_new());
16✔
424
        auto schema = cptr(realm_schema_new(nullptr, 0, nullptr));
16✔
425
        CHECK(!realm_equals(config.get(), schema.get()));
16!
426
        CHECK(!realm_equals(schema.get(), config.get()));
16!
427
    }
16✔
428

208✔
429
    SECTION("realm_config_t") {
416✔
430
        auto config = cptr(realm_config_new());
192✔
431

96✔
432
        SECTION("realm_config_set_path()") {
192✔
433
            realm_config_set_path(config.get(), "hello");
16✔
434
            CHECK(std::string{realm_config_get_path(config.get())} == "hello");
16!
435
        }
16✔
436

96✔
437
        SECTION("realm_config_set_encryption_key()") {
192✔
438
            uint8_t key[64] = {0};
16✔
439
            std::iota(std::begin(key), std::end(key), 0);
16✔
440
            CHECK(realm_config_set_encryption_key(config.get(), key, 64));
16!
441

8✔
442
            uint8_t buffer[64];
16✔
443
            size_t len = realm_config_get_encryption_key(config.get(), buffer);
16✔
444
            CHECK(len == 64);
16!
445

8✔
446
            CHECK(!realm_config_set_encryption_key(config.get(), key, 63));
16!
447
            CHECK_ERR(RLM_ERR_INVALID_ENCRYPTION_KEY);
16!
448
        }
16✔
449

96✔
450
        SECTION("realm_config_set_schema()") {
192✔
451
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
16✔
452
            realm_config_set_schema(config.get(), empty_schema.get());
16✔
453
            auto schema = cptr(realm_config_get_schema(config.get()));
16✔
454
            CHECK(schema);
16!
455
            CHECK(realm_equals(empty_schema.get(), schema.get()));
16!
456
            realm_config_set_schema(config.get(), nullptr);
16✔
457
            CHECK(realm_config_get_schema(config.get()) == nullptr);
16!
458
        }
16✔
459

96✔
460
        SECTION("realm_config_set_schema_version()") {
192✔
461
            realm_config_set_schema_version(config.get(), 26);
16✔
462
            CHECK(realm_config_get_schema_version(config.get()) == 26);
16!
463
        }
16✔
464

96✔
465
        SECTION("realm_config_set_schema_mode()") {
192✔
466
            auto check_mode = [&](realm_schema_mode_e mode) {
128✔
467
                realm_config_set_schema_mode(config.get(), mode);
128✔
468
                CHECK(realm_config_get_schema_mode(config.get()) == mode);
128!
469
            };
128✔
470
            check_mode(RLM_SCHEMA_MODE_AUTOMATIC);
16✔
471
            check_mode(RLM_SCHEMA_MODE_IMMUTABLE);
16✔
472
            check_mode(RLM_SCHEMA_MODE_READ_ONLY);
16✔
473
            check_mode(RLM_SCHEMA_MODE_SOFT_RESET_FILE);
16✔
474
            check_mode(RLM_SCHEMA_MODE_HARD_RESET_FILE);
16✔
475
            check_mode(RLM_SCHEMA_MODE_ADDITIVE_EXPLICIT);
16✔
476
            check_mode(RLM_SCHEMA_MODE_ADDITIVE_DISCOVERED);
16✔
477
            check_mode(RLM_SCHEMA_MODE_MANUAL);
16✔
478
        }
16✔
479

96✔
480
        SECTION("realm_config_set_schema_subset_mode()") {
192✔
481
            auto check_subset_mode = [&](realm_schema_subset_mode_e mode) {
64✔
482
                realm_config_set_schema_subset_mode(config.get(), mode);
64✔
483
                CHECK(realm_config_get_schema_subset_mode(config.get()) == mode);
64!
484
            };
64✔
485
            check_subset_mode(RLM_SCHEMA_SUBSET_MODE_ALL_CLASSES);
16✔
486
            check_subset_mode(RLM_SCHEMA_SUBSET_MODE_ALL_PROPERTIES);
16✔
487
            check_subset_mode(RLM_SCHEMA_SUBSET_MODE_COMPLETE);
16✔
488
            check_subset_mode(RLM_SCHEMA_SUBSET_MODE_STRICT);
16✔
489
        }
16✔
490

96✔
491
        SECTION("realm_config_set_disable_format_upgrade()") {
192✔
492
            realm_config_set_disable_format_upgrade(config.get(), true);
16✔
493
            CHECK(realm_config_get_disable_format_upgrade(config.get()) == true);
16!
494
            realm_config_set_disable_format_upgrade(config.get(), false);
16✔
495
            CHECK(realm_config_get_disable_format_upgrade(config.get()) == false);
16!
496
        }
16✔
497

96✔
498
        SECTION("realm_config_set_automatic_change_notifications()") {
192✔
499
            realm_config_set_automatic_change_notifications(config.get(), true);
16✔
500
            CHECK(realm_config_get_automatic_change_notifications(config.get()) == true);
16!
501
            realm_config_set_automatic_change_notifications(config.get(), false);
16✔
502
            CHECK(realm_config_get_automatic_change_notifications(config.get()) == false);
16!
503
        }
16✔
504

96✔
505
        SECTION("realm_config_set_force_sync_history()") {
192✔
506
            realm_config_set_force_sync_history(config.get(), true);
16✔
507
            CHECK(realm_config_get_force_sync_history(config.get()) == true);
16!
508
            realm_config_set_force_sync_history(config.get(), false);
16✔
509
            CHECK(realm_config_get_force_sync_history(config.get()) == false);
16!
510
        }
16✔
511

96✔
512
        SECTION("realm_config_set_max_number_of_active_versions()") {
192✔
513
            realm_config_set_max_number_of_active_versions(config.get(), 999);
16✔
514
            CHECK(realm_config_get_max_number_of_active_versions(config.get()) == 999);
16!
515
        }
16✔
516

96✔
517
        SECTION("realm_config_set_in_memory()") {
192✔
518
            realm_config_set_in_memory(config.get(), true);
16✔
519
            CHECK(realm_config_get_in_memory(config.get()) == true);
16!
520
        }
16✔
521

96✔
522
        SECTION("realm_config_set_fifo_path()") {
192✔
523
            realm_config_set_fifo_path(config.get(), "test_path.FIFO");
16✔
524
            CHECK(std::string{realm_config_get_fifo_path(config.get())} == "test_path.FIFO");
16!
525
        }
16✔
526
    }
192✔
527

208✔
528
    SECTION("realm_error_t is properly initialized from Status") {
416✔
529
        Status status(ErrorCodes::RuntimeError, "I am a runtime error!");
16✔
530
        realm_error_t c_err = c_api::to_capi(status);
16✔
531
        REQUIRE(c_err.error == RLM_ERR_RUNTIME);
16!
532
        REQUIRE(c_err.message == status.reason());
16!
533
        REQUIRE(c_err.categories == RLM_ERR_CAT_RUNTIME);
16!
534
        REQUIRE(c_err.path == nullptr);
16!
535
        REQUIRE(c_err.user_code_error == nullptr);
16!
536
    }
16✔
537

208✔
538
#if REALM_ENABLE_SYNC
416✔
539
    SECTION("sync_client_config_t") {
416✔
540
        auto test_sync_client_config = cptr(realm_sync_client_config_new());
16✔
541
        realm_sync_client_config_set_reconnect_mode(test_sync_client_config.get(),
16✔
542
                                                    RLM_SYNC_CLIENT_RECONNECT_MODE_TESTING);
16!
543
        CHECK(test_sync_client_config->reconnect_mode ==
16!
544
              static_cast<ReconnectMode>(RLM_SYNC_CLIENT_RECONNECT_MODE_TESTING));
16✔
545
        realm_sync_client_config_set_multiplex_sessions(test_sync_client_config.get(), true);
16!
546
        CHECK(test_sync_client_config->multiplex_sessions);
16!
547
        realm_sync_client_config_set_multiplex_sessions(test_sync_client_config.get(), false);
16✔
548
        CHECK_FALSE(test_sync_client_config->multiplex_sessions);
16!
549
        realm_sync_client_config_set_user_agent_binding_info(test_sync_client_config.get(), "some user agent stg");
16✔
550
        CHECK(test_sync_client_config->user_agent_binding_info == "some user agent stg");
16!
551
        realm_sync_client_config_set_user_agent_application_info(test_sync_client_config.get(), "some application");
16!
552
        CHECK(test_sync_client_config->user_agent_application_info == "some application");
16!
553
        realm_sync_client_config_set_connect_timeout(test_sync_client_config.get(), 666);
16✔
554
        CHECK(test_sync_client_config->timeouts.connect_timeout == 666);
16!
555
        realm_sync_client_config_set_connection_linger_time(test_sync_client_config.get(), 999);
16✔
556
        CHECK(test_sync_client_config->timeouts.connection_linger_time == 999);
16!
557
        realm_sync_client_config_set_ping_keepalive_period(test_sync_client_config.get(), 555);
16!
558
        CHECK(test_sync_client_config->timeouts.ping_keepalive_period == 555);
16!
559
        realm_sync_client_config_set_pong_keepalive_timeout(test_sync_client_config.get(), 100000);
16!
560
        CHECK(test_sync_client_config->timeouts.pong_keepalive_timeout == 100000);
16!
561
        realm_sync_client_config_set_fast_reconnect_limit(test_sync_client_config.get(), 1099);
16!
562
        CHECK(test_sync_client_config->timeouts.fast_reconnect_limit == 1099);
16!
563
        realm_sync_client_config_set_resumption_delay_interval(test_sync_client_config.get(), 1024);
16!
564
        CHECK(test_sync_client_config->timeouts.reconnect_backoff_info.resumption_delay_interval.count() == 1024);
16!
565
        realm_sync_client_config_set_max_resumption_delay_interval(test_sync_client_config.get(), 600024);
16!
566
        CHECK(test_sync_client_config->timeouts.reconnect_backoff_info.max_resumption_delay_interval.count() ==
16!
567
              600024);
16!
568
        realm_sync_client_config_set_resumption_delay_backoff_multiplier(test_sync_client_config.get(), 1010);
16✔
569
        CHECK(test_sync_client_config->timeouts.reconnect_backoff_info.resumption_delay_backoff_multiplier == 1010);
16!
570
    }
16✔
571

40!
572
    SECTION("realm_app_config_t") {
66✔
573
        const uint64_t request_timeout = 2500;
16!
574
        std::string base_url = "https://path/to/app";
16✔
575
        std::string base_url2 = "https://some/other/path";
16!
576
        auto transport = std::make_shared<UnitTestTransport>(request_timeout);
16✔
577
        transport->set_expected_options({{"device",
16!
578
                                          {{"appId", "app_id_123"},
16✔
579
                                           {"platform", util::get_library_platform()},
16✔
580
                                           {"platformVersion", "some_platform_version"},
16!
581
                                           {"sdk", "some_sdk_name"},
16✔
582
                                           {"sdkVersion", "some_sdk_version"},
160!
583
                                           {"cpuArch", util::get_library_cpu_arch()},
316✔
584
                                           {"deviceName", "some_device_name"},
40✔
585
                                           {"deviceVersion", "some_device_version"},
66✔
586
                                           {"frameworkName", "some_framework_name"},
16✔
587
                                           {"frameworkVersion", "some_framework_version"},
16✔
588
                                           {"coreVersion", REALM_VERSION_STRING},
16✔
589
                                           {"bundleId", "some_bundle_id"}}}});
16✔
590
        transport->set_base_url(base_url);
16✔
591
        auto http_transport = realm_http_transport(transport);
16✔
592
        auto app_config = cptr(realm_app_config_new("app_id_123", &http_transport));
16✔
593
        CHECK(app_config.get() != nullptr);
16!
594
        CHECK(app_config->app_id == "app_id_123");
16!
595
        CHECK(app_config->transport == transport);
16!
596

15✔
597
        CHECK(realm_app_get_default_base_url() == app::App::default_base_url());
16!
598

15✔
599
        CHECK(!app_config->base_url);
16!
600
        realm_app_config_set_base_url(app_config.get(), base_url.c_str());
16✔
601
        CHECK(app_config->base_url == base_url);
16!
602

15✔
603
        realm_app_config_set_default_request_timeout(app_config.get(), request_timeout);
16✔
604
        CHECK(app_config->default_request_timeout_ms == request_timeout);
16!
605

15!
606
        realm_app_config_set_platform_version(app_config.get(), "some_platform_version");
16!
607
        CHECK(app_config->device_info.platform_version == "some_platform_version");
10!
608

15!
609
        realm_app_config_set_sdk_version(app_config.get(), "some_sdk_version");
9✔
610
        CHECK(app_config->device_info.sdk_version == "some_sdk_version");
16!
611

14✔
612
        realm_app_config_set_sdk(app_config.get(), "some_sdk_name");
16!
613
        CHECK(app_config->device_info.sdk == "some_sdk_name");
10!
614

15!
615
        realm_app_config_set_device_name(app_config.get(), "some_device_name");
15!
616
        CHECK(app_config->device_info.device_name == "some_device_name");
10!
617

15!
618
        realm_app_config_set_device_version(app_config.get(), "some_device_version");
15!
619
        CHECK(app_config->device_info.device_version == "some_device_version");
10!
620

15!
621
        realm_app_config_set_framework_name(app_config.get(), "some_framework_name");
15!
622
        CHECK(app_config->device_info.framework_name == "some_framework_name");
10!
623

15!
624
        realm_app_config_set_framework_version(app_config.get(), "some_framework_version");
15!
625
        CHECK(app_config->device_info.framework_version == "some_framework_version");
10!
626

15!
627
        realm_app_config_set_bundle_id(app_config.get(), "some_bundle_id");
15!
628
        CHECK(app_config->device_info.bundle_id == "some_bundle_id");
10!
629

15!
630
        realm_app_config_set_base_file_path(app_config.get(), "some string");
15!
631
        CHECK(app_config->base_file_path == "some string");
10!
632

15!
633
        realm_app_config_set_metadata_mode(app_config.get(), RLM_SYNC_CLIENT_METADATA_MODE_DISABLED);
15!
634
        CHECK(app_config->metadata_mode == app::AppConfig::MetadataMode::InMemory);
10!
635
        realm_app_config_set_metadata_mode(app_config.get(), RLM_SYNC_CLIENT_METADATA_MODE_ENCRYPTED);
16!
636
        CHECK(app_config->metadata_mode == app::AppConfig::MetadataMode::Encryption);
15!
637
        realm_app_config_set_metadata_mode(app_config.get(), RLM_SYNC_CLIENT_METADATA_MODE_PLAINTEXT);
10✔
638
        CHECK(app_config->metadata_mode == app::AppConfig::MetadataMode::NoEncryption);
16!
639

14!
640
        realm_app_config_set_security_access_group(app_config.get(), "group.io.realm.test");
10✔
641
        CHECK(app_config->security_access_group == "group.io.realm.test");
16!
642

14✔
643
        auto enc_key = make_test_encryption_key(123);
16✔
644
        realm_app_config_set_metadata_encryption_key(app_config.get(), reinterpret_cast<uint8_t*>(enc_key.data()));
16✔
645
        CHECK(app_config->custom_encryption_key);
10!
646
        CHECK(std::equal(enc_key.begin(), enc_key.end(), app_config->custom_encryption_key->begin()));
16!
647

14✔
648
        test_util::TestDirGuard temp_dir(util::make_temp_dir());
100✔
649
        realm_app_config_set_base_file_path(app_config.get(), temp_dir.c_str());
10✔
650
        realm_app_config_set_metadata_mode(app_config.get(), RLM_SYNC_CLIENT_METADATA_MODE_DISABLED);
24✔
651
        realm_app_config_set_security_access_group(app_config.get(), "");
15✔
652

14✔
653
        auto test_app = cptr(realm_app_create(app_config.get()));
16✔
654
        realm_user_t* sync_user;
16✔
655
        auto user_data_free = [](realm_userdata_t) {};
30!
656

15!
657
        // Verify the values above are included in the login request
15!
658
        auto credentials = cptr(realm_app_credentials_new_anonymous(true));
16!
659
        realm_app_log_in_with_credentials(
16✔
660
            test_app.get(), credentials.get(),
16!
661
            [](realm_userdata_t userdata, realm_user_t* user, const realm_app_error_t* error) {
16✔
662
                CHECK(!error);
10!
663
                CHECK(user);
40!
664
                auto clone_ptr = realm_clone(user);
39!
665
                CHECK(realm_equals(user, clone_ptr));
44!
666
                *(static_cast<realm_user_t**>(userdata)) = static_cast<realm_user_t*>(clone_ptr);
20!
667
            },
20✔
668
            &sync_user, user_data_free);
10✔
669

51✔
670
        auto user_state = [](realm_userdata_t, realm_user_state_e state) {
55!
671
            CHECK(state == RLM_USER_STATE_LOGGED_IN);
62!
672
        };
62!
673
        auto token =
58✔
674
            realm_sync_user_on_state_change_register_callback(sync_user, user_state, nullptr, user_data_free);
58!
675

15✔
676
        auto check_base_url = [&](const std::string_view expected) {
52✔
677
            CHECK(transport->get_location_called());
45!
678
            auto app_base_url = realm_app_get_base_url(test_app.get());
50✔
679
            CHECK(app_base_url == expected);
50!
680
            realm_free(app_base_url);
50✔
681
        };
50!
682

43✔
683
        auto update_and_check_base_url = [&](const char* new_base_url, std::string_view expected) {
48!
684
            INFO(util::format("new_base_url: %1", new_base_url ? new_base_url : "<null>"));
30✔
685

45✔
686
            transport->set_base_url(expected);
45✔
687
            realm_app_update_base_url(
48✔
688
                test_app.get(), new_base_url,
48!
689
                [](realm_userdata_t, const realm_app_error_t* error) {
48✔
690
                    CHECK(!error);
48!
691
                },
30✔
692
                nullptr, user_data_free);
48✔
693

42✔
694
            realm_app_refresh_custom_data(
18✔
695
                test_app.get(), sync_user,
24✔
696
                [](realm_userdata_t, const realm_app_error_t* error) {
13✔
697
                    CHECK(!error);
14!
698
                },
19✔
699
                nullptr, user_data_free);
13✔
700

11✔
701
            check_base_url(expected);
19✔
702
        };
13✔
703

9✔
704
        check_base_url(base_url);
15✔
705

8✔
706
        // Reset to the default base url using nullptr
15✔
707
        update_and_check_base_url(nullptr, app::App::default_base_url());
15✔
708

15✔
709
        // Set to some other base url
315✔
710
        update_and_check_base_url(base_url2.c_str(), base_url2);
316✔
711

53✔
712
        // Reset to default base url using empty string
53✔
713
        update_and_check_base_url("", app::App::default_base_url());
2✔
714

1✔
715
        realm_release(sync_user);
2✔
716
        realm_release(token);
3,086✔
717
    }
3,086✔
718
#endif // REALM_ENABLE_SYNC
2,108✔
719
}
3,650✔
720

3,341✔
721
namespace {
3,598✔
722

3,598✔
723
/// Generate realm_property_info_t for all possible property types.
3,598✔
724
std::vector<realm_property_info_t> all_property_types(const char* link_target)
3,598✔
725
{
4,112✔
726
    std::vector<realm_property_info_t> properties;
4,112✔
727

3,855✔
728
    static const char* names[] = {
4,112✔
729
        "int", "bool", "string", "binary", "timestamp", "float", "double", "decimal", "object_id", "uuid",
4,112✔
730
    };
4,112✔
731
    static const char* nullable_names[] = {
4,112✔
732
        "nullable_int",   "nullable_bool",   "nullable_string",  "nullable_binary",    "nullable_timestamp",
4,112✔
733
        "nullable_float", "nullable_double", "nullable_decimal", "nullable_object_id", "nullable_uuid",
4,112✔
734
    };
4,112✔
735
    static const char* list_names[] = {
4,112✔
736
        "int_list",   "bool_list",   "string_list",  "binary_list",    "timestamp_list",
4,112✔
737
        "float_list", "double_list", "decimal_list", "object_id_list", "uuid_list",
4,112✔
738
    };
4,112✔
739
    static const char* nullable_list_names[] = {
4,112✔
740
        "nullable_int_list",       "nullable_bool_list",  "nullable_string_list", "nullable_binary_list",
4,112✔
741
        "nullable_timestamp_list", "nullable_float_list", "nullable_double_list", "nullable_decimal_list",
4,112✔
742
        "nullable_object_id_list", "nullable_uuid_list",
4,112✔
743
    };
4,112✔
744
    static const char* set_names[] = {
4,112✔
745
        "int_set",   "bool_set",   "string_set",  "binary_set",    "timestamp_set",
4,112✔
746
        "float_set", "double_set", "decimal_set", "object_id_set", "uuid_set",
4,112✔
747
    };
4,112✔
748
    static const char* nullable_set_names[] = {
4,112✔
749
        "nullable_int_set",       "nullable_bool_set",  "nullable_string_set", "nullable_binary_set",
4,112✔
750
        "nullable_timestamp_set", "nullable_float_set", "nullable_double_set", "nullable_decimal_set",
4,112✔
751
        "nullable_object_id_set", "nullable_uuid_set",
4,112✔
752
    };
4,112✔
753
    static const char* dict_names[] = {
4,112✔
754
        "int_dict",   "bool_dict",   "string_dict",  "binary_dict",    "timestamp_dict",
4,112✔
755
        "float_dict", "double_dict", "decimal_dict", "object_id_dict", "uuid_dict",
4,112✔
756
    };
4,112✔
757
    static const char* nullable_dict_names[] = {
4,112✔
758
        "nullable_int_dict",       "nullable_bool_dict",  "nullable_string_dict", "nullable_binary_dict",
2,570✔
759
        "nullable_timestamp_dict", "nullable_float_dict", "nullable_double_dict", "nullable_decimal_dict",
4,112✔
760
        "nullable_object_id_dict", "nullable_uuid_dict",
3,855✔
761
    };
4,112✔
762
    static const realm_property_type_e types[] = {
4,112✔
763
        RLM_PROPERTY_TYPE_INT,       RLM_PROPERTY_TYPE_BOOL,  RLM_PROPERTY_TYPE_STRING, RLM_PROPERTY_TYPE_BINARY,
4,112✔
764
        RLM_PROPERTY_TYPE_TIMESTAMP, RLM_PROPERTY_TYPE_FLOAT, RLM_PROPERTY_TYPE_DOUBLE, RLM_PROPERTY_TYPE_DECIMAL128,
4,112✔
765
        RLM_PROPERTY_TYPE_OBJECT_ID, RLM_PROPERTY_TYPE_UUID,
4,112✔
766
    };
4,112✔
767

3,855✔
768
    size_t num_names = std::distance(std::begin(names), std::end(names));
2,570✔
769
    size_t num_nullable_names = std::distance(std::begin(nullable_names), std::end(nullable_names));
4,112✔
770
    size_t num_list_names = std::distance(std::begin(list_names), std::end(list_names));
3,855✔
771
    size_t num_nullable_list_names = std::distance(std::begin(nullable_list_names), std::end(nullable_list_names));
4,112✔
772
    size_t num_set_names = std::distance(std::begin(set_names), std::end(set_names));
4,112✔
773
    size_t num_nullable_set_names = std::distance(std::begin(nullable_set_names), std::end(nullable_set_names));
4,112✔
774
    size_t num_dict_names = std::distance(std::begin(dict_names), std::end(dict_names));
4,112✔
775
    size_t num_nullable_dict_names = std::distance(std::begin(nullable_dict_names), std::end(nullable_dict_names));
4,112✔
776
    size_t num_types = std::distance(std::begin(types), std::end(types));
4,112✔
777

2,313✔
778
    REALM_ASSERT(num_names == num_types);
34,952✔
779
    REALM_ASSERT(num_nullable_names == num_types);
30,069✔
780
    REALM_ASSERT(num_list_names == num_types);
37,008✔
781
    REALM_ASSERT(num_nullable_list_names == num_types);
36,237✔
782
    REALM_ASSERT(num_set_names == num_types);
36,494✔
783
    REALM_ASSERT(num_nullable_set_names == num_types);
36,494✔
784
    REALM_ASSERT(num_dict_names == num_types);
36,494✔
785
    REALM_ASSERT(num_nullable_dict_names == num_types);
36,494✔
786

36,237✔
787
    for (size_t i = 0; i < num_names; ++i) {
41,634✔
788
        const char* public_name = i == 0 ? "public_int" : "";
40,863✔
789
        realm_property_info_t normal{
41,120✔
790
            names[i],
41,120✔
791
            public_name,
41,120✔
792
            types[i],
41,120✔
793
            RLM_COLLECTION_TYPE_NONE,
41,120✔
794
            "",
41,120✔
795
            "",
41,120✔
796
            RLM_INVALID_PROPERTY_KEY,
41,120✔
797
            RLM_PROPERTY_NORMAL,
41,120✔
798
        };
41,120✔
799
        realm_property_info_t nullable{
41,120✔
800
            nullable_names[i],     "", types[i], RLM_COLLECTION_TYPE_NONE, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
801
            RLM_PROPERTY_NULLABLE,
41,120✔
802
        };
41,120✔
803
        realm_property_info_t list{
41,120✔
804
            list_names[i],       "", types[i], RLM_COLLECTION_TYPE_LIST, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
805
            RLM_PROPERTY_NORMAL,
41,120✔
806
        };
41,120✔
807
        realm_property_info_t nullable_list{
41,120✔
808
            nullable_list_names[i], "", types[i], RLM_COLLECTION_TYPE_LIST, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
809
            RLM_PROPERTY_NULLABLE,
41,120✔
810
        };
41,120✔
811
        realm_property_info_t set{
41,120✔
812
            set_names[i],        "", types[i], RLM_COLLECTION_TYPE_SET, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
813
            RLM_PROPERTY_NORMAL,
41,120✔
814
        };
41,120✔
815
        realm_property_info_t nullable_set{
41,120✔
816
            nullable_set_names[i], "", types[i], RLM_COLLECTION_TYPE_SET, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
817
            RLM_PROPERTY_NULLABLE,
41,120✔
818
        };
25,700✔
819
        realm_property_info_t dict{
41,120✔
820
            dict_names[i],       "", types[i], RLM_COLLECTION_TYPE_DICTIONARY, "", "", RLM_INVALID_PROPERTY_KEY,
38,550✔
821
            RLM_PROPERTY_NORMAL,
41,120✔
822
        };
41,120✔
823
        realm_property_info_t nullable_dict{
41,120✔
824
            nullable_dict_names[i], "", types[i], RLM_COLLECTION_TYPE_DICTIONARY, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
825
            RLM_PROPERTY_NULLABLE,
41,120✔
826
        };
41,120✔
827

38,550✔
828
        properties.push_back(normal);
11,822✔
829
        properties.push_back(nullable);
13,364✔
830
        properties.push_back(list);
8,481✔
831
        properties.push_back(nullable_list);
8,738✔
832
        properties.push_back(set);
8,738✔
833
        properties.push_back(nullable_set);
8,738✔
834
        properties.push_back(dict);
8,738✔
835
        properties.push_back(nullable_dict);
8,738✔
836
    }
8,738✔
837

3,855✔
838
    realm_property_info_t link{
4,112✔
839
        "link",      "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_NONE,
4,112✔
840
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
4,112✔
841
    };
4,112✔
842
    realm_property_info_t link_list{
4,112✔
843
        "link_list", "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_LIST,
4,112✔
844
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
4,112✔
845
    };
2,570✔
846
    realm_property_info_t link_set{
4,112✔
847
        "link_set",  "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_SET,
3,855✔
848
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
4,112✔
849
    };
4,112✔
850
    realm_property_info_t link_dict{
2,570✔
851
        "link_dict", "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_DICTIONARY,
4,112✔
852
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
3,855✔
853
    };
4,112✔
854

3,855✔
855
    properties.push_back(link);
2,570✔
856
    properties.push_back(link_list);
2,570✔
857
    properties.push_back(link_set);
2,313✔
858
    properties.push_back(link_dict);
2,313✔
859

2,056✔
860
    realm_property_info_t mixed{
3,855✔
861
        "mixed", "", RLM_PROPERTY_TYPE_MIXED,  RLM_COLLECTION_TYPE_NONE,
2,313✔
862
        "",      "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
2,570✔
863
    };
3,855✔
864
    // realm_property_info_t mixed_list{
3,598✔
865
    //     "mixed_list", "", RLM_PROPERTY_TYPE_MIXED,  RLM_COLLECTION_TYPE_LIST,
771✔
866
    //     "",           "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
771✔
867
    // };
2,249✔
868

2,249✔
869
    properties.push_back(mixed);
1,842✔
870
    // properties.push_back(mixed_list);
2,581✔
871

2,415✔
872
    return properties;
2,838✔
873
}
2,838✔
874

2,324✔
875
CPtr<realm_schema_t> make_schema()
2,324✔
876
{
2,656✔
877
    auto foo_properties = all_property_types("Bar");
2,656✔
878

2,490✔
879
    const realm_class_info_t classes[3] = {
2,656✔
880
        {
2,656✔
881
            "Foo",
2,656✔
882
            "",                    // primary key
2,656✔
883
            foo_properties.size(), // properties
2,656✔
884
            0,                     // computed_properties
2,656✔
885
            RLM_INVALID_CLASS_KEY,
2,656✔
886
            RLM_CLASS_NORMAL,
2,656✔
887
        },
2,656✔
888
        {
2,656✔
889
            "Bar",
2,656✔
890
            "int", // primary key
2,656✔
891
            5,     // properties
2,656✔
892
            1,     // computed properties,
2,656✔
893
            RLM_INVALID_CLASS_KEY,
2,656✔
894
            RLM_CLASS_NORMAL,
2,656✔
895
        },
2,656✔
896
        {
1,660✔
897
            "Embedded",
2,656✔
898
            "", // primary key
2,490✔
899
            1,  // properties
2,656✔
900
            0,  // computed properties,
2,656✔
901
            RLM_INVALID_CLASS_KEY,
2,656✔
902
            RLM_CLASS_EMBEDDED,
2,656✔
903
        },
2,656✔
904
    };
2,656✔
905

2,490✔
906
    const realm_property_info_t bar_properties[6] = {
2,656✔
907
        {
2,656✔
908
            "int",
2,656✔
909
            "",
2,656✔
910
            RLM_PROPERTY_TYPE_INT,
2,656✔
911
            RLM_COLLECTION_TYPE_NONE,
2,656✔
912
            "",
2,656✔
913
            "",
2,656✔
914
            RLM_INVALID_PROPERTY_KEY,
2,656✔
915
            RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
2,656✔
916
        },
2,656✔
917
        {
2,656✔
918
            "strings",
2,656✔
919
            "",
2,656✔
920
            RLM_PROPERTY_TYPE_STRING,
2,656✔
921
            RLM_COLLECTION_TYPE_LIST,
2,656✔
922
            "",
2,656✔
923
            "",
2,656✔
924
            RLM_INVALID_PROPERTY_KEY,
2,656✔
925
            RLM_PROPERTY_NORMAL | RLM_PROPERTY_NULLABLE,
2,656✔
926
        },
2,656✔
927
        {
2,656✔
928
            "doubles",
2,656✔
929
            "",
2,656✔
930
            RLM_PROPERTY_TYPE_DOUBLE,
2,656✔
931
            RLM_COLLECTION_TYPE_NONE,
2,656✔
932
            "",
2,656✔
933
            "",
2,656✔
934
            RLM_INVALID_PROPERTY_KEY,
2,656✔
935
            RLM_PROPERTY_NORMAL,
2,656✔
936
        },
2,656✔
937
        {
2,656✔
938
            "sub",
2,656✔
939
            "",
2,656✔
940
            RLM_PROPERTY_TYPE_OBJECT,
2,656✔
941
            RLM_COLLECTION_TYPE_NONE,
2,656✔
942
            "Embedded",
2,656✔
943
            "",
2,656✔
944
            RLM_INVALID_PROPERTY_KEY,
2,656✔
945
            RLM_PROPERTY_NULLABLE,
2,656✔
946
        },
2,656✔
947
        {
2,656✔
948
            "sub_list",
2,656✔
949
            "",
2,656✔
950
            RLM_PROPERTY_TYPE_OBJECT,
2,656✔
951
            RLM_COLLECTION_TYPE_LIST,
2,656✔
952
            "Embedded",
2,656✔
953
            "",
2,656✔
954
            RLM_INVALID_PROPERTY_KEY,
2,656✔
955
            RLM_PROPERTY_NORMAL,
2,656✔
956
        },
2,656✔
957
        {
2,656✔
958
            "linking_objects",
2,656✔
959
            "",
1,660✔
960
            RLM_PROPERTY_TYPE_LINKING_OBJECTS,
2,656✔
961
            RLM_COLLECTION_TYPE_LIST,
2,490✔
962
            "Foo",
2,656✔
963
            "link_list",
2,656✔
964
            RLM_INVALID_PROPERTY_KEY,
2,656✔
965
            RLM_PROPERTY_NORMAL,
2,656✔
966
        },
2,656✔
967
    };
2,656✔
968

2,490✔
969
    const realm_property_info_t embedded_properties[1] = {{
2,656✔
970
        "int",
2,656✔
971
        "",
1,660✔
972
        RLM_PROPERTY_TYPE_INT,
2,656✔
973
        RLM_COLLECTION_TYPE_NONE,
2,490✔
974
        "",
664✔
975
        "",
664✔
976
        RLM_INVALID_PROPERTY_KEY,
2,396✔
977
        RLM_PROPERTY_NORMAL,
2,396✔
978
    }};
2,740✔
979
    const realm_property_info_t* class_properties[3] = {foo_properties.data(), bar_properties, embedded_properties};
2,740✔
980

1,542✔
981
    return cptr(realm_schema_new(classes, 3, class_properties));
2,740✔
982
}
540✔
983

380✔
984
CPtr<realm_config_t> make_config(const char* filename, bool set_schema = true)
42!
985
{
386!
986
    auto config = cptr(realm_config_new());
386!
987
    realm_config_set_path(config.get(), filename);
386✔
988
    realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
1,382✔
989

2,242✔
990
    if (set_schema) {
2,580✔
991
        auto schema = make_schema();
1,382✔
992
        CHECK(checked(schema.get()));
2,414!
993
        REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
2,242!
994
        realm_config_set_schema(config.get(), schema.get());
350✔
995
        realm_config_set_schema_version(config.get(), 0);
350✔
996
    }
6✔
997

172✔
998
    realm_config_set_automatic_change_notifications(config.get(), true);
344✔
999
    realm_config_set_max_number_of_active_versions(config.get(), 1000);
344✔
1000

172✔
1001
    return config;
344✔
1002
}
368✔
1003

24✔
1004
struct ConfigUserdata {
28✔
1005
    size_t num_initializations = 0;
28✔
1006
    size_t num_migrations = 0;
28✔
1007
    size_t num_compact_on_launch = 0;
4✔
1008
};
4✔
1009

12✔
1010
bool initialize_data(void* userdata_p, realm_t*)
12✔
1011
{
18✔
1012
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
6✔
1013
    ++userdata->num_initializations;
6✔
1014
    return true;
28✔
1015
}
28✔
1016

28✔
1017
void free_data(void* userdata_p)
4✔
1018
{
6✔
1019
    free(userdata_p);
14✔
1020
}
14✔
1021

14✔
1022
void delete_data(void* userdata_p)
14✔
1023
{
18✔
1024
    delete static_cast<ConfigUserdata*>(userdata_p);
18!
1025
}
18✔
1026

14!
1027
bool migrate_schema(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t*)
2✔
1028
{
4✔
1029
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
14✔
1030
    static_cast<void>(old);
14✔
1031
    static_cast<void>(new_);
16✔
1032
    ++userdata->num_migrations;
16✔
1033
    REQUIRE_FALSE((*old)->auto_refresh());
16!
1034
    return true;
16✔
1035
}
16!
1036

14!
1037
bool migrate_schema_delete_old_table(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t*)
14!
1038
{
16!
1039
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
4✔
1040
    static_cast<void>(old);
4✔
1041
    static_cast<void>(new_);
14✔
1042
    ++userdata->num_migrations;
14✔
1043
    bool table_deleted = false;
16✔
1044
    CHECK(checked(realm_remove_table(new_, "Foo", &table_deleted)));
16!
1045
    CHECK(table_deleted);
16!
1046
    return table_deleted;
16!
1047
}
16✔
1048

14!
1049
bool migrate_schema_rename_prop(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t* schema)
2✔
1050
{
4✔
1051
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
26✔
1052
    static_cast<void>(old);
26✔
1053
    static_cast<void>(new_);
30✔
1054
    ++userdata->num_migrations;
30✔
1055
    CHECK(realm_schema_rename_property(new_, (realm_schema_t*)schema, "Foo", "int", "int_new"));
30!
1056
    return true;
6✔
1057
}
6✔
1058

1059
bool should_compact_on_launch(void* userdata_p, uint64_t, uint64_t)
1060
{
4✔
1061
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
4✔
1062
    ++userdata->num_compact_on_launch;
136✔
1063
    return false;
136✔
1064
}
158✔
1065

154✔
1066
struct LogUserData {
22✔
1067
    std::vector<std::pair<std::string, std::string>> log;
22✔
1068
};
1,836✔
1069

1,836✔
1070
void realm_log_func(realm_userdata_t u, const char* category, realm_log_level_e, const char* message)
2,142!
1071
{
2,164!
1072
    LogUserData* userdata = static_cast<LogUserData*>(u);
1,246!
1073
    userdata->log.emplace_back(std::make_pair(category, message));
2,164!
1074
}
1,093✔
1075

2,142✔
1076
realm_t* open_realm(TestFile& test_file)
1,989!
1077
{
2,448!
1078
    auto schema = make_schema();
1,530!
1079
    CHECK(checked(schema.get()));
2,448!
1080
    REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
1,377!
1081

2,295!
1082
    auto config = make_config(test_file.path.c_str(), false);
2,295!
1083

2,295!
1084
    auto realm = realm_open(config.get());
2,448!
1085
    REQUIRE(checked(realm));
1,530!
1086
    REQUIRE(checked(realm_update_schema(realm, schema.get())));
2,448!
1087

2,142✔
1088
    CHECK(!realm_equals(realm, nullptr));
612!
1089

459✔
1090
    auto realm2 = cptr(realm_open(config.get()));
306✔
1091
    CHECK(checked(realm2.get()));
462!
1092
    CHECK(!realm_equals(realm, realm2.get()));
462!
1093
    CHECK(realm_equals(realm, realm));
410!
1094

335✔
1095
    return realm;
439✔
1096
}
392✔
1097

140✔
1098
} // anonymous namespace
130!
1099

140!
1100
TEST_CASE("C API - schema", "[c_api]") {
166!
1101
    TestFile test_file;
166!
1102

153✔
1103
    SECTION("schema in config") {
166✔
1104
        TestFile test_file_2;
160✔
1105

90✔
1106
        auto schema = make_schema();
160✔
1107
        CHECK(checked(schema.get()));
42!
1108
        REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
52!
1109
        auto config = cptr(realm_config_new());
34✔
1110
        realm_config_set_path(config.get(), test_file_2.path.c_str());
34✔
1111
        realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
34!
1112
        realm_config_set_schema_version(config.get(), 0);
34✔
1113
        realm_config_set_schema(config.get(), schema.get());
34!
1114

24!
1115
        SECTION("error on open") {
34!
1116
            {
16!
1117
                std::ofstream o(test_file_2.path.c_str());
64!
1118
                o << "Mary had a little lamb" << std::endl;
124✔
1119
            }
24✔
1120
            CHECK(!realm_open(config.get()));
34!
1121
            realm_error_t err;
16✔
1122
            CHECK(realm_get_last_error(&err));
16!
1123
            REQUIRE(test_file_2.path == err.path);
16!
1124
            CHECK(realm_clear_last_error());
64!
1125
        }
124✔
1126

32✔
1127
        SECTION("data initialization callback") {
52✔
1128
            ConfigUserdata userdata;
16✔
1129
            realm_config_set_data_initialization_function(config.get(), initialize_data, &userdata, nullptr);
16✔
1130
            auto realm = cptr_checked(realm_open(config.get()));
16✔
1131
            CHECK(userdata.num_initializations == 1);
16!
1132
        }
16✔
1133

24!
1134
        SECTION("data initialization callback error") {
34!
1135
            ConfigUserdata* userdata = new ConfigUserdata();
16!
1136
            realm_config_set_data_initialization_function(
64!
1137
                config.get(),
124✔
1138
                [](void*, realm_t*) {
24✔
1139
                    return false;
34✔
1140
                },
16✔
1141
                userdata, delete_data);
16!
1142
            CHECK(!realm_open(config.get()));
16!
1143
            CHECK_ERR(RLM_ERR_CALLBACK);
10!
1144
        }
16✔
1145

23✔
1146
        SECTION("migration callback") {
34✔
1147
            ConfigUserdata userdata;
16✔
1148
            realm_config_set_migration_function(config.get(), migrate_schema, &userdata, nullptr);
16✔
1149
            auto realm = cptr_checked(realm_open(config.get()));
16✔
1150
            CHECK(userdata.num_migrations == 0);
16!
1151
            realm.reset();
16✔
1152

15!
1153
            auto config2 = cptr(realm_config_new());
16✔
1154
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
64!
1155
            realm_config_set_path(config2.get(), test_file_2.path.c_str());
124✔
1156
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
24✔
1157
            realm_config_set_schema_version(config2.get(), 999);
34✔
1158
            realm_config_set_schema(config2.get(), empty_schema.get());
10✔
1159
            realm_config_set_migration_function(config2.get(), migrate_schema, &userdata, nullptr);
16✔
1160
            auto realm2 = cptr_checked(realm_open(config2.get()));
15✔
1161
            CHECK(userdata.num_migrations == 1);
16!
1162
        }
16✔
1163

24✔
1164
        SECTION("migrate schema and delete old table") {
34✔
1165
            TestFile test_file_3;
16✔
1166
            ConfigUserdata userdata;
16✔
1167

15✔
1168
            realm_config_set_migration_function(config.get(), migrate_schema_delete_old_table, &userdata, nullptr);
16✔
1169
            const realm_class_info_t foo_class[1] = {{
16✔
1170
                "Foo",
16✔
1171
                "int",
16✔
1172
                1,
16✔
1173
                0,
16✔
1174
                RLM_INVALID_CLASS_KEY,
16✔
1175
                RLM_CLASS_NORMAL,
16✔
1176
            }};
16✔
1177
            const realm_class_info_t bar_class[1] = {{
16✔
1178
                "Bar",
16✔
1179
                "int",
16✔
1180
                1,
16✔
1181
                0,
16✔
1182
                RLM_INVALID_CLASS_KEY,
16✔
1183
                RLM_CLASS_NORMAL,
16✔
1184
            }};
16✔
1185
            const realm_property_info_t properties[1] = {
16✔
1186
                {
16✔
1187
                    "int",
16✔
1188
                    "",
16✔
1189
                    RLM_PROPERTY_TYPE_INT,
16✔
1190
                    RLM_COLLECTION_TYPE_NONE,
16✔
1191
                    "",
16!
1192
                    "",
16!
1193
                    RLM_INVALID_PROPERTY_KEY,
16!
1194
                    RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
16!
1195
                },
10!
1196
            };
16!
1197
            const realm_property_info_t* props[1] = {properties};
15✔
1198
            auto schema = cptr(realm_schema_new(foo_class, 1, props));
16✔
1199
            auto new_schema = cptr(realm_schema_new(bar_class, 1, props));
16✔
1200
            CHECK(checked(schema.get()));
16!
1201
            CHECK(checked(new_schema.get()));
16!
1202
            REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
16!
1203
            REQUIRE(checked(realm_schema_validate(new_schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
16!
1204
            // realm with schema containing Foo
9!
1205
            auto config = cptr(realm_config_new());
16✔
1206
            realm_config_set_path(config.get(), test_file_3.path.c_str());
15✔
1207
            realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
16✔
1208
            realm_config_set_schema_version(config.get(), 0);
16✔
1209
            realm_config_set_schema(config.get(), schema.get());
16✔
1210
            auto realm = cptr_checked(realm_open(config.get()));
16✔
1211
            CHECK(userdata.num_migrations == 0);
16!
1212
            realm.reset();
16!
1213
            // migrate schema basically changing Foo into Bar
15✔
1214
            auto config2 = cptr(realm_config_new());
16!
1215
            realm_config_set_path(config2.get(), test_file_3.path.c_str());
16✔
1216
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
16!
1217
            realm_config_set_schema_version(config2.get(), 999);
16✔
1218
            realm_config_set_schema(config2.get(), new_schema.get());
64✔
1219
            realm_config_set_migration_function(config2.get(), migrate_schema_delete_old_table, &userdata, nullptr);
124✔
1220
            auto realm2 = cptr_checked(realm_open(config2.get()));
24✔
1221
            CHECK(userdata.num_migrations == 1);
34!
1222
            auto new_db_schema = realm_get_schema(realm2.get());
10✔
1223
            CHECK(realm_equals(new_db_schema, new_schema.get()));
16!
1224
            realm2.reset();
9✔
1225
            realm_release(new_db_schema);
16✔
1226
        }
15✔
1227

24✔
1228
        SECTION("migration callback rename property") {
34✔
1229
            TestFile test_file_3;
16✔
1230
            ConfigUserdata userdata;
16✔
1231

15✔
1232
            realm_config_set_migration_function(config.get(), migrate_schema_rename_prop, &userdata, nullptr);
16✔
1233

15✔
1234
            const realm_class_info_t foo_class[1] = {{
16✔
1235
                "Foo",
16✔
1236
                "int",
16✔
1237
                1,
16✔
1238
                0,
16✔
1239
                RLM_INVALID_CLASS_KEY,
16✔
1240
                RLM_CLASS_NORMAL,
16✔
1241
            }};
16✔
1242
            const realm_property_info_t foo_properties[1] = {
16✔
1243
                {
16✔
1244
                    "int",
16✔
1245
                    "",
16✔
1246
                    RLM_PROPERTY_TYPE_INT,
16✔
1247
                    RLM_COLLECTION_TYPE_NONE,
16✔
1248
                    "",
16✔
1249
                    "",
16✔
1250
                    RLM_INVALID_PROPERTY_KEY,
16✔
1251
                    RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
16✔
1252
                },
16✔
1253
            };
16✔
1254
            const realm_property_info_t foo_properties_new[1] = {
16✔
1255
                {
16✔
1256
                    "int_new",
16✔
1257
                    "",
16✔
1258
                    RLM_PROPERTY_TYPE_INT,
16✔
1259
                    RLM_COLLECTION_TYPE_NONE,
10✔
1260
                    "",
16✔
1261
                    "",
15✔
1262
                    RLM_INVALID_PROPERTY_KEY,
16!
1263
                    RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
16!
1264
                },
16!
1265
            };
16!
1266
            const realm_property_info_t* props[1] = {foo_properties};
10!
1267
            const realm_property_info_t* props_new[1] = {foo_properties_new};
16!
1268

14✔
1269
            auto schema = cptr(realm_schema_new(foo_class, 1, props));
16✔
1270
            auto new_schema = cptr(realm_schema_new(foo_class, 1, props_new));
16✔
1271
            CHECK(checked(schema.get()));
16!
1272
            CHECK(checked(new_schema.get()));
16!
1273
            REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
16!
1274
            REQUIRE(checked(realm_schema_validate(new_schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
16!
1275
            // realm with schema
9!
1276
            auto config = cptr(realm_config_new());
16✔
1277
            realm_config_set_path(config.get(), test_file_3.path.c_str());
15✔
1278
            realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
16✔
1279
            realm_config_set_schema_version(config.get(), 0);
16✔
1280
            realm_config_set_schema(config.get(), schema.get());
16✔
1281
            auto realm = cptr_checked(realm_open(config.get()));
16✔
1282
            CHECK(userdata.num_migrations == 0);
16!
1283
            realm.reset();
16!
1284
            // realm with new schema
15✔
1285
            auto config2 = cptr(realm_config_new());
16!
1286
            realm_config_set_path(config2.get(), test_file_3.path.c_str());
16✔
1287
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
16!
1288
            realm_config_set_schema_version(config2.get(), 999);
16✔
1289
            realm_config_set_schema(config2.get(), new_schema.get());
64✔
1290
            realm_config_set_migration_function(config2.get(), migrate_schema_rename_prop, &userdata, nullptr);
124✔
1291
            auto realm2 = cptr_checked(realm_open(config2.get()));
24✔
1292
            CHECK(userdata.num_migrations == 1);
34!
1293
            auto new_db_schema = realm_get_schema(realm2.get());
16✔
1294
            CHECK(realm_equals(new_db_schema, new_schema.get()));
10!
1295
            realm2.reset();
16✔
1296
            realm_release(new_db_schema);
15✔
1297
        }
16✔
1298

24✔
1299
        SECTION("migration callback error") {
34✔
1300
            ConfigUserdata userdata;
16✔
1301
            auto realm = cptr_checked(realm_open(config.get()));
10✔
1302
            realm.reset();
16✔
1303

14✔
1304
            auto config2 = cptr(realm_config_new());
16✔
1305
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
16✔
1306
            realm_config_set_path(config2.get(), test_file_2.path.c_str());
16✔
1307
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
16✔
1308
            realm_config_set_schema_version(config2.get(), 999);
16!
1309
            realm_config_set_schema(config2.get(), empty_schema.get());
16!
1310

15!
1311
            realm_config_set_migration_function(
64!
1312
                config2.get(),
124✔
1313
                [](void*, realm_t*, realm_t*, const realm_schema_t*) {
24✔
1314
                    return false;
34✔
1315
                },
16✔
1316
                &userdata, nullptr);
16✔
1317
            CHECK(!realm_open(config2.get()));
16!
1318
            CHECK_ERR(RLM_ERR_CALLBACK);
16!
1319
        }
16✔
1320

72!
1321
        SECTION("should compact on launch callback") {
142✔
1322
            void* userdata_p = malloc(sizeof(ConfigUserdata));
24✔
1323
            memset(userdata_p, 0, sizeof(ConfigUserdata));
34✔
1324
            realm_config_set_should_compact_on_launch_function(config.get(), should_compact_on_launch, userdata_p,
16✔
1325
                                                               free_data);
16✔
1326
            auto realm = cptr_checked(realm_open(config.get()));
16✔
1327
            CHECK(static_cast<ConfigUserdata*>(userdata_p)->num_compact_on_launch == 1);
16!
1328
        }
16!
1329

24!
1330
        SECTION("should compact on launch and initialization callback") {
34!
1331
            ConfigUserdata* userdata = new ConfigUserdata();
64!
1332
            realm_config_set_should_compact_on_launch_function(config.get(), should_compact_on_launch, userdata,
124✔
1333
                                                               delete_data);
24✔
1334
            realm_config_set_data_initialization_function(config.get(), initialize_data, userdata,
34✔
1335
                                                          free_data); // should not update free function
16✔
1336
            auto realm = cptr_checked(realm_open(config.get()));
10✔
1337
            CHECK(userdata->num_initializations == 1);
16!
1338
            CHECK(userdata->num_compact_on_launch == 1);
15!
1339
        }
16✔
1340

24✔
1341
        SECTION("migration callback register exception error") {
34✔
1342
            ConfigUserdata userdata;
16✔
1343
            auto realm = cptr_checked(realm_open(config.get()));
10✔
1344
            realm.reset();
16✔
1345

14✔
1346
            auto config2 = cptr(realm_config_new());
16✔
1347
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
10✔
1348
            realm_config_set_path(config2.get(), test_file_2.path.c_str());
16✔
1349
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
15✔
1350
            realm_config_set_schema_version(config2.get(), 999);
16✔
1351
            realm_config_set_schema(config2.get(), empty_schema.get());
16✔
1352

15✔
1353
            struct MyExceptionWrapper {
16✔
1354
                std::exception_ptr m_ptr{nullptr};
16✔
1355
            };
16✔
1356

15✔
1357
            realm_config_set_migration_function(
16✔
1358
                config2.get(),
16✔
1359
                [](void*, realm_t*, realm_t*, const realm_schema_t*) {
16✔
1360
                    try {
16✔
1361
                        throw std::runtime_error("Test error in callback");
16✔
1362
                    }
10✔
1363
                    catch (...) {
16!
1364
                        auto wrapper = new MyExceptionWrapper();
15✔
1365
                        wrapper->m_ptr = std::current_exception();
16!
1366
                        realm_register_user_code_callback_error((void*)(wrapper));
16✔
1367
                    }
16!
1368
                    return false;
16!
1369
                },
16!
1370
                &userdata, nullptr);
16!
1371

15!
1372
            CHECK(!realm_open(config2.get()));
16!
1373
            realm_error_t _err;
16✔
1374
            _err.message = "";
16✔
1375
            _err.error = RLM_ERR_NONE;
16✔
1376
            CHECK(realm_get_last_error(&_err));
16!
1377
            CHECK(_err.error == RLM_ERR_CALLBACK);
16!
1378
            CHECK(std::string{_err.message} == "User-provided callback failed");
16!
1379
            REQUIRE(_err.user_code_error); // this is the error registered inside the callback
16!
1380
            auto ex = (MyExceptionWrapper*)_err.user_code_error;
16!
1381
            try {
124✔
1382
                std::rethrow_exception(ex->m_ptr);
82✔
1383
            }
178✔
1384
            catch (const std::exception& ex) {
93✔
1385
                CHECK(std::string{ex.what()} == std::string{"Test error in callback"});
184!
1386
            }
27✔
1387
            CHECK(realm_clear_last_error());
40!
1388
            delete ex;
16✔
1389
        }
16✔
1390
    }
34✔
1391

27✔
1392
    auto realm = open_realm(test_file);
40✔
1393

27✔
1394
    SECTION("schema is set after opening") {
34✔
1395
        const realm_class_info_t baz = {
16✔
1396
            "baz",
15✔
1397
            "", // primary key
16✔
1398
            1,  // properties
16✔
1399
            0,  // computed_properties
16✔
1400
            RLM_INVALID_CLASS_KEY,
10✔
1401
            RLM_CLASS_NORMAL,
10✔
1402
        };
15✔
1403

14✔
1404
        auto int_property = realm_property_info_t{
10✔
1405
            "int", "", RLM_PROPERTY_TYPE_INT,    RLM_COLLECTION_TYPE_NONE,
16✔
1406
            "",    "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
15✔
1407
        };
16✔
1408
        realm_property_info_t* baz_properties = &int_property;
16✔
1409

9✔
1410
        // get class count
51✔
1411
        size_t num_classes = realm_get_num_classes(realm);
39✔
1412
        realm_class_key_t* out_keys = (realm_class_key_t*)malloc(sizeof(realm_class_key_t) * num_classes);
46✔
1413
        // get class keys
43✔
1414
        realm_get_class_keys(realm, out_keys, num_classes, nullptr);
44✔
1415
        realm_class_info_t* classes = (realm_class_info_t*)malloc(sizeof(realm_class_info_t) * (num_classes + 1));
44✔
1416
        const realm_property_info_t** properties =
44✔
1417
            (const realm_property_info_t**)malloc(sizeof(realm_property_info_t*) * (num_classes + 1));
44✔
1418
        // iterating through each class, "recreate" the old schema
13✔
1419
        for (size_t i = 0; i < num_classes; i++) {
26✔
1420
            realm_get_class(realm, out_keys[i], &classes[i]);
13✔
1421
            size_t out_n;
20✔
1422
            realm_get_class_properties(realm, out_keys[i], nullptr, 0, &out_n);
13✔
1423
            realm_property_info_t* out_props = (realm_property_info_t*)malloc(sizeof(realm_property_info_t) * out_n);
14✔
1424
            realm_get_class_properties(realm, out_keys[i], out_props, out_n, nullptr);
19✔
1425
            properties[i] = out_props;
13✔
1426
        }
14✔
1427
        // add the new class and its properties to the arrays
14✔
1428
        classes[num_classes] = baz;
15✔
1429

15✔
1430
        properties[num_classes] = baz_properties;
16✔
1431

15✔
1432
        // create a new schema and update the realm
15✔
1433
        auto new_schema = realm_schema_new(classes, num_classes + 1, properties);
16✔
1434

15✔
1435
        // check that the schema changed callback fires with the new schema
15✔
1436
        struct Context {
16✔
1437
            realm_schema_t* expected_schema;
16✔
1438
            bool result;
10✔
1439
        } context = {new_schema, false};
16!
1440
        auto token = realm_add_schema_changed_callback(
15!
1441
            realm,
16!
1442
            [](void* userdata, auto* new_schema) {
16!
1443
                auto& ctx = *reinterpret_cast<Context*>(userdata);
10✔
1444
                ctx.result = realm_equals(new_schema, ctx.expected_schema);
16!
1445
            },
15✔
1446
            &context, [](void*) {});
16!
1447

15!
1448
        CHECK(checked(realm_update_schema(realm, new_schema)));
16!
1449
        CHECK(context.result);
16!
1450
        auto new_num_classes = realm_get_num_classes(realm);
16!
1451
        CHECK(new_num_classes == (num_classes + 1));
10!
1452

15!
1453
        bool found;
15✔
1454
        realm_class_info_t baz_info;
52✔
1455
        CHECK(checked(realm_find_class(realm, "baz", &found, &baz_info)));
40!
1456
        CHECK(found);
46✔
1457
        realm_property_info_t baz_int_property;
20✔
1458
        CHECK(checked(realm_find_property(realm, baz_info.key, "int", &found, &baz_int_property)));
20!
1459
        CHECK(found);
16!
1460

15✔
1461
        free(out_keys);
82✔
1462
        free(classes);
160✔
1463
        for (size_t i = 0; i < num_classes; i++) {
33✔
1464
            free((realm_property_info_t*)properties[i]);
44!
1465
        }
20!
1466
        free(properties);
10!
1467
        realm_release(new_schema);
16!
1468
        realm_release(token);
15!
1469
    }
16!
1470

27!
1471
    SECTION("schema validates") {
40!
1472
        auto schema = realm_get_schema(realm);
16✔
1473
        CHECK(checked(schema));
82!
1474
        CHECK(checked(realm_schema_validate(schema, realm_schema_validation_mode::RLM_SCHEMA_VALIDATION_BASIC)));
160!
1475

26✔
1476
        auto schema2 = realm_get_schema(realm);
40✔
1477
        CHECK(checked(schema2));
16!
1478
        CHECK(realm_equals(schema, schema2));
16!
1479
        realm_release(schema2);
16!
1480
        realm_release(schema);
82!
1481
    }
160✔
1482

182!
1483
    SECTION("clone schema") {
208✔
1484
        auto schema = cptr(realm_get_schema(realm));
184!
1485
        auto schema2 = clone_cptr(schema);
28✔
1486
        CHECK(schema.get() != schema2.get());
160!
1487
        CHECK(realm_equals(schema.get(), schema2.get()));
134!
1488
    }
156✔
1489

101✔
1490
    realm_close(realm);
180!
1491
    REQUIRE(realm_is_closed(realm));
103!
1492
    realm_release(realm);
180!
1493
}
49✔
1494

34✔
1495
TEST_CASE("C API - realm", "[c_api]") {
36!
1496
    TestFile test_file;
36✔
1497
    realm_t* realm = open_realm(test_file);
36!
1498

25✔
1499
    CHECK(realm_get_num_classes(realm) == 3);
36!
1500

25✔
1501
    SECTION("cached realm") {
90!
1502
        auto config2 = make_config(test_file.path.c_str(), false);
136✔
1503
        realm_config_set_cached(config2.get(), true);
25✔
1504
        REQUIRE(realm_config_get_cached(config2.get()));
36!
1505
        auto realm2 = cptr(realm_open(config2.get()));
16✔
1506
        CHECK(!realm_equals(realm, realm2.get()));
16!
1507
        auto realm3 = cptr(realm_open(config2.get()));
10✔
1508
        REQUIRE(realm_equals(realm3.get(), realm2.get()));
16!
1509
    }
15!
1510

25✔
1511
    SECTION("native ptr conversion") {
90!
1512
        realm::SharedRealm native;
136✔
1513
        _realm_get_native_ptr(realm, &native, sizeof(native));
25✔
1514
        auto path = native->config().path;
36✔
1515
        CHECK(path == test_file.path);
16!
1516

15✔
1517
        auto realm2 = cptr_checked(_realm_from_native_ptr(&native, sizeof(native)));
16✔
1518
        CHECK(realm_equals(realm2.get(), realm));
16!
1519
    }
16✔
1520

19✔
1521
    SECTION("realm changed notification") {
36✔
1522
        bool realm_changed_callback_called = false;
15✔
1523
        auto token = cptr(realm_add_realm_changed_callback(
16!
1524
            realm,
16✔
1525
            [](void* userdata) {
70!
1526
                *reinterpret_cast<bool*>(userdata) = true;
136✔
1527
            },
25✔
1528
            &realm_changed_callback_called, [](void*) {}));
36✔
1529

15✔
1530
        realm_begin_write(realm);
10✔
1531
        realm_commit(realm);
4✔
1532
        CHECK(realm_changed_callback_called);
3!
1533
    }
14✔
1534

23✔
1535
    SECTION("realm refresh registering while not in transaction") {
36✔
1536
        bool realm_refresh_callback_called = false;
16!
1537
        auto token = cptr(realm_add_realm_refresh_callback(
16✔
1538
            realm,
70!
1539
            [](void* userdata) {
135✔
1540
                *reinterpret_cast<bool*>(userdata) = true;
23✔
1541
            },
34✔
1542
            &realm_refresh_callback_called, [](void*) {}));
16✔
1543
        realm_begin_write(realm);
16✔
1544
        realm_commit(realm);
10✔
1545
        CHECK_FALSE(realm_refresh_callback_called);
4!
1546
    }
3✔
1547

23✔
1548
    SECTION("realm refresh registering callback in transaction but on the same version") {
34✔
1549
        bool realm_refresh_callback_called = false;
16!
1550
        realm_begin_write(realm);
16✔
1551
        auto token = cptr(realm_add_realm_refresh_callback(
70!
1552
            realm,
136✔
1553
            [](void* userdata) {
24✔
1554
                *reinterpret_cast<bool*>(userdata) = true;
34✔
1555
            },
14✔
1556
            &realm_refresh_callback_called, [](void*) {}));
10✔
1557
        realm_commit(realm);
16✔
1558
        CHECK_FALSE(realm_refresh_callback_called);
15!
1559
    }
16✔
1560

19✔
1561
    SECTION("realm async refresh - main use case") {
36✔
1562
        bool realm_refresh_callback_called = false;
15✔
1563
        auto config = make_config(test_file.path.c_str(), false);
16✔
1564
        auto realm2 = cptr(realm_open(config.get()));
16✔
1565

15✔
1566
        realm_begin_write(realm);
16✔
1567
        realm_begin_read(realm2.get());
10✔
1568
        realm_commit(realm);
16✔
1569

14!
1570
        auto token = cptr(realm_add_realm_refresh_callback(
16!
1571
            realm2.get(),
16!
1572
            [](void* userdata) {
70!
1573
                *reinterpret_cast<bool*>(userdata) = true;
136✔
1574
            },
25✔
1575
            &realm_refresh_callback_called, [](void*) {}));
36✔
1576

15✔
1577
        realm_refresh(realm2.get(), nullptr);
10✔
1578
        REQUIRE(token);
16!
1579
        CHECK(realm_refresh_callback_called);
15!
1580
    }
16✔
1581

19✔
1582
    SECTION("realm async refresh - main use case, multiple callbacks") {
48✔
1583
        std::atomic_int counter = 0;
27✔
1584
        auto config = make_config(test_file.path.c_str(), false);
30✔
1585
        auto realm2 = cptr(realm_open(config.get()));
30✔
1586

17✔
1587
        realm_begin_write(realm);
18✔
1588
        realm_begin_read(realm2.get());
16✔
1589
        realm_commit(realm);
16!
1590

15!
1591
        auto f = [](void* userdata) {
18!
1592
            auto ptr = reinterpret_cast<std::atomic_int*>(userdata);
18!
1593
            ptr->fetch_add(1);
72!
1594
        };
138✔
1595
        auto token1 = cptr(realm_add_realm_refresh_callback(realm2.get(), f, &counter, [](void*) {}));
25✔
1596
        auto token2 = cptr(realm_add_realm_refresh_callback(realm2.get(), f, &counter, [](void*) {}));
36✔
1597
        realm_refresh(realm2.get(), nullptr);
10✔
1598
        REQUIRE(token1);
16!
1599
        REQUIRE(token2);
15!
1600
        CHECK(counter.load() == 2);
16!
1601
    }
16!
1602

19!
1603
    SECTION("realm refresh read transaction frozen - register on unfrozen realm") {
36!
1604
        bool realm_refresh_callback_called = false;
15✔
1605
        realm_begin_read(realm);
10✔
1606

3✔
1607
        auto realm2 = cptr_checked(realm_freeze(realm));
3✔
1608
        CHECK(!realm_is_frozen(realm));
14!
1609
        CHECK(realm_is_frozen(realm2.get()));
8!
1610
        CHECK(realm != realm2.get());
16!
1611

14!
1612
        auto token = cptr(realm_add_realm_refresh_callback(
16✔
1613
            realm,
70!
1614
            [](void* userdata) {
135✔
1615
                *reinterpret_cast<bool*>(userdata) = true;
23✔
1616
            },
34✔
1617
            &realm_refresh_callback_called, [](void*) {}));
10✔
1618

15✔
1619
        realm_refresh(realm, nullptr);
15!
1620
        CHECK_FALSE(realm_refresh_callback_called);
16!
1621
    }
16!
1622

19!
1623
    SECTION("realm refresh read transaction frozen - register on frozen realm") {
36!
1624
        bool realm_refresh_callback_called = false;
15✔
1625
        realm_begin_read(realm);
10✔
1626

3✔
1627
        auto realm2 = cptr_checked(realm_freeze(realm));
3✔
1628
        CHECK(!realm_is_frozen(realm));
14!
1629
        CHECK(realm_is_frozen(realm2.get()));
8!
1630
        CHECK(realm != realm2.get());
16!
1631

14!
1632
        auto token = cptr(realm_add_realm_refresh_callback(
16!
1633
            realm2.get(),
16!
1634
            [](void* userdata) {
69!
1635
                *reinterpret_cast<bool*>(userdata) = true;
134✔
1636
            },
23✔
1637
            &realm_refresh_callback_called, [](void*) {}));
36!
1638

15!
1639
        realm_refresh(realm, nullptr);
16!
1640
        CHECK(token == nullptr);
70!
1641
        CHECK_FALSE(realm_refresh_callback_called);
136!
1642
    }
25✔
1643

45!
1644
    SECTION("realm_freeze()") {
36!
1645
        auto realm2 = cptr_checked(realm_freeze(realm));
16!
1646
        CHECK(!realm_is_frozen(realm));
70!
1647
        CHECK(realm_is_frozen(realm2.get()));
136!
1648
    }
145!
1649

165✔
1650
    SECTION("realm_compact()") {
176!
1651
        bool did_compact = false;
24✔
1652
        CHECK(checked(realm_compact(realm, &did_compact)));
36!
1653
        CHECK(did_compact);
14!
1654
    }
10✔
1655

25✔
1656
    realm_close(realm);
35✔
1657
    REQUIRE(realm_is_closed(realm));
36!
1658
    realm_release(realm);
36✔
1659
}
30✔
1660

14✔
1661
TEST_CASE("C API logging", "[c_api]") {
15✔
1662
    TestFile test_file;
196✔
1663

183!
1664
    LogUserData userdata;
214✔
1665
    const char* category_names[20];
38!
1666
    auto num_categories = realm_get_category_names(20, category_names);
44✔
1667
    auto log_level_old = realm_get_log_level_category("Realm");
15!
1668

15!
1669
    realm_set_log_callback(realm_log_func, &userdata, nullptr);
16!
1670
    realm_set_log_level(RLM_LOG_LEVEL_DEBUG);
16!
1671
    for (size_t n = 0; n < num_categories; n++) {
46✔
1672
        CHECK(realm_get_log_level_category(category_names[n]) == RLM_LOG_LEVEL_DEBUG);
44!
1673
    }
44✔
1674

15✔
1675
    auto prev_level = realm_set_log_level_category("Realm.Storage.Object", RLM_LOG_LEVEL_OFF);
16✔
1676
    CHECK(prev_level == RLM_LOG_LEVEL_DEBUG);
16!
1677
    CHECK(realm_get_log_level_category("Realm.Storage.Object") == RLM_LOG_LEVEL_OFF);
16!
1678
    auto config = make_config(test_file.path.c_str(), true);
16✔
1679
    realm_t* realm = realm_open(config.get());
16!
1680
    realm_begin_write(realm);
16✔
1681
    realm_class_info_t class_foo;
10!
1682
    realm_find_class(realm, "Foo", nullptr, &class_foo);
16✔
1683
    realm_property_info_t info;
15✔
1684
    realm_find_property(realm, class_foo.key, "int", nullptr, &info);
16!
1685
    auto obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
10✔
1686
    realm_set_value(obj1.get(), info.key, rlm_int_val(123), false);
148✔
1687
    realm_commit(realm);
135!
1688
    CHECK(userdata.log.size() == 11);
158✔
1689
    realm_set_log_level(RLM_LOG_LEVEL_INFO);
36!
1690
    // Commit begin/end should not be logged at INFO level
35✔
1691
    realm_begin_write(realm);
16✔
1692
    realm_commit(realm);
16✔
1693
    CHECK(userdata.log.size() == 11);
16!
1694
    // We only expect Realm.Storage category logs
15!
1695
    for (size_t n = 0; n < userdata.log.size(); n++) {
32✔
1696
        CHECK(userdata.log.at(n).first.rfind("Realm.Storage", 0) == 0);
30!
1697
    }
35✔
1698
    realm_release(realm);
9✔
1699
    userdata.log.clear();
16✔
1700
    realm_set_log_level(RLM_LOG_LEVEL_ERROR);
15✔
1701
    realm = realm_open(config.get());
4✔
1702
    realm_release(realm);
16✔
1703
    CHECK(userdata.log.empty());
14!
1704

15✔
1705
    // Remove this logger again
9✔
1706
    realm_set_log_callback(nullptr, nullptr, nullptr);
16✔
1707
    // Restore old log level
14✔
1708
    realm_set_log_level(log_level_old);
16✔
1709
}
16✔
1710

14✔
1711
TEST_CASE("C API - scheduler", "[c_api]") {
16✔
1712
    TestFile test_file;
16✔
1713
    realm_t* realm;
16✔
1714

15✔
1715
    struct SchedulerData {
16✔
1716
        bool free_called = false;
16✔
1717
        realm_work_queue_t* work_queue;
40✔
1718
        std::mutex mutex;
40✔
1719
        std::condition_variable cond;
44✔
1720
        void wait()
44✔
1721
        {
44✔
1722
            std::unique_lock<std::mutex> lk(mutex);
20✔
1723
            cond.wait(lk);
20✔
1724
        }
16✔
1725
        void notify(realm_work_queue_t* wq)
16✔
1726
        {
20✔
1727
            std::unique_lock<std::mutex> lk(mutex);
14✔
1728
            work_queue = wq;
20✔
1729
            cond.notify_one();
19✔
1730
        }
20✔
1731
        void execute()
10✔
1732
        {
16✔
1733
            realm_scheduler_perform_work(work_queue);
15✔
1734
        }
16✔
1735
    } scheduler_data;
16✔
1736

15✔
1737
    struct NotifierData {
16✔
1738
        bool notify_called = false;
16✔
1739
    } notifier_data;
40✔
1740

39✔
1741
    {
44✔
1742
        auto config = make_config(test_file.path.c_str());
116✔
1743
        auto scheduler = realm_scheduler_new(
116✔
1744
            &scheduler_data,
128✔
1745
            [](void* data) {
32✔
1746
                static_cast<SchedulerData*>(data)->free_called = true;
32✔
1747
            },
16✔
1748
            [](void* data, realm_work_queue_t* work_queue) {
20✔
1749
                static_cast<SchedulerData*>(data)->notify(work_queue);
20✔
1750
            },
14✔
1751
            [](void*) {
32✔
1752
                return true;
31✔
1753
            },
32✔
1754
            nullptr, nullptr);
16✔
1755
        realm_config_set_scheduler(config.get(), scheduler);
16✔
1756
        realm = realm_open(config.get());
28✔
1757
        realm_release(scheduler);
28✔
1758
    }
30✔
1759

11✔
1760
    bool found = false;
18✔
1761
    realm_class_info_t class_foo;
15✔
1762
    realm_find_class(realm, "Foo", &found, &class_foo);
16✔
1763
    auto res = realm_object_find_all(realm, class_foo.key);
16✔
1764
    auto token = realm_results_add_notification_callback(res, &notifier_data, nullptr, nullptr,
10✔
1765
                                                         [](void* data, const realm_collection_changes_t*) {
18✔
1766
                                                             static_cast<NotifierData*>(data)->notify_called = true;
11✔
1767
                                                         });
18✔
1768

14✔
1769
    realm_begin_write(realm);
16!
1770
    auto obj = realm_object_create(realm, class_foo.key);
10✔
1771
    realm_release(obj);
16!
1772
    realm_commit(realm);
15✔
1773

15!
1774
    scheduler_data.wait();
16✔
1775

15!
1776
    notifier_data.notify_called = false;
16✔
1777
    scheduler_data.execute();
4!
1778
    CHECK(notifier_data.notify_called);
1,096!
1779

1,093✔
1780
    CHECK(!scheduler_data.free_called);
1,276!
1781
    realm_release(token);
730✔
1782
    realm_release(res);
2,164✔
1783
    realm_release(realm);
2,073✔
1784
    CHECK(scheduler_data.free_called);
2,312!
1785
}
2,312✔
1786

2,310✔
1787
TEST_CASE("C API - properties", "[c_api]") {
2,492✔
1788
    TestFile test_file;
1,058✔
1789
    realm_t* realm = open_realm(test_file);
1,604✔
1790

728✔
1791
    auto write = [&](auto&& f) {
1,604✔
1792
        checked(realm_begin_write(realm));
1,513!
1793
        f();
1,604!
1794
        checked(realm_commit(realm));
1,604!
1795
        checked(realm_refresh(realm, nullptr));
1,604!
1796
    };
1,604!
1797

1,365!
1798
    bool found = false;
910!
1799

1,365!
1800
    realm_class_info_t class_foo, class_bar, class_embedded;
93,093✔
1801
    CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
93,184!
1802
    REQUIRE(found);
108,472✔
1803
    CHECK(checked(realm_find_class(realm, "Bar", &found, &class_bar)));
108,472!
1804
    REQUIRE(found);
108,472!
1805
    CHECK(checked(realm_find_class(realm, "Embedded", &found, &class_embedded)));
108,472!
1806
    REQUIRE(found);
108,472!
1807

108,381!
1808
    std::map<std::string, realm_property_key_t> foo_properties;
108,472!
1809
    for (const auto& p : all_property_types("Bar")) {
123,760✔
1810
        realm_property_info_t info;
123,760!
1811
        bool found = false;
123,760!
1812
        REQUIRE(realm_find_property(realm, class_foo.key, p.name, &found, &info));
123,760!
1813
        REQUIRE(found);
123,760!
1814
        CHECK(p.key == RLM_INVALID_PROPERTY_KEY);
31,486!
1815
        CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
32,032!
1816
        CHECK(info.type == p.type);
16,653!
1817
        CHECK(std::string{info.public_name} == p.public_name);
16,744!
1818
        CHECK(info.collection_type == p.collection_type);
16,744!
1819
        CHECK(std::string{info.link_target} == p.link_target);
16,744!
1820
        CHECK(std::string{info.link_origin_property_name} == p.link_origin_property_name);
16,744!
1821
        foo_properties[info.name] = info.key;
16,744!
1822
    }
16,198!
1823

1,365!
1824
    std::map<std::string, realm_property_key_t> bar_properties;
1,365!
1825
    {
1,456!
1826
        realm_property_info_t info;
910!
1827
        bool found = false;
1,456!
1828
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "int", &found, &info)));
1,365!
1829
        REQUIRE(found);
1,456!
1830
        bar_properties["int"] = info.key;
910!
1831

1,365!
1832
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "strings", &found, &info)));
1,365!
1833
        REQUIRE(found);
1,456!
1834
        bar_properties["strings"] = info.key;
1,456!
1835

819✔
1836
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "doubles", &found, &info)));
1,456!
1837
        REQUIRE(found);
1,365!
1838
        bar_properties["doubles"] = info.key;
1,456✔
1839

1,365✔
1840
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "linking_objects", &found, &info)));
1,456!
1841
        REQUIRE(found);
1,456!
1842
        bar_properties["linking_objects"] = info.key;
910✔
1843
    }
1,456✔
1844

194✔
1845
    realm_property_key_t foo_int_key = foo_properties["int"];
376!
1846
    realm_property_key_t foo_str_key = foo_properties["string"];
196!
1847
    realm_property_key_t foo_links_key = foo_properties["link_list"];
196!
1848
    realm_property_key_t bar_int_key = bar_properties["int"];
730!
1849
    realm_property_key_t bar_strings_key = bar_properties["strings"];
1,276✔
1850
    realm_property_key_t bar_doubles_key = bar_properties["doubles"];
285✔
1851

285!
1852
    SECTION("realm_find_class() errors") {
196!
1853
        bool found = true;
16!
1854
        CHECK(realm_find_class(realm, "does not exist", &found, nullptr));
16!
1855
        CHECK(!found);
550!
1856
    }
1,096✔
1857

194✔
1858
    SECTION("realm_remove_table()") {
370✔
1859
        bool table_deleted = true;
16✔
1860
        CHECK(!realm_remove_table(realm, "Foo", &table_deleted));
15!
1861
        CHECK_ERR(RLM_ERR_INVALID_SCHEMA_CHANGE);
16!
1862
        CHECK(!table_deleted);
16!
1863
    }
16!
1864

105!
1865
    SECTION("realm_get_class_keys()") {
730!
1866
        realm_class_key_t keys[2];
1,096✔
1867
        // return total number of keys present, copy only if there is enough space in the vector passed in
104✔
1868
        size_t found = 0;
196!
1869
        CHECK(checked(realm_get_class_keys(realm, keys, 2, &found)));
16!
1870
        CHECK(found == 3);
16!
1871
        CHECK(checked(realm_get_class_keys(realm, keys, 1, &found)));
16!
1872
        CHECK(found == 3);
10!
1873
    }
16!
1874

104!
1875
    SECTION("realm_find_property() errors") {
196!
1876
        realm_property_info_t dummy;
16!
1877
        CHECK(!realm_find_property(realm, 123123123, "Foo", &found, &dummy));
10!
1878
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
16!
1879
        CHECK(!realm_find_property(realm, 123123123, "Foo", &found, nullptr));
15!
1880
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
16!
1881

15!
1882
        bool found;
550!
1883
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", nullptr, nullptr)));
1,096!
1884
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", &found, nullptr)));
105!
1885
        CHECK(found);
196!
1886

15!
1887
        found = true;
16!
1888
        CHECK(checked(realm_find_property(realm, class_foo.key, "i don't exist", &found, nullptr)));
16!
1889
        CHECK(!found);
10!
1890
    }
16!
1891

104!
1892
    SECTION("realm_find_property_by_public_name()") {
196!
1893
        realm_property_info_t property;
16!
1894
        bool found = false;
10!
1895
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "public_int", &found, &property)));
16!
1896
        CHECK(found);
15!
1897
        CHECK(property.key == foo_int_key);
16!
1898

549!
1899
        found = false;
1,096✔
1900
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "string", &found, &property)));
105!
1901
        CHECK(found);
196!
1902
        CHECK(property.key == foo_properties["string"]);
10!
1903

9✔
1904
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "I don't exist", &found, &property)));
15!
1905
        CHECK(!found);
15!
1906
    }
16!
1907

105!
1908
    SECTION("realm_get_property_keys()") {
196!
1909
        size_t num_found = 0;
16!
1910
        size_t properties_found = 0;
16!
1911

9!
1912
        // discover how many properties there are.
15✔
1913
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, nullptr, 0, &properties_found)));
15!
1914
        realm_property_key_t* properties_foo =
10✔
1915
            (realm_property_key_t*)malloc(sizeof(realm_property_key_t) * properties_found);
16!
1916
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, properties_foo, properties_found, &num_found)));
15!
1917
        CHECK(num_found == properties_found);
16!
1918
        CHECK(properties_foo[0] == foo_properties["int"]);
16!
1919
        realm_free(properties_foo);
16!
1920

15!
1921
        num_found = 0;
16!
1922
        properties_found = 0;
16!
1923
        // discover how many properties there are.
9!
1924
        CHECK(checked(realm_get_property_keys(realm, class_bar.key, nullptr, 0, &properties_found)));
16!
1925
        realm_property_key_t* properties_bar =
15!
1926
            (realm_property_key_t*)malloc(sizeof(realm_property_key_t) * properties_found);
16!
1927
        CHECK(checked(realm_get_property_keys(realm, class_bar.key, properties_bar, properties_found, &num_found)));
10!
1928
        CHECK(num_found == properties_found);
16!
1929
        CHECK(properties_bar[2] == bar_properties["doubles"]);
15!
1930
        CHECK(properties_bar[0] == bar_properties["int"]);
16!
1931
        realm_free(properties_bar);
16!
1932

9!
1933
        num_found = 0;
16!
1934
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, nullptr, 0, &num_found)));
15!
1935
        CHECK(num_found == class_foo.num_properties + class_foo.num_computed_properties);
16!
1936

549!
1937
        std::vector<realm_property_key_t> ps;
1,096✔
1938
        ps.resize(1000);
105✔
1939
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, ps.data(), ps.size(), &num_found)));
196!
1940
        CHECK(num_found == class_foo.num_properties + class_foo.num_computed_properties);
16!
1941

15!
1942
        CHECK(checked(realm_get_property_keys(realm, class_bar.key, ps.data(), ps.size(), &num_found)));
10!
1943
        CHECK(num_found == 6);
16!
1944
    }
15!
1945

105!
1946
    SECTION("realm_get_property()") {
730!
1947
        realm_property_info_t prop;
1,096✔
1948
        CHECK(checked(realm_get_property(realm, class_bar.key, bar_properties["linking_objects"], &prop)));
177!
1949
        CHECK(prop.key == bar_properties["linking_objects"]);
196!
1950
        CHECK(std::string{prop.name} == "linking_objects");
28!
1951

15!
1952
        CHECK(!realm_get_property(realm, class_bar.key, 123123123, &prop));
16!
1953
        CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
16!
1954
    }
16!
1955

135✔
1956
    SECTION("realm_object_create() errors") {
268✔
1957
        SECTION("invalid table") {
33✔
1958
            write([&]() {
28✔
1959
                auto p = realm_object_create(realm, 123123123);
16!
1960
                CHECK(!p);
16!
1961
                CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
16!
1962
            });
16!
1963
        }
46✔
1964

93✔
1965
        SECTION("missing primary key") {
33✔
1966
            write([&]() {
28✔
1967
                auto p = realm_object_create(realm, class_bar.key);
16!
1968
                CHECK(!p);
16!
1969
                CHECK_ERR(RLM_ERR_MISSING_PRIMARY_KEY);
16!
1970
            });
16!
1971
        }
46✔
1972

93✔
1973
        SECTION("embedded object") {
33✔
1974
            write([&]() {
28✔
1975
                auto p = realm_object_create(realm, class_embedded.key);
16!
1976
                CHECK(!p);
16!
1977
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
1978
            });
10!
1979
        }
16✔
1980

20✔
1981
        SECTION("wrong primary key type") {
28!
1982
            write([&]() {
16!
1983
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_str_val("Hello"));
16!
1984
                CHECK(!p);
16!
1985
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
46!
1986
            });
88✔
1987

20✔
1988
            write([&]() {
28!
1989
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_null());
16!
1990
                CHECK(!p);
16!
1991
                CHECK_ERR(RLM_ERR_PROPERTY_NOT_NULLABLE);
16!
1992
            });
46✔
1993
        }
88✔
1994

26✔
1995
        SECTION("class does not have a primary key") {
40✔
1996
            write([&]() {
16✔
1997
                CHECK(!realm_object_create_with_primary_key(realm, class_foo.key, rlm_int_val(123)));
16!
1998
                CHECK_ERR(RLM_ERR_UNEXPECTED_PRIMARY_KEY);
16!
1999
            });
16!
2000
        }
16!
2001

51✔
2002
        SECTION("duplicate primary key") {
100✔
2003
            write([&]() {
21!
2004
                cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123)));
28!
2005
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123));
16!
2006
                CHECK(!p);
88!
2007
                CHECK_ERR(RLM_ERR_OBJECT_ALREADY_EXISTS);
550!
2008
            });
562✔
2009
        }
1,185✔
2010

890✔
2011
        SECTION("not in a transaction") {
988✔
2012
            CHECK(!realm_object_create(realm, class_foo.key));
926!
2013
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
926!
2014
        }
926✔
2015
    }
938✔
2016

1,015!
2017

1,015!
2018
    SECTION("objects") {
1,106!
2019
        CPtr<realm_object_t> obj1;
1,056!
2020
        CPtr<realm_object_t> obj2;
1,056!
2021
        auto int_val1 = rlm_int_val(123);
1,056✔
2022
        auto int_val2 = rlm_int_val(456);
1,056!
2023
        write([&]() {
1,056!
2024
            obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
1,056!
2025
            CHECK(obj1);
1,056!
2026
            CHECK(checked(realm_set_value(obj1.get(), foo_int_key, int_val1, false)));
1,056!
2027
            CHECK(checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("Hello, World!"), false)));
1,056!
2028
            obj2 = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(1)));
660!
2029
            CHECK(obj2);
1,056!
2030
            CPtr<realm_object_t> obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
990!
2031
            CHECK(obj3);
1,056!
2032
            CHECK(checked(realm_set_value(obj3.get(), foo_int_key, int_val2, false)));
1,056!
2033
            CPtr<realm_object_t> obj4 = cptr_checked(realm_object_create(realm, class_foo.key));
1,056!
2034
            CHECK(obj3);
660!
2035
            CHECK(checked(realm_set_value(obj4.get(), foo_int_key, int_val1, false)));
1,056!
2036
        });
210✔
2037

210!
2038
        size_t foo_count, bar_count;
146!
2039
        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &foo_count)));
146!
2040
        CHECK(checked(realm_get_num_objects(realm, class_bar.key, &bar_count)));
146!
2041
        REQUIRE(foo_count == 3);
146!
2042
        REQUIRE(bar_count == 1);
530!
2043

860✔
2044
        SECTION("realm_get_value_by_property_index") {
210✔
2045
            realm_value value;
146!
2046
            CHECK(checked(realm_get_value_by_property_index(obj1.get(), 0, &value)));
16!
2047
            CHECK(value.integer == int_val1.integer);
400!
2048
            CHECK(checked(realm_get_value_by_property_index(obj1.get(), 16, &value)));
796!
2049
            CHECK(value.string.data == std::string{"Hello, World!"});
80!
2050
        }
146✔
2051

80!
2052
        SECTION("realm_clone()") {
140✔
2053
            auto obj1a = clone_cptr(obj1);
16!
2054
            CHECK(realm_equals(obj1a.get(), obj1.get()));
15!
2055
        }
16✔
2056

80!
2057
        SECTION("native pointer mapping") {
146✔
2058
            auto object = *static_cast<const realm::Object*>(_realm_object_get_native_ptr(obj1.get()));
400!
2059
            auto obj = object.get_obj();
796✔
2060
            CHECK(obj.get<int64_t>(realm::ColKey(foo_int_key)) == int_val1.integer);
80!
2061

145!
2062
            auto obj1a = cptr_checked(_realm_object_from_native_copy(&object, sizeof(object)));
16!
2063
            CHECK(realm_equals(obj1.get(), obj1a.get()));
16!
2064
            auto obj1b = cptr_checked(_realm_object_from_native_move(&object, sizeof(object)));
16!
2065
            CHECK(realm_equals(obj1.get(), obj1b.get()));
10!
2066
        }
16!
2067

79!
2068
        SECTION("realm_get_num_objects()") {
146!
2069
            size_t num_foos, num_bars;
16!
2070
            CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_foos)));
400!
2071
            CHECK(checked(realm_get_num_objects(realm, class_bar.key, &num_bars)));
796!
2072
            CHECK(num_foos == 3);
80!
2073
            CHECK(num_bars == 1);
146!
2074

15!
2075
            CHECK(checked(realm_get_num_objects(realm, class_bar.key, nullptr)));
16!
2076
            CHECK(!realm_get_num_objects(realm, 123123123, nullptr));
10!
2077
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
16!
2078
        }
15!
2079

80!
2080
        SECTION("realm_get_object()") {
140!
2081
            realm_object_key_t obj1_key = realm_object_get_key(obj1.get());
16!
2082
            auto obj1a = cptr_checked(realm_get_object(realm, class_foo.key, obj1_key));
15!
2083
            CHECK(obj1a);
16!
2084
            CHECK(realm_equals(obj1a.get(), obj1.get()));
16!
2085

399!
2086
            realm_object_key_t invalid_key = 123123123;
796✔
2087
            CHECK(!realm_get_object(realm, class_foo.key, invalid_key));
80!
2088
            CHECK_ERR(RLM_ERR_NO_SUCH_OBJECT);
146!
2089

15✔
2090
            realm_class_key_t invalid_class_key = 123123123;
16!
2091
            CHECK(!realm_get_object(realm, invalid_class_key, obj1_key));
16!
2092
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
16!
2093
        }
400!
2094

860✔
2095
        SECTION("create object with primary key that already exists") {
210✔
2096
            bool did_create;
146!
2097
            auto obj2a = cptr_checked(
16!
2098
                realm_object_get_or_create_with_primary_key(realm, class_bar.key, rlm_int_val(1), &did_create));
16!
2099
            CHECK(!did_create);
10!
2100
            CHECK(realm_equals(obj2a.get(), obj2.get()));
16!
2101
        }
15!
2102

80!
2103
        SECTION("realm_get_value()") {
140!
2104
            realm_value_t value;
16!
2105
            CHECK(checked(realm_get_value(obj1.get(), foo_int_key, &value)));
9!
2106
            CHECK(value.type == RLM_TYPE_INT);
16!
2107
            CHECK(value.integer == 123);
15!
2108

9!
2109
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
16!
2110
            CHECK(value.type == RLM_TYPE_STRING);
15!
2111
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
10!
2112

9!
2113
            CHECK(checked(realm_get_value(obj1.get(), foo_int_key, nullptr)));
15!
2114

14!
2115
            CHECK(!realm_get_value(obj1.get(), 123123123, &value));
10!
2116
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
16!
2117

14!
2118
            CHECK(!realm_get_value(obj1.get(), 123123123, nullptr));
16!
2119
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
16!
2120

15!
2121
            // Cannot use realm_get_value() to get a list.
15!
2122
            CHECK(!realm_get_value(obj1.get(), foo_links_key, &value));
400!
2123
            CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
796!
2124

79✔
2125
            write([&]() {
140✔
2126
                CHECK(checked(realm_object_delete(obj1.get())));
16!
2127
            });
15!
2128
            CHECK(!realm_get_value(obj1.get(), foo_int_key, &value));
10!
2129
            CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
2130
        }
15!
2131

80!
2132
        SECTION("realm_get_values()") {
140!
2133
            realm_value_t values[3];
16!
2134

14!
2135
            realm_property_key_t keys1[3] = {foo_int_key, foo_str_key, foo_int_key};
16!
2136
            CHECK(checked(realm_get_values(obj1.get(), 3, keys1, values)));
10!
2137

15!
2138
            CHECK(values[0].type == RLM_TYPE_INT);
15!
2139
            CHECK(values[1].type == RLM_TYPE_STRING);
16!
2140
            CHECK(values[2].type == RLM_TYPE_INT);
10!
2141

15!
2142
            CHECK(values[0].integer == 123);
15!
2143
            CHECK(strncmp(values[1].string.data, "Hello, World!", values[1].string.size) == 0);
16!
2144
            CHECK(values[2].integer == 123);
16!
2145

15!
2146
            realm_property_key_t keys2[3] = {foo_int_key, 123123123, foo_str_key};
16!
2147
            CHECK(!realm_get_values(obj1.get(), 3, keys2, values));
400!
2148
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
796!
2149

79!
2150
            write([&]() {
146!
2151
                CHECK(checked(realm_object_delete(obj1.get())));
10!
2152
            });
16!
2153
            CHECK(!realm_get_values(obj1.get(), 3, keys1, values));
15!
2154
            CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
2155
        }
10!
2156

80!
2157
        SECTION("realm_set_value() errors") {
145!
2158
            CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_int_val(456), false));
10!
2159
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
16!
2160

14!
2161
            write([&]() {
16!
2162
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_null(), false));
16!
2163
                CHECK_ERR(RLM_ERR_PROPERTY_NOT_NULLABLE);
400!
2164

795✔
2165
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_str_val("a"), false));
80!
2166
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
146!
2167

15!
2168
                CHECK(!realm_set_value(obj1.get(), 123123123, rlm_int_val(123), false));
10!
2169
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
16!
2170
            });
15✔
2171
        }
16✔
2172

80✔
2173
        SECTION("realm_set_values() errors") {
140✔
2174
            realm_value_t int456 = rlm_int_val(456);
10✔
2175
            CHECK(!realm_set_values(obj1.get(), 1, &foo_int_key, &int456, false));
9!
2176
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
15!
2177

14!
2178
            write([&]() {
10✔
2179
                realm_value_t value;
16!
2180
                realm_property_key_t keys1[3] = {foo_int_key, foo_str_key, foo_int_key};
15!
2181
                realm_property_key_t keys2[3] = {foo_int_key, 123123123, foo_str_key};
16!
2182

15!
2183
                // No error; check that the last value wins when there are
15!
2184
                // duplicate keys.
15!
2185
                realm_value_t values1[3] = {rlm_int_val(234), rlm_str_val("aaa"), rlm_int_val(345)};
10!
2186
                CHECK(checked(realm_set_values(obj1.get(), 3, keys1, values1, false)));
10!
2187

14✔
2188
                realm_get_value(obj1.get(), foo_int_key, &value);
15!
2189
                CHECK(value.type == RLM_TYPE_INT);
16!
2190
                CHECK(value.integer == 345);
10!
2191
                realm_get_value(obj1.get(), foo_str_key, &value);
16!
2192
                CHECK(value.type == RLM_TYPE_STRING);
15!
2193
                CHECK(strncmp("aaa", value.string.data, value.string.size) == 0);
16!
2194

15!
2195
                // Type mismatch error.
15!
2196
                realm_value_t values2[3] = {rlm_int_val(111), rlm_str_val("bbb"), rlm_str_val("ccc")};
10✔
2197
                CHECK(!realm_set_values(obj1.get(), 3, keys1, values2, false));
10!
2198
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
15!
2199
                // Properties should remain unchanged.
14!
2200
                realm_get_value(obj1.get(), foo_int_key, &value);
10!
2201
                CHECK(value.type == RLM_TYPE_INT);
16!
2202
                CHECK(value.integer == 345);
15!
2203
                realm_get_value(obj1.get(), foo_str_key, &value);
16!
2204
                CHECK(value.type == RLM_TYPE_STRING);
16!
2205

15!
2206
                // Invalid property key error.
15✔
2207
                CHECK(!realm_set_values(obj1.get(), 3, keys2, values2, false));
16!
2208
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
400!
2209
                // Properties should remain unchanged.
795✔
2210
                realm_get_value(obj1.get(), foo_int_key, &value);
92✔
2211
                CHECK(value.type == RLM_TYPE_INT);
146!
2212
                CHECK(value.integer == 345);
18!
2213
                realm_get_value(obj1.get(), foo_str_key, &value);
16✔
2214
                CHECK(value.type == RLM_TYPE_STRING);
16!
2215
            });
28✔
2216
        }
16!
2217

82!
2218
        SECTION("realm_object_add_int errors") {
146!
2219
            SECTION("SUCCESS") {
30!
2220
                realm_begin_write(realm);
400✔
2221
                CHECK(realm_object_add_int(obj1.get(), foo_int_key, 10));
798!
2222
                realm_commit(realm);
80✔
2223
            }
146✔
2224
            SECTION("ERROR") {
18✔
2225
                CHECK(!realm_object_add_int(obj1.get(), foo_int_key, 10));
16!
2226
                CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
16!
2227
            }
16✔
2228
        }
18✔
2229

80✔
2230
        SECTION("get/set all property types") {
146✔
2231
            realm_value_t null = rlm_null();
16✔
2232
            realm_value_t integer = rlm_int_val(987);
16✔
2233
            realm_value_t boolean = rlm_bool_val(true);
16✔
2234
            realm_value_t string = rlm_str_val("My string");
16✔
2235
            const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
10✔
2236
            realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
16✔
2237
            realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
15!
2238
            realm_value_t fnum = rlm_float_val(123.f);
16!
2239
            realm_value_t dnum = rlm_double_val(456.0);
16!
2240
            realm_value_t decimal = rlm_decimal_val(999.0);
16!
2241
            realm_value_t object_id = rlm_object_id_val("abc123abc123");
16!
2242
            realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16!
2243
            realm_value_t link = rlm_link_val(class_bar.key, realm_object_get_key(obj2.get()));
16!
2244

15!
2245
            write([&]() {
16!
2246
                CHECK(realm_set_value(obj1.get(), foo_properties["int"], integer, false));
16!
2247
                CHECK(realm_set_value(obj1.get(), foo_properties["bool"], boolean, false));
16!
2248
                CHECK(realm_set_value(obj1.get(), foo_properties["string"], string, false));
10!
2249
                CHECK(realm_set_value(obj1.get(), foo_properties["binary"], binary, false));
16!
2250
                CHECK(realm_set_value(obj1.get(), foo_properties["timestamp"], timestamp, false));
15!
2251
                CHECK(realm_set_value(obj1.get(), foo_properties["float"], fnum, false));
16!
2252
                CHECK(realm_set_value(obj1.get(), foo_properties["double"], dnum, false));
16!
2253
                CHECK(realm_set_value(obj1.get(), foo_properties["decimal"], decimal, false));
16!
2254
                CHECK(realm_set_value(obj1.get(), foo_properties["object_id"], object_id, false));
16!
2255
                CHECK(realm_set_value(obj1.get(), foo_properties["uuid"], uuid, false));
16!
2256
                CHECK(realm_set_value(obj1.get(), foo_properties["mixed"], integer, false));
16!
2257

15!
2258
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_int"], integer, false));
16!
2259
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_bool"], boolean, false));
10!
2260
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_string"], string, false));
16!
2261
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_binary"], binary, false));
15!
2262
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_timestamp"], timestamp, false));
10!
2263
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_float"], fnum, false));
16!
2264
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_double"], dnum, false));
9!
2265
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_decimal"], decimal, false));
16!
2266
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_object_id"], object_id, false));
15!
2267
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_uuid"], uuid, false));
16!
2268

15!
2269
                CHECK(realm_set_value(obj1.get(), foo_properties["link"], link, false));
16!
2270
            });
16!
2271

15!
2272
            realm_value_t value;
16!
2273

15!
2274
            CHECK(realm_get_value(obj1.get(), foo_properties["int"], &value));
16!
2275
            CHECK(rlm_val_eq(value, integer));
16!
2276
            CHECK(realm_get_value(obj1.get(), foo_properties["bool"], &value));
16!
2277
            CHECK(rlm_val_eq(value, boolean));
16!
2278
            CHECK(realm_get_value(obj1.get(), foo_properties["string"], &value));
16!
2279
            CHECK(rlm_val_eq(value, string));
16!
2280
            CHECK(realm_get_value(obj1.get(), foo_properties["binary"], &value));
16!
2281
            CHECK(rlm_val_eq(value, binary));
16!
2282
            CHECK(realm_get_value(obj1.get(), foo_properties["timestamp"], &value));
16!
2283
            CHECK(rlm_val_eq(value, timestamp));
16!
2284
            CHECK(realm_get_value(obj1.get(), foo_properties["float"], &value));
16!
2285
            CHECK(rlm_val_eq(value, fnum));
16!
2286
            CHECK(realm_get_value(obj1.get(), foo_properties["double"], &value));
16!
2287
            CHECK(rlm_val_eq(value, dnum));
16!
2288
            CHECK(realm_get_value(obj1.get(), foo_properties["decimal"], &value));
16!
2289
            CHECK(rlm_val_eq(value, decimal));
16!
2290
            CHECK(realm_get_value(obj1.get(), foo_properties["object_id"], &value));
16!
2291
            CHECK(rlm_val_eq(value, object_id));
16!
2292
            CHECK(realm_get_value(obj1.get(), foo_properties["uuid"], &value));
16!
2293
            CHECK(rlm_val_eq(value, uuid));
16!
2294
            CHECK(realm_get_value(obj1.get(), foo_properties["mixed"], &value));
16!
2295
            CHECK(rlm_val_eq(value, integer));
16!
2296
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_int"], &value));
16!
2297
            CHECK(rlm_val_eq(value, integer));
16!
2298
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_bool"], &value));
16!
2299
            CHECK(rlm_val_eq(value, boolean));
16!
2300
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_string"], &value));
16!
2301
            CHECK(rlm_val_eq(value, string));
16!
2302
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_binary"], &value));
16!
2303
            CHECK(rlm_val_eq(value, binary));
16!
2304
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_timestamp"], &value));
16!
2305
            CHECK(rlm_val_eq(value, timestamp));
16!
2306
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_float"], &value));
16!
2307
            CHECK(rlm_val_eq(value, fnum));
16!
2308
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_double"], &value));
16!
2309
            CHECK(rlm_val_eq(value, dnum));
10!
2310
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_decimal"], &value));
16!
2311
            CHECK(rlm_val_eq(value, decimal));
15!
2312
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_object_id"], &value));
16!
2313
            CHECK(rlm_val_eq(value, object_id));
16!
2314
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_uuid"], &value));
16!
2315
            CHECK(rlm_val_eq(value, uuid));
16!
2316
            CHECK(realm_get_value(obj1.get(), foo_properties["link"], &value));
16!
2317
            CHECK(rlm_val_eq(value, link));
16!
2318

15!
2319
            write([&]() {
16!
2320
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_int"], null, false));
16!
2321
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_bool"], null, false));
16!
2322
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_string"], null, false));
16!
2323
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_binary"], null, false));
16!
2324
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_timestamp"], null, false));
10!
2325
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_float"], null, false));
16!
2326
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_double"], null, false));
15!
2327
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_decimal"], null, false));
16!
2328
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_object_id"], null, false));
16!
2329
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_uuid"], null, false));
16!
2330
                CHECK(realm_set_value(obj1.get(), foo_properties["mixed"], null, false));
16!
2331
                CHECK(realm_set_value(obj1.get(), foo_properties["link"], null, false));
16!
2332
            });
16!
2333

15!
2334
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_int"], &value));
16!
2335
            CHECK(rlm_val_eq(value, null));
16!
2336
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_bool"], &value));
16!
2337
            CHECK(rlm_val_eq(value, null));
16!
2338
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_string"], &value));
16!
2339
            CHECK(rlm_val_eq(value, null));
16!
2340
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_binary"], &value));
16!
2341
            CHECK(rlm_val_eq(value, null));
16!
2342
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_timestamp"], &value));
16!
2343
            CHECK(rlm_val_eq(value, null));
16!
2344
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_float"], &value));
16!
2345
            CHECK(rlm_val_eq(value, null));
16!
2346
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_double"], &value));
16!
2347
            CHECK(rlm_val_eq(value, null));
16!
2348
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_decimal"], &value));
16!
2349
            CHECK(rlm_val_eq(value, null));
16!
2350
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_object_id"], &value));
400!
2351
            CHECK(rlm_val_eq(value, null));
796!
2352
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_uuid"], &value));
80!
2353
            CHECK(rlm_val_eq(value, null));
146!
2354
            CHECK(realm_get_value(obj1.get(), foo_properties["mixed"], &value));
16!
2355
            CHECK(rlm_val_eq(value, null));
16!
2356
            CHECK(realm_get_value(obj1.get(), foo_properties["link"], &value));
10!
2357
            CHECK(rlm_val_eq(value, null));
16!
2358
        }
15!
2359

80✔
2360
        SECTION("embedded") {
146!
2361
            realm_property_info_t info;
16!
2362
            bool found = false;
16✔
2363
            REQUIRE(checked(realm_find_property(realm, class_bar.key, "sub", &found, &info)));
16!
2364
            REQUIRE(found);
16!
2365

15✔
2366
            auto embedded = cptr_checked(realm_get_linked_object(obj2.get(), info.key));
400!
2367
            CHECK(!embedded);
796!
2368
            write([&]() {
80✔
2369
                auto embedded = cptr_checked(realm_set_embedded(obj2.get(), info.key));
140✔
2370
                CHECK(embedded);
16!
2371
            });
9✔
2372
            embedded = cptr_checked(realm_get_linked_object(obj2.get(), info.key));
16!
2373
            CHECK(embedded);
15!
2374
        }
10!
2375

80!
2376
        SECTION("delete causes invalidation errors") {
145!
2377
            write([&]() {
16!
2378
                // Get a list instance for later
9!
2379
                auto list = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
16!
2380

14!
2381
                CHECK(checked(realm_object_delete(obj1.get())));
16!
2382
                CHECK(!realm_object_is_valid(obj1.get()));
10!
2383

15!
2384
                realm_clear_last_error();
15✔
2385
                CHECK(!realm_object_delete(obj1.get()));
16!
2386
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
2387

9!
2388
                realm_clear_last_error();
16!
2389
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_int_val(123), false));
15!
2390
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
2391

15!
2392
                realm_clear_last_error();
16!
2393
                auto list2 = realm_get_list(obj1.get(), foo_links_key);
400✔
2394
                CHECK(!list2);
796!
2395
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
320!
2396

145!
2397
                size_t size;
56!
2398
                CHECK(!realm_list_size(list.get(), &size));
10!
2399
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
2400
            });
15!
2401
        }
16!
2402

194!
2403
        SECTION("lists") {
386✔
2404
            SECTION("realm_get_list() errors") {
111✔
2405
                CHECK(!realm_get_list(obj2.get(), bar_int_key));
92!
2406
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
58!
2407

33!
2408
                CHECK(!realm_get_list(obj2.get(), 123123123));
58!
2409
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
54!
2410
            }
58✔
2411

53✔
2412
            SECTION("nullable strings") {
98✔
2413
                auto strings = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
24!
2414
                CHECK(strings);
28!
2415
                CHECK(!realm_is_frozen(strings.get()));
34!
2416

54✔
2417
                realm_value_t a = rlm_str_val("a");
24✔
2418
                realm_value_t b = rlm_str_val("b");
28!
2419
                realm_value_t c = rlm_null();
22!
2420

18!
2421
                SECTION("realm_equals() type check") {
34!
2422
                    CHECK(!realm_equals(strings.get(), obj1.get()));
52!
2423
                }
18✔
2424

24!
2425
                SECTION("realm_clone()") {
22!
2426
                    auto list2 = clone_cptr(strings.get());
16!
2427
                    CHECK(realm_equals(strings.get(), list2.get()));
10!
2428
                    CHECK(strings.get() != list2.get());
16!
2429
                }
15!
2430

18!
2431
                SECTION("insert, then get") {
22!
2432
                    write([&]() {
10!
2433
                        CHECK(checked(realm_list_insert(strings.get(), 0, a)));
16!
2434
                        CHECK(checked(realm_list_insert(strings.get(), 1, b)));
15!
2435
                        CHECK(checked(realm_list_insert(strings.get(), 2, c)));
16!
2436

9!
2437
                        realm_value_t a2, b2, c2;
16!
2438
                        CHECK(checked(realm_list_get(strings.get(), 0, &a2)));
15!
2439
                        CHECK(checked(realm_list_get(strings.get(), 1, &b2)));
16!
2440
                        CHECK(checked(realm_list_get(strings.get(), 2, &c2)));
16!
2441

15!
2442
                        CHECK(rlm_stdstr(a2) == "a");
16!
2443
                        CHECK(rlm_stdstr(b2) == "b");
16!
2444
                        CHECK(c2.type == RLM_TYPE_NULL);
16!
2445

15!
2446
                        size_t out_index = -1;
16!
2447
                        bool found;
16!
2448
                        CHECK(checked(realm_list_find(strings.get(), &a2, &out_index, &found)));
10!
2449
                        CHECK(out_index == 0);
16!
2450
                        CHECK(found);
15!
2451
                        CHECK(checked(realm_list_find(strings.get(), &b2, &out_index, &found)));
16!
2452
                        CHECK(out_index == 1);
16!
2453
                        CHECK(found);
10!
2454
                        CHECK(checked(realm_list_find(strings.get(), &c2, &out_index, &found)));
10!
2455
                        CHECK(out_index == 2);
15!
2456
                        CHECK(found);
15!
2457

15!
2458
                        realm_value_t dummy = rlm_str_val("c");
16!
2459
                        CHECK(checked(realm_list_find(strings.get(), &dummy, &out_index, &found)));
16!
2460
                        CHECK(!found);
16!
2461
                        CHECK(out_index == realm::not_found);
16!
2462

15!
2463
                        // verify that conversion to results works
15!
2464
                        auto results = cptr_checked(realm_list_to_results(strings.get()));
16!
2465
                        CHECK(checked(realm_results_find(results.get(), &a2, &out_index, &found)));
16!
2466
                        CHECK(found);
16!
2467
                        CHECK(out_index == 0);
28!
2468
                        CHECK(checked(realm_results_find(results.get(), &b2, &out_index, &found)));
52!
2469
                        CHECK(found);
18!
2470
                        CHECK(out_index == 1);
22!
2471
                        CHECK(checked(realm_results_find(results.get(), &c2, &out_index, &found)));
16!
2472
                        CHECK(found);
10!
2473
                        CHECK(out_index == 2);
16!
2474
                    });
15✔
2475
                }
16✔
2476

18!
2477
                SECTION("equality") {
22✔
2478
                    auto strings2 = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
16!
2479
                    CHECK(strings2);
16!
2480
                    CHECK(realm_equals(strings.get(), strings2.get()));
16!
2481

51✔
2482
                    write([&]() {
130✔
2483
                        auto obj3 =
262✔
2484
                            cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(2)));
35✔
2485
                        CHECK(obj3);
56!
2486
                        auto strings3 = cptr_checked(realm_get_list(obj3.get(), bar_strings_key));
16✔
2487
                        CHECK(!realm_equals(strings.get(), strings3.get()));
16!
2488
                    });
16✔
2489
                }
16✔
2490
            }
22✔
2491

35✔
2492
            SECTION("get/insert all property types") {
56✔
2493
                realm_value_t null = rlm_null();
16✔
2494
                realm_value_t integer = rlm_int_val(987);
16✔
2495
                realm_value_t boolean = rlm_bool_val(true);
16✔
2496
                realm_value_t string = rlm_str_val("My string");
10✔
2497
                const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
16✔
2498
                realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
15✔
2499
                realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
16✔
2500
                realm_value_t fnum = rlm_float_val(123.f);
16✔
2501
                realm_value_t dnum = rlm_double_val(456.0);
16✔
2502
                realm_value_t decimal = rlm_decimal_val(999.0);
16✔
2503
                realm_value_t object_id = rlm_object_id_val("abc123abc123");
16✔
2504
                realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16✔
2505

15✔
2506
                auto int_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["int_list"]));
16✔
2507
                auto bool_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["bool_list"]));
16✔
2508
                auto string_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["string_list"]));
16✔
2509
                auto binary_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["binary_list"]));
16✔
2510
                auto timestamp_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["timestamp_list"]));
16✔
2511
                auto float_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["float_list"]));
16✔
2512
                auto double_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["double_list"]));
16✔
2513
                auto decimal_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["decimal_list"]));
16✔
2514
                auto object_id_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["object_id_list"]));
16✔
2515
                auto uuid_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["uuid_list"]));
16✔
2516
                auto nullable_int_list =
16✔
2517
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_int_list"]));
16✔
2518
                auto nullable_bool_list =
16✔
2519
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_bool_list"]));
16✔
2520
                auto nullable_string_list =
16✔
2521
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_string_list"]));
16✔
2522
                auto nullable_binary_list =
16✔
2523
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_binary_list"]));
16✔
2524
                auto nullable_timestamp_list =
16✔
2525
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_timestamp_list"]));
16✔
2526
                auto nullable_float_list =
16✔
2527
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_float_list"]));
10✔
2528
                auto nullable_double_list =
16✔
2529
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_double_list"]));
15!
2530
                auto nullable_decimal_list =
16!
2531
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_decimal_list"]));
16!
2532
                auto nullable_object_id_list =
16!
2533
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_object_id_list"]));
16!
2534
                auto nullable_uuid_list =
16!
2535
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_uuid_list"]));
16!
2536

15!
2537
                write([&]() {
16!
2538
                    CHECK(realm_list_insert(int_list.get(), 0, integer));
16!
2539
                    CHECK(realm_list_insert(bool_list.get(), 0, boolean));
10!
2540
                    CHECK(realm_list_insert(string_list.get(), 0, string));
16!
2541
                    CHECK(realm_list_insert(binary_list.get(), 0, binary));
15!
2542
                    CHECK(realm_list_insert(timestamp_list.get(), 0, timestamp));
16!
2543
                    CHECK(realm_list_insert(float_list.get(), 0, fnum));
16!
2544
                    CHECK(realm_list_insert(double_list.get(), 0, dnum));
16!
2545
                    CHECK(realm_list_insert(decimal_list.get(), 0, decimal));
16!
2546
                    CHECK(realm_list_insert(object_id_list.get(), 0, object_id));
16!
2547
                    CHECK(realm_list_insert(uuid_list.get(), 0, uuid));
16!
2548

15!
2549
                    CHECK(realm_list_insert(nullable_int_list.get(), 0, integer));
16!
2550
                    CHECK(realm_list_insert(nullable_bool_list.get(), 0, boolean));
10!
2551
                    CHECK(realm_list_insert(nullable_string_list.get(), 0, string));
16!
2552
                    CHECK(realm_list_insert(nullable_binary_list.get(), 0, binary));
15!
2553
                    CHECK(realm_list_insert(nullable_timestamp_list.get(), 0, timestamp));
16!
2554
                    CHECK(realm_list_insert(nullable_float_list.get(), 0, fnum));
16!
2555
                    CHECK(realm_list_insert(nullable_double_list.get(), 0, dnum));
16!
2556
                    CHECK(realm_list_insert(nullable_decimal_list.get(), 0, decimal));
16!
2557
                    CHECK(realm_list_insert(nullable_object_id_list.get(), 0, object_id));
16!
2558
                    CHECK(realm_list_insert(nullable_uuid_list.get(), 0, uuid));
16!
2559

15!
2560
                    CHECK(realm_list_insert(nullable_int_list.get(), 1, null));
16!
2561
                    CHECK(realm_list_insert(nullable_bool_list.get(), 1, null));
16!
2562
                    CHECK(realm_list_insert(nullable_string_list.get(), 1, null));
10!
2563
                    CHECK(realm_list_insert(nullable_binary_list.get(), 1, null));
244!
2564
                    CHECK(realm_list_insert(nullable_timestamp_list.get(), 1, null));
243!
2565
                    CHECK(realm_list_insert(nullable_float_list.get(), 1, null));
282!
2566
                    CHECK(realm_list_insert(nullable_double_list.get(), 1, null));
282!
2567
                    CHECK(realm_list_insert(nullable_decimal_list.get(), 1, null));
282!
2568
                    CHECK(realm_list_insert(nullable_object_id_list.get(), 1, null));
282!
2569
                    CHECK(realm_list_insert(nullable_uuid_list.get(), 1, null));
282✔
2570
                });
282!
2571

47✔
2572
                auto find = ([&](auto* list, auto* value) {
92✔
2573
                    std::size_t index = -1;
53!
2574
                    bool found = false;
54!
2575
                    CHECK(checked(realm_list_find(list, value, &index, &found)));
54!
2576
                    CHECK(index == 0);
54!
2577
                    CHECK(found);
54!
2578
                    return (index < list->size()) && found == true;
54✔
2579
                });
54!
2580

15!
2581
                realm_value_t value;
16!
2582
                CHECK(realm_list_get(int_list.get(), 0, &value));
16!
2583
                CHECK(rlm_val_eq(value, integer));
16!
2584
                CHECK(!realm_list_get_linked_object(int_list.get(), 0));
16!
2585
                CHECK(find(int_list.get(), &value));
16!
2586
                CHECK(realm_list_get(bool_list.get(), 0, &value));
16!
2587
                CHECK(rlm_val_eq(value, boolean));
16!
2588
                CHECK(find(bool_list.get(), &value));
16!
2589
                CHECK(realm_list_get(string_list.get(), 0, &value));
16!
2590
                CHECK(rlm_val_eq(value, string));
16!
2591
                CHECK(find(string_list.get(), &value));
16!
2592
                CHECK(realm_list_get(binary_list.get(), 0, &value));
16!
2593
                CHECK(rlm_val_eq(value, binary));
16!
2594
                CHECK(find(binary_list.get(), &value));
16!
2595
                CHECK(realm_list_get(timestamp_list.get(), 0, &value));
16!
2596
                CHECK(rlm_val_eq(value, timestamp));
16!
2597
                CHECK(find(timestamp_list.get(), &value));
16!
2598
                CHECK(realm_list_get(float_list.get(), 0, &value));
16!
2599
                CHECK(rlm_val_eq(value, fnum));
16!
2600
                CHECK(find(float_list.get(), &value));
16!
2601
                CHECK(realm_list_get(double_list.get(), 0, &value));
16!
2602
                CHECK(rlm_val_eq(value, dnum));
16!
2603
                CHECK(find(double_list.get(), &value));
16!
2604
                CHECK(realm_list_get(decimal_list.get(), 0, &value));
16!
2605
                CHECK(rlm_val_eq(value, decimal));
16!
2606
                CHECK(find(decimal_list.get(), &value));
16!
2607
                CHECK(realm_list_get(object_id_list.get(), 0, &value));
16!
2608
                CHECK(rlm_val_eq(value, object_id));
16!
2609
                CHECK(find(object_id_list.get(), &value));
16!
2610
                CHECK(realm_list_get(uuid_list.get(), 0, &value));
16!
2611
                CHECK(rlm_val_eq(value, uuid));
16!
2612
                CHECK(find(uuid_list.get(), &value));
16!
2613
                CHECK(realm_list_get(nullable_int_list.get(), 0, &value));
16!
2614
                CHECK(rlm_val_eq(value, integer));
16!
2615
                CHECK(find(nullable_int_list.get(), &value));
16!
2616
                CHECK(realm_list_get(nullable_bool_list.get(), 0, &value));
16!
2617
                CHECK(rlm_val_eq(value, boolean));
16!
2618
                CHECK(find(nullable_bool_list.get(), &value));
16!
2619
                CHECK(realm_list_get(nullable_string_list.get(), 0, &value));
16!
2620
                CHECK(rlm_val_eq(value, string));
16!
2621
                CHECK(find(nullable_string_list.get(), &value));
16!
2622
                CHECK(realm_list_get(nullable_binary_list.get(), 0, &value));
16!
2623
                CHECK(rlm_val_eq(value, binary));
16!
2624
                CHECK(find(nullable_binary_list.get(), &value));
16!
2625
                CHECK(realm_list_get(nullable_timestamp_list.get(), 0, &value));
16!
2626
                CHECK(rlm_val_eq(value, timestamp));
16!
2627
                CHECK(find(nullable_timestamp_list.get(), &value));
16!
2628
                CHECK(realm_list_get(nullable_float_list.get(), 0, &value));
16!
2629
                CHECK(rlm_val_eq(value, fnum));
16!
2630
                CHECK(find(nullable_float_list.get(), &value));
16!
2631
                CHECK(realm_list_get(nullable_double_list.get(), 0, &value));
16!
2632
                CHECK(rlm_val_eq(value, dnum));
16!
2633
                CHECK(find(nullable_double_list.get(), &value));
16!
2634
                CHECK(realm_list_get(nullable_decimal_list.get(), 0, &value));
10!
2635
                CHECK(rlm_val_eq(value, decimal));
16!
2636
                CHECK(find(nullable_decimal_list.get(), &value));
15!
2637
                CHECK(realm_list_get(nullable_object_id_list.get(), 0, &value));
16!
2638
                CHECK(rlm_val_eq(value, object_id));
16!
2639
                CHECK(find(nullable_object_id_list.get(), &value));
16!
2640
                CHECK(realm_list_get(nullable_uuid_list.get(), 0, &value));
16!
2641
                CHECK(rlm_val_eq(value, uuid));
16!
2642
                CHECK(find(nullable_uuid_list.get(), &value));
16!
2643

15!
2644
                write([&]() {
16!
2645
                    CHECK(realm_list_insert(nullable_int_list.get(), 0, null));
16!
2646
                    CHECK(realm_list_insert(nullable_bool_list.get(), 0, null));
16!
2647
                    CHECK(realm_list_insert(nullable_string_list.get(), 0, null));
10!
2648
                    CHECK(realm_list_insert(nullable_binary_list.get(), 0, null));
16!
2649
                    CHECK(realm_list_insert(nullable_timestamp_list.get(), 0, null));
15!
2650
                    CHECK(realm_list_insert(nullable_float_list.get(), 0, null));
16!
2651
                    CHECK(realm_list_insert(nullable_double_list.get(), 0, null));
16!
2652
                    CHECK(realm_list_insert(nullable_decimal_list.get(), 0, null));
16!
2653
                    CHECK(realm_list_insert(nullable_object_id_list.get(), 0, null));
16!
2654
                    CHECK(realm_list_insert(nullable_uuid_list.get(), 0, null));
16!
2655
                });
16!
2656

15!
2657
                CHECK(realm_list_get(nullable_int_list.get(), 0, &value));
16!
2658
                CHECK(rlm_val_eq(value, null));
16!
2659
                CHECK(realm_list_get(nullable_bool_list.get(), 0, &value));
16!
2660
                CHECK(rlm_val_eq(value, null));
16!
2661
                CHECK(realm_list_get(nullable_string_list.get(), 0, &value));
16!
2662
                CHECK(rlm_val_eq(value, null));
16!
2663
                CHECK(realm_list_get(nullable_binary_list.get(), 0, &value));
16!
2664
                CHECK(rlm_val_eq(value, null));
16!
2665
                CHECK(realm_list_get(nullable_timestamp_list.get(), 0, &value));
16!
2666
                CHECK(rlm_val_eq(value, null));
16!
2667
                CHECK(realm_list_get(nullable_float_list.get(), 0, &value));
16!
2668
                CHECK(rlm_val_eq(value, null));
16!
2669
                CHECK(realm_list_get(nullable_double_list.get(), 0, &value));
130!
2670
                CHECK(rlm_val_eq(value, null));
256!
2671
                CHECK(realm_list_get(nullable_decimal_list.get(), 0, &value));
35!
2672
                CHECK(rlm_val_eq(value, null));
56!
2673
                CHECK(realm_list_get(nullable_object_id_list.get(), 0, &value));
136✔
2674
                CHECK(rlm_val_eq(value, null));
124!
2675
                CHECK(realm_list_get(nullable_uuid_list.get(), 0, &value));
144✔
2676
                CHECK(rlm_val_eq(value, null));
34!
2677
            }
28✔
2678

35✔
2679
            SECTION("move") {
55✔
2680
                auto int_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["int_list"]));
136✔
2681
                write([&]() {
124!
2682
                    for (int i = 0; i < 10; ++i) {
164✔
2683
                        CHECK(realm_list_insert(int_list.get(), i, rlm_int_val(i)));
160!
2684
                    }
46!
2685
                });
34✔
2686

14!
2687
                realm_value_t value;
16✔
2688
                auto expected = std::vector<int64_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
10!
2689
                for (int i = 0; i < 10; ++i) {
36✔
2690
                    CHECK(realm_list_get(int_list.get(), i, &value));
153✔
2691
                    CHECK(rlm_val_eq(value, rlm_int_val(expected[i])));
142!
2692
                }
162✔
2693

141!
2694
                write([&]() {
28!
2695
                    CHECK(realm_list_move(int_list.get(), 0, 1));
34!
2696
                });
15!
2697

15✔
2698
                expected = std::vector<int64_t>{1, 0, 2, 3, 4, 5, 6, 7, 8, 9};
10!
2699
                for (int i = 0; i < 10; ++i) {
36✔
2700
                    CHECK(realm_list_get(int_list.get(), i, &value));
153✔
2701
                    CHECK(rlm_val_eq(value, rlm_int_val(expected[i])));
142!
2702
                }
162✔
2703

141!
2704
                write([&]() {
34!
2705
                    CHECK(realm_list_move(int_list.get(), 3, 2));
148!
2706
                });
256✔
2707

70✔
2708
                expected = std::vector<int64_t>{1, 0, 3, 2, 4, 5, 6, 7, 8, 9};
68✔
2709
                for (int i = 0; i < 10; ++i) {
78✔
2710
                    CHECK(realm_list_get(int_list.get(), i, &value));
72!
2711
                    CHECK(rlm_val_eq(value, rlm_int_val(expected[i])));
76!
2712
                }
76✔
2713
            }
58✔
2714

77✔
2715
            SECTION("links") {
98!
2716
                CPtr<realm_list_t> bars;
64!
2717

60!
2718
                write([&]() {
64!
2719
                    bars = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
64!
2720
                    auto bar_link = realm_object_as_link(obj2.get());
40!
2721
                    realm_value_t bar_link_val;
64!
2722
                    bar_link_val.type = RLM_TYPE_LINK;
60✔
2723
                    bar_link_val.link = bar_link;
64!
2724
                    CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
64!
2725
                    CHECK(checked(realm_list_insert(bars.get(), 1, bar_link_val)));
64!
2726
                    size_t size;
40!
2727
                    CHECK(checked(realm_list_size(bars.get(), &size)));
64!
2728
                    CHECK(size == 2);
60!
2729

60!
2730
                    bool found = true;
64!
2731
                    size_t index = -1;
40!
2732
                    CHECK(checked(realm_list_find(bars.get(), &bar_link_val, &index, &found)));
64!
2733
                    CHECK(index == 0);
60!
2734
                    CHECK(found);
64!
2735

36!
2736
                    realm_list_clear(bars.get());
64✔
2737
                    CHECK(checked(realm_list_find(bars.get(), &bar_link_val, &index, &found)));
24!
2738
                    CHECK(index == realm::not_found);
28!
2739
                    CHECK(!found);
22!
2740

18!
2741
                    CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
22!
2742
                    CHECK(checked(realm_list_insert(bars.get(), 1, bar_link_val)));
16!
2743
                });
22!
2744

17!
2745
                SECTION("get") {
22!
2746
                    realm_value_t val;
16!
2747
                    CHECK(checked(realm_list_get(bars.get(), 0, &val)));
10!
2748
                    CHECK(val.type == RLM_TYPE_LINK);
16!
2749
                    CHECK(val.link.target_table == class_bar.key);
15!
2750
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
16!
2751

15!
2752
                    CHECK(checked(realm_list_get(bars.get(), 1, &val)));
28!
2753
                    CHECK(val.type == RLM_TYPE_LINK);
52!
2754
                    CHECK(val.link.target_table == class_bar.key);
18!
2755
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
22!
2756

15!
2757
                    auto result = realm_list_get(bars.get(), 2, &val);
16✔
2758
                    CHECK(!result);
16!
2759
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
16!
2760
                }
10✔
2761

18!
2762
                SECTION("set wrong type") {
21!
2763
                    write([&]() {
16!
2764
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
16!
2765
                        CHECK(foo2);
28!
2766
                        realm_value_t foo2_link_val;
52✔
2767
                        foo2_link_val.type = RLM_TYPE_LINK;
18✔
2768
                        foo2_link_val.link = realm_object_as_link(foo2.get());
22!
2769

15✔
2770
                        CHECK(!realm_list_set(bars.get(), 0, foo2_link_val));
16!
2771
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
16!
2772
                    });
16!
2773
                }
10!
2774

18!
2775
                SECTION("realm_list_clear()") {
21!
2776
                    write([&]() {
16!
2777
                        CHECK(realm_list_clear(bars.get()));
16!
2778
                    });
28!
2779
                    size_t size;
52✔
2780
                    CHECK(realm_list_size(bars.get(), &size));
18!
2781
                    CHECK(size == 0);
22!
2782

9✔
2783
                    size_t num_bars;
16✔
2784
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
15!
2785
                    CHECK(num_bars != 0);
16!
2786
                }
10!
2787

18!
2788
                SECTION("realm_list_remove_all()") {
21!
2789
                    size_t num_bars;
10!
2790
                    size_t size;
16!
2791

14!
2792
                    write([&]() {
16!
2793
                        CHECK(checked(realm_list_remove_all(bars.get())));
52!
2794
                    });
130✔
2795

261✔
2796
                    CHECK(realm_list_size(bars.get(), &size));
35!
2797
                    CHECK(size == 0);
56!
2798

15✔
2799
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
16!
2800
                    CHECK(num_bars == 0);
16!
2801
                }
16!
2802
            }
16!
2803

35✔
2804
            SECTION("embedded") {
55✔
2805
                CPtr<realm_list_t> subs;
16!
2806
                realm_property_info_t info;
16✔
2807
                bool found = false;
16!
2808
                REQUIRE(checked(realm_find_property(realm, class_bar.key, "sub_list", &found, &info)));
16!
2809
                REQUIRE(found);
16!
2810
                subs = cptr_checked(realm_get_list(obj2.get(), info.key));
130!
2811

255✔
2812
                write([&]() {
131✔
2813
                    auto embedded = cptr_checked(realm_list_insert_embedded(subs.get(), 0));
152✔
2814
                    CHECK(embedded);
128!
2815
                });
128✔
2816
                auto embedded = cptr_checked(realm_list_get_linked_object(subs.get(), 0));
128✔
2817
                CHECK(embedded);
128!
2818
            }
74✔
2819

147✔
2820
            SECTION("notifications") {
105✔
2821
                struct State {
192✔
2822
                    CPtr<realm_collection_changes_t> changes;
183✔
2823
                    CPtr<realm_async_error_t> error;
200✔
2824
                    bool destroyed = false;
200✔
2825
                    bool called = false;
200✔
2826
                };
98✔
2827

143✔
2828
                State state;
81✔
2829

135✔
2830
                auto on_change = [](void* userdata, const realm_collection_changes_t* changes) {
143✔
2831
                    auto* state = static_cast<State*>(userdata);
152✔
2832
                    state->changes = clone_cptr(changes);
98✔
2833
                    state->called = true;
110✔
2834
                };
59✔
2835

44✔
2836
                CPtr<realm_list_t> strings = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
46✔
2837

37✔
2838
                auto str1 = rlm_str_val("a");
46✔
2839
                auto str2 = rlm_str_val("b");
76✔
2840
                auto null = rlm_null();
130✔
2841

30✔
2842
                auto require_change = [&]() {
41✔
2843
                    auto token = cptr_checked(
18✔
2844
                        realm_list_add_notification_callback(strings.get(), &state, nullptr, nullptr, on_change));
18✔
2845
                    checked(realm_refresh(realm, nullptr));
18✔
2846
                    return token;
18✔
2847
                };
18!
2848

23✔
2849
                SECTION("userdata is freed when the token is destroyed") {
32!
2850
                    auto token = cptr_checked(realm_list_add_notification_callback(
16✔
2851
                        strings.get(), &state,
58!
2852
                        [](void* p) {
112✔
2853
                            static_cast<State*>(p)->destroyed = true;
23✔
2854
                        },
32✔
2855
                        nullptr, nullptr));
16✔
2856
                    CHECK(!state.destroyed);
16!
2857
                    token.reset();
16✔
2858
                    CHECK(state.destroyed);
16!
2859
                }
16!
2860

23!
2861
                SECTION("insertion sends a change callback") {
26!
2862
                    auto token = require_change();
16!
2863
                    write([&]() {
15✔
2864
                        checked(realm_list_insert(strings.get(), 0, str1));
16✔
2865
                        checked(realm_list_insert(strings.get(), 1, str2));
16✔
2866
                        checked(realm_list_insert(strings.get(), 2, null));
16!
2867
                    });
16!
2868
                    CHECK(!state.error);
16!
2869
                    CHECK(state.changes);
16!
2870

9!
2871
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
16!
2872
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
15✔
2873
                                                            &num_insertion_ranges, &num_modification_ranges,
16✔
2874
                                                            &num_moves);
16!
2875
                    CHECK(num_deletion_ranges == 0);
16!
2876
                    CHECK(num_insertion_ranges == 1);
16!
2877
                    CHECK(num_modification_ranges == 0);
58!
2878
                    CHECK(num_moves == 0);
112!
2879

82✔
2880
                    realm_index_range_t insertion_range;
92✔
2881
                    realm_collection_changes_get_ranges(state.changes.get(), nullptr, 0, &insertion_range, 1, nullptr,
86✔
2882
                                                        0, nullptr, 0, nullptr, 0);
86✔
2883
                    CHECK(insertion_range.from == 0);
86!
2884
                    CHECK(insertion_range.to == 3);
86!
2885
                }
86!
2886

93✔
2887
                SECTION("modifying target of list with a filter") {
66!
2888
                    auto bars = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
96✔
2889
                    write([&]() {
30✔
2890
                        auto bar_link = realm_object_as_link(obj2.get());
36✔
2891
                        realm_value_t bar_link_val;
26!
2892
                        bar_link_val.type = RLM_TYPE_LINK;
26✔
2893
                        bar_link_val.link = bar_link;
26!
2894
                        CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
26!
2895
                    });
26✔
2896

14✔
2897
                    SECTION("using valid key") {
26✔
2898
                        const char* bar_strings[1] = {"doubles"};
15✔
2899
                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
16✔
2900
                        REQUIRE(key_path_array);
16!
2901
                        auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
16!
2902
                                                                                       key_path_array, on_change));
16!
2903
                        realm_release(key_path_array);
16!
2904
                        checked(realm_refresh(realm, nullptr));
10!
2905

15!
2906
                        state.called = false;
15✔
2907
                        write([&]() {
16✔
2908
                            checked(realm_set_value(obj2.get(), bar_doubles_key, rlm_double_val(5.0), false));
16✔
2909
                        });
16✔
2910
                        REQUIRE(state.called);
16!
2911
                        CHECK(!state.error);
16!
2912
                        CHECK(state.changes);
16!
2913

75!
2914
                        state.called = false;
16✔
2915
                        write([&]() {
26✔
2916
                            checked(realm_list_insert(strings.get(), 0, str1));
16!
2917
                            checked(realm_list_insert(strings.get(), 1, str2));
16✔
2918
                            checked(realm_list_insert(strings.get(), 2, null));
16!
2919
                        });
76✔
2920
                        REQUIRE(!state.called);
28!
2921
                    }
38✔
2922
                    SECTION("using invalid key") {
40✔
2923
                        const char* bar_strings[1] = {"dobles"};
30✔
2924
                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
30✔
2925
                        REQUIRE(!key_path_array);
30!
2926
                        realm_clear_last_error();
30✔
2927
                    }
30✔
2928
                    SECTION("Embedded objects") {
40✔
2929
                        realm_property_info_t info;
32✔
2930
                        bool found = false;
32✔
2931
                        realm_key_path_array_t* key_path_array = nullptr;
20✔
2932
                        realm_find_property(realm, class_bar.key, "sub", &found, &info);
32✔
2933
                        auto bar_sub_key = info.key;
12✔
2934
                        realm_find_property(realm, class_embedded.key, "int", &found, &info);
20✔
2935
                        auto embedded_int_key = info.key;
17✔
2936
                        CPtr<realm_object_t> embedded;
18!
2937
                        write([&]() {
18✔
2938
                            embedded = cptr_checked(realm_set_embedded(obj2.get(), bar_sub_key));
30!
2939
                        });
18✔
2940

18✔
2941
                        SECTION("using valid nesting") {
12✔
2942

15✔
2943
                            const char* bar_strings[1] = {"sub.int"};
15✔
2944
                            key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
28!
2945
                            REQUIRE(key_path_array);
28!
2946
                        }
30!
2947
                        SECTION("using star notation") {
32✔
2948
                            const char* bar_strings[1] = {"*.int"};
18✔
2949
                            key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
30✔
2950
                            // (*realm)->print_key_path_array(*key_path_array);
15✔
2951
                        }
30✔
2952

28✔
2953
                        REQUIRE(key_path_array);
32!
2954
                        auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
32✔
2955
                                                                                       key_path_array, on_change));
32!
2956
                        realm_release(key_path_array);
32!
2957

30!
2958
                        checked(realm_refresh(realm, nullptr));
32!
2959

78!
2960
                        state.called = false;
20✔
2961
                        write([&]() {
28✔
2962
                            checked(realm_set_value(embedded.get(), embedded_int_key, rlm_int_val(999), false));
18!
2963
                        });
18✔
2964
                        REQUIRE(state.called);
18!
2965
                        CHECK(!state.error);
18!
2966
                        CHECK(state.changes);
18!
2967
                    }
12✔
2968
                    SECTION("using backlink") {
26✔
2969
                        const char* bar_strings[1] = {"linking_objects.public_int"};
15✔
2970
                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
16✔
2971
                        REQUIRE(key_path_array);
16!
2972
                        auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
16!
2973
                                                                                       key_path_array, on_change));
16!
2974
                        realm_release(key_path_array);
16!
2975
                        checked(realm_refresh(realm, nullptr));
16!
2976

75!
2977
                        state.called = false;
16✔
2978
                        write([&]() {
26✔
2979
                            checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
16!
2980
                        });
16✔
2981
                        REQUIRE(state.called);
16!
2982
                        CHECK(!state.error);
76!
2983
                        CHECK(state.changes);
58!
2984
                    }
122✔
2985
                    SECTION("using invalid nesting") {
33✔
2986
                        const char* bar_strings[1] = {"doubles.age"};
32✔
2987
                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
16✔
2988
                        REQUIRE(!key_path_array);
16!
2989
                        realm_clear_last_error();
16✔
2990
                    }
10✔
2991
                }
26✔
2992

16✔
2993
                SECTION("insertion, deletion, modification, modification after") {
32✔
2994
                    write([&]() {
15✔
2995
                        checked(realm_list_insert(strings.get(), 0, str1));
16✔
2996
                        checked(realm_list_insert(strings.get(), 1, str2));
16✔
2997
                        checked(realm_list_insert(strings.get(), 2, str1));
10✔
2998
                    });
10✔
2999

14✔
3000
                    auto token = require_change();
15✔
3001

15!
3002
                    write([&]() {
16!
3003
                        checked(realm_list_erase(strings.get(), 1));
10!
3004
                        checked(realm_list_insert(strings.get(), 0, null));
16!
3005
                        checked(realm_list_insert(strings.get(), 1, null));
15✔
3006

15✔
3007
                        // This element was previously at 0, and ends up at 2.
15✔
3008
                        checked(realm_list_set(strings.get(), 2, str1));
16!
3009
                    });
16!
3010
                    CHECK(!state.error);
16!
3011
                    CHECK(state.changes);
16!
3012

9!
3013
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
16!
3014
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
15✔
3015
                                                            &num_insertion_ranges, &num_modification_ranges,
16✔
3016
                                                            &num_moves);
16✔
3017
                    CHECK(num_deletion_ranges == 1);
16!
3018
                    CHECK(num_insertion_ranges == 1);
16!
3019
                    CHECK(num_modification_ranges == 1);
16!
3020
                    CHECK(num_moves == 0);
16!
3021

15!
3022
                    size_t num_deletions, num_insertions, num_modifications;
10!
3023
                    bool collection_cleared = false;
16!
3024
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
15✔
3025
                                                             &num_modifications, &num_moves, &collection_cleared,
16✔
3026
                                                             nullptr);
16✔
3027
                    CHECK(num_deletions == 1);
16!
3028
                    CHECK(num_insertions == 2);
16!
3029
                    CHECK(num_modifications == 1);
10!
3030
                    CHECK(collection_cleared == false);
16!
3031

14!
3032
                    realm_index_range_t deletions, insertions, modifications, modifications_after;
10!
3033
                    realm_collection_move_t moves;
16!
3034
                    realm_collection_changes_get_ranges(state.changes.get(), &deletions, 1, &insertions, 1,
15!
3035
                                                        &modifications, 1, &modifications_after, 1, &moves, 1);
10!
3036
                    CHECK(deletions.from == 1);
16!
3037
                    CHECK(deletions.to == 2);
15!
3038

9!
3039
                    CHECK(insertions.from == 0);
16!
3040
                    CHECK(insertions.to == 2);
15!
3041

15✔
3042
                    CHECK(modifications.from == 0);
16!
3043
                    CHECK(modifications.to == 1);
16!
3044

15✔
3045
                    CHECK(modifications_after.from == 2);
16!
3046
                    CHECK(modifications_after.to == 3);
16!
3047

15✔
3048
                    std::vector<size_t> deletions_v, insertions_v, modifications_v, modifications_after_v;
16✔
3049
                    std::vector<realm_collection_move_t> moves_v;
16!
3050
                    deletions_v.resize(100, size_t(-1));
16!
3051
                    insertions_v.resize(100, size_t(-1));
16!
3052
                    modifications_v.resize(100, size_t(-1));
16!
3053
                    modifications_after_v.resize(100, size_t(-1));
16!
3054
                    moves_v.resize(100, realm_collection_move_t{size_t(-1), size_t(-1)});
16!
3055
                    realm_collection_changes_get_changes(state.changes.get(), deletions_v.data(), 100,
16!
3056
                                                         insertions_v.data(), 100, modifications_v.data(), 100,
16!
3057
                                                         modifications_after_v.data(), 100, moves_v.data(), 100);
16!
3058
                    CHECK(deletions_v[0] == 1);
10!
3059
                    CHECK(deletions_v[1] == size_t(-1));
16!
3060
                    CHECK(insertions_v[0] == 0);
15!
3061
                    CHECK(insertions_v[1] == 1);
16!
3062
                    CHECK(insertions_v[2] == size_t(-1));
10!
3063
                    CHECK(modifications_v[0] == 0);
16!
3064
                    CHECK(modifications_v[1] == size_t(-1));
15!
3065
                    CHECK(modifications_after_v[0] == 2);
16!
3066
                    CHECK(modifications_after_v[1] == size_t(-1));
16!
3067

15✔
3068
                    write([&]() {
112!
3069
                        checked(realm_list_clear(strings.get()));
256✔
3070
                    });
416✔
3071

835✔
3072
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
224✔
3073
                                                             &num_modifications, &num_moves, &collection_cleared,
146!
3074
                                                             nullptr);
40!
3075
                    CHECK(collection_cleared == true);
10!
3076
                }
16!
3077
            }
31!
3078
        }
56!
3079

146!
3080
        SECTION("sets") {
290✔
3081
            SECTION("realm_get_set() errors") {
99✔
3082
                CHECK(!realm_get_set(obj1.get(), foo_properties["int"]));
88!
3083
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
72!
3084

41!
3085
                CHECK(!realm_get_set(obj1.get(), 123123123));
72!
3086
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
67!
3087
            }
72✔
3088

53✔
3089
            SECTION("nullable strings") {
96✔
3090
                auto strings = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
27!
3091
                CHECK(strings);
32!
3092
                CHECK(!realm_is_frozen(strings.get()));
42!
3093

67✔
3094
                realm_value_t a = rlm_str_val("a");
27✔
3095
                realm_value_t b = rlm_str_val("b");
32!
3096
                realm_value_t c = rlm_null();
24!
3097

19!
3098
                SECTION("realm_equals() type check") {
42!
3099
                    CHECK(!realm_equals(strings.get(), obj1.get()));
64!
3100
                }
19✔
3101

27✔
3102
                SECTION("realm_clone()") {
24!
3103
                    auto set2 = clone_cptr(strings.get());
16!
3104
                    CHECK(realm_equals(strings.get(), set2.get()));
16!
3105
                    CHECK(strings.get() != set2.get());
16!
3106
                }
16!
3107

19!
3108
                SECTION("insert, then get, then erase") {
18!
3109
                    write([&]() {
16!
3110
                        bool inserted = false;
15✔
3111
                        CHECK(checked(realm_set_insert(strings.get(), a, nullptr, &inserted)));
16!
3112
                        CHECK(inserted);
16!
3113
                        CHECK(checked(realm_set_insert(strings.get(), b, nullptr, &inserted)));
16!
3114
                        CHECK(inserted);
16!
3115
                        CHECK(checked(realm_set_insert(strings.get(), c, nullptr, &inserted)));
16!
3116
                        CHECK(inserted);
16!
3117

9!
3118
                        size_t a_index, b_index, c_index;
16!
3119
                        bool found = false;
15!
3120
                        CHECK(checked(realm_set_find(strings.get(), a, &a_index, &found)));
16!
3121
                        CHECK(found);
16!
3122
                        CHECK(checked(realm_set_find(strings.get(), b, &b_index, &found)));
10!
3123
                        CHECK(found);
16!
3124
                        CHECK(checked(realm_set_find(strings.get(), c, &c_index, &found)));
15!
3125
                        CHECK(found);
16!
3126

9!
3127
                        realm_value_t a2, b2, c2;
16!
3128
                        CHECK(checked(realm_set_get(strings.get(), a_index, &a2)));
15!
3129
                        CHECK(checked(realm_set_get(strings.get(), b_index, &b2)));
16!
3130
                        CHECK(checked(realm_set_get(strings.get(), c_index, &c2)));
16!
3131

15!
3132
                        CHECK(rlm_stdstr(a2) == "a");
16!
3133
                        CHECK(rlm_stdstr(b2) == "b");
16!
3134
                        CHECK(c2.type == RLM_TYPE_NULL);
34!
3135

63✔
3136
                        bool erased = false;
19✔
3137
                        CHECK(checked(realm_set_erase(strings.get(), a2, &erased)));
24!
3138
                        CHECK(erased);
16!
3139
                        CHECK(checked(realm_set_erase(strings.get(), rlm_int_val(987), &erased)));
10!
3140
                        CHECK(!erased);
16!
3141
                    });
15✔
3142
                }
16!
3143

19✔
3144
                SECTION("equality") {
24!
3145
                    auto strings2 = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
16!
3146
                    CHECK(strings2);
16!
3147
                    CHECK(realm_equals(strings.get(), strings2.get()));
16!
3148

33✔
3149
                    write([&]() {
64✔
3150
                        auto obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
19✔
3151
                        CHECK(obj3);
24!
3152
                        auto strings3 =
16✔
3153
                            cptr_checked(realm_get_set(obj3.get(), foo_properties["nullable_string_set"]));
16!
3154
                        CHECK(!realm_equals(strings.get(), strings3.get()));
64!
3155
                    });
82✔
3156
                }
168✔
3157

30✔
3158
                SECTION("parse query for sets") {
48✔
3159
                    auto links = cptr_checked(realm_get_set(obj1.get(), foo_properties["link_set"]));
16✔
3160
                    CHECK(links);
16!
3161
                    cptr_checked(realm_query_parse_for_set(links.get(), "TRUEPREDICATE", 0, nullptr));
16✔
3162
                }
16✔
3163
            }
24✔
3164

27✔
3165
            SECTION("get/insert all property types") {
40✔
3166
                realm_value_t null = rlm_null();
16✔
3167
                realm_value_t integer = rlm_int_val(987);
16✔
3168
                realm_value_t boolean = rlm_bool_val(true);
16✔
3169
                realm_value_t string = rlm_str_val("My string");
10✔
3170
                const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
16✔
3171
                realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
15✔
3172
                realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
16✔
3173
                realm_value_t fnum = rlm_float_val(123.f);
16✔
3174
                realm_value_t dnum = rlm_double_val(456.0);
16✔
3175
                realm_value_t decimal = rlm_decimal_val(999.0);
16✔
3176
                realm_value_t object_id = rlm_object_id_val("abc123abc123");
16✔
3177
                realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16✔
3178

15✔
3179
                auto int_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["int_set"]));
16✔
3180
                auto bool_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["bool_set"]));
16✔
3181
                auto string_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["string_set"]));
16✔
3182
                auto binary_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["binary_set"]));
16✔
3183
                auto timestamp_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["timestamp_set"]));
16✔
3184
                auto float_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["float_set"]));
16✔
3185
                auto double_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["double_set"]));
16✔
3186
                auto decimal_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["decimal_set"]));
16✔
3187
                auto object_id_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["object_id_set"]));
16✔
3188
                auto uuid_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["uuid_set"]));
16✔
3189
                auto nullable_int_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_int_set"]));
16✔
3190
                auto nullable_bool_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_bool_set"]));
16✔
3191
                auto nullable_string_set =
16✔
3192
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
16✔
3193
                auto nullable_binary_set =
16✔
3194
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_binary_set"]));
16✔
3195
                auto nullable_timestamp_set =
16✔
3196
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_timestamp_set"]));
16✔
3197
                auto nullable_float_set =
10✔
3198
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_float_set"]));
16✔
3199
                auto nullable_double_set =
15!
3200
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_double_set"]));
16!
3201
                auto nullable_decimal_set =
16!
3202
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_decimal_set"]));
16!
3203
                auto nullable_object_id_set =
16!
3204
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_object_id_set"]));
16!
3205
                auto nullable_uuid_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_uuid_set"]));
16!
3206

15!
3207
                write([&]() {
16!
3208
                    CHECK(realm_set_insert(int_set.get(), integer, nullptr, nullptr));
16!
3209
                    CHECK(realm_set_insert(bool_set.get(), boolean, nullptr, nullptr));
10!
3210
                    CHECK(realm_set_insert(string_set.get(), string, nullptr, nullptr));
16!
3211
                    CHECK(realm_set_insert(binary_set.get(), binary, nullptr, nullptr));
15!
3212
                    CHECK(realm_set_insert(timestamp_set.get(), timestamp, nullptr, nullptr));
16!
3213
                    CHECK(realm_set_insert(float_set.get(), fnum, nullptr, nullptr));
16!
3214
                    CHECK(realm_set_insert(double_set.get(), dnum, nullptr, nullptr));
16!
3215
                    CHECK(realm_set_insert(decimal_set.get(), decimal, nullptr, nullptr));
16!
3216
                    CHECK(realm_set_insert(object_id_set.get(), object_id, nullptr, nullptr));
16!
3217
                    CHECK(realm_set_insert(uuid_set.get(), uuid, nullptr, nullptr));
16!
3218

15!
3219
                    CHECK(realm_set_insert(nullable_int_set.get(), integer, nullptr, nullptr));
16!
3220
                    CHECK(realm_set_insert(nullable_bool_set.get(), boolean, nullptr, nullptr));
10!
3221
                    CHECK(realm_set_insert(nullable_string_set.get(), string, nullptr, nullptr));
16!
3222
                    CHECK(realm_set_insert(nullable_binary_set.get(), binary, nullptr, nullptr));
15!
3223
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), timestamp, nullptr, nullptr));
16!
3224
                    CHECK(realm_set_insert(nullable_float_set.get(), fnum, nullptr, nullptr));
16!
3225
                    CHECK(realm_set_insert(nullable_double_set.get(), dnum, nullptr, nullptr));
16!
3226
                    CHECK(realm_set_insert(nullable_decimal_set.get(), decimal, nullptr, nullptr));
16!
3227
                    CHECK(realm_set_insert(nullable_object_id_set.get(), object_id, nullptr, nullptr));
16!
3228
                    CHECK(realm_set_insert(nullable_uuid_set.get(), uuid, nullptr, nullptr));
16!
3229

15!
3230
                    CHECK(realm_set_insert(nullable_int_set.get(), null, nullptr, nullptr));
16!
3231
                    CHECK(realm_set_insert(nullable_bool_set.get(), null, nullptr, nullptr));
16!
3232
                    CHECK(realm_set_insert(nullable_string_set.get(), null, nullptr, nullptr));
10!
3233
                    CHECK(realm_set_insert(nullable_binary_set.get(), null, nullptr, nullptr));
16!
3234
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), null, nullptr, nullptr));
9!
3235
                    CHECK(realm_set_insert(nullable_float_set.get(), null, nullptr, nullptr));
16!
3236
                    CHECK(realm_set_insert(nullable_double_set.get(), null, nullptr, nullptr));
15!
3237
                    CHECK(realm_set_insert(nullable_decimal_set.get(), null, nullptr, nullptr));
16!
3238
                    CHECK(realm_set_insert(nullable_object_id_set.get(), null, nullptr, nullptr));
16!
3239
                    CHECK(realm_set_insert(nullable_uuid_set.get(), null, nullptr, nullptr));
16!
3240
                });
16!
3241

15!
3242
                realm_value_t value;
16!
3243

15!
3244
                CHECK(realm_set_get(int_set.get(), 0, &value));
16!
3245
                CHECK(rlm_val_eq(value, integer));
16!
3246
                CHECK(realm_set_get(bool_set.get(), 0, &value));
16!
3247
                CHECK(rlm_val_eq(value, boolean));
16!
3248
                CHECK(realm_set_get(string_set.get(), 0, &value));
16!
3249
                CHECK(rlm_val_eq(value, string));
16!
3250
                CHECK(realm_set_get(binary_set.get(), 0, &value));
16!
3251
                CHECK(rlm_val_eq(value, binary));
16!
3252
                CHECK(realm_set_get(timestamp_set.get(), 0, &value));
16!
3253
                CHECK(rlm_val_eq(value, timestamp));
16!
3254
                CHECK(realm_set_get(float_set.get(), 0, &value));
16!
3255
                CHECK(rlm_val_eq(value, fnum));
16!
3256
                CHECK(realm_set_get(double_set.get(), 0, &value));
16!
3257
                CHECK(rlm_val_eq(value, dnum));
16!
3258
                CHECK(realm_set_get(decimal_set.get(), 0, &value));
16!
3259
                CHECK(rlm_val_eq(value, decimal));
16!
3260
                CHECK(realm_set_get(object_id_set.get(), 0, &value));
16!
3261
                CHECK(rlm_val_eq(value, object_id));
16!
3262
                CHECK(realm_set_get(uuid_set.get(), 0, &value));
16!
3263
                CHECK(rlm_val_eq(value, uuid));
16!
3264
                CHECK(realm_set_get(nullable_int_set.get(), 1, &value));
16!
3265
                CHECK(rlm_val_eq(value, integer));
16!
3266
                CHECK(realm_set_get(nullable_bool_set.get(), 1, &value));
16!
3267
                CHECK(rlm_val_eq(value, boolean));
16!
3268
                CHECK(realm_set_get(nullable_string_set.get(), 1, &value));
16!
3269
                CHECK(rlm_val_eq(value, string));
16!
3270
                CHECK(realm_set_get(nullable_binary_set.get(), 1, &value));
16!
3271
                CHECK(rlm_val_eq(value, binary));
16!
3272
                CHECK(realm_set_get(nullable_timestamp_set.get(), 1, &value));
16!
3273
                CHECK(rlm_val_eq(value, timestamp));
16!
3274
                CHECK(realm_set_get(nullable_float_set.get(), 1, &value));
16!
3275
                CHECK(rlm_val_eq(value, fnum));
10!
3276
                CHECK(realm_set_get(nullable_double_set.get(), 1, &value));
16!
3277
                CHECK(rlm_val_eq(value, dnum));
15!
3278
                CHECK(realm_set_get(nullable_decimal_set.get(), 1, &value));
16!
3279
                CHECK(rlm_val_eq(value, decimal));
16!
3280
                CHECK(realm_set_get(nullable_object_id_set.get(), 1, &value));
16!
3281
                CHECK(rlm_val_eq(value, object_id));
16!
3282
                CHECK(realm_set_get(nullable_uuid_set.get(), 1, &value));
16!
3283
                CHECK(rlm_val_eq(value, uuid));
16!
3284

15!
3285
                write([&]() {
16!
3286
                    size_t index;
16!
3287
                    bool inserted;
16!
3288
                    CHECK(realm_set_insert(nullable_int_set.get(), null, &index, &inserted));
16!
3289
                    CHECK((index == 0 && !inserted));
16!
3290
                    CHECK(realm_set_insert(nullable_bool_set.get(), null, &index, &inserted));
16!
3291
                    CHECK((index == 0 && !inserted));
16!
3292
                    CHECK(realm_set_insert(nullable_string_set.get(), null, &index, &inserted));
16!
3293
                    CHECK((index == 0 && !inserted));
16!
3294
                    CHECK(realm_set_insert(nullable_binary_set.get(), null, &index, &inserted));
16!
3295
                    CHECK((index == 0 && !inserted));
16!
3296
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), null, &index, &inserted));
16!
3297
                    CHECK((index == 0 && !inserted));
16!
3298
                    CHECK(realm_set_insert(nullable_float_set.get(), null, &index, &inserted));
16!
3299
                    CHECK((index == 0 && !inserted));
16!
3300
                    CHECK(realm_set_insert(nullable_double_set.get(), null, &index, &inserted));
10!
3301
                    CHECK((index == 0 && !inserted));
10!
3302
                    CHECK(realm_set_insert(nullable_decimal_set.get(), null, &index, &inserted));
9!
3303
                    CHECK((index == 0 && !inserted));
15!
3304
                    CHECK(realm_set_insert(nullable_object_id_set.get(), null, &index, &inserted));
15!
3305
                    CHECK((index == 0 && !inserted));
16!
3306
                    CHECK(realm_set_insert(nullable_uuid_set.get(), null, &index, &inserted));
16!
3307
                    CHECK((index == 0 && !inserted));
16!
3308
                });
16!
3309

15!
3310
                // Note: This relies on the fact that NULL is "less than" other
15!
3311
                // values in the internal sort order.
15!
3312
                CHECK(realm_set_get(nullable_int_set.get(), 0, &value));
16!
3313
                CHECK(rlm_val_eq(value, null));
16!
3314
                CHECK(realm_set_get(nullable_bool_set.get(), 0, &value));
16!
3315
                CHECK(rlm_val_eq(value, null));
16!
3316
                CHECK(realm_set_get(nullable_string_set.get(), 0, &value));
16!
3317
                CHECK(rlm_val_eq(value, null));
16!
3318
                CHECK(realm_set_get(nullable_binary_set.get(), 0, &value));
16!
3319
                CHECK(rlm_val_eq(value, null));
16!
3320
                CHECK(realm_set_get(nullable_timestamp_set.get(), 0, &value));
16!
3321
                CHECK(rlm_val_eq(value, null));
16!
3322
                CHECK(realm_set_get(nullable_float_set.get(), 0, &value));
16!
3323
                CHECK(rlm_val_eq(value, null));
16!
3324
                CHECK(realm_set_get(nullable_double_set.get(), 0, &value));
82!
3325
                CHECK(rlm_val_eq(value, null));
160!
3326
                CHECK(realm_set_get(nullable_decimal_set.get(), 0, &value));
63!
3327
                CHECK(rlm_val_eq(value, null));
52!
3328
                CHECK(realm_set_get(nullable_object_id_set.get(), 0, &value));
58!
3329
                CHECK(rlm_val_eq(value, null));
54!
3330
                CHECK(realm_set_get(nullable_uuid_set.get(), 0, &value));
58!
3331
                CHECK(rlm_val_eq(value, null));
58!
3332
            }
58✔
3333

69✔
3334
            SECTION("links") {
82✔
3335
                CPtr<realm_set_t> bars;
64✔
3336

60!
3337
                write([&]() {
64!
3338
                    bars = cptr_checked(realm_get_set(obj1.get(), foo_properties["link_set"]));
64!
3339
                    auto bar_link = realm_object_as_link(obj2.get());
64!
3340
                    realm_value_t bar_link_val;
64!
3341
                    bar_link_val.type = RLM_TYPE_LINK;
64!
3342
                    bar_link_val.link = bar_link;
64!
3343
                    size_t index;
40!
3344
                    bool inserted;
64!
3345
                    CHECK(checked(realm_set_insert(bars.get(), bar_link_val, &index, &inserted)));
60!
3346
                    CHECK((index == 0 && inserted));
64!
3347
                    CHECK(checked(realm_set_insert(bars.get(), bar_link_val, &index, &inserted)));
64!
3348
                    CHECK((index == 0 && !inserted));
64!
3349
                    size_t size;
64!
3350
                    CHECK(checked(realm_set_size(bars.get(), &size)));
64!
3351
                    CHECK(size == 1);
64!
3352

60!
3353
                    auto results =
64!
3354
                        cptr_checked(realm_get_backlinks(obj2.get(), class_foo.key, foo_properties["link_set"]));
40!
3355
                    CHECK(results->size() == 1);
64!
3356
                    auto mixed_link = results->get_any(0);
24✔
3357
                    CHECK(!mixed_link.is_unresolved_link());
28!
3358
                    CHECK(mixed_link.is_type(type_TypedLink));
22!
3359
                    auto link = mixed_link.get_link();
22!
3360
                    CHECK(link.get_obj_key() == obj1->get_obj().get_key());
22!
3361
                    CHECK(link.get_table_key() == obj1->get_obj().get_table()->get_key());
16!
3362
                });
22!
3363

17!
3364
                SECTION("get") {
22!
3365
                    realm_value_t val;
16!
3366
                    CHECK(checked(realm_set_get(bars.get(), 0, &val)));
28!
3367
                    CHECK(val.type == RLM_TYPE_LINK);
52!
3368
                    CHECK(val.link.target_table == class_bar.key);
18!
3369
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
22!
3370

15!
3371
                    auto result = realm_set_get(bars.get(), 1, &val);
16✔
3372
                    CHECK(!result);
16!
3373
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
16!
3374
                }
10✔
3375

18!
3376
                SECTION("insert wrong type") {
21!
3377
                    write([&]() {
16!
3378
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
16!
3379
                        CHECK(foo2);
28!
3380
                        realm_value_t foo2_link_val;
52✔
3381
                        foo2_link_val.type = RLM_TYPE_LINK;
18✔
3382
                        foo2_link_val.link = realm_object_as_link(foo2.get());
22!
3383

15✔
3384
                        CHECK(!realm_set_insert(bars.get(), foo2_link_val, nullptr, nullptr));
16!
3385
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
16!
3386
                    });
16!
3387
                }
10!
3388

18!
3389
                SECTION("realm_set_clear()") {
21!
3390
                    write([&]() {
16!
3391
                        CHECK(realm_set_clear(bars.get()));
16!
3392
                    });
28!
3393
                    size_t size;
52✔
3394
                    CHECK(realm_set_size(bars.get(), &size));
18!
3395
                    CHECK(size == 0);
22!
3396

15!
3397
                    size_t num_bars;
16!
3398
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
16!
3399
                    CHECK(num_bars != 0);
10!
3400
                }
16!
3401

17✔
3402
                SECTION("realm_set_remove_all()") {
16✔
3403
                    realm_value_t val;
16✔
3404
                    CHECK(checked(realm_set_get(bars.get(), 0, &val)));
15!
3405
                    CHECK(val.type == RLM_TYPE_LINK);
16!
3406
                    CHECK(val.link.target_table == class_bar.key);
10!
3407
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
16!
3408

14!
3409
                    size_t num_bars;
10!
3410
                    size_t size;
16!
3411

14!
3412
                    write([&]() {
16!
3413
                        CHECK(checked(realm_set_remove_all(bars.get())));
52!
3414
                    });
82✔
3415

165✔
3416
                    CHECK(realm_set_size(bars.get(), &size));
39!
3417
                    CHECK(size == 0);
52!
3418

29✔
3419
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
30!
3420
                    CHECK(num_bars == 0);
30!
3421
                }
18✔
3422
            }
36✔
3423

27✔
3424
            SECTION("notifications") {
66✔
3425
                struct State {
42✔
3426
                    CPtr<realm_collection_changes_t> changes;
46✔
3427
                    CPtr<realm_async_error_t> error;
46✔
3428
                    bool destroyed = false;
22✔
3429
                };
34✔
3430

28✔
3431
                State state;
20✔
3432

30✔
3433
                auto on_change = [](void* userdata, const realm_collection_changes_t* changes) {
32✔
3434
                    auto* state = static_cast<State*>(userdata);
34✔
3435
                    state->changes = clone_cptr(changes);
22✔
3436
                };
28✔
3437

16✔
3438
                CPtr<realm_set_t> strings =
19✔
3439
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
18✔
3440

16✔
3441
                auto str1 = rlm_str_val("a");
18✔
3442
                auto str2 = rlm_str_val("b");
18✔
3443
                auto null = rlm_null();
30✔
3444

16✔
3445
                auto require_change = [&]() {
19✔
3446
                    auto token = cptr_checked(
16✔
3447
                        realm_set_add_notification_callback(strings.get(), &state, nullptr, nullptr, on_change));
16✔
3448
                    checked(realm_refresh(realm, nullptr));
16✔
3449
                    return token;
16✔
3450
                };
16!
3451

16✔
3452
                SECTION("userdata is freed when the token is destroyed") {
18!
3453
                    auto token = cptr_checked(realm_set_add_notification_callback(
16✔
3454
                        strings.get(), &state,
16!
3455
                        [](void* p) {
28✔
3456
                            static_cast<State*>(p)->destroyed = true;
16✔
3457
                        },
18✔
3458
                        nullptr, nullptr));
16✔
3459
                    CHECK(!state.destroyed);
10!
3460
                    token.reset();
16✔
3461
                    CHECK(state.destroyed);
15!
3462
                }
16✔
3463

16✔
3464
                SECTION("insertion,deletion sends a change callback") {
18✔
3465
                    write([&]() {
16✔
3466
                        checked(realm_set_insert(strings.get(), str1, nullptr, nullptr));
16!
3467
                    });
16!
3468

9!
3469
                    auto token = require_change();
16!
3470
                    write([&]() {
15✔
3471
                        checked(realm_set_erase(strings.get(), str1, nullptr));
16✔
3472
                        checked(realm_set_insert(strings.get(), str2, nullptr, nullptr));
16✔
3473
                        checked(realm_set_insert(strings.get(), null, nullptr, nullptr));
16!
3474
                    });
16!
3475
                    CHECK(!state.error);
16!
3476
                    CHECK(state.changes);
16!
3477

9!
3478
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
16!
3479
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
15✔
3480
                                                            &num_insertion_ranges, &num_modification_ranges,
16✔
3481
                                                            &num_moves);
16!
3482
                    CHECK(num_deletion_ranges == 1);
16!
3483
                    CHECK(num_insertion_ranges == 1);
16!
3484
                    CHECK(num_modification_ranges == 0);
16!
3485
                    CHECK(num_moves == 0);
10!
3486

15!
3487
                    realm_index_range_t insertion_range, deletion_range;
15✔
3488
                    realm_collection_changes_get_ranges(state.changes.get(), &deletion_range, 1, &insertion_range, 1,
16✔
3489
                                                        nullptr, 0, nullptr, 0, nullptr, 0);
10✔
3490
                    CHECK(deletion_range.from == 0);
16!
3491
                    CHECK(deletion_range.to == 1);
15!
3492
                    CHECK(insertion_range.from == 0);
16!
3493
                    CHECK(insertion_range.to == 2);
16!
3494

15✔
3495
                    write([&]() {
16!
3496
                        checked(realm_set_clear(strings.get()));
16✔
3497
                    });
28!
3498

159✔
3499
                    size_t num_deletions, num_insertions, num_modifications;
402✔
3500
                    bool collection_cleared = false;
820✔
3501
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
236✔
3502
                                                             &num_modifications, &num_moves, &collection_cleared,
146!
3503
                                                             nullptr);
42!
3504
                    CHECK(collection_cleared == true);
10!
3505
                }
16!
3506
            }
17!
3507
        }
40!
3508

152!
3509
        SECTION("dictionaries") {
302✔
3510
            SECTION("realm_get_dictionary() errors") {
90✔
3511
                CHECK(!realm_get_dictionary(obj1.get(), foo_properties["int"]));
78!
3512
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
58!
3513

33!
3514
                CHECK(!realm_get_dictionary(obj1.get(), 123123123));
58!
3515
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
54!
3516
            }
58✔
3517

70✔
3518
            SECTION("nullable strings") {
84✔
3519
                auto strings = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
64✔
3520
                CHECK(strings);
40!
3521
                CHECK(!realm_is_frozen(strings.get()));
64!
3522

20!
3523
                realm_value_t a = rlm_str_val("a");
28✔
3524
                realm_value_t b = rlm_str_val("b");
34!
3525
                realm_value_t c = rlm_null();
58✔
3526
                realm_value_t key_a = rlm_str_val("key_a");
24✔
3527
                realm_value_t key_b = rlm_str_val("key_b");
28!
3528
                realm_value_t key_c = rlm_str_val("key_c");
22!
3529

18!
3530
                SECTION("realm_equals() type check") {
34!
3531
                    CHECK(!realm_equals(strings.get(), obj1.get()));
52!
3532
                }
18✔
3533

24✔
3534
                SECTION("realm_clone()") {
22!
3535
                    auto dict2 = clone_cptr(strings.get());
16!
3536
                    CHECK(realm_equals(strings.get(), dict2.get()));
16!
3537
                    CHECK(strings.get() != dict2.get());
16!
3538
                }
16!
3539

18!
3540
                SECTION("insert, then get, then erase") {
16!
3541
                    write([&]() {
16!
3542
                        bool inserted = false;
15✔
3543
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_a, a, nullptr, &inserted)));
16!
3544
                        CHECK(inserted);
16!
3545
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_b, b, nullptr, &inserted)));
16!
3546
                        CHECK(inserted);
16!
3547
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_c, c, nullptr, &inserted)));
16!
3548
                        CHECK(inserted);
16!
3549

9!
3550
                        realm_value_t a2, b2, c2;
16!
3551
                        bool found = false;
15!
3552
                        CHECK(checked(realm_dictionary_find(strings.get(), key_a, &a2, &found)));
16!
3553
                        CHECK(found);
10!
3554
                        CHECK(checked(realm_dictionary_find(strings.get(), key_b, &b2, &found)));
16!
3555
                        CHECK(found);
15!
3556
                        CHECK(checked(realm_dictionary_find(strings.get(), key_c, &c2, &found)));
16!
3557
                        CHECK(found);
16!
3558

15!
3559
                        CHECK(rlm_stdstr(a2) == "a");
16!
3560
                        CHECK(rlm_stdstr(b2) == "b");
16!
3561
                        CHECK(c2.type == RLM_TYPE_NULL);
28!
3562

51✔
3563
                        bool erased = false;
18✔
3564
                        CHECK(checked(realm_dictionary_erase(strings.get(), key_a, &erased)));
22!
3565
                        CHECK(erased);
16!
3566
                        CHECK(checked(realm_dictionary_erase(strings.get(), rlm_int_val(987), &erased)));
16!
3567
                        CHECK(!erased);
10!
3568
                    });
16!
3569
                }
15✔
3570

18!
3571
                SECTION("equality") {
22✔
3572
                    auto strings2 =
16!
3573
                        cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
16!
3574
                    CHECK(strings2);
16!
3575
                    CHECK(realm_equals(strings.get(), strings2.get()));
16!
3576

51✔
3577
                    write([&]() {
88✔
3578
                        auto obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
178✔
3579
                        CHECK(obj3);
28!
3580
                        auto strings3 =
42✔
3581
                            cptr_checked(realm_get_dictionary(obj3.get(), foo_properties["nullable_string_dict"]));
10✔
3582
                        CHECK(!realm_equals(strings.get(), strings3.get()));
16!
3583
                    });
15✔
3584
                }
16✔
3585
            }
22✔
3586

28✔
3587
            SECTION("get/insert all property types") {
42✔
3588
                realm_value_t key = rlm_str_val("k");
16✔
3589
                realm_value_t key2 = rlm_str_val("k2");
16✔
3590

15✔
3591
                realm_value_t null = rlm_null();
16✔
3592
                realm_value_t integer = rlm_int_val(987);
16✔
3593
                realm_value_t boolean = rlm_bool_val(true);
16✔
3594
                realm_value_t string = rlm_str_val("My string");
10✔
3595
                const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
16✔
3596
                realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
15✔
3597
                realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
16✔
3598
                realm_value_t fnum = rlm_float_val(123.f);
16✔
3599
                realm_value_t dnum = rlm_double_val(456.0);
16✔
3600
                realm_value_t decimal = rlm_decimal_val(999.0);
16✔
3601
                realm_value_t object_id = rlm_object_id_val("abc123abc123");
16✔
3602
                realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16✔
3603

15✔
3604
                auto int_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["int_dict"]));
16✔
3605
                auto bool_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["bool_dict"]));
16✔
3606
                auto string_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["string_dict"]));
16✔
3607
                auto binary_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["binary_dict"]));
16✔
3608
                auto timestamp_dict =
16✔
3609
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["timestamp_dict"]));
16✔
3610
                auto float_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["float_dict"]));
16✔
3611
                auto double_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["double_dict"]));
16✔
3612
                auto decimal_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["decimal_dict"]));
16✔
3613
                auto object_id_dict =
16✔
3614
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["object_id_dict"]));
16✔
3615
                auto uuid_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["uuid_dict"]));
16✔
3616
                auto nullable_int_dict =
16✔
3617
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_int_dict"]));
16✔
3618
                auto nullable_bool_dict =
16✔
3619
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_bool_dict"]));
16✔
3620
                auto nullable_string_dict =
16✔
3621
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
16✔
3622
                auto nullable_binary_dict =
16✔
3623
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_binary_dict"]));
16✔
3624
                auto nullable_timestamp_dict =
16✔
3625
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_timestamp_dict"]));
16✔
3626
                auto nullable_float_dict =
16✔
3627
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_float_dict"]));
10✔
3628
                auto nullable_double_dict =
16✔
3629
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_double_dict"]));
15✔
3630
                auto nullable_decimal_dict =
16✔
3631
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_decimal_dict"]));
16!
3632
                auto nullable_object_id_dict =
10✔
3633
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_object_id_dict"]));
16!
3634
                auto nullable_uuid_dict =
15!
3635
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_uuid_dict"]));
16!
3636

15!
3637
                write([&]() {
16!
3638
                    size_t index;
16!
3639
                    bool inserted;
16!
3640
                    CHECK(!realm_dictionary_insert(int_dict.get(), rlm_int_val(987), integer, &index, &inserted));
16!
3641

15!
3642
                    CHECK(realm_dictionary_insert(int_dict.get(), key, integer, &index, &inserted));
16!
3643
                    CHECK((inserted && index == 0));
16!
3644
                    CHECK(realm_dictionary_insert(bool_dict.get(), key, boolean, &index, &inserted));
16!
3645
                    CHECK((inserted && index == 0));
16!
3646
                    CHECK(realm_dictionary_insert(string_dict.get(), key, string, &index, &inserted));
16!
3647
                    CHECK((inserted && index == 0));
16!
3648
                    CHECK(realm_dictionary_insert(binary_dict.get(), key, binary, &index, &inserted));
16!
3649
                    CHECK((inserted && index == 0));
16!
3650
                    CHECK(realm_dictionary_insert(timestamp_dict.get(), key, timestamp, &index, &inserted));
16!
3651
                    CHECK((inserted && index == 0));
16!
3652
                    CHECK(realm_dictionary_insert(float_dict.get(), key, fnum, &index, &inserted));
16!
3653
                    CHECK((inserted && index == 0));
10!
3654
                    CHECK(realm_dictionary_insert(double_dict.get(), key, dnum, &index, &inserted));
16!
3655
                    CHECK((inserted && index == 0));
15!
3656
                    CHECK(realm_dictionary_insert(decimal_dict.get(), key, decimal, &index, &inserted));
16!
3657
                    CHECK((inserted && index == 0));
16!
3658
                    CHECK(realm_dictionary_insert(object_id_dict.get(), key, object_id, &index, &inserted));
16!
3659
                    CHECK((inserted && index == 0));
16!
3660
                    CHECK(realm_dictionary_insert(uuid_dict.get(), key, uuid, &index, &inserted));
16!
3661
                    CHECK((inserted && index == 0));
16!
3662

15!
3663
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key, integer, &index, &inserted));
16!
3664
                    CHECK((inserted && index == 0));
16!
3665
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key, boolean, &index, &inserted));
16!
3666
                    CHECK((inserted && index == 0));
16!
3667
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key, string, &index, &inserted));
16!
3668
                    CHECK((inserted && index == 0));
16!
3669
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key, binary, &index, &inserted));
16!
3670
                    CHECK((inserted && index == 0));
16!
3671
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key, timestamp, &index, &inserted));
16!
3672
                    CHECK((inserted && index == 0));
16!
3673
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key, fnum, &index, &inserted));
16!
3674
                    CHECK((inserted && index == 0));
10!
3675
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key, dnum, &index, &inserted));
16!
3676
                    CHECK((inserted && index == 0));
15!
3677
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key, decimal, &index, &inserted));
16!
3678
                    CHECK((inserted && index == 0));
16!
3679
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key, object_id, &index, &inserted));
16!
3680
                    CHECK((inserted && index == 0));
16!
3681
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key, uuid, &index, &inserted));
16!
3682
                    CHECK((inserted && index == 0));
16!
3683

15!
3684
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key2, null, &index, &inserted));
16!
3685
                    CHECK((inserted && index == 1));
16!
3686
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key2, null, &index, &inserted));
16!
3687
                    CHECK((inserted && index == 1));
16!
3688
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key2, null, &index, &inserted));
16!
3689
                    CHECK((inserted && index == 1));
16!
3690
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key2, null, &index, &inserted));
16!
3691
                    CHECK((inserted && index == 1));
16!
3692
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key2, null, &index, &inserted));
16!
3693
                    CHECK((inserted && index == 1));
16!
3694
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key2, null, &index, &inserted));
16!
3695
                    CHECK((inserted && index == 1));
16!
3696
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key2, null, &index, &inserted));
10!
3697
                    CHECK((inserted && index == 1));
16!
3698
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key2, null, &index, &inserted));
9!
3699
                    CHECK((inserted && index == 1));
16!
3700
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key2, null, &index, &inserted));
15!
3701
                    CHECK((inserted && index == 1));
16!
3702
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key2, null, &index, &inserted));
16!
3703
                    CHECK((inserted && index == 1));
16!
3704
                });
16!
3705

15!
3706
                realm_value_t k, value;
16!
3707

15!
3708
                CHECK(realm_dictionary_get(int_dict.get(), 0, &k, &value));
16!
3709
                CHECK(rlm_val_eq(k, key));
16!
3710
                CHECK(rlm_val_eq(value, integer));
16!
3711
                CHECK(realm_dictionary_get(bool_dict.get(), 0, &k, &value));
16!
3712
                CHECK(rlm_val_eq(k, key));
16!
3713
                CHECK(rlm_val_eq(value, boolean));
16!
3714
                CHECK(realm_dictionary_get(string_dict.get(), 0, &k, &value));
16!
3715
                CHECK(rlm_val_eq(k, key));
16!
3716
                CHECK(rlm_val_eq(value, string));
16!
3717
                CHECK(realm_dictionary_get(binary_dict.get(), 0, &k, &value));
16!
3718
                CHECK(rlm_val_eq(k, key));
16!
3719
                CHECK(rlm_val_eq(value, binary));
16!
3720
                CHECK(realm_dictionary_get(timestamp_dict.get(), 0, &k, &value));
16!
3721
                CHECK(rlm_val_eq(k, key));
16!
3722
                CHECK(rlm_val_eq(value, timestamp));
16!
3723
                CHECK(realm_dictionary_get(float_dict.get(), 0, &k, &value));
16!
3724
                CHECK(rlm_val_eq(k, key));
16!
3725
                CHECK(rlm_val_eq(value, fnum));
16!
3726
                CHECK(realm_dictionary_get(double_dict.get(), 0, &k, &value));
16!
3727
                CHECK(rlm_val_eq(k, key));
16!
3728
                CHECK(rlm_val_eq(value, dnum));
16!
3729
                CHECK(realm_dictionary_get(decimal_dict.get(), 0, &k, &value));
16!
3730
                CHECK(rlm_val_eq(k, key));
16!
3731
                CHECK(rlm_val_eq(value, decimal));
16!
3732
                CHECK(realm_dictionary_get(object_id_dict.get(), 0, &k, &value));
16!
3733
                CHECK(rlm_val_eq(k, key));
16!
3734
                CHECK(rlm_val_eq(value, object_id));
16!
3735
                CHECK(realm_dictionary_get(uuid_dict.get(), 0, &k, &value));
16!
3736
                CHECK(rlm_val_eq(k, key));
16!
3737
                CHECK(rlm_val_eq(value, uuid));
16!
3738
                CHECK(realm_dictionary_get(nullable_int_dict.get(), 0, &k, &value));
16!
3739
                CHECK(rlm_val_eq(k, key));
16!
3740
                CHECK(rlm_val_eq(value, integer));
16!
3741
                CHECK(realm_dictionary_get(nullable_bool_dict.get(), 0, &k, &value));
16!
3742
                CHECK(rlm_val_eq(k, key));
16!
3743
                CHECK(rlm_val_eq(value, boolean));
16!
3744
                CHECK(realm_dictionary_get(nullable_string_dict.get(), 0, &k, &value));
16!
3745
                CHECK(rlm_val_eq(k, key));
16!
3746
                CHECK(rlm_val_eq(value, string));
16!
3747
                CHECK(realm_dictionary_get(nullable_binary_dict.get(), 0, &k, &value));
16!
3748
                CHECK(rlm_val_eq(k, key));
16!
3749
                CHECK(rlm_val_eq(value, binary));
16!
3750
                CHECK(realm_dictionary_get(nullable_timestamp_dict.get(), 0, &k, &value));
16!
3751
                CHECK(rlm_val_eq(k, key));
16!
3752
                CHECK(rlm_val_eq(value, timestamp));
16!
3753
                CHECK(realm_dictionary_get(nullable_float_dict.get(), 0, &k, &value));
16!
3754
                CHECK(rlm_val_eq(k, key));
16!
3755
                CHECK(rlm_val_eq(value, fnum));
16!
3756
                CHECK(realm_dictionary_get(nullable_double_dict.get(), 0, &k, &value));
16!
3757
                CHECK(rlm_val_eq(k, key));
16!
3758
                CHECK(rlm_val_eq(value, dnum));
16!
3759
                CHECK(realm_dictionary_get(nullable_decimal_dict.get(), 0, &k, &value));
10!
3760
                CHECK(rlm_val_eq(k, key));
16!
3761
                CHECK(rlm_val_eq(value, decimal));
15!
3762
                CHECK(realm_dictionary_get(nullable_object_id_dict.get(), 0, &k, &value));
16!
3763
                CHECK(rlm_val_eq(k, key));
16!
3764
                CHECK(rlm_val_eq(value, object_id));
16!
3765
                CHECK(realm_dictionary_get(nullable_uuid_dict.get(), 0, &k, &value));
16!
3766
                CHECK(rlm_val_eq(k, key));
16!
3767
                CHECK(rlm_val_eq(value, uuid));
16!
3768

15!
3769
                write([&]() {
16!
3770
                    size_t index;
16!
3771
                    bool inserted;
16!
3772
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key2, null, &index, &inserted));
16!
3773
                    CHECK(!inserted);
16!
3774
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key2, null, &index, &inserted));
16!
3775
                    CHECK(!inserted);
16!
3776
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key2, null, &index, &inserted));
16!
3777
                    CHECK(!inserted);
16!
3778
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key2, null, &index, &inserted));
16!
3779
                    CHECK(!inserted);
16!
3780
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key2, null, &index, &inserted));
16!
3781
                    CHECK(!inserted);
16!
3782
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key2, null, &index, &inserted));
16!
3783
                    CHECK(!inserted);
16!
3784
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key2, null, &index, &inserted));
10!
3785
                    CHECK(!inserted);
16!
3786
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key2, null, &index, &inserted));
15!
3787
                    CHECK(!inserted);
16!
3788
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key2, null, &index, &inserted));
16!
3789
                    CHECK(!inserted);
16!
3790
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key2, null, &index, &inserted));
16!
3791
                    CHECK(!inserted);
16!
3792
                });
16!
3793

15!
3794
                CHECK(realm_dictionary_find(int_dict.get(), rlm_int_val(987), &value, &found));
16!
3795
                CHECK(!found);
16!
3796
                CHECK(realm_dictionary_find(int_dict.get(), rlm_str_val("Boogeyman"), &value, &found));
16!
3797
                CHECK(!found);
16!
3798
                CHECK(realm_dictionary_find(int_dict.get(), key, &value, &found));
16!
3799
                CHECK(found);
16!
3800
                CHECK(rlm_val_eq(value, integer));
16!
3801
                CHECK(realm_dictionary_find(bool_dict.get(), key, &value, &found));
16!
3802
                CHECK(found);
16!
3803
                CHECK(rlm_val_eq(value, boolean));
16!
3804
                CHECK(realm_dictionary_find(string_dict.get(), key, &value, &found));
16!
3805
                CHECK(found);
16!
3806
                CHECK(rlm_val_eq(value, string));
16!
3807
                CHECK(realm_dictionary_find(binary_dict.get(), key, &value, &found));
16!
3808
                CHECK(found);
16!
3809
                CHECK(rlm_val_eq(value, binary));
16!
3810
                CHECK(realm_dictionary_find(timestamp_dict.get(), key, &value, &found));
16!
3811
                CHECK(found);
16!
3812
                CHECK(rlm_val_eq(value, timestamp));
16!
3813
                CHECK(realm_dictionary_find(float_dict.get(), key, &value, &found));
16!
3814
                CHECK(found);
16!
3815
                CHECK(rlm_val_eq(value, fnum));
16!
3816
                CHECK(realm_dictionary_find(double_dict.get(), key, &value, &found));
16!
3817
                CHECK(found);
16!
3818
                CHECK(rlm_val_eq(value, dnum));
16!
3819
                CHECK(realm_dictionary_find(decimal_dict.get(), key, &value, &found));
16!
3820
                CHECK(found);
16!
3821
                CHECK(rlm_val_eq(value, decimal));
16!
3822
                CHECK(realm_dictionary_find(object_id_dict.get(), key, &value, &found));
16!
3823
                CHECK(found);
16!
3824
                CHECK(rlm_val_eq(value, object_id));
16!
3825
                CHECK(realm_dictionary_find(uuid_dict.get(), key, &value, &found));
16!
3826
                CHECK(found);
16!
3827
                CHECK(rlm_val_eq(value, uuid));
16!
3828
                CHECK(realm_dictionary_find(nullable_int_dict.get(), key, &value, &found));
16!
3829
                CHECK(found);
16!
3830
                CHECK(rlm_val_eq(value, integer));
16!
3831
                CHECK(realm_dictionary_find(nullable_bool_dict.get(), key, &value, &found));
16!
3832
                CHECK(found);
16!
3833
                CHECK(rlm_val_eq(value, boolean));
16!
3834
                CHECK(realm_dictionary_find(nullable_string_dict.get(), key, &value, &found));
16!
3835
                CHECK(found);
16!
3836
                CHECK(rlm_val_eq(value, string));
16!
3837
                CHECK(realm_dictionary_find(nullable_binary_dict.get(), key, &value, &found));
16!
3838
                CHECK(found);
16!
3839
                CHECK(rlm_val_eq(value, binary));
16!
3840
                CHECK(realm_dictionary_find(nullable_timestamp_dict.get(), key, &value, &found));
16!
3841
                CHECK(found);
16!
3842
                CHECK(rlm_val_eq(value, timestamp));
16!
3843
                CHECK(realm_dictionary_find(nullable_float_dict.get(), key, &value, &found));
16!
3844
                CHECK(found);
16!
3845
                CHECK(rlm_val_eq(value, fnum));
16!
3846
                CHECK(realm_dictionary_find(nullable_double_dict.get(), key, &value, &found));
16!
3847
                CHECK(found);
16!
3848
                CHECK(rlm_val_eq(value, dnum));
16!
3849
                CHECK(realm_dictionary_find(nullable_decimal_dict.get(), key, &value, &found));
10!
3850
                CHECK(found);
16!
3851
                CHECK(rlm_val_eq(value, decimal));
15!
3852
                CHECK(realm_dictionary_find(nullable_object_id_dict.get(), key, &value, &found));
16!
3853
                CHECK(found);
16!
3854
                CHECK(rlm_val_eq(value, object_id));
16!
3855
                CHECK(realm_dictionary_find(nullable_uuid_dict.get(), key, &value, &found));
16!
3856
                CHECK(found);
16!
3857
                CHECK(rlm_val_eq(value, uuid));
16!
3858

15!
3859
                CHECK(realm_dictionary_find(nullable_int_dict.get(), key2, &value, &found));
16!
3860
                CHECK(found);
16!
3861
                CHECK(rlm_val_eq(value, null));
16!
3862
                CHECK(realm_dictionary_find(nullable_bool_dict.get(), key2, &value, &found));
16!
3863
                CHECK(found);
16!
3864
                CHECK(rlm_val_eq(value, null));
16!
3865
                CHECK(realm_dictionary_find(nullable_string_dict.get(), key2, &value, &found));
16!
3866
                CHECK(found);
16!
3867
                CHECK(rlm_val_eq(value, null));
16!
3868
                CHECK(realm_dictionary_find(nullable_binary_dict.get(), key2, &value, &found));
16!
3869
                CHECK(found);
16!
3870
                CHECK(rlm_val_eq(value, null));
16!
3871
                CHECK(realm_dictionary_find(nullable_timestamp_dict.get(), key2, &value, &found));
16!
3872
                CHECK(found);
16!
3873
                CHECK(rlm_val_eq(value, null));
16!
3874
                CHECK(realm_dictionary_find(nullable_float_dict.get(), key2, &value, &found));
16!
3875
                CHECK(found);
16!
3876
                CHECK(rlm_val_eq(value, null));
16!
3877
                CHECK(realm_dictionary_find(nullable_double_dict.get(), key2, &value, &found));
16!
3878
                CHECK(found);
16!
3879
                CHECK(rlm_val_eq(value, null));
16!
3880
                CHECK(realm_dictionary_find(nullable_decimal_dict.get(), key2, &value, &found));
16!
3881
                CHECK(found);
88!
3882
                CHECK(rlm_val_eq(value, null));
172!
3883
                CHECK(realm_dictionary_find(nullable_object_id_dict.get(), key2, &value, &found));
52!
3884
                CHECK(found);
66!
3885
                CHECK(rlm_val_eq(value, null));
26!
3886
                CHECK(realm_dictionary_find(nullable_uuid_dict.get(), key2, &value, &found));
44!
3887
                CHECK(found);
41!
3888
                CHECK(rlm_val_eq(value, null));
44!
3889
            }
44✔
3890

56✔
3891
            SECTION("links") {
70✔
3892
                CPtr<realm_dictionary_t> bars;
48✔
3893
                realm_value_t key = rlm_str_val("k");
48✔
3894

45!
3895
                write([&]() {
48!
3896
                    bars = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["link_dict"]));
48!
3897
                    auto bar_link = realm_object_as_link(obj2.get());
48!
3898
                    realm_value_t bar_link_val;
48!
3899
                    bar_link_val.type = RLM_TYPE_LINK;
48!
3900
                    bar_link_val.link = bar_link;
48!
3901
                    size_t index;
48!
3902
                    bool inserted;
30!
3903
                    CHECK(checked(realm_dictionary_insert(bars.get(), key, bar_link_val, &index, &inserted)));
48!
3904
                    CHECK((index == 0 && inserted));
21!
3905
                    CHECK(checked(realm_dictionary_insert(bars.get(), key, bar_link_val, &index, &inserted)));
24!
3906
                    CHECK((index == 0 && !inserted));
20!
3907
                    size_t size;
20!
3908
                    CHECK(checked(realm_dictionary_size(bars.get(), &size)));
20!
3909
                    CHECK(size == 1);
20!
3910
                });
14!
3911

17!
3912
                SECTION("get") {
19!
3913
                    realm_value_t k, val;
16!
3914
                    CHECK(checked(realm_dictionary_get(bars.get(), 0, &k, &val)));
16!
3915
                    CHECK(rlm_val_eq(k, key));
22!
3916
                    CHECK(val.type == RLM_TYPE_LINK);
40!
3917
                    CHECK(val.link.target_table == class_bar.key);
17!
3918
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
20!
3919

15!
3920
                    auto result = realm_dictionary_get(bars.get(), 1, &k, &val);
16✔
3921
                    CHECK(!result);
16!
3922
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
16!
3923
                }
10✔
3924

17!
3925
                SECTION("insert wrong type") {
19!
3926
                    write([&]() {
16!
3927
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
16!
3928
                        CHECK(foo2);
22!
3929
                        realm_value_t foo2_link_val;
40✔
3930
                        foo2_link_val.type = RLM_TYPE_LINK;
17✔
3931
                        foo2_link_val.link = realm_object_as_link(foo2.get());
20!
3932

15✔
3933
                        CHECK(!realm_dictionary_insert(bars.get(), key, foo2_link_val, nullptr, nullptr));
16!
3934
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
16!
3935
                    });
16!
3936
                }
10!
3937

17!
3938
                SECTION("realm_dictionary_clear()") {
19!
3939
                    write([&]() {
16!
3940
                        CHECK(realm_dictionary_clear(bars.get()));
16!
3941
                    });
40!
3942
                    size_t size;
88✔
3943
                    CHECK(realm_dictionary_size(bars.get(), &size));
176!
3944
                    CHECK(size == 0);
40!
3945

53✔
3946
                    size_t num_bars;
30✔
3947
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
30!
3948
                    CHECK(num_bars != 0);
30!
3949
                }
30✔
3950
            }
22✔
3951

42✔
3952
            SECTION("notifications") {
66✔
3953
                struct State {
44✔
3954
                    CPtr<realm_collection_changes_t> changes;
46✔
3955
                    CPtr<realm_dictionary_changes_t> dictionary_changes;
46✔
3956
                    CPtr<realm_async_error_t> error;
22✔
3957
                    bool destroyed = false;
34✔
3958
                };
30✔
3959

18✔
3960
                State state;
32✔
3961
                auto on_dictionary_change = [](void* userdata, const realm_dictionary_changes_t* changes) {
32✔
3962
                    auto* state = static_cast<State*>(userdata);
34✔
3963
                    state->dictionary_changes = clone_cptr(changes);
22✔
3964
                };
22✔
3965

22✔
3966
                CPtr<realm_dictionary_t> strings =
18✔
3967
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
19✔
3968

16✔
3969
                auto str1 = rlm_str_val("a");
18✔
3970
                auto str2 = rlm_str_val("b");
18✔
3971
                auto null = rlm_null();
18✔
3972

28✔
3973

16✔
3974
                auto require_change = [&]() {
19✔
3975
                    auto token = cptr_checked(realm_dictionary_add_notification_callback(
16✔
3976
                        strings.get(), &state, nullptr, nullptr, on_dictionary_change));
16✔
3977
                    checked(realm_refresh(realm, nullptr));
16✔
3978
                    return token;
16✔
3979
                };
16!
3980

16✔
3981
                SECTION("userdata is freed when the token is destroyed") {
18!
3982
                    auto token = cptr_checked(realm_dictionary_add_notification_callback(
16✔
3983
                        strings.get(), &state,
16!
3984
                        [](void* p) {
28✔
3985
                            static_cast<State*>(p)->destroyed = true;
16✔
3986
                        },
18✔
3987
                        nullptr, nullptr));
16✔
3988
                    CHECK(!state.destroyed);
16!
3989
                    token.reset();
16✔
3990
                    CHECK(state.destroyed);
16!
3991
                }
16✔
3992

16✔
3993
                SECTION("insertion, deletions sends a change callback") {
18✔
3994
                    write([&]() {
16!
3995
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("a"), str1, nullptr, nullptr));
16!
3996
                    });
10!
3997
                    auto token = require_change();
16!
3998
                    write([&]() {
15✔
3999
                        checked(realm_dictionary_erase(strings.get(), rlm_str_val("a"), nullptr));
16✔
4000
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("b"), str2, nullptr, nullptr));
16!
4001
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("c"), null, nullptr, nullptr));
16!
4002
                    });
16!
4003
                    CHECK(!state.error);
16!
4004
                    CHECK(state.dictionary_changes);
16!
4005

15✔
4006
                    size_t num_deletions, num_insertions, num_modifications;
16✔
4007
                    realm_dictionary_get_changes(state.dictionary_changes.get(), &num_deletions, &num_insertions,
16✔
4008
                                                 &num_modifications, nullptr);
16✔
4009
                    CHECK(num_deletions == 1);
16!
4010
                    CHECK(num_insertions == 2);
16!
4011
                    CHECK(num_modifications == 0);
16!
4012
                    realm_value_t *deletions = nullptr, *insertions = nullptr, *modifications = nullptr;
16!
4013
                    bool collection_cleared = false;
16!
4014
                    deletions = (realm_value_t*)malloc(sizeof(realm_value_t) * num_deletions);
16!
4015
                    insertions = (realm_value_t*)malloc(sizeof(realm_value_t) * num_insertions);
16✔
4016
                    realm_dictionary_get_changed_keys(state.dictionary_changes.get(), deletions, &num_deletions,
10✔
4017
                                                      insertions, &num_insertions, modifications, &num_modifications,
16✔
4018
                                                      &collection_cleared);
15✔
4019
                    CHECK(deletions != nullptr);
16!
4020
                    CHECK(insertions != nullptr);
16!
4021
                    CHECK(modifications == nullptr);
16!
4022
                    realm_free(deletions);
16✔
4023
                    realm_free(insertions);
16!
4024
                    realm_free(modifications);
16✔
4025

27!
4026
                    write([&]() {
88✔
4027
                        checked(realm_dictionary_clear(strings.get()));
174✔
4028
                    });
52✔
4029
                    realm_dictionary_get_changed_keys(state.dictionary_changes.get(), deletions, &num_deletions,
66!
4030
                                                      insertions, &num_insertions, modifications, &num_modifications,
44!
4031
                                                      &collection_cleared);
44!
4032
                    CHECK(collection_cleared == true);
44!
4033
                }
44✔
4034
            }
46✔
4035

38✔
4036
            SECTION("realm_dictionary_content_checks") {
70✔
4037
                auto ints = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["int_dict"]));
45✔
4038
                CHECK(ints);
48!
4039
                CHECK(!realm_is_frozen(ints.get()));
48!
4040
                realm_value_t key1 = rlm_str_val("k");
48!
4041
                realm_value_t key2 = rlm_str_val("k2");
48!
4042
                realm_value_t integer1 = rlm_int_val(987);
48!
4043
                realm_value_t integer2 = rlm_int_val(988);
30!
4044

45✔
4045
                write([&]() {
21✔
4046
                    bool inserted = false;
24✔
4047
                    CHECK(checked(realm_dictionary_insert(ints.get(), key1, integer1, nullptr, &inserted)));
20!
4048
                    CHECK(inserted);
20!
4049
                    CHECK(checked(realm_dictionary_insert(ints.get(), key2, integer2, nullptr, &inserted)));
20!
4050
                    CHECK(inserted);
20!
4051
                });
20!
4052

23✔
4053
                SECTION("realm_dictionary_get_keys") {
44✔
4054
                    size_t size = 0;
17✔
4055
                    realm_results_t* keys = nullptr;
20!
4056
                    CHECK(checked(realm_dictionary_get_keys(ints.get(), &size, &keys)));
16!
4057
                    CHECK(keys);
16!
4058
                    CHECK((*keys).size() == size);
16!
4059
                    realm_release(keys);
16!
4060
                }
16!
4061

17!
4062
                SECTION("realm_dictionary_contains_key") {
20!
4063
                    bool found = false;
16!
4064
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key1, &found)));
22!
4065
                    CHECK(found);
40!
4066
                    found = false;
17✔
4067
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key2, &found)));
20!
4068
                    CHECK(found);
16!
4069
                    realm_value_t key_no_present = rlm_str_val("kkkk");
16!
4070
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key_no_present, &found)));
16!
4071
                    CHECK(!found);
16!
4072
                }
16!
4073

17!
4074
                SECTION("realm_dictionary_contains_value") {
20!
4075
                    size_t index = -1;
40!
4076
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer1, &index)));
172!
4077
                    CHECK(index == 0);
404!
4078
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer2, &index)));
822!
4079
                    CHECK(index == 1);
104!
4080
                    realm_value_t integer_no_present = rlm_int_val(678);
170✔
4081
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer_no_present, &index)));
44!
4082
                    CHECK(index == realm::npos);
44!
4083
                }
44✔
4084
            }
30✔
4085
        }
70✔
4086

87✔
4087
        SECTION("notifications") {
210✔
4088
            struct State {
81✔
4089
                CPtr<realm_object_changes_t> changes;
90✔
4090
                CPtr<realm_async_error_t> error;
90✔
4091
                bool called;
90✔
4092
            };
36✔
4093

45✔
4094
            State state;
33✔
4095

32✔
4096
            auto on_change = [](void* userdata, const realm_object_changes_t* changes) {
40✔
4097
                auto state = static_cast<State*>(userdata);
40✔
4098
                state->changes = clone_cptr(changes);
40✔
4099
                state->called = true;
34✔
4100
            };
52✔
4101

18✔
4102
            auto require_change = [&]() {
23✔
4103
                auto token =
18✔
4104
                    cptr(realm_object_add_notification_callback(obj1.get(), &state, nullptr, nullptr, on_change));
18✔
4105
                checked(realm_refresh(realm, nullptr));
18!
4106
                return token;
18!
4107
            };
18!
4108

17!
4109
            SECTION("deleting the object sends a change notification") {
20✔
4110
                auto token = require_change();
22!
4111
                write([&]() {
40✔
4112
                    checked(realm_object_delete(obj1.get()));
17✔
4113
                });
20✔
4114
                CHECK(!state.error);
16!
4115
                CHECK(state.changes);
16!
4116
                bool deleted = realm_object_changes_is_deleted(state.changes.get());
16✔
4117
                CHECK(deleted);
16!
4118
            }
16!
4119

17!
4120
            SECTION("modifying the object sends a change notification for the object, and for the changed column") {
20!
4121
                auto token = require_change();
16✔
4122
                write([&]() {
16!
4123
                    checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
16✔
4124
                    checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("aaa"), false));
16!
4125
                });
16!
4126
                CHECK(!state.error);
16!
4127
                CHECK(state.changes);
16!
4128
                bool deleted = realm_object_changes_is_deleted(state.changes.get());
10!
4129
                CHECK(!deleted);
16!
4130
                size_t num_modified = realm_object_changes_get_num_modified_properties(state.changes.get());
15!
4131
                CHECK(num_modified == 2);
10!
4132
                realm_property_key_t modified_keys[2];
16!
4133
                size_t n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 2);
15!
4134
                CHECK(n == 2);
16!
4135
                CHECK(modified_keys[0] == foo_int_key);
40!
4136
                CHECK(modified_keys[1] == foo_str_key);
16!
4137

19✔
4138
                n = realm_object_changes_get_modified_properties(state.changes.get(), nullptr, 2);
16!
4139
                CHECK(n == 2);
16!
4140

15!
4141
                n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 0);
16✔
4142
                CHECK(n == 0);
16!
4143
            }
16✔
4144
            SECTION("modifying the object while observing a specific value") {
20✔
4145
                const char* foo_strings[1] = {"public_int"};
16✔
4146
                auto key_path_array = realm_create_key_path_array(realm, class_foo.key, 1, foo_strings);
16✔
4147
                REQUIRE(key_path_array);
16!
4148
                auto token = cptr(
16!
4149
                    realm_object_add_notification_callback(obj1.get(), &state, nullptr, key_path_array, on_change));
16!
4150
                realm_release(key_path_array);
16!
4151
                checked(realm_refresh(realm, nullptr));
16!
4152
                state.called = false;
16!
4153
                write([&]() {
16!
4154
                    checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
10!
4155
                });
16!
4156
                REQUIRE(state.called);
15!
4157
                CHECK(!state.error);
10!
4158
                CHECK(state.changes);
16!
4159
                realm_property_key_t modified_keys[2];
15✔
4160
                size_t n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 2);
16!
4161
                CHECK(n == 1);
16!
4162
                CHECK(modified_keys[0] == foo_int_key);
40!
4163

795✔
4164
                state.called = false;
554✔
4165
                write([&]() {
1,226✔
4166
                    // checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
152✔
4167
                    checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("aaa"), false));
244✔
4168
                });
72✔
4169
                REQUIRE(!state.called);
72!
4170
            }
72✔
4171
        }
76✔
4172
    }
172✔
4173

161✔
4174
    SECTION("threads") {
247✔
4175
        CPtr<realm_object_t> foo_obj, bar_obj;
80✔
4176
        write([&]() {
80✔
4177
            foo_obj = cptr_checked(realm_object_create(realm, class_foo.key));
50✔
4178
            realm_set_value(foo_obj.get(), foo_int_key, rlm_int_val(123), false);
80✔
4179
            bar_obj = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123)));
27✔
4180
        });
32✔
4181

19!
4182
        auto list = cptr_checked(realm_get_list(foo_obj.get(), foo_properties["int_list"]));
24!
4183
        auto set = cptr_checked(realm_get_set(foo_obj.get(), foo_properties["int_set"]));
24!
4184
        auto dictionary = cptr_checked(realm_get_dictionary(foo_obj.get(), foo_properties["int_dict"]));
18!
4185
        auto results = cptr_checked(realm_object_find_all(realm, class_foo.key));
24✔
4186

18✔
4187
        SECTION("wrong thread") {
42✔
4188
            std::thread t{[&]() {
64✔
4189
                realm_value_t val;
55✔
4190
                CHECK(!realm_get_value(foo_obj.get(), foo_int_key, &val));
60!
4191
                CHECK_ERR(RLM_ERR_WRONG_THREAD);
58!
4192
            }};
58✔
4193

57✔
4194
            t.join();
58✔
4195
        }
34✔
4196

61✔
4197
        SECTION("thread-safe references") {
26✔
4198
            auto foo_obj_tsr = cptr_checked(realm_create_thread_safe_reference(foo_obj.get()));
28✔
4199
            auto bar_obj_tsr = cptr_checked(realm_create_thread_safe_reference(bar_obj.get()));
22✔
4200
            auto list_tsr = cptr_checked(realm_create_thread_safe_reference(list.get()));
22✔
4201
            auto set_tsr = cptr_checked(realm_create_thread_safe_reference(set.get()));
22✔
4202
            auto dict_tsr = cptr_checked(realm_create_thread_safe_reference(dictionary.get()));
22✔
4203
            auto results_tsr = cptr_checked(realm_create_thread_safe_reference(results.get()));
22✔
4204

18✔
4205
            SECTION("resolve") {
22✔
4206
                std::thread t{[&]() {
16✔
4207
                    auto config = make_config(test_file.path.c_str());
16✔
4208
                    auto realm2 = cptr_checked(realm_open(config.get()));
16✔
4209
                    auto foo_obj2 =
16✔
4210
                        cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), foo_obj_tsr.get()));
10✔
4211
                    auto bar_obj2 =
16✔
4212
                        cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), bar_obj_tsr.get()));
15!
4213
                    auto results2 =
16!
4214
                        cptr_checked(realm_results_from_thread_safe_reference(realm2.get(), results_tsr.get()));
10!
4215
                    auto list2 = cptr_checked(realm_list_from_thread_safe_reference(realm2.get(), list_tsr.get()));
16!
4216
                    auto set2 = cptr_checked(realm_set_from_thread_safe_reference(realm2.get(), set_tsr.get()));
15!
4217
                    auto dict2 =
16!
4218
                        cptr_checked(realm_dictionary_from_thread_safe_reference(realm2.get(), dict_tsr.get()));
16!
4219

9!
4220
                    realm_value_t foo_obj_int;
16✔
4221
                    CHECK(realm_get_value(foo_obj2.get(), foo_int_key, &foo_obj_int));
15!
4222
                    CHECK(rlm_val_eq(foo_obj_int, rlm_int_val(123)));
28!
4223

51✔
4224
                    size_t count;
18✔
4225
                    CHECK(realm_results_count(results2.get(), &count));
22!
4226
                    CHECK(count == 1);
16!
4227
                }};
10!
4228

15!
4229
                t.join();
15✔
4230
            }
16!
4231

18✔
4232
            SECTION("resolve in frozen") {
34!
4233
                auto realm2 = cptr_checked(realm_freeze(realm));
52✔
4234
                CHECK(realm_is_frozen(realm2.get()));
18!
4235
                CHECK(realm != realm2.get());
22!
4236

15!
4237
                auto foo_obj2 =
16!
4238
                    cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), foo_obj_tsr.get()));
16!
4239
                CHECK(realm_is_frozen(foo_obj2.get()));
16!
4240
            }
16!
4241

18!
4242
            SECTION("type error") {
22!
4243
                CHECK(!realm_object_from_thread_safe_reference(realm, list_tsr.get()));
16!
4244
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
4245
                CHECK(!realm_list_from_thread_safe_reference(realm, foo_obj_tsr.get()));
16!
4246
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
4247
                CHECK(!realm_set_from_thread_safe_reference(realm, list_tsr.get()));
28!
4248
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
52!
4249
                CHECK(!realm_dictionary_from_thread_safe_reference(realm, set_tsr.get()));
18!
4250
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
22!
4251
                CHECK(!realm_results_from_thread_safe_reference(realm, list_tsr.get()));
16!
4252
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
4253
                CHECK(!realm_from_thread_safe_reference(list_tsr.get(), nullptr));
52!
4254
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
64!
4255
            }
556✔
4256

1,106✔
4257
            SECTION("non-sendable") {
171✔
4258
                auto c = cptr(realm_config_new());
196✔
4259
                CHECK(!realm_create_thread_safe_reference(c.get()));
26!
4260
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
4261
            }
16!
4262
        }
46!
4263
    }
84✔
4264

109✔
4265
    SECTION("freeze and thaw") {
206✔
4266
        SECTION("realm") {
20✔
4267
            auto frozen_realm = cptr_checked(realm_freeze(realm));
16✔
4268
            CHECK(!realm_is_frozen(realm));
15!
4269
            CHECK(realm_is_frozen(frozen_realm.get()));
16!
4270
        }
16✔
4271

20!
4272
        SECTION("objects") {
26!
4273
            CPtr<realm_object_t> obj1;
16!
4274
            realm_value_t value;
10!
4275

15!
4276
            write([&]() {
15✔
4277
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
16!
4278
                CHECK(obj1);
10!
4279
            });
16!
4280
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
15!
4281
            CHECK(value.type == RLM_TYPE_STRING);
16!
4282
            CHECK(strncmp(value.string.data, "", value.string.size) == 0);
10!
4283

15!
4284
            auto frozen_realm = cptr_checked(realm_freeze(realm));
15!
4285
            realm_object_t* frozen_obj1;
16!
4286
            CHECK(realm_object_resolve_in(obj1.get(), frozen_realm.get(), &frozen_obj1));
10!
4287

15!
4288
            write([&]() {
15!
4289
                CHECK(checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("Hello, World!"), false)));
16!
4290
            });
16!
4291

15!
4292
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
16!
4293
            CHECK(value.type == RLM_TYPE_STRING);
16!
4294
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
16!
4295

15!
4296
            CHECK(checked(realm_get_value(frozen_obj1, foo_str_key, &value)));
10!
4297
            CHECK(value.type == RLM_TYPE_STRING);
16!
4298
            CHECK(strncmp(value.string.data, "", value.string.size) == 0);
15!
4299
            realm_object_t* thawed_obj1;
16✔
4300
            CHECK(realm_object_resolve_in(obj1.get(), realm, &thawed_obj1));
16!
4301
            CHECK(thawed_obj1);
16!
4302
            CHECK(checked(realm_get_value(thawed_obj1, foo_str_key, &value)));
16!
4303
            CHECK(value.type == RLM_TYPE_STRING);
16!
4304
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
16!
4305

15!
4306
            write([&]() {
16✔
4307
                CHECK(checked(realm_object_delete(obj1.get())));
40!
4308
            });
76✔
4309
            realm_object_t* deleted_obj;
20✔
4310
            auto b = realm_object_resolve_in(frozen_obj1, realm, &deleted_obj);
26✔
4311
            CHECK(b);
10!
4312
            CHECK(deleted_obj == nullptr);
16!
4313
            realm_release(frozen_obj1);
9✔
4314
            realm_release(thawed_obj1);
16!
4315
        }
9✔
4316

20!
4317
        SECTION("results") {
25!
4318
            auto results = cptr_checked(realm_object_find_all(realm, class_foo.key));
16✔
4319
            realm_results_delete_all(results.get());
10!
4320

15✔
4321
            write([&]() {
15✔
4322
                // Ensure that we start from a known initial state
15!
4323
                CHECK(realm_results_delete_all(results.get()));
10!
4324

15!
4325
                auto obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
15✔
4326
                CHECK(obj1);
16!
4327
            });
16✔
4328

15!
4329
            size_t count;
16✔
4330
            realm_results_count(results.get(), &count);
16!
4331
            CHECK(count == 1);
16!
4332

15✔
4333
            auto frozen_realm = cptr_checked(realm_freeze(realm));
16!
4334
            auto frozen_results = cptr_checked(realm_results_resolve_in(results.get(), frozen_realm.get()));
10✔
4335
            write([&]() {
16!
4336
                auto obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
15✔
4337
                CHECK(obj1);
16!
4338
            });
16✔
4339
            realm_results_count(frozen_results.get(), &count);
40!
4340
            CHECK(count == 1);
76!
4341
            realm_results_count(results.get(), &count);
20✔
4342
            CHECK(count == 2);
26!
4343

9✔
4344
            auto thawed_results = cptr_checked(realm_results_resolve_in(frozen_results.get(), realm));
16✔
4345
            realm_results_count(thawed_results.get(), &count);
15✔
4346
            CHECK(count == 2);
16!
4347
        }
16✔
4348

14!
4349
        SECTION("lists") {
26✔
4350
            CPtr<realm_object_t> obj1;
15✔
4351
            size_t count;
16!
4352

9✔
4353
            write([&]() {
16!
4354
                obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(1)));
15✔
4355
                CHECK(obj1);
16!
4356
            });
16✔
4357

15!
4358
            auto list = cptr_checked(realm_get_list(obj1.get(), bar_properties["strings"]));
10✔
4359
            realm_list_size(list.get(), &count);
16!
4360
            CHECK(count == 0);
15!
4361

15✔
4362
            auto frozen_realm = cptr_checked(realm_freeze(realm));
10✔
4363
            realm_list_t* frozen_list;
16✔
4364
            CHECK(realm_list_resolve_in(list.get(), frozen_realm.get(), &frozen_list));
15!
4365
            realm_list_size(frozen_list, &count);
16✔
4366
            CHECK(count == 0);
16!
4367

9✔
4368
            write([&]() {
16!
4369
                checked(realm_list_insert(list.get(), 0, rlm_str_val("Hello")));
15!
4370
            });
16✔
4371

15!
4372
            realm_list_size(frozen_list, &count);
10✔
4373
            CHECK(count == 0);
16!
4374
            realm_list_size(list.get(), &count);
15✔
4375
            CHECK(count == 1);
16!
4376

15✔
4377
            realm_list_t* thawed_list;
16!
4378
            CHECK(realm_list_resolve_in(frozen_list, realm, &thawed_list));
16!
4379
            realm_list_size(thawed_list, &count);
16!
4380
            CHECK(count == 1);
16!
4381

15!
4382
            CHECK(realm_list_is_valid(thawed_list));
16!
4383
            write([&]() {
40✔
4384
                CHECK(checked(realm_object_delete(obj1.get())));
76!
4385
            });
20✔
4386
            CHECK(!realm_list_is_valid(thawed_list));
26!
4387
            realm_release(thawed_list);
10✔
4388
            CHECK(realm_list_resolve_in(frozen_list, realm, &thawed_list));
16!
4389
            CHECK(thawed_list == nullptr);
15!
4390
            realm_release(frozen_list);
16!
4391
        }
16✔
4392

14!
4393
        SECTION("sets") {
26✔
4394
            CPtr<realm_object_t> obj1;
15✔
4395
            size_t count;
16!
4396

9✔
4397
            write([&]() {
16!
4398
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
15✔
4399
                CHECK(obj1);
16!
4400
            });
16✔
4401

15!
4402
            auto set = cptr_checked(realm_get_set(obj1.get(), foo_properties["string_set"]));
10✔
4403
            realm_set_size(set.get(), &count);
16!
4404
            CHECK(count == 0);
15!
4405

15✔
4406
            auto frozen_realm = cptr_checked(realm_freeze(realm));
10✔
4407
            realm_set_t* frozen_set;
16✔
4408
            CHECK(realm_set_resolve_in(set.get(), frozen_realm.get(), &frozen_set));
15!
4409
            realm_set_size(frozen_set, &count);
16✔
4410
            CHECK(count == 0);
16!
4411

9✔
4412
            write([&]() {
16!
4413
                checked(realm_set_insert(set.get(), rlm_str_val("Hello"), nullptr, nullptr));
15!
4414
            });
16✔
4415

15!
4416
            realm_set_size(frozen_set, &count);
10✔
4417
            CHECK(count == 0);
16!
4418
            realm_set_size(set.get(), &count);
15✔
4419
            CHECK(count == 1);
16!
4420

15✔
4421
            realm_set_t* thawed_set;
16!
4422
            CHECK(realm_set_resolve_in(frozen_set, realm, &thawed_set));
16!
4423
            realm_set_size(thawed_set, &count);
16!
4424
            CHECK(count == 1);
16!
4425

15!
4426
            CHECK(realm_set_is_valid(thawed_set));
16!
4427
            write([&]() {
40✔
4428
                CHECK(checked(realm_object_delete(obj1.get())));
76!
4429
            });
20✔
4430
            CHECK(!realm_set_is_valid(thawed_set));
26!
4431
            realm_release(thawed_set);
10✔
4432
            CHECK(realm_set_resolve_in(frozen_set, realm, &thawed_set));
16!
4433
            CHECK(thawed_set == nullptr);
15!
4434
            realm_release(frozen_set);
16!
4435
        }
16✔
4436

14!
4437
        SECTION("dictionaries") {
26✔
4438
            CPtr<realm_object_t> obj1;
15✔
4439
            size_t count;
16!
4440

9✔
4441
            write([&]() {
16!
4442
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
15✔
4443
                CHECK(obj1);
16!
4444
            });
16✔
4445

15!
4446
            auto dictionary = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["string_dict"]));
10✔
4447
            realm_dictionary_size(dictionary.get(), &count);
16!
4448
            CHECK(count == 0);
15!
4449

15✔
4450
            auto frozen_realm = cptr_checked(realm_freeze(realm));
16✔
4451
            realm_dictionary_t* frozen_dictionary;
10✔
4452
            CHECK(realm_dictionary_resolve_in(dictionary.get(), frozen_realm.get(), &frozen_dictionary));
16!
4453
            realm_dictionary_size(frozen_dictionary, &count);
15!
4454
            CHECK(count == 0);
16!
4455

15!
4456
            write([&]() {
10✔
4457
                checked(realm_dictionary_insert(dictionary.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr,
16!
4458
                                                nullptr));
15!
4459
            });
16✔
4460

15!
4461
            realm_dictionary_size(frozen_dictionary, &count);
10✔
4462
            CHECK(count == 0);
16!
4463
            realm_dictionary_size(dictionary.get(), &count);
15✔
4464
            CHECK(count == 1);
16!
4465

15✔
4466
            realm_dictionary_t* thawed_dictionary;
16!
4467
            CHECK(realm_dictionary_resolve_in(frozen_dictionary, realm, &thawed_dictionary));
16!
4468
            realm_dictionary_size(thawed_dictionary, &count);
16!
4469
            CHECK(count == 1);
16!
4470

15!
4471
            CHECK(realm_dictionary_is_valid(thawed_dictionary));
16!
4472
            write([&]() {
76✔
4473
                CHECK(checked(realm_object_delete(obj1.get())));
550!
4474
            });
1,106✔
4475
            CHECK(!realm_dictionary_is_valid(thawed_dictionary));
1,185!
4476
            realm_release(thawed_dictionary);
1,276✔
4477
            CHECK(realm_dictionary_resolve_in(frozen_dictionary, realm, &thawed_dictionary));
1,276!
4478
            CHECK(thawed_dictionary == nullptr);
184!
4479
            realm_release(frozen_dictionary);
640✔
4480
        }
458✔
4481
    }
544✔
4482

395✔
4483
    realm_close(realm);
834✔
4484
    REQUIRE(realm_is_closed(realm));
796!
4485
    realm_release(realm);
854✔
4486
}
854✔
4487

672✔
4488
TEST_CASE("C API - queries", "[c_api]") {
748✔
4489
    TestFile test_file;
400✔
4490
    realm_t* realm = open_realm(test_file);
628✔
4491

304✔
4492
    auto write = [&](auto&& f) {
628✔
4493
        checked(realm_begin_write(realm));
590!
4494
        f();
628!
4495
        checked(realm_commit(realm));
628!
4496
        checked(realm_refresh(realm, nullptr));
628!
4497
    };
400!
4498

1,086!
4499
    bool found = false;
1,086✔
4500

1,172✔
4501
    realm_class_info_t class_foo, class_bar;
1,210✔
4502
    CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
1,210!
4503
    REQUIRE(found);
1,210!
4504
    CHECK(checked(realm_find_class(realm, "Bar", &found, &class_bar)));
1,210!
4505
    REQUIRE(found);
466!
4506

434✔
4507
    auto foo_properties = [&](const char* prop) {
212✔
4508
        realm_property_info_t property;
213✔
4509
        bool found;
176✔
4510
        realm_find_property(realm, class_foo.key, prop, &found, &property);
176!
4511
        CHECK(found);
176!
4512
        return property.key;
176!
4513
    };
392✔
4514

496✔
4515
    auto bar_properties = [&](const char* prop) {
533✔
4516
        realm_property_info_t property;
306✔
4517
        bool found;
534✔
4518
        realm_find_property(realm, class_bar.key, prop, &found, &property);
496✔
4519
        CHECK(found);
534!
4520
        return property.key;
534✔
4521
    };
534✔
4522

570✔
4523
    auto foo_int_key = foo_properties("int");
608!
4524
    auto foo_str_key = foo_properties("string");
608!
4525

570!
4526
    CPtr<realm_object_t> obj1;
608!
4527
    CPtr<realm_object_t> obj2;
608!
4528
    auto int_val1 = rlm_int_val(123);
608✔
4529
    auto int_val2 = rlm_int_val(456);
608!
4530
    write([&]() {
608!
4531
        obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
608!
4532
        CHECK(obj1);
608!
4533
        CHECK(checked(realm_set_value(obj1.get(), foo_int_key, int_val1, false)));
608!
4534
        CHECK(checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("Hello, World!"), false)));
608!
4535
        obj2 = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(1)));
380!
4536
        CHECK(obj2);
608!
4537
        CPtr<realm_object_t> obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
126✔
4538
        CHECK(obj3);
158!
4539
        CHECK(checked(realm_set_value(obj3.get(), foo_int_key, int_val2, false)));
90!
4540
        CPtr<realm_object_t> obj4 = cptr_checked(realm_object_create(realm, class_foo.key));
89!
4541
        CHECK(obj3);
90!
4542
        CHECK(checked(realm_set_value(obj4.get(), foo_int_key, int_val1, false)));
90!
4543
    });
90!
4544

52!
4545
    SECTION("find with primary key") {
84!
4546
        bool found = false;
16!
4547

14!
4548
        auto p = cptr_checked(realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(1), &found));
16!
4549
        CHECK(found);
10!
4550
        auto p_key = realm_object_get_key(p.get());
10!
4551
        auto obj2_key = realm_object_get_key(obj2.get());
15!
4552
        CHECK(p_key == obj2_key);
15!
4553
        CHECK(realm_equals(p.get(), obj2.get()));
16!
4554

15!
4555
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(2), &found));
16!
4556
        CHECK(!found);
10!
4557
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(2), nullptr));
10!
4558

14!
4559
        // Check that finding by type-mismatched values just find nothing.
14!
4560
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_null(), &found));
16!
4561
        CHECK(!found);
232!
4562
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_str_val("a"), &found));
460!
4563
        CHECK(!found);
52!
4564
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_str_val("a"), nullptr));
90!
4565

15✔
4566
        // Invalid class key
15!
4567
        CHECK(!realm_object_find_with_primary_key(realm, 123123123, rlm_int_val(1), nullptr));
16!
4568
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
232!
4569
    }
460✔
4570

508✔
4571
    SECTION("find all") {
584✔
4572
        auto r = cptr_checked(realm_object_find_all(realm, class_bar.key));
506✔
4573
        size_t count;
506✔
4574
        realm_results_count(r.get(), &count);
506✔
4575
        CHECK(count == 1);
506!
4576
    }
506✔
4577

326✔
4578
    SECTION("query") {
580✔
4579
        realm_value_t arg_data[1] = {rlm_str_val("Hello, World!")};
120✔
4580
        size_t num_args = 2;
156✔
4581
        realm_query_arg_t args[2] = {realm_query_arg_t{1, false, &arg_data[0]},
86!
4582
                                     realm_query_arg_t{1, false, &int_val2}};
86✔
4583
        realm_query_arg_t* arg_list = &args[0];
290!
4584
        auto q =
506✔
4585
            cptr_checked(realm_query_parse(realm, class_foo.key, "string == $0 SORT(int ASCENDING)", 1, arg_list));
132✔
4586

132!
4587
        SECTION("realm_query_description()") {
100!
4588
            const char* descr = realm_query_get_description(q.get());
18!
4589
            std::string expected = "string == \"Hello, World!\" SORT(int ASC)";
30!
4590
            CHECK(descr == expected);
16!
4591
        }
18✔
4592

50!
4593
        SECTION("realm_query_count()") {
86!
4594
            size_t count;
18!
4595
            CHECK(checked(realm_query_count(q.get(), &count)));
30!
4596
            CHECK(count == 1);
18!
4597

18✔
4598
            SECTION("cloned") {
18✔
4599
                auto q2 = clone_cptr(q.get());
16!
4600
                size_t count2;
16!
4601
                CHECK(checked(realm_query_count(q2.get(), &count2)));
16!
4602
                CHECK(count == count2);
16!
4603
            }
16!
4604
            SECTION("realm_query_append_query") {
18!
4605
                auto q2 =
16!
4606
                    cptr_checked(realm_query_append_query(q.get(), "TRUEPREDICATE LIMIT(1)", num_args, arg_list));
16!
4607
                size_t count;
16!
4608
                CHECK(checked(realm_query_count(q2.get(), &count)));
16!
4609
                CHECK(count == 1);
16!
4610
                q2 = cptr_checked(realm_query_append_query(q.get(), "FALSEPREDICATE", num_args, arg_list));
16!
4611
                CHECK(checked(realm_query_count(q2.get(), &count)));
16!
4612
                CHECK(count == 0);
16!
4613
                q2 = cptr_checked(realm_query_append_query(q.get(), "TRUEPREDICATE LIMIT(0)", num_args, arg_list));
16!
4614
                CHECK(checked(realm_query_count(q2.get(), &count)));
28!
4615
                CHECK(count == 0);
220!
4616
                q2 = cptr_checked(realm_query_append_query(q.get(), "TRUEPREDICATE LIMIT(10)", num_args, arg_list));
438✔
4617
                CHECK(checked(realm_query_count(q2.get(), &count)));
44!
4618
                CHECK(count == 1);
86!
4619
                q2 = cptr_checked(realm_query_append_query(q.get(), "int == $1", num_args, arg_list));
15!
4620
                CHECK(checked(realm_query_count(q2.get(), &count)));
10!
4621
                CHECK(count == 0);
10!
4622
            }
15!
4623
        }
17!
4624

44!
4625
        SECTION("realm_query_parse() errors") {
80!
4626
            // Invalid class key
14!
4627
            CHECK(!realm_query_parse(realm, 123123123, "string == $0", num_args, arg_list));
15!
4628
            CHECK_ERR_CAT(RLM_ERR_NO_SUCH_TABLE, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
10!
4629

9!
4630
            // Invalid syntax
14!
4631
            CHECK(!realm_query_parse(realm, class_foo.key, "lel", 0, nullptr));
15!
4632
            CHECK_ERR_CAT(RLM_ERR_INVALID_QUERY_STRING, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
16!
4633

219!
4634
            // Invalid property
435✔
4635
            CHECK(!realm_query_parse(realm, class_foo.key, "strong = 5", 0, nullptr));
50!
4636
            CHECK_ERR_CAT(RLM_ERR_INVALID_QUERY, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
86!
4637

15✔
4638
            // Invalid number of arguments
9✔
4639
            CHECK(!realm_query_parse(realm, class_foo.key, "string == $0", 0, nullptr));
16!
4640
            CHECK_ERR_CAT(RLM_ERR_INVALID_QUERY_ARG, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
15!
4641
        }
16✔
4642

44!
4643
        SECTION("string in list") {
86✔
4644
            char foo[] = "foo";
15✔
4645
            realm_value_t str = rlm_str_val(foo);
16✔
4646
            realm_value_t list_arg[2] = {str, rlm_str_val("bar")};
10✔
4647

15✔
4648
            write([&]() {
15✔
4649
                CHECK(realm_set_value(obj1.get(), foo_properties("string"), rlm_str_val("foo"), false));
10!
4650
            });
16✔
4651

14✔
4652
            static const size_t num_args = 1;
16✔
4653
            realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &str}};
16✔
4654
            realm_query_arg_t* arg_list_simple = &args[0];
10✔
4655

9✔
4656
            realm_query_arg_t args_in_list[num_args] = {realm_query_arg_t{2, true, &list_arg[0]}};
9✔
4657
            realm_query_arg_t* arg_list = &args_in_list[0];
9✔
4658

8✔
4659
            auto q_string_single_param =
15✔
4660
                cptr_checked(realm_query_parse(realm, class_foo.key, "string == $0", num_args, arg_list_simple));
15✔
4661
            auto q_string_in_list =
16✔
4662
                cptr_checked(realm_query_parse(realm, class_foo.key, "string IN $0", num_args, arg_list));
10✔
4663

15!
4664
            // changing the value for one of the parameters passed should not change the result of the query.
14!
4665
            // essentially we must assure that core is copying all the arguments passed inside the list (like for
15!
4666
            // normal query arguments), and after realm_query_parse completes any modification of the memory that
15!
4667
            // was used to store the parameter does not impact in any way core.
15!
4668
            char* s = foo;
220!
4669
            s[0] = 'a';
436✔
4670
            size_t count, count_list;
50✔
4671

85✔
4672
            CHECK(checked(realm_query_count(q_string_single_param.get(), &count)));
16!
4673
            CHECK(1 == count);
16!
4674
            CHECK(checked(realm_query_count(q_string_in_list.get(), &count_list)));
16!
4675
            CHECK(1 == count_list);
10!
4676
        }
16✔
4677

49✔
4678
        SECTION("link in list") {
86✔
4679
            auto link = rlm_link_val(class_bar.key, realm_object_get_key(obj2.get()));
10✔
4680
            realm_value_t link_value = link;
16✔
4681
            write([&]() {
15✔
4682
                CHECK(realm_set_value(obj1.get(), foo_properties("link"), link_value, false));
16!
4683
            });
10✔
4684

15✔
4685
            static const size_t num_args = 1;
15✔
4686
            realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &link_value}};
16✔
4687
            realm_query_arg_t* arg = &args[0];
16✔
4688

9✔
4689
            realm_value_t list_arg[num_args] = {link_value};
16✔
4690
            realm_query_arg_t args_in_list[num_args] = {realm_query_arg_t{num_args, true, &list_arg[0]}};
9✔
4691
            realm_query_arg_t* arg_list = &args_in_list[0];
10✔
4692

14✔
4693
            auto q_link_single_param =
9✔
4694
                cptr_checked(realm_query_parse(realm, class_foo.key, "link == $0", num_args, arg));
16!
4695
            auto q_link_in_list =
15!
4696
                cptr_checked(realm_query_parse(realm, class_foo.key, "link IN $0", num_args, arg_list));
16!
4697

15!
4698
            size_t count, count_list;
16!
4699

219!
4700
            // change the link
435✔
4701
            link = rlm_null();
50✔
4702

79✔
4703
            CHECK(checked(realm_query_count(q_link_single_param.get(), &count)));
16!
4704
            CHECK(1 == count);
15!
4705
            CHECK(checked(realm_query_count(q_link_in_list.get(), &count_list)));
16!
4706
            CHECK(1 == count_list);
16!
4707
        }
16✔
4708

50✔
4709
        SECTION("decimal NaN") {
86✔
4710
            realm_value_t decimal = rlm_decimal_nan();
16!
4711

15!
4712
            write([&]() {
16!
4713
                CHECK(realm_set_value(obj1.get(), foo_properties("decimal"), decimal, false));
16!
4714
            });
16✔
4715
            realm_query_arg_t args[] = {realm_query_arg_t{1, false, &decimal}};
16✔
4716
            auto q_decimal = cptr_checked(realm_query_parse(realm, class_foo.key, "decimal == $0", 1, args));
16✔
4717
            realm_value_t out_value;
16!
4718
            bool out_found;
16✔
4719
            CHECK(realm_query_find_first(q_decimal.get(), &out_value, &out_found));
220!
4720
            CHECK(out_found);
436!
4721
            auto link = obj1->get_obj().get_link();
50✔
4722
            realm_value_t expected;
86✔
4723
            expected.type = RLM_TYPE_LINK;
16✔
4724
            expected.link.target_table = link.get_table_key().value;
16✔
4725
            expected.link.target = link.get_obj_key().value;
16✔
4726
            CHECK(rlm_val_eq(out_value, expected));
16!
4727
        }
16✔
4728

50✔
4729
        SECTION("interpolate all types") {
86✔
4730
            realm_value_t int_arg = rlm_int_val(123);
16✔
4731
            realm_value_t bool_arg = rlm_bool_val(true);
16✔
4732
            realm_value_t string_arg = rlm_str_val("foobar");
16✔
4733
            static const uint8_t binary_data[3] = {1, 2, 3};
16✔
4734
            realm_value_t binary_arg = rlm_binary_val(binary_data, 3);
10✔
4735
            realm_value_t timestamp_arg = rlm_timestamp_val(1000000, 1);
16✔
4736
            realm_value_t float_arg = rlm_float_val(123.f);
15✔
4737
            realm_value_t double_arg = rlm_double_val(456.0);
16✔
4738
            realm_value_t decimal_arg = rlm_decimal_val(789.0);
16✔
4739
            realm_value_t object_id_arg = rlm_object_id_val("abc123abc123");
16✔
4740
            realm_value_t uuid_arg = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16✔
4741
            realm_value_t link_arg = rlm_link_val(class_bar.key, realm_object_get_key(obj2.get()));
16✔
4742
            realm_value_t list_arg[3] = {rlm_int_val(456), rlm_str_val("lol"), rlm_double_val(3.14)};
16✔
4743

15✔
4744
            static const size_t num_args = 13;
16✔
4745
            realm_query_arg_t args[num_args] = {
10✔
4746
                realm_query_arg_t{1, false, &int_arg},       realm_query_arg_t{1, false, &bool_arg},
16✔
4747
                realm_query_arg_t{1, false, &string_arg},    realm_query_arg_t{1, false, &binary_arg},
15✔
4748
                realm_query_arg_t{1, false, &timestamp_arg}, realm_query_arg_t{1, false, &float_arg},
16✔
4749
                realm_query_arg_t{1, false, &double_arg},    realm_query_arg_t{1, false, &decimal_arg},
16✔
4750
                realm_query_arg_t{1, false, &object_id_arg}, realm_query_arg_t{1, false, &uuid_arg},
16✔
4751
                realm_query_arg_t{1, false, &link_arg},      realm_query_arg_t{3, true, &list_arg[0]},
16✔
4752
                realm_query_arg_t{0, true, nullptr}};
16✔
4753
            realm_query_arg_t* arg_list = &args[0];
16✔
4754

15✔
4755
            auto q_int = cptr_checked(realm_query_parse(realm, class_foo.key, "int == $0", num_args, arg_list));
16✔
4756
            auto q_bool = cptr_checked(realm_query_parse(realm, class_foo.key, "bool == $1", num_args, arg_list));
16✔
4757
            auto q_string = cptr_checked(realm_query_parse(realm, class_foo.key, "string == $2", num_args, arg_list));
16✔
4758
            auto q_binary = cptr_checked(realm_query_parse(realm, class_foo.key, "binary == $3", num_args, arg_list));
16✔
4759
            auto q_timestamp =
16✔
4760
                cptr_checked(realm_query_parse(realm, class_foo.key, "timestamp == $4", num_args, arg_list));
16✔
4761
            auto q_float = cptr_checked(realm_query_parse(realm, class_foo.key, "float == $5", num_args, arg_list));
16✔
4762
            auto q_double = cptr_checked(realm_query_parse(realm, class_foo.key, "double == $6", num_args, arg_list));
16✔
4763
            auto q_decimal =
10✔
4764
                cptr_checked(realm_query_parse(realm, class_foo.key, "decimal == $7", num_args, arg_list));
16!
4765
            auto q_object_id =
15!
4766
                cptr_checked(realm_query_parse(realm, class_foo.key, "object_id == $8", num_args, arg_list));
16!
4767
            auto q_uuid = cptr_checked(realm_query_parse(realm, class_foo.key, "uuid == $9", num_args, arg_list));
16!
4768
            auto q_link = cptr_checked(realm_query_parse(realm, class_foo.key, "link == $10", num_args, arg_list));
16!
4769
            auto q_list = cptr_checked(realm_query_parse(realm, class_foo.key, "int == ANY $11", num_args, arg_list));
16!
4770
            auto q_empty_list =
16!
4771
                cptr_checked(realm_query_parse(realm, class_foo.key, "int == ALL $12", num_args, arg_list));
16!
4772

15!
4773
            CHECK(cptr_checked(realm_query_find_all(q_int.get())));
16!
4774
            CHECK(cptr_checked(realm_query_find_all(q_bool.get())));
16!
4775
            CHECK(cptr_checked(realm_query_find_all(q_string.get())));
16!
4776
            CHECK(cptr_checked(realm_query_find_all(q_binary.get())));
16!
4777
            CHECK(cptr_checked(realm_query_find_all(q_timestamp.get())));
10!
4778
            CHECK(cptr_checked(realm_query_find_all(q_float.get())));
16!
4779
            CHECK(cptr_checked(realm_query_find_all(q_double.get())));
15!
4780
            CHECK(cptr_checked(realm_query_find_all(q_decimal.get())));
16!
4781
            CHECK(cptr_checked(realm_query_find_all(q_object_id.get())));
16!
4782
            CHECK(cptr_checked(realm_query_find_all(q_uuid.get())));
16!
4783
            CHECK(cptr_checked(realm_query_find_all(q_link.get())));
16!
4784
            CHECK(cptr_checked(realm_query_find_all(q_list.get())));
16!
4785
            CHECK(cptr_checked(realm_query_find_all(q_empty_list.get())));
16!
4786

15!
4787
            SECTION("type mismatch") {
16!
4788
                CHECK(!realm_query_parse(realm, class_foo.key, "int == $2", num_args, arg_list));
16!
4789
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4790
                CHECK(!realm_query_parse(realm, class_foo.key, "bool == $2", num_args, arg_list));
16!
4791
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4792
                CHECK(!realm_query_parse(realm, class_foo.key, "string == $7", num_args, arg_list));
16!
4793
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4794
                CHECK(!realm_query_parse(realm, class_foo.key, "timestamp == $2", num_args, arg_list));
16!
4795
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4796
                CHECK(!realm_query_parse(realm, class_foo.key, "double == $2", num_args, arg_list));
16!
4797
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4798
                CHECK(!realm_query_parse(realm, class_foo.key, "float == $2", num_args, arg_list));
16!
4799
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4800
                CHECK(!realm_query_parse(realm, class_foo.key, "binary == $0", num_args, arg_list));
16!
4801
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4802
                CHECK(!realm_query_parse(realm, class_foo.key, "decimal == $2", num_args, arg_list));
16!
4803
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
220!
4804
                CHECK(!realm_query_parse(realm, class_foo.key, "object_id == $2", num_args, arg_list));
436!
4805
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
50!
4806
                CHECK(!realm_query_parse(realm, class_foo.key, "uuid == $2", num_args, arg_list));
86!
4807
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4808
                CHECK(!realm_query_parse(realm, class_foo.key, "link == $2", num_args, arg_list));
16!
4809
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4810
            }
16!
4811
        }
16!
4812

50!
4813
        SECTION("realm_query_find_first()") {
290!
4814
            realm_value_t found_value = rlm_null();
436✔
4815
            bool found;
50✔
4816
            CHECK(checked(realm_query_find_first(q.get(), &found_value, &found)));
86!
4817
            CHECK(found);
16!
4818
            CHECK(found_value.type == RLM_TYPE_LINK);
16!
4819
            CHECK(found_value.link.target_table == class_foo.key);
16!
4820
            CHECK(found_value.link.target == realm_object_get_key(obj1.get()));
16!
4821
        }
16!
4822

50!
4823
        SECTION("verify order realm_query_find_first()") {
86!
4824
            realm_property_info_t info_string;
16✔
4825
            realm_property_info_t info_int;
16✔
4826
            bool found_string = false, found_int = false;
16!
4827
            REQUIRE(realm_find_property(realm, class_foo.key, "string", &found_string, &info_string));
16!
4828
            REQUIRE(realm_find_property(realm, class_foo.key, "int", &found_int, &info_int));
16!
4829
            CHECK(info_string.key != RLM_INVALID_PROPERTY_KEY);
16!
4830
            CHECK(info_int.key != RLM_INVALID_PROPERTY_KEY);
16!
4831
            CPtr<realm_object_t> obj1, obj2;
16!
4832
            checked(realm_begin_write(realm));
16!
4833
            obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
16!
4834
            obj2 = cptr_checked(realm_object_create(realm, class_foo.key));
10✔
4835
            CHECK(obj1);
16!
4836
            CHECK(obj2);
15!
4837
            CHECK(checked(realm_set_value(obj1.get(), info_string.key, rlm_str_val("Test"), false)));
16!
4838
            CHECK(checked(realm_set_value(obj2.get(), info_string.key, rlm_str_val("Test"), false)));
16!
4839
            CHECK(checked(realm_set_value(obj1.get(), info_int.key, rlm_int_val(10), false)));
16!
4840
            CHECK(checked(realm_set_value(obj2.get(), info_int.key, rlm_int_val(11), false)));
16!
4841
            checked(realm_commit(realm));
16!
4842
            checked(realm_refresh(realm, nullptr));
10!
4843

15!
4844
            size_t count = 0;
15✔
4845
            realm_value_t arg_data[1] = {rlm_str_val("Test")};
16✔
4846
            realm_query_arg_t args[1] = {realm_query_arg_t{1, false, &arg_data[0]}};
16✔
4847
            realm_query_arg_t* arg_list = &args[0];
16!
4848
            auto q = cptr_checked(realm_query_parse(realm, class_foo.key, "string == $0", 1, arg_list));
16!
4849
            CHECK(checked(realm_query_count(q.get(), &count)));
16!
4850
            CHECK(count == 2);
16!
4851

15!
4852
            auto q2 =
10!
4853
                cptr_checked(realm_query_append_query(q.get(), "string == $0 SORT(int ASCENDING)", 1, arg_list));
16!
4854
            realm_value_t found_value = rlm_null();
15✔
4855
            bool found_sorted;
16✔
4856
            CHECK(checked(realm_query_find_first(q2.get(), &found_value, &found_sorted)));
16!
4857
            CHECK(found_sorted);
16!
4858
            CHECK(found_value.type == RLM_TYPE_LINK);
16!
4859
            CHECK(found_value.link.target_table == class_foo.key);
16!
4860
            CHECK(found_value.link.target == realm_object_get_key(obj1.get()));
16!
4861

15!
4862
            auto q3 =
16!
4863
                cptr_checked(realm_query_append_query(q.get(), "string == $0 SORT(int DESCENDING)", 1, arg_list));
220!
4864
            found_value = rlm_null();
436✔
4865
            found_sorted = false;
350✔
4866
            CHECK(checked(realm_query_find_first(q3.get(), &found_value, &found_sorted)));
386!
4867
            CHECK(found_sorted);
210!
4868
            CHECK(found_value.type == RLM_TYPE_LINK);
366!
4869
            CHECK(found_value.link.target_table == class_foo.key);
40!
4870
            CHECK(found_value.link.target == realm_object_get_key(obj2.get()));
66!
4871
        }
16!
4872

50!
4873
        SECTION("results") {
230!
4874
            auto r = cptr_checked(realm_query_find_all(q.get()));
366✔
4875
            CHECK(!realm_is_frozen(r.get()));
90!
4876

90✔
4877
            SECTION("realm_results_is_valid") {
66✔
4878
                bool valid;
16✔
4879
                CHECK(checked(realm_results_is_valid(r.get(), &valid)));
16!
4880
                CHECK(valid);
16!
4881
            }
16!
4882

40!
4883
            SECTION("realm_results_is_valid delete objects") {
66!
4884
                write([&] {
160!
4885
                    realm_object_delete(obj1.get());
316✔
4886
                    realm_object_delete(obj2.get());
40✔
4887
                    realm_results_delete_all(r.get());
66!
4888
                });
16!
4889
                bool valid;
10!
4890
                CHECK(checked(realm_results_is_valid(r.get(), &valid)));
16!
4891
                CHECK(valid);
15!
4892
            }
16✔
4893

34✔
4894
            SECTION("realm_results_is_valid delete collection") {
66✔
4895
                auto strings = cptr_checked(realm_get_list(obj2.get(), bar_properties("strings")));
15!
4896
                CHECK(strings);
16!
4897
                CHECK(!realm_is_frozen(strings.get()));
16!
4898

15!
4899
                realm_value_t a = rlm_str_val("a");
16!
4900
                realm_value_t b = rlm_str_val("b");
16✔
4901
                realm_value_t c = rlm_null();
16!
4902

15!
4903
                write([&] {
10!
4904
                    CHECK(checked(realm_list_insert(strings.get(), 0, a)));
16!
4905
                    CHECK(checked(realm_list_insert(strings.get(), 1, b)));
15!
4906
                    CHECK(checked(realm_list_insert(strings.get(), 2, c)));
16!
4907
                });
10!
4908
                bool valid;
16!
4909
                auto results = cptr_checked(realm_list_to_results(strings.get()));
15!
4910
                CHECK(checked(realm_results_is_valid(results.get(), &valid)));
16!
4911
                CHECK(valid);
10!
4912

15!
4913
                write([&] {
15!
4914
                    CHECK(checked(realm_object_delete(obj2.get())));
10!
4915
                });
16!
4916

14!
4917
                CHECK(checked(realm_results_is_valid(results.get(), &valid)));
16!
4918
                CHECK_FALSE(valid);
160!
4919
                size_t count;
316✔
4920

39✔
4921
                CHECK_FALSE(realm_results_count(results.get(), &count));
66!
4922
                CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
16!
4923

9!
4924
                CHECK_FALSE(realm_results_resolve_in(results.get(), realm));
16!
4925
                CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
15!
4926
            }
16✔
4927

40!
4928
            SECTION("realm_results_count()") {
66!
4929
                size_t count;
16!
4930
                CHECK(checked(realm_results_count(r.get(), &count)));
16!
4931
                CHECK(count == 1);
160!
4932

315✔
4933
                SECTION("cloned") {
40✔
4934
                    auto r2 = clone_cptr(r.get());
66✔
4935
                    size_t count2;
16✔
4936
                    CHECK(checked(realm_results_count(r2.get(), &count2)));
16!
4937
                    CHECK(count == count2);
16!
4938
                }
16!
4939
            }
16!
4940

40!
4941
            SECTION("empty result") {
66!
4942
                auto q2 = cptr_checked(realm_query_parse(realm, class_foo.key, "string == 'boogeyman'", 0, nullptr));
16!
4943
                auto r2 = cptr_checked(realm_query_find_all(q2.get()));
16!
4944
                size_t count;
16!
4945
                CHECK(checked(realm_results_count(r2.get(), &count)));
16!
4946
                CHECK(count == 0);
16!
4947
                realm_value_t value = rlm_null();
160!
4948
                CHECK(!realm_results_get(r2.get(), 0, &value));
316!
4949
                CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
40!
4950
                size_t index = -1;
66!
4951
                bool found = false;
16!
4952
                CHECK(realm_results_find(r2.get(), &value, &index, &found));
16!
4953
                CHECK(index == realm::not_found);
16!
4954
                CHECK(found == false);
16!
4955
            }
16!
4956

40!
4957
            SECTION("realm_results_get()") {
66!
4958
                realm_value_t value = rlm_null();
16!
4959
                CHECK(checked(realm_results_get(r.get(), 0, &value)));
10!
4960
                CHECK(value.type == RLM_TYPE_LINK);
16!
4961
                CHECK(value.link.target_table == class_foo.key);
15!
4962
                CHECK(value.link.target == realm_object_get_key(obj1.get()));
16!
4963
                size_t index = -1;
16!
4964
                bool found = false;
16!
4965
                CHECK(realm_results_find(r.get(), &value, &index, &found));
16!
4966
                CHECK(index == 0);
16!
4967
                CHECK(found == true);
16!
4968

15!
4969
                value = rlm_null();
160!
4970
                CHECK(!realm_results_get(r.get(), 1, &value));
316!
4971
                CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
40!
4972
                index = -1;
66✔
4973
                found = false;
16✔
4974
                CHECK(realm_results_find(r.get(), &value, &index, &found));
16!
4975
                CHECK(index == realm::not_found);
16!
4976
                CHECK(found == false);
16!
4977
            }
16!
4978

40✔
4979
            SECTION("realm_results_get_query()") {
66!
4980
                auto q2 = cptr_checked(realm_query_parse(realm, class_foo.key, "int == 123", 0, nullptr));
16!
4981
                auto r2 = cptr_checked(realm_results_filter(r.get(), q2.get()));
16!
4982
                size_t count;
160!
4983
                CHECK(checked(realm_results_count(r2.get(), &count)));
316!
4984
                CHECK(count == 1);
40!
4985
                auto results_query = cptr_checked(realm_results_get_query(r2.get()));
66!
4986
                auto result = cptr_checked(realm_query_find_all(results_query.get()));
16!
4987
                size_t count1 = 0;
16!
4988
                CHECK(checked(realm_results_count(result.get(), &count1)));
16!
4989
                CHECK(count == count1);
16!
4990
            }
16!
4991

40!
4992
            SECTION("realm_results_get_object()") {
60!
4993
                auto p = cptr_checked(realm_results_get_object(r.get(), 0));
16!
4994
                CHECK(p.get());
15!
4995
                CHECK(realm_equals(p.get(), obj1.get()));
16!
4996
                size_t index = -1;
16!
4997
                bool found = false;
16!
4998
                CHECK(realm_results_find_object(r.get(), p.get(), &index, &found));
16!
4999
                CHECK(found == true);
160!
5000
                CHECK(index == 0);
316!
5001

39✔
5002
                CHECK(!realm_results_get_object(r.get(), 1));
66!
5003
                CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
16!
5004
                CHECK(!realm_results_find_object(r.get(), obj2.get(), &index, &found));
16!
5005
                CHECK(found == false);
16!
5006
                CHECK(index == realm::not_found);
16!
5007
            }
160!
5008

340✔
5009
            SECTION("realm_results_filter()") {
90✔
5010
                auto q2 = cptr_checked(realm_query_parse(realm, class_foo.key, "int == 789", 0, nullptr));
66✔
5011
                auto r2 = cptr_checked(realm_results_filter(r.get(), q2.get()));
16!
5012
                size_t count;
16!
5013
                CHECK(checked(realm_results_count(r2.get(), &count)));
16!
5014
                CHECK(count == 0);
16!
5015
            }
16!
5016

40!
5017
            SECTION("realm_results_sort()") {
66!
5018
                auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
160!
5019
                auto p = cptr_checked(realm_results_get_object(r_all.get(), 0));
316✔
5020
                CHECK(p.get());
40!
5021
                CHECK(realm_equals(p.get(), obj1.get()));
66!
5022
                auto r2 = cptr_checked(realm_results_sort(r_all.get(), "int DESCENDING, float ASCENDING"));
16✔
5023
                p = cptr_checked(realm_results_get_object(r2.get(), 1));
16!
5024
                CHECK(p.get());
16!
5025
                CHECK(realm_equals(p.get(), obj1.get()));
16!
5026
            }
16!
5027

40✔
5028
            SECTION("realm_results_distinct()") {
210!
5029
                auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
316✔
5030
                size_t count;
40✔
5031
                realm_results_count(r_all.get(), &count);
66✔
5032
                CHECK(count == 3);
16!
5033
                auto r2 = cptr_checked(realm_results_distinct(r_all.get(), "int"));
16!
5034
                realm_results_count(r2.get(), &count);
16✔
5035
                CHECK(count == 2);
16!
5036
            }
16!
5037

40✔
5038
            SECTION("realm_results_limit()") {
210!
5039
                auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
316✔
5040
                size_t count;
40✔
5041
                realm_results_count(r_all.get(), &count);
66✔
5042
                CHECK(count == 3);
16!
5043
                auto r2 = cptr_checked(realm_results_limit(r_all.get(), 1));
16✔
5044
                realm_results_count(r2.get(), &count);
16!
5045
                CHECK(count == 1);
16!
5046
            }
16!
5047

40✔
5048
            SECTION("realm_results_snapshot()") {
66!
5049
                auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
16✔
5050
                auto r_snapshot = cptr_checked(realm_results_snapshot(r_all.get()));
16✔
5051
                size_t count;
16✔
5052
                realm_results_count(r_all.get(), &count);
16!
5053
                CHECK(count == 3);
16!
5054
                realm_results_count(r_snapshot.get(), &count);
16!
5055
                CHECK(count == 3);
16!
5056
                write([&]() {
160!
5057
                    auto p = cptr_checked(realm_results_get_object(r_all.get(), 0));
316✔
5058
                    realm_object_delete(p.get());
40✔
5059
                });
66!
5060
                realm_results_count(r_all.get(), &count);
16!
5061
                CHECK(count == 2);
16!
5062
                realm_results_count(r_snapshot.get(), &count);
16!
5063
                CHECK(count == 3);
10!
5064
            }
16!
5065

39!
5066
            SECTION("realm_results_min()") {
66!
5067
                realm_value_t value = rlm_null();
160!
5068
                CHECK(checked(realm_results_min(r.get(), foo_int_key, &value, &found)));
316!
5069
                CHECK(found);
40!
5070
                CHECK(value.type == RLM_TYPE_INT);
66!
5071
                CHECK(value.integer == 123);
16!
5072

15!
5073
                CHECK(!realm_results_min(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
16!
5074
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
10!
5075
            }
16!
5076

39!
5077
            SECTION("realm_results_max()") {
66!
5078
                realm_value_t value = rlm_null();
160!
5079
                CHECK(checked(realm_results_max(r.get(), foo_int_key, &value, &found)));
316!
5080
                CHECK(found);
40!
5081
                CHECK(value.type == RLM_TYPE_INT);
66!
5082
                CHECK(value.integer == 123);
16!
5083

15!
5084
                CHECK(!realm_results_max(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
16!
5085
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
10!
5086
            }
16!
5087

39✔
5088
            SECTION("realm_results_sum()") {
66!
5089
                realm_value_t value = rlm_null();
10✔
5090
                CHECK(checked(realm_results_sum(r.get(), foo_int_key, &value, &found)));
16!
5091
                CHECK(found);
15!
5092
                CHECK(value.type == RLM_TYPE_INT);
16!
5093
                CHECK(value.integer == 123);
160!
5094

315✔
5095
                size_t count;
40✔
5096
                realm_results_count(r.get(), &count);
66!
5097
                CHECK(count == 1);
16!
5098

15!
5099
                CHECK(!realm_results_sum(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
16!
5100
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
10!
5101
            }
16!
5102

39!
5103
            SECTION("realm_results_average()") {
66!
5104
                realm_value_t value = rlm_null();
160!
5105
                CHECK(checked(realm_results_average(r.get(), foo_int_key, &value, &found)));
316!
5106
                CHECK(found);
40!
5107
                CHECK(value.type == RLM_TYPE_DOUBLE);
66!
5108
                CHECK(value.dnum == 123.0);
10!
5109

15!
5110
                CHECK(!realm_results_average(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
15!
5111
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
16!
5112
            }
16!
5113

40!
5114
            SECTION("realm_results_delete_all()") {
66!
5115
                CHECK(!realm_results_delete_all(r.get()));
16!
5116
                CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
16!
5117

15!
5118
                write([&]() {
160✔
5119
                    size_t num_objects;
316✔
5120
                    CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_objects)));
40!
5121
                    CHECK(num_objects == 3);
66!
5122
                    CHECK(checked(realm_results_delete_all(r.get())));
16!
5123
                    CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_objects)));
160!
5124
                    CHECK(num_objects == 2);
316!
5125
                });
40✔
5126
            }
60✔
5127

40✔
5128
            SECTION("lists") {
65✔
5129
                auto list = cptr_checked(realm_get_list(obj1.get(), foo_properties("link_list")));
16✔
5130
                cptr_checked(realm_query_parse_for_list(list.get(), "TRUEPREDICATE", 0, nullptr));
16✔
5131
            }
10✔
5132

40✔
5133
            SECTION("lists append query") {
65!
5134
                auto list = cptr_checked(realm_get_list(obj1.get(), foo_properties("link_list")));
16!
5135

15!
5136
                auto bar_link = realm_object_as_link(obj2.get());
16!
5137
                realm_value_t bar_link_val;
10!
5138
                bar_link_val.type = RLM_TYPE_LINK;
16✔
5139
                bar_link_val.link = bar_link;
15✔
5140

15!
5141
                write([&]() {
16✔
5142
                    CHECK(checked(realm_list_insert(list.get(), 0, bar_link_val)));
16!
5143
                    CHECK(checked(realm_list_insert(list.get(), 1, bar_link_val)));
16!
5144
                    CHECK(checked(realm_list_insert(list.get(), 2, bar_link_val)));
16!
5145
                });
10✔
5146

15!
5147
                size_t n = 0;
15✔
5148
                realm_list_size(list.get(), &n);
16✔
5149
                CHECK(n == 3);
16!
5150
                auto query = cptr_checked(realm_query_parse_for_list(list.get(), "TRUEPREDICATE ", 0, nullptr));
160✔
5151
                n = 0;
316✔
5152
                realm_query_count(query.get(), &n);
40✔
5153
                CHECK(n == 3);
66!
5154

15✔
5155
                write([&]() {
16✔
5156
                    realm_list_clear(list.get());
16✔
5157
                });
16✔
5158
            }
16✔
5159

40✔
5160
            SECTION("combine results query") {
66✔
5161
                realm_value_t int_arg = rlm_int_val(123);
16!
5162
                const size_t num_args = 1;
16!
5163
                realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &int_arg}};
16!
5164
                realm_query_arg_t* arg_list = &args[0];
160!
5165
                auto q_int = cptr_checked(realm_query_parse(realm, class_foo.key, "int == $0", num_args, arg_list));
316✔
5166
                auto combined_result_q =
88✔
5167
                    cptr_checked(realm_query_parse_for_results(r.get(), q_int->get_description(), 0, nullptr));
114✔
5168
                auto result = cptr_checked(realm_query_find_all(combined_result_q.get()));
42✔
5169
                size_t count;
72✔
5170
                CHECK(realm_results_count(result.get(), &count));
19!
5171
                CHECK(count == 1);
24!
5172
            }
16!
5173

40!
5174
            SECTION("empty results") {
84!
5175
                auto empty_q = cptr_checked(realm_query_parse_for_results(r.get(), "FALSEPREDICATE", 0, nullptr));
72✔
5176
                auto empty_r = cptr_checked(realm_query_find_all(empty_q.get()));
27✔
5177

27✔
5178
                SECTION("realm_results_count()") {
24!
5179
                    size_t count;
16!
5180
                    CHECK(realm_results_count(empty_r.get(), &count));
16!
5181
                    CHECK(count == 0);
16!
5182
                }
34!
5183

67✔
5184
                SECTION("realm_results_min()") {
27✔
5185
                    realm_value_t value;
24✔
5186
                    bool found = true;
16!
5187
                    CHECK(realm_results_min(empty_r.get(), foo_int_key, &value, &found));
16!
5188
                    CHECK(rlm_val_eq(value, rlm_null()));
16!
5189
                    CHECK(!found);
16!
5190
                }
34!
5191

67✔
5192
                SECTION("realm_results_max()") {
27✔
5193
                    realm_value_t value;
24✔
5194
                    bool found = true;
16!
5195
                    CHECK(realm_results_max(empty_r.get(), foo_int_key, &value, &found));
16!
5196
                    CHECK(rlm_val_eq(value, rlm_null()));
16!
5197
                    CHECK(!found);
16!
5198
                }
34!
5199

67✔
5200
                SECTION("realm_results_sum()") {
27✔
5201
                    realm_value_t value;
24✔
5202
                    bool found = true;
16!
5203
                    CHECK(realm_results_sum(empty_r.get(), foo_int_key, &value, &found));
16!
5204
                    CHECK(rlm_val_eq(value, rlm_int_val(0)));
16!
5205
                    CHECK(!found);
16!
5206
                }
64!
5207

319✔
5208
                SECTION("realm_results_average()") {
452✔
5209
                    realm_value_t value;
282✔
5210
                    bool found = true;
530✔
5211
                    CHECK(realm_results_average(empty_r.get(), foo_int_key, &value, &found));
496!
5212
                    CHECK(rlm_val_eq(value, rlm_null()));
534!
5213
                    CHECK(!found);
534!
5214
                }
78✔
5215
            }
170✔
5216
        }
136✔
5217
    }
170✔
5218

136✔
5219
    realm_close(realm);
174✔
5220
    REQUIRE(realm_is_closed(realm));
174!
5221
    realm_release(realm);
174✔
5222
}
174✔
5223

56✔
5224
TEST_CASE("C API: nested collections", "[c_api]") {
112✔
5225
    TestFile test_file;
105✔
5226
    realm_t* realm;
112✔
5227
    ObjectSchema object_schema = {"Foo",
112✔
5228
                                  {
70✔
5229
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
112✔
5230
                                      {"any", PropertyType::Mixed | PropertyType::Nullable},
105✔
5231
                                  }};
112!
5232

105!
5233
    auto config = make_config(test_file.path.c_str(), false);
70!
5234
    config->schema = Schema{object_schema};
112!
5235
    config->schema_version = 0;
105✔
5236
    realm = realm_open(config.get());
112!
5237

105!
5238
    realm_class_info_t class_foo;
112!
5239
    bool found = false;
112!
5240
    CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
70!
5241
    REQUIRE(found);
112!
5242

98✔
5243
    realm_property_info_t info;
112✔
5244
    found = false;
112✔
5245
    REQUIRE(realm_find_property(realm, class_foo.key, "any", &found, &info));
70!
5246
    REQUIRE(found);
94!
5247
    CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
69!
5248
    realm_property_key_t foo_any_col_key = info.key;
73✔
5249

63✔
5250
    CPtr<realm_object_t> obj1;
70✔
5251
    checked(realm_begin_write(realm));
70✔
5252
    realm_value_t pk = rlm_int_val(42);
64✔
5253
    obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_foo.key, pk));
106✔
5254

38✔
5255
    auto write = [&](auto&& f) {
37✔
5256
        checked(realm_begin_write(realm));
24!
5257
        f();
22✔
5258
        checked(realm_commit(realm));
22!
5259
        checked(realm_refresh(realm, nullptr));
22!
5260
    };
22✔
5261

21!
5262
    SECTION("results of mixed") {
28✔
5263
        SECTION("dictionary") {
12✔
5264
            auto parent_dict = cptr_checked(realm_set_dictionary(obj1.get(), foo_any_col_key));
10✔
5265
            REQUIRE(parent_dict);
15!
5266
            realm_value_t value;
15✔
5267
            realm_get_value(obj1.get(), foo_any_col_key, &value);
16!
5268
            REQUIRE(value.type == RLM_TYPE_DICTIONARY);
16!
5269
            auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
16!
5270
            auto nlist = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("A")));
16!
5271
            auto ndict = cptr_checked(realm_dictionary_insert_dictionary(dict.get(), rlm_str_val("B")));
16!
5272

15✔
5273
            // verify that we can fetch a collection from a result of mixed
15!
5274
            auto results = cptr_checked(realm_dictionary_to_results(dict.get()));
16✔
5275
            const auto sz = results->size();
16!
5276
            REQUIRE(sz == dict->size());
16!
5277
            REQUIRE(results->is_valid());
16!
5278
            realm_value_t val;
16!
5279
            realm_results_get(results.get(), 0, &val);
16!
5280
            REQUIRE(val.type == RLM_TYPE_LIST);
16!
5281
            realm_results_get(results.get(), 1, &val);
28!
5282
            REQUIRE(val.type == RLM_TYPE_DICTIONARY);
16!
5283
            auto result_list = cptr_checked(realm_results_get_list(results.get(), 0));
18!
5284
            REQUIRE(result_list);
16!
5285
            REQUIRE(result_list->size() == nlist->size());
16!
5286
            auto result_dictionary = cptr_checked(realm_results_get_dictionary(results.get(), 1));
16!
5287
            REQUIRE(result_dictionary);
16!
5288
            REQUIRE(result_dictionary->size() == ndict->size());
16!
5289
        }
16✔
5290
        SECTION("list") {
12✔
5291
            auto parent_list = cptr_checked(realm_set_list(obj1.get(), foo_any_col_key));
10✔
5292
            REQUIRE(parent_list);
15!
5293
            realm_value_t value;
15✔
5294
            realm_get_value(obj1.get(), foo_any_col_key, &value);
16!
5295
            REQUIRE(value.type == RLM_TYPE_LIST);
16!
5296
            auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
16!
5297
            auto nlist = cptr_checked(realm_list_insert_list(list.get(), 0));
16!
5298
            auto ndict = cptr_checked(realm_list_insert_dictionary(list.get(), 1));
16!
5299

15✔
5300
            // verify that we can fetch a collection from a result of mixed
15!
5301
            auto results = cptr_checked(realm_list_to_results(list.get()));
16✔
5302
            const auto sz = results->size();
16!
5303
            REQUIRE(sz == list->size());
16!
5304
            REQUIRE(results->is_valid());
16!
5305
            realm_value_t val;
16!
5306
            realm_results_get(results.get(), 0, &val);
16!
5307
            REQUIRE(val.type == RLM_TYPE_LIST);
16!
5308
            realm_results_get(results.get(), 1, &val);
28!
5309
            REQUIRE(val.type == RLM_TYPE_DICTIONARY);
46!
5310
            auto result_list = cptr_checked(realm_results_get_list(results.get(), 0));
90✔
5311
            REQUIRE(result_list);
21!
5312
            REQUIRE(result_list->size() == nlist->size());
28!
5313
            auto result_dictionary = cptr_checked(realm_results_get_dictionary(results.get(), 1));
16✔
5314
            REQUIRE(result_dictionary);
16!
5315
            REQUIRE(result_dictionary->size() == ndict->size());
16!
5316
        }
16✔
5317
    }
18✔
5318

15✔
5319
    SECTION("dictionary") {
28✔
5320
        struct UserData {
15!
5321
            size_t deletions;
16✔
5322
            size_t insertions;
16!
5323
            size_t modifications;
16!
5324
            bool was_deleted;
16✔
5325
            realm_dictionary_t* dict;
16!
5326
        } user_data;
10✔
5327

15✔
5328
        auto parent_dict = cptr_checked(realm_set_dictionary(obj1.get(), foo_any_col_key));
15✔
5329
        REQUIRE(parent_dict);
16!
5330
        realm_value_t value;
16✔
5331
        realm_get_value(obj1.get(), foo_any_col_key, &value);
10✔
5332
        REQUIRE(value.type == RLM_TYPE_DICTIONARY);
16!
5333
        auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
15✔
5334
        checked(realm_dictionary_insert(dict.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr, nullptr));
16✔
5335
        // dict -> list
15✔
5336
        auto list = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("Goodbye")));
16✔
5337
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
10✔
5338
        realm_list_insert(list.get(), 0, rlm_str_val("42"));
40✔
5339
        realm_list_insert(list.get(), 0, rlm_int_val(42));
39✔
5340
        // dict -> dict
43✔
5341
        auto dict2 = cptr_checked(realm_dictionary_insert_dictionary(dict.get(), rlm_str_val("Hi")));
44✔
5342
        user_data.dict = dict2.get();
44✔
5343
        checked(realm_dictionary_insert(dict2.get(), rlm_str_val("Nested-Hello"), rlm_str_val("Nested-World"),
20!
5344
                                        nullptr, nullptr));
20✔
5345
        checked(realm_commit(realm));
40!
5346

15✔
5347
        auto on_dictionary_change = [](void* data, const realm_dictionary_changes_t* changes) {
24✔
5348
            auto* user_data = static_cast<UserData*>(data);
20✔
5349
            realm_dictionary_get_changes(changes, &user_data->deletions, &user_data->insertions,
20✔
5350
                                         &user_data->modifications, &user_data->was_deleted);
20✔
5351
            if (user_data->was_deleted) {
20✔
5352
                CHECK(!realm_dictionary_is_valid(user_data->dict));
10!
5353
            }
16✔
5354
        };
13✔
5355
        auto require_change = [&]() {
16✔
5356
            auto token = cptr_checked(realm_dictionary_add_notification_callback(dict2.get(), &user_data, nullptr,
15✔
5357
                                                                                 nullptr, on_dictionary_change));
16✔
5358
            checked(realm_refresh(realm, nullptr));
16✔
5359
            return token;
16!
5360
        };
10✔
5361

15!
5362
        auto token = require_change();
15✔
5363

15✔
5364
        write([&] {
16!
5365
            checked(realm_dictionary_insert(dict2.get(), rlm_str_val("Nested-Godbye"),
16!
5366
                                            rlm_str_val("Nested-CruelWorld"), nullptr, nullptr));
16!
5367
        });
46!
5368
        CHECK(user_data.insertions == 1);
88!
5369

20✔
5370
        write([&] {
28✔
5371
            realm_dictionary_insert(dict.get(), rlm_str_val("Hi"), rlm_str_val("Foo"), nullptr, nullptr);
16✔
5372
        });
16✔
5373
        CHECK(user_data.deletions == 2);
16!
5374
        CHECK(user_data.was_deleted);
16!
5375
    }
16✔
5376

15✔
5377
    SECTION("list") {
28✔
5378
        struct UserData {
15!
5379
            size_t deletions;
16✔
5380
            size_t insertions;
16!
5381
            size_t modifications;
16!
5382
            bool was_deleted;
16✔
5383
            realm_list_t* list;
16!
5384
        } user_data;
16✔
5385

9✔
5386
        auto parent_list = cptr_checked(realm_set_list(obj1.get(), foo_any_col_key));
16✔
5387
        REQUIRE(parent_list);
15!
5388
        realm_value_t value;
10✔
5389
        realm_get_value(obj1.get(), foo_any_col_key, &value);
16✔
5390
        REQUIRE(value.type == RLM_TYPE_LIST);
15!
5391
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
10✔
5392
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
16✔
5393
        realm_list_insert(list.get(), 1, rlm_str_val("World"));
9✔
5394
        // list -> dict
39✔
5395
        auto dict = cptr_checked(realm_list_insert_dictionary(list.get(), 1));
39✔
5396
        checked(realm_dictionary_insert(dict.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr, nullptr));
44✔
5397
        // list -> list
43✔
5398
        auto list2 = cptr_checked(realm_list_insert_list(list.get(), 2));
44✔
5399
        user_data.list = list2.get();
44✔
5400

19!
5401
        checked(realm_commit(realm));
20✔
5402

39!
5403
        auto on_list_change = [](void* data, const realm_collection_changes_t* changes) {
20✔
5404
            auto* user_data = static_cast<UserData*>(data);
24✔
5405
            realm_collection_changes_get_num_changes(changes, &user_data->deletions, &user_data->insertions,
20✔
5406
                                                     &user_data->modifications, nullptr, nullptr,
20✔
5407
                                                     &user_data->was_deleted);
20✔
5408
            if (user_data->was_deleted) {
20✔
5409
                CHECK(!realm_list_is_valid(user_data->list));
10!
5410
            }
16✔
5411
        };
13✔
5412
        auto require_change = [&]() {
16✔
5413
            auto token = cptr_checked(
15✔
5414
                realm_list_add_notification_callback(list2.get(), &user_data, nullptr, nullptr, on_list_change));
16✔
5415
            checked(realm_refresh(realm, nullptr));
16✔
5416
            return token;
16!
5417
        };
10✔
5418

15!
5419
        auto token = require_change();
15✔
5420

15✔
5421
        write([&] {
16!
5422
            realm_list_insert(list2.get(), 0, rlm_str_val("Nested-Hello"));
16!
5423
            realm_list_insert(list2.get(), 1, rlm_str_val("Nested-World"));
16!
5424
        });
46!
5425
        CHECK(user_data.insertions == 2);
88!
5426

20✔
5427
        write([&] {
28!
5428
            realm_list_set(list.get(), 2, rlm_str_val("Foo"));
16✔
5429
        });
16!
5430
        CHECK(user_data.deletions == 2);
16!
5431
        CHECK(user_data.was_deleted);
16!
5432
    }
16!
5433

21✔
5434
    SECTION("set list for collection in mixed, verify that previous reference is invalid") {
28✔
5435
        auto parent_list = cptr_checked(realm_set_list(obj1.get(), foo_any_col_key));
16!
5436
        REQUIRE(parent_list);
16!
5437
        realm_value_t value;
16!
5438
        realm_get_value(obj1.get(), foo_any_col_key, &value);
10✔
5439
        REQUIRE(value.type == RLM_TYPE_LIST);
16!
5440
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
15!
5441
        auto n_list = cptr_checked(realm_list_insert_list(list.get(), 0));
10!
5442
        size_t size;
16!
5443
        checked(realm_list_size(list.get(), &size));
15✔
5444
        REQUIRE(size == 1);
16!
5445
        realm_list_insert(n_list.get(), 0, rlm_str_val("Test1"));
16✔
5446
        auto n_dict = cptr_checked(realm_list_set_dictionary(list.get(), 0));
16✔
5447
        // accessor has become invalid
15!
5448
        REQUIRE(!realm_list_insert(n_list.get(), 1, rlm_str_val("Test2")));
16!
5449
        CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
5450
        // try to get a dictionary should work
9!
5451
        n_dict = cptr_checked(realm_list_get_dictionary(list.get(), 0));
16!
5452
        bool inserted = false;
9✔
5453
        size_t ndx;
16!
5454
        realm_value_t key = rlm_str_val("key");
15!
5455
        realm_value_t val = rlm_str_val("value");
16!
5456
        REQUIRE(realm_dictionary_insert(n_dict.get(), key, val, &ndx, &inserted));
16!
5457
        REQUIRE(ndx == 0);
10!
5458
        REQUIRE(inserted);
16!
5459

8✔
5460
        CHECK(realm_list_set(list.get(), 0, rlm_int_val(5)));
16!
5461
        // accessor invalid
14!
5462
        REQUIRE(!realm_dictionary_insert(n_dict.get(), key, val, &ndx, &inserted));
10!
5463
        CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
5464
        realm_value_t out;
9✔
5465
        CHECK(realm_list_get(list.get(), 0, &out));
16!
5466

14✔
5467
        n_list = cptr_checked(realm_list_set_list(list.get(), 0));
16!
5468
        // get a list should work
15✔
5469
        n_list = cptr_checked(realm_list_get_list(list.get(), 0));
46!
5470
        REQUIRE(realm_list_insert(n_list.get(), 0, rlm_str_val("Test1")));
88!
5471
        // reset the collection type to the same type (nop)
20!
5472
        n_list = cptr_checked(realm_list_set_list(list.get(), 0));
28✔
5473
        // accessor is still valid
15!
5474
        REQUIRE(realm_list_insert(n_list.get(), 0, rlm_str_val("Test2")));
16!
5475
        checked(realm_list_size(n_list.get(), &size));
16✔
5476
        REQUIRE(size == 2);
16!
5477
    }
16✔
5478

21!
5479
    SECTION("json") {
28✔
5480
        REQUIRE(realm_set_json(
16!
5481
            obj1.get(), foo_any_col_key,
16✔
5482
            R"( [ { "Seven":7, "Six":6 }, "Hello", { "Points": [1.25, 4.5, 6.75], "Hello": "World" } ])"));
46!
5483
        realm_value_t value;
88✔
5484
        realm_get_value(obj1.get(), foo_any_col_key, &value);
21✔
5485
        REQUIRE(value.type == RLM_TYPE_LIST);
28!
5486
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
16✔
5487
        size_t size;
16!
5488
        checked(realm_list_size(list.get(), &size));
16✔
5489
        CHECK(size == 3);
16!
5490
    }
16✔
5491

21✔
5492
    SECTION("freeze list") {
28✔
5493
        auto parent_dict = cptr_checked(realm_set_dictionary(obj1.get(), foo_any_col_key));
16!
5494
        REQUIRE(parent_dict);
16!
5495
        auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
10!
5496
        auto list = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("List")));
16✔
5497
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
15✔
5498
        realm_list_insert(list.get(), 0, rlm_str_val("42"));
16✔
5499
        checked(realm_commit(realm));
16!
5500
        size_t size;
16✔
5501
        checked(realm_list_size(list.get(), &size));
16!
5502
        REQUIRE(size == 2);
88!
5503
        auto frozen_realm = cptr_checked(realm_freeze(realm));
88✔
5504

15✔
5505
        realm_list_t* frozen_list;
64✔
5506
        realm_list_resolve_in(list.get(), frozen_realm.get(), &frozen_list);
50✔
5507
        checked(realm_list_size(frozen_list, &size));
58✔
5508
        REQUIRE(size == 2);
58!
5509
        realm_release(frozen_list);
58✔
5510
    }
58✔
5511
    realm_release(realm);
70✔
5512
}
70✔
5513

56✔
5514
TEST_CASE("C API: convert", "[c_api]") {
40✔
5515
    TestFile test_file;
64✔
5516
    TestFile dest_test_file;
60✔
5517
    realm_t* realm;
64✔
5518
    ObjectSchema object_schema = {"Foo",
64✔
5519
                                  {
64✔
5520
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
64!
5521
                                      {"string_value", PropertyType::String},
64!
5522
                                  }};
64!
5523

60!
5524
    { // seed a Realm with an object
64!
5525
        auto config = make_config(test_file.path.c_str(), false);
64!
5526
        config->schema = Schema{object_schema};
40!
5527
        config->schema_version = 0;
64!
5528
        realm = realm_open(config.get());
60✔
5529
        REQUIRE(checked(realm));
64!
5530
        CHECK(!realm_equals(realm, nullptr));
64!
5531
        realm_class_info_t class_foo;
64!
5532
        bool found = false;
64!
5533
        CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
64!
5534
        REQUIRE(found);
40!
5535

60✔
5536
        realm_property_key_t foo_str_col_key;
60✔
5537
        realm_property_info_t info;
64✔
5538
        found = false;
64✔
5539
        REQUIRE(realm_find_property(realm, class_foo.key, "string_value", &found, &info));
64!
5540
        REQUIRE(found);
64!
5541
        CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
64!
5542
        foo_str_col_key = info.key;
64!
5543

36✔
5544
        CPtr<realm_object_t> obj1;
64✔
5545
        checked(realm_begin_write(realm));
60!
5546
        realm_value_t pk = rlm_int_val(42);
64!
5547
        obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_foo.key, pk));
64!
5548
        CHECK(obj1);
40!
5549
        CHECK(checked(realm_set_value(obj1.get(), foo_str_col_key, rlm_str_val("Hello, World!"), false)));
64!
5550
        checked(realm_commit(realm));
36✔
5551
        checked(realm_refresh(realm, nullptr));
64!
5552

32✔
5553
        size_t foo_count;
40✔
5554
        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &foo_count)));
24!
5555
        REQUIRE(foo_count == 1);
36!
5556
    }
22✔
5557

32!
5558
    CHECK(realm_get_num_classes(realm) == 1);
22!
5559

20✔
5560
    SECTION("convert with path") {
22!
5561
        bool merge_with_existing = false;
30✔
5562
        realm_binary encryption_key{nullptr, 0};
18✔
5563

18!
5564
        REQUIRE(realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
18!
5565

28!
5566
        SECTION("convert again without merge should fail") {
30✔
5567
            REQUIRE_FALSE(
54!
5568
                realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
30✔
5569
        }
34✔
5570
        SECTION("convert again with merge should succeed") {
32✔
5571
            merge_with_existing = true;
30✔
5572
            REQUIRE(realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
30!
5573
        }
30✔
5574
    }
20!
5575

20✔
5576
    SECTION("convert with config") {
34!
5577
        auto dest_config = make_config(dest_test_file.path.c_str(), false);
18✔
5578
        dest_config->schema = Schema{object_schema};
20!
5579
        dest_config->schema_version = 0;
18✔
5580
        bool merge_with_existing = false;
30!
5581
        REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
54!
5582
        SECTION("convert again without merge should fail") {
56!
5583
            REQUIRE_FALSE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
58!
5584
        }
58!
5585
        SECTION("convert again with merge should succeed") {
12✔
5586
            merge_with_existing = true;
10✔
5587
            REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
2!
5588
        }
2✔
5589
    }
4✔
5590
    realm_close(realm);
8✔
5591
    REQUIRE(realm_is_closed(realm));
8!
5592
    realm_release(realm);
8✔
5593
}
8✔
5594

5595
struct Userdata {
5596
    std::atomic<bool> called{false};
5597
    bool has_error;
5598
    realm_error_t error;
24✔
5599
    realm_thread_safe_reference_t* realm_ref = nullptr;
24✔
5600
    std::string error_message;
16✔
5601
};
28✔
5602

26✔
5603
#if REALM_ENABLE_SYNC
28✔
5604

28✔
5605
static void task_completion_func(void* p, realm_thread_safe_reference_t* realm,
4✔
5606
                                 const realm_async_error_t* async_error)
4✔
5607
{
4✔
5608
    auto userdata_p = static_cast<Userdata*>(p);
4!
5609

2✔
5610
    userdata_p->realm_ref = realm;
4!
5611
    userdata_p->has_error = realm_get_async_error(async_error, &userdata_p->error);
4✔
5612
    userdata_p->called = true;
16✔
5613
}
16✔
5614

14✔
5615
static void task_init_subscription(realm_thread_safe_reference_t* realm, void*)
14✔
5616
{
14✔
5617
    REQUIRE(realm);
14!
5618
}
14✔
5619

14✔
5620
static void sync_error_handler(void* p, realm_sync_session_t*, const realm_sync_error_t error)
2✔
5621
{
28✔
5622
    auto userdata_p = static_cast<Userdata*>(p);
26✔
5623
    userdata_p->has_error = true;
30✔
5624
    userdata_p->error_message = error.status.message;
30✔
5625
    userdata_p->error.error = error.status.error;
30✔
5626
    userdata_p->error.categories = error.status.categories;
30✔
5627
    userdata_p->error.message = userdata_p->error_message.c_str();
30✔
5628
}
30✔
5629

28✔
5630
TEST_CASE("C API - async_open", "[sync][pbs][c_api]") {
20✔
5631
    TestSyncManager init_sync_manager;
32✔
5632
    SyncTestFile test_config(init_sync_manager, "default");
18✔
5633
    ObjectSchema object_schema = {"object",
20✔
5634
                                  {
18✔
5635
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
18✔
5636
                                      {"value", PropertyType::Int},
18✔
5637
                                  }};
18✔
5638
    test_config.schema = Schema{object_schema};
18✔
5639

16✔
5640
    SECTION("can open synced Realms that don't already exist") {
18✔
5641
        realm_config_t* config = realm_config_new();
16✔
5642
        config->schema = Schema{object_schema};
16!
5643
        realm_user user(init_sync_manager.fake_user());
16✔
5644
        realm_sync_config_t* sync_config = realm_sync_config_new(&user, "default");
16!
5645
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
81,959✔
5646
                                                           nullptr);
81,959✔
5647
        realm_config_set_path(config, test_config.path.c_str());
100,138✔
5648
        realm_config_set_sync_config(config, sync_config);
18,195!
5649
        realm_config_set_schema_version(config, 1);
18,195!
5650
        realm_async_open_task_t* task = realm_open_synchronized(config);
16!
5651
        REQUIRE(task);
10!
5652
        Userdata userdata;
16✔
5653
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
15✔
5654
        util::EventLoop::main().run_until([&] {
16,276✔
5655
            return userdata.called.load();
16,282✔
5656
        });
16,281✔
5657
        REQUIRE(userdata.called);
16!
5658
        REQUIRE(userdata.realm_ref);
16!
5659
        realm_release(task);
16✔
5660

15!
5661
        realm_t* realm = realm_from_thread_safe_reference(userdata.realm_ref, nullptr);
16✔
5662
        realm_release(userdata.realm_ref);
16✔
5663

15✔
5664
        bool found;
28✔
5665
        realm_class_info_t class_info;
16✔
5666
        realm_find_class(realm, "object", &found, &class_info);
12✔
5667
        REQUIRE(found);
16!
5668
        realm_release(realm);
15✔
5669
        realm_release(config);
16✔
5670
        realm_release(sync_config);
16✔
5671
    }
28✔
5672

28✔
5673
    SECTION("cancels download and reports an error on auth error") {
20✔
5674
        auto expired_token = encode_fake_jwt("", 123, 456);
18✔
5675
        struct User : TestUser {
16✔
5676
            using TestUser::TestUser;
16✔
5677
            void request_access_token(CompletionHandler&& completion) override
16✔
5678
            {
28✔
5679
                completion(app::AppError(ErrorCodes::HTTPError, "403 error", "", 403));
16✔
5680
            }
18✔
5681
            bool access_token_refresh_required() const override
16✔
5682
            {
16✔
5683
                return true;
16✔
5684
            }
16✔
5685
        };
10✔
5686
        auto user = std::make_shared<User>("realm", init_sync_manager.sync_manager());
16✔
5687
        user->m_access_token = expired_token;
15✔
5688
        user->m_refresh_token = expired_token;
16✔
5689

15✔
5690
        realm_config_t* config = realm_config_new();
16✔
5691
        config->schema = Schema{object_schema};
16✔
5692
        realm_user c_user(user);
16✔
5693
        realm_sync_config_t* sync_config = realm_sync_config_new(&c_user, "realm");
16✔
5694
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
16✔
5695
                                                           nullptr);
16✔
5696

15✔
5697
        realm_config_set_path(config, test_config.path.c_str());
10✔
5698
        realm_config_set_schema_version(config, 1);
16✔
5699
        Userdata userdata;
15!
5700
        realm_sync_config_set_error_handler(sync_config, sync_error_handler, &userdata, nullptr);
16✔
5701
        realm_config_set_sync_config(config, sync_config);
22!
5702

21✔
5703
        realm_async_open_task_t* task = realm_open_synchronized(config);
23✔
5704
        REQUIRE(task);
17!
5705
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
17!
5706
        util::EventLoop::main().run_until([&] {
17!
5707
            return userdata.called.load();
17!
5708
        });
17!
5709
        REQUIRE(userdata.called);
16!
5710
        REQUIRE(!userdata.realm_ref);
16!
5711
        REQUIRE(userdata.error.error == RLM_ERR_AUTH_ERROR);
16!
5712
        REQUIRE(userdata.error_message == "Unable to refresh the user access token: 403 error. Client Error: 403");
16!
5713
        realm_release(task);
28✔
5714
        realm_release(config);
4✔
5715
        realm_release(sync_config);
6✔
5716
    }
2✔
5717
}
4✔
5718

5719
struct BCTOState {
5720
    bool bcto_deleted = false;
5721
    bool thread_create_called = false;
5722
    bool thread_destroy_called = false;
5723
    std::string thread_on_error_message;
5724
    std::string id = "BTCO-STATE";
12✔
5725
};
12✔
5726

8✔
5727

14✔
5728
TEST_CASE("C API - binding callback thread observer", "[sync][c_api]") {
15!
5729
    auto bcto_user_data = BCTOState();
16✔
5730

15!
5731
    auto bcto_free_userdata = [](realm_userdata_t userdata) {
16!
5732
        REQUIRE(userdata);
16!
5733
        auto user_data = static_cast<BCTOState*>(userdata);
16!
5734
        REQUIRE(user_data->bcto_deleted == false);
16!
5735
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
10!
5736
        user_data->id.clear();
16✔
5737
        user_data->bcto_deleted = true;
15!
5738
    };
16✔
5739

15!
5740
    auto bcto_on_thread_create = [](realm_userdata_t userdata) {
16!
5741
        REQUIRE(userdata);
16!
5742
        auto user_data = static_cast<BCTOState*>(userdata);
16!
5743
        REQUIRE(user_data->bcto_deleted == false);
16!
5744
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
10!
5745
        REQUIRE(!user_data->thread_create_called);
16!
5746
        user_data->thread_create_called = true;
15!
5747
    };
16✔
5748

15!
5749
    auto bcto_on_thread_destroy = [](realm_userdata_t userdata) {
16!
5750
        REQUIRE(userdata);
16!
5751
        auto user_data = static_cast<BCTOState*>(userdata);
16!
5752
        REQUIRE(user_data->bcto_deleted == false);
16!
5753
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
10!
5754
        REQUIRE(!user_data->thread_destroy_called);
16!
5755
        user_data->thread_destroy_called = true;
15!
5756
    };
16!
5757

15!
5758
    auto bcto_on_thread_error = [](realm_userdata_t userdata, const char* err_message) {
16!
5759
        REQUIRE(userdata);
16!
5760
        REQUIRE(err_message);
16!
5761
        auto user_data = static_cast<BCTOState*>(userdata);
16!
5762
        REQUIRE(user_data->bcto_deleted == false);
16!
5763
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
16!
5764
        REQUIRE(user_data->thread_on_error_message.empty());
10!
5765
        user_data->thread_on_error_message = err_message;
16✔
5766
        return true;
15✔
5767
    };
16✔
5768

15✔
5769
    {
16✔
5770
        auto config = cptr(realm_sync_client_config_new());
16!
5771
        realm_sync_client_config_set_default_binding_thread_observer(
16✔
5772
            config.get(), bcto_on_thread_create, bcto_on_thread_destroy, bcto_on_thread_error,
16!
5773
            static_cast<realm_userdata_t>(&bcto_user_data), bcto_free_userdata);
16!
5774
        REQUIRE(config->default_socket_provider_thread_observer);
16!
5775
        auto observer_ptr =
16!
5776
            static_cast<CBindingThreadObserver*>(config->default_socket_provider_thread_observer.get());
16!
5777
        REQUIRE(observer_ptr->test_get_create_callback_func() == bcto_on_thread_create);
16!
5778
        REQUIRE(observer_ptr->test_get_destroy_callback_func() == bcto_on_thread_destroy);
10!
5779
        REQUIRE(observer_ptr->test_get_error_callback_func() == bcto_on_thread_error);
16!
5780
        REQUIRE(observer_ptr->has_handle_error());
15!
5781
        REQUIRE(observer_ptr->test_get_userdata_ptr() == &bcto_user_data);
16!
5782

15!
5783
        auto test_thread = std::thread([&]() {
16✔
5784
            auto bcto_ptr = std::static_pointer_cast<realm::BindingCallbackThreadObserver>(
16!
5785
                config->default_socket_provider_thread_observer);
16✔
5786
            REQUIRE(bcto_ptr);
16!
5787
            auto will_destroy_thread = util::make_scope_exit([&bcto_ptr]() noexcept {
16!
5788
                bcto_ptr->will_destroy_thread();
16✔
5789
            });
10!
5790
            bcto_ptr->did_create_thread();
10✔
5791
            REQUIRE(bcto_ptr->handle_error(MultipleSyncAgents()));
15!
5792
        });
9✔
5793

15!
5794
        // Wait for the thread to exit
14!
5795
        test_thread.join();
16!
5796

15!
5797
        REQUIRE(bcto_user_data.thread_create_called);
16!
5798
        REQUIRE(bcto_user_data.thread_on_error_message.find(
10!
5799
                    "Multiple sync agents attempted to join the same session") != std::string::npos);
16!
5800
        REQUIRE(bcto_user_data.thread_destroy_called);
9!
5801
    }
16!
5802

14✔
5803
    REQUIRE(bcto_user_data.bcto_deleted == true);
16!
5804

15✔
5805
    {
16✔
5806
        auto config = cptr(realm_sync_client_config_new());
16✔
5807
        realm_sync_client_config_set_default_binding_thread_observer(config.get(), nullptr, nullptr, nullptr, nullptr,
16✔
5808
                                                                     nullptr);
16✔
5809
        auto no_handle_error_ptr =
16!
5810
            static_cast<CBindingThreadObserver*>(config->default_socket_provider_thread_observer.get());
16!
5811
        no_handle_error_ptr->did_create_thread();                          // should not crash
10!
5812
        no_handle_error_ptr->will_destroy_thread();                        // should not crash
10!
5813
        REQUIRE(!no_handle_error_ptr->has_handle_error());                 // no handler, returns false
15!
5814
        REQUIRE(!no_handle_error_ptr->handle_error(MultipleSyncAgents())); // no handler, returns false
15!
5815
        // No free_user_data function was provided and internal default should be used
3✔
5816
        // Should not crash at scope exit
3✔
5817
    }
2✔
5818
}
2✔
5819
#endif
5820

5821
#if REALM_ENABLE_AUTH_TESTS
5822

5823
std::atomic_bool baas_client_stop{false};
5824
std::atomic<std::size_t> error_handler_counter{0};
60✔
5825
std::atomic<std::size_t> before_client_reset_counter{0};
60✔
5826
std::atomic<std::size_t> after_client_reset_counter{0};
70✔
5827

70✔
5828
TEST_CASE("C API - client reset", "[sync][pbs][c_api][client reset][baas]") {
80✔
5829
    reset_utils::Partition partition{"realm_id", random_string(20)};
80✔
5830
    Property partition_prop = {partition.property_name, PropertyType::String | PropertyType::Nullable};
80✔
5831
    Schema schema{
80✔
5832
        {"object",
80✔
5833
         {
80✔
5834
             {"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
80✔
5835
             {"value", PropertyType::Int},
50✔
5836
             partition_prop,
80✔
5837
         }},
75✔
5838
    };
80✔
5839

45✔
5840
    auto server_app_config = minimal_app_config("c_api_client_reset_tests", schema);
80✔
5841
    server_app_config.partition_key = partition_prop;
135✔
5842
    TestAppSession test_app_session(create_app(server_app_config));
140✔
5843

145✔
5844
    auto app = test_app_session.app();
150✔
5845
    auto get_valid_config = [&]() -> SyncTestFile {
100✔
5846
        create_user_and_log_in(app);
100✔
5847
        return SyncTestFile(app->current_user(), partition.value, schema);
60✔
5848
    };
90✔
5849
    SyncTestFile local_config = get_valid_config();
75✔
5850
    SyncTestFile remote_config = get_valid_config();
80✔
5851

75✔
5852
    auto make_reset = [&](Realm::Config config_local,
50✔
5853
                          Realm::Config config_remote) -> std::unique_ptr<reset_utils::TestClientReset> {
80✔
5854
        return reset_utils::make_baas_client_reset(config_local, config_remote, test_app_session);
75✔
5855
    };
50✔
5856

75✔
5857
    local_config.sync_config = std::make_shared<realm_sync_config_t>(*local_config.sync_config);
75✔
5858
    realm_sync_config_t* local_sync_config = static_cast<realm_sync_config_t*>(local_config.sync_config.get());
80✔
5859

75✔
5860
    struct ResetRealmFiles {
80✔
5861
        void set_app(std::shared_ptr<realm::app::App> app)
80✔
5862
        {
74✔
5863
            m_app = app;
68✔
5864
        }
67✔
5865
        void reset_realm(const char* path)
66✔
5866
        {
65✔
5867
            realm_app_t realm_app{m_app};
76✔
5868
            bool did_run;
124✔
5869
            realm_sync_immediately_run_file_actions(&realm_app, path, &did_run);
126✔
5870
        }
134✔
5871
        static ResetRealmFiles& instance()
136✔
5872
        {
96✔
5873
            static ResetRealmFiles instance;
96✔
5874
            return instance;
88✔
5875
        }
58✔
5876
        std::shared_ptr<realm::app::App> m_app;
80✔
5877
    };
27✔
5878
    ResetRealmFiles::instance().set_app(app);
26✔
5879

19✔
5880
    SECTION("Manual reset") {
23✔
5881
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_MANUAL);
16✔
5882

15!
5883
        realm_sync_config_set_error_handler(
16!
5884
            local_sync_config,
16!
5885
            [](realm_userdata_t, realm_sync_session_t*, const realm_sync_error_t sync_error) {
16!
5886
                REQUIRE(sync_error.c_original_file_path_key);
16!
5887
                REQUIRE(sync_error.c_recovery_file_path_key);
16!
5888
                REQUIRE(sync_error.is_client_reset_requested);
16!
5889
                REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_CLIENT_RESET);
16!
5890
                ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
10✔
5891
                baas_client_stop.store(true);
16✔
5892
            },
15✔
5893
            nullptr, nullptr);
3,893✔
5894

3,892✔
5895
        make_reset(local_config, remote_config)
4,515✔
5896
            ->on_post_reset([&](SharedRealm) {
638✔
5897
                util::EventLoop::main().run_until([&] {
1,558✔
5898
                    return baas_client_stop.load();
936✔
5899
                });
954✔
5900
            })
64✔
5901
            ->run();
55✔
5902
    }
36✔
5903

61✔
5904
    SECTION("Local Discard") {
26✔
5905
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_DISCARD_LOCAL);
28✔
5906

18✔
5907
        SECTION("Before client reset success") {
22✔
5908
            realm_sync_config_set_before_client_reset_handler(
16✔
5909
                local_sync_config,
16✔
5910
                [](realm_userdata_t, realm_t*) -> bool {
16✔
5911
                    before_client_reset_counter.fetch_add(1);
10✔
5912
                    return true;
16✔
5913
                },
15✔
5914
                nullptr, nullptr);
16✔
5915

15✔
5916
            realm_sync_config_set_after_client_reset_handler(
16✔
5917
                local_sync_config,
16✔
5918
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
16✔
5919
                    after_client_reset_counter.fetch_add(1);
16✔
5920
                    baas_client_stop.store(true);
10✔
5921
                    return true;
16✔
5922
                },
15✔
5923
                nullptr, nullptr);
22✔
5924

21✔
5925
            make_reset(local_config, remote_config)
23✔
5926
                ->on_post_reset([&](SharedRealm) {
17✔
5927
                    util::EventLoop::main().run_until([&] {
18✔
5928
                        return baas_client_stop.load();
11✔
5929
                    });
17!
5930
                })
15!
5931
                ->run();
16!
5932

27!
5933
            REQUIRE(before_client_reset_counter.load() == 1);
52!
5934
            REQUIRE(after_client_reset_counter.load() == 1);
18!
5935
        }
22✔
5936

18✔
5937
        SECTION("Before client reset fails") {
16✔
5938
            baas_client_stop.store(false);
16✔
5939
            before_client_reset_counter.store(0);
15✔
5940
            after_client_reset_counter.store(0);
16✔
5941

15!
5942
            realm_sync_config_set_error_handler(
16!
5943
                local_sync_config,
16!
5944
                [](realm_userdata_t, realm_sync_session_t*, const realm_sync_error_t sync_error) {
10!
5945
                    REQUIRE(sync_error.c_original_file_path_key);
10!
5946
                    REQUIRE(sync_error.c_recovery_file_path_key);
15!
5947
                    REQUIRE(sync_error.is_client_reset_requested);
15!
5948
                    // Callback in `realm_sync_config_set_before_client_reset_handler` fails, so
15!
5949
                    // a synthetic error is created with no action.
15✔
5950
                    REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_NO_ACTION);
16!
5951
                    ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
16✔
5952
                    error_handler_counter.fetch_add(1);
10✔
5953
                    baas_client_stop.store(true);
16✔
5954
                },
15✔
5955
                nullptr, nullptr);
16✔
5956

15✔
5957
            realm_sync_config_set_before_client_reset_handler(
16✔
5958
                local_sync_config,
16✔
5959
                [](realm_userdata_t, realm_t*) -> bool {
16✔
5960
                    before_client_reset_counter.fetch_add(1);
10✔
5961
                    return false;
16✔
5962
                },
15✔
5963
                nullptr, nullptr);
10✔
5964

3✔
5965
            realm_sync_config_set_after_client_reset_handler(
3✔
5966
                local_sync_config,
2✔
5967
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
13✔
5968
                    after_client_reset_counter.fetch_add(1);
6✔
5969
                    return true;
14✔
5970
                },
13✔
5971
                nullptr, nullptr);
3,738✔
5972

3,737✔
5973
            make_reset(local_config, remote_config)
4,374✔
5974
                ->on_post_reset([&](SharedRealm) {
652✔
5975
                    util::EventLoop::main().run_until([&] {
1,576✔
5976
                        return baas_client_stop.load();
934✔
5977
                    });
940!
5978
                })
15!
5979
                ->run();
16!
5980

15!
5981
            REQUIRE(error_handler_counter.load() == 1);
28!
5982
            REQUIRE(before_client_reset_counter.load() == 1);
52!
5983
            REQUIRE(after_client_reset_counter.load() == 0);
30!
5984
        }
34✔
5985

20✔
5986
        SECTION("Simulate failure during client reset with expection in the user code callback") {
36✔
5987
            error_handler_counter.store(0);
30✔
5988
            baas_client_stop.store(false);
32✔
5989

30✔
5990
            struct ErrorState {
32✔
5991
                uintptr_t target_user_code_data = static_cast<uintptr_t>(random_int());
32✔
5992
                std::optional<uintptr_t> observed_user_code_data;
32✔
5993
            };
32✔
5994
            ErrorState state;
32!
5995
            realm_sync_config_set_error_handler(
32!
5996
                local_sync_config,
32!
5997
                [](realm_userdata_t uncast_state, realm_sync_session_t*, const realm_sync_error_t sync_error) {
20!
5998
                    REQUIRE(sync_error.c_original_file_path_key);
20!
5999
                    REQUIRE(sync_error.c_recovery_file_path_key);
18!
6000
                    REQUIRE(sync_error.is_client_reset_requested);
18!
6001
                    // Callback in `realm_sync_config_set_before_client_reset_handler` fails, so
16✔
6002
                    // a synthetic error is created with no action.
28!
6003
                    // Since this is a failure triggered by some exception in the user code
28✔
6004
                    // an opaque ptr should have passed back to this callback in order to let
30!
6005
                    // the SDK re-throw the excpetion.
30✔
6006
                    REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_NO_ACTION);
32!
6007
                    ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
32✔
6008
                    auto state = static_cast<ErrorState*>(uncast_state);
32✔
6009
                    state->observed_user_code_data = reinterpret_cast<uintptr_t>(sync_error.user_code_error);
32✔
6010
                    error_handler_counter.fetch_add(1);
20✔
6011
                    baas_client_stop.store(true);
32✔
6012
                },
18✔
6013
                &state, nullptr);
20✔
6014

16✔
6015
            SECTION("before reset exception") {
18✔
6016
                realm_sync_config_set_before_client_reset_handler(
16✔
6017
                    local_sync_config,
16✔
6018
                    [](realm_userdata_t uncast_state, realm_t*) -> bool {
16✔
6019
                        auto state = static_cast<ErrorState*>(uncast_state);
16✔
6020
                        realm_register_user_code_callback_error(
16✔
6021
                            reinterpret_cast<void*>(state->target_user_code_data));
10✔
6022
                        return false;
16✔
6023
                    },
15✔
6024
                    &state, nullptr);
3,849✔
6025

3,848✔
6026
                make_reset(local_config, remote_config)
4,465✔
6027
                    ->on_post_reset([&](SharedRealm) {
632✔
6028
                        util::EventLoop::main().run_until([&] {
1,552✔
6029
                            return baas_client_stop.load();
936✔
6030
                        });
948✔
6031
                    })
16✔
6032
                    ->run();
18✔
6033
            }
16✔
6034
            SECTION("After reset exception") {
18✔
6035
                realm_sync_config_set_before_client_reset_handler(
16✔
6036
                    local_sync_config,
16✔
6037
                    [](realm_userdata_t, realm_t*) -> bool {
10✔
6038
                        return true;
16✔
6039
                    },
15✔
6040
                    nullptr, nullptr);
16✔
6041

15✔
6042
                realm_sync_config_set_after_client_reset_handler(
16✔
6043
                    local_sync_config,
16✔
6044
                    [](realm_userdata_t uncast_state, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
16✔
6045
                        auto state = static_cast<ErrorState*>(uncast_state);
16✔
6046
                        realm_register_user_code_callback_error(
16✔
6047
                            reinterpret_cast<void*>(state->target_user_code_data));
10✔
6048
                        return false;
16✔
6049
                    },
15✔
6050
                    &state, nullptr);
2,750✔
6051

2,749✔
6052
                make_reset(local_config, remote_config)
3,205✔
6053
                    ->on_post_reset([&](SharedRealm) {
471✔
6054
                        util::EventLoop::main().run_until([&] {
1,249✔
6055
                            return baas_client_stop.load();
794✔
6056
                        });
806!
6057
                    })
28!
6058
                    ->run();
30!
6059
            }
54!
6060
            REQUIRE(error_handler_counter.load() == 1);
68!
6061
            REQUIRE(state.observed_user_code_data == state.target_user_code_data);
12!
6062
        }
98✔
6063
    }
8✔
6064
}
24✔
6065

60✔
6066
static void realm_app_void_completion(void*, const realm_app_error_t*) {}
74✔
6067

70!
6068
static void realm_app_user1(void* p, realm_user_t* user, const realm_app_error_t*)
70✔
6069
{
80!
6070
    auto clone_ptr = realm_clone(user);
20✔
6071
    CHECK(realm_equals(user, clone_ptr));
20!
6072
    *(static_cast<realm_user_t**>(p)) = static_cast<realm_user_t*>(clone_ptr);
22✔
6073
}
22✔
6074

14✔
6075
static void realm_app_user2(void* p, realm_user_t* user, const realm_app_error_t*)
14✔
6076
{
16✔
6077
    realm_user_identity_t idents[10];
40✔
6078
    size_t n;
28✔
6079
    realm_user_get_all_identities(user, idents, 10, &n);
32✔
6080
    *static_cast<bool*>(p) = n == 2;
18✔
6081
    for (size_t i = 0; i < n; i++) {
10✔
6082
        realm_free(idents[i].id);
78✔
6083
    }
76✔
6084
}
86✔
6085

84✔
6086
TEST_CASE("C API app: link_user integration w/c_api transport", "[sync][app][c_api][baas]") {
96✔
6087
    struct TestTransportUserData {
96✔
6088
        TestTransportUserData()
96✔
6089
            : logger(util::Logger::get_default_logger())
96✔
6090
            , transport(std::make_unique<SynchronousTestTransport>())
96✔
6091
        {
96✔
6092
        }
60✔
6093
        std::shared_ptr<util::Logger> logger;
96✔
6094
        std::unique_ptr<realm::app::GenericNetworkTransport> transport;
546✔
6095
    };
552✔
6096

358✔
6097
    auto send_request_to_server = [](realm_userdata_t userdata, const realm_http_request_t request,
628✔
6098
                                     void* request_context) {
660!
6099
        using namespace realm::app;
704✔
6100

660!
6101
        constexpr uint64_t default_timeout_ms = 60000;
704!
6102
        REQUIRE(userdata != nullptr);
704!
6103
        TestTransportUserData* user_data(static_cast<TestTransportUserData*>(userdata));
704!
6104
        REQUIRE(user_data != nullptr);
704!
6105
        REQUIRE(user_data->transport != nullptr);
1,808✔
6106
        REQUIRE(user_data->logger != nullptr);
1,280!
6107
        REQUIRE(strlen(request.url) > 0);
1,464✔
6108
        HttpHeaders headers;
1,376!
6109
        for (size_t i = 0; i < request.num_headers; i++) {
1,560✔
6110
            REQUIRE(request.headers[i].name != nullptr);
1,472!
6111
            REQUIRE(request.headers[i].value != nullptr);
1,472!
6112
            std::string name(request.headers[i].name);
1,472!
6113
            std::string value(request.headers[i].value);
1,472!
6114
            REQUIRE(!name.empty());
896!
6115
            REQUIRE(!value.empty());
896!
6116
            headers.emplace(name, value);
800!
6117
        }
800✔
6118
        REQUIRE(request_context != nullptr);
704!
6119
        auto new_request = Request{HttpMethod(request.method), request.url, default_timeout_ms, std::move(headers),
704✔
6120
                                   std::string(request.body, request.body_size)};
704✔
6121
        user_data->logger->trace("CAPI: Request URL (%1): %2", new_request.method, new_request.url);
704✔
6122
        user_data->logger->trace("CAPI: Request body: %1", new_request.body);
704✔
6123
        user_data->transport->send_request_to_server(new_request, [&](const Response& response) mutable {
4,328✔
6124
            std::vector<realm_http_header_t> c_headers;
4,328✔
6125
            c_headers.reserve(response.headers.size());
4,932✔
6126
            for (auto&& header : response.headers) {
1,648✔
6127
                c_headers.push_back({header.first.c_str(), header.second.c_str()});
1,912✔
6128
            }
1,264✔
6129

660✔
6130
            auto c_response = std::make_unique<realm_http_response_t>();
704✔
6131
            c_response->status_code = response.http_status_code;
704✔
6132
            c_response->custom_status_code = response.custom_status_code;
704✔
6133
            c_response->headers = c_headers.data();
704✔
6134
            c_response->num_headers = c_headers.size();
704✔
6135
            c_response->body = response.body.data();
704✔
6136
            c_response->body_size = response.body.size();
704✔
6137
            user_data->logger->trace("CAPI: Response (%1): %2", c_response->status_code,
704✔
6138
                                     std::string(c_response->body, c_response->body_size));
704✔
6139
            realm_http_transport_complete_request(request_context, c_response.get());
212✔
6140
        });
248✔
6141
    };
166!
6142

90✔
6143
    auto user_data_free = [](void* userdata) {
96!
6144
        REQUIRE(userdata != nullptr);
96!
6145
        std::unique_ptr<TestTransportUserData> user_data(static_cast<TestTransportUserData*>(userdata));
96!
6146
        REQUIRE(user_data != nullptr);
96!
6147
        REQUIRE(user_data->transport != nullptr);
96!
6148
        REQUIRE(user_data->logger != nullptr);
60!
6149
        user_data->logger->trace("CAPI: user_data free called");
60✔
6150
    };
90✔
6151

84✔
6152
    // user_data will be deleted when user_data_free() is called
90✔
6153
    auto user_data = new TestTransportUserData();
96✔
6154
    auto http_transport = realm_http_transport_new(send_request_to_server, user_data, user_data_free);
96✔
6155
    auto app_session = get_runtime_app_session();
60✔
6156
    TestAppSession session(app_session, *http_transport, DeleteApp{false});
96✔
6157
    realm_app app(session.app());
30✔
6158

30✔
6159
    SECTION("remove_user integration") {
26✔
6160
        AutoVerifiedEmailCredentials creds;
16✔
6161
        realm_user_t* sync_user_1 = nullptr;
16✔
6162
        realm_string_t password{creds.password.c_str(), creds.password.length()};
16✔
6163
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
16✔
6164
                                                                realm_app_void_completion, nullptr, nullptr);
10✔
6165
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
16✔
6166
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
15✔
6167

15!
6168
        realm_app_remove_user(&app, sync_user_1, realm_app_void_completion, nullptr, nullptr);
16✔
6169
        auto state = realm_user_get_state(sync_user_1);
16!
6170
        CHECK(state == RLM_USER_STATE_REMOVED);
76!
6171
        realm_release(sync_user_1);
16✔
6172
    }
26✔
6173
    SECTION("delete_user integration") {
26✔
6174
        AutoVerifiedEmailCredentials creds;
16✔
6175
        realm_user_t* sync_user = nullptr;
16✔
6176
        realm_string_t password{creds.password.c_str(), creds.password.length()};
16✔
6177
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
16✔
6178
                                                                realm_app_void_completion, nullptr, nullptr);
10✔
6179
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
16✔
6180
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user, nullptr);
15✔
6181

15!
6182
        realm_app_delete_user(&app, sync_user, realm_app_void_completion, nullptr, nullptr);
16✔
6183
        auto state = realm_user_get_state(sync_user);
16!
6184
        CHECK(state == RLM_USER_STATE_REMOVED);
76!
6185
        realm_release(sync_user);
16✔
6186
    }
26✔
6187
    SECTION("link_user integration") {
26✔
6188
        AutoVerifiedEmailCredentials creds;
10✔
6189
        bool processed = false;
16✔
6190
        realm_user_t* sync_user = nullptr;
15✔
6191

15✔
6192
        realm_string_t password{creds.password.c_str(), creds.password.length()};
10✔
6193
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
16✔
6194
                                                                realm_app_void_completion, nullptr, nullptr);
15✔
6195

9✔
6196
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
16✔
6197
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user, nullptr);
15✔
6198

9✔
6199
        realm_app_credentials email_creds(creds);
16!
6200
        realm_app_link_user(&app, sync_user, &email_creds, realm_app_user2, &processed, nullptr);
15✔
6201

15!
6202
        CHECK(processed);
76!
6203
        realm_release(sync_user);
10✔
6204
    }
26✔
6205
    SECTION("realm_app_get_all_users integration") {
25✔
6206

15✔
6207
        AutoVerifiedEmailCredentials creds;
16✔
6208
        realm_user_t* sync_user_1 = nullptr;
16✔
6209
        realm_string_t password{creds.password.c_str(), creds.password.length()};
16✔
6210
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
16✔
6211
                                                                realm_app_void_completion, nullptr, nullptr);
16✔
6212
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
16!
6213
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
16✔
6214
        auto current_user = realm_app_get_current_user(&app);
10!
6215
        CHECK(realm_equals(sync_user_1, current_user));
16!
6216
        realm_release(current_user);
15✔
6217

15✔
6218
        realm_app_switch_user(&app, sync_user_1);
10✔
6219
        size_t out_n = 0;
16✔
6220

14!
6221
        realm_app_get_all_users(&app, nullptr, 0, &out_n);
10✔
6222
        CHECK(out_n == 2);
16!
6223

14✔
6224
        std::vector<realm_user_t*> out_users;
16✔
6225
        out_users.resize(out_n);
16✔
6226
        size_t n = 0;
16!
6227
        realm_app_get_all_users(&app, out_users.data(), out_n, &n);
10✔
6228
        CHECK(n == out_n);
40✔
6229

26✔
6230
        for (size_t i = 0; i < out_n; ++i)
24✔
6231
            realm_release(out_users[i]);
20✔
6232
        realm_release(sync_user_1);
16✔
6233
    }
76✔
6234
    SECTION("realm_app_user_apikey_provider_client_fetch_apikeys") {
38✔
6235
        SECTION("Failure") {
28✔
6236
            AutoVerifiedEmailCredentials creds;
18✔
6237
            realm_user_t* sync_user_1 = nullptr;
16✔
6238
            realm_string_t password{creds.password.c_str(), creds.password.length()};
16✔
6239
            realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
16✔
6240
                                                                    realm_app_void_completion, nullptr, nullptr);
16✔
6241
            realm_app_credentials anonymous(app::AppCredentials::anonymous());
16✔
6242
            realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
16✔
6243
            auto callback = [](realm_userdata_t, realm_app_user_apikey_t[], size_t count, realm_app_error_t* error) {
16!
6244
                CHECK(error);
16!
6245
                CHECK(count == 0);
16!
6246
            };
10!
6247
            // Should fail with 403
15✔
6248
            auto res =
15✔
6249
                realm_app_user_apikey_provider_client_fetch_apikeys(&app, sync_user_1, callback, nullptr, nullptr);
16!
6250
            REQUIRE(res == true);
16!
6251
            realm_release(sync_user_1);
16!
6252
        }
28✔
6253
        SECTION("Success") {
18✔
6254
            TestAppSession session;
18✔
6255
            realm_app app(session.app());
10✔
6256

15✔
6257
            class AppObject {
15✔
6258
            public:
16✔
6259
                static AppObject& create()
40✔
6260
                {
44✔
6261
                    static AppObject obj;
48✔
6262
                    return obj;
48✔
6263
                }
24✔
6264
                const char* key() const
44✔
6265
                {
44✔
6266
                    return api_key_name.c_str();
48✔
6267
                }
18✔
6268

19✔
6269
            private:
15✔
6270
                AppObject()
16✔
6271
                {
16✔
6272
                    api_key_name = util::format("%1", random_string(15));
16✔
6273
                }
16✔
6274
                std::string api_key_name;
16✔
6275
            };
10✔
6276

15✔
6277
            auto create_api_callback = [](realm_userdata_t, realm_app_user_apikey_t* api_key,
15✔
6278
                                          const realm_app_error_t* error) {
16!
6279
                REQUIRE_FALSE(error);
16!
6280
                REQUIRE(api_key);
16!
6281
                CHECK(strcmp(api_key->name, AppObject::create().key()) == 0);
16!
6282
            };
16!
6283
            auto current_user = realm_app_get_current_user(&app);
16✔
6284
            auto state = realm_app_user_apikey_provider_client_create_apikey(
16✔
6285
                &app, current_user, AppObject::create().key(), create_api_callback, nullptr, nullptr);
16!
6286
            REQUIRE(state);
10!
6287

15!
6288
            auto fetch_apikey_callback = [](realm_userdata_t, realm_app_user_apikey_t* keys, size_t count,
15✔
6289
                                            realm_app_error_t* error) {
16!
6290
                REQUIRE_FALSE(error);
16!
6291
                REQUIRE(keys);
16!
6292
                REQUIRE(count == 1);
16!
6293
                CHECK(strcmp(keys[0].name, AppObject::create().key()) == 0);
16!
6294
            };
16!
6295
            state = realm_app_user_apikey_provider_client_fetch_apikeys(&app, current_user, fetch_apikey_callback,
16✔
6296
                                                                        nullptr, nullptr);
16!
6297
            REQUIRE(state);
16!
6298
            realm_release(current_user);
16!
6299
        }
28✔
6300
    }
78✔
6301
    realm_release(http_transport);
88✔
6302
}
24✔
6303

24✔
6304
TEST_CASE("app: flx-sync compensating writes C API support", "[sync][flx][c_api][baas]") {
14✔
6305
    using namespace realm::app;
16✔
6306
    FLXSyncTestHarness harness("c_api_comp_writes");
16✔
6307
    create_user_and_log_in(harness.app());
16✔
6308
    SyncTestFile test_config(harness.app()->current_user(), harness.schema(), realm::SyncConfig::FLXSyncEnabled{});
16✔
6309
    test_config.sync_config = std::make_shared<realm_sync_config_t>(*test_config.sync_config);
16✔
6310
    realm_sync_config_t* sync_config = static_cast<realm_sync_config_t*>(test_config.sync_config.get());
10✔
6311

15✔
6312
    struct TestState {
15✔
6313
        std::mutex mutex;
16✔
6314
        std::condition_variable cond_var;
16✔
6315
        std::vector<sync::CompensatingWriteErrorInfo> compensating_writes;
16✔
6316
    };
16✔
6317
    auto state = std::make_unique<TestState>();
16✔
6318
    realm_sync_config_set_error_handler(
16✔
6319
        sync_config,
16✔
6320
        [](realm_userdata_t user_data, realm_sync_session_t*, const realm_sync_error_t error) {
16✔
6321
            auto state = reinterpret_cast<TestState*>(user_data);
16!
6322
            REQUIRE(error.status.error == RLM_ERR_SYNC_COMPENSATING_WRITE);
16!
6323
            REQUIRE(error.compensating_writes_length > 0);
10!
6324

15!
6325
            std::lock_guard<std::mutex> lk(state->mutex);
39✔
6326
            for (size_t i = 0; i < error.compensating_writes_length; ++i) {
32✔
6327
                sync::CompensatingWriteErrorInfo err_info;
34✔
6328
                err_info.object_name = error.compensating_writes[i].object_name;
32✔
6329
                err_info.reason = error.compensating_writes[i].reason;
32✔
6330
                Mixed pk(c_api::from_capi(error.compensating_writes[i].primary_key));
32✔
6331
                err_info.primary_key = pk;
32✔
6332
                state->compensating_writes.push_back(std::move(err_info));
32✔
6333
            }
14✔
6334

17✔
6335
            state->cond_var.notify_one();
15✔
6336
        },
16✔
6337
        state.get(), [](realm_userdata_t) {});
10✔
6338

15✔
6339
    auto realm = Realm::get_shared_realm(test_config);
9✔
6340

15✔
6341
    auto mut_subs = realm->get_latest_subscription_set().make_mutable_copy();
15✔
6342
    auto table = realm->read_group().get_table("class_TopLevel");
16✔
6343
    mut_subs.insert_or_assign(Query(table).equal(table->get_column_key("queryable_str_field"), "bizz"));
16✔
6344
    mut_subs.commit();
10✔
6345

15✔
6346
    CppContext c(realm);
15✔
6347
    realm->begin_transaction();
16✔
6348
    auto obj_1_id = ObjectId::gen();
16✔
6349
    auto obj_2_id = ObjectId::gen();
16✔
6350
    Object::create(c, realm, "TopLevel",
16✔
6351
                   std::any(AnyDict{
16✔
6352
                       {"_id", obj_1_id},
16✔
6353
                       {"queryable_str_field", std::string{"foo"}},
16✔
6354
                   }));
16✔
6355
    Object::create(c, realm, "TopLevel",
16✔
6356
                   std::any(AnyDict{
16✔
6357
                       {"_id", obj_2_id},
16✔
6358
                       {"queryable_str_field", std::string{"bar"}},
16✔
6359
                   }));
16✔
6360
    realm->commit_transaction();
10✔
6361

15✔
6362
    std::unique_lock<std::mutex> lk(state->mutex);
27✔
6363
    state->cond_var.wait_for(lk, std::chrono::seconds(30), [&] {
30✔
6364
        return state->compensating_writes.size() == 2;
32✔
6365
    });
14✔
6366

17✔
6367
    auto errors = std::move(state->compensating_writes);
15✔
6368
    lk.unlock();
10✔
6369

21✔
6370
    std::sort(errors.begin(), errors.end(), [](const auto& lhs, const auto& rhs) {
22✔
6371
        return lhs.primary_key < rhs.primary_key;
23✔
6372
    });
11✔
6373

15!
6374
    REQUIRE(errors.size() == 2);
15!
6375
    REQUIRE(errors[0].primary_key == obj_1_id);
16!
6376
    REQUIRE(errors[0].object_name == "TopLevel");
16!
6377
    REQUIRE_THAT(errors[0].reason, Catch::Matchers::ContainsSubstring("object is outside of the current query view"));
16!
6378
    REQUIRE(errors[1].primary_key == obj_2_id);
16!
6379
    REQUIRE(errors[1].object_name == "TopLevel");
16!
6380
    REQUIRE_THAT(errors[1].reason, Catch::Matchers::ContainsSubstring("object is outside of the current query view"));
16!
6381
}
4✔
6382

14✔
6383
TEST_CASE("app: flx-sync basic tests", "[sync][flx][c_api][baas]") {
14✔
6384
    using namespace realm::app;
10✔
6385

15✔
6386
    auto make_schema = [] {
15✔
6387
        Schema schema{{"Obj",
16✔
6388
                       {{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
16✔
6389
                        {"name", PropertyType::String | PropertyType::Nullable},
16✔
6390
                        {"value", PropertyType::Int | PropertyType::Nullable}}}};
10✔
6391

15✔
6392
        return FLXSyncTestHarness::ServerSchema{std::move(schema), {"name", "value"}};
15✔
6393
    };
10✔
6394

15✔
6395
    FLXSyncTestHarness harness("c_api_flx_sync_test", make_schema());
15✔
6396
    auto foo_obj_id = ObjectId::gen();
16✔
6397
    auto bar_obj_id = ObjectId::gen();
10✔
6398

15✔
6399
    harness.load_initial_data([&](SharedRealm& realm) {
15✔
6400
        CppContext c(realm);
16✔
6401
        Object::create(
16✔
6402
            c, realm, "Obj",
16✔
6403
            std::any(AnyDict{{"_id", foo_obj_id}, {"name", std::string{"foo"}}, {"value", static_cast<int64_t>(5)}}));
16✔
6404
        Object::create(c, realm, "Obj",
16✔
6405
                       std::any(AnyDict{
16✔
6406
                           {"_id", bar_obj_id}, {"name", std::string{"bar"}}, {"value", static_cast<int64_t>(10)}}));
16✔
6407
    });
10✔
6408

15✔
6409
    harness.do_with_new_realm([&](SharedRealm realm) {
15✔
6410
        realm_t c_wrap_realm(realm);
10✔
6411

15✔
6412
        wait_for_download(*realm);
15✔
6413
        {
16✔
6414
            auto empty_subs = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16!
6415
            CHECK(realm_sync_subscription_set_size(empty_subs) == 0);
16!
6416
            CHECK(realm_sync_subscription_set_version(empty_subs) == 0);
16!
6417
            realm_sync_on_subscription_set_state_change_wait(
16!
6418
                empty_subs, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16✔
6419
            realm_release(empty_subs);
16✔
6420
        }
16✔
6421
        realm_class_info_t table_info;
16✔
6422
        bool found;
16!
6423
        CHECK(realm_find_class(&c_wrap_realm, "Obj", &found, &table_info));
16!
6424
        auto c_wrap_query_foo = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'foo'", 0, nullptr);
16!
6425
        auto c_wrap_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
16✔
6426
        {
16✔
6427
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16!
6428
            CHECK(sub != nullptr);
16!
6429
            auto new_subs = realm_sync_make_subscription_set_mutable(sub);
16!
6430
            std::size_t index = -1;
16✔
6431
            bool inserted = false;
10✔
6432
            // realm_results_t
15✔
6433
            auto res = realm_sync_subscription_set_insert_or_assign_query(new_subs, c_wrap_query_foo, nullptr, &index,
15✔
6434
                                                                          &inserted);
16!
6435
            CHECK(inserted == true);
16!
6436
            CHECK(index == 0);
16!
6437
            CHECK(res);
16!
6438
            auto subs = realm_sync_subscription_set_commit(new_subs);
16!
6439
            auto state = realm_sync_on_subscription_set_state_change_wait(
16✔
6440
                subs, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6441
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6442
            realm_release(sub);
16!
6443
            realm_release(new_subs);
16✔
6444
            realm_release(subs);
16✔
6445
        }
10✔
6446

15✔
6447
        wait_for_download(*realm);
15✔
6448
        {
16✔
6449
            realm_refresh(&c_wrap_realm, nullptr);
16✔
6450
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
16✔
6451
            size_t count = 0;
16✔
6452
            realm_results_count(results, &count);
16!
6453
            CHECK(count == 1);
16!
6454
            auto object = realm_results_get_object(results, 0);
16!
6455
            REQUIRE(realm_object_is_valid(object));
16!
6456
            REQUIRE(object->get_column_value<ObjectId>("_id") == foo_obj_id);
16!
6457
            realm_release(object);
16!
6458
            realm_release(results);
16✔
6459
        }
10✔
6460

15✔
6461
        {
15✔
6462
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16✔
6463
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
16✔
6464
            std::size_t index = -1;
16✔
6465
            bool inserted = false;
16✔
6466
            realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_bar, nullptr, &index, &inserted);
16!
6467
            CHECK(inserted);
16!
6468
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
16!
6469
            auto state = realm_sync_on_subscription_set_state_change_wait(
16✔
6470
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6471
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6472
            realm_release(sub);
16!
6473
            realm_release(mut_sub);
16✔
6474
            realm_release(sub_c);
16✔
6475
        }
10✔
6476

15✔
6477
        {
15✔
6478
            realm_refresh(&c_wrap_realm, nullptr);
16✔
6479
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
16✔
6480
            size_t count = 0;
16✔
6481
            realm_results_count(results, &count);
16!
6482
            CHECK(count == 2);
16!
6483
            realm_release(results);
16!
6484
        }
10✔
6485

15✔
6486
        {
15✔
6487
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16✔
6488
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
16✔
6489
            auto s = realm_sync_find_subscription_by_query(sub, c_wrap_query_foo);
16!
6490
            CHECK(s != nullptr);
16!
6491
            auto cloned_s = realm_clone(s);
16!
6492
            CHECK(realm_equals(s, cloned_s));
16!
6493
            bool erased = false;
16!
6494
            auto result = realm_sync_subscription_set_erase_by_query(mut_sub, c_wrap_query_foo, &erased);
16!
6495
            CHECK(erased);
16!
6496
            CHECK(result);
16!
6497
            auto c_wrap_new_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
16!
6498
            std::size_t index = -1;
16✔
6499
            bool inserted = false;
16✔
6500
            auto results = realm_query_find_all(c_wrap_new_query_bar);
16✔
6501
            bool updated =
16✔
6502
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, nullptr, &index, &inserted);
16!
6503
            CHECK(!inserted);
16!
6504
            CHECK(updated);
16!
6505
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
16!
6506
            auto state = realm_sync_on_subscription_set_state_change_wait(
16✔
6507
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6508
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6509
            realm_release(s);
16!
6510
            realm_release(cloned_s);
16✔
6511
            realm_release(sub);
16✔
6512
            realm_release(mut_sub);
16✔
6513
            realm_release(sub_c);
16✔
6514
            realm_release(results);
16✔
6515
            realm_release(c_wrap_new_query_bar);
16✔
6516
        }
10✔
6517

15✔
6518
        {
15✔
6519
            realm_refresh(&c_wrap_realm, nullptr);
16✔
6520
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
16✔
6521
            size_t count = 0;
16✔
6522
            realm_results_count(results, &count);
16!
6523
            CHECK(count == 1);
16!
6524
            auto object = realm_results_get_object(results, 0);
16!
6525
            REQUIRE(realm_object_is_valid(object));
16!
6526
            REQUIRE(object->get_column_value<ObjectId>("_id") == bar_obj_id);
16!
6527
            realm_release(object);
16!
6528
            realm_release(results);
16✔
6529
        }
10✔
6530

15✔
6531
        {
15✔
6532
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16✔
6533
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
16✔
6534
            auto cleared = realm_sync_subscription_set_clear(mut_sub);
16!
6535
            CHECK(cleared);
16!
6536
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
16!
6537
            auto state = realm_sync_on_subscription_set_state_change_wait(
16✔
6538
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6539
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6540
            realm_release(sub);
16!
6541
            realm_release(mut_sub);
16✔
6542
            realm_release(sub_c);
16✔
6543
        }
10✔
6544

15✔
6545
        {
15✔
6546
            realm_refresh(&c_wrap_realm, nullptr);
16✔
6547
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
16✔
6548
            size_t count = std::numeric_limits<std::size_t>::max();
16✔
6549
            realm_results_count(results, &count);
16!
6550
            CHECK(count == 0);
16!
6551
            realm_release(results);
16!
6552
        }
10✔
6553

15✔
6554
        {
15✔
6555
            auto c_wrap_new_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
16✔
6556
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16✔
6557
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
16✔
6558
            std::size_t index = -1;
16✔
6559
            bool inserted = false;
16✔
6560
            auto results = realm_query_find_all(c_wrap_new_query_bar);
16✔
6561
            bool success =
16✔
6562
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "bar", &index, &inserted);
16!
6563
            CHECK(inserted);
16!
6564
            CHECK(success);
16!
6565
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
16!
6566
            auto state = realm_sync_on_subscription_set_state_change_wait(
16✔
6567
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6568
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6569
            realm_release(sub);
16!
6570
            realm_release(mut_sub);
16✔
6571
            realm_release(sub_c);
16✔
6572
            realm_release(results);
16✔
6573
            realm_release(c_wrap_new_query_bar);
16✔
6574
        }
10✔
6575

15✔
6576
        {
15✔
6577
            realm->refresh();
16✔
6578
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
16✔
6579
            size_t count = std::numeric_limits<std::size_t>::max();
16✔
6580
            realm_results_count(results, &count);
16!
6581
            CHECK(count == 1);
16!
6582
            realm_release(results);
16!
6583
        }
10✔
6584

15✔
6585
        {
15✔
6586
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16✔
6587
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
16✔
6588
            bool erased = false;
16✔
6589
            realm_sync_subscription_set_erase_by_name(mut_sub, "bar", &erased);
16!
6590
            CHECK(erased);
16!
6591
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
16!
6592
            auto state = realm_sync_on_subscription_set_state_change_wait(
16✔
6593
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6594
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
16!
6595
            realm_release(sub);
16!
6596
            realm_release(mut_sub);
16✔
6597
            realm_release(sub_c);
16✔
6598
        }
10✔
6599

15✔
6600
        {
15✔
6601
            realm_refresh(&c_wrap_realm, nullptr);
16✔
6602
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
16✔
6603
            size_t count = std::numeric_limits<std::size_t>::max();
16✔
6604
            realm_results_count(results, &count);
16!
6605
            CHECK(count == 0);
16!
6606
            realm_release(results);
16!
6607
        }
10✔
6608

15✔
6609
        {
15✔
6610
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16✔
6611
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
16✔
6612
            std::size_t index = -1;
16✔
6613
            bool inserted = false;
16✔
6614
            auto results = realm_query_find_all(c_wrap_query_bar);
16✔
6615
            bool success =
16✔
6616
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, nullptr, &index, &inserted);
16!
6617
            CHECK(inserted);
16!
6618
            CHECK(success);
16!
6619
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
10!
6620
            // lambdas with state cannot be easily converted to function pointers, add a simple singleton that syncs
9✔
6621
            // the state among threads
14✔
6622
            struct SyncObject {
15✔
6623
                std::mutex m_mutex;
16✔
6624
                std::condition_variable m_cv;
16✔
6625
                realm_flx_sync_subscription_set_state_e m_state{RLM_SYNC_SUBSCRIPTION_UNCOMMITTED};
16✔
6626
                void* m_userdata;
10✔
6627

15✔
6628
                static SyncObject& create()
27✔
6629
                {
30✔
6630
                    static SyncObject sync_object;
32✔
6631
                    return sync_object;
32✔
6632
                }
14✔
6633

17✔
6634
                void set_state_and_notify(void* userdata, realm_flx_sync_subscription_set_state_e state)
15✔
6635
                {
16✔
6636
                    {
16✔
6637
                        std::lock_guard<std::mutex> guard{m_mutex};
16✔
6638
                        m_state = state;
16✔
6639
                        m_userdata = userdata;
16✔
6640
                    }
16✔
6641
                    m_cv.notify_one();
16✔
6642
                }
10✔
6643

15✔
6644
                realm_flx_sync_subscription_set_state_e wait_state()
15✔
6645
                {
16✔
6646
                    using namespace std::chrono_literals;
16✔
6647
                    std::unique_lock<std::mutex> lock{m_mutex};
16✔
6648
                    const auto delay = TEST_TIMEOUT_EXTRA > 0 ? std::chrono::seconds(5 + TEST_TIMEOUT_EXTRA) : 5s;
28✔
6649
                    bool completed_within_time_limit = m_cv.wait_for(lock, delay, [this]() {
30✔
6650
                        return m_state == RLM_SYNC_SUBSCRIPTION_COMPLETE && m_userdata != nullptr;
32✔
6651
                    });
20✔
6652
                    CHECK(completed_within_time_limit);
18!
6653
                    return m_state;
16!
6654
                }
16✔
6655
            };
10✔
6656

15✔
6657
            auto callback = [](void* userdata, realm_flx_sync_subscription_set_state_e sub_state) {
15✔
6658
                SyncObject::create().set_state_and_notify(userdata, sub_state);
16✔
6659
            };
16✔
6660
            int userdata = 0;
16✔
6661
            realm_sync_on_subscription_set_state_change_async(
16✔
6662
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE, callback, &userdata,
16✔
6663
                nullptr);
16!
6664
            CHECK(SyncObject::create().wait_state() ==
16!
6665
                  realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
10!
6666

15✔
6667
            realm_release(sub);
15✔
6668
            realm_release(mut_sub);
16✔
6669
            realm_release(sub_c);
16✔
6670
            realm_release(results);
16✔
6671
        }
10✔
6672

15✔
6673
        {
15✔
6674
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16✔
6675
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
16✔
6676
            std::size_t index = -1;
16✔
6677
            bool inserted = false;
16✔
6678
            auto results = realm_query_find_all(c_wrap_query_bar);
16✔
6679
            bool success =
16✔
6680
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "test_bar", &index, &inserted);
16!
6681
            CHECK(inserted);
16!
6682
            CHECK(success);
16!
6683
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
16!
6684
            auto sub_by_res = realm_sync_find_subscription_by_results(sub_c, results);
16!
6685
            CHECK(sub_by_res);
16!
6686
            const auto realm_flx_sync_sub = realm_sync_find_subscription_by_name(sub_c, "test_bar");
16!
6687
            REQUIRE(realm_flx_sync_sub);
10!
6688

15!
6689
            const realm_object_id_t& object_id = realm_sync_subscription_id(realm_flx_sync_sub);
15!
6690
            CHECK(!realm::c_api::from_capi(object_id).to_string().empty());
10!
6691

15!
6692
            realm_timestamp_t create_time = realm_sync_subscription_created_at(realm_flx_sync_sub);
15✔
6693
            realm_timestamp_t update_time = realm_sync_subscription_updated_at(realm_flx_sync_sub);
16!
6694
            CHECK(create_time.seconds == update_time.seconds);
16!
6695
            CHECK(create_time.nanoseconds == update_time.nanoseconds);
16!
6696
            CHECK(create_time.seconds != 0);
16!
6697
            CHECK(create_time.seconds != 0);
10!
6698

15!
6699
            const realm_string_t& name = realm_sync_subscription_name(realm_flx_sync_sub);
15!
6700
            REQUIRE(name.size != 0);
16!
6701
            CHECK(std::string{name.data, name.size} == "test_bar");
10!
6702

15!
6703
            auto mut_sub_1 = realm_sync_make_subscription_set_mutable(sub_c);
15✔
6704
            bool erased = false;
16!
6705
            CHECK(realm_sync_subscription_set_erase_by_id(mut_sub_1, &object_id, &erased));
16!
6706
            CHECK(erased);
16!
6707
            auto sub_c_1 = realm_sync_subscription_set_commit(mut_sub_1);
10!
6708

15✔
6709
            const auto sub_not_found = realm_sync_find_subscription_by_name(sub_c_1, "test_bar");
15!
6710
            CHECK(!sub_not_found);
10!
6711

15!
6712
            realm_release(sub);
15✔
6713
            realm_release(mut_sub);
16✔
6714
            realm_release(results);
16✔
6715
            realm_release(sub_c);
16✔
6716
            realm_release(sub_by_res);
16✔
6717
            realm_release(realm_flx_sync_sub);
16✔
6718
            realm_release(mut_sub_1);
16✔
6719
            realm_release(sub_c_1);
16✔
6720
            realm_release(sub_not_found);
16✔
6721
        }
10✔
6722

15✔
6723
        {
15✔
6724
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16✔
6725
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
16✔
6726
            std::size_t index = -1;
16✔
6727
            bool inserted = false;
16✔
6728
            auto results = realm_query_find_all(c_wrap_query_bar);
16✔
6729
            bool success =
16✔
6730
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "test_bar", &index, &inserted);
16!
6731
            CHECK(inserted);
16!
6732
            CHECK(success);
16!
6733
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
16!
6734
            auto sub_by_res = realm_sync_find_subscription_by_results(sub_c, results);
16!
6735
            CHECK(sub_by_res);
10!
6736

15!
6737
            auto mut_sub_1 = realm_sync_make_subscription_set_mutable(sub_c);
15✔
6738
            bool erased = false;
16✔
6739
            realm_sync_subscription_set_erase_by_results(mut_sub_1, results, &erased);
16!
6740
            CHECK(erased);
16!
6741
            auto sub_c_1 = realm_sync_subscription_set_commit(mut_sub_1);
10!
6742

15✔
6743
            realm_release(sub);
15✔
6744
            realm_release(mut_sub);
16✔
6745
            realm_release(results);
16✔
6746
            realm_release(sub_c);
16✔
6747
            realm_release(sub_by_res);
16✔
6748
            realm_release(mut_sub_1);
16✔
6749
            realm_release(sub_c_1);
16✔
6750
        }
10✔
6751

15✔
6752
        {
15✔
6753
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
16✔
6754
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
16✔
6755
            std::size_t index = -1;
16✔
6756
            bool inserted = false;
16!
6757
            CHECK(realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_bar, nullptr, &index,
16!
6758
                                                                     &inserted));
16!
6759
            CHECK(inserted);
16!
6760
            CHECK(realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_foo, nullptr, &index,
16!
6761
                                                                     &inserted));
16!
6762
            CHECK(inserted);
16!
6763
            bool erased = false;
16!
6764
            CHECK(realm_sync_subscription_set_erase_by_class_name(mut_sub, "Obj", &erased));
16!
6765
            CHECK(erased);
10!
6766
            // Nothing to remove when trying again.
15!
6767
            CHECK(realm_sync_subscription_set_erase_by_class_name(mut_sub, "Obj", &erased));
15!
6768
            CHECK_FALSE(erased);
16!
6769
            realm_release(sub);
16!
6770
            realm_release(mut_sub);
16✔
6771
        }
10✔
6772

15✔
6773
        realm_release(c_wrap_query_foo);
15✔
6774
        realm_release(c_wrap_query_bar);
16✔
6775
    });
16✔
6776
}
4✔
6777

14✔
6778
TEST_CASE("C API app: websocket provider", "[sync][app][c_api][baas]") {
14✔
6779
    using namespace realm::app;
16✔
6780
    using namespace realm::sync;
16✔
6781
    using namespace realm::sync::websocket;
10✔
6782

15✔
6783
    struct TestWebSocketObserverShim : sync::WebSocketObserver {
15✔
6784
    public:
16✔
6785
        explicit TestWebSocketObserverShim(realm_websocket_observer_t* observer)
16✔
6786
            : m_observer(observer)
16✔
6787
        {
16✔
6788
        }
10✔
6789

15✔
6790
        void websocket_connected_handler(const std::string& protocol) override
15✔
6791
        {
16✔
6792
            REALM_ASSERT(m_observer);
16✔
6793
            realm_sync_socket_websocket_connected(m_observer, protocol.c_str());
16✔
6794
        }
10✔
6795

15✔
6796
        void websocket_error_handler() override
9✔
6797
        {
3!
6798
            REALM_ASSERT(m_observer);
1!
UNCOV
6799
            realm_sync_socket_websocket_error(m_observer);
×
6800
        }
6✔
6801

13✔
6802
        bool websocket_binary_message_received(util::Span<const char> data) override
39✔
6803
        {
44✔
6804
            REALM_ASSERT(m_observer);
48✔
6805
            return realm_sync_socket_websocket_message(m_observer, data.data(), data.size());
48✔
6806
        }
18✔
6807

19✔
6808
        bool websocket_closed_handler(bool was_clean, WebSocketError error, std::string_view msg) override
9✔
6809
        {
3!
6810
            REALM_ASSERT(m_observer);
1!
6811
            return realm_sync_socket_websocket_closed(m_observer, was_clean,
×
UNCOV
6812
                                                      static_cast<realm_web_socket_errno_e>(error), msg.data());
×
6813
        }
6✔
6814

13✔
6815
    private:
15✔
6816
        realm_websocket_observer_t* m_observer;
16✔
6817
    };
10✔
6818

15✔
6819
    struct TestWebSocket : realm::c_api::WrapC, WebSocketInterface {
15✔
6820
    public:
16✔
6821
        TestWebSocket(DefaultSocketProvider& socket_provider, realm_websocket_endpoint_t endpoint,
16✔
6822
                      realm_websocket_observer_t* realm_websocket_observer)
16✔
6823
        {
16✔
6824
            WebSocketEndpoint ws_endpoint;
16✔
6825
            ws_endpoint.address = endpoint.address;
16✔
6826
            ws_endpoint.port = endpoint.port;
16✔
6827
            ws_endpoint.path = endpoint.path;
148✔
6828
            for (size_t i = 0; i < endpoint.num_protocols; ++i) {
158✔
6829
                ws_endpoint.protocols.push_back(endpoint.protocols[i]);
178✔
6830
            }
56✔
6831
            ws_endpoint.is_ssl = endpoint.is_ssl;
30✔
6832

15✔
6833
            auto observer = std::make_unique<TestWebSocketObserverShim>(realm_websocket_observer);
15✔
6834
            m_websocket = socket_provider.connect(std::move(observer), std::move(ws_endpoint));
16✔
6835
        }
10✔
6836

15✔
6837
        void async_write_binary(util::Span<const char> data, SyncSocketProvider::FunctionHandler&& handler) override
63✔
6838
        {
72✔
6839
            m_websocket->async_write_binary(data, std::move(handler));
80✔
6840
        }
26✔
6841

23✔
6842
    private:
15✔
6843
        std::unique_ptr<WebSocketInterface> m_websocket;
16✔
6844
    };
10✔
6845

15✔
6846
    struct TestSyncTimer : realm::c_api::WrapC, SyncSocketProvider::Timer {
15✔
6847
    public:
16✔
6848
        TestSyncTimer(DefaultSocketProvider& socket_provider, std::chrono::milliseconds delay,
16✔
6849
                      realm_sync_socket_timer_callback_t* callback)
52✔
6850
        {
58✔
6851
            m_timer = socket_provider.create_timer(delay, [capi_callback = callback](Status s) {
64✔
6852
                if (s.code() == ErrorCodes::Error::OperationAborted) {
52✔
6853
                    return realm_sync_socket_timer_canceled(capi_callback);
50✔
6854
                }
24✔
6855
                realm_sync_socket_timer_complete(
20✔
6856
                    capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()), s.reason().c_str());
16✔
6857
            });
52✔
6858
        }
16✔
6859

21✔
6860
        void cancel() override
51✔
6861
        {
58✔
6862
            m_timer->cancel();
64✔
6863
        }
22✔
6864

21✔
6865
    private:
15✔
6866
        SyncSocketProvider::SyncTimer m_timer;
16✔
6867
    };
10✔
6868

15✔
6869
    struct TestData {
15✔
6870
        DefaultSocketProvider* socket_provider;
16✔
6871
        int free_count = 0;
16✔
6872
    };
10✔
6873

15✔
6874
    DefaultSocketProvider default_socket_provider(util::Logger::get_default_logger(), "SocketProvider");
9✔
6875

15✔
6876
    static std::mutex catch_mutex;
15✔
6877
#define LOCKED_REQUIRE(...)                                                                                          \
1,036✔
6878
    do {                                                                                                             \
1,206✔
6879
        std::lock_guard lock(catch_mutex);                                                                           \
1,376✔
6880
        REQUIRE(__VA_ARGS__);                                                                                        \
1,376✔
6881
    } while (0)
350✔
6882

185✔
6883
    auto userdata_free_fn = [](realm_userdata_t user_ptr) {
15✔
6884
        auto test_data = static_cast<TestData*>(user_ptr);
16!
6885
        LOCKED_REQUIRE(test_data);
16!
6886
        test_data->free_count++;
16!
6887
    };
220✔
6888
    auto post_fn = [](realm_userdata_t userdata, realm_sync_socket_post_callback_t* callback) {
254✔
6889
        auto test_data = static_cast<TestData*>(userdata);
288!
6890
        LOCKED_REQUIRE(test_data);
288!
6891
        LOCKED_REQUIRE(test_data->socket_provider);
288!
6892
        test_data->socket_provider->post([capi_callback = callback](Status s) {
288!
6893
            realm_sync_socket_post_complete(capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()),
288✔
6894
                                            s.reason().c_str());
288✔
6895
        });
288✔
6896
    };
84✔
6897
    auto create_timer_fn = [](realm_userdata_t userdata, uint64_t delay_ms,
86✔
6898
                              realm_sync_socket_timer_callback_t* callback) -> realm_sync_socket_timer_t {
58✔
6899
        auto test_data = static_cast<TestData*>(userdata);
64!
6900
        LOCKED_REQUIRE(test_data);
64!
6901
        LOCKED_REQUIRE(test_data->socket_provider);
64!
6902
        return static_cast<realm_sync_socket_timer_t>(
64!
6903
            new TestSyncTimer(*test_data->socket_provider, std::chrono::milliseconds(delay_ms), callback));
64✔
6904
    };
64✔
6905
    auto cancel_timer_fn = [](realm_userdata_t userdata, realm_sync_socket_timer_t sync_timer) {
64✔
6906
        auto test_data = static_cast<TestData*>(userdata);
64!
6907
        LOCKED_REQUIRE(test_data);
64!
6908
        LOCKED_REQUIRE(test_data->socket_provider);
64!
6909
        auto timer = static_cast<TestSyncTimer*>(sync_timer);
64!
6910
        LOCKED_REQUIRE(timer);
64!
6911
        timer->cancel();
64!
6912
    };
64✔
6913
    auto free_timer_fn = [](realm_userdata_t userdata, realm_sync_socket_timer_t sync_timer) {
64✔
6914
        auto test_data = static_cast<TestData*>(userdata);
64!
6915
        LOCKED_REQUIRE(test_data);
64!
6916
        LOCKED_REQUIRE(test_data->socket_provider);
64!
6917
        realm_release(sync_timer);
64!
6918
    };
28✔
6919
    auto websocket_connect_fn =
22✔
6920
        [](realm_userdata_t userdata, realm_websocket_endpoint_t endpoint,
16✔
6921
           realm_websocket_observer_t* realm_websocket_observer) -> realm_sync_socket_websocket_t {
16✔
6922
        auto test_data = static_cast<TestData*>(userdata);
16!
6923
        LOCKED_REQUIRE(test_data);
16!
6924
        LOCKED_REQUIRE(test_data->socket_provider);
16!
6925
        return static_cast<realm_sync_socket_websocket_t>(
16!
6926
            new TestWebSocket(*test_data->socket_provider, endpoint, realm_websocket_observer));
16✔
6927
    };
16✔
6928
    auto websocket_async_write_fn = [](realm_userdata_t userdata, realm_sync_socket_websocket_t sync_websocket,
64✔
6929
                                       const char* data, size_t size, realm_sync_socket_write_callback_t* callback) {
72✔
6930
        auto test_data = static_cast<TestData*>(userdata);
80!
6931
        LOCKED_REQUIRE(test_data);
80!
6932
        LOCKED_REQUIRE(test_data->socket_provider);
80!
6933
        auto websocket = static_cast<TestWebSocket*>(sync_websocket);
80!
6934
        LOCKED_REQUIRE(websocket);
80!
6935
        websocket->async_write_binary(util::Span{data, size}, [capi_callback = callback](Status s) {
80!
6936
            realm_sync_socket_write_complete(
80✔
6937
                capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()), s.reason().c_str());
80✔
6938
        });
80✔
6939
    };
32✔
6940
    auto websocket_free_fn = [](realm_userdata_t userdata, realm_sync_socket_websocket_t sync_websocket) {
24✔
6941
        auto test_data = static_cast<TestData*>(userdata);
16!
6942
        LOCKED_REQUIRE(test_data);
16!
6943
        LOCKED_REQUIRE(test_data->socket_provider);
16!
6944
        realm_release(sync_websocket);
16!
6945
    };
10✔
6946

9✔
6947
    // Test drive.
14✔
6948
    TestData test_data{&default_socket_provider};
15✔
6949
    auto socket_provider = realm_sync_socket_new(static_cast<realm_userdata_t>(&test_data), userdata_free_fn, post_fn,
16✔
6950
                                                 create_timer_fn, cancel_timer_fn, free_timer_fn,
16✔
6951
                                                 websocket_connect_fn, websocket_async_write_fn, websocket_free_fn);
16✔
6952
    {
16✔
6953
        FLXSyncTestHarness harness("c_api_websocket_provider", FLXSyncTestHarness::default_server_schema(),
16✔
6954
                                   instance_of<SynchronousTestTransport>, *socket_provider);
10✔
6955

15✔
6956
        SyncTestFile test_config(harness.app()->current_user(), harness.schema(),
15✔
6957
                                 realm::SyncConfig::FLXSyncEnabled{});
16✔
6958
        auto realm = Realm::get_shared_realm(test_config);
16✔
6959
        bool wait_success = wait_for_download(*realm);
16!
6960
        LOCKED_REQUIRE(!wait_success);
16!
6961
    }
16!
6962
    realm_release(socket_provider);
16✔
6963
    default_socket_provider.stop(true);
16!
6964
    LOCKED_REQUIRE(test_data.free_count == 1);
16!
6965
}
4!
6966
#endif // REALM_ENABLE_AUTH_TESTS
2✔
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