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

realm / realm-core / thomas.goyne_275

09 Apr 2024 03:33AM UTC 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_base_file_path(test_sync_client_config.get(), "some string");
16✔
542
        CHECK(test_sync_client_config->base_file_path == "some string");
16!
543
        realm_sync_client_config_set_metadata_mode(test_sync_client_config.get(),
16!
544
                                                   RLM_SYNC_CLIENT_METADATA_MODE_ENCRYPTED);
16✔
545
        CHECK(test_sync_client_config->metadata_mode ==
16!
546
              static_cast<realm::SyncClientConfig::MetadataMode>(RLM_SYNC_CLIENT_METADATA_MODE_ENCRYPTED));
16!
547
        auto enc_key = make_test_encryption_key(123);
16✔
548
        realm_sync_client_config_set_metadata_encryption_key(test_sync_client_config.get(),
16!
549
                                                             reinterpret_cast<uint8_t*>(enc_key.data()));
16✔
550
        CHECK(test_sync_client_config->custom_encryption_key);
16!
551
        CHECK(std::equal(enc_key.begin(), enc_key.end(), test_sync_client_config->custom_encryption_key->begin()));
16!
552
        realm_sync_client_config_set_reconnect_mode(test_sync_client_config.get(),
16!
553
                                                    RLM_SYNC_CLIENT_RECONNECT_MODE_TESTING);
16✔
554
        CHECK(test_sync_client_config->reconnect_mode ==
16!
555
              static_cast<ReconnectMode>(RLM_SYNC_CLIENT_RECONNECT_MODE_TESTING));
16✔
556
        realm_sync_client_config_set_multiplex_sessions(test_sync_client_config.get(), true);
16!
557
        CHECK(test_sync_client_config->multiplex_sessions);
16!
558
        realm_sync_client_config_set_multiplex_sessions(test_sync_client_config.get(), false);
16!
559
        CHECK_FALSE(test_sync_client_config->multiplex_sessions);
16!
560
        realm_sync_client_config_set_user_agent_binding_info(test_sync_client_config.get(), "some user agent stg");
16!
561
        CHECK(test_sync_client_config->user_agent_binding_info == "some user agent stg");
16!
562
        realm_sync_client_config_set_user_agent_application_info(test_sync_client_config.get(), "some application");
16!
563
        CHECK(test_sync_client_config->user_agent_application_info == "some application");
16!
564
        realm_sync_client_config_set_connect_timeout(test_sync_client_config.get(), 666);
16!
565
        CHECK(test_sync_client_config->timeouts.connect_timeout == 666);
16!
566
        realm_sync_client_config_set_connection_linger_time(test_sync_client_config.get(), 999);
16!
567
        CHECK(test_sync_client_config->timeouts.connection_linger_time == 999);
16!
568
        realm_sync_client_config_set_ping_keepalive_period(test_sync_client_config.get(), 555);
16✔
569
        CHECK(test_sync_client_config->timeouts.ping_keepalive_period == 555);
16!
570
        realm_sync_client_config_set_pong_keepalive_timeout(test_sync_client_config.get(), 100000);
16✔
571
        CHECK(test_sync_client_config->timeouts.pong_keepalive_timeout == 100000);
40!
572
        realm_sync_client_config_set_fast_reconnect_limit(test_sync_client_config.get(), 1099);
66✔
573
        CHECK(test_sync_client_config->timeouts.fast_reconnect_limit == 1099);
16!
574
        realm_sync_client_config_set_resumption_delay_interval(test_sync_client_config.get(), 1024);
16✔
575
        CHECK(test_sync_client_config->timeouts.reconnect_backoff_info.resumption_delay_interval.count() == 1024);
16!
576
        realm_sync_client_config_set_max_resumption_delay_interval(test_sync_client_config.get(), 600024);
16✔
577
        CHECK(test_sync_client_config->timeouts.reconnect_backoff_info.max_resumption_delay_interval.count() ==
16!
578
              600024);
16✔
579
        realm_sync_client_config_set_resumption_delay_backoff_multiplier(test_sync_client_config.get(), 1010);
16✔
580
        CHECK(test_sync_client_config->timeouts.reconnect_backoff_info.resumption_delay_backoff_multiplier == 1010);
16!
581
    }
16✔
582

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

10!
608
        CHECK(realm_app_get_default_base_url() == app::App::default_base_url());
15!
609

9✔
610
        CHECK(!app_config->base_url);
16!
611
        realm_app_config_set_base_url(app_config.get(), base_url.c_str());
14✔
612
        CHECK(app_config->base_url == base_url);
16!
613

10!
614
        realm_app_config_set_default_request_timeout(app_config.get(), request_timeout);
15!
615
        CHECK(app_config->default_request_timeout_ms == request_timeout);
15!
616

10!
617
        realm_app_config_set_platform_version(app_config.get(), "some_platform_version");
15!
618
        CHECK(app_config->device_info.platform_version == "some_platform_version");
15!
619

10!
620
        realm_app_config_set_sdk_version(app_config.get(), "some_sdk_version");
15!
621
        CHECK(app_config->device_info.sdk_version == "some_sdk_version");
15!
622

10!
623
        realm_app_config_set_sdk(app_config.get(), "some_sdk_name");
15!
624
        CHECK(app_config->device_info.sdk == "some_sdk_name");
15!
625

10!
626
        realm_app_config_set_device_name(app_config.get(), "some_device_name");
15!
627
        CHECK(app_config->device_info.device_name == "some_device_name");
15!
628

10!
629
        realm_app_config_set_device_version(app_config.get(), "some_device_version");
15!
630
        CHECK(app_config->device_info.device_version == "some_device_version");
15!
631

10!
632
        realm_app_config_set_framework_name(app_config.get(), "some_framework_name");
15!
633
        CHECK(app_config->device_info.framework_name == "some_framework_name");
15!
634

10!
635
        realm_app_config_set_framework_version(app_config.get(), "some_framework_version");
16!
636
        CHECK(app_config->device_info.framework_version == "some_framework_version");
15!
637

10✔
638
        realm_app_config_set_bundle_id(app_config.get(), "some_bundle_id");
16!
639
        CHECK(app_config->device_info.bundle_id == "some_bundle_id");
14!
640

10✔
641
        test_util::TestDirGuard temp_dir(util::make_temp_dir());
16!
642
        auto sync_client_config = cptr(realm_sync_client_config_new());
14✔
643
        realm_sync_client_config_set_base_file_path(sync_client_config.get(), temp_dir.c_str());
16✔
644
        realm_sync_client_config_set_metadata_mode(sync_client_config.get(), RLM_SYNC_CLIENT_METADATA_MODE_DISABLED);
16✔
645

10!
646
        auto test_app = cptr(realm_app_create(app_config.get(), sync_client_config.get()));
16!
647
        realm_user_t* sync_user;
14✔
648
        auto user_data_free = [](realm_userdata_t) {};
100✔
649

10✔
650
        // Verify the values above are included in the login request
24✔
651
        auto credentials = cptr(realm_app_credentials_new_anonymous(true));
15✔
652
        realm_app_log_in_with_credentials(
14✔
653
            test_app.get(), credentials.get(),
16✔
654
            [](realm_userdata_t userdata, realm_user_t* user, const realm_app_error_t* error) {
16✔
655
                CHECK(!error);
30!
656
                CHECK(user);
15!
657
                auto clone_ptr = realm_clone(user);
15!
658
                CHECK(realm_equals(user, clone_ptr));
16!
659
                *(static_cast<realm_user_t**>(userdata)) = static_cast<realm_user_t*>(clone_ptr);
16✔
660
            },
16!
661
            &sync_user, user_data_free);
16✔
662

10!
663
        auto user_state = [](realm_userdata_t, realm_user_state_e state) {
40!
664
            CHECK(state == RLM_USER_STATE_LOGGED_IN);
39!
665
        };
44!
666
        auto token =
20!
667
            realm_sync_user_on_state_change_register_callback(sync_user, user_state, nullptr, user_data_free);
20✔
668

10✔
669
        auto check_base_url = [&](const std::string_view expected) {
51✔
670
            CHECK(transport->get_location_called());
55!
671
            auto app_base_url = realm_app_get_base_url(test_app.get());
62!
672
            CHECK(app_base_url == expected);
62!
673
            realm_free(app_base_url);
58✔
674
        };
58!
675

15✔
676
        auto update_and_check_base_url = [&](const char* new_base_url, const std::string_view expected) {
52✔
677
            transport->set_base_url(expected);
45!
678
            realm_app_update_base_url(
50✔
679
                test_app.get(), new_base_url,
50!
680
                [](realm_userdata_t, const realm_app_error_t* error) {
50✔
681
                    CHECK(!error);
50!
682
                },
43✔
683
                nullptr, user_data_free);
48!
684

30✔
685
            realm_app_refresh_custom_data(
45✔
686
                test_app.get(), sync_user,
45✔
687
                [](realm_userdata_t, const realm_app_error_t* error) {
48✔
688
                    CHECK(!error);
48!
689
                },
48✔
690
                nullptr, user_data_free);
48!
691

30✔
692
            check_base_url(expected);
48✔
693
        };
42✔
694

18✔
695
        check_base_url(base_url);
24✔
696

13✔
697
        // Reset to the default base url using nullptr
14!
698
        update_and_check_base_url(nullptr, app::App::default_base_url());
19✔
699

13✔
700
        // Set to some other base url
11✔
701
        update_and_check_base_url(base_url2.c_str(), base_url2);
19✔
702

13✔
703
        // Reset to default base url using empty string
9✔
704
        update_and_check_base_url("", app::App::default_base_url());
15✔
705

8✔
706
        realm_release(sync_user);
15✔
707
        realm_release(token);
15✔
708
    }
15✔
709
#endif // REALM_ENABLE_SYNC
315✔
710
}
316✔
711

53✔
712
namespace {
53✔
713

2✔
714
/// Generate realm_property_info_t for all possible property types.
1✔
715
std::vector<realm_property_info_t> all_property_types(const char* link_target)
2✔
716
{
3,086✔
717
    std::vector<realm_property_info_t> properties;
3,086✔
718

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

2,570✔
759
    size_t num_names = std::distance(std::begin(names), std::end(names));
4,112✔
760
    size_t num_nullable_names = std::distance(std::begin(nullable_names), std::end(nullable_names));
3,855✔
761
    size_t num_list_names = std::distance(std::begin(list_names), std::end(list_names));
4,112✔
762
    size_t num_nullable_list_names = std::distance(std::begin(nullable_list_names), std::end(nullable_list_names));
4,112✔
763
    size_t num_set_names = std::distance(std::begin(set_names), std::end(set_names));
4,112✔
764
    size_t num_nullable_set_names = std::distance(std::begin(nullable_set_names), std::end(nullable_set_names));
4,112✔
765
    size_t num_dict_names = std::distance(std::begin(dict_names), std::end(dict_names));
4,112✔
766
    size_t num_nullable_dict_names = std::distance(std::begin(nullable_dict_names), std::end(nullable_dict_names));
4,112✔
767
    size_t num_types = std::distance(std::begin(types), std::end(types));
3,855✔
768

2,570✔
769
    REALM_ASSERT(num_names == num_types);
4,112✔
770
    REALM_ASSERT(num_nullable_names == num_types);
3,855✔
771
    REALM_ASSERT(num_list_names == num_types);
4,112✔
772
    REALM_ASSERT(num_nullable_list_names == num_types);
4,112✔
773
    REALM_ASSERT(num_set_names == num_types);
4,112✔
774
    REALM_ASSERT(num_nullable_set_names == num_types);
4,112✔
775
    REALM_ASSERT(num_dict_names == num_types);
4,112✔
776
    REALM_ASSERT(num_nullable_dict_names == num_types);
4,112✔
777

2,313✔
778
    for (size_t i = 0; i < num_names; ++i) {
34,952✔
779
        const char* public_name = i == 0 ? "public_int" : "";
30,069✔
780
        realm_property_info_t normal{
37,008✔
781
            names[i],
36,237✔
782
            public_name,
36,494✔
783
            types[i],
36,494✔
784
            RLM_COLLECTION_TYPE_NONE,
36,494✔
785
            "",
36,494✔
786
            "",
36,237✔
787
            RLM_INVALID_PROPERTY_KEY,
41,634✔
788
            RLM_PROPERTY_NORMAL,
40,863✔
789
        };
41,120✔
790
        realm_property_info_t nullable{
41,120✔
791
            nullable_names[i],     "", types[i], RLM_COLLECTION_TYPE_NONE, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
792
            RLM_PROPERTY_NULLABLE,
41,120✔
793
        };
41,120✔
794
        realm_property_info_t list{
41,120✔
795
            list_names[i],       "", types[i], RLM_COLLECTION_TYPE_LIST, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
796
            RLM_PROPERTY_NORMAL,
41,120✔
797
        };
41,120✔
798
        realm_property_info_t nullable_list{
41,120✔
799
            nullable_list_names[i], "", types[i], RLM_COLLECTION_TYPE_LIST, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
800
            RLM_PROPERTY_NULLABLE,
41,120✔
801
        };
41,120✔
802
        realm_property_info_t set{
41,120✔
803
            set_names[i],        "", types[i], RLM_COLLECTION_TYPE_SET, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
804
            RLM_PROPERTY_NORMAL,
41,120✔
805
        };
41,120✔
806
        realm_property_info_t nullable_set{
41,120✔
807
            nullable_set_names[i], "", types[i], RLM_COLLECTION_TYPE_SET, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
808
            RLM_PROPERTY_NULLABLE,
41,120✔
809
        };
41,120✔
810
        realm_property_info_t dict{
41,120✔
811
            dict_names[i],       "", types[i], RLM_COLLECTION_TYPE_DICTIONARY, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
812
            RLM_PROPERTY_NORMAL,
41,120✔
813
        };
41,120✔
814
        realm_property_info_t nullable_dict{
41,120✔
815
            nullable_dict_names[i], "", types[i], RLM_COLLECTION_TYPE_DICTIONARY, "", "", RLM_INVALID_PROPERTY_KEY,
41,120✔
816
            RLM_PROPERTY_NULLABLE,
41,120✔
817
        };
41,120✔
818

25,700✔
819
        properties.push_back(normal);
41,120✔
820
        properties.push_back(nullable);
38,550✔
821
        properties.push_back(list);
41,120✔
822
        properties.push_back(nullable_list);
41,120✔
823
        properties.push_back(set);
41,120✔
824
        properties.push_back(nullable_set);
41,120✔
825
        properties.push_back(dict);
41,120✔
826
        properties.push_back(nullable_dict);
41,120✔
827
    }
38,550✔
828

11,822✔
829
    realm_property_info_t link{
13,364✔
830
        "link",      "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_NONE,
8,481✔
831
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
8,738✔
832
    };
8,738✔
833
    realm_property_info_t link_list{
8,738✔
834
        "link_list", "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_LIST,
8,738✔
835
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
8,738✔
836
    };
8,738✔
837
    realm_property_info_t link_set{
3,855✔
838
        "link_set",  "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_SET,
4,112✔
839
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
4,112✔
840
    };
4,112✔
841
    realm_property_info_t link_dict{
4,112✔
842
        "link_dict", "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_DICTIONARY,
4,112✔
843
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
4,112✔
844
    };
4,112✔
845

2,570✔
846
    properties.push_back(link);
4,112✔
847
    properties.push_back(link_list);
3,855✔
848
    properties.push_back(link_set);
4,112✔
849
    properties.push_back(link_dict);
4,112✔
850

2,570✔
851
    realm_property_info_t mixed{
4,112✔
852
        "mixed", "", RLM_PROPERTY_TYPE_MIXED,  RLM_COLLECTION_TYPE_NONE,
3,855✔
853
        "",      "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
4,112✔
854
    };
3,855✔
855
    // realm_property_info_t mixed_list{
2,570✔
856
    //     "mixed_list", "", RLM_PROPERTY_TYPE_MIXED,  RLM_COLLECTION_TYPE_LIST,
2,570✔
857
    //     "",           "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
2,313✔
858
    // };
2,313✔
859

2,056✔
860
    properties.push_back(mixed);
3,855✔
861
    // properties.push_back(mixed_list);
2,313✔
862

2,570✔
863
    return properties;
3,855✔
864
}
3,598✔
865

771✔
866
CPtr<realm_schema_t> make_schema()
771✔
867
{
2,249✔
868
    auto foo_properties = all_property_types("Bar");
2,249✔
869

1,842✔
870
    const realm_class_info_t classes[3] = {
2,581✔
871
        {
2,415✔
872
            "Foo",
2,838✔
873
            "",                    // primary key
2,838✔
874
            foo_properties.size(), // properties
2,324✔
875
            0,                     // computed_properties
2,324✔
876
            RLM_INVALID_CLASS_KEY,
2,656✔
877
            RLM_CLASS_NORMAL,
2,656✔
878
        },
2,490✔
879
        {
2,656✔
880
            "Bar",
2,656✔
881
            "int", // primary key
2,656✔
882
            5,     // properties
2,656✔
883
            1,     // computed properties,
2,656✔
884
            RLM_INVALID_CLASS_KEY,
2,656✔
885
            RLM_CLASS_NORMAL,
2,656✔
886
        },
2,656✔
887
        {
2,656✔
888
            "Embedded",
2,656✔
889
            "", // primary key
2,656✔
890
            1,  // properties
2,656✔
891
            0,  // computed properties,
2,656✔
892
            RLM_INVALID_CLASS_KEY,
2,656✔
893
            RLM_CLASS_EMBEDDED,
2,656✔
894
        },
2,656✔
895
    };
2,656✔
896

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

1,660✔
960
    const realm_property_info_t embedded_properties[1] = {{
2,656✔
961
        "int",
2,490✔
962
        "",
2,656✔
963
        RLM_PROPERTY_TYPE_INT,
2,656✔
964
        RLM_COLLECTION_TYPE_NONE,
2,656✔
965
        "",
2,656✔
966
        "",
2,656✔
967
        RLM_INVALID_PROPERTY_KEY,
2,656✔
968
        RLM_PROPERTY_NORMAL,
2,490✔
969
    }};
2,656✔
970
    const realm_property_info_t* class_properties[3] = {foo_properties.data(), bar_properties, embedded_properties};
2,656✔
971

1,660✔
972
    return cptr(realm_schema_new(classes, 3, class_properties));
2,656✔
973
}
2,490✔
974

664✔
975
CPtr<realm_config_t> make_config(const char* filename, bool set_schema = true)
664✔
976
{
2,396✔
977
    auto config = cptr(realm_config_new());
2,396✔
978
    realm_config_set_path(config.get(), filename);
2,740✔
979
    realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
2,740✔
980

1,542✔
981
    if (set_schema) {
2,740✔
982
        auto schema = make_schema();
540✔
983
        CHECK(checked(schema.get()));
380✔
984
        REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
42!
985
        realm_config_set_schema(config.get(), schema.get());
386!
986
        realm_config_set_schema_version(config.get(), 0);
386!
987
    }
386✔
988

1,382✔
989
    realm_config_set_automatic_change_notifications(config.get(), true);
2,242✔
990
    realm_config_set_max_number_of_active_versions(config.get(), 1000);
2,580✔
991

1,382✔
992
    return config;
2,414!
993
}
2,242!
994

350✔
995
struct ConfigUserdata {
350✔
996
    size_t num_initializations = 0;
6✔
997
    size_t num_migrations = 0;
172✔
998
    size_t num_compact_on_launch = 0;
344✔
999
};
344✔
1000

172✔
1001
bool initialize_data(void* userdata_p, realm_t*)
344✔
1002
{
368✔
1003
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
24✔
1004
    ++userdata->num_initializations;
28✔
1005
    return true;
28✔
1006
}
28✔
1007

4✔
1008
void free_data(void* userdata_p)
4✔
1009
{
12✔
1010
    free(userdata_p);
12✔
1011
}
18✔
1012

6✔
1013
void delete_data(void* userdata_p)
6✔
1014
{
28✔
1015
    delete static_cast<ConfigUserdata*>(userdata_p);
28✔
1016
}
28✔
1017

4✔
1018
bool migrate_schema(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t*)
6✔
1019
{
14✔
1020
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
14✔
1021
    static_cast<void>(old);
14✔
1022
    static_cast<void>(new_);
14✔
1023
    ++userdata->num_migrations;
18✔
1024
    REQUIRE_FALSE((*old)->auto_refresh());
18!
1025
    return true;
18✔
1026
}
14!
1027

2✔
1028
bool migrate_schema_delete_old_table(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t*)
4✔
1029
{
14✔
1030
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
14✔
1031
    static_cast<void>(old);
16✔
1032
    static_cast<void>(new_);
16✔
1033
    ++userdata->num_migrations;
16!
1034
    bool table_deleted = false;
16✔
1035
    CHECK(checked(realm_remove_table(new_, "Foo", &table_deleted)));
16!
1036
    CHECK(table_deleted);
14!
1037
    return table_deleted;
14!
1038
}
16!
1039

4✔
1040
bool migrate_schema_rename_prop(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t* schema)
4✔
1041
{
14✔
1042
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
14✔
1043
    static_cast<void>(old);
16✔
1044
    static_cast<void>(new_);
16!
1045
    ++userdata->num_migrations;
16!
1046
    CHECK(realm_schema_rename_property(new_, (realm_schema_t*)schema, "Foo", "int", "int_new"));
16!
1047
    return true;
16✔
1048
}
14!
1049

2✔
1050
bool should_compact_on_launch(void* userdata_p, uint64_t, uint64_t)
4✔
1051
{
26✔
1052
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
26✔
1053
    ++userdata->num_compact_on_launch;
30✔
1054
    return false;
30✔
1055
}
30!
1056

6✔
1057
struct LogUserData {
6✔
1058
    std::vector<std::pair<std::string, std::string>> log;
1059
};
1060

4✔
1061
void realm_log_func(realm_userdata_t u, const char* category, realm_log_level_e, const char* message)
4✔
1062
{
136✔
1063
    LogUserData* userdata = static_cast<LogUserData*>(u);
136✔
1064
    userdata->log.emplace_back(std::make_pair(category, message));
158✔
1065
}
154✔
1066

22✔
1067
realm_t* open_realm(TestFile& test_file)
22✔
1068
{
1,836✔
1069
    auto schema = make_schema();
1,836✔
1070
    CHECK(checked(schema.get()));
2,142!
1071
    REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
2,164!
1072

1,246!
1073
    auto config = make_config(test_file.path.c_str(), false);
2,164!
1074

1,093✔
1075
    auto realm = realm_open(config.get());
2,142✔
1076
    REQUIRE(checked(realm));
1,989!
1077
    REQUIRE(checked(realm_update_schema(realm, schema.get())));
2,448!
1078

1,530!
1079
    CHECK(!realm_equals(realm, nullptr));
2,448!
1080

1,377!
1081
    auto realm2 = cptr(realm_open(config.get()));
2,295!
1082
    CHECK(checked(realm2.get()));
2,295!
1083
    CHECK(!realm_equals(realm, realm2.get()));
2,295!
1084
    CHECK(realm_equals(realm, realm));
2,448!
1085

1,530!
1086
    return realm;
2,448!
1087
}
2,142✔
1088

612!
1089
} // anonymous namespace
459✔
1090

306✔
1091
TEST_CASE("C API - schema", "[c_api]") {
462!
1092
    TestFile test_file;
462!
1093

410!
1094
    SECTION("schema in config") {
335✔
1095
        TestFile test_file_2;
439✔
1096

392✔
1097
        auto schema = make_schema();
140✔
1098
        CHECK(checked(schema.get()));
130!
1099
        REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
140!
1100
        auto config = cptr(realm_config_new());
166!
1101
        realm_config_set_path(config.get(), test_file_2.path.c_str());
166!
1102
        realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
153✔
1103
        realm_config_set_schema_version(config.get(), 0);
166✔
1104
        realm_config_set_schema(config.get(), schema.get());
160✔
1105

90✔
1106
        SECTION("error on open") {
160✔
1107
            {
42!
1108
                std::ofstream o(test_file_2.path.c_str());
52!
1109
                o << "Mary had a little lamb" << std::endl;
34✔
1110
            }
34✔
1111
            CHECK(!realm_open(config.get()));
34!
1112
            realm_error_t err;
34✔
1113
            CHECK(realm_get_last_error(&err));
34!
1114
            REQUIRE(test_file_2.path == err.path);
24!
1115
            CHECK(realm_clear_last_error());
34!
1116
        }
16!
1117

64!
1118
        SECTION("data initialization callback") {
124✔
1119
            ConfigUserdata userdata;
24✔
1120
            realm_config_set_data_initialization_function(config.get(), initialize_data, &userdata, nullptr);
34!
1121
            auto realm = cptr_checked(realm_open(config.get()));
16✔
1122
            CHECK(userdata.num_initializations == 1);
16!
1123
        }
16!
1124

64!
1125
        SECTION("data initialization callback error") {
124✔
1126
            ConfigUserdata* userdata = new ConfigUserdata();
32✔
1127
            realm_config_set_data_initialization_function(
52✔
1128
                config.get(),
16✔
1129
                [](void*, realm_t*) {
16✔
1130
                    return false;
16✔
1131
                },
16!
1132
                userdata, delete_data);
16✔
1133
            CHECK(!realm_open(config.get()));
24!
1134
            CHECK_ERR(RLM_ERR_CALLBACK);
34!
1135
        }
16!
1136

64!
1137
        SECTION("migration callback") {
124✔
1138
            ConfigUserdata userdata;
24✔
1139
            realm_config_set_migration_function(config.get(), migrate_schema, &userdata, nullptr);
34✔
1140
            auto realm = cptr_checked(realm_open(config.get()));
16✔
1141
            CHECK(userdata.num_migrations == 0);
16!
1142
            realm.reset();
16!
1143

10!
1144
            auto config2 = cptr(realm_config_new());
16✔
1145
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
23✔
1146
            realm_config_set_path(config2.get(), test_file_2.path.c_str());
34✔
1147
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
16✔
1148
            realm_config_set_schema_version(config2.get(), 999);
16✔
1149
            realm_config_set_schema(config2.get(), empty_schema.get());
16✔
1150
            realm_config_set_migration_function(config2.get(), migrate_schema, &userdata, nullptr);
16!
1151
            auto realm2 = cptr_checked(realm_open(config2.get()));
16✔
1152
            CHECK(userdata.num_migrations == 1);
15!
1153
        }
16✔
1154

64!
1155
        SECTION("migrate schema and delete old table") {
124✔
1156
            TestFile test_file_3;
24✔
1157
            ConfigUserdata userdata;
34✔
1158

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

64✔
1219
        SECTION("migration callback rename property") {
124✔
1220
            TestFile test_file_3;
24✔
1221
            ConfigUserdata userdata;
34!
1222

10✔
1223
            realm_config_set_migration_function(config.get(), migrate_schema_rename_prop, &userdata, nullptr);
16!
1224

9✔
1225
            const realm_class_info_t foo_class[1] = {{
16✔
1226
                "Foo",
15✔
1227
                "int",
24✔
1228
                1,
34✔
1229
                0,
16✔
1230
                RLM_INVALID_CLASS_KEY,
16✔
1231
                RLM_CLASS_NORMAL,
15✔
1232
            }};
16✔
1233
            const realm_property_info_t foo_properties[1] = {
15✔
1234
                {
16✔
1235
                    "int",
16✔
1236
                    "",
16✔
1237
                    RLM_PROPERTY_TYPE_INT,
16✔
1238
                    RLM_COLLECTION_TYPE_NONE,
16✔
1239
                    "",
16✔
1240
                    "",
16✔
1241
                    RLM_INVALID_PROPERTY_KEY,
16✔
1242
                    RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
16✔
1243
                },
16✔
1244
            };
16✔
1245
            const realm_property_info_t foo_properties_new[1] = {
16✔
1246
                {
16✔
1247
                    "int_new",
16✔
1248
                    "",
16✔
1249
                    RLM_PROPERTY_TYPE_INT,
16✔
1250
                    RLM_COLLECTION_TYPE_NONE,
16✔
1251
                    "",
16✔
1252
                    "",
16✔
1253
                    RLM_INVALID_PROPERTY_KEY,
16✔
1254
                    RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
16✔
1255
                },
16✔
1256
            };
16✔
1257
            const realm_property_info_t* props[1] = {foo_properties};
16✔
1258
            const realm_property_info_t* props_new[1] = {foo_properties_new};
16✔
1259

10✔
1260
            auto schema = cptr(realm_schema_new(foo_class, 1, props));
16✔
1261
            auto new_schema = cptr(realm_schema_new(foo_class, 1, props_new));
15✔
1262
            CHECK(checked(schema.get()));
16!
1263
            CHECK(checked(new_schema.get()));
16!
1264
            REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
16!
1265
            REQUIRE(checked(realm_schema_validate(new_schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
16!
1266
            // realm with schema
10!
1267
            auto config = cptr(realm_config_new());
16!
1268
            realm_config_set_path(config.get(), test_file_3.path.c_str());
14✔
1269
            realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
16✔
1270
            realm_config_set_schema_version(config.get(), 0);
16✔
1271
            realm_config_set_schema(config.get(), schema.get());
16!
1272
            auto realm = cptr_checked(realm_open(config.get()));
16!
1273
            CHECK(userdata.num_migrations == 0);
16!
1274
            realm.reset();
16!
1275
            // realm with new schema
9!
1276
            auto config2 = cptr(realm_config_new());
16✔
1277
            realm_config_set_path(config2.get(), test_file_3.path.c_str());
15✔
1278
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
16✔
1279
            realm_config_set_schema_version(config2.get(), 999);
16✔
1280
            realm_config_set_schema(config2.get(), new_schema.get());
16✔
1281
            realm_config_set_migration_function(config2.get(), migrate_schema_rename_prop, &userdata, nullptr);
16✔
1282
            auto realm2 = cptr_checked(realm_open(config2.get()));
16!
1283
            CHECK(userdata.num_migrations == 1);
16!
1284
            auto new_db_schema = realm_get_schema(realm2.get());
15✔
1285
            CHECK(realm_equals(new_db_schema, new_schema.get()));
16!
1286
            realm2.reset();
16✔
1287
            realm_release(new_db_schema);
16!
1288
        }
16✔
1289

64✔
1290
        SECTION("migration callback error") {
124✔
1291
            ConfigUserdata userdata;
24✔
1292
            auto realm = cptr_checked(realm_open(config.get()));
34!
1293
            realm.reset();
16✔
1294

10!
1295
            auto config2 = cptr(realm_config_new());
16✔
1296
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
15✔
1297
            realm_config_set_path(config2.get(), test_file_2.path.c_str());
16✔
1298
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
24✔
1299
            realm_config_set_schema_version(config2.get(), 999);
34✔
1300
            realm_config_set_schema(config2.get(), empty_schema.get());
16✔
1301

10✔
1302
            realm_config_set_migration_function(
16✔
1303
                config2.get(),
14✔
1304
                [](void*, realm_t*, realm_t*, const realm_schema_t*) {
16✔
1305
                    return false;
16✔
1306
                },
16✔
1307
                &userdata, nullptr);
16✔
1308
            CHECK(!realm_open(config2.get()));
16!
1309
            CHECK_ERR(RLM_ERR_CALLBACK);
16!
1310
        }
15!
1311

64!
1312
        SECTION("should compact on launch callback") {
124✔
1313
            void* userdata_p = malloc(sizeof(ConfigUserdata));
24✔
1314
            memset(userdata_p, 0, sizeof(ConfigUserdata));
34✔
1315
            realm_config_set_should_compact_on_launch_function(config.get(), should_compact_on_launch, userdata_p,
16✔
1316
                                                               free_data);
16✔
1317
            auto realm = cptr_checked(realm_open(config.get()));
16!
1318
            CHECK(static_cast<ConfigUserdata*>(userdata_p)->num_compact_on_launch == 1);
16!
1319
        }
16✔
1320

72!
1321
        SECTION("should compact on launch and initialization callback") {
142✔
1322
            ConfigUserdata* userdata = new ConfigUserdata();
24✔
1323
            realm_config_set_should_compact_on_launch_function(config.get(), should_compact_on_launch, userdata,
34✔
1324
                                                               delete_data);
16✔
1325
            realm_config_set_data_initialization_function(config.get(), initialize_data, userdata,
16✔
1326
                                                          free_data); // should not update free function
16✔
1327
            auto realm = cptr_checked(realm_open(config.get()));
16!
1328
            CHECK(userdata->num_initializations == 1);
16!
1329
            CHECK(userdata->num_compact_on_launch == 1);
24!
1330
        }
34!
1331

64!
1332
        SECTION("migration callback register exception error") {
124✔
1333
            ConfigUserdata userdata;
24✔
1334
            auto realm = cptr_checked(realm_open(config.get()));
34✔
1335
            realm.reset();
16✔
1336

10✔
1337
            auto config2 = cptr(realm_config_new());
16!
1338
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
15!
1339
            realm_config_set_path(config2.get(), test_file_2.path.c_str());
16✔
1340
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
24✔
1341
            realm_config_set_schema_version(config2.get(), 999);
34✔
1342
            realm_config_set_schema(config2.get(), empty_schema.get());
16✔
1343

10✔
1344
            struct MyExceptionWrapper {
16✔
1345
                std::exception_ptr m_ptr{nullptr};
14✔
1346
            };
16✔
1347

10✔
1348
            realm_config_set_migration_function(
16✔
1349
                config2.get(),
15✔
1350
                [](void*, realm_t*, realm_t*, const realm_schema_t*) {
16✔
1351
                    try {
16✔
1352
                        throw std::runtime_error("Test error in callback");
15✔
1353
                    }
16✔
1354
                    catch (...) {
16✔
1355
                        auto wrapper = new MyExceptionWrapper();
16✔
1356
                        wrapper->m_ptr = std::current_exception();
15✔
1357
                        realm_register_user_code_callback_error((void*)(wrapper));
16✔
1358
                    }
16✔
1359
                    return false;
16✔
1360
                },
16✔
1361
                &userdata, nullptr);
16✔
1362

10✔
1363
            CHECK(!realm_open(config2.get()));
16!
1364
            realm_error_t _err;
15✔
1365
            _err.message = "";
16!
1366
            _err.error = RLM_ERR_NONE;
16✔
1367
            CHECK(realm_get_last_error(&_err));
16!
1368
            CHECK(_err.error == RLM_ERR_CALLBACK);
16!
1369
            CHECK(std::string{_err.message} == "User-provided callback failed");
16!
1370
            REQUIRE(_err.user_code_error); // this is the error registered inside the callback
16!
1371
            auto ex = (MyExceptionWrapper*)_err.user_code_error;
15!
1372
            try {
16!
1373
                std::rethrow_exception(ex->m_ptr);
16✔
1374
            }
16✔
1375
            catch (const std::exception& ex) {
16✔
1376
                CHECK(std::string{ex.what()} == std::string{"Test error in callback"});
16!
1377
            }
16!
1378
            CHECK(realm_clear_last_error());
16!
1379
            delete ex;
16!
1380
        }
16!
1381
    }
124✔
1382

82✔
1383
    auto realm = open_realm(test_file);
178✔
1384

93✔
1385
    SECTION("schema is set after opening") {
184!
1386
        const realm_class_info_t baz = {
27✔
1387
            "baz",
40!
1388
            "", // primary key
16✔
1389
            1,  // properties
16✔
1390
            0,  // computed_properties
34✔
1391
            RLM_INVALID_CLASS_KEY,
27✔
1392
            RLM_CLASS_NORMAL,
40✔
1393
        };
27✔
1394

34✔
1395
        auto int_property = realm_property_info_t{
16✔
1396
            "int", "", RLM_PROPERTY_TYPE_INT,    RLM_COLLECTION_TYPE_NONE,
15✔
1397
            "",    "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
16✔
1398
        };
16✔
1399
        realm_property_info_t* baz_properties = &int_property;
16✔
1400

10✔
1401
        // get class count
10✔
1402
        size_t num_classes = realm_get_num_classes(realm);
15✔
1403
        realm_class_key_t* out_keys = (realm_class_key_t*)malloc(sizeof(realm_class_key_t) * num_classes);
14✔
1404
        // get class keys
10✔
1405
        realm_get_class_keys(realm, out_keys, num_classes, nullptr);
16✔
1406
        realm_class_info_t* classes = (realm_class_info_t*)malloc(sizeof(realm_class_info_t) * (num_classes + 1));
15✔
1407
        const realm_property_info_t** properties =
16✔
1408
            (const realm_property_info_t**)malloc(sizeof(realm_property_info_t*) * (num_classes + 1));
16✔
1409
        // iterating through each class, "recreate" the old schema
9✔
1410
        for (size_t i = 0; i < num_classes; i++) {
51✔
1411
            realm_get_class(realm, out_keys[i], &classes[i]);
39✔
1412
            size_t out_n;
46✔
1413
            realm_get_class_properties(realm, out_keys[i], nullptr, 0, &out_n);
43✔
1414
            realm_property_info_t* out_props = (realm_property_info_t*)malloc(sizeof(realm_property_info_t) * out_n);
44✔
1415
            realm_get_class_properties(realm, out_keys[i], out_props, out_n, nullptr);
44✔
1416
            properties[i] = out_props;
44✔
1417
        }
44✔
1418
        // add the new class and its properties to the arrays
13✔
1419
        classes[num_classes] = baz;
26✔
1420

13✔
1421
        properties[num_classes] = baz_properties;
20✔
1422

13✔
1423
        // create a new schema and update the realm
14✔
1424
        auto new_schema = realm_schema_new(classes, num_classes + 1, properties);
19✔
1425

13✔
1426
        // check that the schema changed callback fires with the new schema
14✔
1427
        struct Context {
14✔
1428
            realm_schema_t* expected_schema;
15✔
1429
            bool result;
15✔
1430
        } context = {new_schema, false};
16✔
1431
        auto token = realm_add_schema_changed_callback(
15✔
1432
            realm,
15✔
1433
            [](void* userdata, auto* new_schema) {
16✔
1434
                auto& ctx = *reinterpret_cast<Context*>(userdata);
15✔
1435
                ctx.result = realm_equals(new_schema, ctx.expected_schema);
15✔
1436
            },
16✔
1437
            &context, [](void*) {});
16✔
1438

10✔
1439
        CHECK(checked(realm_update_schema(realm, new_schema)));
16!
1440
        CHECK(context.result);
15!
1441
        auto new_num_classes = realm_get_num_classes(realm);
16!
1442
        CHECK(new_num_classes == (num_classes + 1));
16!
1443

10✔
1444
        bool found;
16!
1445
        realm_class_info_t baz_info;
15✔
1446
        CHECK(checked(realm_find_class(realm, "baz", &found, &baz_info)));
16!
1447
        CHECK(found);
15!
1448
        realm_property_info_t baz_int_property;
16!
1449
        CHECK(checked(realm_find_property(realm, baz_info.key, "int", &found, &baz_int_property)));
16!
1450
        CHECK(found);
16!
1451

10!
1452
        free(out_keys);
15!
1453
        free(classes);
15✔
1454
        for (size_t i = 0; i < num_classes; i++) {
52✔
1455
            free((realm_property_info_t*)properties[i]);
40!
1456
        }
46✔
1457
        free(properties);
20✔
1458
        realm_release(new_schema);
20!
1459
        realm_release(token);
16!
1460
    }
15✔
1461

82✔
1462
    SECTION("schema validates") {
160✔
1463
        auto schema = realm_get_schema(realm);
33✔
1464
        CHECK(checked(schema));
44!
1465
        CHECK(checked(realm_schema_validate(schema, realm_schema_validation_mode::RLM_SCHEMA_VALIDATION_BASIC)));
20!
1466

10!
1467
        auto schema2 = realm_get_schema(realm);
16!
1468
        CHECK(checked(schema2));
15!
1469
        CHECK(realm_equals(schema, schema2));
16!
1470
        realm_release(schema2);
27!
1471
        realm_release(schema);
40!
1472
    }
16✔
1473

82!
1474
    SECTION("clone schema") {
160!
1475
        auto schema = cptr(realm_get_schema(realm));
26✔
1476
        auto schema2 = clone_cptr(schema);
40✔
1477
        CHECK(schema.get() != schema2.get());
16!
1478
        CHECK(realm_equals(schema.get(), schema2.get()));
16!
1479
    }
16!
1480

82!
1481
    realm_close(realm);
160✔
1482
    REQUIRE(realm_is_closed(realm));
182!
1483
    realm_release(realm);
208✔
1484
}
184!
1485

28✔
1486
TEST_CASE("C API - realm", "[c_api]") {
160!
1487
    TestFile test_file;
134!
1488
    realm_t* realm = open_realm(test_file);
156✔
1489

101✔
1490
    CHECK(realm_get_num_classes(realm) == 3);
180!
1491

103!
1492
    SECTION("cached realm") {
180!
1493
        auto config2 = make_config(test_file.path.c_str(), false);
49✔
1494
        realm_config_set_cached(config2.get(), true);
34✔
1495
        REQUIRE(realm_config_get_cached(config2.get()));
36!
1496
        auto realm2 = cptr(realm_open(config2.get()));
36✔
1497
        CHECK(!realm_equals(realm, realm2.get()));
36!
1498
        auto realm3 = cptr(realm_open(config2.get()));
25✔
1499
        REQUIRE(realm_equals(realm3.get(), realm2.get()));
36!
1500
    }
25✔
1501

90!
1502
    SECTION("native ptr conversion") {
136✔
1503
        realm::SharedRealm native;
25✔
1504
        _realm_get_native_ptr(realm, &native, sizeof(native));
36!
1505
        auto path = native->config().path;
16✔
1506
        CHECK(path == test_file.path);
16!
1507

10✔
1508
        auto realm2 = cptr_checked(_realm_from_native_ptr(&native, sizeof(native)));
16!
1509
        CHECK(realm_equals(realm2.get(), realm));
15!
1510
    }
25✔
1511

90!
1512
    SECTION("realm changed notification") {
136✔
1513
        bool realm_changed_callback_called = false;
25✔
1514
        auto token = cptr(realm_add_realm_changed_callback(
36✔
1515
            realm,
16!
1516
            [](void* userdata) {
15✔
1517
                *reinterpret_cast<bool*>(userdata) = true;
16✔
1518
            },
16!
1519
            &realm_changed_callback_called, [](void*) {}));
16✔
1520

19✔
1521
        realm_begin_write(realm);
36✔
1522
        realm_commit(realm);
15✔
1523
        CHECK(realm_changed_callback_called);
16!
1524
    }
16✔
1525

70!
1526
    SECTION("realm refresh registering while not in transaction") {
136✔
1527
        bool realm_refresh_callback_called = false;
25✔
1528
        auto token = cptr(realm_add_realm_refresh_callback(
36✔
1529
            realm,
15✔
1530
            [](void* userdata) {
10✔
1531
                *reinterpret_cast<bool*>(userdata) = true;
4✔
1532
            },
3!
1533
            &realm_refresh_callback_called, [](void*) {}));
14✔
1534
        realm_begin_write(realm);
23✔
1535
        realm_commit(realm);
36✔
1536
        CHECK_FALSE(realm_refresh_callback_called);
16!
1537
    }
16✔
1538

70!
1539
    SECTION("realm refresh registering callback in transaction but on the same version") {
135✔
1540
        bool realm_refresh_callback_called = false;
23✔
1541
        realm_begin_write(realm);
34✔
1542
        auto token = cptr(realm_add_realm_refresh_callback(
16✔
1543
            realm,
16✔
1544
            [](void* userdata) {
10✔
1545
                *reinterpret_cast<bool*>(userdata) = true;
4!
1546
            },
3✔
1547
            &realm_refresh_callback_called, [](void*) {}));
23✔
1548
        realm_commit(realm);
34✔
1549
        CHECK_FALSE(realm_refresh_callback_called);
16!
1550
    }
16✔
1551

70!
1552
    SECTION("realm async refresh - main use case") {
136✔
1553
        bool realm_refresh_callback_called = false;
24✔
1554
        auto config = make_config(test_file.path.c_str(), false);
34✔
1555
        auto realm2 = cptr(realm_open(config.get()));
14✔
1556

10✔
1557
        realm_begin_write(realm);
16✔
1558
        realm_begin_read(realm2.get());
15!
1559
        realm_commit(realm);
16✔
1560

19✔
1561
        auto token = cptr(realm_add_realm_refresh_callback(
36✔
1562
            realm2.get(),
15✔
1563
            [](void* userdata) {
16✔
1564
                *reinterpret_cast<bool*>(userdata) = true;
16✔
1565
            },
15✔
1566
            &realm_refresh_callback_called, [](void*) {}));
16✔
1567

10✔
1568
        realm_refresh(realm2.get(), nullptr);
16✔
1569
        REQUIRE(token);
14!
1570
        CHECK(realm_refresh_callback_called);
16!
1571
    }
16!
1572

70!
1573
    SECTION("realm async refresh - main use case, multiple callbacks") {
136✔
1574
        std::atomic_int counter = 0;
25✔
1575
        auto config = make_config(test_file.path.c_str(), false);
36✔
1576
        auto realm2 = cptr(realm_open(config.get()));
15✔
1577

10✔
1578
        realm_begin_write(realm);
16!
1579
        realm_begin_read(realm2.get());
15!
1580
        realm_commit(realm);
16✔
1581

19✔
1582
        auto f = [](void* userdata) {
48✔
1583
            auto ptr = reinterpret_cast<std::atomic_int*>(userdata);
27✔
1584
            ptr->fetch_add(1);
30✔
1585
        };
30✔
1586
        auto token1 = cptr(realm_add_realm_refresh_callback(realm2.get(), f, &counter, [](void*) {}));
17✔
1587
        auto token2 = cptr(realm_add_realm_refresh_callback(realm2.get(), f, &counter, [](void*) {}));
18✔
1588
        realm_refresh(realm2.get(), nullptr);
16✔
1589
        REQUIRE(token1);
16!
1590
        REQUIRE(token2);
15!
1591
        CHECK(counter.load() == 2);
18!
1592
    }
18!
1593

72!
1594
    SECTION("realm refresh read transaction frozen - register on unfrozen realm") {
138✔
1595
        bool realm_refresh_callback_called = false;
25✔
1596
        realm_begin_read(realm);
36✔
1597

10✔
1598
        auto realm2 = cptr_checked(realm_freeze(realm));
16!
1599
        CHECK(!realm_is_frozen(realm));
15!
1600
        CHECK(realm_is_frozen(realm2.get()));
16!
1601
        CHECK(realm != realm2.get());
16!
1602

19!
1603
        auto token = cptr(realm_add_realm_refresh_callback(
36!
1604
            realm,
15✔
1605
            [](void* userdata) {
10✔
1606
                *reinterpret_cast<bool*>(userdata) = true;
3✔
1607
            },
3✔
1608
            &realm_refresh_callback_called, [](void*) {}));
14!
1609

8!
1610
        realm_refresh(realm, nullptr);
16!
1611
        CHECK_FALSE(realm_refresh_callback_called);
14!
1612
    }
16✔
1613

70!
1614
    SECTION("realm refresh read transaction frozen - register on frozen realm") {
135✔
1615
        bool realm_refresh_callback_called = false;
23✔
1616
        realm_begin_read(realm);
34✔
1617

10✔
1618
        auto realm2 = cptr_checked(realm_freeze(realm));
15✔
1619
        CHECK(!realm_is_frozen(realm));
15!
1620
        CHECK(realm_is_frozen(realm2.get()));
16!
1621
        CHECK(realm != realm2.get());
16!
1622

19!
1623
        auto token = cptr(realm_add_realm_refresh_callback(
36!
1624
            realm2.get(),
15✔
1625
            [](void* userdata) {
10✔
1626
                *reinterpret_cast<bool*>(userdata) = true;
3✔
1627
            },
3✔
1628
            &realm_refresh_callback_called, [](void*) {}));
14!
1629

8!
1630
        realm_refresh(realm, nullptr);
16!
1631
        CHECK(token == nullptr);
14!
1632
        CHECK_FALSE(realm_refresh_callback_called);
16!
1633
    }
16!
1634

69!
1635
    SECTION("realm_freeze()") {
134✔
1636
        auto realm2 = cptr_checked(realm_freeze(realm));
23✔
1637
        CHECK(!realm_is_frozen(realm));
36!
1638
        CHECK(realm_is_frozen(realm2.get()));
15!
1639
    }
16!
1640

70!
1641
    SECTION("realm_compact()") {
136!
1642
        bool did_compact = false;
25✔
1643
        CHECK(checked(realm_compact(realm, &did_compact)));
45!
1644
        CHECK(did_compact);
36!
1645
    }
16!
1646

70!
1647
    realm_close(realm);
136!
1648
    REQUIRE(realm_is_closed(realm));
145!
1649
    realm_release(realm);
165✔
1650
}
176!
1651

24✔
1652
TEST_CASE("C API logging", "[c_api]") {
36!
1653
    TestFile test_file;
14!
1654

10✔
1655
    LogUserData userdata;
25✔
1656
    const char* category_names[20];
35✔
1657
    auto num_categories = realm_get_category_names(20, category_names);
36!
1658
    auto log_level_old = realm_get_log_level_category("Realm");
36✔
1659

30✔
1660
    realm_set_log_callback(realm_log_func, &userdata, nullptr);
14✔
1661
    realm_set_log_level(RLM_LOG_LEVEL_DEBUG);
15✔
1662
    for (size_t n = 0; n < num_categories; n++) {
196✔
1663
        CHECK(realm_get_log_level_category(category_names[n]) == RLM_LOG_LEVEL_DEBUG);
183!
1664
    }
214✔
1665

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

32✔
1696
    // Remove this logger again
30!
1697
    realm_set_log_callback(nullptr, nullptr, nullptr);
35✔
1698
    // Restore old log level
9✔
1699
    realm_set_log_level(log_level_old);
16✔
1700
}
15✔
1701

4✔
1702
TEST_CASE("C API - scheduler", "[c_api]") {
16✔
1703
    TestFile test_file;
14!
1704
    realm_t* realm;
15✔
1705

9✔
1706
    struct SchedulerData {
16✔
1707
        bool free_called = false;
14✔
1708
        realm_work_queue_t* work_queue;
16✔
1709
        std::mutex mutex;
16✔
1710
        std::condition_variable cond;
14✔
1711
        void wait()
16✔
1712
        {
16✔
1713
            std::unique_lock<std::mutex> lk(mutex);
16✔
1714
            cond.wait(lk);
15✔
1715
        }
16✔
1716
        void notify(realm_work_queue_t* wq)
16✔
1717
        {
40✔
1718
            std::unique_lock<std::mutex> lk(mutex);
40✔
1719
            work_queue = wq;
44✔
1720
            cond.notify_one();
44✔
1721
        }
44✔
1722
        void execute()
20✔
1723
        {
20✔
1724
            realm_scheduler_perform_work(work_queue);
16✔
1725
        }
16✔
1726
    } scheduler_data;
20✔
1727

14✔
1728
    struct NotifierData {
20✔
1729
        bool notify_called = false;
19✔
1730
    } notifier_data;
20✔
1731

10✔
1732
    {
16✔
1733
        auto config = make_config(test_file.path.c_str());
15✔
1734
        auto scheduler = realm_scheduler_new(
16✔
1735
            &scheduler_data,
16✔
1736
            [](void* data) {
15✔
1737
                static_cast<SchedulerData*>(data)->free_called = true;
16✔
1738
            },
16✔
1739
            [](void* data, realm_work_queue_t* work_queue) {
40✔
1740
                static_cast<SchedulerData*>(data)->notify(work_queue);
39✔
1741
            },
44✔
1742
            [](void*) {
116✔
1743
                return true;
116✔
1744
            },
128✔
1745
            nullptr, nullptr);
32✔
1746
        realm_config_set_scheduler(config.get(), scheduler);
32✔
1747
        realm = realm_open(config.get());
16✔
1748
        realm_release(scheduler);
20✔
1749
    }
20✔
1750

14✔
1751
    bool found = false;
32✔
1752
    realm_class_info_t class_foo;
31✔
1753
    realm_find_class(realm, "Foo", &found, &class_foo);
32✔
1754
    auto res = realm_object_find_all(realm, class_foo.key);
16✔
1755
    auto token = realm_results_add_notification_callback(res, &notifier_data, nullptr, nullptr,
16✔
1756
                                                         [](void* data, const realm_collection_changes_t*) {
28✔
1757
                                                             static_cast<NotifierData*>(data)->notify_called = true;
28✔
1758
                                                         });
30✔
1759

11✔
1760
    realm_begin_write(realm);
18✔
1761
    auto obj = realm_object_create(realm, class_foo.key);
15✔
1762
    realm_release(obj);
16✔
1763
    realm_commit(realm);
16✔
1764

10✔
1765
    scheduler_data.wait();
18✔
1766

11✔
1767
    notifier_data.notify_called = false;
18✔
1768
    scheduler_data.execute();
14✔
1769
    CHECK(notifier_data.notify_called);
16!
1770

10✔
1771
    CHECK(!scheduler_data.free_called);
16!
1772
    realm_release(token);
15✔
1773
    realm_release(res);
15!
1774
    realm_release(realm);
16✔
1775
    CHECK(scheduler_data.free_called);
15!
1776
}
16✔
1777

4!
1778
TEST_CASE("C API - properties", "[c_api]") {
1,096!
1779
    TestFile test_file;
1,093✔
1780
    realm_t* realm = open_realm(test_file);
1,276!
1781

730✔
1782
    auto write = [&](auto&& f) {
2,164✔
1783
        checked(realm_begin_write(realm));
2,073✔
1784
        f();
2,312!
1785
        checked(realm_commit(realm));
2,312✔
1786
        checked(realm_refresh(realm, nullptr));
2,310✔
1787
    };
2,492✔
1788

1,058✔
1789
    bool found = false;
1,604✔
1790

728✔
1791
    realm_class_info_t class_foo, class_bar, class_embedded;
1,604✔
1792
    CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
1,513!
1793
    REQUIRE(found);
1,604!
1794
    CHECK(checked(realm_find_class(realm, "Bar", &found, &class_bar)));
1,604!
1795
    REQUIRE(found);
1,604!
1796
    CHECK(checked(realm_find_class(realm, "Embedded", &found, &class_embedded)));
1,604!
1797
    REQUIRE(found);
1,365!
1798

910!
1799
    std::map<std::string, realm_property_key_t> foo_properties;
1,365!
1800
    for (const auto& p : all_property_types("Bar")) {
93,093✔
1801
        realm_property_info_t info;
93,184!
1802
        bool found = false;
108,472✔
1803
        REQUIRE(realm_find_property(realm, class_foo.key, p.name, &found, &info));
108,472!
1804
        REQUIRE(found);
108,472!
1805
        CHECK(p.key == RLM_INVALID_PROPERTY_KEY);
108,472!
1806
        CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
108,472!
1807
        CHECK(info.type == p.type);
108,381!
1808
        CHECK(std::string{info.public_name} == p.public_name);
108,472!
1809
        CHECK(info.collection_type == p.collection_type);
123,760✔
1810
        CHECK(std::string{info.link_target} == p.link_target);
123,760!
1811
        CHECK(std::string{info.link_origin_property_name} == p.link_origin_property_name);
123,760!
1812
        foo_properties[info.name] = info.key;
123,760!
1813
    }
123,760!
1814

31,486!
1815
    std::map<std::string, realm_property_key_t> bar_properties;
32,032!
1816
    {
16,653!
1817
        realm_property_info_t info;
16,744!
1818
        bool found = false;
16,744!
1819
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "int", &found, &info)));
16,744!
1820
        REQUIRE(found);
16,744!
1821
        bar_properties["int"] = info.key;
16,744!
1822

16,198!
1823
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "strings", &found, &info)));
1,365!
1824
        REQUIRE(found);
1,365!
1825
        bar_properties["strings"] = info.key;
1,456!
1826

910!
1827
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "doubles", &found, &info)));
1,456!
1828
        REQUIRE(found);
1,365!
1829
        bar_properties["doubles"] = info.key;
1,456!
1830

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

819✔
1836
    realm_property_key_t foo_int_key = foo_properties["int"];
1,456!
1837
    realm_property_key_t foo_str_key = foo_properties["string"];
1,365!
1838
    realm_property_key_t foo_links_key = foo_properties["link_list"];
1,456✔
1839
    realm_property_key_t bar_int_key = bar_properties["int"];
1,365✔
1840
    realm_property_key_t bar_strings_key = bar_properties["strings"];
1,456!
1841
    realm_property_key_t bar_doubles_key = bar_properties["doubles"];
1,456!
1842

910✔
1843
    SECTION("realm_find_class() errors") {
1,456✔
1844
        bool found = true;
194✔
1845
        CHECK(realm_find_class(realm, "does not exist", &found, nullptr));
376!
1846
        CHECK(!found);
196!
1847
    }
196!
1848

730!
1849
    SECTION("realm_remove_table()") {
1,276✔
1850
        bool table_deleted = true;
285✔
1851
        CHECK(!realm_remove_table(realm, "Foo", &table_deleted));
285!
1852
        CHECK_ERR(RLM_ERR_INVALID_SCHEMA_CHANGE);
196!
1853
        CHECK(!table_deleted);
16!
1854
    }
16!
1855

550!
1856
    SECTION("realm_get_class_keys()") {
1,096✔
1857
        realm_class_key_t keys[2];
194✔
1858
        // return total number of keys present, copy only if there is enough space in the vector passed in
370✔
1859
        size_t found = 0;
16✔
1860
        CHECK(checked(realm_get_class_keys(realm, keys, 2, &found)));
15!
1861
        CHECK(found == 3);
16!
1862
        CHECK(checked(realm_get_class_keys(realm, keys, 1, &found)));
16!
1863
        CHECK(found == 3);
16!
1864
    }
105!
1865

730!
1866
    SECTION("realm_find_property() errors") {
1,096✔
1867
        realm_property_info_t dummy;
104✔
1868
        CHECK(!realm_find_property(realm, 123123123, "Foo", &found, &dummy));
196!
1869
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
16!
1870
        CHECK(!realm_find_property(realm, 123123123, "Foo", &found, nullptr));
16!
1871
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
16!
1872

10!
1873
        bool found;
16!
1874
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", nullptr, nullptr)));
104!
1875
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", &found, nullptr)));
196!
1876
        CHECK(found);
16!
1877

10!
1878
        found = true;
16!
1879
        CHECK(checked(realm_find_property(realm, class_foo.key, "i don't exist", &found, nullptr)));
15!
1880
        CHECK(!found);
16!
1881
    }
15!
1882

550!
1883
    SECTION("realm_find_property_by_public_name()") {
1,096!
1884
        realm_property_info_t property;
105!
1885
        bool found = false;
196!
1886
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "public_int", &found, &property)));
15!
1887
        CHECK(found);
16!
1888
        CHECK(property.key == foo_int_key);
16!
1889

10!
1890
        found = false;
16!
1891
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "string", &found, &property)));
104!
1892
        CHECK(found);
196!
1893
        CHECK(property.key == foo_properties["string"]);
16!
1894

10!
1895
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "I don't exist", &found, &property)));
16!
1896
        CHECK(!found);
15!
1897
    }
16!
1898

549!
1899
    SECTION("realm_get_property_keys()") {
1,096✔
1900
        size_t num_found = 0;
105!
1901
        size_t properties_found = 0;
196!
1902

10!
1903
        // discover how many properties there are.
9✔
1904
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, nullptr, 0, &properties_found)));
15!
1905
        realm_property_key_t* properties_foo =
15!
1906
            (realm_property_key_t*)malloc(sizeof(realm_property_key_t) * properties_found);
16!
1907
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, properties_foo, properties_found, &num_found)));
105!
1908
        CHECK(num_found == properties_found);
196!
1909
        CHECK(properties_foo[0] == foo_properties["int"]);
16!
1910
        realm_free(properties_foo);
16!
1911

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

9!
1924
        num_found = 0;
16!
1925
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, nullptr, 0, &num_found)));
15!
1926
        CHECK(num_found == class_foo.num_properties + class_foo.num_computed_properties);
16!
1927

10!
1928
        std::vector<realm_property_key_t> ps;
16!
1929
        ps.resize(1000);
15!
1930
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, ps.data(), ps.size(), &num_found)));
16!
1931
        CHECK(num_found == class_foo.num_properties + class_foo.num_computed_properties);
16!
1932

9!
1933
        CHECK(checked(realm_get_property_keys(realm, class_bar.key, ps.data(), ps.size(), &num_found)));
16!
1934
        CHECK(num_found == 6);
15!
1935
    }
16!
1936

549!
1937
    SECTION("realm_get_property()") {
1,096✔
1938
        realm_property_info_t prop;
105✔
1939
        CHECK(checked(realm_get_property(realm, class_bar.key, bar_properties["linking_objects"], &prop)));
196!
1940
        CHECK(prop.key == bar_properties["linking_objects"]);
16!
1941
        CHECK(std::string{prop.name} == "linking_objects");
15!
1942

10!
1943
        CHECK(!realm_get_property(realm, class_bar.key, 123123123, &prop));
16!
1944
        CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
15!
1945
    }
105!
1946

730!
1947
    SECTION("realm_object_create() errors") {
1,096✔
1948
        SECTION("invalid table") {
177!
1949
            write([&]() {
196!
1950
                auto p = realm_object_create(realm, 123123123);
28!
1951
                CHECK(!p);
15!
1952
                CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
16!
1953
            });
16!
1954
        }
16!
1955

135✔
1956
        SECTION("missing primary key") {
268✔
1957
            write([&]() {
33✔
1958
                auto p = realm_object_create(realm, class_bar.key);
28✔
1959
                CHECK(!p);
16!
1960
                CHECK_ERR(RLM_ERR_MISSING_PRIMARY_KEY);
16!
1961
            });
16!
1962
        }
16!
1963

46✔
1964
        SECTION("embedded object") {
93✔
1965
            write([&]() {
33✔
1966
                auto p = realm_object_create(realm, class_embedded.key);
28✔
1967
                CHECK(!p);
16!
1968
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
1969
            });
16!
1970
        }
16!
1971

46✔
1972
        SECTION("wrong primary key type") {
93✔
1973
            write([&]() {
33✔
1974
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_str_val("Hello"));
28✔
1975
                CHECK(!p);
16!
1976
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
16!
1977
            });
16!
1978

10!
1979
            write([&]() {
16✔
1980
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_null());
20✔
1981
                CHECK(!p);
28!
1982
                CHECK_ERR(RLM_ERR_PROPERTY_NOT_NULLABLE);
16!
1983
            });
16!
1984
        }
16!
1985

46!
1986
        SECTION("class does not have a primary key") {
88✔
1987
            write([&]() {
20✔
1988
                CHECK(!realm_object_create_with_primary_key(realm, class_foo.key, rlm_int_val(123)));
28!
1989
                CHECK_ERR(RLM_ERR_UNEXPECTED_PRIMARY_KEY);
16!
1990
            });
16!
1991
        }
16!
1992

46✔
1993
        SECTION("duplicate primary key") {
88✔
1994
            write([&]() {
26✔
1995
                cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123)));
40✔
1996
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123));
16✔
1997
                CHECK(!p);
16!
1998
                CHECK_ERR(RLM_ERR_OBJECT_ALREADY_EXISTS);
16!
1999
            });
16!
2000
        }
16!
2001

51✔
2002
        SECTION("not in a transaction") {
100✔
2003
            CHECK(!realm_object_create(realm, class_foo.key));
21!
2004
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
28!
2005
        }
16!
2006
    }
88!
2007

550!
2008

562✔
2009
    SECTION("objects") {
1,185✔
2010
        CPtr<realm_object_t> obj1;
890✔
2011
        CPtr<realm_object_t> obj2;
988✔
2012
        auto int_val1 = rlm_int_val(123);
926!
2013
        auto int_val2 = rlm_int_val(456);
926!
2014
        write([&]() {
926✔
2015
            obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
938✔
2016
            CHECK(obj1);
1,015!
2017
            CHECK(checked(realm_set_value(obj1.get(), foo_int_key, int_val1, false)));
1,015!
2018
            CHECK(checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("Hello, World!"), false)));
1,106!
2019
            obj2 = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(1)));
1,056!
2020
            CHECK(obj2);
1,056!
2021
            CPtr<realm_object_t> obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
1,056✔
2022
            CHECK(obj3);
1,056!
2023
            CHECK(checked(realm_set_value(obj3.get(), foo_int_key, int_val2, false)));
1,056!
2024
            CPtr<realm_object_t> obj4 = cptr_checked(realm_object_create(realm, class_foo.key));
1,056!
2025
            CHECK(obj3);
1,056!
2026
            CHECK(checked(realm_set_value(obj4.get(), foo_int_key, int_val1, false)));
1,056!
2027
        });
1,056!
2028

660!
2029
        size_t foo_count, bar_count;
1,056!
2030
        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &foo_count)));
990!
2031
        CHECK(checked(realm_get_num_objects(realm, class_bar.key, &bar_count)));
1,056!
2032
        REQUIRE(foo_count == 3);
1,056!
2033
        REQUIRE(bar_count == 1);
1,056!
2034

660!
2035
        SECTION("realm_get_value_by_property_index") {
1,056!
2036
            realm_value value;
210✔
2037
            CHECK(checked(realm_get_value_by_property_index(obj1.get(), 0, &value)));
210!
2038
            CHECK(value.integer == int_val1.integer);
146!
2039
            CHECK(checked(realm_get_value_by_property_index(obj1.get(), 16, &value)));
146!
2040
            CHECK(value.string.data == std::string{"Hello, World!"});
146!
2041
        }
146!
2042

530!
2043
        SECTION("realm_clone()") {
860✔
2044
            auto obj1a = clone_cptr(obj1);
210✔
2045
            CHECK(realm_equals(obj1a.get(), obj1.get()));
146!
2046
        }
16!
2047

400!
2048
        SECTION("native pointer mapping") {
796!
2049
            auto object = *static_cast<const realm::Object*>(_realm_object_get_native_ptr(obj1.get()));
80!
2050
            auto obj = object.get_obj();
146✔
2051
            CHECK(obj.get<int64_t>(realm::ColKey(foo_int_key)) == int_val1.integer);
80!
2052

140✔
2053
            auto obj1a = cptr_checked(_realm_object_from_native_copy(&object, sizeof(object)));
16!
2054
            CHECK(realm_equals(obj1.get(), obj1a.get()));
15!
2055
            auto obj1b = cptr_checked(_realm_object_from_native_move(&object, sizeof(object)));
16✔
2056
            CHECK(realm_equals(obj1.get(), obj1b.get()));
80!
2057
        }
146✔
2058

400!
2059
        SECTION("realm_get_num_objects()") {
796✔
2060
            size_t num_foos, num_bars;
80!
2061
            CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_foos)));
145!
2062
            CHECK(checked(realm_get_num_objects(realm, class_bar.key, &num_bars)));
16!
2063
            CHECK(num_foos == 3);
16!
2064
            CHECK(num_bars == 1);
16!
2065

10!
2066
            CHECK(checked(realm_get_num_objects(realm, class_bar.key, nullptr)));
16!
2067
            CHECK(!realm_get_num_objects(realm, 123123123, nullptr));
79!
2068
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
146!
2069
        }
16!
2070

400!
2071
        SECTION("realm_get_object()") {
796!
2072
            realm_object_key_t obj1_key = realm_object_get_key(obj1.get());
80!
2073
            auto obj1a = cptr_checked(realm_get_object(realm, class_foo.key, obj1_key));
146!
2074
            CHECK(obj1a);
15!
2075
            CHECK(realm_equals(obj1a.get(), obj1.get()));
16!
2076

10!
2077
            realm_object_key_t invalid_key = 123123123;
16!
2078
            CHECK(!realm_get_object(realm, class_foo.key, invalid_key));
15!
2079
            CHECK_ERR(RLM_ERR_NO_SUCH_OBJECT);
80!
2080

140!
2081
            realm_class_key_t invalid_class_key = 123123123;
16!
2082
            CHECK(!realm_get_object(realm, invalid_class_key, obj1_key));
15!
2083
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
16!
2084
        }
16!
2085

399!
2086
        SECTION("create object with primary key that already exists") {
796✔
2087
            bool did_create;
80!
2088
            auto obj2a = cptr_checked(
146!
2089
                realm_object_get_or_create_with_primary_key(realm, class_bar.key, rlm_int_val(1), &did_create));
15✔
2090
            CHECK(!did_create);
16!
2091
            CHECK(realm_equals(obj2a.get(), obj2.get()));
16!
2092
        }
16!
2093

400!
2094
        SECTION("realm_get_value()") {
860✔
2095
            realm_value_t value;
210✔
2096
            CHECK(checked(realm_get_value(obj1.get(), foo_int_key, &value)));
146!
2097
            CHECK(value.type == RLM_TYPE_INT);
16!
2098
            CHECK(value.integer == 123);
16!
2099

10!
2100
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
16!
2101
            CHECK(value.type == RLM_TYPE_STRING);
15!
2102
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
80!
2103

140!
2104
            CHECK(checked(realm_get_value(obj1.get(), foo_int_key, nullptr)));
16!
2105

9!
2106
            CHECK(!realm_get_value(obj1.get(), 123123123, &value));
16!
2107
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
15!
2108

9!
2109
            CHECK(!realm_get_value(obj1.get(), 123123123, nullptr));
16!
2110
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
15!
2111

10!
2112
            // Cannot use realm_get_value() to get a list.
9!
2113
            CHECK(!realm_get_value(obj1.get(), foo_links_key, &value));
15!
2114
            CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
14!
2115

10!
2116
            write([&]() {
16!
2117
                CHECK(checked(realm_object_delete(obj1.get())));
14!
2118
            });
16!
2119
            CHECK(!realm_get_value(obj1.get(), foo_int_key, &value));
16!
2120
            CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
15!
2121
        }
15!
2122

400!
2123
        SECTION("realm_get_values()") {
796!
2124
            realm_value_t values[3];
79✔
2125

140✔
2126
            realm_property_key_t keys1[3] = {foo_int_key, foo_str_key, foo_int_key};
16!
2127
            CHECK(checked(realm_get_values(obj1.get(), 3, keys1, values)));
15!
2128

10!
2129
            CHECK(values[0].type == RLM_TYPE_INT);
16!
2130
            CHECK(values[1].type == RLM_TYPE_STRING);
15!
2131
            CHECK(values[2].type == RLM_TYPE_INT);
80!
2132

140!
2133
            CHECK(values[0].integer == 123);
16!
2134
            CHECK(strncmp(values[1].string.data, "Hello, World!", values[1].string.size) == 0);
14!
2135
            CHECK(values[2].integer == 123);
16!
2136

10!
2137
            realm_property_key_t keys2[3] = {foo_int_key, 123123123, foo_str_key};
15!
2138
            CHECK(!realm_get_values(obj1.get(), 3, keys2, values));
15!
2139
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
16!
2140

10!
2141
            write([&]() {
15!
2142
                CHECK(checked(realm_object_delete(obj1.get())));
15!
2143
            });
16!
2144
            CHECK(!realm_get_values(obj1.get(), 3, keys1, values));
16!
2145
            CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
15!
2146
        }
16!
2147

400!
2148
        SECTION("realm_set_value() errors") {
796!
2149
            CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_int_val(456), false));
79!
2150
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
146!
2151

10!
2152
            write([&]() {
16!
2153
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_null(), false));
15!
2154
                CHECK_ERR(RLM_ERR_PROPERTY_NOT_NULLABLE);
16!
2155

10!
2156
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_str_val("a"), false));
80!
2157
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
145!
2158

10!
2159
                CHECK(!realm_set_value(obj1.get(), 123123123, rlm_int_val(123), false));
16!
2160
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
14!
2161
            });
16!
2162
        }
16!
2163

400!
2164
        SECTION("realm_set_values() errors") {
795✔
2165
            realm_value_t int456 = rlm_int_val(456);
80!
2166
            CHECK(!realm_set_values(obj1.get(), 1, &foo_int_key, &int456, false));
146!
2167
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
15!
2168

10!
2169
            write([&]() {
16!
2170
                realm_value_t value;
15✔
2171
                realm_property_key_t keys1[3] = {foo_int_key, foo_str_key, foo_int_key};
16✔
2172
                realm_property_key_t keys2[3] = {foo_int_key, 123123123, foo_str_key};
80✔
2173

140✔
2174
                // No error; check that the last value wins when there are
10✔
2175
                // duplicate keys.
9!
2176
                realm_value_t values1[3] = {rlm_int_val(234), rlm_str_val("aaa"), rlm_int_val(345)};
15!
2177
                CHECK(checked(realm_set_values(obj1.get(), 3, keys1, values1, false)));
14!
2178

10✔
2179
                realm_get_value(obj1.get(), foo_int_key, &value);
16!
2180
                CHECK(value.type == RLM_TYPE_INT);
15!
2181
                CHECK(value.integer == 345);
16!
2182
                realm_get_value(obj1.get(), foo_str_key, &value);
15!
2183
                CHECK(value.type == RLM_TYPE_STRING);
15!
2184
                CHECK(strncmp("aaa", value.string.data, value.string.size) == 0);
15!
2185

10!
2186
                // Type mismatch error.
10!
2187
                realm_value_t values2[3] = {rlm_int_val(111), rlm_str_val("bbb"), rlm_str_val("ccc")};
14✔
2188
                CHECK(!realm_set_values(obj1.get(), 3, keys1, values2, false));
15!
2189
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
16!
2190
                // Properties should remain unchanged.
10!
2191
                realm_get_value(obj1.get(), foo_int_key, &value);
16!
2192
                CHECK(value.type == RLM_TYPE_INT);
15!
2193
                CHECK(value.integer == 345);
16!
2194
                realm_get_value(obj1.get(), foo_str_key, &value);
15!
2195
                CHECK(value.type == RLM_TYPE_STRING);
15!
2196

10✔
2197
                // Invalid property key error.
10!
2198
                CHECK(!realm_set_values(obj1.get(), 3, keys2, values2, false));
15!
2199
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
14!
2200
                // Properties should remain unchanged.
10!
2201
                realm_get_value(obj1.get(), foo_int_key, &value);
16!
2202
                CHECK(value.type == RLM_TYPE_INT);
15!
2203
                CHECK(value.integer == 345);
16!
2204
                realm_get_value(obj1.get(), foo_str_key, &value);
16!
2205
                CHECK(value.type == RLM_TYPE_STRING);
15!
2206
            });
15✔
2207
        }
16!
2208

400!
2209
        SECTION("realm_object_add_int errors") {
795✔
2210
            SECTION("SUCCESS") {
92✔
2211
                realm_begin_write(realm);
146!
2212
                CHECK(realm_object_add_int(obj1.get(), foo_int_key, 10));
18!
2213
                realm_commit(realm);
16✔
2214
            }
16!
2215
            SECTION("ERROR") {
28✔
2216
                CHECK(!realm_object_add_int(obj1.get(), foo_int_key, 10));
16!
2217
                CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
82!
2218
            }
146!
2219
        }
30!
2220

400✔
2221
        SECTION("get/set all property types") {
798!
2222
            realm_value_t null = rlm_null();
80✔
2223
            realm_value_t integer = rlm_int_val(987);
146✔
2224
            realm_value_t boolean = rlm_bool_val(true);
18✔
2225
            realm_value_t string = rlm_str_val("My string");
16!
2226
            const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
16!
2227
            realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
16✔
2228
            realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
18✔
2229
            realm_value_t fnum = rlm_float_val(123.f);
80✔
2230
            realm_value_t dnum = rlm_double_val(456.0);
146✔
2231
            realm_value_t decimal = rlm_decimal_val(999.0);
16✔
2232
            realm_value_t object_id = rlm_object_id_val("abc123abc123");
16✔
2233
            realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16✔
2234
            realm_value_t link = rlm_link_val(class_bar.key, realm_object_get_key(obj2.get()));
16✔
2235

10✔
2236
            write([&]() {
16✔
2237
                CHECK(realm_set_value(obj1.get(), foo_properties["int"], integer, false));
15!
2238
                CHECK(realm_set_value(obj1.get(), foo_properties["bool"], boolean, false));
16!
2239
                CHECK(realm_set_value(obj1.get(), foo_properties["string"], string, false));
16!
2240
                CHECK(realm_set_value(obj1.get(), foo_properties["binary"], binary, false));
16!
2241
                CHECK(realm_set_value(obj1.get(), foo_properties["timestamp"], timestamp, false));
16!
2242
                CHECK(realm_set_value(obj1.get(), foo_properties["float"], fnum, false));
16!
2243
                CHECK(realm_set_value(obj1.get(), foo_properties["double"], dnum, false));
16!
2244
                CHECK(realm_set_value(obj1.get(), foo_properties["decimal"], decimal, false));
15!
2245
                CHECK(realm_set_value(obj1.get(), foo_properties["object_id"], object_id, false));
16!
2246
                CHECK(realm_set_value(obj1.get(), foo_properties["uuid"], uuid, false));
16!
2247
                CHECK(realm_set_value(obj1.get(), foo_properties["mixed"], integer, false));
16!
2248

10!
2249
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_int"], integer, false));
16!
2250
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_bool"], boolean, false));
15!
2251
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_string"], string, false));
16!
2252
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_binary"], binary, false));
16!
2253
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_timestamp"], timestamp, false));
16!
2254
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_float"], fnum, false));
16!
2255
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_double"], dnum, false));
16!
2256
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_decimal"], decimal, false));
16!
2257
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_object_id"], object_id, false));
15!
2258
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_uuid"], uuid, false));
16!
2259

10!
2260
                CHECK(realm_set_value(obj1.get(), foo_properties["link"], link, false));
16!
2261
            });
15!
2262

10!
2263
            realm_value_t value;
16!
2264

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

10!
2310
            write([&]() {
16!
2311
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_int"], null, false));
15!
2312
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_bool"], null, false));
16!
2313
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_string"], null, false));
16!
2314
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_binary"], null, false));
16!
2315
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_timestamp"], null, false));
16!
2316
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_float"], null, false));
16!
2317
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_double"], null, false));
16!
2318
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_decimal"], null, false));
15!
2319
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_object_id"], null, false));
16!
2320
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_uuid"], null, false));
16!
2321
                CHECK(realm_set_value(obj1.get(), foo_properties["mixed"], null, false));
16!
2322
                CHECK(realm_set_value(obj1.get(), foo_properties["link"], null, false));
16!
2323
            });
16!
2324

10!
2325
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_int"], &value));
16!
2326
            CHECK(rlm_val_eq(value, null));
15!
2327
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_bool"], &value));
16!
2328
            CHECK(rlm_val_eq(value, null));
16!
2329
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_string"], &value));
16!
2330
            CHECK(rlm_val_eq(value, null));
16!
2331
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_binary"], &value));
16!
2332
            CHECK(rlm_val_eq(value, null));
16!
2333
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_timestamp"], &value));
15!
2334
            CHECK(rlm_val_eq(value, null));
16!
2335
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_float"], &value));
16!
2336
            CHECK(rlm_val_eq(value, null));
16!
2337
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_double"], &value));
16!
2338
            CHECK(rlm_val_eq(value, null));
16!
2339
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_decimal"], &value));
16!
2340
            CHECK(rlm_val_eq(value, null));
16!
2341
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_object_id"], &value));
16!
2342
            CHECK(rlm_val_eq(value, null));
16!
2343
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_uuid"], &value));
16!
2344
            CHECK(rlm_val_eq(value, null));
16!
2345
            CHECK(realm_get_value(obj1.get(), foo_properties["mixed"], &value));
16!
2346
            CHECK(rlm_val_eq(value, null));
16!
2347
            CHECK(realm_get_value(obj1.get(), foo_properties["link"], &value));
16!
2348
            CHECK(rlm_val_eq(value, null));
16!
2349
        }
16!
2350

400!
2351
        SECTION("embedded") {
796!
2352
            realm_property_info_t info;
80!
2353
            bool found = false;
146!
2354
            REQUIRE(checked(realm_find_property(realm, class_bar.key, "sub", &found, &info)));
16!
2355
            REQUIRE(found);
16!
2356

10!
2357
            auto embedded = cptr_checked(realm_get_linked_object(obj2.get(), info.key));
16!
2358
            CHECK(!embedded);
15!
2359
            write([&]() {
80✔
2360
                auto embedded = cptr_checked(realm_set_embedded(obj2.get(), info.key));
146!
2361
                CHECK(embedded);
16!
2362
            });
16✔
2363
            embedded = cptr_checked(realm_get_linked_object(obj2.get(), info.key));
16!
2364
            CHECK(embedded);
16!
2365
        }
15✔
2366

400!
2367
        SECTION("delete causes invalidation errors") {
796!
2368
            write([&]() {
80✔
2369
                // Get a list instance for later
140✔
2370
                auto list = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
16!
2371

9✔
2372
                CHECK(checked(realm_object_delete(obj1.get())));
16!
2373
                CHECK(!realm_object_is_valid(obj1.get()));
15!
2374

10!
2375
                realm_clear_last_error();
80!
2376
                CHECK(!realm_object_delete(obj1.get()));
145!
2377
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
2378

9!
2379
                realm_clear_last_error();
16!
2380
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_int_val(123), false));
14!
2381
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
2382

10!
2383
                realm_clear_last_error();
15!
2384
                auto list2 = realm_get_list(obj1.get(), foo_links_key);
15✔
2385
                CHECK(!list2);
16!
2386
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
2387

9!
2388
                size_t size;
16!
2389
                CHECK(!realm_list_size(list.get(), &size));
15!
2390
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
16!
2391
            });
15!
2392
        }
16!
2393

400✔
2394
        SECTION("lists") {
796!
2395
            SECTION("realm_get_list() errors") {
320!
2396
                CHECK(!realm_get_list(obj2.get(), bar_int_key));
145!
2397
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
56!
2398

10!
2399
                CHECK(!realm_get_list(obj2.get(), 123123123));
16!
2400
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
15!
2401
            }
16!
2402

194!
2403
            SECTION("nullable strings") {
386✔
2404
                auto strings = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
111✔
2405
                CHECK(strings);
92!
2406
                CHECK(!realm_is_frozen(strings.get()));
58!
2407

33!
2408
                realm_value_t a = rlm_str_val("a");
58!
2409
                realm_value_t b = rlm_str_val("b");
54!
2410
                realm_value_t c = rlm_null();
58✔
2411

53✔
2412
                SECTION("realm_equals() type check") {
98✔
2413
                    CHECK(!realm_equals(strings.get(), obj1.get()));
24!
2414
                }
28!
2415

34!
2416
                SECTION("realm_clone()") {
54✔
2417
                    auto list2 = clone_cptr(strings.get());
24✔
2418
                    CHECK(realm_equals(strings.get(), list2.get()));
28!
2419
                    CHECK(strings.get() != list2.get());
22!
2420
                }
18!
2421

34!
2422
                SECTION("insert, then get") {
52!
2423
                    write([&]() {
18✔
2424
                        CHECK(checked(realm_list_insert(strings.get(), 0, a)));
24!
2425
                        CHECK(checked(realm_list_insert(strings.get(), 1, b)));
22!
2426
                        CHECK(checked(realm_list_insert(strings.get(), 2, c)));
16!
2427

10!
2428
                        realm_value_t a2, b2, c2;
16!
2429
                        CHECK(checked(realm_list_get(strings.get(), 0, &a2)));
15!
2430
                        CHECK(checked(realm_list_get(strings.get(), 1, &b2)));
18!
2431
                        CHECK(checked(realm_list_get(strings.get(), 2, &c2)));
22!
2432

10!
2433
                        CHECK(rlm_stdstr(a2) == "a");
16!
2434
                        CHECK(rlm_stdstr(b2) == "b");
15!
2435
                        CHECK(c2.type == RLM_TYPE_NULL);
16!
2436

9!
2437
                        size_t out_index = -1;
16!
2438
                        bool found;
15!
2439
                        CHECK(checked(realm_list_find(strings.get(), &a2, &out_index, &found)));
16!
2440
                        CHECK(out_index == 0);
16!
2441
                        CHECK(found);
15!
2442
                        CHECK(checked(realm_list_find(strings.get(), &b2, &out_index, &found)));
16!
2443
                        CHECK(out_index == 1);
16!
2444
                        CHECK(found);
16!
2445
                        CHECK(checked(realm_list_find(strings.get(), &c2, &out_index, &found)));
15!
2446
                        CHECK(out_index == 2);
16!
2447
                        CHECK(found);
16!
2448

10!
2449
                        realm_value_t dummy = rlm_str_val("c");
16!
2450
                        CHECK(checked(realm_list_find(strings.get(), &dummy, &out_index, &found)));
15!
2451
                        CHECK(!found);
16!
2452
                        CHECK(out_index == realm::not_found);
16!
2453

10!
2454
                        // verify that conversion to results works
10!
2455
                        auto results = cptr_checked(realm_list_to_results(strings.get()));
15!
2456
                        CHECK(checked(realm_results_find(results.get(), &a2, &out_index, &found)));
15!
2457
                        CHECK(found);
15!
2458
                        CHECK(out_index == 0);
16!
2459
                        CHECK(checked(realm_results_find(results.get(), &b2, &out_index, &found)));
16!
2460
                        CHECK(found);
16!
2461
                        CHECK(out_index == 1);
16!
2462
                        CHECK(checked(realm_results_find(results.get(), &c2, &out_index, &found)));
15!
2463
                        CHECK(found);
15!
2464
                        CHECK(out_index == 2);
16!
2465
                    });
16!
2466
                }
16!
2467

28!
2468
                SECTION("equality") {
52!
2469
                    auto strings2 = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
18!
2470
                    CHECK(strings2);
22!
2471
                    CHECK(realm_equals(strings.get(), strings2.get()));
16!
2472

10!
2473
                    write([&]() {
16!
2474
                        auto obj3 =
15✔
2475
                            cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(2)));
16✔
2476
                        CHECK(obj3);
18!
2477
                        auto strings3 = cptr_checked(realm_get_list(obj3.get(), bar_strings_key));
22✔
2478
                        CHECK(!realm_equals(strings.get(), strings3.get()));
16!
2479
                    });
16!
2480
                }
16!
2481
            }
51✔
2482

130✔
2483
            SECTION("get/insert all property types") {
262✔
2484
                realm_value_t null = rlm_null();
35✔
2485
                realm_value_t integer = rlm_int_val(987);
56!
2486
                realm_value_t boolean = rlm_bool_val(true);
16✔
2487
                realm_value_t string = rlm_str_val("My string");
16!
2488
                const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
16✔
2489
                realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
16✔
2490
                realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
22✔
2491
                realm_value_t fnum = rlm_float_val(123.f);
35✔
2492
                realm_value_t dnum = rlm_double_val(456.0);
56✔
2493
                realm_value_t decimal = rlm_decimal_val(999.0);
16✔
2494
                realm_value_t object_id = rlm_object_id_val("abc123abc123");
16✔
2495
                realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16✔
2496

10✔
2497
                auto int_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["int_list"]));
16✔
2498
                auto bool_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["bool_list"]));
15✔
2499
                auto string_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["string_list"]));
16✔
2500
                auto binary_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["binary_list"]));
16✔
2501
                auto timestamp_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["timestamp_list"]));
16✔
2502
                auto float_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["float_list"]));
16✔
2503
                auto double_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["double_list"]));
16✔
2504
                auto decimal_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["decimal_list"]));
16✔
2505
                auto object_id_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["object_id_list"]));
15✔
2506
                auto uuid_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["uuid_list"]));
16✔
2507
                auto nullable_int_list =
16✔
2508
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_int_list"]));
16✔
2509
                auto nullable_bool_list =
16✔
2510
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_bool_list"]));
16✔
2511
                auto nullable_string_list =
16✔
2512
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_string_list"]));
16✔
2513
                auto nullable_binary_list =
16✔
2514
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_binary_list"]));
16✔
2515
                auto nullable_timestamp_list =
16✔
2516
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_timestamp_list"]));
16✔
2517
                auto nullable_float_list =
16✔
2518
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_float_list"]));
16✔
2519
                auto nullable_double_list =
16✔
2520
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_double_list"]));
16✔
2521
                auto nullable_decimal_list =
16✔
2522
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_decimal_list"]));
16✔
2523
                auto nullable_object_id_list =
16✔
2524
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_object_id_list"]));
16✔
2525
                auto nullable_uuid_list =
16✔
2526
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_uuid_list"]));
16✔
2527

10✔
2528
                write([&]() {
16✔
2529
                    CHECK(realm_list_insert(int_list.get(), 0, integer));
15!
2530
                    CHECK(realm_list_insert(bool_list.get(), 0, boolean));
16!
2531
                    CHECK(realm_list_insert(string_list.get(), 0, string));
16!
2532
                    CHECK(realm_list_insert(binary_list.get(), 0, binary));
16!
2533
                    CHECK(realm_list_insert(timestamp_list.get(), 0, timestamp));
16!
2534
                    CHECK(realm_list_insert(float_list.get(), 0, fnum));
16!
2535
                    CHECK(realm_list_insert(double_list.get(), 0, dnum));
16!
2536
                    CHECK(realm_list_insert(decimal_list.get(), 0, decimal));
15!
2537
                    CHECK(realm_list_insert(object_id_list.get(), 0, object_id));
16!
2538
                    CHECK(realm_list_insert(uuid_list.get(), 0, uuid));
16!
2539

10!
2540
                    CHECK(realm_list_insert(nullable_int_list.get(), 0, integer));
16!
2541
                    CHECK(realm_list_insert(nullable_bool_list.get(), 0, boolean));
15!
2542
                    CHECK(realm_list_insert(nullable_string_list.get(), 0, string));
16!
2543
                    CHECK(realm_list_insert(nullable_binary_list.get(), 0, binary));
16!
2544
                    CHECK(realm_list_insert(nullable_timestamp_list.get(), 0, timestamp));
16!
2545
                    CHECK(realm_list_insert(nullable_float_list.get(), 0, fnum));
16!
2546
                    CHECK(realm_list_insert(nullable_double_list.get(), 0, dnum));
16!
2547
                    CHECK(realm_list_insert(nullable_decimal_list.get(), 0, decimal));
16!
2548
                    CHECK(realm_list_insert(nullable_object_id_list.get(), 0, object_id));
15!
2549
                    CHECK(realm_list_insert(nullable_uuid_list.get(), 0, uuid));
16!
2550

10!
2551
                    CHECK(realm_list_insert(nullable_int_list.get(), 1, null));
16!
2552
                    CHECK(realm_list_insert(nullable_bool_list.get(), 1, null));
15!
2553
                    CHECK(realm_list_insert(nullable_string_list.get(), 1, null));
16!
2554
                    CHECK(realm_list_insert(nullable_binary_list.get(), 1, null));
16!
2555
                    CHECK(realm_list_insert(nullable_timestamp_list.get(), 1, null));
16!
2556
                    CHECK(realm_list_insert(nullable_float_list.get(), 1, null));
16!
2557
                    CHECK(realm_list_insert(nullable_double_list.get(), 1, null));
16!
2558
                    CHECK(realm_list_insert(nullable_decimal_list.get(), 1, null));
16!
2559
                    CHECK(realm_list_insert(nullable_object_id_list.get(), 1, null));
15!
2560
                    CHECK(realm_list_insert(nullable_uuid_list.get(), 1, null));
16!
2561
                });
16!
2562

10!
2563
                auto find = ([&](auto* list, auto* value) {
244!
2564
                    std::size_t index = -1;
243!
2565
                    bool found = false;
282!
2566
                    CHECK(checked(realm_list_find(list, value, &index, &found)));
282!
2567
                    CHECK(index == 0);
282!
2568
                    CHECK(found);
282!
2569
                    return (index < list->size()) && found == true;
282✔
2570
                });
282!
2571

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

10!
2635
                write([&]() {
16!
2636
                    CHECK(realm_list_insert(nullable_int_list.get(), 0, null));
15!
2637
                    CHECK(realm_list_insert(nullable_bool_list.get(), 0, null));
16!
2638
                    CHECK(realm_list_insert(nullable_string_list.get(), 0, null));
16!
2639
                    CHECK(realm_list_insert(nullable_binary_list.get(), 0, null));
16!
2640
                    CHECK(realm_list_insert(nullable_timestamp_list.get(), 0, null));
16!
2641
                    CHECK(realm_list_insert(nullable_float_list.get(), 0, null));
16!
2642
                    CHECK(realm_list_insert(nullable_double_list.get(), 0, null));
16!
2643
                    CHECK(realm_list_insert(nullable_decimal_list.get(), 0, null));
15!
2644
                    CHECK(realm_list_insert(nullable_object_id_list.get(), 0, null));
16!
2645
                    CHECK(realm_list_insert(nullable_uuid_list.get(), 0, null));
16!
2646
                });
16!
2647

10!
2648
                CHECK(realm_list_get(nullable_int_list.get(), 0, &value));
16!
2649
                CHECK(rlm_val_eq(value, null));
15!
2650
                CHECK(realm_list_get(nullable_bool_list.get(), 0, &value));
16!
2651
                CHECK(rlm_val_eq(value, null));
16!
2652
                CHECK(realm_list_get(nullable_string_list.get(), 0, &value));
16!
2653
                CHECK(rlm_val_eq(value, null));
16!
2654
                CHECK(realm_list_get(nullable_binary_list.get(), 0, &value));
16!
2655
                CHECK(rlm_val_eq(value, null));
16!
2656
                CHECK(realm_list_get(nullable_timestamp_list.get(), 0, &value));
15!
2657
                CHECK(rlm_val_eq(value, null));
16!
2658
                CHECK(realm_list_get(nullable_float_list.get(), 0, &value));
16!
2659
                CHECK(rlm_val_eq(value, null));
16!
2660
                CHECK(realm_list_get(nullable_double_list.get(), 0, &value));
16!
2661
                CHECK(rlm_val_eq(value, null));
16!
2662
                CHECK(realm_list_get(nullable_decimal_list.get(), 0, &value));
16!
2663
                CHECK(rlm_val_eq(value, null));
16!
2664
                CHECK(realm_list_get(nullable_object_id_list.get(), 0, &value));
16!
2665
                CHECK(rlm_val_eq(value, null));
16!
2666
                CHECK(realm_list_get(nullable_uuid_list.get(), 0, &value));
16!
2667
                CHECK(rlm_val_eq(value, null));
16!
2668
            }
16!
2669

130!
2670
            SECTION("move") {
256!
2671
                auto int_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["int_list"]));
35!
2672
                write([&]() {
56!
2673
                    for (int i = 0; i < 10; ++i) {
136✔
2674
                        CHECK(realm_list_insert(int_list.get(), i, rlm_int_val(i)));
124!
2675
                    }
144✔
2676
                });
34!
2677

28✔
2678
                realm_value_t value;
35✔
2679
                auto expected = std::vector<int64_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
55✔
2680
                for (int i = 0; i < 10; ++i) {
136✔
2681
                    CHECK(realm_list_get(int_list.get(), i, &value));
124!
2682
                    CHECK(rlm_val_eq(value, rlm_int_val(expected[i])));
164✔
2683
                }
160!
2684

46!
2685
                write([&]() {
34✔
2686
                    CHECK(realm_list_move(int_list.get(), 0, 1));
14!
2687
                });
16✔
2688

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

28!
2695
                write([&]() {
34!
2696
                    CHECK(realm_list_move(int_list.get(), 3, 2));
15!
2697
                });
15✔
2698

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

148!
2706
            SECTION("links") {
256✔
2707
                CPtr<realm_list_t> bars;
70✔
2708

68✔
2709
                write([&]() {
78✔
2710
                    bars = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
72!
2711
                    auto bar_link = realm_object_as_link(obj2.get());
76!
2712
                    realm_value_t bar_link_val;
76✔
2713
                    bar_link_val.type = RLM_TYPE_LINK;
58✔
2714
                    bar_link_val.link = bar_link;
77✔
2715
                    CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
98!
2716
                    CHECK(checked(realm_list_insert(bars.get(), 1, bar_link_val)));
64!
2717
                    size_t size;
60!
2718
                    CHECK(checked(realm_list_size(bars.get(), &size)));
64!
2719
                    CHECK(size == 2);
64!
2720

40!
2721
                    bool found = true;
64!
2722
                    size_t index = -1;
60✔
2723
                    CHECK(checked(realm_list_find(bars.get(), &bar_link_val, &index, &found)));
64!
2724
                    CHECK(index == 0);
64!
2725
                    CHECK(found);
64!
2726

40!
2727
                    realm_list_clear(bars.get());
64!
2728
                    CHECK(checked(realm_list_find(bars.get(), &bar_link_val, &index, &found)));
60!
2729
                    CHECK(index == realm::not_found);
60!
2730
                    CHECK(!found);
64!
2731

40!
2732
                    CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
64!
2733
                    CHECK(checked(realm_list_insert(bars.get(), 1, bar_link_val)));
60!
2734
                });
64!
2735

36!
2736
                SECTION("get") {
64✔
2737
                    realm_value_t val;
24!
2738
                    CHECK(checked(realm_list_get(bars.get(), 0, &val)));
28!
2739
                    CHECK(val.type == RLM_TYPE_LINK);
22!
2740
                    CHECK(val.link.target_table == class_bar.key);
18!
2741
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
22!
2742

16!
2743
                    CHECK(checked(realm_list_get(bars.get(), 1, &val)));
22!
2744
                    CHECK(val.type == RLM_TYPE_LINK);
17!
2745
                    CHECK(val.link.target_table == class_bar.key);
22!
2746
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
16!
2747

10!
2748
                    auto result = realm_list_get(bars.get(), 2, &val);
16!
2749
                    CHECK(!result);
15!
2750
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
16!
2751
                }
15!
2752

28!
2753
                SECTION("set wrong type") {
52!
2754
                    write([&]() {
18!
2755
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
22!
2756
                        CHECK(foo2);
15!
2757
                        realm_value_t foo2_link_val;
16✔
2758
                        foo2_link_val.type = RLM_TYPE_LINK;
16!
2759
                        foo2_link_val.link = realm_object_as_link(foo2.get());
16!
2760

10✔
2761
                        CHECK(!realm_list_set(bars.get(), 0, foo2_link_val));
18!
2762
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
21!
2763
                    });
16!
2764
                }
16!
2765

28!
2766
                SECTION("realm_list_clear()") {
52✔
2767
                    write([&]() {
18✔
2768
                        CHECK(realm_list_clear(bars.get()));
22!
2769
                    });
15✔
2770
                    size_t size;
16!
2771
                    CHECK(realm_list_size(bars.get(), &size));
16!
2772
                    CHECK(size == 0);
16!
2773

10!
2774
                    size_t num_bars;
18!
2775
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
21!
2776
                    CHECK(num_bars != 0);
16!
2777
                }
16!
2778

28!
2779
                SECTION("realm_list_remove_all()") {
52✔
2780
                    size_t num_bars;
18!
2781
                    size_t size;
22!
2782

9✔
2783
                    write([&]() {
16✔
2784
                        CHECK(checked(realm_list_remove_all(bars.get())));
15!
2785
                    });
16!
2786

10!
2787
                    CHECK(realm_list_size(bars.get(), &size));
18!
2788
                    CHECK(size == 0);
21!
2789

10!
2790
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
16!
2791
                    CHECK(num_bars == 0);
14!
2792
                }
16!
2793
            }
52!
2794

130✔
2795
            SECTION("embedded") {
261✔
2796
                CPtr<realm_list_t> subs;
35!
2797
                realm_property_info_t info;
56!
2798
                bool found = false;
15✔
2799
                REQUIRE(checked(realm_find_property(realm, class_bar.key, "sub_list", &found, &info)));
16!
2800
                REQUIRE(found);
16!
2801
                subs = cptr_checked(realm_get_list(obj2.get(), info.key));
16!
2802

16!
2803
                write([&]() {
35✔
2804
                    auto embedded = cptr_checked(realm_list_insert_embedded(subs.get(), 0));
55✔
2805
                    CHECK(embedded);
16!
2806
                });
16✔
2807
                auto embedded = cptr_checked(realm_list_get_linked_object(subs.get(), 0));
16!
2808
                CHECK(embedded);
16!
2809
            }
16!
2810

130!
2811
            SECTION("notifications") {
255✔
2812
                struct State {
131✔
2813
                    CPtr<realm_collection_changes_t> changes;
152✔
2814
                    CPtr<realm_async_error_t> error;
128!
2815
                    bool destroyed = false;
128✔
2816
                    bool called = false;
128✔
2817
                };
128!
2818

74✔
2819
                State state;
147✔
2820

105✔
2821
                auto on_change = [](void* userdata, const realm_collection_changes_t* changes) {
192✔
2822
                    auto* state = static_cast<State*>(userdata);
183✔
2823
                    state->changes = clone_cptr(changes);
200✔
2824
                    state->called = true;
200✔
2825
                };
200✔
2826

98✔
2827
                CPtr<realm_list_t> strings = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
143✔
2828

81✔
2829
                auto str1 = rlm_str_val("a");
135✔
2830
                auto str2 = rlm_str_val("b");
143✔
2831
                auto null = rlm_null();
152✔
2832

98✔
2833
                auto require_change = [&]() {
110✔
2834
                    auto token = cptr_checked(
59✔
2835
                        realm_list_add_notification_callback(strings.get(), &state, nullptr, nullptr, on_change));
44✔
2836
                    checked(realm_refresh(realm, nullptr));
46✔
2837
                    return token;
37✔
2838
                };
46✔
2839

76✔
2840
                SECTION("userdata is freed when the token is destroyed") {
130✔
2841
                    auto token = cptr_checked(realm_list_add_notification_callback(
30✔
2842
                        strings.get(), &state,
41✔
2843
                        [](void* p) {
18✔
2844
                            static_cast<State*>(p)->destroyed = true;
18✔
2845
                        },
18✔
2846
                        nullptr, nullptr));
18✔
2847
                    CHECK(!state.destroyed);
18!
2848
                    token.reset();
23✔
2849
                    CHECK(state.destroyed);
32!
2850
                }
16✔
2851

58!
2852
                SECTION("insertion sends a change callback") {
112✔
2853
                    auto token = require_change();
23✔
2854
                    write([&]() {
32✔
2855
                        checked(realm_list_insert(strings.get(), 0, str1));
16✔
2856
                        checked(realm_list_insert(strings.get(), 1, str2));
16!
2857
                        checked(realm_list_insert(strings.get(), 2, null));
16✔
2858
                    });
16!
2859
                    CHECK(!state.error);
16!
2860
                    CHECK(state.changes);
23!
2861

26!
2862
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
16!
2863
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
15✔
2864
                                                            &num_insertion_ranges, &num_modification_ranges,
16✔
2865
                                                            &num_moves);
16✔
2866
                    CHECK(num_deletion_ranges == 0);
16!
2867
                    CHECK(num_insertion_ranges == 1);
16!
2868
                    CHECK(num_modification_ranges == 0);
16!
2869
                    CHECK(num_moves == 0);
16!
2870

9!
2871
                    realm_index_range_t insertion_range;
16!
2872
                    realm_collection_changes_get_ranges(state.changes.get(), nullptr, 0, &insertion_range, 1, nullptr,
15✔
2873
                                                        0, nullptr, 0, nullptr, 0);
16✔
2874
                    CHECK(insertion_range.from == 0);
16!
2875
                    CHECK(insertion_range.to == 3);
16!
2876
                }
16!
2877

58!
2878
                SECTION("modifying target of list with a filter") {
112!
2879
                    auto bars = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
82✔
2880
                    write([&]() {
92✔
2881
                        auto bar_link = realm_object_as_link(obj2.get());
86✔
2882
                        realm_value_t bar_link_val;
86✔
2883
                        bar_link_val.type = RLM_TYPE_LINK;
86!
2884
                        bar_link_val.link = bar_link;
86!
2885
                        CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
86!
2886
                    });
93✔
2887

66!
2888
                    SECTION("using valid key") {
96✔
2889
                        const char* bar_strings[1] = {"doubles"};
30✔
2890
                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
36✔
2891
                        REQUIRE(key_path_array);
26!
2892
                        auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
26✔
2893
                                                                                       key_path_array, on_change));
26!
2894
                        realm_release(key_path_array);
26!
2895
                        checked(realm_refresh(realm, nullptr));
26✔
2896

14✔
2897
                        state.called = false;
26✔
2898
                        write([&]() {
15✔
2899
                            checked(realm_set_value(obj2.get(), bar_doubles_key, rlm_double_val(5.0), false));
16✔
2900
                        });
16!
2901
                        REQUIRE(state.called);
16!
2902
                        CHECK(!state.error);
16!
2903
                        CHECK(state.changes);
16!
2904

10!
2905
                        state.called = false;
15!
2906
                        write([&]() {
15✔
2907
                            checked(realm_list_insert(strings.get(), 0, str1));
16✔
2908
                            checked(realm_list_insert(strings.get(), 1, str2));
16✔
2909
                            checked(realm_list_insert(strings.get(), 2, null));
16✔
2910
                        });
16!
2911
                        REQUIRE(!state.called);
16!
2912
                    }
16!
2913
                    SECTION("using invalid key") {
75!
2914
                        const char* bar_strings[1] = {"dobles"};
16✔
2915
                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
26✔
2916
                        REQUIRE(!key_path_array);
16!
2917
                        realm_clear_last_error();
16✔
2918
                    }
16!
2919
                    SECTION("Embedded objects") {
76✔
2920
                        realm_property_info_t info;
28!
2921
                        bool found = false;
38✔
2922
                        realm_key_path_array_t* key_path_array = nullptr;
40✔
2923
                        realm_find_property(realm, class_bar.key, "sub", &found, &info);
30✔
2924
                        auto bar_sub_key = info.key;
30✔
2925
                        realm_find_property(realm, class_embedded.key, "int", &found, &info);
30!
2926
                        auto embedded_int_key = info.key;
30✔
2927
                        CPtr<realm_object_t> embedded;
30✔
2928
                        write([&]() {
40✔
2929
                            embedded = cptr_checked(realm_set_embedded(obj2.get(), bar_sub_key));
32✔
2930
                        });
32✔
2931

20✔
2932
                        SECTION("using valid nesting") {
32✔
2933

12✔
2934
                            const char* bar_strings[1] = {"sub.int"};
20✔
2935
                            key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
17✔
2936
                            REQUIRE(key_path_array);
18!
2937
                        }
18✔
2938
                        SECTION("using star notation") {
30!
2939
                            const char* bar_strings[1] = {"*.int"};
18✔
2940
                            key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
18✔
2941
                            // (*realm)->print_key_path_array(*key_path_array);
12✔
2942
                        }
15✔
2943

15✔
2944
                        REQUIRE(key_path_array);
28!
2945
                        auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
28!
2946
                                                                                       key_path_array, on_change));
30!
2947
                        realm_release(key_path_array);
32✔
2948

18✔
2949
                        checked(realm_refresh(realm, nullptr));
30✔
2950

15✔
2951
                        state.called = false;
30✔
2952
                        write([&]() {
28✔
2953
                            checked(realm_set_value(embedded.get(), embedded_int_key, rlm_int_val(999), false));
32!
2954
                        });
32✔
2955
                        REQUIRE(state.called);
32!
2956
                        CHECK(!state.error);
32!
2957
                        CHECK(state.changes);
30!
2958
                    }
32!
2959
                    SECTION("using backlink") {
78!
2960
                        const char* bar_strings[1] = {"linking_objects.public_int"};
20✔
2961
                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
28✔
2962
                        REQUIRE(key_path_array);
18!
2963
                        auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
18✔
2964
                                                                                       key_path_array, on_change));
18!
2965
                        realm_release(key_path_array);
18!
2966
                        checked(realm_refresh(realm, nullptr));
18!
2967

12✔
2968
                        state.called = false;
26✔
2969
                        write([&]() {
15✔
2970
                            checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
16✔
2971
                        });
16!
2972
                        REQUIRE(state.called);
16!
2973
                        CHECK(!state.error);
16!
2974
                        CHECK(state.changes);
16!
2975
                    }
16!
2976
                    SECTION("using invalid nesting") {
75!
2977
                        const char* bar_strings[1] = {"doubles.age"};
16✔
2978
                        auto key_path_array = realm_create_key_path_array(realm, class_bar.key, 1, bar_strings);
26✔
2979
                        REQUIRE(!key_path_array);
16!
2980
                        realm_clear_last_error();
16✔
2981
                    }
16!
2982
                }
76!
2983

58!
2984
                SECTION("insertion, deletion, modification, modification after") {
122✔
2985
                    write([&]() {
33✔
2986
                        checked(realm_list_insert(strings.get(), 0, str1));
32✔
2987
                        checked(realm_list_insert(strings.get(), 1, str2));
16✔
2988
                        checked(realm_list_insert(strings.get(), 2, str1));
16!
2989
                    });
16✔
2990

10✔
2991
                    auto token = require_change();
26✔
2992

16✔
2993
                    write([&]() {
32✔
2994
                        checked(realm_list_erase(strings.get(), 1));
15✔
2995
                        checked(realm_list_insert(strings.get(), 0, null));
16✔
2996
                        checked(realm_list_insert(strings.get(), 1, null));
16✔
2997

10✔
2998
                        // This element was previously at 0, and ends up at 2.
10✔
2999
                        checked(realm_list_set(strings.get(), 2, str1));
14✔
3000
                    });
15✔
3001
                    CHECK(!state.error);
15!
3002
                    CHECK(state.changes);
16!
3003

10!
3004
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
16!
3005
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
15✔
3006
                                                            &num_insertion_ranges, &num_modification_ranges,
15✔
3007
                                                            &num_moves);
15✔
3008
                    CHECK(num_deletion_ranges == 1);
16!
3009
                    CHECK(num_insertion_ranges == 1);
16!
3010
                    CHECK(num_modification_ranges == 1);
16!
3011
                    CHECK(num_moves == 0);
16!
3012

9!
3013
                    size_t num_deletions, num_insertions, num_modifications;
16!
3014
                    bool collection_cleared = false;
15✔
3015
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
16✔
3016
                                                             &num_modifications, &num_moves, &collection_cleared,
16✔
3017
                                                             nullptr);
16!
3018
                    CHECK(num_deletions == 1);
16!
3019
                    CHECK(num_insertions == 2);
16!
3020
                    CHECK(num_modifications == 1);
16!
3021
                    CHECK(collection_cleared == false);
15!
3022

10!
3023
                    realm_index_range_t deletions, insertions, modifications, modifications_after;
16!
3024
                    realm_collection_move_t moves;
15✔
3025
                    realm_collection_changes_get_ranges(state.changes.get(), &deletions, 1, &insertions, 1,
16✔
3026
                                                        &modifications, 1, &modifications_after, 1, &moves, 1);
16✔
3027
                    CHECK(deletions.from == 1);
16!
3028
                    CHECK(deletions.to == 2);
16!
3029

10!
3030
                    CHECK(insertions.from == 0);
16!
3031
                    CHECK(insertions.to == 2);
14!
3032

10!
3033
                    CHECK(modifications.from == 0);
16!
3034
                    CHECK(modifications.to == 1);
15!
3035

10!
3036
                    CHECK(modifications_after.from == 2);
16!
3037
                    CHECK(modifications_after.to == 3);
15!
3038

9!
3039
                    std::vector<size_t> deletions_v, insertions_v, modifications_v, modifications_after_v;
16!
3040
                    std::vector<realm_collection_move_t> moves_v;
15!
3041
                    deletions_v.resize(100, size_t(-1));
15✔
3042
                    insertions_v.resize(100, size_t(-1));
16!
3043
                    modifications_v.resize(100, size_t(-1));
16!
3044
                    modifications_after_v.resize(100, size_t(-1));
15✔
3045
                    moves_v.resize(100, realm_collection_move_t{size_t(-1), size_t(-1)});
16!
3046
                    realm_collection_changes_get_changes(state.changes.get(), deletions_v.data(), 100,
16!
3047
                                                         insertions_v.data(), 100, modifications_v.data(), 100,
15✔
3048
                                                         modifications_after_v.data(), 100, moves_v.data(), 100);
16✔
3049
                    CHECK(deletions_v[0] == 1);
16!
3050
                    CHECK(deletions_v[1] == size_t(-1));
16!
3051
                    CHECK(insertions_v[0] == 0);
16!
3052
                    CHECK(insertions_v[1] == 1);
16!
3053
                    CHECK(insertions_v[2] == size_t(-1));
16!
3054
                    CHECK(modifications_v[0] == 0);
16!
3055
                    CHECK(modifications_v[1] == size_t(-1));
16!
3056
                    CHECK(modifications_after_v[0] == 2);
16!
3057
                    CHECK(modifications_after_v[1] == size_t(-1));
16!
3058

10!
3059
                    write([&]() {
16!
3060
                        checked(realm_list_clear(strings.get()));
15!
3061
                    });
16!
3062

10!
3063
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
16!
3064
                                                             &num_modifications, &num_moves, &collection_cleared,
15!
3065
                                                             nullptr);
16!
3066
                    CHECK(collection_cleared == true);
16!
3067
                }
15✔
3068
            }
112!
3069
        }
256✔
3070

416✔
3071
        SECTION("sets") {
835✔
3072
            SECTION("realm_get_set() errors") {
224✔
3073
                CHECK(!realm_get_set(obj1.get(), foo_properties["int"]));
146!
3074
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
40!
3075

10!
3076
                CHECK(!realm_get_set(obj1.get(), 123123123));
16!
3077
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
31!
3078
            }
56!
3079

146!
3080
            SECTION("nullable strings") {
290✔
3081
                auto strings = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
99✔
3082
                CHECK(strings);
88!
3083
                CHECK(!realm_is_frozen(strings.get()));
72!
3084

41!
3085
                realm_value_t a = rlm_str_val("a");
72!
3086
                realm_value_t b = rlm_str_val("b");
67!
3087
                realm_value_t c = rlm_null();
72✔
3088

53✔
3089
                SECTION("realm_equals() type check") {
96✔
3090
                    CHECK(!realm_equals(strings.get(), obj1.get()));
27!
3091
                }
32!
3092

42!
3093
                SECTION("realm_clone()") {
67✔
3094
                    auto set2 = clone_cptr(strings.get());
27✔
3095
                    CHECK(realm_equals(strings.get(), set2.get()));
32!
3096
                    CHECK(strings.get() != set2.get());
24!
3097
                }
19!
3098

42!
3099
                SECTION("insert, then get, then erase") {
64!
3100
                    write([&]() {
19✔
3101
                        bool inserted = false;
27✔
3102
                        CHECK(checked(realm_set_insert(strings.get(), a, nullptr, &inserted)));
24!
3103
                        CHECK(inserted);
16!
3104
                        CHECK(checked(realm_set_insert(strings.get(), b, nullptr, &inserted)));
16!
3105
                        CHECK(inserted);
16!
3106
                        CHECK(checked(realm_set_insert(strings.get(), c, nullptr, &inserted)));
16!
3107
                        CHECK(inserted);
19!
3108

18!
3109
                        size_t a_index, b_index, c_index;
16!
3110
                        bool found = false;
15✔
3111
                        CHECK(checked(realm_set_find(strings.get(), a, &a_index, &found)));
16!
3112
                        CHECK(found);
16!
3113
                        CHECK(checked(realm_set_find(strings.get(), b, &b_index, &found)));
16!
3114
                        CHECK(found);
16!
3115
                        CHECK(checked(realm_set_find(strings.get(), c, &c_index, &found)));
16!
3116
                        CHECK(found);
16!
3117

9!
3118
                        realm_value_t a2, b2, c2;
16!
3119
                        CHECK(checked(realm_set_get(strings.get(), a_index, &a2)));
15!
3120
                        CHECK(checked(realm_set_get(strings.get(), b_index, &b2)));
16!
3121
                        CHECK(checked(realm_set_get(strings.get(), c_index, &c2)));
16!
3122

10!
3123
                        CHECK(rlm_stdstr(a2) == "a");
16!
3124
                        CHECK(rlm_stdstr(b2) == "b");
15!
3125
                        CHECK(c2.type == RLM_TYPE_NULL);
16!
3126

9!
3127
                        bool erased = false;
16!
3128
                        CHECK(checked(realm_set_erase(strings.get(), a2, &erased)));
15!
3129
                        CHECK(erased);
16!
3130
                        CHECK(checked(realm_set_erase(strings.get(), rlm_int_val(987), &erased)));
16!
3131
                        CHECK(!erased);
15!
3132
                    });
16!
3133
                }
16!
3134

34!
3135
                SECTION("equality") {
63✔
3136
                    auto strings2 = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
19✔
3137
                    CHECK(strings2);
24!
3138
                    CHECK(realm_equals(strings.get(), strings2.get()));
16!
3139

10!
3140
                    write([&]() {
16!
3141
                        auto obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
15✔
3142
                        CHECK(obj3);
16!
3143
                        auto strings3 =
19✔
3144
                            cptr_checked(realm_get_set(obj3.get(), foo_properties["nullable_string_set"]));
24!
3145
                        CHECK(!realm_equals(strings.get(), strings3.get()));
16!
3146
                    });
16!
3147
                }
16!
3148

33✔
3149
                SECTION("parse query for sets") {
64✔
3150
                    auto links = cptr_checked(realm_get_set(obj1.get(), foo_properties["link_set"]));
19✔
3151
                    CHECK(links);
24!
3152
                    cptr_checked(realm_query_parse_for_set(links.get(), "TRUEPREDICATE", 0, nullptr));
16✔
3153
                }
16!
3154
            }
64!
3155

82✔
3156
            SECTION("get/insert all property types") {
168✔
3157
                realm_value_t null = rlm_null();
30✔
3158
                realm_value_t integer = rlm_int_val(987);
48✔
3159
                realm_value_t boolean = rlm_bool_val(true);
16✔
3160
                realm_value_t string = rlm_str_val("My string");
16!
3161
                const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
16✔
3162
                realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
16✔
3163
                realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
24✔
3164
                realm_value_t fnum = rlm_float_val(123.f);
27✔
3165
                realm_value_t dnum = rlm_double_val(456.0);
40✔
3166
                realm_value_t decimal = rlm_decimal_val(999.0);
16✔
3167
                realm_value_t object_id = rlm_object_id_val("abc123abc123");
16✔
3168
                realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16✔
3169

10✔
3170
                auto int_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["int_set"]));
16✔
3171
                auto bool_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["bool_set"]));
15✔
3172
                auto string_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["string_set"]));
16✔
3173
                auto binary_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["binary_set"]));
16✔
3174
                auto timestamp_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["timestamp_set"]));
16✔
3175
                auto float_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["float_set"]));
16✔
3176
                auto double_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["double_set"]));
16✔
3177
                auto decimal_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["decimal_set"]));
16✔
3178
                auto object_id_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["object_id_set"]));
15✔
3179
                auto uuid_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["uuid_set"]));
16✔
3180
                auto nullable_int_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_int_set"]));
16✔
3181
                auto nullable_bool_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_bool_set"]));
16✔
3182
                auto nullable_string_set =
16✔
3183
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
16✔
3184
                auto nullable_binary_set =
16✔
3185
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_binary_set"]));
16✔
3186
                auto nullable_timestamp_set =
16✔
3187
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_timestamp_set"]));
16✔
3188
                auto nullable_float_set =
16✔
3189
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_float_set"]));
16✔
3190
                auto nullable_double_set =
16✔
3191
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_double_set"]));
16✔
3192
                auto nullable_decimal_set =
16✔
3193
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_decimal_set"]));
16✔
3194
                auto nullable_object_id_set =
16✔
3195
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_object_id_set"]));
16✔
3196
                auto nullable_uuid_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_uuid_set"]));
16✔
3197

10✔
3198
                write([&]() {
16✔
3199
                    CHECK(realm_set_insert(int_set.get(), integer, nullptr, nullptr));
15!
3200
                    CHECK(realm_set_insert(bool_set.get(), boolean, nullptr, nullptr));
16!
3201
                    CHECK(realm_set_insert(string_set.get(), string, nullptr, nullptr));
16!
3202
                    CHECK(realm_set_insert(binary_set.get(), binary, nullptr, nullptr));
16!
3203
                    CHECK(realm_set_insert(timestamp_set.get(), timestamp, nullptr, nullptr));
16!
3204
                    CHECK(realm_set_insert(float_set.get(), fnum, nullptr, nullptr));
16!
3205
                    CHECK(realm_set_insert(double_set.get(), dnum, nullptr, nullptr));
16!
3206
                    CHECK(realm_set_insert(decimal_set.get(), decimal, nullptr, nullptr));
15!
3207
                    CHECK(realm_set_insert(object_id_set.get(), object_id, nullptr, nullptr));
16!
3208
                    CHECK(realm_set_insert(uuid_set.get(), uuid, nullptr, nullptr));
16!
3209

10!
3210
                    CHECK(realm_set_insert(nullable_int_set.get(), integer, nullptr, nullptr));
16!
3211
                    CHECK(realm_set_insert(nullable_bool_set.get(), boolean, nullptr, nullptr));
15!
3212
                    CHECK(realm_set_insert(nullable_string_set.get(), string, nullptr, nullptr));
16!
3213
                    CHECK(realm_set_insert(nullable_binary_set.get(), binary, nullptr, nullptr));
16!
3214
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), timestamp, nullptr, nullptr));
16!
3215
                    CHECK(realm_set_insert(nullable_float_set.get(), fnum, nullptr, nullptr));
16!
3216
                    CHECK(realm_set_insert(nullable_double_set.get(), dnum, nullptr, nullptr));
16!
3217
                    CHECK(realm_set_insert(nullable_decimal_set.get(), decimal, nullptr, nullptr));
16!
3218
                    CHECK(realm_set_insert(nullable_object_id_set.get(), object_id, nullptr, nullptr));
15!
3219
                    CHECK(realm_set_insert(nullable_uuid_set.get(), uuid, nullptr, nullptr));
16!
3220

10!
3221
                    CHECK(realm_set_insert(nullable_int_set.get(), null, nullptr, nullptr));
16!
3222
                    CHECK(realm_set_insert(nullable_bool_set.get(), null, nullptr, nullptr));
15!
3223
                    CHECK(realm_set_insert(nullable_string_set.get(), null, nullptr, nullptr));
16!
3224
                    CHECK(realm_set_insert(nullable_binary_set.get(), null, nullptr, nullptr));
16!
3225
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), null, nullptr, nullptr));
16!
3226
                    CHECK(realm_set_insert(nullable_float_set.get(), null, nullptr, nullptr));
16!
3227
                    CHECK(realm_set_insert(nullable_double_set.get(), null, nullptr, nullptr));
16!
3228
                    CHECK(realm_set_insert(nullable_decimal_set.get(), null, nullptr, nullptr));
16!
3229
                    CHECK(realm_set_insert(nullable_object_id_set.get(), null, nullptr, nullptr));
15!
3230
                    CHECK(realm_set_insert(nullable_uuid_set.get(), null, nullptr, nullptr));
16!
3231
                });
16!
3232

10!
3233
                realm_value_t value;
16!
3234

9!
3235
                CHECK(realm_set_get(int_set.get(), 0, &value));
16!
3236
                CHECK(rlm_val_eq(value, integer));
15!
3237
                CHECK(realm_set_get(bool_set.get(), 0, &value));
16!
3238
                CHECK(rlm_val_eq(value, boolean));
16!
3239
                CHECK(realm_set_get(string_set.get(), 0, &value));
16!
3240
                CHECK(rlm_val_eq(value, string));
16!
3241
                CHECK(realm_set_get(binary_set.get(), 0, &value));
15!
3242
                CHECK(rlm_val_eq(value, binary));
16!
3243
                CHECK(realm_set_get(timestamp_set.get(), 0, &value));
15!
3244
                CHECK(rlm_val_eq(value, timestamp));
16!
3245
                CHECK(realm_set_get(float_set.get(), 0, &value));
16!
3246
                CHECK(rlm_val_eq(value, fnum));
16!
3247
                CHECK(realm_set_get(double_set.get(), 0, &value));
16!
3248
                CHECK(rlm_val_eq(value, dnum));
16!
3249
                CHECK(realm_set_get(decimal_set.get(), 0, &value));
16!
3250
                CHECK(rlm_val_eq(value, decimal));
16!
3251
                CHECK(realm_set_get(object_id_set.get(), 0, &value));
16!
3252
                CHECK(rlm_val_eq(value, object_id));
16!
3253
                CHECK(realm_set_get(uuid_set.get(), 0, &value));
16!
3254
                CHECK(rlm_val_eq(value, uuid));
16!
3255
                CHECK(realm_set_get(nullable_int_set.get(), 1, &value));
16!
3256
                CHECK(rlm_val_eq(value, integer));
16!
3257
                CHECK(realm_set_get(nullable_bool_set.get(), 1, &value));
16!
3258
                CHECK(rlm_val_eq(value, boolean));
16!
3259
                CHECK(realm_set_get(nullable_string_set.get(), 1, &value));
16!
3260
                CHECK(rlm_val_eq(value, string));
16!
3261
                CHECK(realm_set_get(nullable_binary_set.get(), 1, &value));
16!
3262
                CHECK(rlm_val_eq(value, binary));
16!
3263
                CHECK(realm_set_get(nullable_timestamp_set.get(), 1, &value));
16!
3264
                CHECK(rlm_val_eq(value, timestamp));
16!
3265
                CHECK(realm_set_get(nullable_float_set.get(), 1, &value));
16!
3266
                CHECK(rlm_val_eq(value, fnum));
16!
3267
                CHECK(realm_set_get(nullable_double_set.get(), 1, &value));
16!
3268
                CHECK(rlm_val_eq(value, dnum));
16!
3269
                CHECK(realm_set_get(nullable_decimal_set.get(), 1, &value));
16!
3270
                CHECK(rlm_val_eq(value, decimal));
16!
3271
                CHECK(realm_set_get(nullable_object_id_set.get(), 1, &value));
16!
3272
                CHECK(rlm_val_eq(value, object_id));
16!
3273
                CHECK(realm_set_get(nullable_uuid_set.get(), 1, &value));
16!
3274
                CHECK(rlm_val_eq(value, uuid));
16!
3275

10!
3276
                write([&]() {
16!
3277
                    size_t index;
15!
3278
                    bool inserted;
16!
3279
                    CHECK(realm_set_insert(nullable_int_set.get(), null, &index, &inserted));
16!
3280
                    CHECK((index == 0 && !inserted));
16!
3281
                    CHECK(realm_set_insert(nullable_bool_set.get(), null, &index, &inserted));
16!
3282
                    CHECK((index == 0 && !inserted));
16!
3283
                    CHECK(realm_set_insert(nullable_string_set.get(), null, &index, &inserted));
16!
3284
                    CHECK((index == 0 && !inserted));
15!
3285
                    CHECK(realm_set_insert(nullable_binary_set.get(), null, &index, &inserted));
16!
3286
                    CHECK((index == 0 && !inserted));
16!
3287
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), null, &index, &inserted));
16!
3288
                    CHECK((index == 0 && !inserted));
16!
3289
                    CHECK(realm_set_insert(nullable_float_set.get(), null, &index, &inserted));
16!
3290
                    CHECK((index == 0 && !inserted));
16!
3291
                    CHECK(realm_set_insert(nullable_double_set.get(), null, &index, &inserted));
16!
3292
                    CHECK((index == 0 && !inserted));
16!
3293
                    CHECK(realm_set_insert(nullable_decimal_set.get(), null, &index, &inserted));
16!
3294
                    CHECK((index == 0 && !inserted));
16!
3295
                    CHECK(realm_set_insert(nullable_object_id_set.get(), null, &index, &inserted));
16!
3296
                    CHECK((index == 0 && !inserted));
16!
3297
                    CHECK(realm_set_insert(nullable_uuid_set.get(), null, &index, &inserted));
16!
3298
                    CHECK((index == 0 && !inserted));
16!
3299
                });
16!
3300

10!
3301
                // Note: This relies on the fact that NULL is "less than" other
10!
3302
                // values in the internal sort order.
9!
3303
                CHECK(realm_set_get(nullable_int_set.get(), 0, &value));
15!
3304
                CHECK(rlm_val_eq(value, null));
15!
3305
                CHECK(realm_set_get(nullable_bool_set.get(), 0, &value));
16!
3306
                CHECK(rlm_val_eq(value, null));
16!
3307
                CHECK(realm_set_get(nullable_string_set.get(), 0, &value));
16!
3308
                CHECK(rlm_val_eq(value, null));
16!
3309
                CHECK(realm_set_get(nullable_binary_set.get(), 0, &value));
15!
3310
                CHECK(rlm_val_eq(value, null));
15!
3311
                CHECK(realm_set_get(nullable_timestamp_set.get(), 0, &value));
15!
3312
                CHECK(rlm_val_eq(value, null));
16!
3313
                CHECK(realm_set_get(nullable_float_set.get(), 0, &value));
16!
3314
                CHECK(rlm_val_eq(value, null));
16!
3315
                CHECK(realm_set_get(nullable_double_set.get(), 0, &value));
16!
3316
                CHECK(rlm_val_eq(value, null));
16!
3317
                CHECK(realm_set_get(nullable_decimal_set.get(), 0, &value));
16!
3318
                CHECK(rlm_val_eq(value, null));
16!
3319
                CHECK(realm_set_get(nullable_object_id_set.get(), 0, &value));
16!
3320
                CHECK(rlm_val_eq(value, null));
16!
3321
                CHECK(realm_set_get(nullable_uuid_set.get(), 0, &value));
16!
3322
                CHECK(rlm_val_eq(value, null));
16!
3323
            }
16!
3324

82!
3325
            SECTION("links") {
160!
3326
                CPtr<realm_set_t> bars;
63!
3327

52!
3328
                write([&]() {
58!
3329
                    bars = cptr_checked(realm_get_set(obj1.get(), foo_properties["link_set"]));
54!
3330
                    auto bar_link = realm_object_as_link(obj2.get());
58!
3331
                    realm_value_t bar_link_val;
58!
3332
                    bar_link_val.type = RLM_TYPE_LINK;
58✔
3333
                    bar_link_val.link = bar_link;
69✔
3334
                    size_t index;
82✔
3335
                    bool inserted;
64✔
3336
                    CHECK(checked(realm_set_insert(bars.get(), bar_link_val, &index, &inserted)));
60!
3337
                    CHECK((index == 0 && inserted));
64!
3338
                    CHECK(checked(realm_set_insert(bars.get(), bar_link_val, &index, &inserted)));
64!
3339
                    CHECK((index == 0 && !inserted));
64!
3340
                    size_t size;
64!
3341
                    CHECK(checked(realm_set_size(bars.get(), &size)));
64!
3342
                    CHECK(size == 1);
64!
3343

40!
3344
                    auto results =
64!
3345
                        cptr_checked(realm_get_backlinks(obj2.get(), class_foo.key, foo_properties["link_set"]));
60!
3346
                    CHECK(results->size() == 1);
64!
3347
                    auto mixed_link = results->get_any(0);
64!
3348
                    CHECK(!mixed_link.is_unresolved_link());
64!
3349
                    CHECK(mixed_link.is_type(type_TypedLink));
64!
3350
                    auto link = mixed_link.get_link();
64!
3351
                    CHECK(link.get_obj_key() == obj1->get_obj().get_key());
64!
3352
                    CHECK(link.get_table_key() == obj1->get_obj().get_table()->get_key());
60!
3353
                });
64!
3354

40!
3355
                SECTION("get") {
64!
3356
                    realm_value_t val;
24✔
3357
                    CHECK(checked(realm_set_get(bars.get(), 0, &val)));
28!
3358
                    CHECK(val.type == RLM_TYPE_LINK);
22!
3359
                    CHECK(val.link.target_table == class_bar.key);
22!
3360
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
22!
3361

16!
3362
                    auto result = realm_set_get(bars.get(), 1, &val);
22!
3363
                    CHECK(!result);
17!
3364
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
22!
3365
                }
16!
3366

28!
3367
                SECTION("insert wrong type") {
52!
3368
                    write([&]() {
18!
3369
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
22!
3370
                        CHECK(foo2);
15!
3371
                        realm_value_t foo2_link_val;
16✔
3372
                        foo2_link_val.type = RLM_TYPE_LINK;
16!
3373
                        foo2_link_val.link = realm_object_as_link(foo2.get());
16!
3374

10✔
3375
                        CHECK(!realm_set_insert(bars.get(), foo2_link_val, nullptr, nullptr));
18!
3376
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
21!
3377
                    });
16!
3378
                }
16!
3379

28!
3380
                SECTION("realm_set_clear()") {
52✔
3381
                    write([&]() {
18✔
3382
                        CHECK(realm_set_clear(bars.get()));
22!
3383
                    });
15✔
3384
                    size_t size;
16!
3385
                    CHECK(realm_set_size(bars.get(), &size));
16!
3386
                    CHECK(size == 0);
16!
3387

10!
3388
                    size_t num_bars;
18!
3389
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
21!
3390
                    CHECK(num_bars != 0);
16!
3391
                }
16!
3392

28!
3393
                SECTION("realm_set_remove_all()") {
52✔
3394
                    realm_value_t val;
18!
3395
                    CHECK(checked(realm_set_get(bars.get(), 0, &val)));
22!
3396
                    CHECK(val.type == RLM_TYPE_LINK);
15!
3397
                    CHECK(val.link.target_table == class_bar.key);
16!
3398
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
16!
3399

10!
3400
                    size_t num_bars;
16!
3401
                    size_t size;
17✔
3402

16✔
3403
                    write([&]() {
16✔
3404
                        CHECK(checked(realm_set_remove_all(bars.get())));
15!
3405
                    });
16!
3406

10!
3407
                    CHECK(realm_set_size(bars.get(), &size));
16!
3408
                    CHECK(size == 0);
14!
3409

10!
3410
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
16!
3411
                    CHECK(num_bars == 0);
14!
3412
                }
16!
3413
            }
52!
3414

82✔
3415
            SECTION("notifications") {
165✔
3416
                struct State {
39!
3417
                    CPtr<realm_collection_changes_t> changes;
52!
3418
                    CPtr<realm_async_error_t> error;
29✔
3419
                    bool destroyed = false;
30!
3420
                };
30!
3421

18✔
3422
                State state;
36✔
3423

27✔
3424
                auto on_change = [](void* userdata, const realm_collection_changes_t* changes) {
66✔
3425
                    auto* state = static_cast<State*>(userdata);
42✔
3426
                    state->changes = clone_cptr(changes);
46✔
3427
                };
46✔
3428

22✔
3429
                CPtr<realm_set_t> strings =
34✔
3430
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
28✔
3431

20✔
3432
                auto str1 = rlm_str_val("a");
30✔
3433
                auto str2 = rlm_str_val("b");
32✔
3434
                auto null = rlm_null();
34✔
3435

22✔
3436
                auto require_change = [&]() {
28✔
3437
                    auto token = cptr_checked(
16✔
3438
                        realm_set_add_notification_callback(strings.get(), &state, nullptr, nullptr, on_change));
19✔
3439
                    checked(realm_refresh(realm, nullptr));
18✔
3440
                    return token;
16✔
3441
                };
18✔
3442

18✔
3443
                SECTION("userdata is freed when the token is destroyed") {
30✔
3444
                    auto token = cptr_checked(realm_set_add_notification_callback(
16✔
3445
                        strings.get(), &state,
19✔
3446
                        [](void* p) {
16✔
3447
                            static_cast<State*>(p)->destroyed = true;
16✔
3448
                        },
16✔
3449
                        nullptr, nullptr));
16✔
3450
                    CHECK(!state.destroyed);
16!
3451
                    token.reset();
16✔
3452
                    CHECK(state.destroyed);
18!
3453
                }
16✔
3454

16!
3455
                SECTION("insertion,deletion sends a change callback") {
28✔
3456
                    write([&]() {
16✔
3457
                        checked(realm_set_insert(strings.get(), str1, nullptr, nullptr));
18✔
3458
                    });
16✔
3459

10!
3460
                    auto token = require_change();
16✔
3461
                    write([&]() {
15!
3462
                        checked(realm_set_erase(strings.get(), str1, nullptr));
16✔
3463
                        checked(realm_set_insert(strings.get(), str2, nullptr, nullptr));
16✔
3464
                        checked(realm_set_insert(strings.get(), null, nullptr, nullptr));
18✔
3465
                    });
16✔
3466
                    CHECK(!state.error);
16!
3467
                    CHECK(state.changes);
16!
3468

9!
3469
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
16!
3470
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
15✔
3471
                                                            &num_insertion_ranges, &num_modification_ranges,
16✔
3472
                                                            &num_moves);
16✔
3473
                    CHECK(num_deletion_ranges == 1);
16!
3474
                    CHECK(num_insertion_ranges == 1);
16!
3475
                    CHECK(num_modification_ranges == 0);
16!
3476
                    CHECK(num_moves == 0);
16!
3477

9!
3478
                    realm_index_range_t insertion_range, deletion_range;
16!
3479
                    realm_collection_changes_get_ranges(state.changes.get(), &deletion_range, 1, &insertion_range, 1,
15✔
3480
                                                        nullptr, 0, nullptr, 0, nullptr, 0);
16✔
3481
                    CHECK(deletion_range.from == 0);
16!
3482
                    CHECK(deletion_range.to == 1);
16!
3483
                    CHECK(insertion_range.from == 0);
16!
3484
                    CHECK(insertion_range.to == 2);
16!
3485

10!
3486
                    write([&]() {
15!
3487
                        checked(realm_set_clear(strings.get()));
15✔
3488
                    });
16✔
3489

10✔
3490
                    size_t num_deletions, num_insertions, num_modifications;
16!
3491
                    bool collection_cleared = false;
15!
3492
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
16!
3493
                                                             &num_modifications, &num_moves, &collection_cleared,
16!
3494
                                                             nullptr);
15✔
3495
                    CHECK(collection_cleared == true);
16!
3496
                }
16✔
3497
            }
28!
3498
        }
159✔
3499

402✔
3500
        SECTION("dictionaries") {
820✔
3501
            SECTION("realm_get_dictionary() errors") {
236✔
3502
                CHECK(!realm_get_dictionary(obj1.get(), foo_properties["int"]));
146!
3503
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
42!
3504

10!
3505
                CHECK(!realm_get_dictionary(obj1.get(), 123123123));
16!
3506
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
17!
3507
            }
40!
3508

152!
3509
            SECTION("nullable strings") {
302✔
3510
                auto strings = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
90✔
3511
                CHECK(strings);
78!
3512
                CHECK(!realm_is_frozen(strings.get()));
58!
3513

33!
3514
                realm_value_t a = rlm_str_val("a");
58!
3515
                realm_value_t b = rlm_str_val("b");
54!
3516
                realm_value_t c = rlm_null();
58✔
3517
                realm_value_t key_a = rlm_str_val("key_a");
70✔
3518
                realm_value_t key_b = rlm_str_val("key_b");
84✔
3519
                realm_value_t key_c = rlm_str_val("key_c");
64✔
3520

40!
3521
                SECTION("realm_equals() type check") {
64!
3522
                    CHECK(!realm_equals(strings.get(), obj1.get()));
20!
3523
                }
28✔
3524

34!
3525
                SECTION("realm_clone()") {
58✔
3526
                    auto dict2 = clone_cptr(strings.get());
24✔
3527
                    CHECK(realm_equals(strings.get(), dict2.get()));
28!
3528
                    CHECK(strings.get() != dict2.get());
22!
3529
                }
18!
3530

34!
3531
                SECTION("insert, then get, then erase") {
52!
3532
                    write([&]() {
18✔
3533
                        bool inserted = false;
24✔
3534
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_a, a, nullptr, &inserted)));
22!
3535
                        CHECK(inserted);
16!
3536
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_b, b, nullptr, &inserted)));
16!
3537
                        CHECK(inserted);
16!
3538
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_c, c, nullptr, &inserted)));
16!
3539
                        CHECK(inserted);
18!
3540

16!
3541
                        realm_value_t a2, b2, c2;
16!
3542
                        bool found = false;
15✔
3543
                        CHECK(checked(realm_dictionary_find(strings.get(), key_a, &a2, &found)));
16!
3544
                        CHECK(found);
16!
3545
                        CHECK(checked(realm_dictionary_find(strings.get(), key_b, &b2, &found)));
16!
3546
                        CHECK(found);
16!
3547
                        CHECK(checked(realm_dictionary_find(strings.get(), key_c, &c2, &found)));
16!
3548
                        CHECK(found);
16!
3549

9!
3550
                        CHECK(rlm_stdstr(a2) == "a");
16!
3551
                        CHECK(rlm_stdstr(b2) == "b");
15!
3552
                        CHECK(c2.type == RLM_TYPE_NULL);
16!
3553

10!
3554
                        bool erased = false;
16!
3555
                        CHECK(checked(realm_dictionary_erase(strings.get(), key_a, &erased)));
15!
3556
                        CHECK(erased);
16!
3557
                        CHECK(checked(realm_dictionary_erase(strings.get(), rlm_int_val(987), &erased)));
16!
3558
                        CHECK(!erased);
15!
3559
                    });
16!
3560
                }
16!
3561

28!
3562
                SECTION("equality") {
51✔
3563
                    auto strings2 =
18✔
3564
                        cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
22!
3565
                    CHECK(strings2);
16!
3566
                    CHECK(realm_equals(strings.get(), strings2.get()));
16!
3567

10!
3568
                    write([&]() {
16!
3569
                        auto obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
15✔
3570
                        CHECK(obj3);
18!
3571
                        auto strings3 =
22✔
3572
                            cptr_checked(realm_get_dictionary(obj3.get(), foo_properties["nullable_string_dict"]));
16!
3573
                        CHECK(!realm_equals(strings.get(), strings3.get()));
16!
3574
                    });
16!
3575
                }
16!
3576
            }
51✔
3577

88✔
3578
            SECTION("get/insert all property types") {
178✔
3579
                realm_value_t key = rlm_str_val("k");
28!
3580
                realm_value_t key2 = rlm_str_val("k2");
42✔
3581

10✔
3582
                realm_value_t null = rlm_null();
16!
3583
                realm_value_t integer = rlm_int_val(987);
15✔
3584
                realm_value_t boolean = rlm_bool_val(true);
16✔
3585
                realm_value_t string = rlm_str_val("My string");
22✔
3586
                const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
28✔
3587
                realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
42✔
3588
                realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
16✔
3589
                realm_value_t fnum = rlm_float_val(123.f);
16✔
3590
                realm_value_t dnum = rlm_double_val(456.0);
15✔
3591
                realm_value_t decimal = rlm_decimal_val(999.0);
16✔
3592
                realm_value_t object_id = rlm_object_id_val("abc123abc123");
16✔
3593
                realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16✔
3594

10✔
3595
                auto int_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["int_dict"]));
16✔
3596
                auto bool_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["bool_dict"]));
15✔
3597
                auto string_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["string_dict"]));
16✔
3598
                auto binary_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["binary_dict"]));
16✔
3599
                auto timestamp_dict =
16✔
3600
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["timestamp_dict"]));
16✔
3601
                auto float_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["float_dict"]));
16✔
3602
                auto double_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["double_dict"]));
16✔
3603
                auto decimal_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["decimal_dict"]));
15✔
3604
                auto object_id_dict =
16✔
3605
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["object_id_dict"]));
16✔
3606
                auto uuid_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["uuid_dict"]));
16✔
3607
                auto nullable_int_dict =
16✔
3608
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_int_dict"]));
16✔
3609
                auto nullable_bool_dict =
16✔
3610
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_bool_dict"]));
16✔
3611
                auto nullable_string_dict =
16✔
3612
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
16✔
3613
                auto nullable_binary_dict =
16✔
3614
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_binary_dict"]));
16✔
3615
                auto nullable_timestamp_dict =
16✔
3616
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_timestamp_dict"]));
16✔
3617
                auto nullable_float_dict =
16✔
3618
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_float_dict"]));
16✔
3619
                auto nullable_double_dict =
16✔
3620
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_double_dict"]));
16✔
3621
                auto nullable_decimal_dict =
16✔
3622
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_decimal_dict"]));
16✔
3623
                auto nullable_object_id_dict =
16✔
3624
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_object_id_dict"]));
16✔
3625
                auto nullable_uuid_dict =
16✔
3626
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_uuid_dict"]));
16✔
3627

10✔
3628
                write([&]() {
16✔
3629
                    size_t index;
15✔
3630
                    bool inserted;
16✔
3631
                    CHECK(!realm_dictionary_insert(int_dict.get(), rlm_int_val(987), integer, &index, &inserted));
16!
3632

10✔
3633
                    CHECK(realm_dictionary_insert(int_dict.get(), key, integer, &index, &inserted));
16!
3634
                    CHECK((inserted && index == 0));
15!
3635
                    CHECK(realm_dictionary_insert(bool_dict.get(), key, boolean, &index, &inserted));
16!
3636
                    CHECK((inserted && index == 0));
15!
3637
                    CHECK(realm_dictionary_insert(string_dict.get(), key, string, &index, &inserted));
16!
3638
                    CHECK((inserted && index == 0));
16!
3639
                    CHECK(realm_dictionary_insert(binary_dict.get(), key, binary, &index, &inserted));
16!
3640
                    CHECK((inserted && index == 0));
16!
3641
                    CHECK(realm_dictionary_insert(timestamp_dict.get(), key, timestamp, &index, &inserted));
15!
3642
                    CHECK((inserted && index == 0));
16!
3643
                    CHECK(realm_dictionary_insert(float_dict.get(), key, fnum, &index, &inserted));
16!
3644
                    CHECK((inserted && index == 0));
16!
3645
                    CHECK(realm_dictionary_insert(double_dict.get(), key, dnum, &index, &inserted));
16!
3646
                    CHECK((inserted && index == 0));
16!
3647
                    CHECK(realm_dictionary_insert(decimal_dict.get(), key, decimal, &index, &inserted));
16!
3648
                    CHECK((inserted && index == 0));
16!
3649
                    CHECK(realm_dictionary_insert(object_id_dict.get(), key, object_id, &index, &inserted));
16!
3650
                    CHECK((inserted && index == 0));
16!
3651
                    CHECK(realm_dictionary_insert(uuid_dict.get(), key, uuid, &index, &inserted));
16!
3652
                    CHECK((inserted && index == 0));
16!
3653

10!
3654
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key, integer, &index, &inserted));
16!
3655
                    CHECK((inserted && index == 0));
15!
3656
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key, boolean, &index, &inserted));
16!
3657
                    CHECK((inserted && index == 0));
16!
3658
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key, string, &index, &inserted));
16!
3659
                    CHECK((inserted && index == 0));
16!
3660
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key, binary, &index, &inserted));
16!
3661
                    CHECK((inserted && index == 0));
16!
3662
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key, timestamp, &index, &inserted));
15!
3663
                    CHECK((inserted && index == 0));
16!
3664
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key, fnum, &index, &inserted));
16!
3665
                    CHECK((inserted && index == 0));
16!
3666
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key, dnum, &index, &inserted));
16!
3667
                    CHECK((inserted && index == 0));
16!
3668
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key, decimal, &index, &inserted));
16!
3669
                    CHECK((inserted && index == 0));
16!
3670
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key, object_id, &index, &inserted));
16!
3671
                    CHECK((inserted && index == 0));
16!
3672
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key, uuid, &index, &inserted));
16!
3673
                    CHECK((inserted && index == 0));
16!
3674

10!
3675
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key2, null, &index, &inserted));
16!
3676
                    CHECK((inserted && index == 1));
15!
3677
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key2, null, &index, &inserted));
16!
3678
                    CHECK((inserted && index == 1));
16!
3679
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key2, null, &index, &inserted));
16!
3680
                    CHECK((inserted && index == 1));
16!
3681
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key2, null, &index, &inserted));
16!
3682
                    CHECK((inserted && index == 1));
16!
3683
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key2, null, &index, &inserted));
15!
3684
                    CHECK((inserted && index == 1));
16!
3685
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key2, null, &index, &inserted));
16!
3686
                    CHECK((inserted && index == 1));
16!
3687
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key2, null, &index, &inserted));
16!
3688
                    CHECK((inserted && index == 1));
16!
3689
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key2, null, &index, &inserted));
16!
3690
                    CHECK((inserted && index == 1));
16!
3691
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key2, null, &index, &inserted));
16!
3692
                    CHECK((inserted && index == 1));
16!
3693
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key2, null, &index, &inserted));
16!
3694
                    CHECK((inserted && index == 1));
16!
3695
                });
16!
3696

10!
3697
                realm_value_t k, value;
16!
3698

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

10!
3760
                write([&]() {
16!
3761
                    size_t index;
15!
3762
                    bool inserted;
16!
3763
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key2, null, &index, &inserted));
16!
3764
                    CHECK(!inserted);
16!
3765
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key2, null, &index, &inserted));
16!
3766
                    CHECK(!inserted);
16!
3767
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key2, null, &index, &inserted));
16!
3768
                    CHECK(!inserted);
15!
3769
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key2, null, &index, &inserted));
16!
3770
                    CHECK(!inserted);
16!
3771
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key2, null, &index, &inserted));
16!
3772
                    CHECK(!inserted);
16!
3773
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key2, null, &index, &inserted));
16!
3774
                    CHECK(!inserted);
16!
3775
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key2, null, &index, &inserted));
16!
3776
                    CHECK(!inserted);
16!
3777
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key2, null, &index, &inserted));
16!
3778
                    CHECK(!inserted);
16!
3779
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key2, null, &index, &inserted));
16!
3780
                    CHECK(!inserted);
16!
3781
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key2, null, &index, &inserted));
16!
3782
                    CHECK(!inserted);
16!
3783
                });
16!
3784

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

10!
3850
                CHECK(realm_dictionary_find(nullable_int_dict.get(), key2, &value, &found));
16!
3851
                CHECK(found);
15!
3852
                CHECK(rlm_val_eq(value, null));
16!
3853
                CHECK(realm_dictionary_find(nullable_bool_dict.get(), key2, &value, &found));
16!
3854
                CHECK(found);
16!
3855
                CHECK(rlm_val_eq(value, null));
16!
3856
                CHECK(realm_dictionary_find(nullable_string_dict.get(), key2, &value, &found));
16!
3857
                CHECK(found);
16!
3858
                CHECK(rlm_val_eq(value, null));
15!
3859
                CHECK(realm_dictionary_find(nullable_binary_dict.get(), key2, &value, &found));
16!
3860
                CHECK(found);
16!
3861
                CHECK(rlm_val_eq(value, null));
16!
3862
                CHECK(realm_dictionary_find(nullable_timestamp_dict.get(), key2, &value, &found));
16!
3863
                CHECK(found);
16!
3864
                CHECK(rlm_val_eq(value, null));
16!
3865
                CHECK(realm_dictionary_find(nullable_float_dict.get(), key2, &value, &found));
16!
3866
                CHECK(found);
16!
3867
                CHECK(rlm_val_eq(value, null));
16!
3868
                CHECK(realm_dictionary_find(nullable_double_dict.get(), key2, &value, &found));
16!
3869
                CHECK(found);
16!
3870
                CHECK(rlm_val_eq(value, null));
16!
3871
                CHECK(realm_dictionary_find(nullable_decimal_dict.get(), key2, &value, &found));
16!
3872
                CHECK(found);
16!
3873
                CHECK(rlm_val_eq(value, null));
16!
3874
                CHECK(realm_dictionary_find(nullable_object_id_dict.get(), key2, &value, &found));
16!
3875
                CHECK(found);
16!
3876
                CHECK(rlm_val_eq(value, null));
16!
3877
                CHECK(realm_dictionary_find(nullable_uuid_dict.get(), key2, &value, &found));
16!
3878
                CHECK(found);
16!
3879
                CHECK(rlm_val_eq(value, null));
16!
3880
            }
16!
3881

88!
3882
            SECTION("links") {
172!
3883
                CPtr<realm_dictionary_t> bars;
52!
3884
                realm_value_t key = rlm_str_val("k");
66!
3885

26!
3886
                write([&]() {
44!
3887
                    bars = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["link_dict"]));
41!
3888
                    auto bar_link = realm_object_as_link(obj2.get());
44!
3889
                    realm_value_t bar_link_val;
44✔
3890
                    bar_link_val.type = RLM_TYPE_LINK;
56✔
3891
                    bar_link_val.link = bar_link;
70✔
3892
                    size_t index;
48✔
3893
                    bool inserted;
48✔
3894
                    CHECK(checked(realm_dictionary_insert(bars.get(), key, bar_link_val, &index, &inserted)));
45!
3895
                    CHECK((index == 0 && inserted));
48!
3896
                    CHECK(checked(realm_dictionary_insert(bars.get(), key, bar_link_val, &index, &inserted)));
48!
3897
                    CHECK((index == 0 && !inserted));
48!
3898
                    size_t size;
48!
3899
                    CHECK(checked(realm_dictionary_size(bars.get(), &size)));
48!
3900
                    CHECK(size == 1);
48!
3901
                });
48!
3902

30!
3903
                SECTION("get") {
48!
3904
                    realm_value_t k, val;
21!
3905
                    CHECK(checked(realm_dictionary_get(bars.get(), 0, &k, &val)));
24!
3906
                    CHECK(rlm_val_eq(k, key));
20!
3907
                    CHECK(val.type == RLM_TYPE_LINK);
20!
3908
                    CHECK(val.link.target_table == class_bar.key);
20!
3909
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
20!
3910

14!
3911
                    auto result = realm_dictionary_get(bars.get(), 1, &k, &val);
17!
3912
                    CHECK(!result);
19!
3913
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
16!
3914
                }
16!
3915

22!
3916
                SECTION("insert wrong type") {
40!
3917
                    write([&]() {
17!
3918
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
20!
3919
                        CHECK(foo2);
15!
3920
                        realm_value_t foo2_link_val;
16✔
3921
                        foo2_link_val.type = RLM_TYPE_LINK;
16!
3922
                        foo2_link_val.link = realm_object_as_link(foo2.get());
16!
3923

10✔
3924
                        CHECK(!realm_dictionary_insert(bars.get(), key, foo2_link_val, nullptr, nullptr));
17!
3925
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
19!
3926
                    });
16!
3927
                }
16!
3928

22!
3929
                SECTION("realm_dictionary_clear()") {
40✔
3930
                    write([&]() {
17✔
3931
                        CHECK(realm_dictionary_clear(bars.get()));
20!
3932
                    });
15✔
3933
                    size_t size;
16!
3934
                    CHECK(realm_dictionary_size(bars.get(), &size));
16!
3935
                    CHECK(size == 0);
16!
3936

10!
3937
                    size_t num_bars;
17!
3938
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
19!
3939
                    CHECK(num_bars != 0);
16!
3940
                }
16!
3941
            }
40!
3942

88✔
3943
            SECTION("notifications") {
176!
3944
                struct State {
40!
3945
                    CPtr<realm_collection_changes_t> changes;
53✔
3946
                    CPtr<realm_dictionary_changes_t> dictionary_changes;
30✔
3947
                    CPtr<realm_async_error_t> error;
30!
3948
                    bool destroyed = false;
30!
3949
                };
30✔
3950

22✔
3951
                State state;
42✔
3952
                auto on_dictionary_change = [](void* userdata, const realm_dictionary_changes_t* changes) {
66✔
3953
                    auto* state = static_cast<State*>(userdata);
44✔
3954
                    state->dictionary_changes = clone_cptr(changes);
46✔
3955
                };
46✔
3956

22✔
3957
                CPtr<realm_dictionary_t> strings =
34✔
3958
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
30✔
3959

18✔
3960
                auto str1 = rlm_str_val("a");
32✔
3961
                auto str2 = rlm_str_val("b");
32✔
3962
                auto null = rlm_null();
34✔
3963

22✔
3964

22✔
3965
                auto require_change = [&]() {
22✔
3966
                    auto token = cptr_checked(realm_dictionary_add_notification_callback(
18✔
3967
                        strings.get(), &state, nullptr, nullptr, on_dictionary_change));
19✔
3968
                    checked(realm_refresh(realm, nullptr));
16✔
3969
                    return token;
18✔
3970
                };
18✔
3971

18✔
3972
                SECTION("userdata is freed when the token is destroyed") {
28✔
3973
                    auto token = cptr_checked(realm_dictionary_add_notification_callback(
16✔
3974
                        strings.get(), &state,
19✔
3975
                        [](void* p) {
16✔
3976
                            static_cast<State*>(p)->destroyed = true;
16✔
3977
                        },
16✔
3978
                        nullptr, nullptr));
16✔
3979
                    CHECK(!state.destroyed);
16!
3980
                    token.reset();
16✔
3981
                    CHECK(state.destroyed);
18!
3982
                }
16✔
3983

16!
3984
                SECTION("insertion, deletions sends a change callback") {
28✔
3985
                    write([&]() {
16✔
3986
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("a"), str1, nullptr, nullptr));
18✔
3987
                    });
16✔
3988
                    auto token = require_change();
16!
3989
                    write([&]() {
16✔
3990
                        checked(realm_dictionary_erase(strings.get(), rlm_str_val("a"), nullptr));
16!
3991
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("b"), str2, nullptr, nullptr));
16✔
3992
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("c"), null, nullptr, nullptr));
16✔
3993
                    });
18✔
3994
                    CHECK(!state.error);
16!
3995
                    CHECK(state.dictionary_changes);
16!
3996

10!
3997
                    size_t num_deletions, num_insertions, num_modifications;
16!
3998
                    realm_dictionary_get_changes(state.dictionary_changes.get(), &num_deletions, &num_insertions,
15✔
3999
                                                 &num_modifications, nullptr);
16✔
4000
                    CHECK(num_deletions == 1);
16!
4001
                    CHECK(num_insertions == 2);
16!
4002
                    CHECK(num_modifications == 0);
16!
4003
                    realm_value_t *deletions = nullptr, *insertions = nullptr, *modifications = nullptr;
16!
4004
                    bool collection_cleared = false;
16!
4005
                    deletions = (realm_value_t*)malloc(sizeof(realm_value_t) * num_deletions);
15✔
4006
                    insertions = (realm_value_t*)malloc(sizeof(realm_value_t) * num_insertions);
16✔
4007
                    realm_dictionary_get_changed_keys(state.dictionary_changes.get(), deletions, &num_deletions,
16✔
4008
                                                      insertions, &num_insertions, modifications, &num_modifications,
16✔
4009
                                                      &collection_cleared);
16!
4010
                    CHECK(deletions != nullptr);
16!
4011
                    CHECK(insertions != nullptr);
16!
4012
                    CHECK(modifications == nullptr);
16!
4013
                    realm_free(deletions);
16!
4014
                    realm_free(insertions);
16!
4015
                    realm_free(modifications);
16✔
4016

10✔
4017
                    write([&]() {
16✔
4018
                        checked(realm_dictionary_clear(strings.get()));
15✔
4019
                    });
16!
4020
                    realm_dictionary_get_changed_keys(state.dictionary_changes.get(), deletions, &num_deletions,
16!
4021
                                                      insertions, &num_insertions, modifications, &num_modifications,
16!
4022
                                                      &collection_cleared);
16✔
4023
                    CHECK(collection_cleared == true);
16!
4024
                }
16✔
4025
            }
27!
4026

88✔
4027
            SECTION("realm_dictionary_content_checks") {
174✔
4028
                auto ints = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["int_dict"]));
52✔
4029
                CHECK(ints);
66!
4030
                CHECK(!realm_is_frozen(ints.get()));
44!
4031
                realm_value_t key1 = rlm_str_val("k");
44!
4032
                realm_value_t key2 = rlm_str_val("k2");
44!
4033
                realm_value_t integer1 = rlm_int_val(987);
44✔
4034
                realm_value_t integer2 = rlm_int_val(988);
46✔
4035

38✔
4036
                write([&]() {
70✔
4037
                    bool inserted = false;
45✔
4038
                    CHECK(checked(realm_dictionary_insert(ints.get(), key1, integer1, nullptr, &inserted)));
48!
4039
                    CHECK(inserted);
48!
4040
                    CHECK(checked(realm_dictionary_insert(ints.get(), key2, integer2, nullptr, &inserted)));
48!
4041
                    CHECK(inserted);
48!
4042
                });
48!
4043

30!
4044
                SECTION("realm_dictionary_get_keys") {
45✔
4045
                    size_t size = 0;
21✔
4046
                    realm_results_t* keys = nullptr;
24✔
4047
                    CHECK(checked(realm_dictionary_get_keys(ints.get(), &size, &keys)));
20!
4048
                    CHECK(keys);
20!
4049
                    CHECK((*keys).size() == size);
20!
4050
                    realm_release(keys);
20!
4051
                }
20!
4052

23✔
4053
                SECTION("realm_dictionary_contains_key") {
44✔
4054
                    bool found = false;
17✔
4055
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key1, &found)));
20!
4056
                    CHECK(found);
16!
4057
                    found = false;
16!
4058
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key2, &found)));
16!
4059
                    CHECK(found);
16!
4060
                    realm_value_t key_no_present = rlm_str_val("kkkk");
16!
4061
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key_no_present, &found)));
17!
4062
                    CHECK(!found);
20!
4063
                }
16!
4064

22!
4065
                SECTION("realm_dictionary_contains_value") {
40!
4066
                    size_t index = -1;
17✔
4067
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer1, &index)));
20!
4068
                    CHECK(index == 0);
16!
4069
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer2, &index)));
16!
4070
                    CHECK(index == 1);
16!
4071
                    realm_value_t integer_no_present = rlm_int_val(678);
16!
4072
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer_no_present, &index)));
16!
4073
                    CHECK(index == realm::npos);
17!
4074
                }
20!
4075
            }
40!
4076
        }
172!
4077

404!
4078
        SECTION("notifications") {
822!
4079
            struct State {
104!
4080
                CPtr<realm_object_changes_t> changes;
170✔
4081
                CPtr<realm_async_error_t> error;
44!
4082
                bool called;
44!
4083
            };
44✔
4084

30✔
4085
            State state;
70✔
4086

87✔
4087
            auto on_change = [](void* userdata, const realm_object_changes_t* changes) {
210✔
4088
                auto state = static_cast<State*>(userdata);
81✔
4089
                state->changes = clone_cptr(changes);
90✔
4090
                state->called = true;
90✔
4091
            };
90✔
4092

36✔
4093
            auto require_change = [&]() {
45✔
4094
                auto token =
33✔
4095
                    cptr(realm_object_add_notification_callback(obj1.get(), &state, nullptr, nullptr, on_change));
32✔
4096
                checked(realm_refresh(realm, nullptr));
40✔
4097
                return token;
40✔
4098
            };
40✔
4099

34✔
4100
            SECTION("deleting the object sends a change notification") {
52✔
4101
                auto token = require_change();
18✔
4102
                write([&]() {
23✔
4103
                    checked(realm_object_delete(obj1.get()));
18✔
4104
                });
18✔
4105
                CHECK(!state.error);
18!
4106
                CHECK(state.changes);
18!
4107
                bool deleted = realm_object_changes_is_deleted(state.changes.get());
18!
4108
                CHECK(deleted);
17!
4109
            }
20✔
4110

22!
4111
            SECTION("modifying the object sends a change notification for the object, and for the changed column") {
40✔
4112
                auto token = require_change();
17✔
4113
                write([&]() {
20✔
4114
                    checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
16!
4115
                    checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("aaa"), false));
16!
4116
                });
16✔
4117
                CHECK(!state.error);
16!
4118
                CHECK(state.changes);
16!
4119
                bool deleted = realm_object_changes_is_deleted(state.changes.get());
17!
4120
                CHECK(!deleted);
20!
4121
                size_t num_modified = realm_object_changes_get_num_modified_properties(state.changes.get());
16✔
4122
                CHECK(num_modified == 2);
16!
4123
                realm_property_key_t modified_keys[2];
16✔
4124
                size_t n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 2);
16!
4125
                CHECK(n == 2);
16!
4126
                CHECK(modified_keys[0] == foo_int_key);
16!
4127
                CHECK(modified_keys[1] == foo_str_key);
16!
4128

10!
4129
                n = realm_object_changes_get_modified_properties(state.changes.get(), nullptr, 2);
16!
4130
                CHECK(n == 2);
15!
4131

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

10!
4155
                state.called = false;
16!
4156
                write([&]() {
15!
4157
                    // checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
10!
4158
                    checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("aaa"), false));
16!
4159
                });
15✔
4160
                REQUIRE(!state.called);
16!
4161
            }
16!
4162
        }
40!
4163
    }
795✔
4164

554✔
4165
    SECTION("threads") {
1,226✔
4166
        CPtr<realm_object_t> foo_obj, bar_obj;
152✔
4167
        write([&]() {
244✔
4168
            foo_obj = cptr_checked(realm_object_create(realm, class_foo.key));
72✔
4169
            realm_set_value(foo_obj.get(), foo_int_key, rlm_int_val(123), false);
72!
4170
            bar_obj = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123)));
72✔
4171
        });
76✔
4172

172✔
4173
        auto list = cptr_checked(realm_get_list(foo_obj.get(), foo_properties["int_list"]));
161✔
4174
        auto set = cptr_checked(realm_get_set(foo_obj.get(), foo_properties["int_set"]));
247✔
4175
        auto dictionary = cptr_checked(realm_get_dictionary(foo_obj.get(), foo_properties["int_dict"]));
80✔
4176
        auto results = cptr_checked(realm_object_find_all(realm, class_foo.key));
80✔
4177

50✔
4178
        SECTION("wrong thread") {
80✔
4179
            std::thread t{[&]() {
27✔
4180
                realm_value_t val;
32✔
4181
                CHECK(!realm_get_value(foo_obj.get(), foo_int_key, &val));
19!
4182
                CHECK_ERR(RLM_ERR_WRONG_THREAD);
24!
4183
            }};
24!
4184

18!
4185
            t.join();
24✔
4186
        }
18✔
4187

42✔
4188
        SECTION("thread-safe references") {
64✔
4189
            auto foo_obj_tsr = cptr_checked(realm_create_thread_safe_reference(foo_obj.get()));
55✔
4190
            auto bar_obj_tsr = cptr_checked(realm_create_thread_safe_reference(bar_obj.get()));
60!
4191
            auto list_tsr = cptr_checked(realm_create_thread_safe_reference(list.get()));
58!
4192
            auto set_tsr = cptr_checked(realm_create_thread_safe_reference(set.get()));
58✔
4193
            auto dict_tsr = cptr_checked(realm_create_thread_safe_reference(dictionary.get()));
57✔
4194
            auto results_tsr = cptr_checked(realm_create_thread_safe_reference(results.get()));
58✔
4195

34✔
4196
            SECTION("resolve") {
61✔
4197
                std::thread t{[&]() {
26✔
4198
                    auto config = make_config(test_file.path.c_str());
28✔
4199
                    auto realm2 = cptr_checked(realm_open(config.get()));
22✔
4200
                    auto foo_obj2 =
22✔
4201
                        cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), foo_obj_tsr.get()));
22✔
4202
                    auto bar_obj2 =
22✔
4203
                        cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), bar_obj_tsr.get()));
22✔
4204
                    auto results2 =
18✔
4205
                        cptr_checked(realm_results_from_thread_safe_reference(realm2.get(), results_tsr.get()));
22✔
4206
                    auto list2 = cptr_checked(realm_list_from_thread_safe_reference(realm2.get(), list_tsr.get()));
16✔
4207
                    auto set2 = cptr_checked(realm_set_from_thread_safe_reference(realm2.get(), set_tsr.get()));
16✔
4208
                    auto dict2 =
16✔
4209
                        cptr_checked(realm_dictionary_from_thread_safe_reference(realm2.get(), dict_tsr.get()));
16✔
4210

10✔
4211
                    realm_value_t foo_obj_int;
16✔
4212
                    CHECK(realm_get_value(foo_obj2.get(), foo_int_key, &foo_obj_int));
15!
4213
                    CHECK(rlm_val_eq(foo_obj_int, rlm_int_val(123)));
16!
4214

10!
4215
                    size_t count;
16!
4216
                    CHECK(realm_results_count(results2.get(), &count));
15!
4217
                    CHECK(count == 1);
16!
4218
                }};
16!
4219

9!
4220
                t.join();
16✔
4221
            }
15!
4222

28!
4223
            SECTION("resolve in frozen") {
51✔
4224
                auto realm2 = cptr_checked(realm_freeze(realm));
18✔
4225
                CHECK(realm_is_frozen(realm2.get()));
22!
4226
                CHECK(realm != realm2.get());
16!
4227

10!
4228
                auto foo_obj2 =
15!
4229
                    cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), foo_obj_tsr.get()));
15✔
4230
                CHECK(realm_is_frozen(foo_obj2.get()));
16!
4231
            }
18✔
4232

34!
4233
            SECTION("type error") {
52✔
4234
                CHECK(!realm_object_from_thread_safe_reference(realm, list_tsr.get()));
18!
4235
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
22!
4236
                CHECK(!realm_list_from_thread_safe_reference(realm, foo_obj_tsr.get()));
15!
4237
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
4238
                CHECK(!realm_set_from_thread_safe_reference(realm, list_tsr.get()));
16!
4239
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
4240
                CHECK(!realm_dictionary_from_thread_safe_reference(realm, set_tsr.get()));
16!
4241
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
18!
4242
                CHECK(!realm_results_from_thread_safe_reference(realm, list_tsr.get()));
22!
4243
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
4244
                CHECK(!realm_from_thread_safe_reference(list_tsr.get(), nullptr));
16!
4245
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
4246
            }
16!
4247

28!
4248
            SECTION("non-sendable") {
52!
4249
                auto c = cptr(realm_config_new());
18!
4250
                CHECK(!realm_create_thread_safe_reference(c.get()));
22!
4251
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
16!
4252
            }
16!
4253
        }
52!
4254
    }
64!
4255

556✔
4256
    SECTION("freeze and thaw") {
1,106✔
4257
        SECTION("realm") {
171✔
4258
            auto frozen_realm = cptr_checked(realm_freeze(realm));
196✔
4259
            CHECK(!realm_is_frozen(realm));
26!
4260
            CHECK(realm_is_frozen(frozen_realm.get()));
16!
4261
        }
16!
4262

46!
4263
        SECTION("objects") {
84✔
4264
            CPtr<realm_object_t> obj1;
109✔
4265
            realm_value_t value;
206✔
4266

20✔
4267
            write([&]() {
16✔
4268
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
15!
4269
                CHECK(obj1);
16!
4270
            });
16✔
4271
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
20!
4272
            CHECK(value.type == RLM_TYPE_STRING);
26!
4273
            CHECK(strncmp(value.string.data, "", value.string.size) == 0);
16!
4274

10!
4275
            auto frozen_realm = cptr_checked(realm_freeze(realm));
15!
4276
            realm_object_t* frozen_obj1;
15✔
4277
            CHECK(realm_object_resolve_in(obj1.get(), frozen_realm.get(), &frozen_obj1));
16!
4278

10!
4279
            write([&]() {
16!
4280
                CHECK(checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("Hello, World!"), false)));
15!
4281
            });
16!
4282

10!
4283
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
15!
4284
            CHECK(value.type == RLM_TYPE_STRING);
15!
4285
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
16!
4286

10!
4287
            CHECK(checked(realm_get_value(frozen_obj1, foo_str_key, &value)));
15!
4288
            CHECK(value.type == RLM_TYPE_STRING);
15!
4289
            CHECK(strncmp(value.string.data, "", value.string.size) == 0);
16!
4290
            realm_object_t* thawed_obj1;
16!
4291
            CHECK(realm_object_resolve_in(obj1.get(), realm, &thawed_obj1));
15!
4292
            CHECK(thawed_obj1);
16!
4293
            CHECK(checked(realm_get_value(thawed_obj1, foo_str_key, &value)));
16!
4294
            CHECK(value.type == RLM_TYPE_STRING);
16!
4295
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
15!
4296

10!
4297
            write([&]() {
16!
4298
                CHECK(checked(realm_object_delete(obj1.get())));
15!
4299
            });
16✔
4300
            realm_object_t* deleted_obj;
16!
4301
            auto b = realm_object_resolve_in(frozen_obj1, realm, &deleted_obj);
16!
4302
            CHECK(b);
16!
4303
            CHECK(deleted_obj == nullptr);
16!
4304
            realm_release(frozen_obj1);
16!
4305
            realm_release(thawed_obj1);
15!
4306
        }
16✔
4307

40!
4308
        SECTION("results") {
76✔
4309
            auto results = cptr_checked(realm_object_find_all(realm, class_foo.key));
20✔
4310
            realm_results_delete_all(results.get());
26✔
4311

10!
4312
            write([&]() {
16!
4313
                // Ensure that we start from a known initial state
9✔
4314
                CHECK(realm_results_delete_all(results.get()));
16!
4315

9✔
4316
                auto obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
20!
4317
                CHECK(obj1);
25!
4318
            });
16✔
4319

10!
4320
            size_t count;
15✔
4321
            realm_results_count(results.get(), &count);
15✔
4322
            CHECK(count == 1);
15!
4323

10!
4324
            auto frozen_realm = cptr_checked(realm_freeze(realm));
15!
4325
            auto frozen_results = cptr_checked(realm_results_resolve_in(results.get(), frozen_realm.get()));
15✔
4326
            write([&]() {
16!
4327
                auto obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
16✔
4328
                CHECK(obj1);
15!
4329
            });
16✔
4330
            realm_results_count(frozen_results.get(), &count);
16!
4331
            CHECK(count == 1);
16!
4332
            realm_results_count(results.get(), &count);
15✔
4333
            CHECK(count == 2);
16!
4334

10✔
4335
            auto thawed_results = cptr_checked(realm_results_resolve_in(frozen_results.get(), realm));
16!
4336
            realm_results_count(thawed_results.get(), &count);
15✔
4337
            CHECK(count == 2);
16!
4338
        }
16✔
4339

40!
4340
        SECTION("lists") {
76!
4341
            CPtr<realm_object_t> obj1;
20✔
4342
            size_t count;
26!
4343

9✔
4344
            write([&]() {
16✔
4345
                obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(1)));
15✔
4346
                CHECK(obj1);
16!
4347
            });
16✔
4348

14!
4349
            auto list = cptr_checked(realm_get_list(obj1.get(), bar_properties["strings"]));
26✔
4350
            realm_list_size(list.get(), &count);
15✔
4351
            CHECK(count == 0);
16!
4352

9✔
4353
            auto frozen_realm = cptr_checked(realm_freeze(realm));
16!
4354
            realm_list_t* frozen_list;
15✔
4355
            CHECK(realm_list_resolve_in(list.get(), frozen_realm.get(), &frozen_list));
16!
4356
            realm_list_size(frozen_list, &count);
16✔
4357
            CHECK(count == 0);
15!
4358

10✔
4359
            write([&]() {
16!
4360
                checked(realm_list_insert(list.get(), 0, rlm_str_val("Hello")));
15!
4361
            });
15✔
4362

10✔
4363
            realm_list_size(frozen_list, &count);
16✔
4364
            CHECK(count == 0);
15!
4365
            realm_list_size(list.get(), &count);
16✔
4366
            CHECK(count == 1);
16!
4367

9✔
4368
            realm_list_t* thawed_list;
16!
4369
            CHECK(realm_list_resolve_in(frozen_list, realm, &thawed_list));
15!
4370
            realm_list_size(thawed_list, &count);
16✔
4371
            CHECK(count == 1);
15!
4372

10✔
4373
            CHECK(realm_list_is_valid(thawed_list));
16!
4374
            write([&]() {
15✔
4375
                CHECK(checked(realm_object_delete(obj1.get())));
16!
4376
            });
15✔
4377
            CHECK(!realm_list_is_valid(thawed_list));
16!
4378
            realm_release(thawed_list);
16!
4379
            CHECK(realm_list_resolve_in(frozen_list, realm, &thawed_list));
16!
4380
            CHECK(thawed_list == nullptr);
16!
4381
            realm_release(frozen_list);
15!
4382
        }
16!
4383

40✔
4384
        SECTION("sets") {
76!
4385
            CPtr<realm_object_t> obj1;
20✔
4386
            size_t count;
26!
4387

10✔
4388
            write([&]() {
16!
4389
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
15!
4390
                CHECK(obj1);
16!
4391
            });
16✔
4392

14!
4393
            auto set = cptr_checked(realm_get_set(obj1.get(), foo_properties["string_set"]));
26✔
4394
            realm_set_size(set.get(), &count);
15✔
4395
            CHECK(count == 0);
16!
4396

9✔
4397
            auto frozen_realm = cptr_checked(realm_freeze(realm));
16!
4398
            realm_set_t* frozen_set;
15✔
4399
            CHECK(realm_set_resolve_in(set.get(), frozen_realm.get(), &frozen_set));
16!
4400
            realm_set_size(frozen_set, &count);
16✔
4401
            CHECK(count == 0);
15!
4402

10✔
4403
            write([&]() {
16!
4404
                checked(realm_set_insert(set.get(), rlm_str_val("Hello"), nullptr, nullptr));
15!
4405
            });
15✔
4406

10✔
4407
            realm_set_size(frozen_set, &count);
16✔
4408
            CHECK(count == 0);
15!
4409
            realm_set_size(set.get(), &count);
16✔
4410
            CHECK(count == 1);
16!
4411

9✔
4412
            realm_set_t* thawed_set;
16!
4413
            CHECK(realm_set_resolve_in(frozen_set, realm, &thawed_set));
15!
4414
            realm_set_size(thawed_set, &count);
16✔
4415
            CHECK(count == 1);
15!
4416

10✔
4417
            CHECK(realm_set_is_valid(thawed_set));
16!
4418
            write([&]() {
15✔
4419
                CHECK(checked(realm_object_delete(obj1.get())));
16!
4420
            });
15✔
4421
            CHECK(!realm_set_is_valid(thawed_set));
16!
4422
            realm_release(thawed_set);
16!
4423
            CHECK(realm_set_resolve_in(frozen_set, realm, &thawed_set));
16!
4424
            CHECK(thawed_set == nullptr);
16!
4425
            realm_release(frozen_set);
15!
4426
        }
16!
4427

40✔
4428
        SECTION("dictionaries") {
76!
4429
            CPtr<realm_object_t> obj1;
20✔
4430
            size_t count;
26!
4431

10✔
4432
            write([&]() {
16!
4433
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
15!
4434
                CHECK(obj1);
16!
4435
            });
16✔
4436

14!
4437
            auto dictionary = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["string_dict"]));
26✔
4438
            realm_dictionary_size(dictionary.get(), &count);
15✔
4439
            CHECK(count == 0);
16!
4440

9✔
4441
            auto frozen_realm = cptr_checked(realm_freeze(realm));
16!
4442
            realm_dictionary_t* frozen_dictionary;
15✔
4443
            CHECK(realm_dictionary_resolve_in(dictionary.get(), frozen_realm.get(), &frozen_dictionary));
16!
4444
            realm_dictionary_size(frozen_dictionary, &count);
16✔
4445
            CHECK(count == 0);
15!
4446

10✔
4447
            write([&]() {
16!
4448
                checked(realm_dictionary_insert(dictionary.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr,
15!
4449
                                                nullptr));
15✔
4450
            });
16✔
4451

10✔
4452
            realm_dictionary_size(frozen_dictionary, &count);
16!
4453
            CHECK(count == 0);
15!
4454
            realm_dictionary_size(dictionary.get(), &count);
16!
4455
            CHECK(count == 1);
15!
4456

10✔
4457
            realm_dictionary_t* thawed_dictionary;
16!
4458
            CHECK(realm_dictionary_resolve_in(frozen_dictionary, realm, &thawed_dictionary));
15!
4459
            realm_dictionary_size(thawed_dictionary, &count);
16✔
4460
            CHECK(count == 1);
15!
4461

10✔
4462
            CHECK(realm_dictionary_is_valid(thawed_dictionary));
16!
4463
            write([&]() {
15✔
4464
                CHECK(checked(realm_object_delete(obj1.get())));
16!
4465
            });
15✔
4466
            CHECK(!realm_dictionary_is_valid(thawed_dictionary));
16!
4467
            realm_release(thawed_dictionary);
16!
4468
            CHECK(realm_dictionary_resolve_in(frozen_dictionary, realm, &thawed_dictionary));
16!
4469
            CHECK(thawed_dictionary == nullptr);
16!
4470
            realm_release(frozen_dictionary);
15!
4471
        }
16!
4472
    }
76✔
4473

550!
4474
    realm_close(realm);
1,106✔
4475
    REQUIRE(realm_is_closed(realm));
1,185!
4476
    realm_release(realm);
1,276✔
4477
}
1,276!
4478

184!
4479
TEST_CASE("C API - queries", "[c_api]") {
640✔
4480
    TestFile test_file;
458✔
4481
    realm_t* realm = open_realm(test_file);
544✔
4482

395✔
4483
    auto write = [&](auto&& f) {
834✔
4484
        checked(realm_begin_write(realm));
796!
4485
        f();
854✔
4486
        checked(realm_commit(realm));
854✔
4487
        checked(realm_refresh(realm, nullptr));
672✔
4488
    };
748✔
4489

400✔
4490
    bool found = false;
628✔
4491

304✔
4492
    realm_class_info_t class_foo, class_bar;
628✔
4493
    CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
590!
4494
    REQUIRE(found);
628!
4495
    CHECK(checked(realm_find_class(realm, "Bar", &found, &class_bar)));
628!
4496
    REQUIRE(found);
628!
4497

400!
4498
    auto foo_properties = [&](const char* prop) {
1,086!
4499
        realm_property_info_t property;
1,086✔
4500
        bool found;
1,172✔
4501
        realm_find_property(realm, class_foo.key, prop, &found, &property);
1,210✔
4502
        CHECK(found);
1,210!
4503
        return property.key;
1,210!
4504
    };
1,210!
4505

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

392✔
4514
    auto foo_int_key = foo_properties("int");
496✔
4515
    auto foo_str_key = foo_properties("string");
533✔
4516

306✔
4517
    CPtr<realm_object_t> obj1;
534✔
4518
    CPtr<realm_object_t> obj2;
496✔
4519
    auto int_val1 = rlm_int_val(123);
534!
4520
    auto int_val2 = rlm_int_val(456);
534✔
4521
    write([&]() {
534✔
4522
        obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
570✔
4523
        CHECK(obj1);
608!
4524
        CHECK(checked(realm_set_value(obj1.get(), foo_int_key, int_val1, false)));
608!
4525
        CHECK(checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("Hello, World!"), false)));
570!
4526
        obj2 = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(1)));
608!
4527
        CHECK(obj2);
608!
4528
        CPtr<realm_object_t> obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
608✔
4529
        CHECK(obj3);
608!
4530
        CHECK(checked(realm_set_value(obj3.get(), foo_int_key, int_val2, false)));
608!
4531
        CPtr<realm_object_t> obj4 = cptr_checked(realm_object_create(realm, class_foo.key));
608!
4532
        CHECK(obj3);
608!
4533
        CHECK(checked(realm_set_value(obj4.get(), foo_int_key, int_val1, false)));
608!
4534
    });
608!
4535

380!
4536
    SECTION("find with primary key") {
608!
4537
        bool found = false;
126✔
4538

158!
4539
        auto p = cptr_checked(realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(1), &found));
90!
4540
        CHECK(found);
89!
4541
        auto p_key = realm_object_get_key(p.get());
90!
4542
        auto obj2_key = realm_object_get_key(obj2.get());
90!
4543
        CHECK(p_key == obj2_key);
90!
4544
        CHECK(realm_equals(p.get(), obj2.get()));
52!
4545

84!
4546
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(2), &found));
16!
4547
        CHECK(!found);
14!
4548
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(2), nullptr));
16!
4549

10!
4550
        // Check that finding by type-mismatched values just find nothing.
10!
4551
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_null(), &found));
15!
4552
        CHECK(!found);
15!
4553
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_str_val("a"), &found));
16!
4554
        CHECK(!found);
15!
4555
        CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_str_val("a"), nullptr));
16!
4556

10!
4557
        // Invalid class key
10!
4558
        CHECK(!realm_object_find_with_primary_key(realm, 123123123, rlm_int_val(1), nullptr));
14!
4559
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
14!
4560
    }
16!
4561

232!
4562
    SECTION("find all") {
460!
4563
        auto r = cptr_checked(realm_object_find_all(realm, class_bar.key));
52!
4564
        size_t count;
90!
4565
        realm_results_count(r.get(), &count);
15✔
4566
        CHECK(count == 1);
15!
4567
    }
16!
4568

232!
4569
    SECTION("query") {
460✔
4570
        realm_value_t arg_data[1] = {rlm_str_val("Hello, World!")};
508✔
4571
        size_t num_args = 2;
584✔
4572
        realm_query_arg_t args[2] = {realm_query_arg_t{1, false, &arg_data[0]},
506✔
4573
                                     realm_query_arg_t{1, false, &int_val2}};
506✔
4574
        realm_query_arg_t* arg_list = &args[0];
506✔
4575
        auto q =
506!
4576
            cptr_checked(realm_query_parse(realm, class_foo.key, "string == $0 SORT(int ASCENDING)", 1, arg_list));
506✔
4577

326✔
4578
        SECTION("realm_query_description()") {
580✔
4579
            const char* descr = realm_query_get_description(q.get());
120✔
4580
            std::string expected = "string == \"Hello, World!\" SORT(int ASC)";
156✔
4581
            CHECK(descr == expected);
86!
4582
        }
86✔
4583

290!
4584
        SECTION("realm_query_count()") {
506✔
4585
            size_t count;
132✔
4586
            CHECK(checked(realm_query_count(q.get(), &count)));
132!
4587
            CHECK(count == 1);
100!
4588

18!
4589
            SECTION("cloned") {
30!
4590
                auto q2 = clone_cptr(q.get());
16!
4591
                size_t count2;
18✔
4592
                CHECK(checked(realm_query_count(q2.get(), &count2)));
50!
4593
                CHECK(count == count2);
86!
4594
            }
18!
4595
            SECTION("realm_query_append_query") {
30!
4596
                auto q2 =
18!
4597
                    cptr_checked(realm_query_append_query(q.get(), "TRUEPREDICATE LIMIT(1)", num_args, arg_list));
18✔
4598
                size_t count;
18✔
4599
                CHECK(checked(realm_query_count(q2.get(), &count)));
16!
4600
                CHECK(count == 1);
16!
4601
                q2 = cptr_checked(realm_query_append_query(q.get(), "FALSEPREDICATE", num_args, arg_list));
16!
4602
                CHECK(checked(realm_query_count(q2.get(), &count)));
16!
4603
                CHECK(count == 0);
16!
4604
                q2 = cptr_checked(realm_query_append_query(q.get(), "TRUEPREDICATE LIMIT(0)", num_args, arg_list));
18!
4605
                CHECK(checked(realm_query_count(q2.get(), &count)));
16!
4606
                CHECK(count == 0);
16!
4607
                q2 = cptr_checked(realm_query_append_query(q.get(), "TRUEPREDICATE LIMIT(10)", num_args, arg_list));
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(), "int == $1", num_args, arg_list));
16!
4611
                CHECK(checked(realm_query_count(q2.get(), &count)));
16!
4612
                CHECK(count == 0);
16!
4613
            }
16!
4614
        }
28!
4615

220!
4616
        SECTION("realm_query_parse() errors") {
438✔
4617
            // Invalid class key
44!
4618
            CHECK(!realm_query_parse(realm, 123123123, "string == $0", num_args, arg_list));
86!
4619
            CHECK_ERR_CAT(RLM_ERR_NO_SUCH_TABLE, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
15!
4620

10!
4621
            // Invalid syntax
10!
4622
            CHECK(!realm_query_parse(realm, class_foo.key, "lel", 0, nullptr));
15!
4623
            CHECK_ERR_CAT(RLM_ERR_INVALID_QUERY_STRING, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
17!
4624

44!
4625
            // Invalid property
80!
4626
            CHECK(!realm_query_parse(realm, class_foo.key, "strong = 5", 0, nullptr));
14!
4627
            CHECK_ERR_CAT(RLM_ERR_INVALID_QUERY, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
15!
4628

10!
4629
            // Invalid number of arguments
9!
4630
            CHECK(!realm_query_parse(realm, class_foo.key, "string == $0", 0, nullptr));
14!
4631
            CHECK_ERR_CAT(RLM_ERR_INVALID_QUERY_ARG, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
15!
4632
        }
16!
4633

219!
4634
        SECTION("string in list") {
435✔
4635
            char foo[] = "foo";
50!
4636
            realm_value_t str = rlm_str_val(foo);
86!
4637
            realm_value_t list_arg[2] = {str, rlm_str_val("bar")};
15✔
4638

9✔
4639
            write([&]() {
16!
4640
                CHECK(realm_set_value(obj1.get(), foo_properties("string"), rlm_str_val("foo"), false));
15!
4641
            });
16✔
4642

44!
4643
            static const size_t num_args = 1;
86✔
4644
            realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &str}};
15✔
4645
            realm_query_arg_t* arg_list_simple = &args[0];
16✔
4646

10✔
4647
            realm_query_arg_t args_in_list[num_args] = {realm_query_arg_t{2, true, &list_arg[0]}};
15✔
4648
            realm_query_arg_t* arg_list = &args_in_list[0];
15✔
4649

10!
4650
            auto q_string_single_param =
16✔
4651
                cptr_checked(realm_query_parse(realm, class_foo.key, "string == $0", num_args, arg_list_simple));
14✔
4652
            auto q_string_in_list =
16✔
4653
                cptr_checked(realm_query_parse(realm, class_foo.key, "string IN $0", num_args, arg_list));
16✔
4654

10✔
4655
            // changing the value for one of the parameters passed should not change the result of the query.
9✔
4656
            // essentially we must assure that core is copying all the arguments passed inside the list (like for
9✔
4657
            // normal query arguments), and after realm_query_parse completes any modification of the memory that
9✔
4658
            // was used to store the parameter does not impact in any way core.
8✔
4659
            char* s = foo;
15✔
4660
            s[0] = 'a';
15✔
4661
            size_t count, count_list;
16✔
4662

10✔
4663
            CHECK(checked(realm_query_count(q_string_single_param.get(), &count)));
15!
4664
            CHECK(1 == count);
14!
4665
            CHECK(checked(realm_query_count(q_string_in_list.get(), &count_list)));
15!
4666
            CHECK(1 == count_list);
15!
4667
        }
15!
4668

220!
4669
        SECTION("link in list") {
436✔
4670
            auto link = rlm_link_val(class_bar.key, realm_object_get_key(obj2.get()));
50✔
4671
            realm_value_t link_value = link;
85✔
4672
            write([&]() {
16!
4673
                CHECK(realm_set_value(obj1.get(), foo_properties("link"), link_value, false));
16!
4674
            });
16!
4675

10!
4676
            static const size_t num_args = 1;
16✔
4677
            realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &link_value}};
49✔
4678
            realm_query_arg_t* arg = &args[0];
86✔
4679

10✔
4680
            realm_value_t list_arg[num_args] = {link_value};
16✔
4681
            realm_query_arg_t args_in_list[num_args] = {realm_query_arg_t{num_args, true, &list_arg[0]}};
15✔
4682
            realm_query_arg_t* arg_list = &args_in_list[0];
16!
4683

10✔
4684
            auto q_link_single_param =
15✔
4685
                cptr_checked(realm_query_parse(realm, class_foo.key, "link == $0", num_args, arg));
15✔
4686
            auto q_link_in_list =
16✔
4687
                cptr_checked(realm_query_parse(realm, class_foo.key, "link IN $0", num_args, arg_list));
16✔
4688

9✔
4689
            size_t count, count_list;
16✔
4690

9✔
4691
            // change the link
10✔
4692
            link = rlm_null();
14✔
4693

9✔
4694
            CHECK(checked(realm_query_count(q_link_single_param.get(), &count)));
16!
4695
            CHECK(1 == count);
15!
4696
            CHECK(checked(realm_query_count(q_link_in_list.get(), &count_list)));
16!
4697
            CHECK(1 == count_list);
15!
4698
        }
16!
4699

219!
4700
        SECTION("decimal NaN") {
435✔
4701
            realm_value_t decimal = rlm_decimal_nan();
50✔
4702

79✔
4703
            write([&]() {
16!
4704
                CHECK(realm_set_value(obj1.get(), foo_properties("decimal"), decimal, false));
15!
4705
            });
16!
4706
            realm_query_arg_t args[] = {realm_query_arg_t{1, false, &decimal}};
16!
4707
            auto q_decimal = cptr_checked(realm_query_parse(realm, class_foo.key, "decimal == $0", 1, args));
16✔
4708
            realm_value_t out_value;
50✔
4709
            bool out_found;
86✔
4710
            CHECK(realm_query_find_first(q_decimal.get(), &out_value, &out_found));
16!
4711
            CHECK(out_found);
15!
4712
            auto link = obj1->get_obj().get_link();
16!
4713
            realm_value_t expected;
16!
4714
            expected.type = RLM_TYPE_LINK;
16✔
4715
            expected.link.target_table = link.get_table_key().value;
16✔
4716
            expected.link.target = link.get_obj_key().value;
16✔
4717
            CHECK(rlm_val_eq(out_value, expected));
16!
4718
        }
16✔
4719

220!
4720
        SECTION("interpolate all types") {
436!
4721
            realm_value_t int_arg = rlm_int_val(123);
50✔
4722
            realm_value_t bool_arg = rlm_bool_val(true);
86✔
4723
            realm_value_t string_arg = rlm_str_val("foobar");
16✔
4724
            static const uint8_t binary_data[3] = {1, 2, 3};
16✔
4725
            realm_value_t binary_arg = rlm_binary_val(binary_data, 3);
16✔
4726
            realm_value_t timestamp_arg = rlm_timestamp_val(1000000, 1);
16!
4727
            realm_value_t float_arg = rlm_float_val(123.f);
16✔
4728
            realm_value_t double_arg = rlm_double_val(456.0);
50✔
4729
            realm_value_t decimal_arg = rlm_decimal_val(789.0);
86✔
4730
            realm_value_t object_id_arg = rlm_object_id_val("abc123abc123");
16✔
4731
            realm_value_t uuid_arg = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
16✔
4732
            realm_value_t link_arg = rlm_link_val(class_bar.key, realm_object_get_key(obj2.get()));
16✔
4733
            realm_value_t list_arg[3] = {rlm_int_val(456), rlm_str_val("lol"), rlm_double_val(3.14)};
16✔
4734

10✔
4735
            static const size_t num_args = 13;
16✔
4736
            realm_query_arg_t args[num_args] = {
15✔
4737
                realm_query_arg_t{1, false, &int_arg},       realm_query_arg_t{1, false, &bool_arg},
16✔
4738
                realm_query_arg_t{1, false, &string_arg},    realm_query_arg_t{1, false, &binary_arg},
16✔
4739
                realm_query_arg_t{1, false, &timestamp_arg}, realm_query_arg_t{1, false, &float_arg},
16✔
4740
                realm_query_arg_t{1, false, &double_arg},    realm_query_arg_t{1, false, &decimal_arg},
16✔
4741
                realm_query_arg_t{1, false, &object_id_arg}, realm_query_arg_t{1, false, &uuid_arg},
16✔
4742
                realm_query_arg_t{1, false, &link_arg},      realm_query_arg_t{3, true, &list_arg[0]},
16✔
4743
                realm_query_arg_t{0, true, nullptr}};
15✔
4744
            realm_query_arg_t* arg_list = &args[0];
16✔
4745

10✔
4746
            auto q_int = cptr_checked(realm_query_parse(realm, class_foo.key, "int == $0", num_args, arg_list));
16✔
4747
            auto q_bool = cptr_checked(realm_query_parse(realm, class_foo.key, "bool == $1", num_args, arg_list));
15✔
4748
            auto q_string = cptr_checked(realm_query_parse(realm, class_foo.key, "string == $2", num_args, arg_list));
16✔
4749
            auto q_binary = cptr_checked(realm_query_parse(realm, class_foo.key, "binary == $3", num_args, arg_list));
16✔
4750
            auto q_timestamp =
16✔
4751
                cptr_checked(realm_query_parse(realm, class_foo.key, "timestamp == $4", num_args, arg_list));
16✔
4752
            auto q_float = cptr_checked(realm_query_parse(realm, class_foo.key, "float == $5", num_args, arg_list));
16✔
4753
            auto q_double = cptr_checked(realm_query_parse(realm, class_foo.key, "double == $6", num_args, arg_list));
16✔
4754
            auto q_decimal =
15✔
4755
                cptr_checked(realm_query_parse(realm, class_foo.key, "decimal == $7", num_args, arg_list));
16✔
4756
            auto q_object_id =
16✔
4757
                cptr_checked(realm_query_parse(realm, class_foo.key, "object_id == $8", num_args, arg_list));
16✔
4758
            auto q_uuid = cptr_checked(realm_query_parse(realm, class_foo.key, "uuid == $9", num_args, arg_list));
16✔
4759
            auto q_link = cptr_checked(realm_query_parse(realm, class_foo.key, "link == $10", num_args, arg_list));
16✔
4760
            auto q_list = cptr_checked(realm_query_parse(realm, class_foo.key, "int == ANY $11", num_args, arg_list));
16✔
4761
            auto q_empty_list =
16✔
4762
                cptr_checked(realm_query_parse(realm, class_foo.key, "int == ALL $12", num_args, arg_list));
16✔
4763

10✔
4764
            CHECK(cptr_checked(realm_query_find_all(q_int.get())));
16!
4765
            CHECK(cptr_checked(realm_query_find_all(q_bool.get())));
15!
4766
            CHECK(cptr_checked(realm_query_find_all(q_string.get())));
16!
4767
            CHECK(cptr_checked(realm_query_find_all(q_binary.get())));
16!
4768
            CHECK(cptr_checked(realm_query_find_all(q_timestamp.get())));
16!
4769
            CHECK(cptr_checked(realm_query_find_all(q_float.get())));
16!
4770
            CHECK(cptr_checked(realm_query_find_all(q_double.get())));
16!
4771
            CHECK(cptr_checked(realm_query_find_all(q_decimal.get())));
16!
4772
            CHECK(cptr_checked(realm_query_find_all(q_object_id.get())));
15!
4773
            CHECK(cptr_checked(realm_query_find_all(q_uuid.get())));
16!
4774
            CHECK(cptr_checked(realm_query_find_all(q_link.get())));
16!
4775
            CHECK(cptr_checked(realm_query_find_all(q_list.get())));
16!
4776
            CHECK(cptr_checked(realm_query_find_all(q_empty_list.get())));
16!
4777

10!
4778
            SECTION("type mismatch") {
16!
4779
                CHECK(!realm_query_parse(realm, class_foo.key, "int == $2", num_args, arg_list));
15!
4780
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4781
                CHECK(!realm_query_parse(realm, class_foo.key, "bool == $2", num_args, arg_list));
16!
4782
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4783
                CHECK(!realm_query_parse(realm, class_foo.key, "string == $7", num_args, arg_list));
16!
4784
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4785
                CHECK(!realm_query_parse(realm, class_foo.key, "timestamp == $2", num_args, arg_list));
16!
4786
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
15!
4787
                CHECK(!realm_query_parse(realm, class_foo.key, "double == $2", num_args, arg_list));
16!
4788
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4789
                CHECK(!realm_query_parse(realm, class_foo.key, "float == $2", num_args, arg_list));
16!
4790
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4791
                CHECK(!realm_query_parse(realm, class_foo.key, "binary == $0", num_args, arg_list));
16!
4792
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4793
                CHECK(!realm_query_parse(realm, class_foo.key, "decimal == $2", num_args, arg_list));
16!
4794
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4795
                CHECK(!realm_query_parse(realm, class_foo.key, "object_id == $2", num_args, arg_list));
16!
4796
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4797
                CHECK(!realm_query_parse(realm, class_foo.key, "uuid == $2", num_args, arg_list));
16!
4798
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4799
                CHECK(!realm_query_parse(realm, class_foo.key, "link == $2", num_args, arg_list));
16!
4800
                CHECK_ERR(RLM_ERR_INVALID_QUERY_ARG);
16!
4801
            }
16!
4802
        }
16!
4803

220!
4804
        SECTION("realm_query_find_first()") {
436!
4805
            realm_value_t found_value = rlm_null();
50!
4806
            bool found;
86!
4807
            CHECK(checked(realm_query_find_first(q.get(), &found_value, &found)));
16!
4808
            CHECK(found);
16!
4809
            CHECK(found_value.type == RLM_TYPE_LINK);
16!
4810
            CHECK(found_value.link.target_table == class_foo.key);
16!
4811
            CHECK(found_value.link.target == realm_object_get_key(obj1.get()));
16!
4812
        }
50!
4813

290!
4814
        SECTION("verify order realm_query_find_first()") {
436✔
4815
            realm_property_info_t info_string;
50✔
4816
            realm_property_info_t info_int;
86!
4817
            bool found_string = false, found_int = false;
16!
4818
            REQUIRE(realm_find_property(realm, class_foo.key, "string", &found_string, &info_string));
16!
4819
            REQUIRE(realm_find_property(realm, class_foo.key, "int", &found_int, &info_int));
16!
4820
            CHECK(info_string.key != RLM_INVALID_PROPERTY_KEY);
16!
4821
            CHECK(info_int.key != RLM_INVALID_PROPERTY_KEY);
16!
4822
            CPtr<realm_object_t> obj1, obj2;
50!
4823
            checked(realm_begin_write(realm));
86!
4824
            obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
16✔
4825
            obj2 = cptr_checked(realm_object_create(realm, class_foo.key));
16✔
4826
            CHECK(obj1);
16!
4827
            CHECK(obj2);
16!
4828
            CHECK(checked(realm_set_value(obj1.get(), info_string.key, rlm_str_val("Test"), false)));
16!
4829
            CHECK(checked(realm_set_value(obj2.get(), info_string.key, rlm_str_val("Test"), false)));
16!
4830
            CHECK(checked(realm_set_value(obj1.get(), info_int.key, rlm_int_val(10), false)));
16!
4831
            CHECK(checked(realm_set_value(obj2.get(), info_int.key, rlm_int_val(11), false)));
16!
4832
            checked(realm_commit(realm));
16!
4833
            checked(realm_refresh(realm, nullptr));
16!
4834

10✔
4835
            size_t count = 0;
16!
4836
            realm_value_t arg_data[1] = {rlm_str_val("Test")};
15!
4837
            realm_query_arg_t args[1] = {realm_query_arg_t{1, false, &arg_data[0]}};
16!
4838
            realm_query_arg_t* arg_list = &args[0];
16!
4839
            auto q = cptr_checked(realm_query_parse(realm, class_foo.key, "string == $0", 1, arg_list));
16!
4840
            CHECK(checked(realm_query_count(q.get(), &count)));
16!
4841
            CHECK(count == 2);
16!
4842

10!
4843
            auto q2 =
15!
4844
                cptr_checked(realm_query_append_query(q.get(), "string == $0 SORT(int ASCENDING)", 1, arg_list));
15✔
4845
            realm_value_t found_value = rlm_null();
16✔
4846
            bool found_sorted;
16✔
4847
            CHECK(checked(realm_query_find_first(q2.get(), &found_value, &found_sorted)));
16!
4848
            CHECK(found_sorted);
16!
4849
            CHECK(found_value.type == RLM_TYPE_LINK);
16!
4850
            CHECK(found_value.link.target_table == class_foo.key);
16!
4851
            CHECK(found_value.link.target == realm_object_get_key(obj1.get()));
15!
4852

10!
4853
            auto q3 =
16!
4854
                cptr_checked(realm_query_append_query(q.get(), "string == $0 SORT(int DESCENDING)", 1, arg_list));
15✔
4855
            found_value = rlm_null();
16✔
4856
            found_sorted = false;
16!
4857
            CHECK(checked(realm_query_find_first(q3.get(), &found_value, &found_sorted)));
16!
4858
            CHECK(found_sorted);
16!
4859
            CHECK(found_value.type == RLM_TYPE_LINK);
16!
4860
            CHECK(found_value.link.target_table == class_foo.key);
16!
4861
            CHECK(found_value.link.target == realm_object_get_key(obj2.get()));
15!
4862
        }
16!
4863

220!
4864
        SECTION("results") {
436✔
4865
            auto r = cptr_checked(realm_query_find_all(q.get()));
350✔
4866
            CHECK(!realm_is_frozen(r.get()));
386!
4867

210!
4868
            SECTION("realm_results_is_valid") {
366!
4869
                bool valid;
40!
4870
                CHECK(checked(realm_results_is_valid(r.get(), &valid)));
66!
4871
                CHECK(valid);
16!
4872
            }
50!
4873

230!
4874
            SECTION("realm_results_is_valid delete objects") {
366✔
4875
                write([&] {
90!
4876
                    realm_object_delete(obj1.get());
90✔
4877
                    realm_object_delete(obj2.get());
66✔
4878
                    realm_results_delete_all(r.get());
16✔
4879
                });
16!
4880
                bool valid;
16!
4881
                CHECK(checked(realm_results_is_valid(r.get(), &valid)));
16!
4882
                CHECK(valid);
40!
4883
            }
66!
4884

160!
4885
            SECTION("realm_results_is_valid delete collection") {
316✔
4886
                auto strings = cptr_checked(realm_get_list(obj2.get(), bar_properties("strings")));
40✔
4887
                CHECK(strings);
66!
4888
                CHECK(!realm_is_frozen(strings.get()));
16!
4889

10!
4890
                realm_value_t a = rlm_str_val("a");
16!
4891
                realm_value_t b = rlm_str_val("b");
15!
4892
                realm_value_t c = rlm_null();
16✔
4893

34✔
4894
                write([&] {
66✔
4895
                    CHECK(checked(realm_list_insert(strings.get(), 0, a)));
15!
4896
                    CHECK(checked(realm_list_insert(strings.get(), 1, b)));
16!
4897
                    CHECK(checked(realm_list_insert(strings.get(), 2, c)));
16!
4898
                });
15!
4899
                bool valid;
16!
4900
                auto results = cptr_checked(realm_list_to_results(strings.get()));
16✔
4901
                CHECK(checked(realm_results_is_valid(results.get(), &valid)));
16!
4902
                CHECK(valid);
15!
4903

10!
4904
                write([&] {
16!
4905
                    CHECK(checked(realm_object_delete(obj2.get())));
15!
4906
                });
16!
4907

10!
4908
                CHECK(checked(realm_results_is_valid(results.get(), &valid)));
16!
4909
                CHECK_FALSE(valid);
15!
4910
                size_t count;
16!
4911

10!
4912
                CHECK_FALSE(realm_results_count(results.get(), &count));
15!
4913
                CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
15!
4914

10!
4915
                CHECK_FALSE(realm_results_resolve_in(results.get(), realm));
16!
4916
                CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
14!
4917
            }
16!
4918

160!
4919
            SECTION("realm_results_count()") {
316✔
4920
                size_t count;
39✔
4921
                CHECK(checked(realm_results_count(r.get(), &count)));
66!
4922
                CHECK(count == 1);
16!
4923

9!
4924
                SECTION("cloned") {
16!
4925
                    auto r2 = clone_cptr(r.get());
15!
4926
                    size_t count2;
16✔
4927
                    CHECK(checked(realm_results_count(r2.get(), &count2)));
40!
4928
                    CHECK(count == count2);
66!
4929
                }
16!
4930
            }
16!
4931

160!
4932
            SECTION("empty result") {
315✔
4933
                auto q2 = cptr_checked(realm_query_parse(realm, class_foo.key, "string == 'boogeyman'", 0, nullptr));
40✔
4934
                auto r2 = cptr_checked(realm_query_find_all(q2.get()));
66✔
4935
                size_t count;
16✔
4936
                CHECK(checked(realm_results_count(r2.get(), &count)));
16!
4937
                CHECK(count == 0);
16!
4938
                realm_value_t value = rlm_null();
16!
4939
                CHECK(!realm_results_get(r2.get(), 0, &value));
16!
4940
                CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
40!
4941
                size_t index = -1;
66!
4942
                bool found = false;
16!
4943
                CHECK(realm_results_find(r2.get(), &value, &index, &found));
16!
4944
                CHECK(index == realm::not_found);
16!
4945
                CHECK(found == false);
16!
4946
            }
16!
4947

160!
4948
            SECTION("realm_results_get()") {
316!
4949
                realm_value_t value = rlm_null();
40!
4950
                CHECK(checked(realm_results_get(r.get(), 0, &value)));
66!
4951
                CHECK(value.type == RLM_TYPE_LINK);
16!
4952
                CHECK(value.link.target_table == class_foo.key);
16!
4953
                CHECK(value.link.target == realm_object_get_key(obj1.get()));
16!
4954
                size_t index = -1;
16!
4955
                bool found = false;
16!
4956
                CHECK(realm_results_find(r.get(), &value, &index, &found));
40!
4957
                CHECK(index == 0);
66!
4958
                CHECK(found == true);
16!
4959

10!
4960
                value = rlm_null();
16!
4961
                CHECK(!realm_results_get(r.get(), 1, &value));
15!
4962
                CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
16!
4963
                index = -1;
16!
4964
                found = false;
16!
4965
                CHECK(realm_results_find(r.get(), &value, &index, &found));
16!
4966
                CHECK(index == realm::not_found);
16!
4967
                CHECK(found == false);
16!
4968
            }
15!
4969

160!
4970
            SECTION("realm_results_get_query()") {
316!
4971
                auto q2 = cptr_checked(realm_query_parse(realm, class_foo.key, "int == 123", 0, nullptr));
40!
4972
                auto r2 = cptr_checked(realm_results_filter(r.get(), q2.get()));
66✔
4973
                size_t count;
16✔
4974
                CHECK(checked(realm_results_count(r2.get(), &count)));
16!
4975
                CHECK(count == 1);
16!
4976
                auto results_query = cptr_checked(realm_results_get_query(r2.get()));
16!
4977
                auto result = cptr_checked(realm_query_find_all(results_query.get()));
16!
4978
                size_t count1 = 0;
40✔
4979
                CHECK(checked(realm_results_count(result.get(), &count1)));
66!
4980
                CHECK(count == count1);
16!
4981
            }
16!
4982

160!
4983
            SECTION("realm_results_get_object()") {
316!
4984
                auto p = cptr_checked(realm_results_get_object(r.get(), 0));
40!
4985
                CHECK(p.get());
66!
4986
                CHECK(realm_equals(p.get(), obj1.get()));
16!
4987
                size_t index = -1;
16!
4988
                bool found = false;
16!
4989
                CHECK(realm_results_find_object(r.get(), p.get(), &index, &found));
16!
4990
                CHECK(found == true);
16!
4991
                CHECK(index == 0);
40!
4992

60!
4993
                CHECK(!realm_results_get_object(r.get(), 1));
16!
4994
                CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
15!
4995
                CHECK(!realm_results_find_object(r.get(), obj2.get(), &index, &found));
16!
4996
                CHECK(found == false);
16!
4997
                CHECK(index == realm::not_found);
16!
4998
            }
16!
4999

160!
5000
            SECTION("realm_results_filter()") {
316!
5001
                auto q2 = cptr_checked(realm_query_parse(realm, class_foo.key, "int == 789", 0, nullptr));
39✔
5002
                auto r2 = cptr_checked(realm_results_filter(r.get(), q2.get()));
66!
5003
                size_t count;
16!
5004
                CHECK(checked(realm_results_count(r2.get(), &count)));
16!
5005
                CHECK(count == 0);
16!
5006
            }
16!
5007

160!
5008
            SECTION("realm_results_sort()") {
340✔
5009
                auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
90✔
5010
                auto p = cptr_checked(realm_results_get_object(r_all.get(), 0));
66✔
5011
                CHECK(p.get());
16!
5012
                CHECK(realm_equals(p.get(), obj1.get()));
16!
5013
                auto r2 = cptr_checked(realm_results_sort(r_all.get(), "int DESCENDING, float ASCENDING"));
16!
5014
                p = cptr_checked(realm_results_get_object(r2.get(), 1));
16!
5015
                CHECK(p.get());
16!
5016
                CHECK(realm_equals(p.get(), obj1.get()));
40!
5017
            }
66!
5018

160!
5019
            SECTION("realm_results_distinct()") {
316✔
5020
                auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
40!
5021
                size_t count;
66!
5022
                realm_results_count(r_all.get(), &count);
16✔
5023
                CHECK(count == 3);
16!
5024
                auto r2 = cptr_checked(realm_results_distinct(r_all.get(), "int"));
16!
5025
                realm_results_count(r2.get(), &count);
16!
5026
                CHECK(count == 2);
16!
5027
            }
40✔
5028

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

210!
5039
            SECTION("realm_results_snapshot()") {
316✔
5040
                auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
40✔
5041
                auto r_snapshot = cptr_checked(realm_results_snapshot(r_all.get()));
66✔
5042
                size_t count;
16!
5043
                realm_results_count(r_all.get(), &count);
16✔
5044
                CHECK(count == 3);
16!
5045
                realm_results_count(r_snapshot.get(), &count);
16!
5046
                CHECK(count == 3);
16!
5047
                write([&]() {
40✔
5048
                    auto p = cptr_checked(realm_results_get_object(r_all.get(), 0));
66!
5049
                    realm_object_delete(p.get());
16✔
5050
                });
16✔
5051
                realm_results_count(r_all.get(), &count);
16✔
5052
                CHECK(count == 2);
16!
5053
                realm_results_count(r_snapshot.get(), &count);
16!
5054
                CHECK(count == 3);
16!
5055
            }
16!
5056

160!
5057
            SECTION("realm_results_min()") {
316✔
5058
                realm_value_t value = rlm_null();
40✔
5059
                CHECK(checked(realm_results_min(r.get(), foo_int_key, &value, &found)));
66!
5060
                CHECK(found);
16!
5061
                CHECK(value.type == RLM_TYPE_INT);
16!
5062
                CHECK(value.integer == 123);
16!
5063

10!
5064
                CHECK(!realm_results_min(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
16!
5065
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
39!
5066
            }
66!
5067

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

10!
5075
                CHECK(!realm_results_max(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
16!
5076
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
39!
5077
            }
66!
5078

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

10!
5086
                size_t count;
16!
5087
                realm_results_count(r.get(), &count);
39✔
5088
                CHECK(count == 1);
66!
5089

10✔
5090
                CHECK(!realm_results_sum(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
16!
5091
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
15!
5092
            }
16!
5093

160!
5094
            SECTION("realm_results_average()") {
315✔
5095
                realm_value_t value = rlm_null();
40✔
5096
                CHECK(checked(realm_results_average(r.get(), foo_int_key, &value, &found)));
66!
5097
                CHECK(found);
16!
5098
                CHECK(value.type == RLM_TYPE_DOUBLE);
15!
5099
                CHECK(value.dnum == 123.0);
16!
5100

10!
5101
                CHECK(!realm_results_average(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
16!
5102
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
39!
5103
            }
66!
5104

160!
5105
            SECTION("realm_results_delete_all()") {
316!
5106
                CHECK(!realm_results_delete_all(r.get()));
40!
5107
                CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
66!
5108

10!
5109
                write([&]() {
15!
5110
                    size_t num_objects;
15!
5111
                    CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_objects)));
16!
5112
                    CHECK(num_objects == 3);
16!
5113
                    CHECK(checked(realm_results_delete_all(r.get())));
40!
5114
                    CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_objects)));
66!
5115
                    CHECK(num_objects == 2);
16!
5116
                });
16!
5117
            }
15!
5118

160✔
5119
            SECTION("lists") {
316✔
5120
                auto list = cptr_checked(realm_get_list(obj1.get(), foo_properties("link_list")));
40!
5121
                cptr_checked(realm_query_parse_for_list(list.get(), "TRUEPREDICATE", 0, nullptr));
66!
5122
            }
16!
5123

160!
5124
            SECTION("lists append query") {
316!
5125
                auto list = cptr_checked(realm_get_list(obj1.get(), foo_properties("link_list")));
40✔
5126

60✔
5127
                auto bar_link = realm_object_as_link(obj2.get());
40✔
5128
                realm_value_t bar_link_val;
65✔
5129
                bar_link_val.type = RLM_TYPE_LINK;
16✔
5130
                bar_link_val.link = bar_link;
16✔
5131

10✔
5132
                write([&]() {
40✔
5133
                    CHECK(checked(realm_list_insert(list.get(), 0, bar_link_val)));
65!
5134
                    CHECK(checked(realm_list_insert(list.get(), 1, bar_link_val)));
16!
5135
                    CHECK(checked(realm_list_insert(list.get(), 2, bar_link_val)));
15!
5136
                });
16!
5137

10!
5138
                size_t n = 0;
16✔
5139
                realm_list_size(list.get(), &n);
15✔
5140
                CHECK(n == 3);
15!
5141
                auto query = cptr_checked(realm_query_parse_for_list(list.get(), "TRUEPREDICATE ", 0, nullptr));
16✔
5142
                n = 0;
16!
5143
                realm_query_count(query.get(), &n);
16!
5144
                CHECK(n == 3);
16!
5145

10✔
5146
                write([&]() {
15!
5147
                    realm_list_clear(list.get());
15✔
5148
                });
16✔
5149
            }
16!
5150

160✔
5151
            SECTION("combine results query") {
316✔
5152
                realm_value_t int_arg = rlm_int_val(123);
40✔
5153
                const size_t num_args = 1;
66!
5154
                realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &int_arg}};
15✔
5155
                realm_query_arg_t* arg_list = &args[0];
16✔
5156
                auto q_int = cptr_checked(realm_query_parse(realm, class_foo.key, "int == $0", num_args, arg_list));
16✔
5157
                auto combined_result_q =
16✔
5158
                    cptr_checked(realm_query_parse_for_results(r.get(), q_int->get_description(), 0, nullptr));
16✔
5159
                auto result = cptr_checked(realm_query_find_all(combined_result_q.get()));
40✔
5160
                size_t count;
66✔
5161
                CHECK(realm_results_count(result.get(), &count));
16!
5162
                CHECK(count == 1);
16!
5163
            }
16!
5164

160!
5165
            SECTION("empty results") {
316✔
5166
                auto empty_q = cptr_checked(realm_query_parse_for_results(r.get(), "FALSEPREDICATE", 0, nullptr));
88✔
5167
                auto empty_r = cptr_checked(realm_query_find_all(empty_q.get()));
114✔
5168

42✔
5169
                SECTION("realm_results_count()") {
72✔
5170
                    size_t count;
19!
5171
                    CHECK(realm_results_count(empty_r.get(), &count));
24!
5172
                    CHECK(count == 0);
16!
5173
                }
40!
5174

84!
5175
                SECTION("realm_results_min()") {
72✔
5176
                    realm_value_t value;
27✔
5177
                    bool found = true;
27✔
5178
                    CHECK(realm_results_min(empty_r.get(), foo_int_key, &value, &found));
24!
5179
                    CHECK(rlm_val_eq(value, rlm_null()));
16!
5180
                    CHECK(!found);
16!
5181
                }
16!
5182

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

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

34!
5199
                SECTION("realm_results_average()") {
67✔
5200
                    realm_value_t value;
27✔
5201
                    bool found = true;
24✔
5202
                    CHECK(realm_results_average(empty_r.get(), foo_int_key, &value, &found));
16!
5203
                    CHECK(rlm_val_eq(value, rlm_null()));
16!
5204
                    CHECK(!found);
16!
5205
                }
16!
5206
            }
64!
5207
        }
319✔
5208
    }
452✔
5209

282✔
5210
    realm_close(realm);
530✔
5211
    REQUIRE(realm_is_closed(realm));
496!
5212
    realm_release(realm);
534!
5213
}
534!
5214

78✔
5215
TEST_CASE("C API: nested collections", "[c_api]") {
170✔
5216
    TestFile test_file;
136✔
5217
    realm_t* realm;
170✔
5218
    ObjectSchema object_schema = {"Foo",
136✔
5219
                                  {
174✔
5220
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
174!
5221
                                      {"any", PropertyType::Mixed | PropertyType::Nullable},
174✔
5222
                                  }};
174✔
5223

56✔
5224
    auto config = make_config(test_file.path.c_str(), false);
112✔
5225
    config->schema = Schema{object_schema};
105✔
5226
    config->schema_version = 0;
112✔
5227
    realm = realm_open(config.get());
112✔
5228

70✔
5229
    realm_class_info_t class_foo;
112✔
5230
    bool found = false;
105✔
5231
    CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
112!
5232
    REQUIRE(found);
105!
5233

70!
5234
    realm_property_info_t info;
112!
5235
    found = false;
105✔
5236
    REQUIRE(realm_find_property(realm, class_foo.key, "any", &found, &info));
112!
5237
    REQUIRE(found);
105!
5238
    CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
112!
5239
    realm_property_key_t foo_any_col_key = info.key;
112!
5240

70!
5241
    CPtr<realm_object_t> obj1;
112!
5242
    checked(realm_begin_write(realm));
98✔
5243
    realm_value_t pk = rlm_int_val(42);
112✔
5244
    obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_foo.key, pk));
112✔
5245

70!
5246
    auto write = [&](auto&& f) {
94!
5247
        checked(realm_begin_write(realm));
69!
5248
        f();
73✔
5249
        checked(realm_commit(realm));
63✔
5250
        checked(realm_refresh(realm, nullptr));
70✔
5251
    };
70✔
5252

64✔
5253
    SECTION("results of mixed") {
106✔
5254
        SECTION("dictionary") {
38✔
5255
            auto parent_dict = cptr_checked(realm_set_dictionary(obj1.get(), foo_any_col_key));
37✔
5256
            REQUIRE(parent_dict);
24!
5257
            realm_value_t value;
22✔
5258
            realm_get_value(obj1.get(), foo_any_col_key, &value);
22!
5259
            REQUIRE(value.type == RLM_TYPE_DICTIONARY);
22!
5260
            auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
22✔
5261
            auto nlist = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("A")));
21!
5262
            auto ndict = cptr_checked(realm_dictionary_insert_dictionary(dict.get(), rlm_str_val("B")));
28✔
5263

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

12✔
5291
            // verify that we can fetch a collection from a result of mixed
10✔
5292
            auto results = cptr_checked(realm_list_to_results(list.get()));
15!
5293
            const auto sz = results->size();
15✔
5294
            REQUIRE(sz == list->size());
16!
5295
            REQUIRE(results->is_valid());
16!
5296
            realm_value_t val;
16!
5297
            realm_results_get(results.get(), 0, &val);
16!
5298
            REQUIRE(val.type == RLM_TYPE_LIST);
16!
5299
            realm_results_get(results.get(), 1, &val);
15✔
5300
            REQUIRE(val.type == RLM_TYPE_DICTIONARY);
15!
5301
            auto result_list = cptr_checked(realm_results_get_list(results.get(), 0));
16✔
5302
            REQUIRE(result_list);
16!
5303
            REQUIRE(result_list->size() == nlist->size());
16!
5304
            auto result_dictionary = cptr_checked(realm_results_get_dictionary(results.get(), 1));
16!
5305
            REQUIRE(result_dictionary);
16!
5306
            REQUIRE(result_dictionary->size() == ndict->size());
16!
5307
        }
16!
5308
    }
28!
5309

46!
5310
    SECTION("dictionary") {
90✔
5311
        struct UserData {
21!
5312
            size_t deletions;
28!
5313
            size_t insertions;
16✔
5314
            size_t modifications;
16!
5315
            bool was_deleted;
16!
5316
            realm_dictionary_t* dict;
16✔
5317
        } user_data;
18✔
5318

15✔
5319
        auto parent_dict = cptr_checked(realm_set_dictionary(obj1.get(), foo_any_col_key));
28✔
5320
        REQUIRE(parent_dict);
15!
5321
        realm_value_t value;
16✔
5322
        realm_get_value(obj1.get(), foo_any_col_key, &value);
16!
5323
        REQUIRE(value.type == RLM_TYPE_DICTIONARY);
16!
5324
        auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
16✔
5325
        checked(realm_dictionary_insert(dict.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr, nullptr));
16!
5326
        // dict -> list
10✔
5327
        auto list = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("Goodbye")));
15✔
5328
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
15✔
5329
        realm_list_insert(list.get(), 0, rlm_str_val("42"));
16!
5330
        realm_list_insert(list.get(), 0, rlm_int_val(42));
16✔
5331
        // dict -> dict
10✔
5332
        auto dict2 = cptr_checked(realm_dictionary_insert_dictionary(dict.get(), rlm_str_val("Hi")));
16!
5333
        user_data.dict = dict2.get();
15✔
5334
        checked(realm_dictionary_insert(dict2.get(), rlm_str_val("Nested-Hello"), rlm_str_val("Nested-World"),
16✔
5335
                                        nullptr, nullptr));
15✔
5336
        checked(realm_commit(realm));
16✔
5337

10✔
5338
        auto on_dictionary_change = [](void* data, const realm_dictionary_changes_t* changes) {
40✔
5339
            auto* user_data = static_cast<UserData*>(data);
39✔
5340
            realm_dictionary_get_changes(changes, &user_data->deletions, &user_data->insertions,
43✔
5341
                                         &user_data->modifications, &user_data->was_deleted);
44✔
5342
            if (user_data->was_deleted) {
44✔
5343
                CHECK(!realm_dictionary_is_valid(user_data->dict));
20!
5344
            }
20✔
5345
        };
40!
5346
        auto require_change = [&]() {
15✔
5347
            auto token = cptr_checked(realm_dictionary_add_notification_callback(dict2.get(), &user_data, nullptr,
24✔
5348
                                                                                 nullptr, on_dictionary_change));
20✔
5349
            checked(realm_refresh(realm, nullptr));
20✔
5350
            return token;
20✔
5351
        };
20✔
5352

10!
5353
        auto token = require_change();
16✔
5354

13✔
5355
        write([&] {
16✔
5356
            checked(realm_dictionary_insert(dict2.get(), rlm_str_val("Nested-Godbye"),
15✔
5357
                                            rlm_str_val("Nested-CruelWorld"), nullptr, nullptr));
16✔
5358
        });
16✔
5359
        CHECK(user_data.insertions == 1);
16!
5360

10✔
5361
        write([&] {
15!
5362
            realm_dictionary_insert(dict.get(), rlm_str_val("Hi"), rlm_str_val("Foo"), nullptr, nullptr);
15✔
5363
        });
15✔
5364
        CHECK(user_data.deletions == 2);
16!
5365
        CHECK(user_data.was_deleted);
16!
5366
    }
16!
5367

46!
5368
    SECTION("list") {
88!
5369
        struct UserData {
20✔
5370
            size_t deletions;
28✔
5371
            size_t insertions;
16✔
5372
            size_t modifications;
16✔
5373
            bool was_deleted;
16!
5374
            realm_list_t* list;
16!
5375
        } user_data;
16✔
5376

15✔
5377
        auto parent_list = cptr_checked(realm_set_list(obj1.get(), foo_any_col_key));
28✔
5378
        REQUIRE(parent_list);
15!
5379
        realm_value_t value;
16✔
5380
        realm_get_value(obj1.get(), foo_any_col_key, &value);
16!
5381
        REQUIRE(value.type == RLM_TYPE_LIST);
16!
5382
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
16✔
5383
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
16!
5384
        realm_list_insert(list.get(), 1, rlm_str_val("World"));
16✔
5385
        // list -> dict
9✔
5386
        auto dict = cptr_checked(realm_list_insert_dictionary(list.get(), 1));
16✔
5387
        checked(realm_dictionary_insert(dict.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr, nullptr));
15!
5388
        // list -> list
10✔
5389
        auto list2 = cptr_checked(realm_list_insert_list(list.get(), 2));
16✔
5390
        user_data.list = list2.get();
15!
5391

10✔
5392
        checked(realm_commit(realm));
16✔
5393

9✔
5394
        auto on_list_change = [](void* data, const realm_collection_changes_t* changes) {
39✔
5395
            auto* user_data = static_cast<UserData*>(data);
39✔
5396
            realm_collection_changes_get_num_changes(changes, &user_data->deletions, &user_data->insertions,
44✔
5397
                                                     &user_data->modifications, nullptr, nullptr,
43✔
5398
                                                     &user_data->was_deleted);
44✔
5399
            if (user_data->was_deleted) {
44✔
5400
                CHECK(!realm_list_is_valid(user_data->list));
19!
5401
            }
20✔
5402
        };
39!
5403
        auto require_change = [&]() {
20✔
5404
            auto token = cptr_checked(
24✔
5405
                realm_list_add_notification_callback(list2.get(), &user_data, nullptr, nullptr, on_list_change));
20✔
5406
            checked(realm_refresh(realm, nullptr));
20✔
5407
            return token;
20✔
5408
        };
20✔
5409

10!
5410
        auto token = require_change();
16✔
5411

13✔
5412
        write([&] {
16✔
5413
            realm_list_insert(list2.get(), 0, rlm_str_val("Nested-Hello"));
15✔
5414
            realm_list_insert(list2.get(), 1, rlm_str_val("Nested-World"));
16✔
5415
        });
16✔
5416
        CHECK(user_data.insertions == 2);
16!
5417

10✔
5418
        write([&] {
15!
5419
            realm_list_set(list.get(), 2, rlm_str_val("Foo"));
15✔
5420
        });
15✔
5421
        CHECK(user_data.deletions == 2);
16!
5422
        CHECK(user_data.was_deleted);
16!
5423
    }
16!
5424

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

9!
5451
        CHECK(realm_list_set(list.get(), 0, rlm_int_val(5)));
16!
5452
        // accessor invalid
9✔
5453
        REQUIRE(!realm_dictionary_insert(n_dict.get(), key, val, &ndx, &inserted));
16!
5454
        CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
15!
5455
        realm_value_t out;
16!
5456
        CHECK(realm_list_get(list.get(), 0, &out));
16!
5457

10!
5458
        n_list = cptr_checked(realm_list_set_list(list.get(), 0));
16!
5459
        // get a list should work
8✔
5460
        n_list = cptr_checked(realm_list_get_list(list.get(), 0));
16!
5461
        REQUIRE(realm_list_insert(n_list.get(), 0, rlm_str_val("Test1")));
14!
5462
        // reset the collection type to the same type (nop)
10!
5463
        n_list = cptr_checked(realm_list_set_list(list.get(), 0));
16!
5464
        // accessor is still valid
9✔
5465
        REQUIRE(realm_list_insert(n_list.get(), 0, rlm_str_val("Test2")));
16!
5466
        checked(realm_list_size(n_list.get(), &size));
14✔
5467
        REQUIRE(size == 2);
16!
5468
    }
15✔
5469

46!
5470
    SECTION("json") {
88!
5471
        REQUIRE(realm_set_json(
20!
5472
            obj1.get(), foo_any_col_key,
28✔
5473
            R"( [ { "Seven":7, "Six":6 }, "Hello", { "Points": [1.25, 4.5, 6.75], "Hello": "World" } ])"));
15!
5474
        realm_value_t value;
16!
5475
        realm_get_value(obj1.get(), foo_any_col_key, &value);
16✔
5476
        REQUIRE(value.type == RLM_TYPE_LIST);
16!
5477
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
16✔
5478
        size_t size;
21!
5479
        checked(realm_list_size(list.get(), &size));
28✔
5480
        CHECK(size == 3);
16!
5481
    }
16✔
5482

46!
5483
    SECTION("freeze list") {
88✔
5484
        auto parent_dict = cptr_checked(realm_set_dictionary(obj1.get(), foo_any_col_key));
21✔
5485
        REQUIRE(parent_dict);
28!
5486
        auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
16✔
5487
        auto list = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("List")));
16!
5488
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
16✔
5489
        realm_list_insert(list.get(), 0, rlm_str_val("42"));
16!
5490
        checked(realm_commit(realm));
16✔
5491
        size_t size;
21✔
5492
        checked(realm_list_size(list.get(), &size));
28✔
5493
        REQUIRE(size == 2);
16!
5494
        auto frozen_realm = cptr_checked(realm_freeze(realm));
16!
5495

10!
5496
        realm_list_t* frozen_list;
16✔
5497
        realm_list_resolve_in(list.get(), frozen_realm.get(), &frozen_list);
15✔
5498
        checked(realm_list_size(frozen_list, &size));
16✔
5499
        REQUIRE(size == 2);
16!
5500
        realm_release(frozen_list);
16✔
5501
    }
16!
5502
    realm_release(realm);
88!
5503
}
88✔
5504

15✔
5505
TEST_CASE("C API: convert", "[c_api]") {
64✔
5506
    TestFile test_file;
50✔
5507
    TestFile dest_test_file;
58✔
5508
    realm_t* realm;
58!
5509
    ObjectSchema object_schema = {"Foo",
58✔
5510
                                  {
58✔
5511
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
70✔
5512
                                      {"string_value", PropertyType::String},
70✔
5513
                                  }};
56✔
5514

40✔
5515
    { // seed a Realm with an object
64✔
5516
        auto config = make_config(test_file.path.c_str(), false);
60✔
5517
        config->schema = Schema{object_schema};
64✔
5518
        config->schema_version = 0;
64✔
5519
        realm = realm_open(config.get());
64✔
5520
        REQUIRE(checked(realm));
64!
5521
        CHECK(!realm_equals(realm, nullptr));
64!
5522
        realm_class_info_t class_foo;
64!
5523
        bool found = false;
60!
5524
        CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
64!
5525
        REQUIRE(found);
64!
5526

40!
5527
        realm_property_key_t foo_str_col_key;
64!
5528
        realm_property_info_t info;
60✔
5529
        found = false;
64!
5530
        REQUIRE(realm_find_property(realm, class_foo.key, "string_value", &found, &info));
64!
5531
        REQUIRE(found);
64!
5532
        CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
64!
5533
        foo_str_col_key = info.key;
64!
5534

40!
5535
        CPtr<realm_object_t> obj1;
60✔
5536
        checked(realm_begin_write(realm));
60✔
5537
        realm_value_t pk = rlm_int_val(42);
64✔
5538
        obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_foo.key, pk));
64✔
5539
        CHECK(obj1);
64!
5540
        CHECK(checked(realm_set_value(obj1.get(), foo_str_col_key, rlm_str_val("Hello, World!"), false)));
64!
5541
        checked(realm_commit(realm));
64!
5542
        checked(realm_refresh(realm, nullptr));
64!
5543

36✔
5544
        size_t foo_count;
64✔
5545
        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &foo_count)));
60!
5546
        REQUIRE(foo_count == 1);
64!
5547
    }
64!
5548

40!
5549
    CHECK(realm_get_num_classes(realm) == 1);
64!
5550

36✔
5551
    SECTION("convert with path") {
64!
5552
        bool merge_with_existing = false;
32✔
5553
        realm_binary encryption_key{nullptr, 0};
40✔
5554

24!
5555
        REQUIRE(realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
36!
5556

22✔
5557
        SECTION("convert again without merge should fail") {
32!
5558
            REQUIRE_FALSE(
22!
5559
                realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
20✔
5560
        }
22!
5561
        SECTION("convert again with merge should succeed") {
30✔
5562
            merge_with_existing = true;
18✔
5563
            REQUIRE(realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
18!
5564
        }
18!
5565
    }
28!
5566

30✔
5567
    SECTION("convert with config") {
54!
5568
        auto dest_config = make_config(dest_test_file.path.c_str(), false);
30✔
5569
        dest_config->schema = Schema{object_schema};
34✔
5570
        dest_config->schema_version = 0;
32✔
5571
        bool merge_with_existing = false;
30✔
5572
        REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
30!
5573
        SECTION("convert again without merge should fail") {
30✔
5574
            REQUIRE_FALSE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
20!
5575
        }
20✔
5576
        SECTION("convert again with merge should succeed") {
34!
5577
            merge_with_existing = true;
18✔
5578
            REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
20!
5579
        }
18✔
5580
    }
30!
5581
    realm_close(realm);
54!
5582
    REQUIRE(realm_is_closed(realm));
56!
5583
    realm_release(realm);
58!
5584
}
58!
5585

12✔
5586
struct Userdata {
10✔
5587
    std::atomic<bool> called{false};
2!
5588
    bool has_error;
2✔
5589
    realm_error_t error;
4✔
5590
    realm_thread_safe_reference_t* realm_ref = nullptr;
8✔
5591
    std::string error_message;
8!
5592
};
8✔
5593

8✔
5594
#if REALM_ENABLE_SYNC
5595

5596
static void task_completion_func(void* p, realm_thread_safe_reference_t* realm,
5597
                                 const realm_async_error_t* async_error)
5598
{
24✔
5599
    auto userdata_p = static_cast<Userdata*>(p);
24✔
5600

16✔
5601
    userdata_p->realm_ref = realm;
28✔
5602
    userdata_p->has_error = realm_get_async_error(async_error, &userdata_p->error);
26✔
5603
    userdata_p->called = true;
28✔
5604
}
28✔
5605

4✔
5606
static void task_init_subscription(realm_thread_safe_reference_t* realm, void*)
4✔
5607
{
4✔
5608
    REQUIRE(realm);
4!
5609
}
2✔
5610

4!
5611
static void sync_error_handler(void* p, realm_sync_session_t*, const realm_sync_error_t error)
4✔
5612
{
16✔
5613
    auto userdata_p = static_cast<Userdata*>(p);
16✔
5614
    userdata_p->has_error = true;
14✔
5615
    userdata_p->error_message = error.status.message;
14✔
5616
    userdata_p->error.error = error.status.error;
14✔
5617
    userdata_p->error.categories = error.status.categories;
14!
5618
    userdata_p->error.message = userdata_p->error_message.c_str();
14✔
5619
}
14✔
5620

2✔
5621
TEST_CASE("C API - async_open", "[sync][pbs][c_api]") {
28✔
5622
    TestSyncManager init_sync_manager;
26✔
5623
    SyncTestFile test_config(init_sync_manager, "default");
30✔
5624
    ObjectSchema object_schema = {"object",
30✔
5625
                                  {
30✔
5626
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
30✔
5627
                                      {"value", PropertyType::Int},
30✔
5628
                                  }};
30✔
5629
    test_config.schema = Schema{object_schema};
28✔
5630

20✔
5631
    SECTION("can open synced Realms that don't already exist") {
32✔
5632
        realm_config_t* config = realm_config_new();
18✔
5633
        config->schema = Schema{object_schema};
20✔
5634
        realm_user user(test_config.sync_config->user);
18✔
5635
        realm_sync_config_t* sync_config = realm_sync_config_new(&user, "default");
18✔
5636
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
18✔
5637
                                                           nullptr);
18✔
5638
        realm_config_set_path(config, test_config.path.c_str());
18✔
5639
        realm_config_set_sync_config(config, sync_config);
16✔
5640
        realm_config_set_schema_version(config, 1);
18✔
5641
        realm_async_open_task_t* task = realm_open_synchronized(config);
16✔
5642
        REQUIRE(task);
16!
5643
        Userdata userdata;
16✔
5644
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
16!
5645
        util::EventLoop::main().run_until([&] {
81,959✔
5646
            return userdata.called.load();
81,959✔
5647
        });
100,138✔
5648
        REQUIRE(userdata.called);
18,195!
5649
        REQUIRE(userdata.realm_ref);
18,195!
5650
        realm_release(task);
16!
5651

10!
5652
        realm_t* realm = realm_from_thread_safe_reference(userdata.realm_ref, nullptr);
16✔
5653
        realm_release(userdata.realm_ref);
15✔
5654

16,276✔
5655
        bool found;
16,282✔
5656
        realm_class_info_t class_info;
16,281✔
5657
        realm_find_class(realm, "object", &found, &class_info);
16!
5658
        REQUIRE(found);
16!
5659
        realm_release(realm);
16✔
5660
        realm_release(config);
15!
5661
        realm_release(sync_config);
16✔
5662
    }
16✔
5663

15✔
5664
    SECTION("cancels download and reports an error on auth error") {
28✔
5665
        auto expired_token = encode_fake_jwt("", 123, 456);
16✔
5666

12✔
5667
        struct Transport : UnitTestTransport {
16!
5668
            void send_request_to_server(
15✔
5669
                const realm::app::Request& req,
16✔
5670
                realm::util::UniqueFunction<void(const realm::app::Response&)>&& completion) override
16✔
5671
            {
28✔
5672
                if (req.url.find("/auth/session") != std::string::npos) {
28✔
5673
                    completion(app::Response{403});
20✔
5674
                }
18✔
5675
                else {
16✔
5676
                    UnitTestTransport::send_request_to_server(req, std::move(completion));
16✔
5677
                }
16✔
5678
            }
28✔
5679
        };
16✔
5680
        OfflineAppSession::Config oas_config;
18✔
5681
        oas_config.transport = std::make_shared<Transport>();
16✔
5682
        OfflineAppSession oas(oas_config);
16✔
5683
        SyncTestFile test_config(oas, "realm");
16✔
5684
        test_config.sync_config->user->log_in(expired_token, expired_token);
16✔
5685

10✔
5686
        realm_config_t* config = realm_config_new();
16✔
5687
        config->schema = Schema{object_schema};
15✔
5688
        realm_user user(test_config.sync_config->user);
16✔
5689
        realm_sync_config_t* sync_config = realm_sync_config_new(&user, "realm");
15✔
5690
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
16✔
5691
                                                           nullptr);
16✔
5692
        realm_config_set_path(config, test_config.path.c_str());
16✔
5693
        realm_config_set_schema_version(config, 1);
16✔
5694
        Userdata userdata;
16✔
5695
        realm_sync_config_set_error_handler(sync_config, sync_error_handler, &userdata, nullptr);
16✔
5696
        realm_config_set_sync_config(config, sync_config);
15✔
5697

10✔
5698
        realm_async_open_task_t* task = realm_open_synchronized(config);
16✔
5699
        REQUIRE(task);
15!
5700
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
16✔
5701
        util::EventLoop::main().run_until([&] {
22!
5702
            return userdata.called.load();
21✔
5703
        });
23✔
5704
        REQUIRE(userdata.called);
17!
5705
        REQUIRE(!userdata.realm_ref);
17!
5706
        REQUIRE(userdata.error.error == RLM_ERR_AUTH_ERROR);
17!
5707
        REQUIRE(userdata.error_message ==
17!
5708
                "Unable to refresh the user access token: http error code considered fatal. Client Error: 403");
17!
5709
        realm_release(task);
16!
5710
        realm_release(config);
16!
5711
        realm_release(sync_config);
16!
5712
    }
16!
5713
}
28✔
5714

4✔
5715
struct BCTOState {
6✔
5716
    bool bcto_deleted = false;
2✔
5717
    bool thread_create_called = false;
4✔
5718
    bool thread_destroy_called = false;
5719
    std::string thread_on_error_message;
5720
    std::string id = "BTCO-STATE";
5721
};
5722

5723

5724
TEST_CASE("C API - binding callback thread observer", "[sync][c_api]") {
12✔
5725
    auto bcto_user_data = BCTOState();
12✔
5726

8✔
5727
    auto bcto_free_userdata = [](realm_userdata_t userdata) {
14✔
5728
        REQUIRE(userdata);
15!
5729
        auto user_data = static_cast<BCTOState*>(userdata);
16✔
5730
        REQUIRE(user_data->bcto_deleted == false);
15!
5731
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
16!
5732
        user_data->id.clear();
16!
5733
        user_data->bcto_deleted = true;
16!
5734
    };
16!
5735

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

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

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

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

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

10!
5790
        // Wait for the thread to exit
10✔
5791
        test_thread.join();
15!
5792

9✔
5793
        REQUIRE(bcto_user_data.thread_create_called);
15!
5794
        REQUIRE(bcto_user_data.thread_on_error_message.find(
14!
5795
                    "Multiple sync agents attempted to join the same session") != std::string::npos);
16!
5796
        REQUIRE(bcto_user_data.thread_destroy_called);
15!
5797
    }
16!
5798

10!
5799
    REQUIRE(bcto_user_data.bcto_deleted == true);
16!
5800

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

3✔
5817
#if REALM_ENABLE_AUTH_TESTS
2✔
5818

2✔
5819
std::atomic_bool baas_client_stop{false};
5820
std::atomic<std::size_t> error_handler_counter{0};
5821
std::atomic<std::size_t> before_client_reset_counter{0};
5822
std::atomic<std::size_t> after_client_reset_counter{0};
5823

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

50✔
5836
    auto server_app_config = minimal_app_config("c_api_client_reset_tests", schema);
80✔
5837
    server_app_config.partition_key = partition_prop;
75✔
5838
    TestAppSession test_app_session(create_app(server_app_config));
80✔
5839

45✔
5840
    auto app = test_app_session.app();
80✔
5841
    auto get_valid_config = [&]() -> SyncTestFile {
135✔
5842
        create_user_and_log_in(app);
140✔
5843
        return SyncTestFile(app->current_user(), partition.value, schema);
145✔
5844
    };
150✔
5845
    SyncTestFile local_config = get_valid_config();
100✔
5846
    SyncTestFile remote_config = get_valid_config();
100✔
5847

60✔
5848
    auto make_reset = [&](Realm::Config config_local,
90✔
5849
                          Realm::Config config_remote) -> std::unique_ptr<reset_utils::TestClientReset> {
75✔
5850
        return reset_utils::make_baas_client_reset(config_local, config_remote, test_app_session);
80✔
5851
    };
75✔
5852

50✔
5853
    local_config.sync_config = std::make_shared<realm_sync_config_t>(*local_config.sync_config);
80✔
5854
    realm_sync_config_t* local_sync_config = static_cast<realm_sync_config_t*>(local_config.sync_config.get());
75✔
5855

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

58✔
5876
    SECTION("Manual reset") {
80✔
5877
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_MANUAL);
27✔
5878

26✔
5879
        realm_sync_config_set_error_handler(
19✔
5880
            local_sync_config,
23✔
5881
            [](realm_userdata_t, realm_sync_session_t*, const realm_sync_error_t sync_error) {
16✔
5882
                REQUIRE(sync_error.c_original_file_path_key);
15!
5883
                REQUIRE(sync_error.c_recovery_file_path_key);
16!
5884
                REQUIRE(sync_error.is_client_reset_requested);
16!
5885
                REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_CLIENT_RESET);
16!
5886
                ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
16!
5887
                baas_client_stop.store(true);
16!
5888
            },
16!
5889
            nullptr, nullptr);
16!
5890

10✔
5891
        make_reset(local_config, remote_config)
16✔
5892
            ->on_post_reset([&](SharedRealm) {
15✔
5893
                util::EventLoop::main().run_until([&] {
3,893✔
5894
                    return baas_client_stop.load();
3,892✔
5895
                });
4,515✔
5896
            })
638✔
5897
            ->run();
1,558✔
5898
    }
936✔
5899

954✔
5900
    SECTION("Local Discard") {
64✔
5901
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_DISCARD_LOCAL);
55✔
5902

36✔
5903
        SECTION("Before client reset success") {
61✔
5904
            realm_sync_config_set_before_client_reset_handler(
26✔
5905
                local_sync_config,
28✔
5906
                [](realm_userdata_t, realm_t*) -> bool {
18✔
5907
                    before_client_reset_counter.fetch_add(1);
22✔
5908
                    return true;
16✔
5909
                },
16✔
5910
                nullptr, nullptr);
16✔
5911

10✔
5912
            realm_sync_config_set_after_client_reset_handler(
16✔
5913
                local_sync_config,
15✔
5914
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
16✔
5915
                    after_client_reset_counter.fetch_add(1);
15✔
5916
                    baas_client_stop.store(true);
16✔
5917
                    return true;
16✔
5918
                },
16✔
5919
                nullptr, nullptr);
16✔
5920

10✔
5921
            make_reset(local_config, remote_config)
16✔
5922
                ->on_post_reset([&](SharedRealm) {
15✔
5923
                    util::EventLoop::main().run_until([&] {
22✔
5924
                        return baas_client_stop.load();
21✔
5925
                    });
23✔
5926
                })
17✔
5927
                ->run();
18✔
5928

11✔
5929
            REQUIRE(before_client_reset_counter.load() == 1);
17!
5930
            REQUIRE(after_client_reset_counter.load() == 1);
15!
5931
        }
16!
5932

27!
5933
        SECTION("Before client reset fails") {
52!
5934
            baas_client_stop.store(false);
18!
5935
            before_client_reset_counter.store(0);
22✔
5936
            after_client_reset_counter.store(0);
18✔
5937

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

10✔
5953
            realm_sync_config_set_before_client_reset_handler(
16✔
5954
                local_sync_config,
15✔
5955
                [](realm_userdata_t, realm_t*) -> bool {
16✔
5956
                    before_client_reset_counter.fetch_add(1);
15✔
5957
                    return false;
16✔
5958
                },
16✔
5959
                nullptr, nullptr);
16✔
5960

10✔
5961
            realm_sync_config_set_after_client_reset_handler(
16✔
5962
                local_sync_config,
15✔
5963
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
10✔
5964
                    after_client_reset_counter.fetch_add(1);
3✔
5965
                    return true;
3✔
5966
                },
2✔
5967
                nullptr, nullptr);
13✔
5968

6✔
5969
            make_reset(local_config, remote_config)
14✔
5970
                ->on_post_reset([&](SharedRealm) {
13✔
5971
                    util::EventLoop::main().run_until([&] {
3,738✔
5972
                        return baas_client_stop.load();
3,737✔
5973
                    });
4,374✔
5974
                })
652✔
5975
                ->run();
1,576✔
5976

934✔
5977
            REQUIRE(error_handler_counter.load() == 1);
940!
5978
            REQUIRE(before_client_reset_counter.load() == 1);
15!
5979
            REQUIRE(after_client_reset_counter.load() == 0);
16!
5980
        }
15!
5981

28!
5982
        SECTION("Simulate failure during client reset with expection in the user code callback") {
52!
5983
            error_handler_counter.store(0);
30!
5984
            baas_client_stop.store(false);
34✔
5985

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

20✔
6011
            SECTION("before reset exception") {
32✔
6012
                realm_sync_config_set_before_client_reset_handler(
18✔
6013
                    local_sync_config,
20✔
6014
                    [](realm_userdata_t uncast_state, realm_t*) -> bool {
16✔
6015
                        auto state = static_cast<ErrorState*>(uncast_state);
18✔
6016
                        realm_register_user_code_callback_error(
16✔
6017
                            reinterpret_cast<void*>(state->target_user_code_data));
16✔
6018
                        return false;
16✔
6019
                    },
16✔
6020
                    &state, nullptr);
16✔
6021

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

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

10✔
6048
                make_reset(local_config, remote_config)
16✔
6049
                    ->on_post_reset([&](SharedRealm) {
15✔
6050
                        util::EventLoop::main().run_until([&] {
2,750✔
6051
                            return baas_client_stop.load();
2,749✔
6052
                        });
3,205✔
6053
                    })
471✔
6054
                    ->run();
1,249✔
6055
            }
794✔
6056
            REQUIRE(error_handler_counter.load() == 1);
806!
6057
            REQUIRE(state.observed_user_code_data == state.target_user_code_data);
28!
6058
        }
30!
6059
    }
54!
6060
}
68!
6061

12!
6062
static void realm_app_void_completion(void*, const realm_app_error_t*) {}
98✔
6063

8✔
6064
static void realm_app_user1(void* p, realm_user_t* user, const realm_app_error_t*)
24✔
6065
{
60✔
6066
    auto clone_ptr = realm_clone(user);
74✔
6067
    CHECK(realm_equals(user, clone_ptr));
70!
6068
    *(static_cast<realm_user_t**>(p)) = static_cast<realm_user_t*>(clone_ptr);
70✔
6069
}
80!
6070

20✔
6071
static void realm_app_user2(void* p, realm_user_t* user, const realm_app_error_t*)
20!
6072
{
22✔
6073
    realm_user_identity_t idents[10];
22✔
6074
    size_t n;
14✔
6075
    realm_user_get_all_identities(user, idents, 10, &n);
14✔
6076
    *static_cast<bool*>(p) = n == 2;
16✔
6077
    for (size_t i = 0; i < n; i++) {
40✔
6078
        realm_free(idents[i].id);
28✔
6079
    }
32✔
6080
}
18✔
6081

10✔
6082
TEST_CASE("C API app: link_user integration w/c_api transport", "[sync][app][c_api][baas]") {
78✔
6083
    struct TestTransportUserData {
76✔
6084
        TestTransportUserData()
86✔
6085
            : logger(util::Logger::get_default_logger())
84✔
6086
            , transport(std::make_unique<SynchronousTestTransport>())
96✔
6087
        {
96✔
6088
        }
96✔
6089
        std::shared_ptr<util::Logger> logger;
96✔
6090
        std::unique_ptr<realm::app::GenericNetworkTransport> transport;
96✔
6091
    };
96✔
6092

60✔
6093
    auto send_request_to_server = [](realm_userdata_t userdata, const realm_http_request_t request,
96✔
6094
                                     void* request_context) {
546✔
6095
        using namespace realm::app;
552✔
6096

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

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

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

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

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

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

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

10✔
6189
        realm_string_t password{creds.password.c_str(), creds.password.length()};
16✔
6190
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
15✔
6191
                                                                realm_app_void_completion, nullptr, nullptr);
15✔
6192

10✔
6193
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
16✔
6194
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user, nullptr);
15✔
6195

9✔
6196
        realm_app_credentials email_creds(creds);
16✔
6197
        realm_app_link_user(&app, sync_user, &email_creds, realm_app_user2, &processed, nullptr);
15✔
6198

9✔
6199
        CHECK(processed);
16!
6200
        realm_release(sync_user);
15✔
6201
    }
15!
6202
    SECTION("realm_app_get_all_users integration") {
76!
6203

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

10!
6215
        realm_user_t* sync_user_2;
16!
6216
        realm_app_switch_user(&app, sync_user_1, &sync_user_2);
15✔
6217
        size_t out_n = 0;
15✔
6218

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

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

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

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

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

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

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

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

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

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

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

10✔
6338
    auto realm = Realm::get_shared_realm(test_config);
15✔
6339

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

10✔
6772
        realm_release(c_wrap_query_foo);
15✔
6773
        realm_release(c_wrap_query_bar);
15✔
6774
    });
16✔
6775
}
16✔
6776

4✔
6777
TEST_CASE("C API app: websocket provider", "[sync][app][c_api][baas]") {
14✔
6778
    using namespace realm::app;
14✔
6779
    using namespace realm::sync;
16✔
6780
    using namespace realm::sync::websocket;
16✔
6781

10✔
6782
    struct TestWebSocketObserverShim : sync::WebSocketObserver {
15✔
6783
    public:
15✔
6784
        explicit TestWebSocketObserverShim(realm_websocket_observer_t* observer)
16✔
6785
            : m_observer(observer)
16✔
6786
        {
16✔
6787
        }
16✔
6788

10✔
6789
        void websocket_connected_handler(const std::string& protocol) override
15✔
6790
        {
15✔
6791
            REALM_ASSERT(m_observer);
16✔
6792
            realm_sync_socket_websocket_connected(m_observer, protocol.c_str());
16✔
6793
        }
16✔
6794

10✔
6795
        void websocket_error_handler() override
15✔
6796
        {
9✔
6797
            REALM_ASSERT(m_observer);
3!
6798
            realm_sync_socket_websocket_error(m_observer);
1!
UNCOV
6799
        }
×
6800

6✔
6801
        bool websocket_binary_message_received(util::Span<const char> data) override
13✔
6802
        {
39✔
6803
            REALM_ASSERT(m_observer);
44✔
6804
            return realm_sync_socket_websocket_message(m_observer, data.data(), data.size());
48✔
6805
        }
48✔
6806

18✔
6807
        bool websocket_closed_handler(bool was_clean, WebSocketError error, std::string_view msg) override
19✔
6808
        {
9✔
6809
            REALM_ASSERT(m_observer);
3!
6810
            return realm_sync_socket_websocket_closed(m_observer, was_clean,
1!
6811
                                                      static_cast<realm_web_socket_errno_e>(error), msg.data());
×
UNCOV
6812
        }
×
6813

6✔
6814
    private:
13✔
6815
        realm_websocket_observer_t* m_observer;
15✔
6816
    };
16✔
6817

10✔
6818
    struct TestWebSocket : realm::c_api::WrapC, WebSocketInterface {
15✔
6819
    public:
15✔
6820
        TestWebSocket(DefaultSocketProvider& socket_provider, realm_websocket_endpoint_t endpoint,
16✔
6821
                      realm_websocket_observer_t* realm_websocket_observer)
16✔
6822
        {
16✔
6823
            WebSocketEndpoint ws_endpoint;
16✔
6824
            ws_endpoint.address = endpoint.address;
16✔
6825
            ws_endpoint.port = endpoint.port;
16✔
6826
            ws_endpoint.path = endpoint.path;
16✔
6827
            for (size_t i = 0; i < endpoint.num_protocols; ++i) {
148✔
6828
                ws_endpoint.protocols.push_back(endpoint.protocols[i]);
158✔
6829
            }
178✔
6830
            ws_endpoint.is_ssl = endpoint.is_ssl;
56✔
6831

30✔
6832
            auto observer = std::make_unique<TestWebSocketObserverShim>(realm_websocket_observer);
15✔
6833
            m_websocket = socket_provider.connect(std::move(observer), std::move(ws_endpoint));
15✔
6834
        }
16✔
6835

10✔
6836
        void async_write_binary(util::Span<const char> data, SyncSocketProvider::FunctionHandler&& handler) override
15✔
6837
        {
63✔
6838
            m_websocket->async_write_binary(data, std::move(handler));
72✔
6839
        }
80✔
6840

26✔
6841
    private:
23✔
6842
        std::unique_ptr<WebSocketInterface> m_websocket;
15✔
6843
    };
16✔
6844

10✔
6845
    struct TestSyncTimer : realm::c_api::WrapC, SyncSocketProvider::Timer {
15✔
6846
    public:
15✔
6847
        TestSyncTimer(DefaultSocketProvider& socket_provider, std::chrono::milliseconds delay,
16✔
6848
                      realm_sync_socket_timer_callback_t* callback)
16✔
6849
        {
52✔
6850
            m_timer = socket_provider.create_timer(delay, [capi_callback = callback](Status s) {
58✔
6851
                if (s.code() == ErrorCodes::Error::OperationAborted) {
64✔
6852
                    return realm_sync_socket_timer_canceled(capi_callback);
52✔
6853
                }
50✔
6854
                realm_sync_socket_timer_complete(
24✔
6855
                    capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()), s.reason().c_str());
20✔
6856
            });
16✔
6857
        }
52✔
6858

16✔
6859
        void cancel() override
21✔
6860
        {
51✔
6861
            m_timer->cancel();
58✔
6862
        }
64✔
6863

22✔
6864
    private:
21✔
6865
        SyncSocketProvider::SyncTimer m_timer;
15✔
6866
    };
16✔
6867

10✔
6868
    struct TestData {
15✔
6869
        DefaultSocketProvider* socket_provider;
15✔
6870
        int free_count = 0;
16✔
6871
    };
16✔
6872

10✔
6873
    DefaultSocketProvider default_socket_provider(util::Logger::get_default_logger(), "SocketProvider");
15✔
6874

9✔
6875
    static std::mutex catch_mutex;
15✔
6876
#define LOCKED_REQUIRE(...)                                                                                          \
15✔
6877
    do {                                                                                                             \
1,036✔
6878
        std::lock_guard lock(catch_mutex);                                                                           \
1,206✔
6879
        REQUIRE(__VA_ARGS__);                                                                                        \
1,376✔
6880
    } while (0)
1,376✔
6881

350✔
6882
    auto userdata_free_fn = [](realm_userdata_t user_ptr) {
185✔
6883
        auto test_data = static_cast<TestData*>(user_ptr);
15✔
6884
        LOCKED_REQUIRE(test_data);
16!
6885
        test_data->free_count++;
16!
6886
    };
16!
6887
    auto post_fn = [](realm_userdata_t userdata, realm_sync_socket_post_callback_t* callback) {
220✔
6888
        auto test_data = static_cast<TestData*>(userdata);
254✔
6889
        LOCKED_REQUIRE(test_data);
288!
6890
        LOCKED_REQUIRE(test_data->socket_provider);
288!
6891
        test_data->socket_provider->post([capi_callback = callback](Status s) {
288!
6892
            realm_sync_socket_post_complete(capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()),
288!
6893
                                            s.reason().c_str());
288✔
6894
        });
288✔
6895
    };
288✔
6896
    auto create_timer_fn = [](realm_userdata_t userdata, uint64_t delay_ms,
84✔
6897
                              realm_sync_socket_timer_callback_t* callback) -> realm_sync_socket_timer_t {
86✔
6898
        auto test_data = static_cast<TestData*>(userdata);
58✔
6899
        LOCKED_REQUIRE(test_data);
64!
6900
        LOCKED_REQUIRE(test_data->socket_provider);
64!
6901
        return static_cast<realm_sync_socket_timer_t>(
64!
6902
            new TestSyncTimer(*test_data->socket_provider, std::chrono::milliseconds(delay_ms), callback));
64!
6903
    };
64✔
6904
    auto cancel_timer_fn = [](realm_userdata_t userdata, realm_sync_socket_timer_t sync_timer) {
64✔
6905
        auto test_data = static_cast<TestData*>(userdata);
64✔
6906
        LOCKED_REQUIRE(test_data);
64!
6907
        LOCKED_REQUIRE(test_data->socket_provider);
64!
6908
        auto timer = static_cast<TestSyncTimer*>(sync_timer);
64!
6909
        LOCKED_REQUIRE(timer);
64!
6910
        timer->cancel();
64!
6911
    };
64!
6912
    auto free_timer_fn = [](realm_userdata_t userdata, realm_sync_socket_timer_t sync_timer) {
64✔
6913
        auto test_data = static_cast<TestData*>(userdata);
64✔
6914
        LOCKED_REQUIRE(test_data);
64!
6915
        LOCKED_REQUIRE(test_data->socket_provider);
64!
6916
        realm_release(sync_timer);
64!
6917
    };
64!
6918
    auto websocket_connect_fn =
28✔
6919
        [](realm_userdata_t userdata, realm_websocket_endpoint_t endpoint,
22✔
6920
           realm_websocket_observer_t* realm_websocket_observer) -> realm_sync_socket_websocket_t {
16✔
6921
        auto test_data = static_cast<TestData*>(userdata);
16✔
6922
        LOCKED_REQUIRE(test_data);
16!
6923
        LOCKED_REQUIRE(test_data->socket_provider);
16!
6924
        return static_cast<realm_sync_socket_websocket_t>(
16!
6925
            new TestWebSocket(*test_data->socket_provider, endpoint, realm_websocket_observer));
16!
6926
    };
16✔
6927
    auto websocket_async_write_fn = [](realm_userdata_t userdata, realm_sync_socket_websocket_t sync_websocket,
16✔
6928
                                       const char* data, size_t size, realm_sync_socket_write_callback_t* callback) {
64✔
6929
        auto test_data = static_cast<TestData*>(userdata);
72✔
6930
        LOCKED_REQUIRE(test_data);
80!
6931
        LOCKED_REQUIRE(test_data->socket_provider);
80!
6932
        auto websocket = static_cast<TestWebSocket*>(sync_websocket);
80!
6933
        LOCKED_REQUIRE(websocket);
80!
6934
        websocket->async_write_binary(util::Span{data, size}, [capi_callback = callback](Status s) {
80!
6935
            realm_sync_socket_write_complete(
80!
6936
                capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()), s.reason().c_str());
80✔
6937
        });
80✔
6938
    };
80✔
6939
    auto websocket_free_fn = [](realm_userdata_t userdata, realm_sync_socket_websocket_t sync_websocket) {
32✔
6940
        auto test_data = static_cast<TestData*>(userdata);
24✔
6941
        LOCKED_REQUIRE(test_data);
16!
6942
        LOCKED_REQUIRE(test_data->socket_provider);
16!
6943
        realm_release(sync_websocket);
16!
6944
    };
16!
6945

10✔
6946
    // Test drive.
9✔
6947
    TestData test_data{&default_socket_provider};
14✔
6948
    auto socket_provider = realm_sync_socket_new(static_cast<realm_userdata_t>(&test_data), userdata_free_fn, post_fn,
15✔
6949
                                                 create_timer_fn, cancel_timer_fn, free_timer_fn,
16✔
6950
                                                 websocket_connect_fn, websocket_async_write_fn, websocket_free_fn);
16✔
6951
    {
16✔
6952
        FLXSyncTestHarness harness("c_api_websocket_provider", FLXSyncTestHarness::default_server_schema(),
16✔
6953
                                   instance_of<SynchronousTestTransport>, *socket_provider);
16✔
6954

10✔
6955
        SyncTestFile test_config(harness.app()->current_user(), harness.schema(),
15✔
6956
                                 realm::SyncConfig::FLXSyncEnabled{});
15✔
6957
        auto realm = Realm::get_shared_realm(test_config);
16✔
6958
        bool wait_success = wait_for_download(*realm);
16✔
6959
        LOCKED_REQUIRE(!wait_success);
16!
6960
    }
16!
6961
    realm_release(socket_provider);
16!
6962
    default_socket_provider.stop(true);
16✔
6963
    LOCKED_REQUIRE(test_data.free_count == 1);
16!
6964
}
16!
6965
#endif // REALM_ENABLE_AUTH_TESTS
4!
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