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

realm / realm-core / 2214

10 Apr 2024 11:21PM UTC coverage: 91.813% (-0.8%) from 92.623%
2214

push

Evergreen

web-flow
Add missing availability checks for SecCopyErrorMessageString (#7577)

This requires iOS 11.3 and we currently target iOS 11.

94848 of 175770 branches covered (53.96%)

7 of 22 new or added lines in 2 files covered. (31.82%)

1815 existing lines in 77 files now uncovered.

242945 of 264608 relevant lines covered (91.81%)

6136478.37 hits per line

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

99.53
/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
{
7,806✔
69
    if (!x) {
7,806✔
70
        realm_error_t err_info;
2✔
71
        if (realm_get_last_error(&err_info)) {
2!
72
            FAIL(err_info.message);
×
73
        }
×
74
    }
2✔
75
    return x;
7,806✔
76
}
7,806✔
77

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

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

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

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

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

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

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

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

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

5✔
148
    return val;
10✔
149
}
10✔
150

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

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

1✔
160
    return val;
2✔
161
}
2✔
162

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

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

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

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

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

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

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

243
struct RealmReleaseDeleter {
244
    void operator()(void* ptr)
245
    {
2,898✔
246
        realm_release(ptr);
2,898✔
247
    }
2,898✔
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
{
2,842✔
256
    return CPtr<T>{ptr};
2,842✔
257
}
2,842✔
258

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

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

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

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

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

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

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

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

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

26✔
334
    SECTION("realm_release(NULL)") {
52✔
335
        // Just check that it doesn't crash.
1✔
336
        realm_release(nullptr);
2✔
337
    }
2✔
338

26✔
339
    SECTION("realm_get_last_error()") {
52✔
340
        CHECK(!realm_get_last_error(nullptr));
2!
341
        CHECK(!realm_clear_last_error());
2!
342

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

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

26✔
355
    SECTION("realm_get_last_error_as_async_error()") {
52✔
356
        CHECK(!realm_get_last_error_as_async_error());
4!
357

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

2✔
363
        realm_async_error_t* async_err = realm_get_last_error_as_async_error();
4✔
364
        CHECK(async_err);
4!
365

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

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

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

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

2✔
388
        realm_release(async_err);
4✔
389
        realm_clear_last_error();
4✔
390
    }
4✔
391

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

1✔
398
        CHECK(realm_clear_last_error());
2!
399
        CHECK(!realm_get_last_error(nullptr));
2!
400
    }
2✔
401

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

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

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

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

26✔
429
    SECTION("realm_config_t") {
52✔
430
        auto config = cptr(realm_config_new());
24✔
431

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

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

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

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

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

12✔
460
        SECTION("realm_config_set_schema_version()") {
24✔
461
            realm_config_set_schema_version(config.get(), 26);
2✔
462
            CHECK(realm_config_get_schema_version(config.get()) == 26);
2!
463
        }
2✔
464

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

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

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

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

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

12✔
512
        SECTION("realm_config_set_max_number_of_active_versions()") {
24✔
513
            realm_config_set_max_number_of_active_versions(config.get(), 999);
2✔
514
            CHECK(realm_config_get_max_number_of_active_versions(config.get()) == 999);
2!
515
        }
2✔
516

12✔
517
        SECTION("realm_config_set_in_memory()") {
24✔
518
            realm_config_set_in_memory(config.get(), true);
2✔
519
            CHECK(realm_config_get_in_memory(config.get()) == true);
2!
520
        }
2✔
521

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

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

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

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

1✔
597
        CHECK(realm_app_get_default_base_url() == app::App::default_base_url());
2!
598

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

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

1✔
606
        realm_app_config_set_platform_version(app_config.get(), "some_platform_version");
2✔
607
        CHECK(app_config->device_info.platform_version == "some_platform_version");
2!
608

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

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

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

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

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

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

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

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

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

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

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

1✔
648
        test_util::TestDirGuard temp_dir(util::make_temp_dir());
2✔
649
        realm_app_config_set_base_file_path(app_config.get(), temp_dir.c_str());
2✔
650
        realm_app_config_set_metadata_mode(app_config.get(), RLM_SYNC_CLIENT_METADATA_MODE_DISABLED);
2✔
651
        realm_app_config_set_security_access_group(app_config.get(), "");
2✔
652

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

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

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

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

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

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

3✔
694
            realm_app_refresh_custom_data(
6✔
695
                test_app.get(), sync_user,
6✔
696
                [](realm_userdata_t, const realm_app_error_t* error) {
6✔
697
                    CHECK(!error);
6!
698
                },
6✔
699
                nullptr, user_data_free);
6✔
700

3✔
701
            check_base_url(expected);
6✔
702
        };
6✔
703

1✔
704
        check_base_url(base_url);
2✔
705

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

1✔
709
        // Set to some other base url
1✔
710
        update_and_check_base_url(base_url2.c_str(), base_url2);
2✔
711

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

1✔
715
        realm_release(sync_user);
2✔
716
        realm_release(token);
2✔
717
    }
2✔
718
#endif // REALM_ENABLE_SYNC
52✔
719
}
52✔
720

721
namespace {
722

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

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

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

257✔
778
    REALM_ASSERT(num_names == num_types);
514✔
779
    REALM_ASSERT(num_nullable_names == num_types);
514✔
780
    REALM_ASSERT(num_list_names == num_types);
514✔
781
    REALM_ASSERT(num_nullable_list_names == num_types);
514✔
782
    REALM_ASSERT(num_set_names == num_types);
514✔
783
    REALM_ASSERT(num_nullable_set_names == num_types);
514✔
784
    REALM_ASSERT(num_dict_names == num_types);
514✔
785
    REALM_ASSERT(num_nullable_dict_names == num_types);
514✔
786

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

2,570✔
828
        properties.push_back(normal);
5,140✔
829
        properties.push_back(nullable);
5,140✔
830
        properties.push_back(list);
5,140✔
831
        properties.push_back(nullable_list);
5,140✔
832
        properties.push_back(set);
5,140✔
833
        properties.push_back(nullable_set);
5,140✔
834
        properties.push_back(dict);
5,140✔
835
        properties.push_back(nullable_dict);
5,140✔
836
    }
5,140✔
837

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

257✔
855
    properties.push_back(link);
514✔
856
    properties.push_back(link_list);
514✔
857
    properties.push_back(link_set);
514✔
858
    properties.push_back(link_dict);
514✔
859

257✔
860
    realm_property_info_t mixed{
514✔
861
        "mixed", "", RLM_PROPERTY_TYPE_MIXED,  RLM_COLLECTION_TYPE_NONE,
514✔
862
        "",      "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
514✔
863
    };
514✔
864
    // realm_property_info_t mixed_list{
257✔
865
    //     "mixed_list", "", RLM_PROPERTY_TYPE_MIXED,  RLM_COLLECTION_TYPE_LIST,
257✔
866
    //     "",           "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
257✔
867
    // };
257✔
868

257✔
869
    properties.push_back(mixed);
514✔
870
    // properties.push_back(mixed_list);
257✔
871

257✔
872
    return properties;
514✔
873
}
514✔
874

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

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

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

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

166✔
981
    return cptr(realm_schema_new(classes, 3, class_properties));
332✔
982
}
332✔
983

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

173✔
990
    if (set_schema) {
346✔
991
        auto schema = make_schema();
6✔
992
        CHECK(checked(schema.get()));
6!
993
        REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
6!
994
        realm_config_set_schema(config.get(), schema.get());
6✔
995
        realm_config_set_schema_version(config.get(), 0);
6✔
996
    }
6✔
997

173✔
998
    realm_config_set_automatic_change_notifications(config.get(), true);
346✔
999
    realm_config_set_max_number_of_active_versions(config.get(), 1000);
346✔
1000

173✔
1001
    return config;
346✔
1002
}
346✔
1003

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

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

1017
void free_data(void* userdata_p)
1018
{
2✔
1019
    free(userdata_p);
2✔
1020
}
2✔
1021

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

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

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

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

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

1066
struct LogUserData {
1067
    std::vector<std::pair<std::string, std::string>> log;
1068
};
1069

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

1076
realm_t* open_realm(TestFile& test_file)
1077
{
306✔
1078
    auto schema = make_schema();
306✔
1079
    CHECK(checked(schema.get()));
306!
1080
    REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
306!
1081

153✔
1082
    auto config = make_config(test_file.path.c_str(), false);
306✔
1083

153✔
1084
    auto realm = realm_open(config.get());
306✔
1085
    REQUIRE(checked(realm));
306!
1086
    REQUIRE(checked(realm_update_schema(realm, schema.get())));
306!
1087

153✔
1088
    CHECK(!realm_equals(realm, nullptr));
306!
1089

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

153✔
1095
    return realm;
306✔
1096
}
306✔
1097

1098
} // anonymous namespace
1099

1100
TEST_CASE("C API - schema", "[c_api]") {
26✔
1101
    TestFile test_file;
26✔
1102

13✔
1103
    SECTION("schema in config") {
26✔
1104
        TestFile test_file_2;
20✔
1105

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

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

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

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

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

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

10✔
1164
        SECTION("migrate schema and delete old table") {
20✔
1165
            TestFile test_file_3;
2✔
1166
            ConfigUserdata userdata;
2✔
1167

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

10✔
1228
        SECTION("migration callback rename property") {
20✔
1229
            TestFile test_file_3;
2✔
1230
            ConfigUserdata userdata;
2✔
1231

1✔
1232
            realm_config_set_migration_function(config.get(), migrate_schema_rename_prop, &userdata, nullptr);
2✔
1233

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

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

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

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

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

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

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

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

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

1✔
1353
            struct MyExceptionWrapper {
2✔
1354
                std::exception_ptr m_ptr{nullptr};
2✔
1355
            };
2✔
1356

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

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

13✔
1392
    auto realm = open_realm(test_file);
26✔
1393

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

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

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

1✔
1430
        properties[num_classes] = baz_properties;
2✔
1431

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

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

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

1✔
1453
        bool found;
2✔
1454
        realm_class_info_t baz_info;
2✔
1455
        CHECK(checked(realm_find_class(realm, "baz", &found, &baz_info)));
2!
1456
        CHECK(found);
2!
1457
        realm_property_info_t baz_int_property;
2✔
1458
        CHECK(checked(realm_find_property(realm, baz_info.key, "int", &found, &baz_int_property)));
2!
1459
        CHECK(found);
2!
1460

1✔
1461
        free(out_keys);
2✔
1462
        free(classes);
2✔
1463
        for (size_t i = 0; i < num_classes; i++) {
8✔
1464
            free((realm_property_info_t*)properties[i]);
6✔
1465
        }
6✔
1466
        free(properties);
2✔
1467
        realm_release(new_schema);
2✔
1468
        realm_release(token);
2✔
1469
    }
2✔
1470

13✔
1471
    SECTION("schema validates") {
26✔
1472
        auto schema = realm_get_schema(realm);
2✔
1473
        CHECK(checked(schema));
2!
1474
        CHECK(checked(realm_schema_validate(schema, realm_schema_validation_mode::RLM_SCHEMA_VALIDATION_BASIC)));
2!
1475

1✔
1476
        auto schema2 = realm_get_schema(realm);
2✔
1477
        CHECK(checked(schema2));
2!
1478
        CHECK(realm_equals(schema, schema2));
2!
1479
        realm_release(schema2);
2✔
1480
        realm_release(schema);
2✔
1481
    }
2✔
1482

13✔
1483
    SECTION("clone schema") {
26✔
1484
        auto schema = cptr(realm_get_schema(realm));
2✔
1485
        auto schema2 = clone_cptr(schema);
2✔
1486
        CHECK(schema.get() != schema2.get());
2!
1487
        CHECK(realm_equals(schema.get(), schema2.get()));
2!
1488
    }
2✔
1489

13✔
1490
    realm_close(realm);
26✔
1491
    REQUIRE(realm_is_closed(realm));
26!
1492
    realm_release(realm);
26✔
1493
}
26✔
1494

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

11✔
1499
    CHECK(realm_get_num_classes(realm) == 3);
22!
1500

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

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

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

11✔
1521
    SECTION("realm changed notification") {
22✔
1522
        bool realm_changed_callback_called = false;
2✔
1523
        auto token = cptr(realm_add_realm_changed_callback(
2✔
1524
            realm,
2✔
1525
            [](void* userdata) {
2✔
1526
                *reinterpret_cast<bool*>(userdata) = true;
2✔
1527
            },
2✔
1528
            &realm_changed_callback_called, [](void*) {}));
2✔
1529

1✔
1530
        realm_begin_write(realm);
2✔
1531
        realm_commit(realm);
2✔
1532
        CHECK(realm_changed_callback_called);
2!
1533
    }
2✔
1534

11✔
1535
    SECTION("realm refresh registering while not in transaction") {
22✔
1536
        bool realm_refresh_callback_called = false;
2✔
1537
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1538
            realm,
2✔
1539
            [](void* userdata) {
1✔
UNCOV
1540
                *reinterpret_cast<bool*>(userdata) = true;
×
UNCOV
1541
            },
×
1542
            &realm_refresh_callback_called, [](void*) {}));
2✔
1543
        realm_begin_write(realm);
2✔
1544
        realm_commit(realm);
2✔
1545
        CHECK_FALSE(realm_refresh_callback_called);
2!
1546
    }
2✔
1547

11✔
1548
    SECTION("realm refresh registering callback in transaction but on the same version") {
22✔
1549
        bool realm_refresh_callback_called = false;
2✔
1550
        realm_begin_write(realm);
2✔
1551
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1552
            realm,
2✔
1553
            [](void* userdata) {
1✔
UNCOV
1554
                *reinterpret_cast<bool*>(userdata) = true;
×
UNCOV
1555
            },
×
1556
            &realm_refresh_callback_called, [](void*) {}));
2✔
1557
        realm_commit(realm);
2✔
1558
        CHECK_FALSE(realm_refresh_callback_called);
2!
1559
    }
2✔
1560

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

1✔
1566
        realm_begin_write(realm);
2✔
1567
        realm_begin_read(realm2.get());
2✔
1568
        realm_commit(realm);
2✔
1569

1✔
1570
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1571
            realm2.get(),
2✔
1572
            [](void* userdata) {
2✔
1573
                *reinterpret_cast<bool*>(userdata) = true;
2✔
1574
            },
2✔
1575
            &realm_refresh_callback_called, [](void*) {}));
2✔
1576

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

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

1✔
1587
        realm_begin_write(realm);
2✔
1588
        realm_begin_read(realm2.get());
2✔
1589
        realm_commit(realm);
2✔
1590

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

11✔
1603
    SECTION("realm refresh read transaction frozen - register on unfrozen realm") {
22✔
1604
        bool realm_refresh_callback_called = false;
2✔
1605
        realm_begin_read(realm);
2✔
1606

1✔
1607
        auto realm2 = cptr_checked(realm_freeze(realm));
2✔
1608
        CHECK(!realm_is_frozen(realm));
2!
1609
        CHECK(realm_is_frozen(realm2.get()));
2!
1610
        CHECK(realm != realm2.get());
2!
1611

1✔
1612
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1613
            realm,
2✔
1614
            [](void* userdata) {
1✔
UNCOV
1615
                *reinterpret_cast<bool*>(userdata) = true;
×
UNCOV
1616
            },
×
1617
            &realm_refresh_callback_called, [](void*) {}));
2✔
1618

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

11✔
1623
    SECTION("realm refresh read transaction frozen - register on frozen realm") {
22✔
1624
        bool realm_refresh_callback_called = false;
2✔
1625
        realm_begin_read(realm);
2✔
1626

1✔
1627
        auto realm2 = cptr_checked(realm_freeze(realm));
2✔
1628
        CHECK(!realm_is_frozen(realm));
2!
1629
        CHECK(realm_is_frozen(realm2.get()));
2!
1630
        CHECK(realm != realm2.get());
2!
1631

1✔
1632
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1633
            realm2.get(),
2✔
1634
            [](void* userdata) {
1✔
UNCOV
1635
                *reinterpret_cast<bool*>(userdata) = true;
×
UNCOV
1636
            },
×
1637
            &realm_refresh_callback_called, [](void*) {}));
2✔
1638

1✔
1639
        realm_refresh(realm, nullptr);
2✔
1640
        CHECK(token == nullptr);
2!
1641
        CHECK_FALSE(realm_refresh_callback_called);
2!
1642
    }
2✔
1643

11✔
1644
    SECTION("realm_freeze()") {
22✔
1645
        auto realm2 = cptr_checked(realm_freeze(realm));
2✔
1646
        CHECK(!realm_is_frozen(realm));
2!
1647
        CHECK(realm_is_frozen(realm2.get()));
2!
1648
    }
2✔
1649

11✔
1650
    SECTION("realm_compact()") {
22✔
1651
        bool did_compact = false;
2✔
1652
        CHECK(checked(realm_compact(realm, &did_compact)));
2!
1653
        CHECK(did_compact);
2!
1654
    }
2✔
1655

11✔
1656
    realm_close(realm);
22✔
1657
    REQUIRE(realm_is_closed(realm));
22!
1658
    realm_release(realm);
22✔
1659
}
22✔
1660

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

1✔
1664
    LogUserData userdata;
2✔
1665
    const char* category_names[20];
2✔
1666
    auto num_categories = realm_get_category_names(20, category_names);
2✔
1667
    auto log_level_old = realm_get_log_level_category("Realm");
2✔
1668

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

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

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

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

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

1✔
1737
    struct NotifierData {
2✔
1738
        bool notify_called = false;
2✔
1739
    } notifier_data;
2✔
1740

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

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

1✔
1769
    realm_begin_write(realm);
2✔
1770
    auto obj = realm_object_create(realm, class_foo.key);
2✔
1771
    realm_release(obj);
2✔
1772
    realm_commit(realm);
2✔
1773

1✔
1774
    scheduler_data.wait();
2✔
1775

1✔
1776
    notifier_data.notify_called = false;
2✔
1777
    scheduler_data.execute();
2✔
1778
    CHECK(notifier_data.notify_called);
2!
1779

1✔
1780
    CHECK(!scheduler_data.free_called);
2!
1781
    realm_release(token);
2✔
1782
    realm_release(res);
2✔
1783
    realm_release(realm);
2✔
1784
    CHECK(scheduler_data.free_called);
2!
1785
}
2✔
1786

1787
TEST_CASE("C API - properties", "[c_api]") {
182✔
1788
    TestFile test_file;
182✔
1789
    realm_t* realm = open_realm(test_file);
182✔
1790

91✔
1791
    auto write = [&](auto&& f) {
330✔
1792
        checked(realm_begin_write(realm));
330✔
1793
        f();
330✔
1794
        checked(realm_commit(realm));
330✔
1795
        checked(realm_refresh(realm, nullptr));
330✔
1796
    };
330✔
1797

91✔
1798
    bool found = false;
182✔
1799

91✔
1800
    realm_class_info_t class_foo, class_bar, class_embedded;
182✔
1801
    CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
182!
1802
    REQUIRE(found);
182!
1803
    CHECK(checked(realm_find_class(realm, "Bar", &found, &class_bar)));
182!
1804
    REQUIRE(found);
182!
1805
    CHECK(checked(realm_find_class(realm, "Embedded", &found, &class_embedded)));
182!
1806
    REQUIRE(found);
182!
1807

91✔
1808
    std::map<std::string, realm_property_key_t> foo_properties;
182✔
1809
    for (const auto& p : all_property_types("Bar")) {
15,470✔
1810
        realm_property_info_t info;
15,470✔
1811
        bool found = false;
15,470✔
1812
        REQUIRE(realm_find_property(realm, class_foo.key, p.name, &found, &info));
15,470!
1813
        REQUIRE(found);
15,470!
1814
        CHECK(p.key == RLM_INVALID_PROPERTY_KEY);
15,470!
1815
        CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
15,470!
1816
        CHECK(info.type == p.type);
15,470!
1817
        CHECK(std::string{info.public_name} == p.public_name);
15,470!
1818
        CHECK(info.collection_type == p.collection_type);
15,470!
1819
        CHECK(std::string{info.link_target} == p.link_target);
15,470!
1820
        CHECK(std::string{info.link_origin_property_name} == p.link_origin_property_name);
15,470!
1821
        foo_properties[info.name] = info.key;
15,470✔
1822
    }
15,470✔
1823

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

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

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

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

91✔
1845
    realm_property_key_t foo_int_key = foo_properties["int"];
182✔
1846
    realm_property_key_t foo_str_key = foo_properties["string"];
182✔
1847
    realm_property_key_t foo_links_key = foo_properties["link_list"];
182✔
1848
    realm_property_key_t bar_int_key = bar_properties["int"];
182✔
1849
    realm_property_key_t bar_strings_key = bar_properties["strings"];
182✔
1850
    realm_property_key_t bar_doubles_key = bar_properties["doubles"];
182✔
1851

91✔
1852
    SECTION("realm_find_class() errors") {
182✔
1853
        bool found = true;
2✔
1854
        CHECK(realm_find_class(realm, "does not exist", &found, nullptr));
2!
1855
        CHECK(!found);
2!
1856
    }
2✔
1857

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

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

91✔
1875
    SECTION("realm_find_property() errors") {
182✔
1876
        realm_property_info_t dummy;
2✔
1877
        CHECK(!realm_find_property(realm, 123123123, "Foo", &found, &dummy));
2!
1878
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1879
        CHECK(!realm_find_property(realm, 123123123, "Foo", &found, nullptr));
2!
1880
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1881

1✔
1882
        bool found;
2✔
1883
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", nullptr, nullptr)));
2!
1884
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", &found, nullptr)));
2!
1885
        CHECK(found);
2!
1886

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

91✔
1892
    SECTION("realm_find_property_by_public_name()") {
182✔
1893
        realm_property_info_t property;
2✔
1894
        bool found = false;
2✔
1895
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "public_int", &found, &property)));
2!
1896
        CHECK(found);
2!
1897
        CHECK(property.key == foo_int_key);
2!
1898

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

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

91✔
1908
    SECTION("realm_get_property_keys()") {
182✔
1909
        size_t num_found = 0;
2✔
1910
        size_t properties_found = 0;
2✔
1911

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

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

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

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

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

91✔
1946
    SECTION("realm_get_property()") {
182✔
1947
        realm_property_info_t prop;
2✔
1948
        CHECK(checked(realm_get_property(realm, class_bar.key, bar_properties["linking_objects"], &prop)));
2!
1949
        CHECK(prop.key == bar_properties["linking_objects"]);
2!
1950
        CHECK(std::string{prop.name} == "linking_objects");
2!
1951

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

91✔
1956
    SECTION("realm_object_create() errors") {
182✔
1957
        SECTION("invalid table") {
14✔
1958
            write([&]() {
2✔
1959
                auto p = realm_object_create(realm, 123123123);
2✔
1960
                CHECK(!p);
2!
1961
                CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1962
            });
2✔
1963
        }
2✔
1964

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

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

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

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

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

7✔
2002
        SECTION("duplicate primary key") {
14✔
2003
            write([&]() {
2✔
2004
                cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123)));
2✔
2005
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123));
2✔
2006
                CHECK(!p);
2!
2007
                CHECK_ERR(RLM_ERR_OBJECT_ALREADY_EXISTS);
2!
2008
            });
2✔
2009
        }
2✔
2010

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

91✔
2017

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

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

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

66✔
2052
        SECTION("realm_clone()") {
132✔
2053
            auto obj1a = clone_cptr(obj1);
2✔
2054
            CHECK(realm_equals(obj1a.get(), obj1.get()));
2!
2055
        }
2✔
2056

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

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

66✔
2068
        SECTION("realm_get_num_objects()") {
132✔
2069
            size_t num_foos, num_bars;
2✔
2070
            CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_foos)));
2!
2071
            CHECK(checked(realm_get_num_objects(realm, class_bar.key, &num_bars)));
2!
2072
            CHECK(num_foos == 3);
2!
2073
            CHECK(num_bars == 1);
2!
2074

1✔
2075
            CHECK(checked(realm_get_num_objects(realm, class_bar.key, nullptr)));
2!
2076
            CHECK(!realm_get_num_objects(realm, 123123123, nullptr));
2!
2077
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
2078
        }
2✔
2079

66✔
2080
        SECTION("realm_get_object()") {
132✔
2081
            realm_object_key_t obj1_key = realm_object_get_key(obj1.get());
2✔
2082
            auto obj1a = cptr_checked(realm_get_object(realm, class_foo.key, obj1_key));
2✔
2083
            CHECK(obj1a);
2!
2084
            CHECK(realm_equals(obj1a.get(), obj1.get()));
2!
2085

1✔
2086
            realm_object_key_t invalid_key = 123123123;
2✔
2087
            CHECK(!realm_get_object(realm, class_foo.key, invalid_key));
2!
2088
            CHECK_ERR(RLM_ERR_NO_SUCH_OBJECT);
2!
2089

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

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

66✔
2103
        SECTION("realm_get_value()") {
132✔
2104
            realm_value_t value;
2✔
2105
            CHECK(checked(realm_get_value(obj1.get(), foo_int_key, &value)));
2!
2106
            CHECK(value.type == RLM_TYPE_INT);
2!
2107
            CHECK(value.integer == 123);
2!
2108

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

1✔
2113
            CHECK(checked(realm_get_value(obj1.get(), foo_int_key, nullptr)));
2!
2114

1✔
2115
            CHECK(!realm_get_value(obj1.get(), 123123123, &value));
2!
2116
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2117

1✔
2118
            CHECK(!realm_get_value(obj1.get(), 123123123, nullptr));
2!
2119
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2120

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

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

66✔
2132
        SECTION("realm_get_values()") {
132✔
2133
            realm_value_t values[3];
2✔
2134

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

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

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

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

1✔
2150
            write([&]() {
2✔
2151
                CHECK(checked(realm_object_delete(obj1.get())));
2!
2152
            });
2✔
2153
            CHECK(!realm_get_values(obj1.get(), 3, keys1, values));
2!
2154
            CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2155
        }
2✔
2156

66✔
2157
        SECTION("realm_set_value() errors") {
132✔
2158
            CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_int_val(456), false));
2!
2159
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
2160

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

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

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

66✔
2173
        SECTION("realm_set_values() errors") {
132✔
2174
            realm_value_t int456 = rlm_int_val(456);
2✔
2175
            CHECK(!realm_set_values(obj1.get(), 1, &foo_int_key, &int456, false));
2!
2176
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
2177

1✔
2178
            write([&]() {
2✔
2179
                realm_value_t value;
2✔
2180
                realm_property_key_t keys1[3] = {foo_int_key, foo_str_key, foo_int_key};
2✔
2181
                realm_property_key_t keys2[3] = {foo_int_key, 123123123, foo_str_key};
2✔
2182

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

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

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

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

66✔
2218
        SECTION("realm_object_add_int errors") {
132✔
2219
            SECTION("SUCCESS") {
4✔
2220
                realm_begin_write(realm);
2✔
2221
                CHECK(realm_object_add_int(obj1.get(), foo_int_key, 10));
2!
2222
                realm_commit(realm);
2✔
2223
            }
2✔
2224
            SECTION("ERROR") {
4✔
2225
                CHECK(!realm_object_add_int(obj1.get(), foo_int_key, 10));
2!
2226
                CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
2227
            }
2✔
2228
        }
4✔
2229

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

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

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

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

1✔
2272
            realm_value_t value;
2✔
2273

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

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

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

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

1✔
2366
            auto embedded = cptr_checked(realm_get_linked_object(obj2.get(), info.key));
2✔
2367
            CHECK(!embedded);
2!
2368
            write([&]() {
2✔
2369
                auto embedded = cptr_checked(realm_set_embedded(obj2.get(), info.key));
2✔
2370
                CHECK(embedded);
2!
2371
            });
2✔
2372
            embedded = cptr_checked(realm_get_linked_object(obj2.get(), info.key));
2✔
2373
            CHECK(embedded);
2!
2374
        }
2✔
2375

66✔
2376
        SECTION("delete causes invalidation errors") {
132✔
2377
            write([&]() {
2✔
2378
                // Get a list instance for later
1✔
2379
                auto list = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
2✔
2380

1✔
2381
                CHECK(checked(realm_object_delete(obj1.get())));
2!
2382
                CHECK(!realm_object_is_valid(obj1.get()));
2!
2383

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

1✔
2388
                realm_clear_last_error();
2✔
2389
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_int_val(123), false));
2!
2390
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2391

1✔
2392
                realm_clear_last_error();
2✔
2393
                auto list2 = realm_get_list(obj1.get(), foo_links_key);
2✔
2394
                CHECK(!list2);
2!
2395
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2396

1✔
2397
                size_t size;
2✔
2398
                CHECK(!realm_list_size(list.get(), &size));
2!
2399
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2400
            });
2✔
2401
        }
2✔
2402

66✔
2403
        SECTION("lists") {
132✔
2404
            SECTION("realm_get_list() errors") {
42✔
2405
                CHECK(!realm_get_list(obj2.get(), bar_int_key));
2!
2406
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
2407

1✔
2408
                CHECK(!realm_get_list(obj2.get(), 123123123));
2!
2409
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2410
            }
2✔
2411

21✔
2412
            SECTION("nullable strings") {
42✔
2413
                auto strings = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
8✔
2414
                CHECK(strings);
8!
2415
                CHECK(!realm_is_frozen(strings.get()));
8!
2416

4✔
2417
                realm_value_t a = rlm_str_val("a");
8✔
2418
                realm_value_t b = rlm_str_val("b");
8✔
2419
                realm_value_t c = rlm_null();
8✔
2420

4✔
2421
                SECTION("realm_equals() type check") {
8✔
2422
                    CHECK(!realm_equals(strings.get(), obj1.get()));
2!
2423
                }
2✔
2424

4✔
2425
                SECTION("realm_clone()") {
8✔
2426
                    auto list2 = clone_cptr(strings.get());
2✔
2427
                    CHECK(realm_equals(strings.get(), list2.get()));
2!
2428
                    CHECK(strings.get() != list2.get());
2!
2429
                }
2✔
2430

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1✔
2657
                CHECK(realm_list_get(nullable_int_list.get(), 0, &value));
2!
2658
                CHECK(rlm_val_eq(value, null));
2!
2659
                CHECK(realm_list_get(nullable_bool_list.get(), 0, &value));
2!
2660
                CHECK(rlm_val_eq(value, null));
2!
2661
                CHECK(realm_list_get(nullable_string_list.get(), 0, &value));
2!
2662
                CHECK(rlm_val_eq(value, null));
2!
2663
                CHECK(realm_list_get(nullable_binary_list.get(), 0, &value));
2!
2664
                CHECK(rlm_val_eq(value, null));
2!
2665
                CHECK(realm_list_get(nullable_timestamp_list.get(), 0, &value));
2!
2666
                CHECK(rlm_val_eq(value, null));
2!
2667
                CHECK(realm_list_get(nullable_float_list.get(), 0, &value));
2!
2668
                CHECK(rlm_val_eq(value, null));
2!
2669
                CHECK(realm_list_get(nullable_double_list.get(), 0, &value));
2!
2670
                CHECK(rlm_val_eq(value, null));
2!
2671
                CHECK(realm_list_get(nullable_decimal_list.get(), 0, &value));
2!
2672
                CHECK(rlm_val_eq(value, null));
2!
2673
                CHECK(realm_list_get(nullable_object_id_list.get(), 0, &value));
2!
2674
                CHECK(rlm_val_eq(value, null));
2!
2675
                CHECK(realm_list_get(nullable_uuid_list.get(), 0, &value));
2!
2676
                CHECK(rlm_val_eq(value, null));
2!
2677
            }
2✔
2678

21✔
2679
            SECTION("move") {
42✔
2680
                auto int_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["int_list"]));
2✔
2681
                write([&]() {
2✔
2682
                    for (int i = 0; i < 10; ++i) {
22✔
2683
                        CHECK(realm_list_insert(int_list.get(), i, rlm_int_val(i)));
20!
2684
                    }
20✔
2685
                });
2✔
2686

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

1✔
2694
                write([&]() {
2✔
2695
                    CHECK(realm_list_move(int_list.get(), 0, 1));
2!
2696
                });
2✔
2697

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

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

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

21✔
2715
            SECTION("links") {
42✔
2716
                CPtr<realm_list_t> bars;
8✔
2717

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

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

4✔
2736
                    realm_list_clear(bars.get());
8✔
2737
                    CHECK(checked(realm_list_find(bars.get(), &bar_link_val, &index, &found)));
8!
2738
                    CHECK(index == realm::not_found);
8!
2739
                    CHECK(!found);
8!
2740

4✔
2741
                    CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
8!
2742
                    CHECK(checked(realm_list_insert(bars.get(), 1, bar_link_val)));
8!
2743
                });
8✔
2744

4✔
2745
                SECTION("get") {
8✔
2746
                    realm_value_t val;
2✔
2747
                    CHECK(checked(realm_list_get(bars.get(), 0, &val)));
2!
2748
                    CHECK(val.type == RLM_TYPE_LINK);
2!
2749
                    CHECK(val.link.target_table == class_bar.key);
2!
2750
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
2751

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

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

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

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

4✔
2775
                SECTION("realm_list_clear()") {
8✔
2776
                    write([&]() {
2✔
2777
                        CHECK(realm_list_clear(bars.get()));
2!
2778
                    });
2✔
2779
                    size_t size;
2✔
2780
                    CHECK(realm_list_size(bars.get(), &size));
2!
2781
                    CHECK(size == 0);
2!
2782

1✔
2783
                    size_t num_bars;
2✔
2784
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
2785
                    CHECK(num_bars != 0);
2!
2786
                }
2✔
2787

4✔
2788
                SECTION("realm_list_remove_all()") {
8✔
2789
                    size_t num_bars;
2✔
2790
                    size_t size;
2✔
2791

1✔
2792
                    write([&]() {
2✔
2793
                        CHECK(checked(realm_list_remove_all(bars.get())));
2!
2794
                    });
2✔
2795

1✔
2796
                    CHECK(realm_list_size(bars.get(), &size));
2!
2797
                    CHECK(size == 0);
2!
2798

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

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

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

21✔
2820
            SECTION("notifications") {
42✔
2821
                struct State {
18✔
2822
                    CPtr<realm_collection_changes_t> changes;
18✔
2823
                    CPtr<realm_async_error_t> error;
18✔
2824
                    bool destroyed = false;
18✔
2825
                    bool called = false;
18✔
2826
                };
18✔
2827

9✔
2828
                State state;
18✔
2829

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

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

9✔
2838
                auto str1 = rlm_str_val("a");
18✔
2839
                auto str2 = rlm_str_val("b");
18✔
2840
                auto null = rlm_null();
18✔
2841

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

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

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

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

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

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

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

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

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

2✔
2941
                        SECTION("using valid nesting") {
4✔
2942

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

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

2✔
2958
                        checked(realm_refresh(realm, nullptr));
4✔
2959

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

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

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

1✔
3000
                    auto token = require_change();
2✔
3001

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

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

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

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

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

1✔
3039
                    CHECK(insertions.from == 0);
2!
3040
                    CHECK(insertions.to == 2);
2!
3041

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

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

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

1✔
3068
                    write([&]() {
2✔
3069
                        checked(realm_list_clear(strings.get()));
2✔
3070
                    });
2✔
3071

1✔
3072
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
2✔
3073
                                                             &num_modifications, &num_moves, &collection_cleared,
2✔
3074
                                                             nullptr);
2✔
3075
                    CHECK(collection_cleared == true);
2!
3076
                }
2✔
3077
            }
18✔
3078
        }
42✔
3079

66✔
3080
        SECTION("sets") {
132✔
3081
            SECTION("realm_get_set() errors") {
26✔
3082
                CHECK(!realm_get_set(obj1.get(), foo_properties["int"]));
2!
3083
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3084

1✔
3085
                CHECK(!realm_get_set(obj1.get(), 123123123));
2!
3086
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
3087
            }
2✔
3088

13✔
3089
            SECTION("nullable strings") {
26✔
3090
                auto strings = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
10✔
3091
                CHECK(strings);
10!
3092
                CHECK(!realm_is_frozen(strings.get()));
10!
3093

5✔
3094
                realm_value_t a = rlm_str_val("a");
10✔
3095
                realm_value_t b = rlm_str_val("b");
10✔
3096
                realm_value_t c = rlm_null();
10✔
3097

5✔
3098
                SECTION("realm_equals() type check") {
10✔
3099
                    CHECK(!realm_equals(strings.get(), obj1.get()));
2!
3100
                }
2✔
3101

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

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

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

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

1✔
3132
                        CHECK(rlm_stdstr(a2) == "a");
2!
3133
                        CHECK(rlm_stdstr(b2) == "b");
2!
3134
                        CHECK(c2.type == RLM_TYPE_NULL);
2!
3135

1✔
3136
                        bool erased = false;
2✔
3137
                        CHECK(checked(realm_set_erase(strings.get(), a2, &erased)));
2!
3138
                        CHECK(erased);
2!
3139
                        CHECK(checked(realm_set_erase(strings.get(), rlm_int_val(987), &erased)));
2!
3140
                        CHECK(!erased);
2!
3141
                    });
2✔
3142
                }
2✔
3143

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

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

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

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

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

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

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

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

1✔
3242
                realm_value_t value;
2✔
3243

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

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

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

13✔
3334
            SECTION("links") {
26✔
3335
                CPtr<realm_set_t> bars;
8✔
3336

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

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

4✔
3364
                SECTION("get") {
8✔
3365
                    realm_value_t val;
2✔
3366
                    CHECK(checked(realm_set_get(bars.get(), 0, &val)));
2!
3367
                    CHECK(val.type == RLM_TYPE_LINK);
2!
3368
                    CHECK(val.link.target_table == class_bar.key);
2!
3369
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
3370

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

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

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

4✔
3389
                SECTION("realm_set_clear()") {
8✔
3390
                    write([&]() {
2✔
3391
                        CHECK(realm_set_clear(bars.get()));
2!
3392
                    });
2✔
3393
                    size_t size;
2✔
3394
                    CHECK(realm_set_size(bars.get(), &size));
2!
3395
                    CHECK(size == 0);
2!
3396

1✔
3397
                    size_t num_bars;
2✔
3398
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3399
                    CHECK(num_bars != 0);
2!
3400
                }
2✔
3401

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

1✔
3409
                    size_t num_bars;
2✔
3410
                    size_t size;
2✔
3411

1✔
3412
                    write([&]() {
2✔
3413
                        CHECK(checked(realm_set_remove_all(bars.get())));
2!
3414
                    });
2✔
3415

1✔
3416
                    CHECK(realm_set_size(bars.get(), &size));
2!
3417
                    CHECK(size == 0);
2!
3418

1✔
3419
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3420
                    CHECK(num_bars == 0);
2!
3421
                }
2✔
3422
            }
8✔
3423

13✔
3424
            SECTION("notifications") {
26✔
3425
                struct State {
4✔
3426
                    CPtr<realm_collection_changes_t> changes;
4✔
3427
                    CPtr<realm_async_error_t> error;
4✔
3428
                    bool destroyed = false;
4✔
3429
                };
4✔
3430

2✔
3431
                State state;
4✔
3432

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

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

2✔
3441
                auto str1 = rlm_str_val("a");
4✔
3442
                auto str2 = rlm_str_val("b");
4✔
3443
                auto null = rlm_null();
4✔
3444

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

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

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

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

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

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

1✔
3495
                    write([&]() {
2✔
3496
                        checked(realm_set_clear(strings.get()));
2✔
3497
                    });
2✔
3498

1✔
3499
                    size_t num_deletions, num_insertions, num_modifications;
2✔
3500
                    bool collection_cleared = false;
2✔
3501
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
2✔
3502
                                                             &num_modifications, &num_moves, &collection_cleared,
2✔
3503
                                                             nullptr);
2✔
3504
                    CHECK(collection_cleared == true);
2!
3505
                }
2✔
3506
            }
4✔
3507
        }
26✔
3508

66✔
3509
        SECTION("dictionaries") {
132✔
3510
            SECTION("realm_get_dictionary() errors") {
28✔
3511
                CHECK(!realm_get_dictionary(obj1.get(), foo_properties["int"]));
2!
3512
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3513

1✔
3514
                CHECK(!realm_get_dictionary(obj1.get(), 123123123));
2!
3515
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
3516
            }
2✔
3517

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

4✔
3523
                realm_value_t a = rlm_str_val("a");
8✔
3524
                realm_value_t b = rlm_str_val("b");
8✔
3525
                realm_value_t c = rlm_null();
8✔
3526
                realm_value_t key_a = rlm_str_val("key_a");
8✔
3527
                realm_value_t key_b = rlm_str_val("key_b");
8✔
3528
                realm_value_t key_c = rlm_str_val("key_c");
8✔
3529

4✔
3530
                SECTION("realm_equals() type check") {
8✔
3531
                    CHECK(!realm_equals(strings.get(), obj1.get()));
2!
3532
                }
2✔
3533

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

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

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

1✔
3559
                        CHECK(rlm_stdstr(a2) == "a");
2!
3560
                        CHECK(rlm_stdstr(b2) == "b");
2!
3561
                        CHECK(c2.type == RLM_TYPE_NULL);
2!
3562

1✔
3563
                        bool erased = false;
2✔
3564
                        CHECK(checked(realm_dictionary_erase(strings.get(), key_a, &erased)));
2!
3565
                        CHECK(erased);
2!
3566
                        CHECK(checked(realm_dictionary_erase(strings.get(), rlm_int_val(987), &erased)));
2!
3567
                        CHECK(!erased);
2!
3568
                    });
2✔
3569
                }
2✔
3570

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

1✔
3577
                    write([&]() {
2✔
3578
                        auto obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
3579
                        CHECK(obj3);
2!
3580
                        auto strings3 =
2✔
3581
                            cptr_checked(realm_get_dictionary(obj3.get(), foo_properties["nullable_string_dict"]));
2✔
3582
                        CHECK(!realm_equals(strings.get(), strings3.get()));
2!
3583
                    });
2✔
3584
                }
2✔
3585
            }
8✔
3586

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

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

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

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

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

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

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

1✔
3706
                realm_value_t k, value;
2✔
3707

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

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

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

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

14✔
3891
            SECTION("links") {
28✔
3892
                CPtr<realm_dictionary_t> bars;
6✔
3893
                realm_value_t key = rlm_str_val("k");
6✔
3894

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

3✔
3912
                SECTION("get") {
6✔
3913
                    realm_value_t k, val;
2✔
3914
                    CHECK(checked(realm_dictionary_get(bars.get(), 0, &k, &val)));
2!
3915
                    CHECK(rlm_val_eq(k, key));
2!
3916
                    CHECK(val.type == RLM_TYPE_LINK);
2!
3917
                    CHECK(val.link.target_table == class_bar.key);
2!
3918
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
3919

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

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

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

3✔
3938
                SECTION("realm_dictionary_clear()") {
6✔
3939
                    write([&]() {
2✔
3940
                        CHECK(realm_dictionary_clear(bars.get()));
2!
3941
                    });
2✔
3942
                    size_t size;
2✔
3943
                    CHECK(realm_dictionary_size(bars.get(), &size));
2!
3944
                    CHECK(size == 0);
2!
3945

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

14✔
3952
            SECTION("notifications") {
28✔
3953
                struct State {
4✔
3954
                    CPtr<realm_collection_changes_t> changes;
4✔
3955
                    CPtr<realm_dictionary_changes_t> dictionary_changes;
4✔
3956
                    CPtr<realm_async_error_t> error;
4✔
3957
                    bool destroyed = false;
4✔
3958
                };
4✔
3959

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

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

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

2✔
3973

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

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

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

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

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

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

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

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

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

3✔
4074
                SECTION("realm_dictionary_contains_value") {
6✔
4075
                    size_t index = -1;
2✔
4076
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer1, &index)));
2!
4077
                    CHECK(index == 0);
2!
4078
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer2, &index)));
2!
4079
                    CHECK(index == 1);
2!
4080
                    realm_value_t integer_no_present = rlm_int_val(678);
2✔
4081
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer_no_present, &index)));
2!
4082
                    CHECK(index == realm::npos);
2!
4083
                }
2✔
4084
            }
6✔
4085
        }
28✔
4086

66✔
4087
        SECTION("notifications") {
132✔
4088
            struct State {
6✔
4089
                CPtr<realm_object_changes_t> changes;
6✔
4090
                CPtr<realm_async_error_t> error;
6✔
4091
                bool called;
6✔
4092
            };
6✔
4093

3✔
4094
            State state;
6✔
4095

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

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

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

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

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

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

1✔
4164
                state.called = false;
2✔
4165
                write([&]() {
2✔
4166
                    // checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
1✔
4167
                    checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("aaa"), false));
2✔
4168
                });
2✔
4169
                REQUIRE(!state.called);
2!
4170
            }
2✔
4171
        }
6✔
4172
    }
132✔
4173

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

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

5✔
4187
        SECTION("wrong thread") {
10✔
4188
            std::thread t{[&]() {
2✔
4189
                realm_value_t val;
2✔
4190
                CHECK(!realm_get_value(foo_obj.get(), foo_int_key, &val));
2!
4191
                CHECK_ERR(RLM_ERR_WRONG_THREAD);
2!
4192
            }};
2✔
4193

1✔
4194
            t.join();
2✔
4195
        }
2✔
4196

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

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

1✔
4220
                    realm_value_t foo_obj_int;
2✔
4221
                    CHECK(realm_get_value(foo_obj2.get(), foo_int_key, &foo_obj_int));
2!
4222
                    CHECK(rlm_val_eq(foo_obj_int, rlm_int_val(123)));
2!
4223

1✔
4224
                    size_t count;
2✔
4225
                    CHECK(realm_results_count(results2.get(), &count));
2!
4226
                    CHECK(count == 1);
2!
4227
                }};
2✔
4228

1✔
4229
                t.join();
2✔
4230
            }
2✔
4231

4✔
4232
            SECTION("resolve in frozen") {
8✔
4233
                auto realm2 = cptr_checked(realm_freeze(realm));
2✔
4234
                CHECK(realm_is_frozen(realm2.get()));
2!
4235
                CHECK(realm != realm2.get());
2!
4236

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

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

4✔
4257
            SECTION("non-sendable") {
8✔
4258
                auto c = cptr(realm_config_new());
2✔
4259
                CHECK(!realm_create_thread_safe_reference(c.get()));
2!
4260
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4261
            }
2✔
4262
        }
8✔
4263
    }
10✔
4264

91✔
4265
    SECTION("freeze and thaw") {
182✔
4266
        SECTION("realm") {
12✔
4267
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4268
            CHECK(!realm_is_frozen(realm));
2!
4269
            CHECK(realm_is_frozen(frozen_realm.get()));
2!
4270
        }
2✔
4271

6✔
4272
        SECTION("objects") {
12✔
4273
            CPtr<realm_object_t> obj1;
2✔
4274
            realm_value_t value;
2✔
4275

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

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

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

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

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

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

6✔
4317
        SECTION("results") {
12✔
4318
            auto results = cptr_checked(realm_object_find_all(realm, class_foo.key));
2✔
4319
            realm_results_delete_all(results.get());
2✔
4320

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

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

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

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

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

6✔
4349
        SECTION("lists") {
12✔
4350
            CPtr<realm_object_t> obj1;
2✔
4351
            size_t count;
2✔
4352

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

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

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

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

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

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

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

6✔
4393
        SECTION("sets") {
12✔
4394
            CPtr<realm_object_t> obj1;
2✔
4395
            size_t count;
2✔
4396

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

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

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

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

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

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

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

6✔
4437
        SECTION("dictionaries") {
12✔
4438
            CPtr<realm_object_t> obj1;
2✔
4439
            size_t count;
2✔
4440

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

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

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

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

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

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

1✔
4471
            CHECK(realm_dictionary_is_valid(thawed_dictionary));
2!
4472
            write([&]() {
2✔
4473
                CHECK(checked(realm_object_delete(obj1.get())));
2!
4474
            });
2✔
4475
            CHECK(!realm_dictionary_is_valid(thawed_dictionary));
2!
4476
            realm_release(thawed_dictionary);
2✔
4477
            CHECK(realm_dictionary_resolve_in(frozen_dictionary, realm, &thawed_dictionary));
2!
4478
            CHECK(thawed_dictionary == nullptr);
2!
4479
            realm_release(frozen_dictionary);
2✔
4480
        }
2✔
4481
    }
12✔
4482

91✔
4483
    realm_close(realm);
182✔
4484
    REQUIRE(realm_is_closed(realm));
182!
4485
    realm_release(realm);
182✔
4486
}
182✔
4487

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

38✔
4492
    auto write = [&](auto&& f) {
96✔
4493
        checked(realm_begin_write(realm));
96✔
4494
        f();
96✔
4495
        checked(realm_commit(realm));
96✔
4496
        checked(realm_refresh(realm, nullptr));
96✔
4497
    };
96✔
4498

38✔
4499
    bool found = false;
76✔
4500

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

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

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

38✔
4523
    auto foo_int_key = foo_properties("int");
76✔
4524
    auto foo_str_key = foo_properties("string");
76✔
4525

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

38✔
4545
    SECTION("find with primary key") {
76✔
4546
        bool found = false;
2✔
4547

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

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

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

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

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

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

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

36✔
4593
        SECTION("realm_query_count()") {
72✔
4594
            size_t count;
4✔
4595
            CHECK(checked(realm_query_count(q.get(), &count)));
4!
4596
            CHECK(count == 1);
4!
4597

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

36✔
4625
        SECTION("realm_query_parse() errors") {
72✔
4626
            // Invalid class key
1✔
4627
            CHECK(!realm_query_parse(realm, 123123123, "string == $0", num_args, arg_list));
2!
4628
            CHECK_ERR_CAT(RLM_ERR_NO_SUCH_TABLE, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
2!
4629

1✔
4630
            // Invalid syntax
1✔
4631
            CHECK(!realm_query_parse(realm, class_foo.key, "lel", 0, nullptr));
2!
4632
            CHECK_ERR_CAT(RLM_ERR_INVALID_QUERY_STRING, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
2!
4633

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

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

36✔
4643
        SECTION("string in list") {
72✔
4644
            char foo[] = "foo";
2✔
4645
            realm_value_t str = rlm_str_val(foo);
2✔
4646
            realm_value_t list_arg[2] = {str, rlm_str_val("bar")};
2✔
4647

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

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

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

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

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

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

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

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

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

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

1✔
4698
            size_t count, count_list;
2✔
4699

1✔
4700
            // change the link
1✔
4701
            link = rlm_null();
2✔
4702

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

36✔
4709
        SECTION("decimal NaN") {
72✔
4710
            realm_value_t decimal = rlm_decimal_nan();
2✔
4711

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

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

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

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

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

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

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

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

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

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

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

36✔
4873
        SECTION("results") {
72✔
4874
            auto r = cptr_checked(realm_query_find_all(q.get()));
52✔
4875
            CHECK(!realm_is_frozen(r.get()));
52!
4876

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

26✔
4883
            SECTION("realm_results_is_valid delete objects") {
52✔
4884
                write([&] {
2✔
4885
                    realm_object_delete(obj1.get());
2✔
4886
                    realm_object_delete(obj2.get());
2✔
4887
                    realm_results_delete_all(r.get());
2✔
4888
                });
2✔
4889
                bool valid;
2✔
4890
                CHECK(checked(realm_results_is_valid(r.get(), &valid)));
2!
4891
                CHECK(valid);
2!
4892
            }
2✔
4893

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

1✔
4899
                realm_value_t a = rlm_str_val("a");
2✔
4900
                realm_value_t b = rlm_str_val("b");
2✔
4901
                realm_value_t c = rlm_null();
2✔
4902

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

1✔
4913
                write([&] {
2✔
4914
                    CHECK(checked(realm_object_delete(obj2.get())));
2!
4915
                });
2✔
4916

1✔
4917
                CHECK(checked(realm_results_is_valid(results.get(), &valid)));
2!
4918
                CHECK_FALSE(valid);
2!
4919
                size_t count;
2✔
4920

1✔
4921
                CHECK_FALSE(realm_results_count(results.get(), &count));
2!
4922
                CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
2!
4923

1✔
4924
                CHECK_FALSE(realm_results_resolve_in(results.get(), realm));
2!
4925
                CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
2!
4926
            }
2✔
4927

26✔
4928
            SECTION("realm_results_count()") {
52✔
4929
                size_t count;
2✔
4930
                CHECK(checked(realm_results_count(r.get(), &count)));
2!
4931
                CHECK(count == 1);
2!
4932

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

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

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

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

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

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

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

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

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

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

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

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

26✔
5066
            SECTION("realm_results_min()") {
52✔
5067
                realm_value_t value = rlm_null();
2✔
5068
                CHECK(checked(realm_results_min(r.get(), foo_int_key, &value, &found)));
2!
5069
                CHECK(found);
2!
5070
                CHECK(value.type == RLM_TYPE_INT);
2!
5071
                CHECK(value.integer == 123);
2!
5072

1✔
5073
                CHECK(!realm_results_min(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
5074
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
5075
            }
2✔
5076

26✔
5077
            SECTION("realm_results_max()") {
52✔
5078
                realm_value_t value = rlm_null();
2✔
5079
                CHECK(checked(realm_results_max(r.get(), foo_int_key, &value, &found)));
2!
5080
                CHECK(found);
2!
5081
                CHECK(value.type == RLM_TYPE_INT);
2!
5082
                CHECK(value.integer == 123);
2!
5083

1✔
5084
                CHECK(!realm_results_max(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
5085
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
5086
            }
2✔
5087

26✔
5088
            SECTION("realm_results_sum()") {
52✔
5089
                realm_value_t value = rlm_null();
2✔
5090
                CHECK(checked(realm_results_sum(r.get(), foo_int_key, &value, &found)));
2!
5091
                CHECK(found);
2!
5092
                CHECK(value.type == RLM_TYPE_INT);
2!
5093
                CHECK(value.integer == 123);
2!
5094

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

1✔
5099
                CHECK(!realm_results_sum(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
5100
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
5101
            }
2✔
5102

26✔
5103
            SECTION("realm_results_average()") {
52✔
5104
                realm_value_t value = rlm_null();
2✔
5105
                CHECK(checked(realm_results_average(r.get(), foo_int_key, &value, &found)));
2!
5106
                CHECK(found);
2!
5107
                CHECK(value.type == RLM_TYPE_DOUBLE);
2!
5108
                CHECK(value.dnum == 123.0);
2!
5109

1✔
5110
                CHECK(!realm_results_average(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
5111
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
5112
            }
2✔
5113

26✔
5114
            SECTION("realm_results_delete_all()") {
52✔
5115
                CHECK(!realm_results_delete_all(r.get()));
2!
5116
                CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
5117

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

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

26✔
5133
            SECTION("lists append query") {
52✔
5134
                auto list = cptr_checked(realm_get_list(obj1.get(), foo_properties("link_list")));
2✔
5135

1✔
5136
                auto bar_link = realm_object_as_link(obj2.get());
2✔
5137
                realm_value_t bar_link_val;
2✔
5138
                bar_link_val.type = RLM_TYPE_LINK;
2✔
5139
                bar_link_val.link = bar_link;
2✔
5140

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

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

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

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

26✔
5174
            SECTION("empty results") {
52✔
5175
                auto empty_q = cptr_checked(realm_query_parse_for_results(r.get(), "FALSEPREDICATE", 0, nullptr));
10✔
5176
                auto empty_r = cptr_checked(realm_query_find_all(empty_q.get()));
10✔
5177

5✔
5178
                SECTION("realm_results_count()") {
10✔
5179
                    size_t count;
2✔
5180
                    CHECK(realm_results_count(empty_r.get(), &count));
2!
5181
                    CHECK(count == 0);
2!
5182
                }
2✔
5183

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

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

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

5✔
5208
                SECTION("realm_results_average()") {
10✔
5209
                    realm_value_t value;
2✔
5210
                    bool found = true;
2✔
5211
                    CHECK(realm_results_average(empty_r.get(), foo_int_key, &value, &found));
2!
5212
                    CHECK(rlm_val_eq(value, rlm_null()));
2!
5213
                    CHECK(!found);
2!
5214
                }
2✔
5215
            }
10✔
5216
        }
52✔
5217
    }
72✔
5218

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

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

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

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

8✔
5243
    realm_property_info_t info;
16✔
5244
    found = false;
16✔
5245
    REQUIRE(realm_find_property(realm, class_foo.key, "any", &found, &info));
16!
5246
    REQUIRE(found);
16!
5247
    CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
16!
5248
    realm_property_key_t foo_any_col_key = info.key;
16✔
5249

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

8✔
5255
    auto write = [&](auto&& f) {
13✔
5256
        checked(realm_begin_write(realm));
10✔
5257
        f();
10✔
5258
        checked(realm_commit(realm));
10✔
5259
        checked(realm_refresh(realm, nullptr));
10✔
5260
    };
10✔
5261

8✔
5262
    SECTION("deletion of nested list through clearing of parent") {
16✔
5263
        struct UserData {
2✔
5264
            size_t deletions;
2✔
5265
            size_t insertions;
2✔
5266
            size_t modifications;
2✔
5267
            bool was_deleted;
2✔
5268
            realm_list_t* list;
2✔
5269
            realm_dictionary_t* dict;
2✔
5270
        } user_data;
2✔
5271
        auto parent_list = cptr_checked(realm_set_list(obj1.get(), foo_any_col_key));
2✔
5272
        REQUIRE(parent_list);
2!
5273
        realm_value_t value;
2✔
5274
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5275
        REQUIRE(value.type == RLM_TYPE_LIST);
2!
5276
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
2✔
5277
        // list[0] = nestedlist
1✔
5278
        auto nested_list = cptr_checked(realm_list_insert_list(list.get(), 0));
2✔
5279
        auto nested_dict = cptr_checked(realm_list_insert_dictionary(list.get(), 1));
2✔
5280
        user_data.list = nested_list.get();
2✔
5281
        user_data.dict = nested_dict.get();
2✔
5282

1✔
5283
        checked(realm_commit(realm));
2✔
5284

1✔
5285
        auto on_list_change = [](void* data, const realm_collection_changes_t* changes) {
4✔
5286
            auto* user_data = static_cast<UserData*>(data);
4✔
5287
            realm_collection_changes_get_num_changes(changes, &user_data->deletions, &user_data->insertions,
4✔
5288
                                                     &user_data->modifications, nullptr, nullptr,
4✔
5289
                                                     &user_data->was_deleted);
4✔
5290
            if (user_data->was_deleted) {
4✔
5291
                CHECK(!realm_list_is_valid(user_data->list));
2!
5292
            }
2✔
5293
        };
4✔
5294
        auto require_change = [&]() {
2✔
5295
            auto token = cptr_checked(realm_list_add_notification_callback(nested_list.get(), &user_data, nullptr,
2✔
5296
                                                                           nullptr, on_list_change));
2✔
5297
            checked(realm_refresh(realm, nullptr));
2✔
5298
            return token;
2✔
5299
        };
2✔
5300

1✔
5301
        auto token = require_change();
2✔
5302

1✔
5303
        write([&] {
2✔
5304
            realm_list_clear(list.get());
2✔
5305
            // realm_list_set(list.get(), 0, rlm_str_val("Foo"));
1✔
5306
        });
2✔
5307
        CHECK(user_data.was_deleted);
2!
5308
        CHECK(!realm_list_is_valid(user_data.list));
2!
5309
        CHECK(!realm_dictionary_is_valid(user_data.dict));
2!
5310
    }
2✔
5311

8✔
5312
    SECTION("results of mixed") {
16✔
5313
        SECTION("dictionary") {
4✔
5314
            auto parent_dict = cptr_checked(realm_set_dictionary(obj1.get(), foo_any_col_key));
2✔
5315
            REQUIRE(parent_dict);
2!
5316
            realm_value_t value;
2✔
5317
            realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5318
            REQUIRE(value.type == RLM_TYPE_DICTIONARY);
2!
5319
            auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
2✔
5320
            auto nlist = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("A")));
2✔
5321
            auto ndict = cptr_checked(realm_dictionary_insert_dictionary(dict.get(), rlm_str_val("B")));
2✔
5322

1✔
5323
            // verify that we can fetch a collection from a result of mixed
1✔
5324
            auto results = cptr_checked(realm_dictionary_to_results(dict.get()));
2✔
5325
            const auto sz = results->size();
2✔
5326
            REQUIRE(sz == dict->size());
2!
5327
            REQUIRE(results->is_valid());
2!
5328
            realm_value_t val;
2✔
5329
            realm_results_get(results.get(), 0, &val);
2✔
5330
            REQUIRE(val.type == RLM_TYPE_LIST);
2!
5331
            realm_results_get(results.get(), 1, &val);
2✔
5332
            REQUIRE(val.type == RLM_TYPE_DICTIONARY);
2!
5333
            auto result_list = cptr_checked(realm_results_get_list(results.get(), 0));
2✔
5334
            REQUIRE(result_list);
2!
5335
            REQUIRE(result_list->size() == nlist->size());
2!
5336
            auto result_dictionary = cptr_checked(realm_results_get_dictionary(results.get(), 1));
2✔
5337
            REQUIRE(result_dictionary);
2!
5338
            REQUIRE(result_dictionary->size() == ndict->size());
2!
5339
        }
2✔
5340
        SECTION("list") {
4✔
5341
            auto parent_list = cptr_checked(realm_set_list(obj1.get(), foo_any_col_key));
2✔
5342
            REQUIRE(parent_list);
2!
5343
            realm_value_t value;
2✔
5344
            realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5345
            REQUIRE(value.type == RLM_TYPE_LIST);
2!
5346
            auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
2✔
5347
            auto nlist = cptr_checked(realm_list_insert_list(list.get(), 0));
2✔
5348
            auto ndict = cptr_checked(realm_list_insert_dictionary(list.get(), 1));
2✔
5349

1✔
5350
            // verify that we can fetch a collection from a result of mixed
1✔
5351
            auto results = cptr_checked(realm_list_to_results(list.get()));
2✔
5352
            const auto sz = results->size();
2✔
5353
            REQUIRE(sz == list->size());
2!
5354
            REQUIRE(results->is_valid());
2!
5355
            realm_value_t val;
2✔
5356
            realm_results_get(results.get(), 0, &val);
2✔
5357
            REQUIRE(val.type == RLM_TYPE_LIST);
2!
5358
            realm_results_get(results.get(), 1, &val);
2✔
5359
            REQUIRE(val.type == RLM_TYPE_DICTIONARY);
2!
5360
            auto result_list = cptr_checked(realm_results_get_list(results.get(), 0));
2✔
5361
            REQUIRE(result_list);
2!
5362
            REQUIRE(result_list->size() == nlist->size());
2!
5363
            auto result_dictionary = cptr_checked(realm_results_get_dictionary(results.get(), 1));
2✔
5364
            REQUIRE(result_dictionary);
2!
5365
            REQUIRE(result_dictionary->size() == ndict->size());
2!
5366
        }
2✔
5367
    }
4✔
5368

8✔
5369
    SECTION("dictionary") {
16✔
5370
        struct UserData {
2✔
5371
            size_t deletions;
2✔
5372
            size_t insertions;
2✔
5373
            size_t modifications;
2✔
5374
            bool was_deleted;
2✔
5375
            realm_dictionary_t* dict;
2✔
5376
        } user_data;
2✔
5377

1✔
5378
        auto parent_dict = cptr_checked(realm_set_dictionary(obj1.get(), foo_any_col_key));
2✔
5379
        REQUIRE(parent_dict);
2!
5380
        realm_value_t value;
2✔
5381
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5382
        REQUIRE(value.type == RLM_TYPE_DICTIONARY);
2!
5383
        auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
2✔
5384
        checked(realm_dictionary_insert(dict.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr, nullptr));
2✔
5385
        // dict -> list
1✔
5386
        auto list = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("Goodbye")));
2✔
5387
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
2✔
5388
        realm_list_insert(list.get(), 0, rlm_str_val("42"));
2✔
5389
        realm_list_insert(list.get(), 0, rlm_int_val(42));
2✔
5390
        // dict -> dict
1✔
5391
        auto dict2 = cptr_checked(realm_dictionary_insert_dictionary(dict.get(), rlm_str_val("Hi")));
2✔
5392
        user_data.dict = dict2.get();
2✔
5393
        checked(realm_dictionary_insert(dict2.get(), rlm_str_val("Nested-Hello"), rlm_str_val("Nested-World"),
2✔
5394
                                        nullptr, nullptr));
2✔
5395
        checked(realm_commit(realm));
2✔
5396

1✔
5397
        auto on_dictionary_change = [](void* data, const realm_dictionary_changes_t* changes) {
6✔
5398
            auto* user_data = static_cast<UserData*>(data);
6✔
5399
            realm_dictionary_get_changes(changes, &user_data->deletions, &user_data->insertions,
6✔
5400
                                         &user_data->modifications, &user_data->was_deleted);
6✔
5401
            if (user_data->was_deleted) {
6✔
5402
                CHECK(!realm_dictionary_is_valid(user_data->dict));
2!
5403
            }
2✔
5404
        };
6✔
5405
        auto require_change = [&]() {
2✔
5406
            auto token = cptr_checked(realm_dictionary_add_notification_callback(dict2.get(), &user_data, nullptr,
2✔
5407
                                                                                 nullptr, on_dictionary_change));
2✔
5408
            checked(realm_refresh(realm, nullptr));
2✔
5409
            return token;
2✔
5410
        };
2✔
5411

1✔
5412
        auto token = require_change();
2✔
5413

1✔
5414
        write([&] {
2✔
5415
            checked(realm_dictionary_insert(dict2.get(), rlm_str_val("Nested-Godbye"),
2✔
5416
                                            rlm_str_val("Nested-CruelWorld"), nullptr, nullptr));
2✔
5417
        });
2✔
5418
        CHECK(user_data.insertions == 1);
2!
5419

1✔
5420
        write([&] {
2✔
5421
            realm_dictionary_insert(dict.get(), rlm_str_val("Hi"), rlm_str_val("Foo"), nullptr, nullptr);
2✔
5422
        });
2✔
5423
        CHECK(user_data.deletions == 2);
2!
5424
        CHECK(user_data.was_deleted);
2!
5425
    }
2✔
5426

8✔
5427
    SECTION("list") {
16✔
5428
        struct UserData {
2✔
5429
            size_t deletions;
2✔
5430
            size_t insertions;
2✔
5431
            size_t modifications;
2✔
5432
            bool was_deleted;
2✔
5433
            realm_list_t* list;
2✔
5434
        } user_data;
2✔
5435

1✔
5436
        auto parent_list = cptr_checked(realm_set_list(obj1.get(), foo_any_col_key));
2✔
5437
        REQUIRE(parent_list);
2!
5438
        realm_value_t value;
2✔
5439
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5440
        REQUIRE(value.type == RLM_TYPE_LIST);
2!
5441
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
2✔
5442
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
2✔
5443
        realm_list_insert(list.get(), 1, rlm_str_val("World"));
2✔
5444
        // list -> dict
1✔
5445
        auto dict = cptr_checked(realm_list_insert_dictionary(list.get(), 1));
2✔
5446
        checked(realm_dictionary_insert(dict.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr, nullptr));
2✔
5447
        // list -> list
1✔
5448
        auto list2 = cptr_checked(realm_list_insert_list(list.get(), 2));
2✔
5449
        user_data.list = list2.get();
2✔
5450

1✔
5451
        checked(realm_commit(realm));
2✔
5452

1✔
5453
        auto on_list_change = [](void* data, const realm_collection_changes_t* changes) {
6✔
5454
            auto* user_data = static_cast<UserData*>(data);
6✔
5455
            realm_collection_changes_get_num_changes(changes, &user_data->deletions, &user_data->insertions,
6✔
5456
                                                     &user_data->modifications, nullptr, nullptr,
6✔
5457
                                                     &user_data->was_deleted);
6✔
5458
            if (user_data->was_deleted) {
6✔
5459
                CHECK(!realm_list_is_valid(user_data->list));
2!
5460
            }
2✔
5461
        };
6✔
5462
        auto require_change = [&]() {
2✔
5463
            auto token = cptr_checked(
2✔
5464
                realm_list_add_notification_callback(list2.get(), &user_data, nullptr, nullptr, on_list_change));
2✔
5465
            checked(realm_refresh(realm, nullptr));
2✔
5466
            return token;
2✔
5467
        };
2✔
5468

1✔
5469
        auto token = require_change();
2✔
5470

1✔
5471
        write([&] {
2✔
5472
            realm_list_insert(list2.get(), 0, rlm_str_val("Nested-Hello"));
2✔
5473
            realm_list_insert(list2.get(), 1, rlm_str_val("Nested-World"));
2✔
5474
        });
2✔
5475
        CHECK(user_data.insertions == 2);
2!
5476

1✔
5477
        write([&] {
2✔
5478
            realm_list_set(list.get(), 2, rlm_str_val("Foo"));
2✔
5479
        });
2✔
5480
        CHECK(user_data.deletions == 2);
2!
5481
        CHECK(user_data.was_deleted);
2!
5482
    }
2✔
5483

8✔
5484
    SECTION("set list for collection in mixed, verify that previous reference is invalid") {
16✔
5485
        auto parent_list = cptr_checked(realm_set_list(obj1.get(), foo_any_col_key));
2✔
5486
        REQUIRE(parent_list);
2!
5487
        realm_value_t value;
2✔
5488
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5489
        REQUIRE(value.type == RLM_TYPE_LIST);
2!
5490
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
2✔
5491
        auto n_list = cptr_checked(realm_list_insert_list(list.get(), 0));
2✔
5492
        size_t size;
2✔
5493
        checked(realm_list_size(list.get(), &size));
2✔
5494
        REQUIRE(size == 1);
2!
5495
        realm_list_insert(n_list.get(), 0, rlm_str_val("Test1"));
2✔
5496
        auto n_dict = cptr_checked(realm_list_set_dictionary(list.get(), 0));
2✔
5497
        // accessor has become invalid
1✔
5498
        REQUIRE(!realm_list_insert(n_list.get(), 1, rlm_str_val("Test2")));
2!
5499
        CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
5500
        // try to get a dictionary should work
1✔
5501
        n_dict = cptr_checked(realm_list_get_dictionary(list.get(), 0));
2✔
5502
        bool inserted = false;
2✔
5503
        size_t ndx;
2✔
5504
        realm_value_t key = rlm_str_val("key");
2✔
5505
        realm_value_t val = rlm_str_val("value");
2✔
5506
        REQUIRE(realm_dictionary_insert(n_dict.get(), key, val, &ndx, &inserted));
2!
5507
        REQUIRE(ndx == 0);
2!
5508
        REQUIRE(inserted);
2!
5509

1✔
5510
        CHECK(realm_list_set(list.get(), 0, rlm_int_val(5)));
2!
5511
        // accessor invalid
1✔
5512
        REQUIRE(!realm_dictionary_insert(n_dict.get(), key, val, &ndx, &inserted));
2!
5513
        CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
5514
        realm_value_t out;
2✔
5515
        CHECK(realm_list_get(list.get(), 0, &out));
2!
5516

1✔
5517
        n_list = cptr_checked(realm_list_set_list(list.get(), 0));
2✔
5518
        // get a list should work
1✔
5519
        n_list = cptr_checked(realm_list_get_list(list.get(), 0));
2✔
5520
        REQUIRE(realm_list_insert(n_list.get(), 0, rlm_str_val("Test1")));
2!
5521
        // reset the collection type to the same type (nop)
1✔
5522
        n_list = cptr_checked(realm_list_set_list(list.get(), 0));
2✔
5523
        // accessor is still valid
1✔
5524
        REQUIRE(realm_list_insert(n_list.get(), 0, rlm_str_val("Test2")));
2!
5525
        checked(realm_list_size(n_list.get(), &size));
2✔
5526
        REQUIRE(size == 2);
2!
5527
    }
2✔
5528

8✔
5529
    SECTION("json") {
16✔
5530
        REQUIRE(realm_set_json(
2!
5531
            obj1.get(), foo_any_col_key,
2✔
5532
            R"( [ { "Seven":7, "Six":6 }, "Hello", { "Points": [1.25, 4.5, 6.75], "Hello": "World" } ])"));
2✔
5533
        realm_value_t value;
2✔
5534
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5535
        REQUIRE(value.type == RLM_TYPE_LIST);
2!
5536
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
2✔
5537
        size_t size;
2✔
5538
        checked(realm_list_size(list.get(), &size));
2✔
5539
        CHECK(size == 3);
2!
5540
    }
2✔
5541

8✔
5542
    SECTION("freeze list") {
16✔
5543
        auto parent_dict = cptr_checked(realm_set_dictionary(obj1.get(), foo_any_col_key));
2✔
5544
        REQUIRE(parent_dict);
2!
5545
        auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
2✔
5546
        auto list = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("List")));
2✔
5547
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
2✔
5548
        realm_list_insert(list.get(), 0, rlm_str_val("42"));
2✔
5549
        checked(realm_commit(realm));
2✔
5550
        size_t size;
2✔
5551
        checked(realm_list_size(list.get(), &size));
2✔
5552
        REQUIRE(size == 2);
2!
5553
        auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
5554

1✔
5555
        realm_list_t* frozen_list;
2✔
5556
        realm_list_resolve_in(list.get(), frozen_realm.get(), &frozen_list);
2✔
5557
        checked(realm_list_size(frozen_list, &size));
2✔
5558
        REQUIRE(size == 2);
2!
5559
        realm_release(frozen_list);
2✔
5560
    }
2✔
5561
    realm_release(realm);
16✔
5562
}
16✔
5563

5564
TEST_CASE("C API: convert", "[c_api]") {
8✔
5565
    TestFile test_file;
8✔
5566
    TestFile dest_test_file;
8✔
5567
    realm_t* realm;
8✔
5568
    ObjectSchema object_schema = {"Foo",
8✔
5569
                                  {
8✔
5570
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
8✔
5571
                                      {"string_value", PropertyType::String},
8✔
5572
                                  }};
8✔
5573

4✔
5574
    { // seed a Realm with an object
8✔
5575
        auto config = make_config(test_file.path.c_str(), false);
8✔
5576
        config->schema = Schema{object_schema};
8✔
5577
        config->schema_version = 0;
8✔
5578
        realm = realm_open(config.get());
8✔
5579
        REQUIRE(checked(realm));
8!
5580
        CHECK(!realm_equals(realm, nullptr));
8!
5581
        realm_class_info_t class_foo;
8✔
5582
        bool found = false;
8✔
5583
        CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
8!
5584
        REQUIRE(found);
8!
5585

4✔
5586
        realm_property_key_t foo_str_col_key;
8✔
5587
        realm_property_info_t info;
8✔
5588
        found = false;
8✔
5589
        REQUIRE(realm_find_property(realm, class_foo.key, "string_value", &found, &info));
8!
5590
        REQUIRE(found);
8!
5591
        CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
8!
5592
        foo_str_col_key = info.key;
8✔
5593

4✔
5594
        CPtr<realm_object_t> obj1;
8✔
5595
        checked(realm_begin_write(realm));
8✔
5596
        realm_value_t pk = rlm_int_val(42);
8✔
5597
        obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_foo.key, pk));
8✔
5598
        CHECK(obj1);
8!
5599
        CHECK(checked(realm_set_value(obj1.get(), foo_str_col_key, rlm_str_val("Hello, World!"), false)));
8!
5600
        checked(realm_commit(realm));
8✔
5601
        checked(realm_refresh(realm, nullptr));
8✔
5602

4✔
5603
        size_t foo_count;
8✔
5604
        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &foo_count)));
8!
5605
        REQUIRE(foo_count == 1);
8!
5606
    }
8✔
5607

4✔
5608
    CHECK(realm_get_num_classes(realm) == 1);
8!
5609

4✔
5610
    SECTION("convert with path") {
8✔
5611
        bool merge_with_existing = false;
4✔
5612
        realm_binary encryption_key{nullptr, 0};
4✔
5613

2✔
5614
        REQUIRE(realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
4!
5615

2✔
5616
        SECTION("convert again without merge should fail") {
4✔
5617
            REQUIRE_FALSE(
2!
5618
                realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
2✔
5619
        }
2✔
5620
        SECTION("convert again with merge should succeed") {
4✔
5621
            merge_with_existing = true;
2✔
5622
            REQUIRE(realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
2!
5623
        }
2✔
5624
    }
4✔
5625

4✔
5626
    SECTION("convert with config") {
8✔
5627
        auto dest_config = make_config(dest_test_file.path.c_str(), false);
4✔
5628
        dest_config->schema = Schema{object_schema};
4✔
5629
        dest_config->schema_version = 0;
4✔
5630
        bool merge_with_existing = false;
4✔
5631
        REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
4!
5632
        SECTION("convert again without merge should fail") {
4✔
5633
            REQUIRE_FALSE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
2!
5634
        }
2✔
5635
        SECTION("convert again with merge should succeed") {
4✔
5636
            merge_with_existing = true;
2✔
5637
            REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
2!
5638
        }
2✔
5639
    }
4✔
5640
    realm_close(realm);
8✔
5641
    REQUIRE(realm_is_closed(realm));
8!
5642
    realm_release(realm);
8✔
5643
}
8✔
5644

5645
struct Userdata {
5646
    std::atomic<bool> called{false};
5647
    bool has_error;
5648
    realm_error_t error;
5649
    realm_thread_safe_reference_t* realm_ref = nullptr;
5650
    std::string error_message;
5651
};
5652

5653
#if REALM_ENABLE_SYNC
5654

5655
static void task_completion_func(void* p, realm_thread_safe_reference_t* realm,
5656
                                 const realm_async_error_t* async_error)
5657
{
4✔
5658
    auto userdata_p = static_cast<Userdata*>(p);
4✔
5659

2✔
5660
    userdata_p->realm_ref = realm;
4✔
5661
    userdata_p->has_error = realm_get_async_error(async_error, &userdata_p->error);
4✔
5662
    userdata_p->called = true;
4✔
5663
}
4✔
5664

5665
static void task_init_subscription(realm_thread_safe_reference_t* realm, void*)
UNCOV
5666
{
×
UNCOV
5667
    REQUIRE(realm);
×
UNCOV
5668
}
×
5669

5670
static void sync_error_handler(void* p, realm_sync_session_t*, const realm_sync_error_t error)
5671
{
2✔
5672
    auto userdata_p = static_cast<Userdata*>(p);
2✔
5673
    userdata_p->has_error = true;
2✔
5674
    userdata_p->error_message = error.status.message;
2✔
5675
    userdata_p->error.error = error.status.error;
2✔
5676
    userdata_p->error.categories = error.status.categories;
2✔
5677
    userdata_p->error.message = userdata_p->error_message.c_str();
2✔
5678
}
2✔
5679

5680
TEST_CASE("C API - async_open", "[sync][pbs][c_api]") {
4✔
5681
    TestSyncManager init_sync_manager;
4✔
5682
    SyncTestFile test_config(init_sync_manager, "default");
4✔
5683
    ObjectSchema object_schema = {"object",
4✔
5684
                                  {
4✔
5685
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
4✔
5686
                                      {"value", PropertyType::Int},
4✔
5687
                                  }};
4✔
5688
    test_config.schema = Schema{object_schema};
4✔
5689

2✔
5690
    SECTION("can open synced Realms that don't already exist") {
4✔
5691
        realm_config_t* config = realm_config_new();
2✔
5692
        config->schema = Schema{object_schema};
2✔
5693
        realm_user user(init_sync_manager.fake_user());
2✔
5694
        realm_sync_config_t* sync_config = realm_sync_config_new(&user, "default");
2✔
5695
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
2✔
5696
                                                           nullptr);
2✔
5697
        realm_config_set_path(config, test_config.path.c_str());
2✔
5698
        realm_config_set_sync_config(config, sync_config);
2✔
5699
        realm_config_set_schema_version(config, 1);
2✔
5700
        realm_async_open_task_t* task = realm_open_synchronized(config);
2✔
5701
        REQUIRE(task);
2!
5702
        Userdata userdata;
2✔
5703
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
2✔
5704
        util::EventLoop::main().run_until([&] {
11,023✔
5705
            return userdata.called.load();
11,023✔
5706
        });
11,023✔
5707
        REQUIRE(userdata.called);
2!
5708
        REQUIRE(userdata.realm_ref);
2!
5709
        realm_release(task);
2✔
5710

1✔
5711
        realm_t* realm = realm_from_thread_safe_reference(userdata.realm_ref, nullptr);
2✔
5712
        realm_release(userdata.realm_ref);
2✔
5713

1✔
5714
        bool found;
2✔
5715
        realm_class_info_t class_info;
2✔
5716
        realm_find_class(realm, "object", &found, &class_info);
2✔
5717
        REQUIRE(found);
2!
5718
        realm_release(realm);
2✔
5719
        realm_release(config);
2✔
5720
        realm_release(sync_config);
2✔
5721
    }
2✔
5722

2✔
5723
    SECTION("cancels download and reports an error on auth error") {
4✔
5724
        auto expired_token = encode_fake_jwt("", 123, 456);
2✔
5725
        struct User : TestUser {
2✔
5726
            using TestUser::TestUser;
2✔
5727
            void request_access_token(CompletionHandler&& completion) override
2✔
5728
            {
2✔
5729
                completion(app::AppError(ErrorCodes::HTTPError, "403 error", "", 403));
2✔
5730
            }
2✔
5731
            bool access_token_refresh_required() const override
2✔
5732
            {
2✔
5733
                return true;
2✔
5734
            }
2✔
5735
        };
2✔
5736
        auto user = std::make_shared<User>("realm", init_sync_manager.sync_manager());
2✔
5737
        user->m_access_token = expired_token;
2✔
5738
        user->m_refresh_token = expired_token;
2✔
5739

1✔
5740
        realm_config_t* config = realm_config_new();
2✔
5741
        config->schema = Schema{object_schema};
2✔
5742
        realm_user c_user(user);
2✔
5743
        realm_sync_config_t* sync_config = realm_sync_config_new(&c_user, "realm");
2✔
5744
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
2✔
5745
                                                           nullptr);
2✔
5746

1✔
5747
        realm_config_set_path(config, test_config.path.c_str());
2✔
5748
        realm_config_set_schema_version(config, 1);
2✔
5749
        Userdata userdata;
2✔
5750
        realm_sync_config_set_error_handler(sync_config, sync_error_handler, &userdata, nullptr);
2✔
5751
        realm_config_set_sync_config(config, sync_config);
2✔
5752

1✔
5753
        realm_async_open_task_t* task = realm_open_synchronized(config);
2✔
5754
        REQUIRE(task);
2!
5755
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
2✔
5756
        util::EventLoop::main().run_until([&] {
3✔
5757
            return userdata.called.load();
3✔
5758
        });
3✔
5759
        REQUIRE(userdata.called);
2!
5760
        REQUIRE(!userdata.realm_ref);
2!
5761
        REQUIRE(userdata.error.error == RLM_ERR_AUTH_ERROR);
2!
5762
        REQUIRE(userdata.error_message == "Unable to refresh the user access token: 403 error. Client Error: 403");
2!
5763
        realm_release(task);
2✔
5764
        realm_release(config);
2✔
5765
        realm_release(sync_config);
2✔
5766
    }
2✔
5767
}
4✔
5768

5769
struct BCTOState {
5770
    bool bcto_deleted = false;
5771
    bool thread_create_called = false;
5772
    bool thread_destroy_called = false;
5773
    std::string thread_on_error_message;
5774
    std::string id = "BTCO-STATE";
5775
};
5776

5777

5778
TEST_CASE("C API - binding callback thread observer", "[sync][c_api]") {
2✔
5779
    auto bcto_user_data = BCTOState();
2✔
5780

1✔
5781
    auto bcto_free_userdata = [](realm_userdata_t userdata) {
2✔
5782
        REQUIRE(userdata);
2!
5783
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5784
        REQUIRE(user_data->bcto_deleted == false);
2!
5785
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5786
        user_data->id.clear();
2✔
5787
        user_data->bcto_deleted = true;
2✔
5788
    };
2✔
5789

1✔
5790
    auto bcto_on_thread_create = [](realm_userdata_t userdata) {
2✔
5791
        REQUIRE(userdata);
2!
5792
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5793
        REQUIRE(user_data->bcto_deleted == false);
2!
5794
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5795
        REQUIRE(!user_data->thread_create_called);
2!
5796
        user_data->thread_create_called = true;
2✔
5797
    };
2✔
5798

1✔
5799
    auto bcto_on_thread_destroy = [](realm_userdata_t userdata) {
2✔
5800
        REQUIRE(userdata);
2!
5801
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5802
        REQUIRE(user_data->bcto_deleted == false);
2!
5803
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5804
        REQUIRE(!user_data->thread_destroy_called);
2!
5805
        user_data->thread_destroy_called = true;
2✔
5806
    };
2✔
5807

1✔
5808
    auto bcto_on_thread_error = [](realm_userdata_t userdata, const char* err_message) {
2✔
5809
        REQUIRE(userdata);
2!
5810
        REQUIRE(err_message);
2!
5811
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5812
        REQUIRE(user_data->bcto_deleted == false);
2!
5813
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5814
        REQUIRE(user_data->thread_on_error_message.empty());
2!
5815
        user_data->thread_on_error_message = err_message;
2✔
5816
        return true;
2✔
5817
    };
2✔
5818

1✔
5819
    {
2✔
5820
        auto config = cptr(realm_sync_client_config_new());
2✔
5821
        realm_sync_client_config_set_default_binding_thread_observer(
2✔
5822
            config.get(), bcto_on_thread_create, bcto_on_thread_destroy, bcto_on_thread_error,
2✔
5823
            static_cast<realm_userdata_t>(&bcto_user_data), bcto_free_userdata);
2✔
5824
        REQUIRE(config->default_socket_provider_thread_observer);
2!
5825
        auto observer_ptr =
2✔
5826
            static_cast<CBindingThreadObserver*>(config->default_socket_provider_thread_observer.get());
2✔
5827
        REQUIRE(observer_ptr->test_get_create_callback_func() == bcto_on_thread_create);
2!
5828
        REQUIRE(observer_ptr->test_get_destroy_callback_func() == bcto_on_thread_destroy);
2!
5829
        REQUIRE(observer_ptr->test_get_error_callback_func() == bcto_on_thread_error);
2!
5830
        REQUIRE(observer_ptr->has_handle_error());
2!
5831
        REQUIRE(observer_ptr->test_get_userdata_ptr() == &bcto_user_data);
2!
5832

1✔
5833
        auto test_thread = std::thread([&]() {
2✔
5834
            auto bcto_ptr = std::static_pointer_cast<realm::BindingCallbackThreadObserver>(
2✔
5835
                config->default_socket_provider_thread_observer);
2✔
5836
            REQUIRE(bcto_ptr);
2!
5837
            auto will_destroy_thread = util::make_scope_exit([&bcto_ptr]() noexcept {
2✔
5838
                bcto_ptr->will_destroy_thread();
2✔
5839
            });
2✔
5840
            bcto_ptr->did_create_thread();
2✔
5841
            REQUIRE(bcto_ptr->handle_error(MultipleSyncAgents()));
2!
5842
        });
2✔
5843

1✔
5844
        // Wait for the thread to exit
1✔
5845
        test_thread.join();
2✔
5846

1✔
5847
        REQUIRE(bcto_user_data.thread_create_called);
2!
5848
        REQUIRE(bcto_user_data.thread_on_error_message.find(
2!
5849
                    "Multiple sync agents attempted to join the same session") != std::string::npos);
2✔
5850
        REQUIRE(bcto_user_data.thread_destroy_called);
2!
5851
    }
2✔
5852

1✔
5853
    REQUIRE(bcto_user_data.bcto_deleted == true);
2!
5854

1✔
5855
    {
2✔
5856
        auto config = cptr(realm_sync_client_config_new());
2✔
5857
        realm_sync_client_config_set_default_binding_thread_observer(config.get(), nullptr, nullptr, nullptr, nullptr,
2✔
5858
                                                                     nullptr);
2✔
5859
        auto no_handle_error_ptr =
2✔
5860
            static_cast<CBindingThreadObserver*>(config->default_socket_provider_thread_observer.get());
2✔
5861
        no_handle_error_ptr->did_create_thread();                          // should not crash
2✔
5862
        no_handle_error_ptr->will_destroy_thread();                        // should not crash
2✔
5863
        REQUIRE(!no_handle_error_ptr->has_handle_error());                 // no handler, returns false
2!
5864
        REQUIRE(!no_handle_error_ptr->handle_error(MultipleSyncAgents())); // no handler, returns false
2!
5865
        // No free_user_data function was provided and internal default should be used
1✔
5866
        // Should not crash at scope exit
1✔
5867
    }
2✔
5868
}
2✔
5869
#endif
5870

5871
#if REALM_ENABLE_AUTH_TESTS
5872

5873
std::atomic_bool baas_client_stop{false};
5874
std::atomic<std::size_t> error_handler_counter{0};
5875
std::atomic<std::size_t> before_client_reset_counter{0};
5876
std::atomic<std::size_t> after_client_reset_counter{0};
5877

5878
TEST_CASE("C API - client reset", "[sync][pbs][c_api][client reset][baas]") {
10✔
5879
    reset_utils::Partition partition{"realm_id", random_string(20)};
10✔
5880
    Property partition_prop = {partition.property_name, PropertyType::String | PropertyType::Nullable};
10✔
5881
    Schema schema{
10✔
5882
        {"object",
10✔
5883
         {
10✔
5884
             {"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
10✔
5885
             {"value", PropertyType::Int},
10✔
5886
             partition_prop,
10✔
5887
         }},
10✔
5888
    };
10✔
5889

5✔
5890
    auto server_app_config = minimal_app_config("c_api_client_reset_tests", schema);
10✔
5891
    server_app_config.partition_key = partition_prop;
10✔
5892
    TestAppSession test_app_session(create_app(server_app_config));
10✔
5893

5✔
5894
    auto app = test_app_session.app();
10✔
5895
    auto get_valid_config = [&]() -> SyncTestFile {
20✔
5896
        create_user_and_log_in(app);
20✔
5897
        return SyncTestFile(app->current_user(), partition.value, schema);
20✔
5898
    };
20✔
5899
    SyncTestFile local_config = get_valid_config();
10✔
5900
    SyncTestFile remote_config = get_valid_config();
10✔
5901

5✔
5902
    auto make_reset = [&](Realm::Config config_local,
10✔
5903
                          Realm::Config config_remote) -> std::unique_ptr<reset_utils::TestClientReset> {
10✔
5904
        return reset_utils::make_baas_client_reset(config_local, config_remote, test_app_session);
10✔
5905
    };
10✔
5906

5✔
5907
    local_config.sync_config = std::make_shared<realm_sync_config_t>(*local_config.sync_config);
10✔
5908
    realm_sync_config_t* local_sync_config = static_cast<realm_sync_config_t*>(local_config.sync_config.get());
10✔
5909

5✔
5910
    struct ResetRealmFiles {
10✔
5911
        void set_app(std::shared_ptr<realm::app::App> app)
10✔
5912
        {
10✔
5913
            m_app = app;
10✔
5914
        }
10✔
5915
        void reset_realm(const char* path)
10✔
5916
        {
9✔
5917
            realm_app_t realm_app{m_app};
8✔
5918
            bool did_run;
8✔
5919
            realm_sync_immediately_run_file_actions(&realm_app, path, &did_run);
8✔
5920
        }
8✔
5921
        static ResetRealmFiles& instance()
10✔
5922
        {
18✔
5923
            static ResetRealmFiles instance;
18✔
5924
            return instance;
18✔
5925
        }
18✔
5926
        std::shared_ptr<realm::app::App> m_app;
10✔
5927
    };
10✔
5928
    ResetRealmFiles::instance().set_app(app);
10✔
5929

5✔
5930
    SECTION("Manual reset") {
10✔
5931
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_MANUAL);
2✔
5932

1✔
5933
        realm_sync_config_set_error_handler(
2✔
5934
            local_sync_config,
2✔
5935
            [](realm_userdata_t, realm_sync_session_t*, const realm_sync_error_t sync_error) {
2✔
5936
                REQUIRE(sync_error.c_original_file_path_key);
2!
5937
                REQUIRE(sync_error.c_recovery_file_path_key);
2!
5938
                REQUIRE(sync_error.is_client_reset_requested);
2!
5939
                REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_CLIENT_RESET);
2!
5940
                ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
2✔
5941
                baas_client_stop.store(true);
2✔
5942
            },
2✔
5943
            nullptr, nullptr);
2✔
5944

1✔
5945
        make_reset(local_config, remote_config)
2✔
5946
            ->on_post_reset([&](SharedRealm) {
2✔
5947
                util::EventLoop::main().run_until([&] {
800✔
5948
                    return baas_client_stop.load();
800✔
5949
                });
800✔
5950
            })
2✔
5951
            ->run();
2✔
5952
    }
2✔
5953

5✔
5954
    SECTION("Local Discard") {
10✔
5955
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_DISCARD_LOCAL);
8✔
5956

4✔
5957
        SECTION("Before client reset success") {
8✔
5958
            realm_sync_config_set_before_client_reset_handler(
2✔
5959
                local_sync_config,
2✔
5960
                [](realm_userdata_t, realm_t*) -> bool {
2✔
5961
                    before_client_reset_counter.fetch_add(1);
2✔
5962
                    return true;
2✔
5963
                },
2✔
5964
                nullptr, nullptr);
2✔
5965

1✔
5966
            realm_sync_config_set_after_client_reset_handler(
2✔
5967
                local_sync_config,
2✔
5968
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
2✔
5969
                    after_client_reset_counter.fetch_add(1);
2✔
5970
                    baas_client_stop.store(true);
2✔
5971
                    return true;
2✔
5972
                },
2✔
5973
                nullptr, nullptr);
2✔
5974

1✔
5975
            make_reset(local_config, remote_config)
2✔
5976
                ->on_post_reset([&](SharedRealm) {
2✔
5977
                    util::EventLoop::main().run_until([&] {
3✔
5978
                        return baas_client_stop.load();
3✔
5979
                    });
3✔
5980
                })
2✔
5981
                ->run();
2✔
5982

1✔
5983
            REQUIRE(before_client_reset_counter.load() == 1);
2!
5984
            REQUIRE(after_client_reset_counter.load() == 1);
2!
5985
        }
2✔
5986

4✔
5987
        SECTION("Before client reset fails") {
8✔
5988
            baas_client_stop.store(false);
2✔
5989
            before_client_reset_counter.store(0);
2✔
5990
            after_client_reset_counter.store(0);
2✔
5991

1✔
5992
            realm_sync_config_set_error_handler(
2✔
5993
                local_sync_config,
2✔
5994
                [](realm_userdata_t, realm_sync_session_t*, const realm_sync_error_t sync_error) {
2✔
5995
                    REQUIRE(sync_error.c_original_file_path_key);
2!
5996
                    REQUIRE(sync_error.c_recovery_file_path_key);
2!
5997
                    REQUIRE(sync_error.is_client_reset_requested);
2!
5998
                    // Callback in `realm_sync_config_set_before_client_reset_handler` fails, so
1✔
5999
                    // a synthetic error is created with no action.
1✔
6000
                    REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_NO_ACTION);
2!
6001
                    ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
2✔
6002
                    error_handler_counter.fetch_add(1);
2✔
6003
                    baas_client_stop.store(true);
2✔
6004
                },
2✔
6005
                nullptr, nullptr);
2✔
6006

1✔
6007
            realm_sync_config_set_before_client_reset_handler(
2✔
6008
                local_sync_config,
2✔
6009
                [](realm_userdata_t, realm_t*) -> bool {
2✔
6010
                    before_client_reset_counter.fetch_add(1);
2✔
6011
                    return false;
2✔
6012
                },
2✔
6013
                nullptr, nullptr);
2✔
6014

1✔
6015
            realm_sync_config_set_after_client_reset_handler(
2✔
6016
                local_sync_config,
2✔
6017
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
1✔
UNCOV
6018
                    after_client_reset_counter.fetch_add(1);
×
UNCOV
6019
                    return true;
×
UNCOV
6020
                },
×
6021
                nullptr, nullptr);
2✔
6022

1✔
6023
            make_reset(local_config, remote_config)
2✔
6024
                ->on_post_reset([&](SharedRealm) {
2✔
6025
                    util::EventLoop::main().run_until([&] {
806✔
6026
                        return baas_client_stop.load();
806✔
6027
                    });
806✔
6028
                })
2✔
6029
                ->run();
2✔
6030

1✔
6031
            REQUIRE(error_handler_counter.load() == 1);
2!
6032
            REQUIRE(before_client_reset_counter.load() == 1);
2!
6033
            REQUIRE(after_client_reset_counter.load() == 0);
2!
6034
        }
2✔
6035

4✔
6036
        SECTION("Simulate failure during client reset with expection in the user code callback") {
8✔
6037
            error_handler_counter.store(0);
4✔
6038
            baas_client_stop.store(false);
4✔
6039

2✔
6040
            struct ErrorState {
4✔
6041
                uintptr_t target_user_code_data = static_cast<uintptr_t>(random_int());
4✔
6042
                std::optional<uintptr_t> observed_user_code_data;
4✔
6043
            };
4✔
6044
            ErrorState state;
4✔
6045
            realm_sync_config_set_error_handler(
4✔
6046
                local_sync_config,
4✔
6047
                [](realm_userdata_t uncast_state, realm_sync_session_t*, const realm_sync_error_t sync_error) {
4✔
6048
                    REQUIRE(sync_error.c_original_file_path_key);
4!
6049
                    REQUIRE(sync_error.c_recovery_file_path_key);
4!
6050
                    REQUIRE(sync_error.is_client_reset_requested);
4!
6051
                    // Callback in `realm_sync_config_set_before_client_reset_handler` fails, so
2✔
6052
                    // a synthetic error is created with no action.
2✔
6053
                    // Since this is a failure triggered by some exception in the user code
2✔
6054
                    // an opaque ptr should have passed back to this callback in order to let
2✔
6055
                    // the SDK re-throw the excpetion.
2✔
6056
                    REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_NO_ACTION);
4!
6057
                    ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
4✔
6058
                    auto state = static_cast<ErrorState*>(uncast_state);
4✔
6059
                    state->observed_user_code_data = reinterpret_cast<uintptr_t>(sync_error.user_code_error);
4✔
6060
                    error_handler_counter.fetch_add(1);
4✔
6061
                    baas_client_stop.store(true);
4✔
6062
                },
4✔
6063
                &state, nullptr);
4✔
6064

2✔
6065
            SECTION("before reset exception") {
4✔
6066
                realm_sync_config_set_before_client_reset_handler(
2✔
6067
                    local_sync_config,
2✔
6068
                    [](realm_userdata_t uncast_state, realm_t*) -> bool {
2✔
6069
                        auto state = static_cast<ErrorState*>(uncast_state);
2✔
6070
                        realm_register_user_code_callback_error(
2✔
6071
                            reinterpret_cast<void*>(state->target_user_code_data));
2✔
6072
                        return false;
2✔
6073
                    },
2✔
6074
                    &state, nullptr);
2✔
6075

1✔
6076
                make_reset(local_config, remote_config)
2✔
6077
                    ->on_post_reset([&](SharedRealm) {
2✔
6078
                        util::EventLoop::main().run_until([&] {
815✔
6079
                            return baas_client_stop.load();
815✔
6080
                        });
815✔
6081
                    })
2✔
6082
                    ->run();
2✔
6083
            }
2✔
6084
            SECTION("After reset exception") {
4✔
6085
                realm_sync_config_set_before_client_reset_handler(
2✔
6086
                    local_sync_config,
2✔
6087
                    [](realm_userdata_t, realm_t*) -> bool {
2✔
6088
                        return true;
2✔
6089
                    },
2✔
6090
                    nullptr, nullptr);
2✔
6091

1✔
6092
                realm_sync_config_set_after_client_reset_handler(
2✔
6093
                    local_sync_config,
2✔
6094
                    [](realm_userdata_t uncast_state, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
2✔
6095
                        auto state = static_cast<ErrorState*>(uncast_state);
2✔
6096
                        realm_register_user_code_callback_error(
2✔
6097
                            reinterpret_cast<void*>(state->target_user_code_data));
2✔
6098
                        return false;
2✔
6099
                    },
2✔
6100
                    &state, nullptr);
2✔
6101

1✔
6102
                make_reset(local_config, remote_config)
2✔
6103
                    ->on_post_reset([&](SharedRealm) {
2✔
6104
                        util::EventLoop::main().run_until([&] {
645✔
6105
                            return baas_client_stop.load();
645✔
6106
                        });
645✔
6107
                    })
2✔
6108
                    ->run();
2✔
6109
            }
2✔
6110
            REQUIRE(error_handler_counter.load() == 1);
4!
6111
            REQUIRE(state.observed_user_code_data == state.target_user_code_data);
4!
6112
        }
4✔
6113
    }
8✔
6114
}
10✔
6115

6116
static void realm_app_void_completion(void*, const realm_app_error_t*) {}
14✔
6117

6118
static void realm_app_user1(void* p, realm_user_t* user, const realm_app_error_t*)
6119
{
10✔
6120
    auto clone_ptr = realm_clone(user);
10✔
6121
    CHECK(realm_equals(user, clone_ptr));
10!
6122
    *(static_cast<realm_user_t**>(p)) = static_cast<realm_user_t*>(clone_ptr);
10✔
6123
}
10✔
6124

6125
static void realm_app_user2(void* p, realm_user_t* user, const realm_app_error_t*)
6126
{
2✔
6127
    realm_user_identity_t idents[10];
2✔
6128
    size_t n;
2✔
6129
    realm_user_get_all_identities(user, idents, 10, &n);
2✔
6130
    *static_cast<bool*>(p) = n == 2;
2✔
6131
    for (size_t i = 0; i < n; i++) {
6✔
6132
        realm_free(idents[i].id);
4✔
6133
    }
4✔
6134
}
2✔
6135

6136
TEST_CASE("C API app: link_user integration w/c_api transport", "[sync][app][c_api][baas]") {
12✔
6137
    struct TestTransportUserData {
12✔
6138
        TestTransportUserData()
12✔
6139
            : logger(util::Logger::get_default_logger())
12✔
6140
            , transport(std::make_unique<SynchronousTestTransport>())
12✔
6141
        {
12✔
6142
        }
12✔
6143
        std::shared_ptr<util::Logger> logger;
12✔
6144
        std::unique_ptr<realm::app::GenericNetworkTransport> transport;
12✔
6145
    };
12✔
6146

6✔
6147
    auto send_request_to_server = [](realm_userdata_t userdata, const realm_http_request_t request,
12✔
6148
                                     void* request_context) {
88✔
6149
        using namespace realm::app;
88✔
6150

44✔
6151
        constexpr uint64_t default_timeout_ms = 60000;
88✔
6152
        REQUIRE(userdata != nullptr);
88!
6153
        TestTransportUserData* user_data(static_cast<TestTransportUserData*>(userdata));
88✔
6154
        REQUIRE(user_data != nullptr);
88!
6155
        REQUIRE(user_data->transport != nullptr);
88!
6156
        REQUIRE(user_data->logger != nullptr);
88!
6157
        REQUIRE(strlen(request.url) > 0);
88!
6158
        HttpHeaders headers;
88✔
6159
        for (size_t i = 0; i < request.num_headers; i++) {
272✔
6160
            REQUIRE(request.headers[i].name != nullptr);
184!
6161
            REQUIRE(request.headers[i].value != nullptr);
184!
6162
            std::string name(request.headers[i].name);
184✔
6163
            std::string value(request.headers[i].value);
184✔
6164
            REQUIRE(!name.empty());
184!
6165
            REQUIRE(!value.empty());
184!
6166
            headers.emplace(name, value);
184✔
6167
        }
184✔
6168
        REQUIRE(request_context != nullptr);
88!
6169
        auto new_request = Request{HttpMethod(request.method), request.url, default_timeout_ms, std::move(headers),
88✔
6170
                                   std::string(request.body, request.body_size)};
88✔
6171
        user_data->logger->trace("CAPI: Request URL (%1): %2", new_request.method, new_request.url);
88✔
6172
        user_data->logger->trace("CAPI: Request body: %1", new_request.body);
88✔
6173
        user_data->transport->send_request_to_server(new_request, [&](const Response& response) mutable {
88✔
6174
            std::vector<realm_http_header_t> c_headers;
88✔
6175
            c_headers.reserve(response.headers.size());
88✔
6176
            for (auto&& header : response.headers) {
692✔
6177
                c_headers.push_back({header.first.c_str(), header.second.c_str()});
692✔
6178
            }
692✔
6179

44✔
6180
            auto c_response = std::make_unique<realm_http_response_t>();
88✔
6181
            c_response->status_code = response.http_status_code;
88✔
6182
            c_response->custom_status_code = response.custom_status_code;
88✔
6183
            c_response->headers = c_headers.data();
88✔
6184
            c_response->num_headers = c_headers.size();
88✔
6185
            c_response->body = response.body.data();
88✔
6186
            c_response->body_size = response.body.size();
88✔
6187
            user_data->logger->trace("CAPI: Response (%1): %2", c_response->status_code,
88✔
6188
                                     std::string(c_response->body, c_response->body_size));
88✔
6189
            realm_http_transport_complete_request(request_context, c_response.get());
88✔
6190
        });
88✔
6191
    };
88✔
6192

6✔
6193
    auto user_data_free = [](void* userdata) {
12✔
6194
        REQUIRE(userdata != nullptr);
12!
6195
        std::unique_ptr<TestTransportUserData> user_data(static_cast<TestTransportUserData*>(userdata));
12✔
6196
        REQUIRE(user_data != nullptr);
12!
6197
        REQUIRE(user_data->transport != nullptr);
12!
6198
        REQUIRE(user_data->logger != nullptr);
12!
6199
        user_data->logger->trace("CAPI: user_data free called");
12✔
6200
    };
12✔
6201

6✔
6202
    // user_data will be deleted when user_data_free() is called
6✔
6203
    auto user_data = new TestTransportUserData();
12✔
6204
    auto http_transport = realm_http_transport_new(send_request_to_server, user_data, user_data_free);
12✔
6205
    auto app_session = get_runtime_app_session();
12✔
6206
    TestAppSession session(app_session, *http_transport, DeleteApp{false});
12✔
6207
    realm_app app(session.app());
12✔
6208

6✔
6209
    SECTION("remove_user integration") {
12✔
6210
        AutoVerifiedEmailCredentials creds;
2✔
6211
        realm_user_t* sync_user_1 = nullptr;
2✔
6212
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
6213
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
6214
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
6215
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
6216
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
2✔
6217

1✔
6218
        realm_app_remove_user(&app, sync_user_1, realm_app_void_completion, nullptr, nullptr);
2✔
6219
        auto state = realm_user_get_state(sync_user_1);
2✔
6220
        CHECK(state == RLM_USER_STATE_REMOVED);
2!
6221
        realm_release(sync_user_1);
2✔
6222
    }
2✔
6223
    SECTION("delete_user integration") {
12✔
6224
        AutoVerifiedEmailCredentials creds;
2✔
6225
        realm_user_t* sync_user = nullptr;
2✔
6226
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
6227
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
6228
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
6229
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
6230
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user, nullptr);
2✔
6231

1✔
6232
        realm_app_delete_user(&app, sync_user, realm_app_void_completion, nullptr, nullptr);
2✔
6233
        auto state = realm_user_get_state(sync_user);
2✔
6234
        CHECK(state == RLM_USER_STATE_REMOVED);
2!
6235
        realm_release(sync_user);
2✔
6236
    }
2✔
6237
    SECTION("link_user integration") {
12✔
6238
        AutoVerifiedEmailCredentials creds;
2✔
6239
        bool processed = false;
2✔
6240
        realm_user_t* sync_user = nullptr;
2✔
6241

1✔
6242
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
6243
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
6244
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
6245

1✔
6246
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
6247
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user, nullptr);
2✔
6248

1✔
6249
        realm_app_credentials email_creds(creds);
2✔
6250
        realm_app_link_user(&app, sync_user, &email_creds, realm_app_user2, &processed, nullptr);
2✔
6251

1✔
6252
        CHECK(processed);
2!
6253
        realm_release(sync_user);
2✔
6254
    }
2✔
6255
    SECTION("realm_app_get_all_users integration") {
12✔
6256

1✔
6257
        AutoVerifiedEmailCredentials creds;
2✔
6258
        realm_user_t* sync_user_1 = nullptr;
2✔
6259
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
6260
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
6261
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
6262
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
6263
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
2✔
6264
        auto current_user = realm_app_get_current_user(&app);
2✔
6265
        CHECK(realm_equals(sync_user_1, current_user));
2!
6266
        realm_release(current_user);
2✔
6267

1✔
6268
        realm_app_switch_user(&app, sync_user_1);
2✔
6269
        size_t out_n = 0;
2✔
6270

1✔
6271
        realm_app_get_all_users(&app, nullptr, 0, &out_n);
2✔
6272
        CHECK(out_n == 2);
2!
6273

1✔
6274
        std::vector<realm_user_t*> out_users;
2✔
6275
        out_users.resize(out_n);
2✔
6276
        size_t n = 0;
2✔
6277
        realm_app_get_all_users(&app, out_users.data(), out_n, &n);
2✔
6278
        CHECK(n == out_n);
2!
6279

1✔
6280
        for (size_t i = 0; i < out_n; ++i)
6✔
6281
            realm_release(out_users[i]);
4✔
6282
        realm_release(sync_user_1);
2✔
6283
    }
2✔
6284
    SECTION("realm_app_user_apikey_provider_client_fetch_apikeys") {
12✔
6285
        SECTION("Failure") {
4✔
6286
            AutoVerifiedEmailCredentials creds;
2✔
6287
            realm_user_t* sync_user_1 = nullptr;
2✔
6288
            realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
6289
            realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
6290
                                                                    realm_app_void_completion, nullptr, nullptr);
2✔
6291
            realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
6292
            realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
2✔
6293
            auto callback = [](realm_userdata_t, realm_app_user_apikey_t[], size_t count, realm_app_error_t* error) {
2✔
6294
                CHECK(error);
2!
6295
                CHECK(count == 0);
2!
6296
            };
2✔
6297
            // Should fail with 403
1✔
6298
            auto res =
2✔
6299
                realm_app_user_apikey_provider_client_fetch_apikeys(&app, sync_user_1, callback, nullptr, nullptr);
2✔
6300
            REQUIRE(res == true);
2!
6301
            realm_release(sync_user_1);
2✔
6302
        }
2✔
6303
        SECTION("Success") {
4✔
6304
            TestAppSession session;
2✔
6305
            realm_app app(session.app());
2✔
6306

1✔
6307
            class AppObject {
2✔
6308
            public:
2✔
6309
                static AppObject& create()
2✔
6310
                {
6✔
6311
                    static AppObject obj;
6✔
6312
                    return obj;
6✔
6313
                }
6✔
6314
                const char* key() const
2✔
6315
                {
6✔
6316
                    return api_key_name.c_str();
6✔
6317
                }
6✔
6318

1✔
6319
            private:
2✔
6320
                AppObject()
2✔
6321
                {
2✔
6322
                    api_key_name = util::format("%1", random_string(15));
2✔
6323
                }
2✔
6324
                std::string api_key_name;
2✔
6325
            };
2✔
6326

1✔
6327
            auto create_api_callback = [](realm_userdata_t, realm_app_user_apikey_t* api_key,
2✔
6328
                                          const realm_app_error_t* error) {
2✔
6329
                REQUIRE_FALSE(error);
2!
6330
                REQUIRE(api_key);
2!
6331
                CHECK(strcmp(api_key->name, AppObject::create().key()) == 0);
2!
6332
            };
2✔
6333
            auto current_user = realm_app_get_current_user(&app);
2✔
6334
            auto state = realm_app_user_apikey_provider_client_create_apikey(
2✔
6335
                &app, current_user, AppObject::create().key(), create_api_callback, nullptr, nullptr);
2✔
6336
            REQUIRE(state);
2!
6337

1✔
6338
            auto fetch_apikey_callback = [](realm_userdata_t, realm_app_user_apikey_t* keys, size_t count,
2✔
6339
                                            realm_app_error_t* error) {
2✔
6340
                REQUIRE_FALSE(error);
2!
6341
                REQUIRE(keys);
2!
6342
                REQUIRE(count == 1);
2!
6343
                CHECK(strcmp(keys[0].name, AppObject::create().key()) == 0);
2!
6344
            };
2✔
6345
            state = realm_app_user_apikey_provider_client_fetch_apikeys(&app, current_user, fetch_apikey_callback,
2✔
6346
                                                                        nullptr, nullptr);
2✔
6347
            REQUIRE(state);
2!
6348
            realm_release(current_user);
2✔
6349
        }
2✔
6350
    }
4✔
6351
    realm_release(http_transport);
12✔
6352
}
12✔
6353

6354
TEST_CASE("app: flx-sync compensating writes C API support", "[sync][flx][c_api][baas]") {
2✔
6355
    using namespace realm::app;
2✔
6356
    FLXSyncTestHarness harness("c_api_comp_writes");
2✔
6357
    create_user_and_log_in(harness.app());
2✔
6358
    SyncTestFile test_config(harness.app()->current_user(), harness.schema(), realm::SyncConfig::FLXSyncEnabled{});
2✔
6359
    test_config.sync_config = std::make_shared<realm_sync_config_t>(*test_config.sync_config);
2✔
6360
    realm_sync_config_t* sync_config = static_cast<realm_sync_config_t*>(test_config.sync_config.get());
2✔
6361

1✔
6362
    struct TestState {
2✔
6363
        std::mutex mutex;
2✔
6364
        std::condition_variable cond_var;
2✔
6365
        std::vector<sync::CompensatingWriteErrorInfo> compensating_writes;
2✔
6366
    };
2✔
6367
    auto state = std::make_unique<TestState>();
2✔
6368
    realm_sync_config_set_error_handler(
2✔
6369
        sync_config,
2✔
6370
        [](realm_userdata_t user_data, realm_sync_session_t*, const realm_sync_error_t error) {
2✔
6371
            auto state = reinterpret_cast<TestState*>(user_data);
2✔
6372
            REQUIRE(error.status.error == RLM_ERR_SYNC_COMPENSATING_WRITE);
2!
6373
            REQUIRE(error.compensating_writes_length > 0);
2!
6374

1✔
6375
            std::lock_guard<std::mutex> lk(state->mutex);
2✔
6376
            for (size_t i = 0; i < error.compensating_writes_length; ++i) {
6✔
6377
                sync::CompensatingWriteErrorInfo err_info;
4✔
6378
                err_info.object_name = error.compensating_writes[i].object_name;
4✔
6379
                err_info.reason = error.compensating_writes[i].reason;
4✔
6380
                Mixed pk(c_api::from_capi(error.compensating_writes[i].primary_key));
4✔
6381
                err_info.primary_key = pk;
4✔
6382
                state->compensating_writes.push_back(std::move(err_info));
4✔
6383
            }
4✔
6384

1✔
6385
            state->cond_var.notify_one();
2✔
6386
        },
2✔
6387
        state.get(), [](realm_userdata_t) {});
2✔
6388

1✔
6389
    auto realm = Realm::get_shared_realm(test_config);
2✔
6390

1✔
6391
    auto mut_subs = realm->get_latest_subscription_set().make_mutable_copy();
2✔
6392
    auto table = realm->read_group().get_table("class_TopLevel");
2✔
6393
    mut_subs.insert_or_assign(Query(table).equal(table->get_column_key("queryable_str_field"), "bizz"));
2✔
6394
    mut_subs.commit();
2✔
6395

1✔
6396
    CppContext c(realm);
2✔
6397
    realm->begin_transaction();
2✔
6398
    auto obj_1_id = ObjectId::gen();
2✔
6399
    auto obj_2_id = ObjectId::gen();
2✔
6400
    Object::create(c, realm, "TopLevel",
2✔
6401
                   std::any(AnyDict{
2✔
6402
                       {"_id", obj_1_id},
2✔
6403
                       {"queryable_str_field", std::string{"foo"}},
2✔
6404
                   }));
2✔
6405
    Object::create(c, realm, "TopLevel",
2✔
6406
                   std::any(AnyDict{
2✔
6407
                       {"_id", obj_2_id},
2✔
6408
                       {"queryable_str_field", std::string{"bar"}},
2✔
6409
                   }));
2✔
6410
    realm->commit_transaction();
2✔
6411

1✔
6412
    std::unique_lock<std::mutex> lk(state->mutex);
2✔
6413
    state->cond_var.wait_for(lk, std::chrono::seconds(30), [&] {
4✔
6414
        return state->compensating_writes.size() == 2;
4✔
6415
    });
4✔
6416

1✔
6417
    auto errors = std::move(state->compensating_writes);
2✔
6418
    lk.unlock();
2✔
6419

1✔
6420
    std::sort(errors.begin(), errors.end(), [](const auto& lhs, const auto& rhs) {
3✔
6421
        return lhs.primary_key < rhs.primary_key;
3✔
6422
    });
3✔
6423

1✔
6424
    REQUIRE(errors.size() == 2);
2!
6425
    REQUIRE(errors[0].primary_key == obj_1_id);
2!
6426
    REQUIRE(errors[0].object_name == "TopLevel");
2!
6427
    REQUIRE_THAT(errors[0].reason, Catch::Matchers::ContainsSubstring("object is outside of the current query view"));
2✔
6428
    REQUIRE(errors[1].primary_key == obj_2_id);
2!
6429
    REQUIRE(errors[1].object_name == "TopLevel");
2!
6430
    REQUIRE_THAT(errors[1].reason, Catch::Matchers::ContainsSubstring("object is outside of the current query view"));
2✔
6431
}
2✔
6432

6433
TEST_CASE("app: flx-sync basic tests", "[sync][flx][c_api][baas]") {
2✔
6434
    using namespace realm::app;
2✔
6435

1✔
6436
    auto make_schema = [] {
2✔
6437
        Schema schema{{"Obj",
2✔
6438
                       {{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
2✔
6439
                        {"name", PropertyType::String | PropertyType::Nullable},
2✔
6440
                        {"value", PropertyType::Int | PropertyType::Nullable}}}};
2✔
6441

1✔
6442
        return FLXSyncTestHarness::ServerSchema{std::move(schema), {"name", "value"}};
2✔
6443
    };
2✔
6444

1✔
6445
    FLXSyncTestHarness harness("c_api_flx_sync_test", make_schema());
2✔
6446
    auto foo_obj_id = ObjectId::gen();
2✔
6447
    auto bar_obj_id = ObjectId::gen();
2✔
6448

1✔
6449
    harness.load_initial_data([&](SharedRealm& realm) {
2✔
6450
        CppContext c(realm);
2✔
6451
        Object::create(
2✔
6452
            c, realm, "Obj",
2✔
6453
            std::any(AnyDict{{"_id", foo_obj_id}, {"name", std::string{"foo"}}, {"value", static_cast<int64_t>(5)}}));
2✔
6454
        Object::create(c, realm, "Obj",
2✔
6455
                       std::any(AnyDict{
2✔
6456
                           {"_id", bar_obj_id}, {"name", std::string{"bar"}}, {"value", static_cast<int64_t>(10)}}));
2✔
6457
    });
2✔
6458

1✔
6459
    harness.do_with_new_realm([&](SharedRealm realm) {
2✔
6460
        realm_t c_wrap_realm(realm);
2✔
6461

1✔
6462
        wait_for_download(*realm);
2✔
6463
        {
2✔
6464
            auto empty_subs = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6465
            CHECK(realm_sync_subscription_set_size(empty_subs) == 0);
2!
6466
            CHECK(realm_sync_subscription_set_version(empty_subs) == 0);
2!
6467
            realm_sync_on_subscription_set_state_change_wait(
2✔
6468
                empty_subs, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6469
            realm_release(empty_subs);
2✔
6470
        }
2✔
6471
        realm_class_info_t table_info;
2✔
6472
        bool found;
2✔
6473
        CHECK(realm_find_class(&c_wrap_realm, "Obj", &found, &table_info));
2!
6474
        auto c_wrap_query_foo = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'foo'", 0, nullptr);
2✔
6475
        auto c_wrap_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
2✔
6476
        {
2✔
6477
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6478
            CHECK(sub != nullptr);
2!
6479
            auto new_subs = realm_sync_make_subscription_set_mutable(sub);
2✔
6480
            std::size_t index = -1;
2✔
6481
            bool inserted = false;
2✔
6482
            // realm_results_t
1✔
6483
            auto res = realm_sync_subscription_set_insert_or_assign_query(new_subs, c_wrap_query_foo, nullptr, &index,
2✔
6484
                                                                          &inserted);
2✔
6485
            CHECK(inserted == true);
2!
6486
            CHECK(index == 0);
2!
6487
            CHECK(res);
2!
6488
            auto subs = realm_sync_subscription_set_commit(new_subs);
2✔
6489
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6490
                subs, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6491
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6492
            realm_release(sub);
2✔
6493
            realm_release(new_subs);
2✔
6494
            realm_release(subs);
2✔
6495
        }
2✔
6496

1✔
6497
        wait_for_download(*realm);
2✔
6498
        {
2✔
6499
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6500
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6501
            size_t count = 0;
2✔
6502
            realm_results_count(results, &count);
2✔
6503
            CHECK(count == 1);
2!
6504
            auto object = realm_results_get_object(results, 0);
2✔
6505
            REQUIRE(realm_object_is_valid(object));
2!
6506
            REQUIRE(object->get_column_value<ObjectId>("_id") == foo_obj_id);
2!
6507
            realm_release(object);
2✔
6508
            realm_release(results);
2✔
6509
        }
2✔
6510

1✔
6511
        {
2✔
6512
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6513
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6514
            std::size_t index = -1;
2✔
6515
            bool inserted = false;
2✔
6516
            realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_bar, nullptr, &index, &inserted);
2✔
6517
            CHECK(inserted);
2!
6518
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6519
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6520
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6521
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6522
            realm_release(sub);
2✔
6523
            realm_release(mut_sub);
2✔
6524
            realm_release(sub_c);
2✔
6525
        }
2✔
6526

1✔
6527
        {
2✔
6528
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6529
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6530
            size_t count = 0;
2✔
6531
            realm_results_count(results, &count);
2✔
6532
            CHECK(count == 2);
2!
6533
            realm_release(results);
2✔
6534
        }
2✔
6535

1✔
6536
        {
2✔
6537
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6538
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6539
            auto s = realm_sync_find_subscription_by_query(sub, c_wrap_query_foo);
2✔
6540
            CHECK(s != nullptr);
2!
6541
            auto cloned_s = realm_clone(s);
2✔
6542
            CHECK(realm_equals(s, cloned_s));
2!
6543
            bool erased = false;
2✔
6544
            auto result = realm_sync_subscription_set_erase_by_query(mut_sub, c_wrap_query_foo, &erased);
2✔
6545
            CHECK(erased);
2!
6546
            CHECK(result);
2!
6547
            auto c_wrap_new_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
2✔
6548
            std::size_t index = -1;
2✔
6549
            bool inserted = false;
2✔
6550
            auto results = realm_query_find_all(c_wrap_new_query_bar);
2✔
6551
            bool updated =
2✔
6552
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, nullptr, &index, &inserted);
2✔
6553
            CHECK(!inserted);
2!
6554
            CHECK(updated);
2!
6555
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6556
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6557
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6558
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6559
            realm_release(s);
2✔
6560
            realm_release(cloned_s);
2✔
6561
            realm_release(sub);
2✔
6562
            realm_release(mut_sub);
2✔
6563
            realm_release(sub_c);
2✔
6564
            realm_release(results);
2✔
6565
            realm_release(c_wrap_new_query_bar);
2✔
6566
        }
2✔
6567

1✔
6568
        {
2✔
6569
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6570
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6571
            size_t count = 0;
2✔
6572
            realm_results_count(results, &count);
2✔
6573
            CHECK(count == 1);
2!
6574
            auto object = realm_results_get_object(results, 0);
2✔
6575
            REQUIRE(realm_object_is_valid(object));
2!
6576
            REQUIRE(object->get_column_value<ObjectId>("_id") == bar_obj_id);
2!
6577
            realm_release(object);
2✔
6578
            realm_release(results);
2✔
6579
        }
2✔
6580

1✔
6581
        {
2✔
6582
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6583
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6584
            auto cleared = realm_sync_subscription_set_clear(mut_sub);
2✔
6585
            CHECK(cleared);
2!
6586
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6587
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6588
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6589
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6590
            realm_release(sub);
2✔
6591
            realm_release(mut_sub);
2✔
6592
            realm_release(sub_c);
2✔
6593
        }
2✔
6594

1✔
6595
        {
2✔
6596
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6597
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6598
            size_t count = std::numeric_limits<std::size_t>::max();
2✔
6599
            realm_results_count(results, &count);
2✔
6600
            CHECK(count == 0);
2!
6601
            realm_release(results);
2✔
6602
        }
2✔
6603

1✔
6604
        {
2✔
6605
            auto c_wrap_new_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
2✔
6606
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6607
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6608
            std::size_t index = -1;
2✔
6609
            bool inserted = false;
2✔
6610
            auto results = realm_query_find_all(c_wrap_new_query_bar);
2✔
6611
            bool success =
2✔
6612
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "bar", &index, &inserted);
2✔
6613
            CHECK(inserted);
2!
6614
            CHECK(success);
2!
6615
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6616
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6617
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6618
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6619
            realm_release(sub);
2✔
6620
            realm_release(mut_sub);
2✔
6621
            realm_release(sub_c);
2✔
6622
            realm_release(results);
2✔
6623
            realm_release(c_wrap_new_query_bar);
2✔
6624
        }
2✔
6625

1✔
6626
        {
2✔
6627
            realm->refresh();
2✔
6628
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6629
            size_t count = std::numeric_limits<std::size_t>::max();
2✔
6630
            realm_results_count(results, &count);
2✔
6631
            CHECK(count == 1);
2!
6632
            realm_release(results);
2✔
6633
        }
2✔
6634

1✔
6635
        {
2✔
6636
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6637
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6638
            bool erased = false;
2✔
6639
            realm_sync_subscription_set_erase_by_name(mut_sub, "bar", &erased);
2✔
6640
            CHECK(erased);
2!
6641
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6642
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6643
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6644
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6645
            realm_release(sub);
2✔
6646
            realm_release(mut_sub);
2✔
6647
            realm_release(sub_c);
2✔
6648
        }
2✔
6649

1✔
6650
        {
2✔
6651
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6652
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6653
            size_t count = std::numeric_limits<std::size_t>::max();
2✔
6654
            realm_results_count(results, &count);
2✔
6655
            CHECK(count == 0);
2!
6656
            realm_release(results);
2✔
6657
        }
2✔
6658

1✔
6659
        {
2✔
6660
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6661
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6662
            std::size_t index = -1;
2✔
6663
            bool inserted = false;
2✔
6664
            auto results = realm_query_find_all(c_wrap_query_bar);
2✔
6665
            bool success =
2✔
6666
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, nullptr, &index, &inserted);
2✔
6667
            CHECK(inserted);
2!
6668
            CHECK(success);
2!
6669
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6670
            // lambdas with state cannot be easily converted to function pointers, add a simple singleton that syncs
1✔
6671
            // the state among threads
1✔
6672
            struct SyncObject {
2✔
6673
                std::mutex m_mutex;
2✔
6674
                std::condition_variable m_cv;
2✔
6675
                realm_flx_sync_subscription_set_state_e m_state{RLM_SYNC_SUBSCRIPTION_UNCOMMITTED};
2✔
6676
                void* m_userdata;
2✔
6677

1✔
6678
                static SyncObject& create()
2✔
6679
                {
4✔
6680
                    static SyncObject sync_object;
4✔
6681
                    return sync_object;
4✔
6682
                }
4✔
6683

1✔
6684
                void set_state_and_notify(void* userdata, realm_flx_sync_subscription_set_state_e state)
2✔
6685
                {
2✔
6686
                    {
2✔
6687
                        std::lock_guard<std::mutex> guard{m_mutex};
2✔
6688
                        m_state = state;
2✔
6689
                        m_userdata = userdata;
2✔
6690
                    }
2✔
6691
                    m_cv.notify_one();
2✔
6692
                }
2✔
6693

1✔
6694
                realm_flx_sync_subscription_set_state_e wait_state()
2✔
6695
                {
2✔
6696
                    using namespace std::chrono_literals;
2✔
6697
                    std::unique_lock<std::mutex> lock{m_mutex};
2✔
6698
                    const auto delay = TEST_TIMEOUT_EXTRA > 0 ? std::chrono::seconds(5 + TEST_TIMEOUT_EXTRA) : 5s;
2✔
6699
                    bool completed_within_time_limit = m_cv.wait_for(lock, delay, [this]() {
4✔
6700
                        return m_state == RLM_SYNC_SUBSCRIPTION_COMPLETE && m_userdata != nullptr;
4✔
6701
                    });
4✔
6702
                    CHECK(completed_within_time_limit);
2!
6703
                    return m_state;
2✔
6704
                }
2✔
6705
            };
2✔
6706

1✔
6707
            auto callback = [](void* userdata, realm_flx_sync_subscription_set_state_e sub_state) {
2✔
6708
                SyncObject::create().set_state_and_notify(userdata, sub_state);
2✔
6709
            };
2✔
6710
            int userdata = 0;
2✔
6711
            realm_sync_on_subscription_set_state_change_async(
2✔
6712
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE, callback, &userdata,
2✔
6713
                nullptr);
2✔
6714
            CHECK(SyncObject::create().wait_state() ==
2!
6715
                  realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6716

1✔
6717
            realm_release(sub);
2✔
6718
            realm_release(mut_sub);
2✔
6719
            realm_release(sub_c);
2✔
6720
            realm_release(results);
2✔
6721
        }
2✔
6722

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

1✔
6739
            const realm_object_id_t& object_id = realm_sync_subscription_id(realm_flx_sync_sub);
2✔
6740
            CHECK(!realm::c_api::from_capi(object_id).to_string().empty());
2!
6741

1✔
6742
            realm_timestamp_t create_time = realm_sync_subscription_created_at(realm_flx_sync_sub);
2✔
6743
            realm_timestamp_t update_time = realm_sync_subscription_updated_at(realm_flx_sync_sub);
2✔
6744
            CHECK(create_time.seconds == update_time.seconds);
2!
6745
            CHECK(create_time.nanoseconds == update_time.nanoseconds);
2!
6746
            CHECK(create_time.seconds != 0);
2!
6747
            CHECK(create_time.seconds != 0);
2!
6748

1✔
6749
            const realm_string_t& name = realm_sync_subscription_name(realm_flx_sync_sub);
2✔
6750
            REQUIRE(name.size != 0);
2!
6751
            CHECK(std::string{name.data, name.size} == "test_bar");
2!
6752

1✔
6753
            auto mut_sub_1 = realm_sync_make_subscription_set_mutable(sub_c);
2✔
6754
            bool erased = false;
2✔
6755
            CHECK(realm_sync_subscription_set_erase_by_id(mut_sub_1, &object_id, &erased));
2!
6756
            CHECK(erased);
2!
6757
            auto sub_c_1 = realm_sync_subscription_set_commit(mut_sub_1);
2✔
6758

1✔
6759
            const auto sub_not_found = realm_sync_find_subscription_by_name(sub_c_1, "test_bar");
2✔
6760
            CHECK(!sub_not_found);
2!
6761

1✔
6762
            realm_release(sub);
2✔
6763
            realm_release(mut_sub);
2✔
6764
            realm_release(results);
2✔
6765
            realm_release(sub_c);
2✔
6766
            realm_release(sub_by_res);
2✔
6767
            realm_release(realm_flx_sync_sub);
2✔
6768
            realm_release(mut_sub_1);
2✔
6769
            realm_release(sub_c_1);
2✔
6770
            realm_release(sub_not_found);
2✔
6771
        }
2✔
6772

1✔
6773
        {
2✔
6774
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6775
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6776
            std::size_t index = -1;
2✔
6777
            bool inserted = false;
2✔
6778
            auto results = realm_query_find_all(c_wrap_query_bar);
2✔
6779
            bool success =
2✔
6780
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "test_bar", &index, &inserted);
2✔
6781
            CHECK(inserted);
2!
6782
            CHECK(success);
2!
6783
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6784
            auto sub_by_res = realm_sync_find_subscription_by_results(sub_c, results);
2✔
6785
            CHECK(sub_by_res);
2!
6786

1✔
6787
            auto mut_sub_1 = realm_sync_make_subscription_set_mutable(sub_c);
2✔
6788
            bool erased = false;
2✔
6789
            realm_sync_subscription_set_erase_by_results(mut_sub_1, results, &erased);
2✔
6790
            CHECK(erased);
2!
6791
            auto sub_c_1 = realm_sync_subscription_set_commit(mut_sub_1);
2✔
6792

1✔
6793
            realm_release(sub);
2✔
6794
            realm_release(mut_sub);
2✔
6795
            realm_release(results);
2✔
6796
            realm_release(sub_c);
2✔
6797
            realm_release(sub_by_res);
2✔
6798
            realm_release(mut_sub_1);
2✔
6799
            realm_release(sub_c_1);
2✔
6800
        }
2✔
6801

1✔
6802
        {
2✔
6803
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6804
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6805
            std::size_t index = -1;
2✔
6806
            bool inserted = false;
2✔
6807
            CHECK(realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_bar, nullptr, &index,
2!
6808
                                                                     &inserted));
2✔
6809
            CHECK(inserted);
2!
6810
            CHECK(realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_foo, nullptr, &index,
2!
6811
                                                                     &inserted));
2✔
6812
            CHECK(inserted);
2!
6813
            bool erased = false;
2✔
6814
            CHECK(realm_sync_subscription_set_erase_by_class_name(mut_sub, "Obj", &erased));
2!
6815
            CHECK(erased);
2!
6816
            // Nothing to remove when trying again.
1✔
6817
            CHECK(realm_sync_subscription_set_erase_by_class_name(mut_sub, "Obj", &erased));
2!
6818
            CHECK_FALSE(erased);
2!
6819
            realm_release(sub);
2✔
6820
            realm_release(mut_sub);
2✔
6821
        }
2✔
6822

1✔
6823
        realm_release(c_wrap_query_foo);
2✔
6824
        realm_release(c_wrap_query_bar);
2✔
6825
    });
2✔
6826
}
2✔
6827

6828
TEST_CASE("C API app: websocket provider", "[sync][app][c_api][baas]") {
2✔
6829
    using namespace realm::app;
2✔
6830
    using namespace realm::sync;
2✔
6831
    using namespace realm::sync::websocket;
2✔
6832

1✔
6833
    struct TestWebSocketObserverShim : sync::WebSocketObserver {
2✔
6834
    public:
2✔
6835
        explicit TestWebSocketObserverShim(realm_websocket_observer_t* observer)
2✔
6836
            : m_observer(observer)
2✔
6837
        {
2✔
6838
        }
2✔
6839

1✔
6840
        void websocket_connected_handler(const std::string& protocol) override
2✔
6841
        {
2✔
6842
            REALM_ASSERT(m_observer);
2✔
6843
            realm_sync_socket_websocket_connected(m_observer, protocol.c_str());
2✔
6844
        }
2✔
6845

1✔
6846
        void websocket_error_handler() override
2✔
6847
        {
1✔
UNCOV
6848
            REALM_ASSERT(m_observer);
×
UNCOV
6849
            realm_sync_socket_websocket_error(m_observer);
×
UNCOV
6850
        }
×
6851

1✔
6852
        bool websocket_binary_message_received(util::Span<const char> data) override
2✔
6853
        {
6✔
6854
            REALM_ASSERT(m_observer);
6✔
6855
            return realm_sync_socket_websocket_message(m_observer, data.data(), data.size());
6✔
6856
        }
6✔
6857

1✔
6858
        bool websocket_closed_handler(bool was_clean, WebSocketError error, std::string_view msg) override
2✔
6859
        {
1✔
UNCOV
6860
            REALM_ASSERT(m_observer);
×
UNCOV
6861
            return realm_sync_socket_websocket_closed(m_observer, was_clean,
×
UNCOV
6862
                                                      static_cast<realm_web_socket_errno_e>(error), msg.data());
×
UNCOV
6863
        }
×
6864

1✔
6865
    private:
2✔
6866
        realm_websocket_observer_t* m_observer;
2✔
6867
    };
2✔
6868

1✔
6869
    struct TestWebSocket : realm::c_api::WrapC, WebSocketInterface {
2✔
6870
    public:
2✔
6871
        TestWebSocket(DefaultSocketProvider& socket_provider, realm_websocket_endpoint_t endpoint,
2✔
6872
                      realm_websocket_observer_t* realm_websocket_observer)
2✔
6873
        {
2✔
6874
            WebSocketEndpoint ws_endpoint;
2✔
6875
            ws_endpoint.address = endpoint.address;
2✔
6876
            ws_endpoint.port = endpoint.port;
2✔
6877
            ws_endpoint.path = endpoint.path;
2✔
6878
            for (size_t i = 0; i < endpoint.num_protocols; ++i) {
24✔
6879
                ws_endpoint.protocols.push_back(endpoint.protocols[i]);
22✔
6880
            }
22✔
6881
            ws_endpoint.is_ssl = endpoint.is_ssl;
2✔
6882

1✔
6883
            auto observer = std::make_unique<TestWebSocketObserverShim>(realm_websocket_observer);
2✔
6884
            m_websocket = socket_provider.connect(std::move(observer), std::move(ws_endpoint));
2✔
6885
        }
2✔
6886

1✔
6887
        void async_write_binary(util::Span<const char> data, SyncSocketProvider::FunctionHandler&& handler) override
2✔
6888
        {
10✔
6889
            m_websocket->async_write_binary(data, std::move(handler));
10✔
6890
        }
10✔
6891

1✔
6892
    private:
2✔
6893
        std::unique_ptr<WebSocketInterface> m_websocket;
2✔
6894
    };
2✔
6895

1✔
6896
    struct TestSyncTimer : realm::c_api::WrapC, SyncSocketProvider::Timer {
2✔
6897
    public:
2✔
6898
        TestSyncTimer(DefaultSocketProvider& socket_provider, std::chrono::milliseconds delay,
2✔
6899
                      realm_sync_socket_timer_callback_t* callback)
2✔
6900
        {
8✔
6901
            m_timer = socket_provider.create_timer(delay, [capi_callback = callback](Status s) {
8✔
6902
                if (s.code() == ErrorCodes::Error::OperationAborted) {
8✔
6903
                    return realm_sync_socket_timer_canceled(capi_callback);
6✔
6904
                }
6✔
6905
                realm_sync_socket_timer_complete(
2✔
6906
                    capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()), s.reason().c_str());
2✔
6907
            });
2✔
6908
        }
8✔
6909

1✔
6910
        void cancel() override
2✔
6911
        {
8✔
6912
            m_timer->cancel();
8✔
6913
        }
8✔
6914

1✔
6915
    private:
2✔
6916
        SyncSocketProvider::SyncTimer m_timer;
2✔
6917
    };
2✔
6918

1✔
6919
    struct TestData {
2✔
6920
        DefaultSocketProvider* socket_provider;
2✔
6921
        int free_count = 0;
2✔
6922
    };
2✔
6923

1✔
6924
    DefaultSocketProvider default_socket_provider(util::Logger::get_default_logger(), "SocketProvider");
2✔
6925

1✔
6926
    static std::mutex catch_mutex;
2✔
6927
#define LOCKED_REQUIRE(...)                                                                                          \
2✔
6928
    do {                                                                                                             \
172✔
6929
        std::lock_guard lock(catch_mutex);                                                                           \
172✔
6930
        REQUIRE(__VA_ARGS__);                                                                                        \
172✔
6931
    } while (0)
172✔
6932

1✔
6933
    auto userdata_free_fn = [](realm_userdata_t user_ptr) {
2✔
6934
        auto test_data = static_cast<TestData*>(user_ptr);
2✔
6935
        LOCKED_REQUIRE(test_data);
2!
6936
        test_data->free_count++;
2✔
6937
    };
2✔
6938
    auto post_fn = [](realm_userdata_t userdata, realm_sync_socket_post_callback_t* callback) {
36✔
6939
        auto test_data = static_cast<TestData*>(userdata);
36✔
6940
        LOCKED_REQUIRE(test_data);
36!
6941
        LOCKED_REQUIRE(test_data->socket_provider);
36!
6942
        test_data->socket_provider->post([capi_callback = callback](Status s) {
36✔
6943
            realm_sync_socket_post_complete(capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()),
36✔
6944
                                            s.reason().c_str());
36✔
6945
        });
36✔
6946
    };
36✔
6947
    auto create_timer_fn = [](realm_userdata_t userdata, uint64_t delay_ms,
2✔
6948
                              realm_sync_socket_timer_callback_t* callback) -> realm_sync_socket_timer_t {
8✔
6949
        auto test_data = static_cast<TestData*>(userdata);
8✔
6950
        LOCKED_REQUIRE(test_data);
8!
6951
        LOCKED_REQUIRE(test_data->socket_provider);
8!
6952
        return static_cast<realm_sync_socket_timer_t>(
8✔
6953
            new TestSyncTimer(*test_data->socket_provider, std::chrono::milliseconds(delay_ms), callback));
8✔
6954
    };
8✔
6955
    auto cancel_timer_fn = [](realm_userdata_t userdata, realm_sync_socket_timer_t sync_timer) {
8✔
6956
        auto test_data = static_cast<TestData*>(userdata);
8✔
6957
        LOCKED_REQUIRE(test_data);
8!
6958
        LOCKED_REQUIRE(test_data->socket_provider);
8!
6959
        auto timer = static_cast<TestSyncTimer*>(sync_timer);
8✔
6960
        LOCKED_REQUIRE(timer);
8!
6961
        timer->cancel();
8✔
6962
    };
8✔
6963
    auto free_timer_fn = [](realm_userdata_t userdata, realm_sync_socket_timer_t sync_timer) {
8✔
6964
        auto test_data = static_cast<TestData*>(userdata);
8✔
6965
        LOCKED_REQUIRE(test_data);
8!
6966
        LOCKED_REQUIRE(test_data->socket_provider);
8!
6967
        realm_release(sync_timer);
8✔
6968
    };
8✔
6969
    auto websocket_connect_fn =
2✔
6970
        [](realm_userdata_t userdata, realm_websocket_endpoint_t endpoint,
2✔
6971
           realm_websocket_observer_t* realm_websocket_observer) -> realm_sync_socket_websocket_t {
2✔
6972
        auto test_data = static_cast<TestData*>(userdata);
2✔
6973
        LOCKED_REQUIRE(test_data);
2!
6974
        LOCKED_REQUIRE(test_data->socket_provider);
2!
6975
        return static_cast<realm_sync_socket_websocket_t>(
2✔
6976
            new TestWebSocket(*test_data->socket_provider, endpoint, realm_websocket_observer));
2✔
6977
    };
2✔
6978
    auto websocket_async_write_fn = [](realm_userdata_t userdata, realm_sync_socket_websocket_t sync_websocket,
2✔
6979
                                       const char* data, size_t size, realm_sync_socket_write_callback_t* callback) {
10✔
6980
        auto test_data = static_cast<TestData*>(userdata);
10✔
6981
        LOCKED_REQUIRE(test_data);
10!
6982
        LOCKED_REQUIRE(test_data->socket_provider);
10!
6983
        auto websocket = static_cast<TestWebSocket*>(sync_websocket);
10✔
6984
        LOCKED_REQUIRE(websocket);
10!
6985
        websocket->async_write_binary(util::Span{data, size}, [capi_callback = callback](Status s) {
10✔
6986
            realm_sync_socket_write_complete(
10✔
6987
                capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()), s.reason().c_str());
10✔
6988
        });
10✔
6989
    };
10✔
6990
    auto websocket_free_fn = [](realm_userdata_t userdata, realm_sync_socket_websocket_t sync_websocket) {
2✔
6991
        auto test_data = static_cast<TestData*>(userdata);
2✔
6992
        LOCKED_REQUIRE(test_data);
2!
6993
        LOCKED_REQUIRE(test_data->socket_provider);
2!
6994
        realm_release(sync_websocket);
2✔
6995
    };
2✔
6996

1✔
6997
    // Test drive.
1✔
6998
    TestData test_data{&default_socket_provider};
2✔
6999
    auto socket_provider = realm_sync_socket_new(static_cast<realm_userdata_t>(&test_data), userdata_free_fn, post_fn,
2✔
7000
                                                 create_timer_fn, cancel_timer_fn, free_timer_fn,
2✔
7001
                                                 websocket_connect_fn, websocket_async_write_fn, websocket_free_fn);
2✔
7002
    {
2✔
7003
        FLXSyncTestHarness harness("c_api_websocket_provider", FLXSyncTestHarness::default_server_schema(),
2✔
7004
                                   instance_of<SynchronousTestTransport>, *socket_provider);
2✔
7005

1✔
7006
        SyncTestFile test_config(harness.app()->current_user(), harness.schema(),
2✔
7007
                                 realm::SyncConfig::FLXSyncEnabled{});
2✔
7008
        auto realm = Realm::get_shared_realm(test_config);
2✔
7009
        bool wait_success = wait_for_download(*realm);
2✔
7010
        LOCKED_REQUIRE(!wait_success);
2!
7011
    }
2✔
7012
    realm_release(socket_provider);
2✔
7013
    default_socket_provider.stop(true);
2✔
7014
    LOCKED_REQUIRE(test_data.free_count == 1);
2!
7015
}
2✔
7016
#endif // REALM_ENABLE_AUTH_TESTS
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