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

realm / realm-core / nicola.cabiddu_1042

27 Sep 2023 06:04PM CUT coverage: 91.085% (-1.8%) from 92.915%
nicola.cabiddu_1042

Pull #6766

Evergreen

nicola-cab
Fix logic for dictionaries
Pull Request #6766: Client Reset for collections in mixed / nested collections

97276 of 178892 branches covered (0.0%)

1994 of 2029 new or added lines in 7 files covered. (98.28%)

4556 existing lines in 112 files now uncovered.

237059 of 260260 relevant lines covered (91.09%)

6321099.55 hits per line

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

99.52
/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/unit_test_transport.hpp"
45

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

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

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

60
using namespace realm;
61

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

201✔
210
    switch (lhs.type) {
402✔
211
        case RLM_TYPE_NULL:
90✔
212
        case RLM_TYPE_LIST:
90✔
213
        case RLM_TYPE_SET:
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✔
UNCOV
239
    }
×
UNCOV
240
    REALM_TERMINATE("");
×
UNCOV
241
}
×
242

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

259
template <class T>
260
CPtr<T> cptr_checked(T* ptr)
261
{
1,642✔
262
    return cptr(checked(ptr));
1,642✔
263
}
1,642✔
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
{
50✔
275
    void* clone = realm_clone(ptr);
50✔
276
    return CPtr<T>{static_cast<T*>(clone)};
50✔
277
}
50✔
278
} // anonymous namespace
279

280
#define CHECK_ERR(err)                                                                                               \
281
    do {                                                                                                             \
166✔
282
        realm_error_t _err;                                                                                          \
166✔
283
        _err.message = "";                                                                                           \
166✔
284
        _err.error = RLM_ERR_NONE;                                                                                   \
166✔
285
        CHECK(realm_get_last_error(&_err));                                                                          \
166✔
286
        if (_err.error != err) {                                                                                     \
166✔
UNCOV
287
            CHECK(_err.error == err);                                                                                \
×
UNCOV
288
            CHECK(std::string{_err.message} == "");                                                                  \
×
UNCOV
289
        }                                                                                                            \
×
290
        else {                                                                                                       \
166✔
291
            realm_clear_last_error();                                                                                \
166✔
292
        }                                                                                                            \
166✔
293
    } while (false);
166✔
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✔
UNCOV
304
            CHECK(_err.error == err);                                                                                \
×
UNCOV
305
            CHECK(std::string{_err.message} == "");                                                                  \
×
UNCOV
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]") {
48✔
318
    SECTION("realm_get_library_version_numbers()") {
48✔
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

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

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

24✔
339
    SECTION("realm_get_last_error()") {
48✔
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

24✔
355
    SECTION("realm_get_last_error_as_async_error()") {
48✔
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

24✔
392
    SECTION("realm_clear_last_error()") {
48✔
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

24✔
402
    SECTION("realm_clone() error") {
48✔
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

24✔
409
    SECTION("realm_create_thread_safe_reference() error") {
48✔
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

24✔
416
    SECTION("realm_is_frozen() false by default") {
48✔
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

24✔
422
    SECTION("realm_equals() with different object types returns false") {
48✔
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

24✔
429
    SECTION("realm_config_t") {
48✔
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

24✔
528
#if REALM_ENABLE_SYNC
48✔
529
    SECTION("realm_app_config_t") {
48✔
530
        const uint64_t request_timeout = 2500;
2✔
531
        auto transport = std::make_shared<UnitTestTransport>(request_timeout);
2✔
532
        transport->set_expected_options({{"device",
2✔
533
                                          {{"appId", "app_id_123"},
2✔
534
                                           {"platform", util::get_library_platform()},
2✔
535
                                           {"platformVersion", "some_platform_version"},
2✔
536
                                           {"sdk", "some_sdk_name"},
2✔
537
                                           {"sdkVersion", "some_sdk_version"},
2✔
538
                                           {"cpuArch", util::get_library_cpu_arch()},
2✔
539
                                           {"deviceName", "some_device_name"},
2✔
540
                                           {"deviceVersion", "some_device_version"},
2✔
541
                                           {"frameworkName", "some_framework_name"},
2✔
542
                                           {"frameworkVersion", "some_framework_version"},
2✔
543
                                           {"coreVersion", REALM_VERSION_STRING},
2✔
544
                                           {"bundleId", "some_bundle_id"}}}});
2✔
545

1✔
546
        auto http_transport = realm_http_transport(transport);
2✔
547
        auto app_config = cptr(realm_app_config_new("app_id_123", &http_transport));
2✔
548
        CHECK(app_config.get() != nullptr);
2!
549
        CHECK(app_config->app_id == "app_id_123");
2!
550
        CHECK(app_config->transport == transport);
2!
551

1✔
552
        realm_app_config_set_base_url(app_config.get(), "https://path/to/app");
2✔
553
        CHECK(app_config->base_url == "https://path/to/app");
2!
554

1✔
555
        realm_app_config_set_default_request_timeout(app_config.get(), request_timeout);
2✔
556
        CHECK(app_config->default_request_timeout_ms == request_timeout);
2!
557

1✔
558
        realm_app_config_set_platform_version(app_config.get(), "some_platform_version");
2✔
559
        CHECK(app_config->device_info.platform_version == "some_platform_version");
2!
560

1✔
561
        realm_app_config_set_sdk_version(app_config.get(), "some_sdk_version");
2✔
562
        CHECK(app_config->device_info.sdk_version == "some_sdk_version");
2!
563

1✔
564
        realm_app_config_set_sdk(app_config.get(), "some_sdk_name");
2✔
565
        CHECK(app_config->device_info.sdk == "some_sdk_name");
2!
566

1✔
567
        realm_app_config_set_device_name(app_config.get(), "some_device_name");
2✔
568
        CHECK(app_config->device_info.device_name == "some_device_name");
2!
569

1✔
570
        realm_app_config_set_device_version(app_config.get(), "some_device_version");
2✔
571
        CHECK(app_config->device_info.device_version == "some_device_version");
2!
572

1✔
573
        realm_app_config_set_framework_name(app_config.get(), "some_framework_name");
2✔
574
        CHECK(app_config->device_info.framework_name == "some_framework_name");
2!
575

1✔
576
        realm_app_config_set_framework_version(app_config.get(), "some_framework_version");
2✔
577
        CHECK(app_config->device_info.framework_version == "some_framework_version");
2!
578

1✔
579
        realm_app_config_set_bundle_id(app_config.get(), "some_bundle_id");
2✔
580
        CHECK(app_config->device_info.bundle_id == "some_bundle_id");
2!
581

1✔
582
        auto test_app = std::make_shared<app::App>(*app_config);
2✔
583
        auto credentials = app::AppCredentials::anonymous();
2✔
584
        // Verify the values above are included in the login request
1✔
585
        test_app->log_in_with_credentials(credentials, [&](const std::shared_ptr<realm::SyncUser>&,
2✔
586
                                                           realm::util::Optional<realm::app::AppError> error) {
2✔
587
            CHECK(!error);
2!
588
        });
2✔
589
    }
2✔
590
#endif // REALM_ENABLE_SYNC
48✔
591
}
48✔
592

593
namespace {
594

595
/// Generate realm_property_info_t for all possible property types.
596
std::vector<realm_property_info_t> all_property_types(const char* link_target)
597
{
618✔
598
    std::vector<realm_property_info_t> properties;
618✔
599

309✔
600
    static const char* names[] = {
618✔
601
        "int", "bool", "string", "binary", "timestamp", "float", "double", "decimal", "object_id", "uuid",
618✔
602
    };
618✔
603
    static const char* nullable_names[] = {
618✔
604
        "nullable_int",   "nullable_bool",   "nullable_string",  "nullable_binary",    "nullable_timestamp",
618✔
605
        "nullable_float", "nullable_double", "nullable_decimal", "nullable_object_id", "nullable_uuid",
618✔
606
    };
618✔
607
    static const char* list_names[] = {
618✔
608
        "int_list",   "bool_list",   "string_list",  "binary_list",    "timestamp_list",
618✔
609
        "float_list", "double_list", "decimal_list", "object_id_list", "uuid_list",
618✔
610
    };
618✔
611
    static const char* nullable_list_names[] = {
618✔
612
        "nullable_int_list",       "nullable_bool_list",  "nullable_string_list", "nullable_binary_list",
618✔
613
        "nullable_timestamp_list", "nullable_float_list", "nullable_double_list", "nullable_decimal_list",
618✔
614
        "nullable_object_id_list", "nullable_uuid_list",
618✔
615
    };
618✔
616
    static const char* set_names[] = {
618✔
617
        "int_set",   "bool_set",   "string_set",  "binary_set",    "timestamp_set",
618✔
618
        "float_set", "double_set", "decimal_set", "object_id_set", "uuid_set",
618✔
619
    };
618✔
620
    static const char* nullable_set_names[] = {
618✔
621
        "nullable_int_set",       "nullable_bool_set",  "nullable_string_set", "nullable_binary_set",
618✔
622
        "nullable_timestamp_set", "nullable_float_set", "nullable_double_set", "nullable_decimal_set",
618✔
623
        "nullable_object_id_set", "nullable_uuid_set",
618✔
624
    };
618✔
625
    static const char* dict_names[] = {
618✔
626
        "int_dict",   "bool_dict",   "string_dict",  "binary_dict",    "timestamp_dict",
618✔
627
        "float_dict", "double_dict", "decimal_dict", "object_id_dict", "uuid_dict",
618✔
628
    };
618✔
629
    static const char* nullable_dict_names[] = {
618✔
630
        "nullable_int_dict",       "nullable_bool_dict",  "nullable_string_dict", "nullable_binary_dict",
618✔
631
        "nullable_timestamp_dict", "nullable_float_dict", "nullable_double_dict", "nullable_decimal_dict",
618✔
632
        "nullable_object_id_dict", "nullable_uuid_dict",
618✔
633
    };
618✔
634
    static const realm_property_type_e types[] = {
618✔
635
        RLM_PROPERTY_TYPE_INT,       RLM_PROPERTY_TYPE_BOOL,  RLM_PROPERTY_TYPE_STRING, RLM_PROPERTY_TYPE_BINARY,
618✔
636
        RLM_PROPERTY_TYPE_TIMESTAMP, RLM_PROPERTY_TYPE_FLOAT, RLM_PROPERTY_TYPE_DOUBLE, RLM_PROPERTY_TYPE_DECIMAL128,
618✔
637
        RLM_PROPERTY_TYPE_OBJECT_ID, RLM_PROPERTY_TYPE_UUID,
618✔
638
    };
618✔
639

309✔
640
    size_t num_names = std::distance(std::begin(names), std::end(names));
618✔
641
    size_t num_nullable_names = std::distance(std::begin(nullable_names), std::end(nullable_names));
618✔
642
    size_t num_list_names = std::distance(std::begin(list_names), std::end(list_names));
618✔
643
    size_t num_nullable_list_names = std::distance(std::begin(nullable_list_names), std::end(nullable_list_names));
618✔
644
    size_t num_set_names = std::distance(std::begin(set_names), std::end(set_names));
618✔
645
    size_t num_nullable_set_names = std::distance(std::begin(nullable_set_names), std::end(nullable_set_names));
618✔
646
    size_t num_dict_names = std::distance(std::begin(dict_names), std::end(dict_names));
618✔
647
    size_t num_nullable_dict_names = std::distance(std::begin(nullable_dict_names), std::end(nullable_dict_names));
618✔
648
    size_t num_types = std::distance(std::begin(types), std::end(types));
618✔
649

309✔
650
    REALM_ASSERT(num_names == num_types);
618✔
651
    REALM_ASSERT(num_nullable_names == num_types);
618✔
652
    REALM_ASSERT(num_list_names == num_types);
618✔
653
    REALM_ASSERT(num_nullable_list_names == num_types);
618✔
654
    REALM_ASSERT(num_set_names == num_types);
618✔
655
    REALM_ASSERT(num_nullable_set_names == num_types);
618✔
656
    REALM_ASSERT(num_dict_names == num_types);
618✔
657
    REALM_ASSERT(num_nullable_dict_names == num_types);
618✔
658

309✔
659
    for (size_t i = 0; i < num_names; ++i) {
6,798✔
660
        const char* public_name = i == 0 ? "public_int" : "";
5,871✔
661
        realm_property_info_t normal{
6,180✔
662
            names[i],
6,180✔
663
            public_name,
6,180✔
664
            types[i],
6,180✔
665
            RLM_COLLECTION_TYPE_NONE,
6,180✔
666
            "",
6,180✔
667
            "",
6,180✔
668
            RLM_INVALID_PROPERTY_KEY,
6,180✔
669
            RLM_PROPERTY_NORMAL,
6,180✔
670
        };
6,180✔
671
        realm_property_info_t nullable{
6,180✔
672
            nullable_names[i],     "", types[i], RLM_COLLECTION_TYPE_NONE, "", "", RLM_INVALID_PROPERTY_KEY,
6,180✔
673
            RLM_PROPERTY_NULLABLE,
6,180✔
674
        };
6,180✔
675
        realm_property_info_t list{
6,180✔
676
            list_names[i],       "", types[i], RLM_COLLECTION_TYPE_LIST, "", "", RLM_INVALID_PROPERTY_KEY,
6,180✔
677
            RLM_PROPERTY_NORMAL,
6,180✔
678
        };
6,180✔
679
        realm_property_info_t nullable_list{
6,180✔
680
            nullable_list_names[i], "", types[i], RLM_COLLECTION_TYPE_LIST, "", "", RLM_INVALID_PROPERTY_KEY,
6,180✔
681
            RLM_PROPERTY_NULLABLE,
6,180✔
682
        };
6,180✔
683
        realm_property_info_t set{
6,180✔
684
            set_names[i],        "", types[i], RLM_COLLECTION_TYPE_SET, "", "", RLM_INVALID_PROPERTY_KEY,
6,180✔
685
            RLM_PROPERTY_NORMAL,
6,180✔
686
        };
6,180✔
687
        realm_property_info_t nullable_set{
6,180✔
688
            nullable_set_names[i], "", types[i], RLM_COLLECTION_TYPE_SET, "", "", RLM_INVALID_PROPERTY_KEY,
6,180✔
689
            RLM_PROPERTY_NULLABLE,
6,180✔
690
        };
6,180✔
691
        realm_property_info_t dict{
6,180✔
692
            dict_names[i],       "", types[i], RLM_COLLECTION_TYPE_DICTIONARY, "", "", RLM_INVALID_PROPERTY_KEY,
6,180✔
693
            RLM_PROPERTY_NORMAL,
6,180✔
694
        };
6,180✔
695
        realm_property_info_t nullable_dict{
6,180✔
696
            nullable_dict_names[i], "", types[i], RLM_COLLECTION_TYPE_DICTIONARY, "", "", RLM_INVALID_PROPERTY_KEY,
6,180✔
697
            RLM_PROPERTY_NULLABLE,
6,180✔
698
        };
6,180✔
699

3,090✔
700
        properties.push_back(normal);
6,180✔
701
        properties.push_back(nullable);
6,180✔
702
        properties.push_back(list);
6,180✔
703
        properties.push_back(nullable_list);
6,180✔
704
        properties.push_back(set);
6,180✔
705
        properties.push_back(nullable_set);
6,180✔
706
        properties.push_back(dict);
6,180✔
707
        properties.push_back(nullable_dict);
6,180✔
708
    }
6,180✔
709

309✔
710
    realm_property_info_t link{
618✔
711
        "link",      "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_NONE,
618✔
712
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
618✔
713
    };
618✔
714
    realm_property_info_t link_list{
618✔
715
        "link_list", "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_LIST,
618✔
716
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
618✔
717
    };
618✔
718
    realm_property_info_t link_set{
618✔
719
        "link_set",  "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_SET,
618✔
720
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
618✔
721
    };
618✔
722
    realm_property_info_t link_dict{
618✔
723
        "link_dict", "", RLM_PROPERTY_TYPE_OBJECT, RLM_COLLECTION_TYPE_DICTIONARY,
618✔
724
        link_target, "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
618✔
725
    };
618✔
726

309✔
727
    properties.push_back(link);
618✔
728
    properties.push_back(link_list);
618✔
729
    properties.push_back(link_set);
618✔
730
    properties.push_back(link_dict);
618✔
731

309✔
732
    realm_property_info_t mixed{
618✔
733
        "mixed", "", RLM_PROPERTY_TYPE_MIXED,  RLM_COLLECTION_TYPE_NONE,
618✔
734
        "",      "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NULLABLE,
618✔
735
    };
618✔
736
    // realm_property_info_t mixed_list{
309✔
737
    //     "mixed_list", "", RLM_PROPERTY_TYPE_MIXED,  RLM_COLLECTION_TYPE_LIST,
309✔
738
    //     "",           "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
309✔
739
    // };
309✔
740

309✔
741
    properties.push_back(mixed);
618✔
742
    // properties.push_back(mixed_list);
309✔
743

309✔
744
    return properties;
618✔
745
}
618✔
746

747
CPtr<realm_schema_t> make_schema()
748
{
320✔
749
    auto foo_properties = all_property_types("Bar");
320✔
750

160✔
751
    const realm_class_info_t classes[3] = {
320✔
752
        {
320✔
753
            "Foo",
320✔
754
            "",                    // primary key
320✔
755
            foo_properties.size(), // properties
320✔
756
            0,                     // computed_properties
320✔
757
            RLM_INVALID_CLASS_KEY,
320✔
758
            RLM_CLASS_NORMAL,
320✔
759
        },
320✔
760
        {
320✔
761
            "Bar",
320✔
762
            "int", // primary key
320✔
763
            5,     // properties
320✔
764
            1,     // computed properties,
320✔
765
            RLM_INVALID_CLASS_KEY,
320✔
766
            RLM_CLASS_NORMAL,
320✔
767
        },
320✔
768
        {
320✔
769
            "Embedded",
320✔
770
            "", // primary key
320✔
771
            1,  // properties
320✔
772
            0,  // computed properties,
320✔
773
            RLM_INVALID_CLASS_KEY,
320✔
774
            RLM_CLASS_EMBEDDED,
320✔
775
        },
320✔
776
    };
320✔
777

160✔
778
    const realm_property_info_t bar_properties[6] = {
320✔
779
        {
320✔
780
            "int",
320✔
781
            "",
320✔
782
            RLM_PROPERTY_TYPE_INT,
320✔
783
            RLM_COLLECTION_TYPE_NONE,
320✔
784
            "",
320✔
785
            "",
320✔
786
            RLM_INVALID_PROPERTY_KEY,
320✔
787
            RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
320✔
788
        },
320✔
789
        {
320✔
790
            "strings",
320✔
791
            "",
320✔
792
            RLM_PROPERTY_TYPE_STRING,
320✔
793
            RLM_COLLECTION_TYPE_LIST,
320✔
794
            "",
320✔
795
            "",
320✔
796
            RLM_INVALID_PROPERTY_KEY,
320✔
797
            RLM_PROPERTY_NORMAL | RLM_PROPERTY_NULLABLE,
320✔
798
        },
320✔
799
        {
320✔
800
            "doubles",
320✔
801
            "",
320✔
802
            RLM_PROPERTY_TYPE_DOUBLE,
320✔
803
            RLM_COLLECTION_TYPE_NONE,
320✔
804
            "",
320✔
805
            "",
320✔
806
            RLM_INVALID_PROPERTY_KEY,
320✔
807
            RLM_PROPERTY_NORMAL,
320✔
808
        },
320✔
809
        {
320✔
810
            "sub",
320✔
811
            "",
320✔
812
            RLM_PROPERTY_TYPE_OBJECT,
320✔
813
            RLM_COLLECTION_TYPE_NONE,
320✔
814
            "Embedded",
320✔
815
            "",
320✔
816
            RLM_INVALID_PROPERTY_KEY,
320✔
817
            RLM_PROPERTY_NULLABLE,
320✔
818
        },
320✔
819
        {
320✔
820
            "sub_list",
320✔
821
            "",
320✔
822
            RLM_PROPERTY_TYPE_OBJECT,
320✔
823
            RLM_COLLECTION_TYPE_LIST,
320✔
824
            "Embedded",
320✔
825
            "",
320✔
826
            RLM_INVALID_PROPERTY_KEY,
320✔
827
            RLM_PROPERTY_NORMAL,
320✔
828
        },
320✔
829
        {
320✔
830
            "linking_objects",
320✔
831
            "",
320✔
832
            RLM_PROPERTY_TYPE_LINKING_OBJECTS,
320✔
833
            RLM_COLLECTION_TYPE_LIST,
320✔
834
            "Foo",
320✔
835
            "link_list",
320✔
836
            RLM_INVALID_PROPERTY_KEY,
320✔
837
            RLM_PROPERTY_NORMAL,
320✔
838
        },
320✔
839
    };
320✔
840

160✔
841
    const realm_property_info_t embedded_properties[1] = {{
320✔
842
        "int",
320✔
843
        "",
320✔
844
        RLM_PROPERTY_TYPE_INT,
320✔
845
        RLM_COLLECTION_TYPE_NONE,
320✔
846
        "",
320✔
847
        "",
320✔
848
        RLM_INVALID_PROPERTY_KEY,
320✔
849
        RLM_PROPERTY_NORMAL,
320✔
850
    }};
320✔
851
    const realm_property_info_t* class_properties[3] = {foo_properties.data(), bar_properties, embedded_properties};
320✔
852

160✔
853
    return cptr(realm_schema_new(classes, 3, class_properties));
320✔
854
}
320✔
855

856
CPtr<realm_config_t> make_config(const char* filename, bool set_schema = true)
857
{
336✔
858
    auto config = cptr(realm_config_new());
336✔
859
    realm_config_set_path(config.get(), filename);
336✔
860
    realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
336✔
861

168✔
862
    if (set_schema) {
336✔
863
        auto schema = make_schema();
2✔
864
        CHECK(checked(schema.get()));
2!
865
        REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
2!
866
        realm_config_set_schema(config.get(), schema.get());
2✔
867
        realm_config_set_schema_version(config.get(), 0);
2✔
868
    }
2✔
869

168✔
870
    realm_config_set_automatic_change_notifications(config.get(), true);
336✔
871
    realm_config_set_max_number_of_active_versions(config.get(), 1000);
336✔
872

168✔
873
    return config;
336✔
874
}
336✔
875

876
struct ConfigUserdata {
877
    size_t num_initializations = 0;
878
    size_t num_migrations = 0;
879
    size_t num_compact_on_launch = 0;
880
};
881

882
bool initialize_data(void* userdata_p, realm_t*)
883
{
4✔
884
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
4✔
885
    ++userdata->num_initializations;
4✔
886
    return true;
4✔
887
}
4✔
888

889
void free_data(void* userdata_p)
890
{
2✔
891
    free(userdata_p);
2✔
892
}
2✔
893

894
void delete_data(void* userdata_p)
895
{
4✔
896
    delete static_cast<ConfigUserdata*>(userdata_p);
4✔
897
}
4✔
898

899
bool migrate_schema(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t*)
900
{
2✔
901
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
2✔
902
    static_cast<void>(old);
2✔
903
    static_cast<void>(new_);
2✔
904
    ++userdata->num_migrations;
2✔
905
    REQUIRE_FALSE((*old)->auto_refresh());
2!
906
    return true;
2✔
907
}
2✔
908

909
bool migrate_schema_delete_old_table(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t*)
910
{
2✔
911
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
2✔
912
    static_cast<void>(old);
2✔
913
    static_cast<void>(new_);
2✔
914
    ++userdata->num_migrations;
2✔
915
    bool table_deleted = false;
2✔
916
    CHECK(checked(realm_remove_table(new_, "Foo", &table_deleted)));
2!
917
    CHECK(table_deleted);
2!
918
    return table_deleted;
2✔
919
}
2✔
920

921
bool migrate_schema_rename_prop(void* userdata_p, realm_t* old, realm_t* new_, const realm_schema_t* schema)
922
{
2✔
923
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
2✔
924
    static_cast<void>(old);
2✔
925
    static_cast<void>(new_);
2✔
926
    ++userdata->num_migrations;
2✔
927
    CHECK(realm_schema_rename_property(new_, (realm_schema_t*)schema, "Foo", "int", "int_new"));
2!
928
    return true;
2✔
929
}
2✔
930

931
bool should_compact_on_launch(void* userdata_p, uint64_t, uint64_t)
932
{
4✔
933
    auto userdata = static_cast<ConfigUserdata*>(userdata_p);
4✔
934
    ++userdata->num_compact_on_launch;
4✔
935
    return false;
4✔
936
}
4✔
937

938
struct LogUserData {
939
    std::vector<std::string> log;
940
};
941

942
void realm_log_func(realm_userdata_t u, realm_log_level_e, const char* message)
943
{
6✔
944
    LogUserData* userdata = static_cast<LogUserData*>(u);
6✔
945
    userdata->log.emplace_back(message);
6✔
946
}
6✔
947

948
} // anonymous namespace
949

950
TEST_CASE("C API", "[c_api]") {
298✔
951
    TestFile test_file;
298✔
952

149✔
953
    SECTION("schema in config") {
298✔
954
        TestFile test_file_2;
20✔
955

10✔
956
        auto schema = make_schema();
20✔
957
        CHECK(checked(schema.get()));
20!
958
        REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
20!
959
        auto config = cptr(realm_config_new());
20✔
960
        realm_config_set_path(config.get(), test_file_2.path.c_str());
20✔
961
        realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
20✔
962
        realm_config_set_schema_version(config.get(), 0);
20✔
963
        realm_config_set_schema(config.get(), schema.get());
20✔
964

10✔
965
        SECTION("error on open") {
20✔
966
            {
2✔
967
                std::ofstream o(test_file_2.path.c_str());
2✔
968
                o << "Mary had a little lamb" << std::endl;
2✔
969
            }
2✔
970
            CHECK(!realm_open(config.get()));
2!
971
            realm_error_t err;
2✔
972
            CHECK(realm_get_last_error(&err));
2!
973
            REQUIRE(test_file_2.path == err.path);
2!
974
            CHECK(realm_clear_last_error());
2!
975
        }
2✔
976

10✔
977
        SECTION("data initialization callback") {
20✔
978
            ConfigUserdata userdata;
2✔
979
            realm_config_set_data_initialization_function(config.get(), initialize_data, &userdata, nullptr);
2✔
980
            auto realm = cptr_checked(realm_open(config.get()));
2✔
981
            CHECK(userdata.num_initializations == 1);
2!
982
        }
2✔
983

10✔
984
        SECTION("data initialization callback error") {
20✔
985
            ConfigUserdata* userdata = new ConfigUserdata();
2✔
986
            realm_config_set_data_initialization_function(
2✔
987
                config.get(),
2✔
988
                [](void*, realm_t*) {
2✔
989
                    return false;
2✔
990
                },
2✔
991
                userdata, delete_data);
2✔
992
            CHECK(!realm_open(config.get()));
2!
993
            CHECK_ERR(RLM_ERR_CALLBACK);
2!
994
        }
2✔
995

10✔
996
        SECTION("migration callback") {
20✔
997
            ConfigUserdata userdata;
2✔
998
            realm_config_set_migration_function(config.get(), migrate_schema, &userdata, nullptr);
2✔
999
            auto realm = cptr_checked(realm_open(config.get()));
2✔
1000
            CHECK(userdata.num_migrations == 0);
2!
1001
            realm.reset();
2✔
1002

1✔
1003
            auto config2 = cptr(realm_config_new());
2✔
1004
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
2✔
1005
            realm_config_set_path(config2.get(), test_file_2.path.c_str());
2✔
1006
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
2✔
1007
            realm_config_set_schema_version(config2.get(), 999);
2✔
1008
            realm_config_set_schema(config2.get(), empty_schema.get());
2✔
1009
            realm_config_set_migration_function(config2.get(), migrate_schema, &userdata, nullptr);
2✔
1010
            auto realm2 = cptr_checked(realm_open(config2.get()));
2✔
1011
            CHECK(userdata.num_migrations == 1);
2!
1012
        }
2✔
1013

10✔
1014
        SECTION("migrate schema and delete old table") {
20✔
1015
            TestFile test_file_3;
2✔
1016
            ConfigUserdata userdata;
2✔
1017

1✔
1018
            realm_config_set_migration_function(config.get(), migrate_schema_delete_old_table, &userdata, nullptr);
2✔
1019
            const realm_class_info_t foo_class[1] = {{
2✔
1020
                "Foo",
2✔
1021
                "int",
2✔
1022
                1,
2✔
1023
                0,
2✔
1024
                RLM_INVALID_CLASS_KEY,
2✔
1025
                RLM_CLASS_NORMAL,
2✔
1026
            }};
2✔
1027
            const realm_class_info_t bar_class[1] = {{
2✔
1028
                "Bar",
2✔
1029
                "int",
2✔
1030
                1,
2✔
1031
                0,
2✔
1032
                RLM_INVALID_CLASS_KEY,
2✔
1033
                RLM_CLASS_NORMAL,
2✔
1034
            }};
2✔
1035
            const realm_property_info_t properties[1] = {
2✔
1036
                {
2✔
1037
                    "int",
2✔
1038
                    "",
2✔
1039
                    RLM_PROPERTY_TYPE_INT,
2✔
1040
                    RLM_COLLECTION_TYPE_NONE,
2✔
1041
                    "",
2✔
1042
                    "",
2✔
1043
                    RLM_INVALID_PROPERTY_KEY,
2✔
1044
                    RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
2✔
1045
                },
2✔
1046
            };
2✔
1047
            const realm_property_info_t* props[1] = {properties};
2✔
1048
            auto schema = cptr(realm_schema_new(foo_class, 1, props));
2✔
1049
            auto new_schema = cptr(realm_schema_new(bar_class, 1, props));
2✔
1050
            CHECK(checked(schema.get()));
2!
1051
            CHECK(checked(new_schema.get()));
2!
1052
            REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
2!
1053
            REQUIRE(checked(realm_schema_validate(new_schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
2!
1054
            // realm with schema containing Foo
1✔
1055
            auto config = cptr(realm_config_new());
2✔
1056
            realm_config_set_path(config.get(), test_file_3.path.c_str());
2✔
1057
            realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
2✔
1058
            realm_config_set_schema_version(config.get(), 0);
2✔
1059
            realm_config_set_schema(config.get(), schema.get());
2✔
1060
            auto realm = cptr_checked(realm_open(config.get()));
2✔
1061
            CHECK(userdata.num_migrations == 0);
2!
1062
            realm.reset();
2✔
1063
            // migrate schema basically changing Foo into Bar
1✔
1064
            auto config2 = cptr(realm_config_new());
2✔
1065
            realm_config_set_path(config2.get(), test_file_3.path.c_str());
2✔
1066
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
2✔
1067
            realm_config_set_schema_version(config2.get(), 999);
2✔
1068
            realm_config_set_schema(config2.get(), new_schema.get());
2✔
1069
            realm_config_set_migration_function(config2.get(), migrate_schema_delete_old_table, &userdata, nullptr);
2✔
1070
            auto realm2 = cptr_checked(realm_open(config2.get()));
2✔
1071
            CHECK(userdata.num_migrations == 1);
2!
1072
            auto new_db_schema = realm_get_schema(realm2.get());
2✔
1073
            CHECK(realm_equals(new_db_schema, new_schema.get()));
2!
1074
            realm2.reset();
2✔
1075
            realm_release(new_db_schema);
2✔
1076
        }
2✔
1077

10✔
1078
        SECTION("migration callback rename property") {
20✔
1079
            TestFile test_file_3;
2✔
1080
            ConfigUserdata userdata;
2✔
1081

1✔
1082
            realm_config_set_migration_function(config.get(), migrate_schema_rename_prop, &userdata, nullptr);
2✔
1083

1✔
1084
            const realm_class_info_t foo_class[1] = {{
2✔
1085
                "Foo",
2✔
1086
                "int",
2✔
1087
                1,
2✔
1088
                0,
2✔
1089
                RLM_INVALID_CLASS_KEY,
2✔
1090
                RLM_CLASS_NORMAL,
2✔
1091
            }};
2✔
1092
            const realm_property_info_t foo_properties[1] = {
2✔
1093
                {
2✔
1094
                    "int",
2✔
1095
                    "",
2✔
1096
                    RLM_PROPERTY_TYPE_INT,
2✔
1097
                    RLM_COLLECTION_TYPE_NONE,
2✔
1098
                    "",
2✔
1099
                    "",
2✔
1100
                    RLM_INVALID_PROPERTY_KEY,
2✔
1101
                    RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
2✔
1102
                },
2✔
1103
            };
2✔
1104
            const realm_property_info_t foo_properties_new[1] = {
2✔
1105
                {
2✔
1106
                    "int_new",
2✔
1107
                    "",
2✔
1108
                    RLM_PROPERTY_TYPE_INT,
2✔
1109
                    RLM_COLLECTION_TYPE_NONE,
2✔
1110
                    "",
2✔
1111
                    "",
2✔
1112
                    RLM_INVALID_PROPERTY_KEY,
2✔
1113
                    RLM_PROPERTY_INDEXED | RLM_PROPERTY_PRIMARY_KEY,
2✔
1114
                },
2✔
1115
            };
2✔
1116
            const realm_property_info_t* props[1] = {foo_properties};
2✔
1117
            const realm_property_info_t* props_new[1] = {foo_properties_new};
2✔
1118

1✔
1119
            auto schema = cptr(realm_schema_new(foo_class, 1, props));
2✔
1120
            auto new_schema = cptr(realm_schema_new(foo_class, 1, props_new));
2✔
1121
            CHECK(checked(schema.get()));
2!
1122
            CHECK(checked(new_schema.get()));
2!
1123
            REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
2!
1124
            REQUIRE(checked(realm_schema_validate(new_schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
2!
1125
            // realm with schema
1✔
1126
            auto config = cptr(realm_config_new());
2✔
1127
            realm_config_set_path(config.get(), test_file_3.path.c_str());
2✔
1128
            realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
2✔
1129
            realm_config_set_schema_version(config.get(), 0);
2✔
1130
            realm_config_set_schema(config.get(), schema.get());
2✔
1131
            auto realm = cptr_checked(realm_open(config.get()));
2✔
1132
            CHECK(userdata.num_migrations == 0);
2!
1133
            realm.reset();
2✔
1134
            // realm with new schema
1✔
1135
            auto config2 = cptr(realm_config_new());
2✔
1136
            realm_config_set_path(config2.get(), test_file_3.path.c_str());
2✔
1137
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
2✔
1138
            realm_config_set_schema_version(config2.get(), 999);
2✔
1139
            realm_config_set_schema(config2.get(), new_schema.get());
2✔
1140
            realm_config_set_migration_function(config2.get(), migrate_schema_rename_prop, &userdata, nullptr);
2✔
1141
            auto realm2 = cptr_checked(realm_open(config2.get()));
2✔
1142
            CHECK(userdata.num_migrations == 1);
2!
1143
            auto new_db_schema = realm_get_schema(realm2.get());
2✔
1144
            CHECK(realm_equals(new_db_schema, new_schema.get()));
2!
1145
            realm2.reset();
2✔
1146
            realm_release(new_db_schema);
2✔
1147
        }
2✔
1148

10✔
1149
        SECTION("migration callback error") {
20✔
1150
            ConfigUserdata userdata;
2✔
1151
            auto realm = cptr_checked(realm_open(config.get()));
2✔
1152
            realm.reset();
2✔
1153

1✔
1154
            auto config2 = cptr(realm_config_new());
2✔
1155
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
2✔
1156
            realm_config_set_path(config2.get(), test_file_2.path.c_str());
2✔
1157
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
2✔
1158
            realm_config_set_schema_version(config2.get(), 999);
2✔
1159
            realm_config_set_schema(config2.get(), empty_schema.get());
2✔
1160

1✔
1161
            realm_config_set_migration_function(
2✔
1162
                config2.get(),
2✔
1163
                [](void*, realm_t*, realm_t*, const realm_schema_t*) {
2✔
1164
                    return false;
2✔
1165
                },
2✔
1166
                &userdata, nullptr);
2✔
1167
            CHECK(!realm_open(config2.get()));
2!
1168
            CHECK_ERR(RLM_ERR_CALLBACK);
2!
1169
        }
2✔
1170

10✔
1171
        SECTION("should compact on launch callback") {
20✔
1172
            void* userdata_p = malloc(sizeof(ConfigUserdata));
2✔
1173
            memset(userdata_p, 0, sizeof(ConfigUserdata));
2✔
1174
            realm_config_set_should_compact_on_launch_function(config.get(), should_compact_on_launch, userdata_p,
2✔
1175
                                                               free_data);
2✔
1176
            auto realm = cptr_checked(realm_open(config.get()));
2✔
1177
            CHECK(static_cast<ConfigUserdata*>(userdata_p)->num_compact_on_launch == 1);
2!
1178
        }
2✔
1179

10✔
1180
        SECTION("should compact on launch and initialization callback") {
20✔
1181
            ConfigUserdata* userdata = new ConfigUserdata();
2✔
1182
            realm_config_set_should_compact_on_launch_function(config.get(), should_compact_on_launch, userdata,
2✔
1183
                                                               delete_data);
2✔
1184
            realm_config_set_data_initialization_function(config.get(), initialize_data, userdata,
2✔
1185
                                                          free_data); // should not update free function
2✔
1186
            auto realm = cptr_checked(realm_open(config.get()));
2✔
1187
            CHECK(userdata->num_initializations == 1);
2!
1188
            CHECK(userdata->num_compact_on_launch == 1);
2!
1189
        }
2✔
1190

10✔
1191
        SECTION("migration callback register exception error") {
20✔
1192
            ConfigUserdata userdata;
2✔
1193
            auto realm = cptr_checked(realm_open(config.get()));
2✔
1194
            realm.reset();
2✔
1195

1✔
1196
            auto config2 = cptr(realm_config_new());
2✔
1197
            auto empty_schema = cptr(realm_schema_new(nullptr, 0, nullptr));
2✔
1198
            realm_config_set_path(config2.get(), test_file_2.path.c_str());
2✔
1199
            realm_config_set_schema_mode(config2.get(), RLM_SCHEMA_MODE_AUTOMATIC);
2✔
1200
            realm_config_set_schema_version(config2.get(), 999);
2✔
1201
            realm_config_set_schema(config2.get(), empty_schema.get());
2✔
1202

1✔
1203
            struct MyExceptionWrapper {
2✔
1204
                std::exception_ptr m_ptr{nullptr};
2✔
1205
            };
2✔
1206

1✔
1207
            realm_config_set_migration_function(
2✔
1208
                config2.get(),
2✔
1209
                [](void*, realm_t*, realm_t*, const realm_schema_t*) {
2✔
1210
                    try {
2✔
1211
                        throw std::runtime_error("Test error in callback");
2✔
1212
                    }
2✔
1213
                    catch (...) {
2✔
1214
                        auto wrapper = new MyExceptionWrapper();
2✔
1215
                        wrapper->m_ptr = std::current_exception();
2✔
1216
                        realm_register_user_code_callback_error((void*)(wrapper));
2✔
1217
                    }
2✔
1218
                    return false;
2✔
1219
                },
2✔
1220
                &userdata, nullptr);
2✔
1221

1✔
1222
            CHECK(!realm_open(config2.get()));
2!
1223
            realm_error_t _err;
2✔
1224
            _err.message = "";
2✔
1225
            _err.error = RLM_ERR_NONE;
2✔
1226
            CHECK(realm_get_last_error(&_err));
2!
1227
            CHECK(_err.error == RLM_ERR_CALLBACK);
2!
1228
            CHECK(std::string{_err.message} == "User-provided callback failed");
2!
1229
            REQUIRE(_err.usercode_error); // this is the error registered inside the callback
2!
1230
            auto ex = (MyExceptionWrapper*)_err.usercode_error;
2✔
1231
            try {
2✔
1232
                std::rethrow_exception(ex->m_ptr);
2✔
1233
            }
2✔
1234
            catch (const std::exception& ex) {
2✔
1235
                CHECK(std::string{ex.what()} == std::string{"Test error in callback"});
2!
1236
            }
2✔
1237
            CHECK(realm_clear_last_error());
2!
1238
            delete ex;
2✔
1239
        }
2✔
1240
    }
20✔
1241

149✔
1242
    SECTION("logging") {
298✔
1243
        LogUserData userdata;
2✔
1244
        auto log_level_old = util::Logger::get_default_level_threshold();
2✔
1245
        realm_set_log_callback(realm_log_func, RLM_LOG_LEVEL_DEBUG, &userdata, nullptr);
2✔
1246
        auto config = make_config(test_file.path.c_str(), false);
2✔
1247
        realm_t* realm = realm_open(config.get());
2✔
1248
        realm_begin_write(realm);
2✔
1249
        realm_commit(realm);
2✔
1250
        REQUIRE(userdata.log.size() == 3);
2!
1251
        realm_set_log_level(RLM_LOG_LEVEL_INFO);
2✔
1252
        // Commit begin/end should not be logged at INFO level
1✔
1253
        realm_begin_write(realm);
2✔
1254
        realm_commit(realm);
2✔
1255
        REQUIRE(userdata.log.size() == 3);
2!
1256
        realm_release(realm);
2✔
1257
        userdata.log.clear();
2✔
1258
        realm_set_log_level(RLM_LOG_LEVEL_ERROR);
2✔
1259
        realm = realm_open(config.get());
2✔
1260
        realm_release(realm);
2✔
1261
        REQUIRE(userdata.log.empty());
2!
1262

1✔
1263
        // Remove this logger again
1✔
1264
        realm_set_log_callback(nullptr, RLM_LOG_LEVEL_DEBUG, nullptr, nullptr);
2✔
1265
        // Restore old log level
1✔
1266
        util::Logger::set_default_level_threshold(log_level_old);
2✔
1267
    }
2✔
1268

149✔
1269
    realm_t* realm;
298✔
1270
    {
298✔
1271
        auto schema = make_schema();
298✔
1272
        CHECK(checked(schema.get()));
298!
1273
        REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
298!
1274

149✔
1275
        auto config = make_config(test_file.path.c_str(), false);
298✔
1276

149✔
1277
        realm = realm_open(config.get());
298✔
1278
        REQUIRE(checked(realm));
298!
1279
        REQUIRE(checked(realm_update_schema(realm, schema.get())));
298!
1280

149✔
1281
        CHECK(!realm_equals(realm, nullptr));
298!
1282

149✔
1283
        auto realm2 = cptr(realm_open(config.get()));
298✔
1284
        CHECK(checked(realm2.get()));
298!
1285
        CHECK(!realm_equals(realm, realm2.get()));
298!
1286
        CHECK(realm_equals(realm, realm));
298!
1287
    }
298✔
1288

149✔
1289
    CHECK(realm_get_num_classes(realm) == 3);
298!
1290

149✔
1291
    SECTION("cached realm") {
298✔
1292
        auto config2 = make_config(test_file.path.c_str(), false);
2✔
1293
        realm_config_set_cached(config2.get(), true);
2✔
1294
        REQUIRE(realm_config_get_cached(config2.get()));
2!
1295
        auto realm2 = cptr(realm_open(config2.get()));
2✔
1296
        CHECK(!realm_equals(realm, realm2.get()));
2!
1297
        auto realm3 = cptr(realm_open(config2.get()));
2✔
1298
        REQUIRE(realm_equals(realm3.get(), realm2.get()));
2!
1299
    }
2✔
1300

149✔
1301
    SECTION("native ptr conversion") {
298✔
1302
        realm::SharedRealm native;
2✔
1303
        _realm_get_native_ptr(realm, &native, sizeof(native));
2✔
1304
        auto path = native->config().path;
2✔
1305
        CHECK(path == test_file.path);
2!
1306

1✔
1307
        auto realm2 = cptr_checked(_realm_from_native_ptr(&native, sizeof(native)));
2✔
1308
        CHECK(realm_equals(realm2.get(), realm));
2!
1309
    }
2✔
1310

149✔
1311
    SECTION("realm changed notification") {
298✔
1312
        bool realm_changed_callback_called = false;
2✔
1313
        auto token = cptr(realm_add_realm_changed_callback(
2✔
1314
            realm,
2✔
1315
            [](void* userdata) {
2✔
1316
                *reinterpret_cast<bool*>(userdata) = true;
2✔
1317
            },
2✔
1318
            &realm_changed_callback_called, [](void*) {}));
2✔
1319

1✔
1320
        realm_begin_write(realm);
2✔
1321
        realm_commit(realm);
2✔
1322
        CHECK(realm_changed_callback_called);
2!
1323
    }
2✔
1324

149✔
1325
    SECTION("realm refresh registering while not in transaction") {
298✔
1326
        bool realm_refresh_callback_called = false;
2✔
1327
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1328
            realm,
2✔
1329
            [](void* userdata) {
1✔
UNCOV
1330
                *reinterpret_cast<bool*>(userdata) = true;
×
UNCOV
1331
            },
×
1332
            &realm_refresh_callback_called, [](void*) {}));
2✔
1333
        realm_begin_write(realm);
2✔
1334
        realm_commit(realm);
2✔
1335
        CHECK_FALSE(realm_refresh_callback_called);
2!
1336
    }
2✔
1337

149✔
1338
    SECTION("realm refresh registering callback in transaction but on the same version") {
298✔
1339
        bool realm_refresh_callback_called = false;
2✔
1340
        realm_begin_write(realm);
2✔
1341
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1342
            realm,
2✔
1343
            [](void* userdata) {
1✔
UNCOV
1344
                *reinterpret_cast<bool*>(userdata) = true;
×
UNCOV
1345
            },
×
1346
            &realm_refresh_callback_called, [](void*) {}));
2✔
1347
        realm_commit(realm);
2✔
1348
        CHECK_FALSE(realm_refresh_callback_called);
2!
1349
    }
2✔
1350

149✔
1351
    SECTION("realm async refresh - main use case") {
298✔
1352
        bool realm_refresh_callback_called = false;
2✔
1353
        auto config = make_config(test_file.path.c_str(), false);
2✔
1354
        auto realm2 = cptr(realm_open(config.get()));
2✔
1355

1✔
1356
        realm_begin_write(realm);
2✔
1357
        realm_begin_read(realm2.get());
2✔
1358
        realm_commit(realm);
2✔
1359

1✔
1360
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1361
            realm2.get(),
2✔
1362
            [](void* userdata) {
2✔
1363
                *reinterpret_cast<bool*>(userdata) = true;
2✔
1364
            },
2✔
1365
            &realm_refresh_callback_called, [](void*) {}));
2✔
1366

1✔
1367
        realm_refresh(realm2.get(), nullptr);
2✔
1368
        REQUIRE(token);
2!
1369
        CHECK(realm_refresh_callback_called);
2!
1370
    }
2✔
1371

149✔
1372
    SECTION("realm async refresh - main use case, multiple callbacks") {
298✔
1373
        std::atomic_int counter = 0;
2✔
1374
        auto config = make_config(test_file.path.c_str(), false);
2✔
1375
        auto realm2 = cptr(realm_open(config.get()));
2✔
1376

1✔
1377
        realm_begin_write(realm);
2✔
1378
        realm_begin_read(realm2.get());
2✔
1379
        realm_commit(realm);
2✔
1380

1✔
1381
        auto f = [](void* userdata) {
4✔
1382
            auto ptr = reinterpret_cast<std::atomic_int*>(userdata);
4✔
1383
            ptr->fetch_add(1);
4✔
1384
        };
4✔
1385
        auto token1 = cptr(realm_add_realm_refresh_callback(realm2.get(), f, &counter, [](void*) {}));
2✔
1386
        auto token2 = cptr(realm_add_realm_refresh_callback(realm2.get(), f, &counter, [](void*) {}));
2✔
1387
        realm_refresh(realm2.get(), nullptr);
2✔
1388
        REQUIRE(token1);
2!
1389
        REQUIRE(token2);
2!
1390
        CHECK(counter.load() == 2);
2!
1391
    }
2✔
1392

149✔
1393
    SECTION("realm refresh read transaction frozen - register on unfrozen realm") {
298✔
1394
        bool realm_refresh_callback_called = false;
2✔
1395
        realm_begin_read(realm);
2✔
1396

1✔
1397
        auto realm2 = cptr_checked(realm_freeze(realm));
2✔
1398
        CHECK(!realm_is_frozen(realm));
2!
1399
        CHECK(realm_is_frozen(realm2.get()));
2!
1400
        CHECK(realm != realm2.get());
2!
1401

1✔
1402
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1403
            realm,
2✔
1404
            [](void* userdata) {
1✔
UNCOV
1405
                *reinterpret_cast<bool*>(userdata) = true;
×
UNCOV
1406
            },
×
1407
            &realm_refresh_callback_called, [](void*) {}));
2✔
1408

1✔
1409
        realm_refresh(realm, nullptr);
2✔
1410
        CHECK_FALSE(realm_refresh_callback_called);
2!
1411
    }
2✔
1412

149✔
1413
    SECTION("realm refresh read transaction frozen - register on frozen realm") {
298✔
1414
        bool realm_refresh_callback_called = false;
2✔
1415
        realm_begin_read(realm);
2✔
1416

1✔
1417
        auto realm2 = cptr_checked(realm_freeze(realm));
2✔
1418
        CHECK(!realm_is_frozen(realm));
2!
1419
        CHECK(realm_is_frozen(realm2.get()));
2!
1420
        CHECK(realm != realm2.get());
2!
1421

1✔
1422
        auto token = cptr(realm_add_realm_refresh_callback(
2✔
1423
            realm2.get(),
2✔
1424
            [](void* userdata) {
1✔
UNCOV
1425
                *reinterpret_cast<bool*>(userdata) = true;
×
UNCOV
1426
            },
×
1427
            &realm_refresh_callback_called, [](void*) {}));
2✔
1428

1✔
1429
        realm_refresh(realm, nullptr);
2✔
1430
        CHECK(token == nullptr);
2!
1431
        CHECK_FALSE(realm_refresh_callback_called);
2!
1432
    }
2✔
1433

149✔
1434
    SECTION("schema is set after opening") {
298✔
1435
        const realm_class_info_t baz = {
2✔
1436
            "baz",
2✔
1437
            "", // primary key
2✔
1438
            1,  // properties
2✔
1439
            0,  // computed_properties
2✔
1440
            RLM_INVALID_CLASS_KEY,
2✔
1441
            RLM_CLASS_NORMAL,
2✔
1442
        };
2✔
1443

1✔
1444
        auto int_property = realm_property_info_t{
2✔
1445
            "int", "", RLM_PROPERTY_TYPE_INT,    RLM_COLLECTION_TYPE_NONE,
2✔
1446
            "",    "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
2✔
1447
        };
2✔
1448
        realm_property_info_t* baz_properties = &int_property;
2✔
1449

1✔
1450
        // get class count
1✔
1451
        size_t num_classes = realm_get_num_classes(realm);
2✔
1452
        realm_class_key_t* out_keys = (realm_class_key_t*)malloc(sizeof(realm_class_key_t) * num_classes);
2✔
1453
        // get class keys
1✔
1454
        realm_get_class_keys(realm, out_keys, num_classes, nullptr);
2✔
1455
        realm_class_info_t* classes = (realm_class_info_t*)malloc(sizeof(realm_class_info_t) * (num_classes + 1));
2✔
1456
        const realm_property_info_t** properties =
2✔
1457
            (const realm_property_info_t**)malloc(sizeof(realm_property_info_t*) * (num_classes + 1));
2✔
1458
        // iterating through each class, "recreate" the old schema
1✔
1459
        for (size_t i = 0; i < num_classes; i++) {
8✔
1460
            realm_get_class(realm, out_keys[i], &classes[i]);
6✔
1461
            size_t out_n;
6✔
1462
            realm_get_class_properties(realm, out_keys[i], nullptr, 0, &out_n);
6✔
1463
            realm_property_info_t* out_props = (realm_property_info_t*)malloc(sizeof(realm_property_info_t) * out_n);
6✔
1464
            realm_get_class_properties(realm, out_keys[i], out_props, out_n, nullptr);
6✔
1465
            properties[i] = out_props;
6✔
1466
        }
6✔
1467
        // add the new class and its properties to the arrays
1✔
1468
        classes[num_classes] = baz;
2✔
1469

1✔
1470
        properties[num_classes] = baz_properties;
2✔
1471

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

1✔
1475
        // check that the schema changed callback fires with the new schema
1✔
1476
        struct Context {
2✔
1477
            realm_schema_t* expected_schema;
2✔
1478
            bool result;
2✔
1479
        } context = {new_schema, false};
2✔
1480
        auto token = realm_add_schema_changed_callback(
2✔
1481
            realm,
2✔
1482
            [](void* userdata, auto* new_schema) {
2✔
1483
                auto& ctx = *reinterpret_cast<Context*>(userdata);
2✔
1484
                ctx.result = realm_equals(new_schema, ctx.expected_schema);
2✔
1485
            },
2✔
1486
            &context, [](void*) {});
2✔
1487

1✔
1488
        CHECK(checked(realm_update_schema(realm, new_schema)));
2!
1489
        CHECK(context.result);
2!
1490
        auto new_num_classes = realm_get_num_classes(realm);
2✔
1491
        CHECK(new_num_classes == (num_classes + 1));
2!
1492

1✔
1493
        bool found;
2✔
1494
        realm_class_info_t baz_info;
2✔
1495
        CHECK(checked(realm_find_class(realm, "baz", &found, &baz_info)));
2!
1496
        CHECK(found);
2!
1497
        realm_property_info_t baz_int_property;
2✔
1498
        CHECK(checked(realm_find_property(realm, baz_info.key, "int", &found, &baz_int_property)));
2!
1499
        CHECK(found);
2!
1500

1✔
1501
        free(out_keys);
2✔
1502
        free(classes);
2✔
1503
        for (size_t i = 0; i < num_classes; i++) {
8✔
1504
            free((realm_property_info_t*)properties[i]);
6✔
1505
        }
6✔
1506
        free(properties);
2✔
1507
        realm_release(new_schema);
2✔
1508
        realm_release(token);
2✔
1509
    }
2✔
1510

149✔
1511
    SECTION("schema validates") {
298✔
1512
        auto schema = realm_get_schema(realm);
2✔
1513
        CHECK(checked(schema));
2!
1514
        CHECK(checked(realm_schema_validate(schema, realm_schema_validation_mode::RLM_SCHEMA_VALIDATION_BASIC)));
2!
1515

1✔
1516
        auto schema2 = realm_get_schema(realm);
2✔
1517
        CHECK(checked(schema2));
2!
1518
        CHECK(realm_equals(schema, schema2));
2!
1519
        realm_release(schema2);
2✔
1520
        realm_release(schema);
2✔
1521
    }
2✔
1522

149✔
1523
    SECTION("clone schema") {
298✔
1524
        auto schema = cptr(realm_get_schema(realm));
2✔
1525
        auto schema2 = clone_cptr(schema);
2✔
1526
        CHECK(schema.get() != schema2.get());
2!
1527
        CHECK(realm_equals(schema.get(), schema2.get()));
2!
1528
    }
2✔
1529

149✔
1530
    auto write = [&](auto&& f) {
396✔
1531
        checked(realm_begin_write(realm));
396✔
1532
        f();
396✔
1533
        checked(realm_commit(realm));
396✔
1534
        checked(realm_refresh(realm, nullptr));
396✔
1535
    };
396✔
1536

149✔
1537
    bool found = false;
298✔
1538

149✔
1539
    realm_class_info_t class_foo, class_bar, class_embedded;
298✔
1540
    CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
298!
1541
    REQUIRE(found);
298!
1542
    CHECK(checked(realm_find_class(realm, "Bar", &found, &class_bar)));
298!
1543
    REQUIRE(found);
298!
1544
    CHECK(checked(realm_find_class(realm, "Embedded", &found, &class_embedded)));
298!
1545
    REQUIRE(found);
298!
1546

149✔
1547
    std::map<std::string, realm_property_key_t> foo_properties;
298✔
1548
    for (const auto& p : all_property_types("Bar")) {
25,330✔
1549
        realm_property_info_t info;
25,330✔
1550
        bool found = false;
25,330✔
1551
        REQUIRE(realm_find_property(realm, class_foo.key, p.name, &found, &info));
25,330!
1552
        REQUIRE(found);
25,330!
1553
        CHECK(p.key == RLM_INVALID_PROPERTY_KEY);
25,330!
1554
        CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
25,330!
1555
        CHECK(info.type == p.type);
25,330!
1556
        CHECK(std::string{info.public_name} == p.public_name);
25,330!
1557
        CHECK(info.collection_type == p.collection_type);
25,330!
1558
        CHECK(std::string{info.link_target} == p.link_target);
25,330!
1559
        CHECK(std::string{info.link_origin_property_name} == p.link_origin_property_name);
25,330!
1560
        foo_properties[info.name] = info.key;
25,330✔
1561
    }
25,330✔
1562

149✔
1563
    std::map<std::string, realm_property_key_t> bar_properties;
298✔
1564
    {
298✔
1565
        realm_property_info_t info;
298✔
1566
        bool found = false;
298✔
1567
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "int", &found, &info)));
298!
1568
        REQUIRE(found);
298!
1569
        bar_properties["int"] = info.key;
298✔
1570

149✔
1571
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "strings", &found, &info)));
298!
1572
        REQUIRE(found);
298!
1573
        bar_properties["strings"] = info.key;
298✔
1574

149✔
1575
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "doubles", &found, &info)));
298!
1576
        REQUIRE(found);
298!
1577
        bar_properties["doubles"] = info.key;
298✔
1578

149✔
1579
        REQUIRE(checked(realm_find_property(realm, class_bar.key, "linking_objects", &found, &info)));
298!
1580
        REQUIRE(found);
298!
1581
        bar_properties["linking_objects"] = info.key;
298✔
1582
    }
298✔
1583

149✔
1584
    realm_property_key_t foo_int_key = foo_properties["int"];
298✔
1585
    realm_property_key_t foo_str_key = foo_properties["string"];
298✔
1586
    realm_property_key_t foo_links_key = foo_properties["link_list"];
298✔
1587
    realm_property_key_t bar_int_key = bar_properties["int"];
298✔
1588
    realm_property_key_t bar_strings_key = bar_properties["strings"];
298✔
1589
    realm_property_key_t bar_doubles_key = bar_properties["doubles"];
298✔
1590

149✔
1591
    SECTION("realm_freeze()") {
298✔
1592
        auto realm2 = cptr_checked(realm_freeze(realm));
2✔
1593
        CHECK(!realm_is_frozen(realm));
2!
1594
        CHECK(realm_is_frozen(realm2.get()));
2!
1595
    }
2✔
1596

149✔
1597
    SECTION("realm_find_class() errors") {
298✔
1598
        bool found = true;
2✔
1599
        CHECK(realm_find_class(realm, "does not exist", &found, nullptr));
2!
1600
        CHECK(!found);
2!
1601
    }
2✔
1602

149✔
1603
    SECTION("realm_compact()") {
298✔
1604
        bool did_compact = false;
2✔
1605
        CHECK(checked(realm_compact(realm, &did_compact)));
2!
1606
        CHECK(did_compact);
2!
1607
    }
2✔
1608

149✔
1609
    SECTION("realm_remove_table()") {
298✔
1610
        bool table_deleted = true;
2✔
1611
        CHECK(!realm_remove_table(realm, "Foo", &table_deleted));
2!
1612
        CHECK_ERR(RLM_ERR_INVALID_SCHEMA_CHANGE);
2!
1613
        CHECK(!table_deleted);
2!
1614
    }
2✔
1615

149✔
1616
    SECTION("realm_get_class_keys()") {
298✔
1617
        realm_class_key_t keys[2];
2✔
1618
        // return total number of keys present, copy only if there is enough space in the vector passed in
1✔
1619
        size_t found = 0;
2✔
1620
        CHECK(checked(realm_get_class_keys(realm, keys, 2, &found)));
2!
1621
        CHECK(found == 3);
2!
1622
        CHECK(checked(realm_get_class_keys(realm, keys, 1, &found)));
2!
1623
        CHECK(found == 3);
2!
1624
    }
2✔
1625

149✔
1626
    SECTION("realm_find_property() errors") {
298✔
1627
        realm_property_info_t dummy;
2✔
1628
        CHECK(!realm_find_property(realm, 123123123, "Foo", &found, &dummy));
2!
1629
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1630
        CHECK(!realm_find_property(realm, 123123123, "Foo", &found, nullptr));
2!
1631
        CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1632

1✔
1633
        bool found;
2✔
1634
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", nullptr, nullptr)));
2!
1635
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", &found, nullptr)));
2!
1636
        CHECK(found);
2!
1637

1✔
1638
        found = true;
2✔
1639
        CHECK(checked(realm_find_property(realm, class_foo.key, "i don't exist", &found, nullptr)));
2!
1640
        CHECK(!found);
2!
1641
    }
2✔
1642

149✔
1643
    SECTION("realm_find_property_by_public_name()") {
298✔
1644
        realm_property_info_t property;
2✔
1645
        bool found = false;
2✔
1646
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "public_int", &found, &property)));
2!
1647
        CHECK(found);
2!
1648
        CHECK(property.key == foo_int_key);
2!
1649

1✔
1650
        found = false;
2✔
1651
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "string", &found, &property)));
2!
1652
        CHECK(found);
2!
1653
        CHECK(property.key == foo_properties["string"]);
2!
1654

1✔
1655
        CHECK(checked(realm_find_property_by_public_name(realm, class_foo.key, "I don't exist", &found, &property)));
2!
1656
        CHECK(!found);
2!
1657
    }
2✔
1658

149✔
1659
    SECTION("realm_get_property_keys()") {
298✔
1660
        size_t num_found = 0;
2✔
1661
        size_t properties_found = 0;
2✔
1662

1✔
1663
        // discover how many properties there are.
1✔
1664
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, nullptr, 0, &properties_found)));
2!
1665
        realm_property_key_t* properties_foo =
2✔
1666
            (realm_property_key_t*)malloc(sizeof(realm_property_key_t) * properties_found);
2✔
1667
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, properties_foo, properties_found, &num_found)));
2!
1668
        CHECK(num_found == properties_found);
2!
1669
        CHECK(properties_foo[0] == foo_properties["int"]);
2!
1670
        realm_free(properties_foo);
2✔
1671

1✔
1672
        num_found = 0;
2✔
1673
        properties_found = 0;
2✔
1674
        // discover how many properties there are.
1✔
1675
        CHECK(checked(realm_get_property_keys(realm, class_bar.key, nullptr, 0, &properties_found)));
2!
1676
        realm_property_key_t* properties_bar =
2✔
1677
            (realm_property_key_t*)malloc(sizeof(realm_property_key_t) * properties_found);
2✔
1678
        CHECK(checked(realm_get_property_keys(realm, class_bar.key, properties_bar, properties_found, &num_found)));
2!
1679
        CHECK(num_found == properties_found);
2!
1680
        CHECK(properties_bar[2] == bar_properties["doubles"]);
2!
1681
        CHECK(properties_bar[0] == bar_properties["int"]);
2!
1682
        realm_free(properties_bar);
2✔
1683

1✔
1684
        num_found = 0;
2✔
1685
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, nullptr, 0, &num_found)));
2!
1686
        CHECK(num_found == class_foo.num_properties + class_foo.num_computed_properties);
2!
1687

1✔
1688
        std::vector<realm_property_key_t> ps;
2✔
1689
        ps.resize(1000);
2✔
1690
        CHECK(checked(realm_get_property_keys(realm, class_foo.key, ps.data(), ps.size(), &num_found)));
2!
1691
        CHECK(num_found == class_foo.num_properties + class_foo.num_computed_properties);
2!
1692

1✔
1693
        CHECK(checked(realm_get_property_keys(realm, class_bar.key, ps.data(), ps.size(), &num_found)));
2!
1694
        CHECK(num_found == 6);
2!
1695
    }
2✔
1696

149✔
1697
    SECTION("realm_get_property()") {
298✔
1698
        realm_property_info_t prop;
2✔
1699
        CHECK(checked(realm_get_property(realm, class_bar.key, bar_properties["linking_objects"], &prop)));
2!
1700
        CHECK(prop.key == bar_properties["linking_objects"]);
2!
1701
        CHECK(std::string{prop.name} == "linking_objects");
2!
1702

1✔
1703
        CHECK(!realm_get_property(realm, class_bar.key, 123123123, &prop));
2!
1704
        CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
1705
    }
2✔
1706

149✔
1707
    SECTION("realm_object_create() errors") {
298✔
1708
        SECTION("invalid table") {
14✔
1709
            write([&]() {
2✔
1710
                auto p = realm_object_create(realm, 123123123);
2✔
1711
                CHECK(!p);
2!
1712
                CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1713
            });
2✔
1714
        }
2✔
1715

7✔
1716
        SECTION("missing primary key") {
14✔
1717
            write([&]() {
2✔
1718
                auto p = realm_object_create(realm, class_bar.key);
2✔
1719
                CHECK(!p);
2!
1720
                CHECK_ERR(RLM_ERR_MISSING_PRIMARY_KEY);
2!
1721
            });
2✔
1722
        }
2✔
1723

7✔
1724
        SECTION("embedded object") {
14✔
1725
            write([&]() {
2✔
1726
                auto p = realm_object_create(realm, class_embedded.key);
2✔
1727
                CHECK(!p);
2!
1728
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
1729
            });
2✔
1730
        }
2✔
1731

7✔
1732
        SECTION("wrong primary key type") {
14✔
1733
            write([&]() {
2✔
1734
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_str_val("Hello"));
2✔
1735
                CHECK(!p);
2!
1736
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
1737
            });
2✔
1738

1✔
1739
            write([&]() {
2✔
1740
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_null());
2✔
1741
                CHECK(!p);
2!
1742
                CHECK_ERR(RLM_ERR_PROPERTY_NOT_NULLABLE);
2!
1743
            });
2✔
1744
        }
2✔
1745

7✔
1746
        SECTION("class does not have a primary key") {
14✔
1747
            write([&]() {
2✔
1748
                CHECK(!realm_object_create_with_primary_key(realm, class_foo.key, rlm_int_val(123)));
2!
1749
                CHECK_ERR(RLM_ERR_UNEXPECTED_PRIMARY_KEY);
2!
1750
            });
2✔
1751
        }
2✔
1752

7✔
1753
        SECTION("duplicate primary key") {
14✔
1754
            write([&]() {
2✔
1755
                cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123)));
2✔
1756
                auto p = realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123));
2✔
1757
                CHECK(!p);
2!
1758
                CHECK_ERR(RLM_ERR_OBJECT_ALREADY_EXISTS);
2!
1759
            });
2✔
1760
        }
2✔
1761

7✔
1762
        SECTION("not in a transaction") {
14✔
1763
            CHECK(!realm_object_create(realm, class_foo.key));
2!
1764
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
1765
        }
2✔
1766
    }
14✔
1767

149✔
1768

149✔
1769
    SECTION("objects") {
298✔
1770
        CPtr<realm_object_t> obj1;
198✔
1771
        CPtr<realm_object_t> obj2;
198✔
1772
        auto int_val1 = rlm_int_val(123);
198✔
1773
        auto int_val2 = rlm_int_val(456);
198✔
1774
        write([&]() {
198✔
1775
            obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
198✔
1776
            CHECK(obj1);
198!
1777
            CHECK(checked(realm_set_value(obj1.get(), foo_int_key, int_val1, false)));
198!
1778
            CHECK(checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("Hello, World!"), false)));
198!
1779
            obj2 = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(1)));
198✔
1780
            CHECK(obj2);
198!
1781
            CPtr<realm_object_t> obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
198✔
1782
            CHECK(obj3);
198!
1783
            CHECK(checked(realm_set_value(obj3.get(), foo_int_key, int_val2, false)));
198!
1784
            CPtr<realm_object_t> obj4 = cptr_checked(realm_object_create(realm, class_foo.key));
198✔
1785
            CHECK(obj3);
198!
1786
            CHECK(checked(realm_set_value(obj4.get(), foo_int_key, int_val1, false)));
198!
1787
        });
198✔
1788

99✔
1789
        size_t foo_count, bar_count;
198✔
1790
        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &foo_count)));
198!
1791
        CHECK(checked(realm_get_num_objects(realm, class_bar.key, &bar_count)));
198!
1792
        REQUIRE(foo_count == 3);
198!
1793
        REQUIRE(bar_count == 1);
198!
1794

99✔
1795
        SECTION("realm_get_value_by_property_index") {
198✔
1796
            realm_value value;
2✔
1797
            CHECK(checked(realm_get_value_by_property_index(obj1.get(), 0, &value)));
2!
1798
            CHECK(value.integer == int_val1.integer);
2!
1799
            CHECK(checked(realm_get_value_by_property_index(obj1.get(), 16, &value)));
2!
1800
            CHECK(value.string.data == std::string{"Hello, World!"});
2!
1801
        }
2✔
1802

99✔
1803
        SECTION("realm_clone()") {
198✔
1804
            auto obj1a = clone_cptr(obj1);
2✔
1805
            CHECK(realm_equals(obj1a.get(), obj1.get()));
2!
1806
        }
2✔
1807

99✔
1808
        SECTION("native pointer mapping") {
198✔
1809
            auto object = *static_cast<const realm::Object*>(_realm_object_get_native_ptr(obj1.get()));
2✔
1810
            auto obj = object.get_obj();
2✔
1811
            CHECK(obj.get<int64_t>(realm::ColKey(foo_int_key)) == int_val1.integer);
2!
1812

1✔
1813
            auto obj1a = cptr_checked(_realm_object_from_native_copy(&object, sizeof(object)));
2✔
1814
            CHECK(realm_equals(obj1.get(), obj1a.get()));
2!
1815
            auto obj1b = cptr_checked(_realm_object_from_native_move(&object, sizeof(object)));
2✔
1816
            CHECK(realm_equals(obj1.get(), obj1b.get()));
2!
1817
        }
2✔
1818

99✔
1819
        SECTION("realm_get_num_objects()") {
198✔
1820
            size_t num_foos, num_bars;
2✔
1821
            CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_foos)));
2!
1822
            CHECK(checked(realm_get_num_objects(realm, class_bar.key, &num_bars)));
2!
1823
            CHECK(num_foos == 3);
2!
1824
            CHECK(num_bars == 1);
2!
1825

1✔
1826
            CHECK(checked(realm_get_num_objects(realm, class_bar.key, nullptr)));
2!
1827
            CHECK(!realm_get_num_objects(realm, 123123123, nullptr));
2!
1828
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1829
        }
2✔
1830

99✔
1831
        SECTION("realm_get_object()") {
198✔
1832
            realm_object_key_t obj1_key = realm_object_get_key(obj1.get());
2✔
1833
            auto obj1a = cptr_checked(realm_get_object(realm, class_foo.key, obj1_key));
2✔
1834
            CHECK(obj1a);
2!
1835
            CHECK(realm_equals(obj1a.get(), obj1.get()));
2!
1836

1✔
1837
            realm_object_key_t invalid_key = 123123123;
2✔
1838
            CHECK(!realm_get_object(realm, class_foo.key, invalid_key));
2!
1839
            CHECK_ERR(RLM_ERR_NO_SUCH_OBJECT);
2!
1840

1✔
1841
            realm_class_key_t invalid_class_key = 123123123;
2✔
1842
            CHECK(!realm_get_object(realm, invalid_class_key, obj1_key));
2!
1843
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1844
        }
2✔
1845

99✔
1846
        SECTION("create object with primary key that already exists") {
198✔
1847
            bool did_create;
2✔
1848
            auto obj2a = cptr_checked(
2✔
1849
                realm_object_get_or_create_with_primary_key(realm, class_bar.key, rlm_int_val(1), &did_create));
2✔
1850
            CHECK(!did_create);
2!
1851
            CHECK(realm_equals(obj2a.get(), obj2.get()));
2!
1852
        }
2✔
1853

99✔
1854
        SECTION("realm_get_value()") {
198✔
1855
            realm_value_t value;
2✔
1856
            CHECK(checked(realm_get_value(obj1.get(), foo_int_key, &value)));
2!
1857
            CHECK(value.type == RLM_TYPE_INT);
2!
1858
            CHECK(value.integer == 123);
2!
1859

1✔
1860
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
2!
1861
            CHECK(value.type == RLM_TYPE_STRING);
2!
1862
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
2!
1863

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

1✔
1866
            CHECK(!realm_get_value(obj1.get(), 123123123, &value));
2!
1867
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
1868

1✔
1869
            CHECK(!realm_get_value(obj1.get(), 123123123, nullptr));
2!
1870
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
1871

1✔
1872
            // Cannot use realm_get_value() to get a list.
1✔
1873
            CHECK(!realm_get_value(obj1.get(), foo_links_key, &value));
2!
1874
            CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
1875

1✔
1876
            write([&]() {
2✔
1877
                CHECK(checked(realm_object_delete(obj1.get())));
2!
1878
            });
2✔
1879
            CHECK(!realm_get_value(obj1.get(), foo_int_key, &value));
2!
1880
            CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
1881
        }
2✔
1882

99✔
1883
        SECTION("realm_get_values()") {
198✔
1884
            realm_value_t values[3];
2✔
1885

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

1✔
1889
            CHECK(values[0].type == RLM_TYPE_INT);
2!
1890
            CHECK(values[1].type == RLM_TYPE_STRING);
2!
1891
            CHECK(values[2].type == RLM_TYPE_INT);
2!
1892

1✔
1893
            CHECK(values[0].integer == 123);
2!
1894
            CHECK(strncmp(values[1].string.data, "Hello, World!", values[1].string.size) == 0);
2!
1895
            CHECK(values[2].integer == 123);
2!
1896

1✔
1897
            realm_property_key_t keys2[3] = {foo_int_key, 123123123, foo_str_key};
2✔
1898
            CHECK(!realm_get_values(obj1.get(), 3, keys2, values));
2!
1899
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
1900

1✔
1901
            write([&]() {
2✔
1902
                CHECK(checked(realm_object_delete(obj1.get())));
2!
1903
            });
2✔
1904
            CHECK(!realm_get_values(obj1.get(), 3, keys1, values));
2!
1905
            CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
1906
        }
2✔
1907

99✔
1908
        SECTION("realm_set_value() errors") {
198✔
1909
            CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_int_val(456), false));
2!
1910
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
1911

1✔
1912
            write([&]() {
2✔
1913
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_null(), false));
2!
1914
                CHECK_ERR(RLM_ERR_PROPERTY_NOT_NULLABLE);
2!
1915

1✔
1916
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_str_val("a"), false));
2!
1917
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
1918

1✔
1919
                CHECK(!realm_set_value(obj1.get(), 123123123, rlm_int_val(123), false));
2!
1920
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
1921
            });
2✔
1922
        }
2✔
1923

99✔
1924
        SECTION("realm_set_values() errors") {
198✔
1925
            realm_value_t int456 = rlm_int_val(456);
2✔
1926
            CHECK(!realm_set_values(obj1.get(), 1, &foo_int_key, &int456, false));
2!
1927
            CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
1928

1✔
1929
            write([&]() {
2✔
1930
                realm_value_t value;
2✔
1931
                realm_property_key_t keys1[3] = {foo_int_key, foo_str_key, foo_int_key};
2✔
1932
                realm_property_key_t keys2[3] = {foo_int_key, 123123123, foo_str_key};
2✔
1933

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

1✔
1939
                realm_get_value(obj1.get(), foo_int_key, &value);
2✔
1940
                CHECK(value.type == RLM_TYPE_INT);
2!
1941
                CHECK(value.integer == 345);
2!
1942
                realm_get_value(obj1.get(), foo_str_key, &value);
2✔
1943
                CHECK(value.type == RLM_TYPE_STRING);
2!
1944
                CHECK(strncmp("aaa", value.string.data, value.string.size) == 0);
2!
1945

1✔
1946
                // Type mismatch error.
1✔
1947
                realm_value_t values2[3] = {rlm_int_val(111), rlm_str_val("bbb"), rlm_str_val("ccc")};
2✔
1948
                CHECK(!realm_set_values(obj1.get(), 3, keys1, values2, false));
2!
1949
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
1950
                // Properties should remain unchanged.
1✔
1951
                realm_get_value(obj1.get(), foo_int_key, &value);
2✔
1952
                CHECK(value.type == RLM_TYPE_INT);
2!
1953
                CHECK(value.integer == 345);
2!
1954
                realm_get_value(obj1.get(), foo_str_key, &value);
2✔
1955
                CHECK(value.type == RLM_TYPE_STRING);
2!
1956

1✔
1957
                // Invalid property key error.
1✔
1958
                CHECK(!realm_set_values(obj1.get(), 3, keys2, values2, false));
2!
1959
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
1960
                // Properties should remain unchanged.
1✔
1961
                realm_get_value(obj1.get(), foo_int_key, &value);
2✔
1962
                CHECK(value.type == RLM_TYPE_INT);
2!
1963
                CHECK(value.integer == 345);
2!
1964
                realm_get_value(obj1.get(), foo_str_key, &value);
2✔
1965
                CHECK(value.type == RLM_TYPE_STRING);
2!
1966
            });
2✔
1967
        }
2✔
1968

99✔
1969
        SECTION("realm_object_add_int errors") {
198✔
1970
            SECTION("SUCCESS") {
4✔
1971
                realm_begin_write(realm);
2✔
1972
                CHECK(realm_object_add_int(obj1.get(), foo_int_key, 10));
2!
1973
                realm_commit(realm);
2✔
1974
            }
2✔
1975
            SECTION("ERROR") {
4✔
1976
                CHECK(!realm_object_add_int(obj1.get(), foo_int_key, 10));
2!
1977
                CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
1978
            }
2✔
1979
        }
4✔
1980

99✔
1981
        SECTION("get/set all property types") {
198✔
1982
            realm_value_t null = rlm_null();
2✔
1983
            realm_value_t integer = rlm_int_val(987);
2✔
1984
            realm_value_t boolean = rlm_bool_val(true);
2✔
1985
            realm_value_t string = rlm_str_val("My string");
2✔
1986
            const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
2✔
1987
            realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
2✔
1988
            realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
2✔
1989
            realm_value_t fnum = rlm_float_val(123.f);
2✔
1990
            realm_value_t dnum = rlm_double_val(456.0);
2✔
1991
            realm_value_t decimal = rlm_decimal_val(999.0);
2✔
1992
            realm_value_t object_id = rlm_object_id_val("abc123abc123");
2✔
1993
            realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
2✔
1994
            realm_value_t link = rlm_link_val(class_bar.key, realm_object_get_key(obj2.get()));
2✔
1995

1✔
1996
            write([&]() {
2✔
1997
                CHECK(realm_set_value(obj1.get(), foo_properties["int"], integer, false));
2!
1998
                CHECK(realm_set_value(obj1.get(), foo_properties["bool"], boolean, false));
2!
1999
                CHECK(realm_set_value(obj1.get(), foo_properties["string"], string, false));
2!
2000
                CHECK(realm_set_value(obj1.get(), foo_properties["binary"], binary, false));
2!
2001
                CHECK(realm_set_value(obj1.get(), foo_properties["timestamp"], timestamp, false));
2!
2002
                CHECK(realm_set_value(obj1.get(), foo_properties["float"], fnum, false));
2!
2003
                CHECK(realm_set_value(obj1.get(), foo_properties["double"], dnum, false));
2!
2004
                CHECK(realm_set_value(obj1.get(), foo_properties["decimal"], decimal, false));
2!
2005
                CHECK(realm_set_value(obj1.get(), foo_properties["object_id"], object_id, false));
2!
2006
                CHECK(realm_set_value(obj1.get(), foo_properties["uuid"], uuid, false));
2!
2007
                CHECK(realm_set_value(obj1.get(), foo_properties["mixed"], integer, false));
2!
2008

1✔
2009
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_int"], integer, false));
2!
2010
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_bool"], boolean, false));
2!
2011
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_string"], string, false));
2!
2012
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_binary"], binary, false));
2!
2013
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_timestamp"], timestamp, false));
2!
2014
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_float"], fnum, false));
2!
2015
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_double"], dnum, false));
2!
2016
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_decimal"], decimal, false));
2!
2017
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_object_id"], object_id, false));
2!
2018
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_uuid"], uuid, false));
2!
2019

1✔
2020
                CHECK(realm_set_value(obj1.get(), foo_properties["link"], link, false));
2!
2021
            });
2✔
2022

1✔
2023
            realm_value_t value;
2✔
2024

1✔
2025
            CHECK(realm_get_value(obj1.get(), foo_properties["int"], &value));
2!
2026
            CHECK(rlm_val_eq(value, integer));
2!
2027
            CHECK(realm_get_value(obj1.get(), foo_properties["bool"], &value));
2!
2028
            CHECK(rlm_val_eq(value, boolean));
2!
2029
            CHECK(realm_get_value(obj1.get(), foo_properties["string"], &value));
2!
2030
            CHECK(rlm_val_eq(value, string));
2!
2031
            CHECK(realm_get_value(obj1.get(), foo_properties["binary"], &value));
2!
2032
            CHECK(rlm_val_eq(value, binary));
2!
2033
            CHECK(realm_get_value(obj1.get(), foo_properties["timestamp"], &value));
2!
2034
            CHECK(rlm_val_eq(value, timestamp));
2!
2035
            CHECK(realm_get_value(obj1.get(), foo_properties["float"], &value));
2!
2036
            CHECK(rlm_val_eq(value, fnum));
2!
2037
            CHECK(realm_get_value(obj1.get(), foo_properties["double"], &value));
2!
2038
            CHECK(rlm_val_eq(value, dnum));
2!
2039
            CHECK(realm_get_value(obj1.get(), foo_properties["decimal"], &value));
2!
2040
            CHECK(rlm_val_eq(value, decimal));
2!
2041
            CHECK(realm_get_value(obj1.get(), foo_properties["object_id"], &value));
2!
2042
            CHECK(rlm_val_eq(value, object_id));
2!
2043
            CHECK(realm_get_value(obj1.get(), foo_properties["uuid"], &value));
2!
2044
            CHECK(rlm_val_eq(value, uuid));
2!
2045
            CHECK(realm_get_value(obj1.get(), foo_properties["mixed"], &value));
2!
2046
            CHECK(rlm_val_eq(value, integer));
2!
2047
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_int"], &value));
2!
2048
            CHECK(rlm_val_eq(value, integer));
2!
2049
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_bool"], &value));
2!
2050
            CHECK(rlm_val_eq(value, boolean));
2!
2051
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_string"], &value));
2!
2052
            CHECK(rlm_val_eq(value, string));
2!
2053
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_binary"], &value));
2!
2054
            CHECK(rlm_val_eq(value, binary));
2!
2055
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_timestamp"], &value));
2!
2056
            CHECK(rlm_val_eq(value, timestamp));
2!
2057
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_float"], &value));
2!
2058
            CHECK(rlm_val_eq(value, fnum));
2!
2059
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_double"], &value));
2!
2060
            CHECK(rlm_val_eq(value, dnum));
2!
2061
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_decimal"], &value));
2!
2062
            CHECK(rlm_val_eq(value, decimal));
2!
2063
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_object_id"], &value));
2!
2064
            CHECK(rlm_val_eq(value, object_id));
2!
2065
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_uuid"], &value));
2!
2066
            CHECK(rlm_val_eq(value, uuid));
2!
2067
            CHECK(realm_get_value(obj1.get(), foo_properties["link"], &value));
2!
2068
            CHECK(rlm_val_eq(value, link));
2!
2069

1✔
2070
            write([&]() {
2✔
2071
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_int"], null, false));
2!
2072
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_bool"], null, false));
2!
2073
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_string"], null, false));
2!
2074
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_binary"], null, false));
2!
2075
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_timestamp"], null, false));
2!
2076
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_float"], null, false));
2!
2077
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_double"], null, false));
2!
2078
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_decimal"], null, false));
2!
2079
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_object_id"], null, false));
2!
2080
                CHECK(realm_set_value(obj1.get(), foo_properties["nullable_uuid"], null, false));
2!
2081
                CHECK(realm_set_value(obj1.get(), foo_properties["mixed"], null, false));
2!
2082
                CHECK(realm_set_value(obj1.get(), foo_properties["link"], null, false));
2!
2083
            });
2✔
2084

1✔
2085
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_int"], &value));
2!
2086
            CHECK(rlm_val_eq(value, null));
2!
2087
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_bool"], &value));
2!
2088
            CHECK(rlm_val_eq(value, null));
2!
2089
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_string"], &value));
2!
2090
            CHECK(rlm_val_eq(value, null));
2!
2091
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_binary"], &value));
2!
2092
            CHECK(rlm_val_eq(value, null));
2!
2093
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_timestamp"], &value));
2!
2094
            CHECK(rlm_val_eq(value, null));
2!
2095
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_float"], &value));
2!
2096
            CHECK(rlm_val_eq(value, null));
2!
2097
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_double"], &value));
2!
2098
            CHECK(rlm_val_eq(value, null));
2!
2099
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_decimal"], &value));
2!
2100
            CHECK(rlm_val_eq(value, null));
2!
2101
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_object_id"], &value));
2!
2102
            CHECK(rlm_val_eq(value, null));
2!
2103
            CHECK(realm_get_value(obj1.get(), foo_properties["nullable_uuid"], &value));
2!
2104
            CHECK(rlm_val_eq(value, null));
2!
2105
            CHECK(realm_get_value(obj1.get(), foo_properties["mixed"], &value));
2!
2106
            CHECK(rlm_val_eq(value, null));
2!
2107
            CHECK(realm_get_value(obj1.get(), foo_properties["link"], &value));
2!
2108
            CHECK(rlm_val_eq(value, null));
2!
2109
        }
2✔
2110

99✔
2111
        SECTION("embedded") {
198✔
2112
            realm_property_info_t info;
2✔
2113
            bool found = false;
2✔
2114
            REQUIRE(checked(realm_find_property(realm, class_bar.key, "sub", &found, &info)));
2!
2115
            REQUIRE(found);
2!
2116

1✔
2117
            auto embedded = cptr_checked(realm_get_linked_object(obj2.get(), info.key));
2✔
2118
            CHECK(!embedded);
2!
2119
            write([&]() {
2✔
2120
                auto embedded = cptr_checked(realm_set_embedded(obj2.get(), info.key));
2✔
2121
                CHECK(embedded);
2!
2122
            });
2✔
2123
            embedded = cptr_checked(realm_get_linked_object(obj2.get(), info.key));
2✔
2124
            CHECK(embedded);
2!
2125
        }
2✔
2126

99✔
2127

99✔
2128
        SECTION("find with primary key") {
198✔
2129
            bool found = false;
2✔
2130

1✔
2131
            auto p = cptr_checked(realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(1), &found));
2✔
2132
            CHECK(found);
2!
2133
            auto p_key = realm_object_get_key(p.get());
2✔
2134
            auto obj2_key = realm_object_get_key(obj2.get());
2✔
2135
            CHECK(p_key == obj2_key);
2!
2136
            CHECK(realm_equals(p.get(), obj2.get()));
2!
2137

1✔
2138
            CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(2), &found));
2!
2139
            CHECK(!found);
2!
2140
            CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(2), nullptr));
2!
2141

1✔
2142
            // Check that finding by type-mismatched values just find nothing.
1✔
2143
            CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_null(), &found));
2!
2144
            CHECK(!found);
2!
2145
            CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_str_val("a"), &found));
2!
2146
            CHECK(!found);
2!
2147
            CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_str_val("a"), nullptr));
2!
2148

1✔
2149
            // Invalid class key
1✔
2150
            CHECK(!realm_object_find_with_primary_key(realm, 123123123, rlm_int_val(1), nullptr));
2!
2151
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
2152
        }
2✔
2153

99✔
2154
        SECTION("find all") {
198✔
2155
            auto r = cptr_checked(realm_object_find_all(realm, class_bar.key));
2✔
2156
            size_t count;
2✔
2157
            realm_results_count(r.get(), &count);
2✔
2158
            CHECK(count == 1);
2!
2159
        }
2✔
2160

99✔
2161
        SECTION("query") {
198✔
2162
            realm_value_t arg_data[1] = {rlm_str_val("Hello, World!")};
72✔
2163
            size_t num_args = 2;
72✔
2164
            realm_query_arg_t args[2] = {realm_query_arg_t{1, false, &arg_data[0]},
72✔
2165
                                         realm_query_arg_t{1, false, &int_val2}};
72✔
2166
            realm_query_arg_t* arg_list = &args[0];
72✔
2167
            auto q = cptr_checked(
72✔
2168
                realm_query_parse(realm, class_foo.key, "string == $0 SORT(int ASCENDING)", 1, arg_list));
72✔
2169

36✔
2170
            SECTION("realm_query_description()") {
72✔
2171
                const char* descr = realm_query_get_description(q.get());
2✔
2172
                std::string expected = "string == \"Hello, World!\" SORT(int ASC)";
2✔
2173
                CHECK(descr == expected);
2!
2174
            }
2✔
2175

36✔
2176
            SECTION("realm_query_count()") {
72✔
2177
                size_t count;
4✔
2178
                CHECK(checked(realm_query_count(q.get(), &count)));
4!
2179
                CHECK(count == 1);
4!
2180

2✔
2181
                SECTION("cloned") {
4✔
2182
                    auto q2 = clone_cptr(q.get());
2✔
2183
                    size_t count2;
2✔
2184
                    CHECK(checked(realm_query_count(q2.get(), &count2)));
2!
2185
                    CHECK(count == count2);
2!
2186
                }
2✔
2187
                SECTION("realm_query_append_query") {
4✔
2188
                    auto q2 =
2✔
2189
                        cptr_checked(realm_query_append_query(q.get(), "TRUEPREDICATE LIMIT(1)", num_args, arg_list));
2✔
2190
                    size_t count;
2✔
2191
                    CHECK(checked(realm_query_count(q2.get(), &count)));
2!
2192
                    CHECK(count == 1);
2!
2193
                    q2 = cptr_checked(realm_query_append_query(q.get(), "FALSEPREDICATE", num_args, arg_list));
2✔
2194
                    CHECK(checked(realm_query_count(q2.get(), &count)));
2!
2195
                    CHECK(count == 0);
2!
2196
                    q2 =
2✔
2197
                        cptr_checked(realm_query_append_query(q.get(), "TRUEPREDICATE LIMIT(0)", num_args, arg_list));
2✔
2198
                    CHECK(checked(realm_query_count(q2.get(), &count)));
2!
2199
                    CHECK(count == 0);
2!
2200
                    q2 = cptr_checked(
2✔
2201
                        realm_query_append_query(q.get(), "TRUEPREDICATE LIMIT(10)", num_args, arg_list));
2✔
2202
                    CHECK(checked(realm_query_count(q2.get(), &count)));
2!
2203
                    CHECK(count == 1);
2!
2204
                    q2 = cptr_checked(realm_query_append_query(q.get(), "int == $1", num_args, arg_list));
2✔
2205
                    CHECK(checked(realm_query_count(q2.get(), &count)));
2!
2206
                    CHECK(count == 0);
2!
2207
                }
2✔
2208
            }
4✔
2209

36✔
2210
            SECTION("realm_query_parse() errors") {
72✔
2211
                // Invalid class key
1✔
2212
                CHECK(!realm_query_parse(realm, 123123123, "string == $0", num_args, arg_list));
2!
2213
                CHECK_ERR_CAT(RLM_ERR_NO_SUCH_TABLE, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
2!
2214

1✔
2215
                // Invalid syntax
1✔
2216
                CHECK(!realm_query_parse(realm, class_foo.key, "lel", 0, nullptr));
2!
2217
                CHECK_ERR_CAT(RLM_ERR_INVALID_QUERY_STRING, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
2!
2218

1✔
2219
                // Invalid property
1✔
2220
                CHECK(!realm_query_parse(realm, class_foo.key, "strong = 5", 0, nullptr));
2!
2221
                CHECK_ERR_CAT(RLM_ERR_INVALID_QUERY, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
2!
2222

1✔
2223
                // Invalid number of arguments
1✔
2224
                CHECK(!realm_query_parse(realm, class_foo.key, "string == $0", 0, nullptr));
2!
2225
                CHECK_ERR_CAT(RLM_ERR_INDEX_OUT_OF_BOUNDS, (RLM_ERR_CAT_INVALID_ARG | RLM_ERR_CAT_LOGIC));
2!
2226
            }
2✔
2227

36✔
2228
            SECTION("string in list") {
72✔
2229
                char foo[] = "foo";
2✔
2230
                realm_value_t str = rlm_str_val(foo);
2✔
2231
                realm_value_t list_arg[2] = {str, rlm_str_val("bar")};
2✔
2232

1✔
2233
                write([&]() {
2✔
2234
                    CHECK(realm_set_value(obj1.get(), foo_properties["string"], rlm_str_val("foo"), false));
2!
2235
                });
2✔
2236

1✔
2237
                static const size_t num_args = 1;
2✔
2238
                realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &str}};
2✔
2239
                realm_query_arg_t* arg_list_simple = &args[0];
2✔
2240

1✔
2241
                realm_query_arg_t args_in_list[num_args] = {realm_query_arg_t{2, true, &list_arg[0]}};
2✔
2242
                realm_query_arg_t* arg_list = &args_in_list[0];
2✔
2243

1✔
2244
                auto q_string_single_param =
2✔
2245
                    cptr_checked(realm_query_parse(realm, class_foo.key, "string == $0", num_args, arg_list_simple));
2✔
2246
                auto q_string_in_list =
2✔
2247
                    cptr_checked(realm_query_parse(realm, class_foo.key, "string IN $0", num_args, arg_list));
2✔
2248

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

1✔
2257
                CHECK(checked(realm_query_count(q_string_single_param.get(), &count)));
2!
2258
                CHECK(1 == count);
2!
2259
                CHECK(checked(realm_query_count(q_string_in_list.get(), &count_list)));
2!
2260
                CHECK(1 == count_list);
2!
2261
            }
2✔
2262

36✔
2263
            SECTION("link in list") {
72✔
2264
                auto link = rlm_link_val(class_bar.key, realm_object_get_key(obj2.get()));
2✔
2265
                realm_value_t link_value = link;
2✔
2266
                write([&]() {
2✔
2267
                    CHECK(realm_set_value(obj1.get(), foo_properties["link"], link_value, false));
2!
2268
                });
2✔
2269

1✔
2270
                static const size_t num_args = 1;
2✔
2271
                realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &link_value}};
2✔
2272
                realm_query_arg_t* arg = &args[0];
2✔
2273

1✔
2274
                realm_value_t list_arg[num_args] = {link_value};
2✔
2275
                realm_query_arg_t args_in_list[num_args] = {realm_query_arg_t{num_args, true, &list_arg[0]}};
2✔
2276
                realm_query_arg_t* arg_list = &args_in_list[0];
2✔
2277

1✔
2278
                auto q_link_single_param =
2✔
2279
                    cptr_checked(realm_query_parse(realm, class_foo.key, "link == $0", num_args, arg));
2✔
2280
                auto q_link_in_list =
2✔
2281
                    cptr_checked(realm_query_parse(realm, class_foo.key, "link IN $0", num_args, arg_list));
2✔
2282

1✔
2283
                size_t count, count_list;
2✔
2284

1✔
2285
                // change the link
1✔
2286
                link = rlm_null();
2✔
2287

1✔
2288
                CHECK(checked(realm_query_count(q_link_single_param.get(), &count)));
2!
2289
                CHECK(1 == count);
2!
2290
                CHECK(checked(realm_query_count(q_link_in_list.get(), &count_list)));
2!
2291
                CHECK(1 == count_list);
2!
2292
            }
2✔
2293

36✔
2294
            SECTION("decimal NaN") {
72✔
2295
                realm_value_t decimal = rlm_decimal_nan();
2✔
2296

1✔
2297
                write([&]() {
2✔
2298
                    CHECK(realm_set_value(obj1.get(), foo_properties["decimal"], decimal, false));
2!
2299
                });
2✔
2300
                realm_query_arg_t args[] = {realm_query_arg_t{1, false, &decimal}};
2✔
2301
                auto q_decimal = cptr_checked(realm_query_parse(realm, class_foo.key, "decimal == $0", 1, args));
2✔
2302
                realm_value_t out_value;
2✔
2303
                bool out_found;
2✔
2304
                CHECK(realm_query_find_first(q_decimal.get(), &out_value, &out_found));
2!
2305
                CHECK(out_found);
2!
2306
                auto link = obj1->get_obj().get_link();
2✔
2307
                realm_value_t expected;
2✔
2308
                expected.type = RLM_TYPE_LINK;
2✔
2309
                expected.link.target_table = link.get_table_key().value;
2✔
2310
                expected.link.target = link.get_obj_key().value;
2✔
2311
                CHECK(rlm_val_eq(out_value, expected));
2!
2312
            }
2✔
2313

36✔
2314
            SECTION("interpolate all types") {
72✔
2315
                realm_value_t int_arg = rlm_int_val(123);
2✔
2316
                realm_value_t bool_arg = rlm_bool_val(true);
2✔
2317
                realm_value_t string_arg = rlm_str_val("foobar");
2✔
2318
                static const uint8_t binary_data[3] = {1, 2, 3};
2✔
2319
                realm_value_t binary_arg = rlm_binary_val(binary_data, 3);
2✔
2320
                realm_value_t timestamp_arg = rlm_timestamp_val(1000000, 1);
2✔
2321
                realm_value_t float_arg = rlm_float_val(123.f);
2✔
2322
                realm_value_t double_arg = rlm_double_val(456.0);
2✔
2323
                realm_value_t decimal_arg = rlm_decimal_val(789.0);
2✔
2324
                realm_value_t object_id_arg = rlm_object_id_val("abc123abc123");
2✔
2325
                realm_value_t uuid_arg = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
2✔
2326
                realm_value_t link_arg = rlm_link_val(class_bar.key, realm_object_get_key(obj2.get()));
2✔
2327
                realm_value_t list_arg[3] = {rlm_int_val(456), rlm_str_val("lol"), rlm_double_val(3.14)};
2✔
2328

1✔
2329
                static const size_t num_args = 13;
2✔
2330
                realm_query_arg_t args[num_args] = {
2✔
2331
                    realm_query_arg_t{1, false, &int_arg},       realm_query_arg_t{1, false, &bool_arg},
2✔
2332
                    realm_query_arg_t{1, false, &string_arg},    realm_query_arg_t{1, false, &binary_arg},
2✔
2333
                    realm_query_arg_t{1, false, &timestamp_arg}, realm_query_arg_t{1, false, &float_arg},
2✔
2334
                    realm_query_arg_t{1, false, &double_arg},    realm_query_arg_t{1, false, &decimal_arg},
2✔
2335
                    realm_query_arg_t{1, false, &object_id_arg}, realm_query_arg_t{1, false, &uuid_arg},
2✔
2336
                    realm_query_arg_t{1, false, &link_arg},      realm_query_arg_t{3, true, &list_arg[0]},
2✔
2337
                    realm_query_arg_t{0, true, nullptr}};
2✔
2338
                realm_query_arg_t* arg_list = &args[0];
2✔
2339

1✔
2340
                auto q_int = cptr_checked(realm_query_parse(realm, class_foo.key, "int == $0", num_args, arg_list));
2✔
2341
                auto q_bool = cptr_checked(realm_query_parse(realm, class_foo.key, "bool == $1", num_args, arg_list));
2✔
2342
                auto q_string =
2✔
2343
                    cptr_checked(realm_query_parse(realm, class_foo.key, "string == $2", num_args, arg_list));
2✔
2344
                auto q_binary =
2✔
2345
                    cptr_checked(realm_query_parse(realm, class_foo.key, "binary == $3", num_args, arg_list));
2✔
2346
                auto q_timestamp =
2✔
2347
                    cptr_checked(realm_query_parse(realm, class_foo.key, "timestamp == $4", num_args, arg_list));
2✔
2348
                auto q_float =
2✔
2349
                    cptr_checked(realm_query_parse(realm, class_foo.key, "float == $5", num_args, arg_list));
2✔
2350
                auto q_double =
2✔
2351
                    cptr_checked(realm_query_parse(realm, class_foo.key, "double == $6", num_args, arg_list));
2✔
2352
                auto q_decimal =
2✔
2353
                    cptr_checked(realm_query_parse(realm, class_foo.key, "decimal == $7", num_args, arg_list));
2✔
2354
                auto q_object_id =
2✔
2355
                    cptr_checked(realm_query_parse(realm, class_foo.key, "object_id == $8", num_args, arg_list));
2✔
2356
                auto q_uuid = cptr_checked(realm_query_parse(realm, class_foo.key, "uuid == $9", num_args, arg_list));
2✔
2357
                auto q_link =
2✔
2358
                    cptr_checked(realm_query_parse(realm, class_foo.key, "link == $10", num_args, arg_list));
2✔
2359
                auto q_list =
2✔
2360
                    cptr_checked(realm_query_parse(realm, class_foo.key, "int == ANY $11", num_args, arg_list));
2✔
2361
                auto q_empty_list =
2✔
2362
                    cptr_checked(realm_query_parse(realm, class_foo.key, "int == ALL $12", num_args, arg_list));
2✔
2363

1✔
2364
                CHECK(cptr_checked(realm_query_find_all(q_int.get())));
2!
2365
                CHECK(cptr_checked(realm_query_find_all(q_bool.get())));
2!
2366
                CHECK(cptr_checked(realm_query_find_all(q_string.get())));
2!
2367
                CHECK(cptr_checked(realm_query_find_all(q_binary.get())));
2!
2368
                CHECK(cptr_checked(realm_query_find_all(q_timestamp.get())));
2!
2369
                CHECK(cptr_checked(realm_query_find_all(q_float.get())));
2!
2370
                CHECK(cptr_checked(realm_query_find_all(q_double.get())));
2!
2371
                CHECK(cptr_checked(realm_query_find_all(q_decimal.get())));
2!
2372
                CHECK(cptr_checked(realm_query_find_all(q_object_id.get())));
2!
2373
                CHECK(cptr_checked(realm_query_find_all(q_uuid.get())));
2!
2374
                CHECK(cptr_checked(realm_query_find_all(q_link.get())));
2!
2375
                CHECK(cptr_checked(realm_query_find_all(q_list.get())));
2!
2376
                CHECK(cptr_checked(realm_query_find_all(q_empty_list.get())));
2!
2377

1✔
2378
                SECTION("type mismatch") {
2✔
2379
                    CHECK(!realm_query_parse(realm, class_foo.key, "int == $2", num_args, arg_list));
2!
2380
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2381
                    CHECK(!realm_query_parse(realm, class_foo.key, "bool == $2", num_args, arg_list));
2!
2382
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2383
                    CHECK(!realm_query_parse(realm, class_foo.key, "string == $7", num_args, arg_list));
2!
2384
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2385
                    CHECK(!realm_query_parse(realm, class_foo.key, "timestamp == $2", num_args, arg_list));
2!
2386
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2387
                    CHECK(!realm_query_parse(realm, class_foo.key, "double == $2", num_args, arg_list));
2!
2388
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2389
                    CHECK(!realm_query_parse(realm, class_foo.key, "float == $2", num_args, arg_list));
2!
2390
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2391
                    CHECK(!realm_query_parse(realm, class_foo.key, "binary == $0", num_args, arg_list));
2!
2392
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2393
                    CHECK(!realm_query_parse(realm, class_foo.key, "decimal == $2", num_args, arg_list));
2!
2394
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2395
                    CHECK(!realm_query_parse(realm, class_foo.key, "object_id == $2", num_args, arg_list));
2!
2396
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2397
                    CHECK(!realm_query_parse(realm, class_foo.key, "uuid == $2", num_args, arg_list));
2!
2398
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2399
                    CHECK(!realm_query_parse(realm, class_foo.key, "link == $2", num_args, arg_list));
2!
2400
                    CHECK_ERR(RLM_ERR_INVALID_QUERY);
2!
2401
                }
2✔
2402
            }
2✔
2403

36✔
2404
            SECTION("realm_query_find_first()") {
72✔
2405
                realm_value_t found_value = rlm_null();
2✔
2406
                bool found;
2✔
2407
                CHECK(checked(realm_query_find_first(q.get(), &found_value, &found)));
2!
2408
                CHECK(found);
2!
2409
                CHECK(found_value.type == RLM_TYPE_LINK);
2!
2410
                CHECK(found_value.link.target_table == class_foo.key);
2!
2411
                CHECK(found_value.link.target == realm_object_get_key(obj1.get()));
2!
2412
            }
2✔
2413

36✔
2414
            SECTION("verify order realm_query_find_first()") {
72✔
2415
                realm_property_info_t info_string;
2✔
2416
                realm_property_info_t info_int;
2✔
2417
                bool found_string = false, found_int = false;
2✔
2418
                REQUIRE(realm_find_property(realm, class_foo.key, "string", &found_string, &info_string));
2!
2419
                REQUIRE(realm_find_property(realm, class_foo.key, "int", &found_int, &info_int));
2!
2420
                CHECK(info_string.key != RLM_INVALID_PROPERTY_KEY);
2!
2421
                CHECK(info_int.key != RLM_INVALID_PROPERTY_KEY);
2!
2422
                CPtr<realm_object_t> obj1, obj2;
2✔
2423
                checked(realm_begin_write(realm));
2✔
2424
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
2425
                obj2 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
2426
                CHECK(obj1);
2!
2427
                CHECK(obj2);
2!
2428
                CHECK(checked(realm_set_value(obj1.get(), info_string.key, rlm_str_val("Test"), false)));
2!
2429
                CHECK(checked(realm_set_value(obj2.get(), info_string.key, rlm_str_val("Test"), false)));
2!
2430
                CHECK(checked(realm_set_value(obj1.get(), info_int.key, rlm_int_val(10), false)));
2!
2431
                CHECK(checked(realm_set_value(obj2.get(), info_int.key, rlm_int_val(11), false)));
2!
2432
                checked(realm_commit(realm));
2✔
2433
                checked(realm_refresh(realm, nullptr));
2✔
2434

1✔
2435
                size_t count = 0;
2✔
2436
                realm_value_t arg_data[1] = {rlm_str_val("Test")};
2✔
2437
                realm_query_arg_t args[1] = {realm_query_arg_t{1, false, &arg_data[0]}};
2✔
2438
                realm_query_arg_t* arg_list = &args[0];
2✔
2439
                auto q = cptr_checked(realm_query_parse(realm, class_foo.key, "string == $0", 1, arg_list));
2✔
2440
                CHECK(checked(realm_query_count(q.get(), &count)));
2!
2441
                CHECK(count == 2);
2!
2442

1✔
2443
                auto q2 =
2✔
2444
                    cptr_checked(realm_query_append_query(q.get(), "string == $0 SORT(int ASCENDING)", 1, arg_list));
2✔
2445
                realm_value_t found_value = rlm_null();
2✔
2446
                bool found_sorted;
2✔
2447
                CHECK(checked(realm_query_find_first(q2.get(), &found_value, &found_sorted)));
2!
2448
                CHECK(found_sorted);
2!
2449
                CHECK(found_value.type == RLM_TYPE_LINK);
2!
2450
                CHECK(found_value.link.target_table == class_foo.key);
2!
2451
                CHECK(found_value.link.target == realm_object_get_key(obj1.get()));
2!
2452

1✔
2453
                auto q3 =
2✔
2454
                    cptr_checked(realm_query_append_query(q.get(), "string == $0 SORT(int DESCENDING)", 1, arg_list));
2✔
2455
                found_value = rlm_null();
2✔
2456
                found_sorted = false;
2✔
2457
                CHECK(checked(realm_query_find_first(q3.get(), &found_value, &found_sorted)));
2!
2458
                CHECK(found_sorted);
2!
2459
                CHECK(found_value.type == RLM_TYPE_LINK);
2!
2460
                CHECK(found_value.link.target_table == class_foo.key);
2!
2461
                CHECK(found_value.link.target == realm_object_get_key(obj2.get()));
2!
2462
            }
2✔
2463

36✔
2464
            SECTION("results") {
72✔
2465
                auto r = cptr_checked(realm_query_find_all(q.get()));
52✔
2466
                CHECK(!realm_is_frozen(r.get()));
52!
2467

26✔
2468
                SECTION("realm_results_is_valid") {
52✔
2469
                    bool valid;
2✔
2470
                    CHECK(checked(realm_results_is_valid(r.get(), &valid)));
2!
2471
                    CHECK(valid);
2!
2472
                }
2✔
2473

26✔
2474
                SECTION("realm_results_is_valid delete objects") {
52✔
2475
                    write([&] {
2✔
2476
                        realm_object_delete(obj1.get());
2✔
2477
                        realm_object_delete(obj2.get());
2✔
2478
                        realm_results_delete_all(r.get());
2✔
2479
                    });
2✔
2480
                    bool valid;
2✔
2481
                    CHECK(checked(realm_results_is_valid(r.get(), &valid)));
2!
2482
                    CHECK(valid);
2!
2483
                }
2✔
2484

26✔
2485
                SECTION("realm_results_is_valid delete collection") {
52✔
2486
                    auto strings = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
2✔
2487
                    CHECK(strings);
2!
2488
                    CHECK(!realm_is_frozen(strings.get()));
2!
2489

1✔
2490
                    realm_value_t a = rlm_str_val("a");
2✔
2491
                    realm_value_t b = rlm_str_val("b");
2✔
2492
                    realm_value_t c = rlm_null();
2✔
2493

1✔
2494
                    write([&] {
2✔
2495
                        CHECK(checked(realm_list_insert(strings.get(), 0, a)));
2!
2496
                        CHECK(checked(realm_list_insert(strings.get(), 1, b)));
2!
2497
                        CHECK(checked(realm_list_insert(strings.get(), 2, c)));
2!
2498
                    });
2✔
2499
                    bool valid;
2✔
2500
                    auto results = cptr_checked(realm_list_to_results(strings.get()));
2✔
2501
                    CHECK(checked(realm_results_is_valid(results.get(), &valid)));
2!
2502
                    CHECK(valid);
2!
2503

1✔
2504
                    write([&] {
2✔
2505
                        CHECK(checked(realm_object_delete(obj2.get())));
2!
2506
                    });
2✔
2507

1✔
2508
                    CHECK(checked(realm_results_is_valid(results.get(), &valid)));
2!
2509
                    CHECK_FALSE(valid);
2!
2510
                    size_t count;
2✔
2511

1✔
2512
                    CHECK_FALSE(realm_results_count(results.get(), &count));
2!
2513
                    CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
2!
2514

1✔
2515
                    CHECK_FALSE(realm_results_resolve_in(results.get(), realm));
2!
2516
                    CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
2!
2517
                }
2✔
2518

26✔
2519
                SECTION("realm_results_count()") {
52✔
2520
                    size_t count;
2✔
2521
                    CHECK(checked(realm_results_count(r.get(), &count)));
2!
2522
                    CHECK(count == 1);
2!
2523

1✔
2524
                    SECTION("cloned") {
2✔
2525
                        auto r2 = clone_cptr(r.get());
2✔
2526
                        size_t count2;
2✔
2527
                        CHECK(checked(realm_results_count(r2.get(), &count2)));
2!
2528
                        CHECK(count == count2);
2!
2529
                    }
2✔
2530
                }
2✔
2531

26✔
2532
                SECTION("empty result") {
52✔
2533
                    auto q2 =
2✔
2534
                        cptr_checked(realm_query_parse(realm, class_foo.key, "string == 'boogeyman'", 0, nullptr));
2✔
2535
                    auto r2 = cptr_checked(realm_query_find_all(q2.get()));
2✔
2536
                    size_t count;
2✔
2537
                    CHECK(checked(realm_results_count(r2.get(), &count)));
2!
2538
                    CHECK(count == 0);
2!
2539
                    realm_value_t value = rlm_null();
2✔
2540
                    CHECK(!realm_results_get(r2.get(), 0, &value));
2!
2541
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
2!
2542
                    size_t index = -1;
2✔
2543
                    bool found = false;
2✔
2544
                    CHECK(realm_results_find(r2.get(), &value, &index, &found));
2!
2545
                    CHECK(index == realm::not_found);
2!
2546
                    CHECK(found == false);
2!
2547
                }
2✔
2548

26✔
2549
                SECTION("realm_results_get()") {
52✔
2550
                    realm_value_t value = rlm_null();
2✔
2551
                    CHECK(checked(realm_results_get(r.get(), 0, &value)));
2!
2552
                    CHECK(value.type == RLM_TYPE_LINK);
2!
2553
                    CHECK(value.link.target_table == class_foo.key);
2!
2554
                    CHECK(value.link.target == realm_object_get_key(obj1.get()));
2!
2555
                    size_t index = -1;
2✔
2556
                    bool found = false;
2✔
2557
                    CHECK(realm_results_find(r.get(), &value, &index, &found));
2!
2558
                    CHECK(index == 0);
2!
2559
                    CHECK(found == true);
2!
2560

1✔
2561
                    value = rlm_null();
2✔
2562
                    CHECK(!realm_results_get(r.get(), 1, &value));
2!
2563
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
2!
2564
                    index = -1;
2✔
2565
                    found = false;
2✔
2566
                    CHECK(realm_results_find(r.get(), &value, &index, &found));
2!
2567
                    CHECK(index == realm::not_found);
2!
2568
                    CHECK(found == false);
2!
2569
                }
2✔
2570

26✔
2571
                SECTION("realm_results_get_query()") {
52✔
2572
                    auto q2 = cptr_checked(realm_query_parse(realm, class_foo.key, "int == 123", 0, nullptr));
2✔
2573
                    auto r2 = cptr_checked(realm_results_filter(r.get(), q2.get()));
2✔
2574
                    size_t count;
2✔
2575
                    CHECK(checked(realm_results_count(r2.get(), &count)));
2!
2576
                    CHECK(count == 1);
2!
2577
                    auto results_query = cptr_checked(realm_results_get_query(r2.get()));
2✔
2578
                    auto result = cptr_checked(realm_query_find_all(results_query.get()));
2✔
2579
                    size_t count1 = 0;
2✔
2580
                    CHECK(checked(realm_results_count(result.get(), &count1)));
2!
2581
                    CHECK(count == count1);
2!
2582
                }
2✔
2583

26✔
2584
                SECTION("realm_results_get_object()") {
52✔
2585
                    auto p = cptr_checked(realm_results_get_object(r.get(), 0));
2✔
2586
                    CHECK(p.get());
2!
2587
                    CHECK(realm_equals(p.get(), obj1.get()));
2!
2588
                    size_t index = -1;
2✔
2589
                    bool found = false;
2✔
2590
                    CHECK(realm_results_find_object(r.get(), p.get(), &index, &found));
2!
2591
                    CHECK(found == true);
2!
2592
                    CHECK(index == 0);
2!
2593

1✔
2594
                    CHECK(!realm_results_get_object(r.get(), 1));
2!
2595
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
2!
2596
                    CHECK(!realm_results_find_object(r.get(), obj2.get(), &index, &found));
2!
2597
                    CHECK(found == false);
2!
2598
                    CHECK(index == realm::not_found);
2!
2599
                }
2✔
2600

26✔
2601
                SECTION("realm_results_filter()") {
52✔
2602
                    auto q2 = cptr_checked(realm_query_parse(realm, class_foo.key, "int == 789", 0, nullptr));
2✔
2603
                    auto r2 = cptr_checked(realm_results_filter(r.get(), q2.get()));
2✔
2604
                    size_t count;
2✔
2605
                    CHECK(checked(realm_results_count(r2.get(), &count)));
2!
2606
                    CHECK(count == 0);
2!
2607
                }
2✔
2608

26✔
2609
                SECTION("realm_results_sort()") {
52✔
2610
                    auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
2✔
2611
                    auto p = cptr_checked(realm_results_get_object(r_all.get(), 0));
2✔
2612
                    CHECK(p.get());
2!
2613
                    CHECK(realm_equals(p.get(), obj1.get()));
2!
2614
                    auto r2 = cptr_checked(realm_results_sort(r_all.get(), "int DESCENDING, float ASCENDING"));
2✔
2615
                    p = cptr_checked(realm_results_get_object(r2.get(), 1));
2✔
2616
                    CHECK(p.get());
2!
2617
                    CHECK(realm_equals(p.get(), obj1.get()));
2!
2618
                }
2✔
2619

26✔
2620
                SECTION("realm_results_distinct()") {
52✔
2621
                    auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
2✔
2622
                    size_t count;
2✔
2623
                    realm_results_count(r_all.get(), &count);
2✔
2624
                    CHECK(count == 3);
2!
2625
                    auto r2 = cptr_checked(realm_results_distinct(r_all.get(), "int"));
2✔
2626
                    realm_results_count(r2.get(), &count);
2✔
2627
                    CHECK(count == 2);
2!
2628
                }
2✔
2629

26✔
2630
                SECTION("realm_results_limit()") {
52✔
2631
                    auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
2✔
2632
                    size_t count;
2✔
2633
                    realm_results_count(r_all.get(), &count);
2✔
2634
                    CHECK(count == 3);
2!
2635
                    auto r2 = cptr_checked(realm_results_limit(r_all.get(), 1));
2✔
2636
                    realm_results_count(r2.get(), &count);
2✔
2637
                    CHECK(count == 1);
2!
2638
                }
2✔
2639

26✔
2640
                SECTION("realm_results_snapshot()") {
52✔
2641
                    auto r_all = cptr_checked(realm_object_find_all(realm, class_foo.key));
2✔
2642
                    auto r_snapshot = cptr_checked(realm_results_snapshot(r_all.get()));
2✔
2643
                    size_t count;
2✔
2644
                    realm_results_count(r_all.get(), &count);
2✔
2645
                    CHECK(count == 3);
2!
2646
                    realm_results_count(r_snapshot.get(), &count);
2✔
2647
                    CHECK(count == 3);
2!
2648
                    write([&]() {
2✔
2649
                        auto p = cptr_checked(realm_results_get_object(r_all.get(), 0));
2✔
2650
                        realm_object_delete(p.get());
2✔
2651
                    });
2✔
2652
                    realm_results_count(r_all.get(), &count);
2✔
2653
                    CHECK(count == 2);
2!
2654
                    realm_results_count(r_snapshot.get(), &count);
2✔
2655
                    CHECK(count == 3);
2!
2656
                }
2✔
2657

26✔
2658
                SECTION("realm_results_min()") {
52✔
2659
                    realm_value_t value = rlm_null();
2✔
2660
                    CHECK(checked(realm_results_min(r.get(), foo_int_key, &value, &found)));
2!
2661
                    CHECK(found);
2!
2662
                    CHECK(value.type == RLM_TYPE_INT);
2!
2663
                    CHECK(value.integer == 123);
2!
2664

1✔
2665
                    CHECK(!realm_results_min(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
2666
                    CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2667
                }
2✔
2668

26✔
2669
                SECTION("realm_results_max()") {
52✔
2670
                    realm_value_t value = rlm_null();
2✔
2671
                    CHECK(checked(realm_results_max(r.get(), foo_int_key, &value, &found)));
2!
2672
                    CHECK(found);
2!
2673
                    CHECK(value.type == RLM_TYPE_INT);
2!
2674
                    CHECK(value.integer == 123);
2!
2675

1✔
2676
                    CHECK(!realm_results_max(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
2677
                    CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2678
                }
2✔
2679

26✔
2680
                SECTION("realm_results_sum()") {
52✔
2681
                    realm_value_t value = rlm_null();
2✔
2682
                    CHECK(checked(realm_results_sum(r.get(), foo_int_key, &value, &found)));
2!
2683
                    CHECK(found);
2!
2684
                    CHECK(value.type == RLM_TYPE_INT);
2!
2685
                    CHECK(value.integer == 123);
2!
2686

1✔
2687
                    size_t count;
2✔
2688
                    realm_results_count(r.get(), &count);
2✔
2689
                    CHECK(count == 1);
2!
2690

1✔
2691
                    CHECK(!realm_results_sum(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
2692
                    CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2693
                }
2✔
2694

26✔
2695
                SECTION("realm_results_average()") {
52✔
2696
                    realm_value_t value = rlm_null();
2✔
2697
                    CHECK(checked(realm_results_average(r.get(), foo_int_key, &value, &found)));
2!
2698
                    CHECK(found);
2!
2699
                    CHECK(value.type == RLM_TYPE_DOUBLE);
2!
2700
                    CHECK(value.dnum == 123.0);
2!
2701

1✔
2702
                    CHECK(!realm_results_average(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
2703
                    CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2704
                }
2✔
2705

26✔
2706
                SECTION("realm_results_delete_all()") {
52✔
2707
                    CHECK(!realm_results_delete_all(r.get()));
2!
2708
                    CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
2709

1✔
2710
                    write([&]() {
2✔
2711
                        size_t num_objects;
2✔
2712
                        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_objects)));
2!
2713
                        CHECK(num_objects == 3);
2!
2714
                        CHECK(checked(realm_results_delete_all(r.get())));
2!
2715
                        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &num_objects)));
2!
2716
                        CHECK(num_objects == 2);
2!
2717
                    });
2✔
2718
                }
2✔
2719

26✔
2720
                SECTION("lists") {
52✔
2721
                    auto list = cptr_checked(realm_get_list(obj1.get(), foo_properties["link_list"]));
2✔
2722
                    cptr_checked(realm_query_parse_for_list(list.get(), "TRUEPREDICATE", 0, nullptr));
2✔
2723
                }
2✔
2724

26✔
2725
                SECTION("lists append query") {
52✔
2726
                    auto list = cptr_checked(realm_get_list(obj1.get(), foo_properties["link_list"]));
2✔
2727

1✔
2728
                    auto bar_link = realm_object_as_link(obj2.get());
2✔
2729
                    realm_value_t bar_link_val;
2✔
2730
                    bar_link_val.type = RLM_TYPE_LINK;
2✔
2731
                    bar_link_val.link = bar_link;
2✔
2732

1✔
2733
                    write([&]() {
2✔
2734
                        CHECK(checked(realm_list_insert(list.get(), 0, bar_link_val)));
2!
2735
                        CHECK(checked(realm_list_insert(list.get(), 1, bar_link_val)));
2!
2736
                        CHECK(checked(realm_list_insert(list.get(), 2, bar_link_val)));
2!
2737
                    });
2✔
2738

1✔
2739
                    size_t n = 0;
2✔
2740
                    realm_list_size(list.get(), &n);
2✔
2741
                    CHECK(n == 3);
2!
2742
                    auto query = cptr_checked(realm_query_parse_for_list(list.get(), "TRUEPREDICATE ", 0, nullptr));
2✔
2743
                    n = 0;
2✔
2744
                    realm_query_count(query.get(), &n);
2✔
2745
                    CHECK(n == 3);
2!
2746

1✔
2747
                    write([&]() {
2✔
2748
                        realm_list_clear(list.get());
2✔
2749
                    });
2✔
2750
                }
2✔
2751

26✔
2752
                SECTION("combine results query") {
52✔
2753
                    realm_value_t int_arg = rlm_int_val(123);
2✔
2754
                    const size_t num_args = 1;
2✔
2755
                    realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &int_arg}};
2✔
2756
                    realm_query_arg_t* arg_list = &args[0];
2✔
2757
                    auto q_int =
2✔
2758
                        cptr_checked(realm_query_parse(realm, class_foo.key, "int == $0", num_args, arg_list));
2✔
2759
                    auto combined_result_q =
2✔
2760
                        cptr_checked(realm_query_parse_for_results(r.get(), q_int->get_description(), 0, nullptr));
2✔
2761
                    auto result = cptr_checked(realm_query_find_all(combined_result_q.get()));
2✔
2762
                    size_t count;
2✔
2763
                    CHECK(realm_results_count(result.get(), &count));
2!
2764
                    CHECK(count == 1);
2!
2765
                }
2✔
2766

26✔
2767
                SECTION("empty results") {
52✔
2768
                    auto empty_q = cptr_checked(realm_query_parse_for_results(r.get(), "FALSEPREDICATE", 0, nullptr));
10✔
2769
                    auto empty_r = cptr_checked(realm_query_find_all(empty_q.get()));
10✔
2770

5✔
2771
                    SECTION("realm_results_count()") {
10✔
2772
                        size_t count;
2✔
2773
                        CHECK(realm_results_count(empty_r.get(), &count));
2!
2774
                        CHECK(count == 0);
2!
2775
                    }
2✔
2776

5✔
2777
                    SECTION("realm_results_min()") {
10✔
2778
                        realm_value_t value;
2✔
2779
                        bool found = true;
2✔
2780
                        CHECK(realm_results_min(empty_r.get(), foo_int_key, &value, &found));
2!
2781
                        CHECK(rlm_val_eq(value, rlm_null()));
2!
2782
                        CHECK(!found);
2!
2783
                    }
2✔
2784

5✔
2785
                    SECTION("realm_results_max()") {
10✔
2786
                        realm_value_t value;
2✔
2787
                        bool found = true;
2✔
2788
                        CHECK(realm_results_max(empty_r.get(), foo_int_key, &value, &found));
2!
2789
                        CHECK(rlm_val_eq(value, rlm_null()));
2!
2790
                        CHECK(!found);
2!
2791
                    }
2✔
2792

5✔
2793
                    SECTION("realm_results_sum()") {
10✔
2794
                        realm_value_t value;
2✔
2795
                        bool found = true;
2✔
2796
                        CHECK(realm_results_sum(empty_r.get(), foo_int_key, &value, &found));
2!
2797
                        CHECK(rlm_val_eq(value, rlm_int_val(0)));
2!
2798
                        CHECK(!found);
2!
2799
                    }
2✔
2800

5✔
2801
                    SECTION("realm_results_average()") {
10✔
2802
                        realm_value_t value;
2✔
2803
                        bool found = true;
2✔
2804
                        CHECK(realm_results_average(empty_r.get(), foo_int_key, &value, &found));
2!
2805
                        CHECK(rlm_val_eq(value, rlm_null()));
2!
2806
                        CHECK(!found);
2!
2807
                    }
2✔
2808
                }
10✔
2809
            }
52✔
2810
        }
72✔
2811

99✔
2812
        SECTION("delete causes invalidation errors") {
198✔
2813
            write([&]() {
2✔
2814
                // Get a list instance for later
1✔
2815
                auto list = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
2✔
2816

1✔
2817
                CHECK(checked(realm_object_delete(obj1.get())));
2!
2818
                CHECK(!realm_object_is_valid(obj1.get()));
2!
2819

1✔
2820
                realm_clear_last_error();
2✔
2821
                CHECK(!realm_object_delete(obj1.get()));
2!
2822
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2823

1✔
2824
                realm_clear_last_error();
2✔
2825
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_int_val(123), false));
2!
2826
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2827

1✔
2828
                realm_clear_last_error();
2✔
2829
                auto list2 = realm_get_list(obj1.get(), foo_links_key);
2✔
2830
                CHECK(!list2);
2!
2831
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2832

1✔
2833
                size_t size;
2✔
2834
                CHECK(!realm_list_size(list.get(), &size));
2!
2835
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2836
            });
2✔
2837
        }
2✔
2838

99✔
2839
        SECTION("lists") {
198✔
2840
            SECTION("realm_get_list() errors") {
32✔
2841
                CHECK(!realm_get_list(obj2.get(), bar_int_key));
2!
2842
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
2843

1✔
2844
                CHECK(!realm_get_list(obj2.get(), 123123123));
2!
2845
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2846
            }
2✔
2847

16✔
2848
            SECTION("nullable strings") {
32✔
2849
                auto strings = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
8✔
2850
                CHECK(strings);
8!
2851
                CHECK(!realm_is_frozen(strings.get()));
8!
2852

4✔
2853
                realm_value_t a = rlm_str_val("a");
8✔
2854
                realm_value_t b = rlm_str_val("b");
8✔
2855
                realm_value_t c = rlm_null();
8✔
2856

4✔
2857
                SECTION("realm_equals() type check") {
8✔
2858
                    CHECK(!realm_equals(strings.get(), obj1.get()));
2!
2859
                }
2✔
2860

4✔
2861
                SECTION("realm_clone()") {
8✔
2862
                    auto list2 = clone_cptr(strings.get());
2✔
2863
                    CHECK(realm_equals(strings.get(), list2.get()));
2!
2864
                    CHECK(strings.get() != list2.get());
2!
2865
                }
2✔
2866

4✔
2867
                SECTION("insert, then get") {
8✔
2868
                    write([&]() {
2✔
2869
                        CHECK(checked(realm_list_insert(strings.get(), 0, a)));
2!
2870
                        CHECK(checked(realm_list_insert(strings.get(), 1, b)));
2!
2871
                        CHECK(checked(realm_list_insert(strings.get(), 2, c)));
2!
2872

1✔
2873
                        realm_value_t a2, b2, c2;
2✔
2874
                        CHECK(checked(realm_list_get(strings.get(), 0, &a2)));
2!
2875
                        CHECK(checked(realm_list_get(strings.get(), 1, &b2)));
2!
2876
                        CHECK(checked(realm_list_get(strings.get(), 2, &c2)));
2!
2877

1✔
2878
                        CHECK(rlm_stdstr(a2) == "a");
2!
2879
                        CHECK(rlm_stdstr(b2) == "b");
2!
2880
                        CHECK(c2.type == RLM_TYPE_NULL);
2!
2881

1✔
2882
                        size_t out_index = -1;
2✔
2883
                        bool found;
2✔
2884
                        CHECK(checked(realm_list_find(strings.get(), &a2, &out_index, &found)));
2!
2885
                        CHECK(out_index == 0);
2!
2886
                        CHECK(found);
2!
2887
                        CHECK(checked(realm_list_find(strings.get(), &b2, &out_index, &found)));
2!
2888
                        CHECK(out_index == 1);
2!
2889
                        CHECK(found);
2!
2890
                        CHECK(checked(realm_list_find(strings.get(), &c2, &out_index, &found)));
2!
2891
                        CHECK(out_index == 2);
2!
2892
                        CHECK(found);
2!
2893

1✔
2894
                        realm_value_t dummy = rlm_str_val("c");
2✔
2895
                        CHECK(checked(realm_list_find(strings.get(), &dummy, &out_index, &found)));
2!
2896
                        CHECK(!found);
2!
2897
                        CHECK(out_index == realm::not_found);
2!
2898

1✔
2899
                        // verify that conversion to results works
1✔
2900
                        auto results = cptr_checked(realm_list_to_results(strings.get()));
2✔
2901
                        CHECK(checked(realm_results_find(results.get(), &a2, &out_index, &found)));
2!
2902
                        CHECK(found);
2!
2903
                        CHECK(out_index == 0);
2!
2904
                        CHECK(checked(realm_results_find(results.get(), &b2, &out_index, &found)));
2!
2905
                        CHECK(found);
2!
2906
                        CHECK(out_index == 1);
2!
2907
                        CHECK(checked(realm_results_find(results.get(), &c2, &out_index, &found)));
2!
2908
                        CHECK(found);
2!
2909
                        CHECK(out_index == 2);
2!
2910
                    });
2✔
2911
                }
2✔
2912

4✔
2913
                SECTION("equality") {
8✔
2914
                    auto strings2 = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
2✔
2915
                    CHECK(strings2);
2!
2916
                    CHECK(realm_equals(strings.get(), strings2.get()));
2!
2917

1✔
2918
                    write([&]() {
2✔
2919
                        auto obj3 =
2✔
2920
                            cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(2)));
2✔
2921
                        CHECK(obj3);
2!
2922
                        auto strings3 = cptr_checked(realm_get_list(obj3.get(), bar_strings_key));
2✔
2923
                        CHECK(!realm_equals(strings.get(), strings3.get()));
2!
2924
                    });
2✔
2925
                }
2✔
2926
            }
8✔
2927

16✔
2928
            SECTION("get/insert all property types") {
32✔
2929
                realm_value_t null = rlm_null();
2✔
2930
                realm_value_t integer = rlm_int_val(987);
2✔
2931
                realm_value_t boolean = rlm_bool_val(true);
2✔
2932
                realm_value_t string = rlm_str_val("My string");
2✔
2933
                const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
2✔
2934
                realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
2✔
2935
                realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
2✔
2936
                realm_value_t fnum = rlm_float_val(123.f);
2✔
2937
                realm_value_t dnum = rlm_double_val(456.0);
2✔
2938
                realm_value_t decimal = rlm_decimal_val(999.0);
2✔
2939
                realm_value_t object_id = rlm_object_id_val("abc123abc123");
2✔
2940
                realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
2✔
2941

1✔
2942
                auto int_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["int_list"]));
2✔
2943
                auto bool_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["bool_list"]));
2✔
2944
                auto string_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["string_list"]));
2✔
2945
                auto binary_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["binary_list"]));
2✔
2946
                auto timestamp_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["timestamp_list"]));
2✔
2947
                auto float_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["float_list"]));
2✔
2948
                auto double_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["double_list"]));
2✔
2949
                auto decimal_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["decimal_list"]));
2✔
2950
                auto object_id_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["object_id_list"]));
2✔
2951
                auto uuid_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["uuid_list"]));
2✔
2952
                auto nullable_int_list =
2✔
2953
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_int_list"]));
2✔
2954
                auto nullable_bool_list =
2✔
2955
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_bool_list"]));
2✔
2956
                auto nullable_string_list =
2✔
2957
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_string_list"]));
2✔
2958
                auto nullable_binary_list =
2✔
2959
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_binary_list"]));
2✔
2960
                auto nullable_timestamp_list =
2✔
2961
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_timestamp_list"]));
2✔
2962
                auto nullable_float_list =
2✔
2963
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_float_list"]));
2✔
2964
                auto nullable_double_list =
2✔
2965
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_double_list"]));
2✔
2966
                auto nullable_decimal_list =
2✔
2967
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_decimal_list"]));
2✔
2968
                auto nullable_object_id_list =
2✔
2969
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_object_id_list"]));
2✔
2970
                auto nullable_uuid_list =
2✔
2971
                    cptr_checked(realm_get_list(obj1.get(), foo_properties["nullable_uuid_list"]));
2✔
2972

1✔
2973
                write([&]() {
2✔
2974
                    CHECK(realm_list_insert(int_list.get(), 0, integer));
2!
2975
                    CHECK(realm_list_insert(bool_list.get(), 0, boolean));
2!
2976
                    CHECK(realm_list_insert(string_list.get(), 0, string));
2!
2977
                    CHECK(realm_list_insert(binary_list.get(), 0, binary));
2!
2978
                    CHECK(realm_list_insert(timestamp_list.get(), 0, timestamp));
2!
2979
                    CHECK(realm_list_insert(float_list.get(), 0, fnum));
2!
2980
                    CHECK(realm_list_insert(double_list.get(), 0, dnum));
2!
2981
                    CHECK(realm_list_insert(decimal_list.get(), 0, decimal));
2!
2982
                    CHECK(realm_list_insert(object_id_list.get(), 0, object_id));
2!
2983
                    CHECK(realm_list_insert(uuid_list.get(), 0, uuid));
2!
2984

1✔
2985
                    CHECK(realm_list_insert(nullable_int_list.get(), 0, integer));
2!
2986
                    CHECK(realm_list_insert(nullable_bool_list.get(), 0, boolean));
2!
2987
                    CHECK(realm_list_insert(nullable_string_list.get(), 0, string));
2!
2988
                    CHECK(realm_list_insert(nullable_binary_list.get(), 0, binary));
2!
2989
                    CHECK(realm_list_insert(nullable_timestamp_list.get(), 0, timestamp));
2!
2990
                    CHECK(realm_list_insert(nullable_float_list.get(), 0, fnum));
2!
2991
                    CHECK(realm_list_insert(nullable_double_list.get(), 0, dnum));
2!
2992
                    CHECK(realm_list_insert(nullable_decimal_list.get(), 0, decimal));
2!
2993
                    CHECK(realm_list_insert(nullable_object_id_list.get(), 0, object_id));
2!
2994
                    CHECK(realm_list_insert(nullable_uuid_list.get(), 0, uuid));
2!
2995

1✔
2996
                    CHECK(realm_list_insert(nullable_int_list.get(), 1, null));
2!
2997
                    CHECK(realm_list_insert(nullable_bool_list.get(), 1, null));
2!
2998
                    CHECK(realm_list_insert(nullable_string_list.get(), 1, null));
2!
2999
                    CHECK(realm_list_insert(nullable_binary_list.get(), 1, null));
2!
3000
                    CHECK(realm_list_insert(nullable_timestamp_list.get(), 1, null));
2!
3001
                    CHECK(realm_list_insert(nullable_float_list.get(), 1, null));
2!
3002
                    CHECK(realm_list_insert(nullable_double_list.get(), 1, null));
2!
3003
                    CHECK(realm_list_insert(nullable_decimal_list.get(), 1, null));
2!
3004
                    CHECK(realm_list_insert(nullable_object_id_list.get(), 1, null));
2!
3005
                    CHECK(realm_list_insert(nullable_uuid_list.get(), 1, null));
2!
3006
                });
2✔
3007

1✔
3008
                auto find = ([&](auto* list, auto* value) {
40✔
3009
                    std::size_t index = -1;
40✔
3010
                    bool found = false;
40✔
3011
                    CHECK(checked(realm_list_find(list, value, &index, &found)));
40!
3012
                    CHECK(index == 0);
40!
3013
                    CHECK(found);
40!
3014
                    return (index < list->size()) && found == true;
40✔
3015
                });
40✔
3016

1✔
3017
                realm_value_t value;
2✔
3018
                CHECK(realm_list_get(int_list.get(), 0, &value));
2!
3019
                CHECK(rlm_val_eq(value, integer));
2!
3020
                CHECK(!realm_list_get_linked_object(int_list.get(), 0));
2!
3021
                CHECK(find(int_list.get(), &value));
2!
3022
                CHECK(realm_list_get(bool_list.get(), 0, &value));
2!
3023
                CHECK(rlm_val_eq(value, boolean));
2!
3024
                CHECK(find(bool_list.get(), &value));
2!
3025
                CHECK(realm_list_get(string_list.get(), 0, &value));
2!
3026
                CHECK(rlm_val_eq(value, string));
2!
3027
                CHECK(find(string_list.get(), &value));
2!
3028
                CHECK(realm_list_get(binary_list.get(), 0, &value));
2!
3029
                CHECK(rlm_val_eq(value, binary));
2!
3030
                CHECK(find(binary_list.get(), &value));
2!
3031
                CHECK(realm_list_get(timestamp_list.get(), 0, &value));
2!
3032
                CHECK(rlm_val_eq(value, timestamp));
2!
3033
                CHECK(find(timestamp_list.get(), &value));
2!
3034
                CHECK(realm_list_get(float_list.get(), 0, &value));
2!
3035
                CHECK(rlm_val_eq(value, fnum));
2!
3036
                CHECK(find(float_list.get(), &value));
2!
3037
                CHECK(realm_list_get(double_list.get(), 0, &value));
2!
3038
                CHECK(rlm_val_eq(value, dnum));
2!
3039
                CHECK(find(double_list.get(), &value));
2!
3040
                CHECK(realm_list_get(decimal_list.get(), 0, &value));
2!
3041
                CHECK(rlm_val_eq(value, decimal));
2!
3042
                CHECK(find(decimal_list.get(), &value));
2!
3043
                CHECK(realm_list_get(object_id_list.get(), 0, &value));
2!
3044
                CHECK(rlm_val_eq(value, object_id));
2!
3045
                CHECK(find(object_id_list.get(), &value));
2!
3046
                CHECK(realm_list_get(uuid_list.get(), 0, &value));
2!
3047
                CHECK(rlm_val_eq(value, uuid));
2!
3048
                CHECK(find(uuid_list.get(), &value));
2!
3049
                CHECK(realm_list_get(nullable_int_list.get(), 0, &value));
2!
3050
                CHECK(rlm_val_eq(value, integer));
2!
3051
                CHECK(find(nullable_int_list.get(), &value));
2!
3052
                CHECK(realm_list_get(nullable_bool_list.get(), 0, &value));
2!
3053
                CHECK(rlm_val_eq(value, boolean));
2!
3054
                CHECK(find(nullable_bool_list.get(), &value));
2!
3055
                CHECK(realm_list_get(nullable_string_list.get(), 0, &value));
2!
3056
                CHECK(rlm_val_eq(value, string));
2!
3057
                CHECK(find(nullable_string_list.get(), &value));
2!
3058
                CHECK(realm_list_get(nullable_binary_list.get(), 0, &value));
2!
3059
                CHECK(rlm_val_eq(value, binary));
2!
3060
                CHECK(find(nullable_binary_list.get(), &value));
2!
3061
                CHECK(realm_list_get(nullable_timestamp_list.get(), 0, &value));
2!
3062
                CHECK(rlm_val_eq(value, timestamp));
2!
3063
                CHECK(find(nullable_timestamp_list.get(), &value));
2!
3064
                CHECK(realm_list_get(nullable_float_list.get(), 0, &value));
2!
3065
                CHECK(rlm_val_eq(value, fnum));
2!
3066
                CHECK(find(nullable_float_list.get(), &value));
2!
3067
                CHECK(realm_list_get(nullable_double_list.get(), 0, &value));
2!
3068
                CHECK(rlm_val_eq(value, dnum));
2!
3069
                CHECK(find(nullable_double_list.get(), &value));
2!
3070
                CHECK(realm_list_get(nullable_decimal_list.get(), 0, &value));
2!
3071
                CHECK(rlm_val_eq(value, decimal));
2!
3072
                CHECK(find(nullable_decimal_list.get(), &value));
2!
3073
                CHECK(realm_list_get(nullable_object_id_list.get(), 0, &value));
2!
3074
                CHECK(rlm_val_eq(value, object_id));
2!
3075
                CHECK(find(nullable_object_id_list.get(), &value));
2!
3076
                CHECK(realm_list_get(nullable_uuid_list.get(), 0, &value));
2!
3077
                CHECK(rlm_val_eq(value, uuid));
2!
3078
                CHECK(find(nullable_uuid_list.get(), &value));
2!
3079

1✔
3080
                write([&]() {
2✔
3081
                    CHECK(realm_list_insert(nullable_int_list.get(), 0, null));
2!
3082
                    CHECK(realm_list_insert(nullable_bool_list.get(), 0, null));
2!
3083
                    CHECK(realm_list_insert(nullable_string_list.get(), 0, null));
2!
3084
                    CHECK(realm_list_insert(nullable_binary_list.get(), 0, null));
2!
3085
                    CHECK(realm_list_insert(nullable_timestamp_list.get(), 0, null));
2!
3086
                    CHECK(realm_list_insert(nullable_float_list.get(), 0, null));
2!
3087
                    CHECK(realm_list_insert(nullable_double_list.get(), 0, null));
2!
3088
                    CHECK(realm_list_insert(nullable_decimal_list.get(), 0, null));
2!
3089
                    CHECK(realm_list_insert(nullable_object_id_list.get(), 0, null));
2!
3090
                    CHECK(realm_list_insert(nullable_uuid_list.get(), 0, null));
2!
3091
                });
2✔
3092

1✔
3093
                CHECK(realm_list_get(nullable_int_list.get(), 0, &value));
2!
3094
                CHECK(rlm_val_eq(value, null));
2!
3095
                CHECK(realm_list_get(nullable_bool_list.get(), 0, &value));
2!
3096
                CHECK(rlm_val_eq(value, null));
2!
3097
                CHECK(realm_list_get(nullable_string_list.get(), 0, &value));
2!
3098
                CHECK(rlm_val_eq(value, null));
2!
3099
                CHECK(realm_list_get(nullable_binary_list.get(), 0, &value));
2!
3100
                CHECK(rlm_val_eq(value, null));
2!
3101
                CHECK(realm_list_get(nullable_timestamp_list.get(), 0, &value));
2!
3102
                CHECK(rlm_val_eq(value, null));
2!
3103
                CHECK(realm_list_get(nullable_float_list.get(), 0, &value));
2!
3104
                CHECK(rlm_val_eq(value, null));
2!
3105
                CHECK(realm_list_get(nullable_double_list.get(), 0, &value));
2!
3106
                CHECK(rlm_val_eq(value, null));
2!
3107
                CHECK(realm_list_get(nullable_decimal_list.get(), 0, &value));
2!
3108
                CHECK(rlm_val_eq(value, null));
2!
3109
                CHECK(realm_list_get(nullable_object_id_list.get(), 0, &value));
2!
3110
                CHECK(rlm_val_eq(value, null));
2!
3111
                CHECK(realm_list_get(nullable_uuid_list.get(), 0, &value));
2!
3112
                CHECK(rlm_val_eq(value, null));
2!
3113
            }
2✔
3114

16✔
3115
            SECTION("move") {
32✔
3116
                auto int_list = cptr_checked(realm_get_list(obj1.get(), foo_properties["int_list"]));
2✔
3117
                write([&]() {
2✔
3118
                    for (int i = 0; i < 10; ++i) {
22✔
3119
                        CHECK(realm_list_insert(int_list.get(), i, rlm_int_val(i)));
20!
3120
                    }
20✔
3121
                });
2✔
3122

1✔
3123
                realm_value_t value;
2✔
3124
                auto expected = std::vector<int64_t>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
2✔
3125
                for (int i = 0; i < 10; ++i) {
22✔
3126
                    CHECK(realm_list_get(int_list.get(), i, &value));
20!
3127
                    CHECK(rlm_val_eq(value, rlm_int_val(expected[i])));
20!
3128
                }
20✔
3129

1✔
3130
                write([&]() {
2✔
3131
                    CHECK(realm_list_move(int_list.get(), 0, 1));
2!
3132
                });
2✔
3133

1✔
3134
                expected = std::vector<int64_t>{1, 0, 2, 3, 4, 5, 6, 7, 8, 9};
2✔
3135
                for (int i = 0; i < 10; ++i) {
22✔
3136
                    CHECK(realm_list_get(int_list.get(), i, &value));
20!
3137
                    CHECK(rlm_val_eq(value, rlm_int_val(expected[i])));
20!
3138
                }
20✔
3139

1✔
3140
                write([&]() {
2✔
3141
                    CHECK(realm_list_move(int_list.get(), 3, 2));
2!
3142
                });
2✔
3143

1✔
3144
                expected = std::vector<int64_t>{1, 0, 3, 2, 4, 5, 6, 7, 8, 9};
2✔
3145
                for (int i = 0; i < 10; ++i) {
22✔
3146
                    CHECK(realm_list_get(int_list.get(), i, &value));
20!
3147
                    CHECK(rlm_val_eq(value, rlm_int_val(expected[i])));
20!
3148
                }
20✔
3149
            }
2✔
3150

16✔
3151
            SECTION("links") {
32✔
3152
                CPtr<realm_list_t> bars;
8✔
3153

4✔
3154
                write([&]() {
8✔
3155
                    bars = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
8✔
3156
                    auto bar_link = realm_object_as_link(obj2.get());
8✔
3157
                    realm_value_t bar_link_val;
8✔
3158
                    bar_link_val.type = RLM_TYPE_LINK;
8✔
3159
                    bar_link_val.link = bar_link;
8✔
3160
                    CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
8!
3161
                    CHECK(checked(realm_list_insert(bars.get(), 1, bar_link_val)));
8!
3162
                    size_t size;
8✔
3163
                    CHECK(checked(realm_list_size(bars.get(), &size)));
8!
3164
                    CHECK(size == 2);
8!
3165

4✔
3166
                    bool found = true;
8✔
3167
                    size_t index = -1;
8✔
3168
                    CHECK(checked(realm_list_find(bars.get(), &bar_link_val, &index, &found)));
8!
3169
                    CHECK(index == 0);
8!
3170
                    CHECK(found);
8!
3171

4✔
3172
                    realm_list_clear(bars.get());
8✔
3173
                    CHECK(checked(realm_list_find(bars.get(), &bar_link_val, &index, &found)));
8!
3174
                    CHECK(index == realm::not_found);
8!
3175
                    CHECK(!found);
8!
3176

4✔
3177
                    CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
8!
3178
                    CHECK(checked(realm_list_insert(bars.get(), 1, bar_link_val)));
8!
3179
                });
8✔
3180

4✔
3181
                SECTION("get") {
8✔
3182
                    realm_value_t val;
2✔
3183
                    CHECK(checked(realm_list_get(bars.get(), 0, &val)));
2!
3184
                    CHECK(val.type == RLM_TYPE_LINK);
2!
3185
                    CHECK(val.link.target_table == class_bar.key);
2!
3186
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
3187

1✔
3188
                    CHECK(checked(realm_list_get(bars.get(), 1, &val)));
2!
3189
                    CHECK(val.type == RLM_TYPE_LINK);
2!
3190
                    CHECK(val.link.target_table == class_bar.key);
2!
3191
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
3192

1✔
3193
                    auto result = realm_list_get(bars.get(), 2, &val);
2✔
3194
                    CHECK(!result);
2!
3195
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
2!
3196
                }
2✔
3197

4✔
3198
                SECTION("set wrong type") {
8✔
3199
                    write([&]() {
2✔
3200
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
2✔
3201
                        CHECK(foo2);
2!
3202
                        realm_value_t foo2_link_val;
2✔
3203
                        foo2_link_val.type = RLM_TYPE_LINK;
2✔
3204
                        foo2_link_val.link = realm_object_as_link(foo2.get());
2✔
3205

1✔
3206
                        CHECK(!realm_list_set(bars.get(), 0, foo2_link_val));
2!
3207
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3208
                    });
2✔
3209
                }
2✔
3210

4✔
3211
                SECTION("realm_list_clear()") {
8✔
3212
                    write([&]() {
2✔
3213
                        CHECK(realm_list_clear(bars.get()));
2!
3214
                    });
2✔
3215
                    size_t size;
2✔
3216
                    CHECK(realm_list_size(bars.get(), &size));
2!
3217
                    CHECK(size == 0);
2!
3218

1✔
3219
                    size_t num_bars;
2✔
3220
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3221
                    CHECK(num_bars != 0);
2!
3222
                }
2✔
3223

4✔
3224
                SECTION("realm_list_remove_all()") {
8✔
3225
                    size_t num_bars;
2✔
3226
                    size_t size;
2✔
3227

1✔
3228
                    write([&]() {
2✔
3229
                        CHECK(checked(realm_list_remove_all(bars.get())));
2!
3230
                    });
2✔
3231

1✔
3232
                    CHECK(realm_list_size(bars.get(), &size));
2!
3233
                    CHECK(size == 0);
2!
3234

1✔
3235
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3236
                    CHECK(num_bars == 0);
2!
3237
                }
2✔
3238
            }
8✔
3239

16✔
3240
            SECTION("embedded") {
32✔
3241
                CPtr<realm_list_t> subs;
2✔
3242
                realm_property_info_t info;
2✔
3243
                bool found = false;
2✔
3244
                REQUIRE(checked(realm_find_property(realm, class_bar.key, "sub_list", &found, &info)));
2!
3245
                REQUIRE(found);
2!
3246
                subs = cptr_checked(realm_get_list(obj2.get(), info.key));
2✔
3247

1✔
3248
                write([&]() {
2✔
3249
                    auto embedded = cptr_checked(realm_list_insert_embedded(subs.get(), 0));
2✔
3250
                    CHECK(embedded);
2!
3251
                });
2✔
3252
                auto embedded = cptr_checked(realm_list_get_linked_object(subs.get(), 0));
2✔
3253
                CHECK(embedded);
2!
3254
            }
2✔
3255

16✔
3256
            SECTION("notifications") {
32✔
3257
                struct State {
8✔
3258
                    CPtr<realm_collection_changes_t> changes;
8✔
3259
                    CPtr<realm_async_error_t> error;
8✔
3260
                    bool destroyed = false;
8✔
3261
                    bool called = false;
8✔
3262
                };
8✔
3263

4✔
3264
                State state;
8✔
3265

4✔
3266
                auto on_change = [](void* userdata, const realm_collection_changes_t* changes) {
14✔
3267
                    auto* state = static_cast<State*>(userdata);
14✔
3268
                    state->changes = clone_cptr(changes);
14✔
3269
                    state->called = true;
14✔
3270
                };
14✔
3271

4✔
3272
                CPtr<realm_list_t> strings = cptr_checked(realm_get_list(obj2.get(), bar_strings_key));
8✔
3273

4✔
3274
                auto str1 = rlm_str_val("a");
8✔
3275
                auto str2 = rlm_str_val("b");
8✔
3276
                auto null = rlm_null();
8✔
3277

4✔
3278
                auto require_change = [&]() {
6✔
3279
                    auto token = cptr_checked(
4✔
3280
                        realm_list_add_notification_callback(strings.get(), &state, nullptr, nullptr, on_change));
4✔
3281
                    checked(realm_refresh(realm, nullptr));
4✔
3282
                    return token;
4✔
3283
                };
4✔
3284

4✔
3285
                SECTION("userdata is freed when the token is destroyed") {
8✔
3286
                    auto token = cptr_checked(realm_list_add_notification_callback(
2✔
3287
                        strings.get(), &state,
2✔
3288
                        [](void* p) {
2✔
3289
                            static_cast<State*>(p)->destroyed = true;
2✔
3290
                        },
2✔
3291
                        nullptr, nullptr));
2✔
3292
                    CHECK(!state.destroyed);
2!
3293
                    token.reset();
2✔
3294
                    CHECK(state.destroyed);
2!
3295
                }
2✔
3296

4✔
3297
                SECTION("insertion sends a change callback") {
8✔
3298
                    auto token = require_change();
2✔
3299
                    write([&]() {
2✔
3300
                        checked(realm_list_insert(strings.get(), 0, str1));
2✔
3301
                        checked(realm_list_insert(strings.get(), 1, str2));
2✔
3302
                        checked(realm_list_insert(strings.get(), 2, null));
2✔
3303
                    });
2✔
3304
                    CHECK(!state.error);
2!
3305
                    CHECK(state.changes);
2!
3306

1✔
3307
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
2✔
3308
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
2✔
3309
                                                            &num_insertion_ranges, &num_modification_ranges,
2✔
3310
                                                            &num_moves);
2✔
3311
                    CHECK(num_deletion_ranges == 0);
2!
3312
                    CHECK(num_insertion_ranges == 1);
2!
3313
                    CHECK(num_modification_ranges == 0);
2!
3314
                    CHECK(num_moves == 0);
2!
3315

1✔
3316
                    realm_index_range_t insertion_range;
2✔
3317
                    realm_collection_changes_get_ranges(state.changes.get(), nullptr, 0, &insertion_range, 1, nullptr,
2✔
3318
                                                        0, nullptr, 0, nullptr, 0);
2✔
3319
                    CHECK(insertion_range.from == 0);
2!
3320
                    CHECK(insertion_range.to == 3);
2!
3321
                }
2✔
3322

4✔
3323
                SECTION("modifying target of list with a filter") {
8✔
3324
                    auto bars = cptr_checked(realm_get_list(obj1.get(), foo_links_key));
2✔
3325
                    write([&]() {
2✔
3326
                        auto bar_link = realm_object_as_link(obj2.get());
2✔
3327
                        realm_value_t bar_link_val;
2✔
3328
                        bar_link_val.type = RLM_TYPE_LINK;
2✔
3329
                        bar_link_val.link = bar_link;
2✔
3330
                        CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
2!
3331
                    });
2✔
3332

1✔
3333
                    realm_key_path_elem_t bar_strings[] = {{class_bar.key, bar_doubles_key}};
2✔
3334
                    realm_key_path_t key_path_bar_strings[] = {{1, bar_strings}};
2✔
3335
                    realm_key_path_array_t key_path_array = {1, key_path_bar_strings};
2✔
3336
                    auto token = cptr_checked(realm_list_add_notification_callback(bars.get(), &state, nullptr,
2✔
3337
                                                                                   &key_path_array, on_change));
2✔
3338
                    checked(realm_refresh(realm, nullptr));
2✔
3339

1✔
3340
                    state.called = false;
2✔
3341
                    write([&]() {
2✔
3342
                        checked(realm_set_value(obj2.get(), bar_doubles_key, rlm_double_val(5.0), false));
2✔
3343
                    });
2✔
3344
                    REQUIRE(state.called);
2!
3345
                    CHECK(!state.error);
2!
3346
                    CHECK(state.changes);
2!
3347

1✔
3348
                    state.called = false;
2✔
3349
                    write([&]() {
2✔
3350
                        checked(realm_list_insert(strings.get(), 0, str1));
2✔
3351
                        checked(realm_list_insert(strings.get(), 1, str2));
2✔
3352
                        checked(realm_list_insert(strings.get(), 2, null));
2✔
3353
                    });
2✔
3354
                    REQUIRE(!state.called);
2!
3355
                }
2✔
3356

4✔
3357
                SECTION("insertion, deletion, modification, modification after") {
8✔
3358
                    write([&]() {
2✔
3359
                        checked(realm_list_insert(strings.get(), 0, str1));
2✔
3360
                        checked(realm_list_insert(strings.get(), 1, str2));
2✔
3361
                        checked(realm_list_insert(strings.get(), 2, str1));
2✔
3362
                    });
2✔
3363

1✔
3364
                    auto token = require_change();
2✔
3365

1✔
3366
                    write([&]() {
2✔
3367
                        checked(realm_list_erase(strings.get(), 1));
2✔
3368
                        checked(realm_list_insert(strings.get(), 0, null));
2✔
3369
                        checked(realm_list_insert(strings.get(), 1, null));
2✔
3370

1✔
3371
                        // This element was previously at 0, and ends up at 2.
1✔
3372
                        checked(realm_list_set(strings.get(), 2, str1));
2✔
3373
                    });
2✔
3374
                    CHECK(!state.error);
2!
3375
                    CHECK(state.changes);
2!
3376

1✔
3377
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
2✔
3378
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
2✔
3379
                                                            &num_insertion_ranges, &num_modification_ranges,
2✔
3380
                                                            &num_moves);
2✔
3381
                    CHECK(num_deletion_ranges == 1);
2!
3382
                    CHECK(num_insertion_ranges == 1);
2!
3383
                    CHECK(num_modification_ranges == 1);
2!
3384
                    CHECK(num_moves == 0);
2!
3385

1✔
3386
                    size_t num_deletions, num_insertions, num_modifications;
2✔
3387
                    bool collection_cleared = false;
2✔
3388
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
2✔
3389
                                                             &num_modifications, &num_moves, &collection_cleared,
2✔
3390
                                                             nullptr);
2✔
3391
                    CHECK(num_deletions == 1);
2!
3392
                    CHECK(num_insertions == 2);
2!
3393
                    CHECK(num_modifications == 1);
2!
3394
                    CHECK(collection_cleared == false);
2!
3395

1✔
3396
                    realm_index_range_t deletions, insertions, modifications, modifications_after;
2✔
3397
                    realm_collection_move_t moves;
2✔
3398
                    realm_collection_changes_get_ranges(state.changes.get(), &deletions, 1, &insertions, 1,
2✔
3399
                                                        &modifications, 1, &modifications_after, 1, &moves, 1);
2✔
3400
                    CHECK(deletions.from == 1);
2!
3401
                    CHECK(deletions.to == 2);
2!
3402

1✔
3403
                    CHECK(insertions.from == 0);
2!
3404
                    CHECK(insertions.to == 2);
2!
3405

1✔
3406
                    CHECK(modifications.from == 0);
2!
3407
                    CHECK(modifications.to == 1);
2!
3408

1✔
3409
                    CHECK(modifications_after.from == 2);
2!
3410
                    CHECK(modifications_after.to == 3);
2!
3411

1✔
3412
                    std::vector<size_t> deletions_v, insertions_v, modifications_v, modifications_after_v;
2✔
3413
                    std::vector<realm_collection_move_t> moves_v;
2✔
3414
                    deletions_v.resize(100, size_t(-1));
2✔
3415
                    insertions_v.resize(100, size_t(-1));
2✔
3416
                    modifications_v.resize(100, size_t(-1));
2✔
3417
                    modifications_after_v.resize(100, size_t(-1));
2✔
3418
                    moves_v.resize(100, realm_collection_move_t{size_t(-1), size_t(-1)});
2✔
3419
                    realm_collection_changes_get_changes(state.changes.get(), deletions_v.data(), 100,
2✔
3420
                                                         insertions_v.data(), 100, modifications_v.data(), 100,
2✔
3421
                                                         modifications_after_v.data(), 100, moves_v.data(), 100);
2✔
3422
                    CHECK(deletions_v[0] == 1);
2!
3423
                    CHECK(deletions_v[1] == size_t(-1));
2!
3424
                    CHECK(insertions_v[0] == 0);
2!
3425
                    CHECK(insertions_v[1] == 1);
2!
3426
                    CHECK(insertions_v[2] == size_t(-1));
2!
3427
                    CHECK(modifications_v[0] == 0);
2!
3428
                    CHECK(modifications_v[1] == size_t(-1));
2!
3429
                    CHECK(modifications_after_v[0] == 2);
2!
3430
                    CHECK(modifications_after_v[1] == size_t(-1));
2!
3431

1✔
3432
                    write([&]() {
2✔
3433
                        checked(realm_list_clear(strings.get()));
2✔
3434
                    });
2✔
3435

1✔
3436
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
2✔
3437
                                                             &num_modifications, &num_moves, &collection_cleared,
2✔
3438
                                                             nullptr);
2✔
3439
                    CHECK(collection_cleared == true);
2!
3440
                }
2✔
3441
            }
8✔
3442
        }
32✔
3443

99✔
3444
        SECTION("sets") {
198✔
3445
            SECTION("realm_get_set() errors") {
26✔
3446
                CHECK(!realm_get_set(obj1.get(), foo_properties["int"]));
2!
3447
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3448

1✔
3449
                CHECK(!realm_get_set(obj1.get(), 123123123));
2!
3450
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
3451
            }
2✔
3452

13✔
3453
            SECTION("nullable strings") {
26✔
3454
                auto strings = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
10✔
3455
                CHECK(strings);
10!
3456
                CHECK(!realm_is_frozen(strings.get()));
10!
3457

5✔
3458
                realm_value_t a = rlm_str_val("a");
10✔
3459
                realm_value_t b = rlm_str_val("b");
10✔
3460
                realm_value_t c = rlm_null();
10✔
3461

5✔
3462
                SECTION("realm_equals() type check") {
10✔
3463
                    CHECK(!realm_equals(strings.get(), obj1.get()));
2!
3464
                }
2✔
3465

5✔
3466
                SECTION("realm_clone()") {
10✔
3467
                    auto set2 = clone_cptr(strings.get());
2✔
3468
                    CHECK(realm_equals(strings.get(), set2.get()));
2!
3469
                    CHECK(strings.get() != set2.get());
2!
3470
                }
2✔
3471

5✔
3472
                SECTION("insert, then get, then erase") {
10✔
3473
                    write([&]() {
2✔
3474
                        bool inserted = false;
2✔
3475
                        CHECK(checked(realm_set_insert(strings.get(), a, nullptr, &inserted)));
2!
3476
                        CHECK(inserted);
2!
3477
                        CHECK(checked(realm_set_insert(strings.get(), b, nullptr, &inserted)));
2!
3478
                        CHECK(inserted);
2!
3479
                        CHECK(checked(realm_set_insert(strings.get(), c, nullptr, &inserted)));
2!
3480
                        CHECK(inserted);
2!
3481

1✔
3482
                        size_t a_index, b_index, c_index;
2✔
3483
                        bool found = false;
2✔
3484
                        CHECK(checked(realm_set_find(strings.get(), a, &a_index, &found)));
2!
3485
                        CHECK(found);
2!
3486
                        CHECK(checked(realm_set_find(strings.get(), b, &b_index, &found)));
2!
3487
                        CHECK(found);
2!
3488
                        CHECK(checked(realm_set_find(strings.get(), c, &c_index, &found)));
2!
3489
                        CHECK(found);
2!
3490

1✔
3491
                        realm_value_t a2, b2, c2;
2✔
3492
                        CHECK(checked(realm_set_get(strings.get(), a_index, &a2)));
2!
3493
                        CHECK(checked(realm_set_get(strings.get(), b_index, &b2)));
2!
3494
                        CHECK(checked(realm_set_get(strings.get(), c_index, &c2)));
2!
3495

1✔
3496
                        CHECK(rlm_stdstr(a2) == "a");
2!
3497
                        CHECK(rlm_stdstr(b2) == "b");
2!
3498
                        CHECK(c2.type == RLM_TYPE_NULL);
2!
3499

1✔
3500
                        bool erased = false;
2✔
3501
                        CHECK(checked(realm_set_erase(strings.get(), a2, &erased)));
2!
3502
                        CHECK(erased);
2!
3503
                        CHECK(checked(realm_set_erase(strings.get(), rlm_int_val(987), &erased)));
2!
3504
                        CHECK(!erased);
2!
3505
                    });
2✔
3506
                }
2✔
3507

5✔
3508
                SECTION("equality") {
10✔
3509
                    auto strings2 = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
2✔
3510
                    CHECK(strings2);
2!
3511
                    CHECK(realm_equals(strings.get(), strings2.get()));
2!
3512

1✔
3513
                    write([&]() {
2✔
3514
                        auto obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
3515
                        CHECK(obj3);
2!
3516
                        auto strings3 =
2✔
3517
                            cptr_checked(realm_get_set(obj3.get(), foo_properties["nullable_string_set"]));
2✔
3518
                        CHECK(!realm_equals(strings.get(), strings3.get()));
2!
3519
                    });
2✔
3520
                }
2✔
3521

5✔
3522
                SECTION("parse query for sets") {
10✔
3523
                    auto links = cptr_checked(realm_get_set(obj1.get(), foo_properties["link_set"]));
2✔
3524
                    CHECK(links);
2!
3525
                    cptr_checked(realm_query_parse_for_set(links.get(), "TRUEPREDICATE", 0, nullptr));
2✔
3526
                }
2✔
3527
            }
10✔
3528

13✔
3529
            SECTION("get/insert all property types") {
26✔
3530
                realm_value_t null = rlm_null();
2✔
3531
                realm_value_t integer = rlm_int_val(987);
2✔
3532
                realm_value_t boolean = rlm_bool_val(true);
2✔
3533
                realm_value_t string = rlm_str_val("My string");
2✔
3534
                const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
2✔
3535
                realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
2✔
3536
                realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
2✔
3537
                realm_value_t fnum = rlm_float_val(123.f);
2✔
3538
                realm_value_t dnum = rlm_double_val(456.0);
2✔
3539
                realm_value_t decimal = rlm_decimal_val(999.0);
2✔
3540
                realm_value_t object_id = rlm_object_id_val("abc123abc123");
2✔
3541
                realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
2✔
3542

1✔
3543
                auto int_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["int_set"]));
2✔
3544
                auto bool_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["bool_set"]));
2✔
3545
                auto string_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["string_set"]));
2✔
3546
                auto binary_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["binary_set"]));
2✔
3547
                auto timestamp_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["timestamp_set"]));
2✔
3548
                auto float_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["float_set"]));
2✔
3549
                auto double_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["double_set"]));
2✔
3550
                auto decimal_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["decimal_set"]));
2✔
3551
                auto object_id_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["object_id_set"]));
2✔
3552
                auto uuid_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["uuid_set"]));
2✔
3553
                auto nullable_int_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_int_set"]));
2✔
3554
                auto nullable_bool_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_bool_set"]));
2✔
3555
                auto nullable_string_set =
2✔
3556
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
2✔
3557
                auto nullable_binary_set =
2✔
3558
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_binary_set"]));
2✔
3559
                auto nullable_timestamp_set =
2✔
3560
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_timestamp_set"]));
2✔
3561
                auto nullable_float_set =
2✔
3562
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_float_set"]));
2✔
3563
                auto nullable_double_set =
2✔
3564
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_double_set"]));
2✔
3565
                auto nullable_decimal_set =
2✔
3566
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_decimal_set"]));
2✔
3567
                auto nullable_object_id_set =
2✔
3568
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_object_id_set"]));
2✔
3569
                auto nullable_uuid_set = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_uuid_set"]));
2✔
3570

1✔
3571
                write([&]() {
2✔
3572
                    CHECK(realm_set_insert(int_set.get(), integer, nullptr, nullptr));
2!
3573
                    CHECK(realm_set_insert(bool_set.get(), boolean, nullptr, nullptr));
2!
3574
                    CHECK(realm_set_insert(string_set.get(), string, nullptr, nullptr));
2!
3575
                    CHECK(realm_set_insert(binary_set.get(), binary, nullptr, nullptr));
2!
3576
                    CHECK(realm_set_insert(timestamp_set.get(), timestamp, nullptr, nullptr));
2!
3577
                    CHECK(realm_set_insert(float_set.get(), fnum, nullptr, nullptr));
2!
3578
                    CHECK(realm_set_insert(double_set.get(), dnum, nullptr, nullptr));
2!
3579
                    CHECK(realm_set_insert(decimal_set.get(), decimal, nullptr, nullptr));
2!
3580
                    CHECK(realm_set_insert(object_id_set.get(), object_id, nullptr, nullptr));
2!
3581
                    CHECK(realm_set_insert(uuid_set.get(), uuid, nullptr, nullptr));
2!
3582

1✔
3583
                    CHECK(realm_set_insert(nullable_int_set.get(), integer, nullptr, nullptr));
2!
3584
                    CHECK(realm_set_insert(nullable_bool_set.get(), boolean, nullptr, nullptr));
2!
3585
                    CHECK(realm_set_insert(nullable_string_set.get(), string, nullptr, nullptr));
2!
3586
                    CHECK(realm_set_insert(nullable_binary_set.get(), binary, nullptr, nullptr));
2!
3587
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), timestamp, nullptr, nullptr));
2!
3588
                    CHECK(realm_set_insert(nullable_float_set.get(), fnum, nullptr, nullptr));
2!
3589
                    CHECK(realm_set_insert(nullable_double_set.get(), dnum, nullptr, nullptr));
2!
3590
                    CHECK(realm_set_insert(nullable_decimal_set.get(), decimal, nullptr, nullptr));
2!
3591
                    CHECK(realm_set_insert(nullable_object_id_set.get(), object_id, nullptr, nullptr));
2!
3592
                    CHECK(realm_set_insert(nullable_uuid_set.get(), uuid, nullptr, nullptr));
2!
3593

1✔
3594
                    CHECK(realm_set_insert(nullable_int_set.get(), null, nullptr, nullptr));
2!
3595
                    CHECK(realm_set_insert(nullable_bool_set.get(), null, nullptr, nullptr));
2!
3596
                    CHECK(realm_set_insert(nullable_string_set.get(), null, nullptr, nullptr));
2!
3597
                    CHECK(realm_set_insert(nullable_binary_set.get(), null, nullptr, nullptr));
2!
3598
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), null, nullptr, nullptr));
2!
3599
                    CHECK(realm_set_insert(nullable_float_set.get(), null, nullptr, nullptr));
2!
3600
                    CHECK(realm_set_insert(nullable_double_set.get(), null, nullptr, nullptr));
2!
3601
                    CHECK(realm_set_insert(nullable_decimal_set.get(), null, nullptr, nullptr));
2!
3602
                    CHECK(realm_set_insert(nullable_object_id_set.get(), null, nullptr, nullptr));
2!
3603
                    CHECK(realm_set_insert(nullable_uuid_set.get(), null, nullptr, nullptr));
2!
3604
                });
2✔
3605

1✔
3606
                realm_value_t value;
2✔
3607

1✔
3608
                CHECK(realm_set_get(int_set.get(), 0, &value));
2!
3609
                CHECK(rlm_val_eq(value, integer));
2!
3610
                CHECK(realm_set_get(bool_set.get(), 0, &value));
2!
3611
                CHECK(rlm_val_eq(value, boolean));
2!
3612
                CHECK(realm_set_get(string_set.get(), 0, &value));
2!
3613
                CHECK(rlm_val_eq(value, string));
2!
3614
                CHECK(realm_set_get(binary_set.get(), 0, &value));
2!
3615
                CHECK(rlm_val_eq(value, binary));
2!
3616
                CHECK(realm_set_get(timestamp_set.get(), 0, &value));
2!
3617
                CHECK(rlm_val_eq(value, timestamp));
2!
3618
                CHECK(realm_set_get(float_set.get(), 0, &value));
2!
3619
                CHECK(rlm_val_eq(value, fnum));
2!
3620
                CHECK(realm_set_get(double_set.get(), 0, &value));
2!
3621
                CHECK(rlm_val_eq(value, dnum));
2!
3622
                CHECK(realm_set_get(decimal_set.get(), 0, &value));
2!
3623
                CHECK(rlm_val_eq(value, decimal));
2!
3624
                CHECK(realm_set_get(object_id_set.get(), 0, &value));
2!
3625
                CHECK(rlm_val_eq(value, object_id));
2!
3626
                CHECK(realm_set_get(uuid_set.get(), 0, &value));
2!
3627
                CHECK(rlm_val_eq(value, uuid));
2!
3628
                CHECK(realm_set_get(nullable_int_set.get(), 1, &value));
2!
3629
                CHECK(rlm_val_eq(value, integer));
2!
3630
                CHECK(realm_set_get(nullable_bool_set.get(), 1, &value));
2!
3631
                CHECK(rlm_val_eq(value, boolean));
2!
3632
                CHECK(realm_set_get(nullable_string_set.get(), 1, &value));
2!
3633
                CHECK(rlm_val_eq(value, string));
2!
3634
                CHECK(realm_set_get(nullable_binary_set.get(), 1, &value));
2!
3635
                CHECK(rlm_val_eq(value, binary));
2!
3636
                CHECK(realm_set_get(nullable_timestamp_set.get(), 1, &value));
2!
3637
                CHECK(rlm_val_eq(value, timestamp));
2!
3638
                CHECK(realm_set_get(nullable_float_set.get(), 1, &value));
2!
3639
                CHECK(rlm_val_eq(value, fnum));
2!
3640
                CHECK(realm_set_get(nullable_double_set.get(), 1, &value));
2!
3641
                CHECK(rlm_val_eq(value, dnum));
2!
3642
                CHECK(realm_set_get(nullable_decimal_set.get(), 1, &value));
2!
3643
                CHECK(rlm_val_eq(value, decimal));
2!
3644
                CHECK(realm_set_get(nullable_object_id_set.get(), 1, &value));
2!
3645
                CHECK(rlm_val_eq(value, object_id));
2!
3646
                CHECK(realm_set_get(nullable_uuid_set.get(), 1, &value));
2!
3647
                CHECK(rlm_val_eq(value, uuid));
2!
3648

1✔
3649
                write([&]() {
2✔
3650
                    size_t index;
2✔
3651
                    bool inserted;
2✔
3652
                    CHECK(realm_set_insert(nullable_int_set.get(), null, &index, &inserted));
2!
3653
                    CHECK((index == 0 && !inserted));
2!
3654
                    CHECK(realm_set_insert(nullable_bool_set.get(), null, &index, &inserted));
2!
3655
                    CHECK((index == 0 && !inserted));
2!
3656
                    CHECK(realm_set_insert(nullable_string_set.get(), null, &index, &inserted));
2!
3657
                    CHECK((index == 0 && !inserted));
2!
3658
                    CHECK(realm_set_insert(nullable_binary_set.get(), null, &index, &inserted));
2!
3659
                    CHECK((index == 0 && !inserted));
2!
3660
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), null, &index, &inserted));
2!
3661
                    CHECK((index == 0 && !inserted));
2!
3662
                    CHECK(realm_set_insert(nullable_float_set.get(), null, &index, &inserted));
2!
3663
                    CHECK((index == 0 && !inserted));
2!
3664
                    CHECK(realm_set_insert(nullable_double_set.get(), null, &index, &inserted));
2!
3665
                    CHECK((index == 0 && !inserted));
2!
3666
                    CHECK(realm_set_insert(nullable_decimal_set.get(), null, &index, &inserted));
2!
3667
                    CHECK((index == 0 && !inserted));
2!
3668
                    CHECK(realm_set_insert(nullable_object_id_set.get(), null, &index, &inserted));
2!
3669
                    CHECK((index == 0 && !inserted));
2!
3670
                    CHECK(realm_set_insert(nullable_uuid_set.get(), null, &index, &inserted));
2!
3671
                    CHECK((index == 0 && !inserted));
2!
3672
                });
2✔
3673

1✔
3674
                // Note: This relies on the fact that NULL is "less than" other
1✔
3675
                // values in the internal sort order.
1✔
3676
                CHECK(realm_set_get(nullable_int_set.get(), 0, &value));
2!
3677
                CHECK(rlm_val_eq(value, null));
2!
3678
                CHECK(realm_set_get(nullable_bool_set.get(), 0, &value));
2!
3679
                CHECK(rlm_val_eq(value, null));
2!
3680
                CHECK(realm_set_get(nullable_string_set.get(), 0, &value));
2!
3681
                CHECK(rlm_val_eq(value, null));
2!
3682
                CHECK(realm_set_get(nullable_binary_set.get(), 0, &value));
2!
3683
                CHECK(rlm_val_eq(value, null));
2!
3684
                CHECK(realm_set_get(nullable_timestamp_set.get(), 0, &value));
2!
3685
                CHECK(rlm_val_eq(value, null));
2!
3686
                CHECK(realm_set_get(nullable_float_set.get(), 0, &value));
2!
3687
                CHECK(rlm_val_eq(value, null));
2!
3688
                CHECK(realm_set_get(nullable_double_set.get(), 0, &value));
2!
3689
                CHECK(rlm_val_eq(value, null));
2!
3690
                CHECK(realm_set_get(nullable_decimal_set.get(), 0, &value));
2!
3691
                CHECK(rlm_val_eq(value, null));
2!
3692
                CHECK(realm_set_get(nullable_object_id_set.get(), 0, &value));
2!
3693
                CHECK(rlm_val_eq(value, null));
2!
3694
                CHECK(realm_set_get(nullable_uuid_set.get(), 0, &value));
2!
3695
                CHECK(rlm_val_eq(value, null));
2!
3696
            }
2✔
3697

13✔
3698
            SECTION("links") {
26✔
3699
                CPtr<realm_set_t> bars;
8✔
3700

4✔
3701
                write([&]() {
8✔
3702
                    bars = cptr_checked(realm_get_set(obj1.get(), foo_properties["link_set"]));
8✔
3703
                    auto bar_link = realm_object_as_link(obj2.get());
8✔
3704
                    realm_value_t bar_link_val;
8✔
3705
                    bar_link_val.type = RLM_TYPE_LINK;
8✔
3706
                    bar_link_val.link = bar_link;
8✔
3707
                    size_t index;
8✔
3708
                    bool inserted;
8✔
3709
                    CHECK(checked(realm_set_insert(bars.get(), bar_link_val, &index, &inserted)));
8!
3710
                    CHECK((index == 0 && inserted));
8!
3711
                    CHECK(checked(realm_set_insert(bars.get(), bar_link_val, &index, &inserted)));
8!
3712
                    CHECK((index == 0 && !inserted));
8!
3713
                    size_t size;
8✔
3714
                    CHECK(checked(realm_set_size(bars.get(), &size)));
8!
3715
                    CHECK(size == 1);
8!
3716

4✔
3717
                    auto results =
8✔
3718
                        cptr_checked(realm_get_backlinks(obj2.get(), class_foo.key, foo_properties["link_set"]));
8✔
3719
                    CHECK(results->size() == 1);
8!
3720
                    auto mixed_link = results->get_any(0);
8✔
3721
                    CHECK(!mixed_link.is_unresolved_link());
8!
3722
                    CHECK(mixed_link.is_type(type_TypedLink));
8!
3723
                    auto link = mixed_link.get_link();
8✔
3724
                    CHECK(link.get_obj_key() == obj1->get_obj().get_key());
8!
3725
                    CHECK(link.get_table_key() == obj1->get_obj().get_table()->get_key());
8!
3726
                });
8✔
3727

4✔
3728
                SECTION("get") {
8✔
3729
                    realm_value_t val;
2✔
3730
                    CHECK(checked(realm_set_get(bars.get(), 0, &val)));
2!
3731
                    CHECK(val.type == RLM_TYPE_LINK);
2!
3732
                    CHECK(val.link.target_table == class_bar.key);
2!
3733
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
3734

1✔
3735
                    auto result = realm_set_get(bars.get(), 1, &val);
2✔
3736
                    CHECK(!result);
2!
3737
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
2!
3738
                }
2✔
3739

4✔
3740
                SECTION("insert wrong type") {
8✔
3741
                    write([&]() {
2✔
3742
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
2✔
3743
                        CHECK(foo2);
2!
3744
                        realm_value_t foo2_link_val;
2✔
3745
                        foo2_link_val.type = RLM_TYPE_LINK;
2✔
3746
                        foo2_link_val.link = realm_object_as_link(foo2.get());
2✔
3747

1✔
3748
                        CHECK(!realm_set_insert(bars.get(), foo2_link_val, nullptr, nullptr));
2!
3749
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3750
                    });
2✔
3751
                }
2✔
3752

4✔
3753
                SECTION("realm_set_clear()") {
8✔
3754
                    write([&]() {
2✔
3755
                        CHECK(realm_set_clear(bars.get()));
2!
3756
                    });
2✔
3757
                    size_t size;
2✔
3758
                    CHECK(realm_set_size(bars.get(), &size));
2!
3759
                    CHECK(size == 0);
2!
3760

1✔
3761
                    size_t num_bars;
2✔
3762
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3763
                    CHECK(num_bars != 0);
2!
3764
                }
2✔
3765

4✔
3766
                SECTION("realm_set_remove_all()") {
8✔
3767
                    realm_value_t val;
2✔
3768
                    CHECK(checked(realm_set_get(bars.get(), 0, &val)));
2!
3769
                    CHECK(val.type == RLM_TYPE_LINK);
2!
3770
                    CHECK(val.link.target_table == class_bar.key);
2!
3771
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
3772

1✔
3773
                    size_t num_bars;
2✔
3774
                    size_t size;
2✔
3775

1✔
3776
                    write([&]() {
2✔
3777
                        CHECK(checked(realm_set_remove_all(bars.get())));
2!
3778
                    });
2✔
3779

1✔
3780
                    CHECK(realm_set_size(bars.get(), &size));
2!
3781
                    CHECK(size == 0);
2!
3782

1✔
3783
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3784
                    CHECK(num_bars == 0);
2!
3785
                }
2✔
3786
            }
8✔
3787

13✔
3788
            SECTION("notifications") {
26✔
3789
                struct State {
4✔
3790
                    CPtr<realm_collection_changes_t> changes;
4✔
3791
                    CPtr<realm_async_error_t> error;
4✔
3792
                    bool destroyed = false;
4✔
3793
                };
4✔
3794

2✔
3795
                State state;
4✔
3796

2✔
3797
                auto on_change = [](void* userdata, const realm_collection_changes_t* changes) {
6✔
3798
                    auto* state = static_cast<State*>(userdata);
6✔
3799
                    state->changes = clone_cptr(changes);
6✔
3800
                };
6✔
3801

2✔
3802
                CPtr<realm_set_t> strings =
4✔
3803
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
4✔
3804

2✔
3805
                auto str1 = rlm_str_val("a");
4✔
3806
                auto str2 = rlm_str_val("b");
4✔
3807
                auto null = rlm_null();
4✔
3808

2✔
3809
                auto require_change = [&]() {
3✔
3810
                    auto token = cptr_checked(
2✔
3811
                        realm_set_add_notification_callback(strings.get(), &state, nullptr, nullptr, on_change));
2✔
3812
                    checked(realm_refresh(realm, nullptr));
2✔
3813
                    return token;
2✔
3814
                };
2✔
3815

2✔
3816
                SECTION("userdata is freed when the token is destroyed") {
4✔
3817
                    auto token = cptr_checked(realm_set_add_notification_callback(
2✔
3818
                        strings.get(), &state,
2✔
3819
                        [](void* p) {
2✔
3820
                            static_cast<State*>(p)->destroyed = true;
2✔
3821
                        },
2✔
3822
                        nullptr, nullptr));
2✔
3823
                    CHECK(!state.destroyed);
2!
3824
                    token.reset();
2✔
3825
                    CHECK(state.destroyed);
2!
3826
                }
2✔
3827

2✔
3828
                SECTION("insertion,deletion sends a change callback") {
4✔
3829
                    write([&]() {
2✔
3830
                        checked(realm_set_insert(strings.get(), str1, nullptr, nullptr));
2✔
3831
                    });
2✔
3832

1✔
3833
                    auto token = require_change();
2✔
3834
                    write([&]() {
2✔
3835
                        checked(realm_set_erase(strings.get(), str1, nullptr));
2✔
3836
                        checked(realm_set_insert(strings.get(), str2, nullptr, nullptr));
2✔
3837
                        checked(realm_set_insert(strings.get(), null, nullptr, nullptr));
2✔
3838
                    });
2✔
3839
                    CHECK(!state.error);
2!
3840
                    CHECK(state.changes);
2!
3841

1✔
3842
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
2✔
3843
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
2✔
3844
                                                            &num_insertion_ranges, &num_modification_ranges,
2✔
3845
                                                            &num_moves);
2✔
3846
                    CHECK(num_deletion_ranges == 1);
2!
3847
                    CHECK(num_insertion_ranges == 1);
2!
3848
                    CHECK(num_modification_ranges == 0);
2!
3849
                    CHECK(num_moves == 0);
2!
3850

1✔
3851
                    realm_index_range_t insertion_range, deletion_range;
2✔
3852
                    realm_collection_changes_get_ranges(state.changes.get(), &deletion_range, 1, &insertion_range, 1,
2✔
3853
                                                        nullptr, 0, nullptr, 0, nullptr, 0);
2✔
3854
                    CHECK(deletion_range.from == 0);
2!
3855
                    CHECK(deletion_range.to == 1);
2!
3856
                    CHECK(insertion_range.from == 0);
2!
3857
                    CHECK(insertion_range.to == 2);
2!
3858

1✔
3859
                    write([&]() {
2✔
3860
                        checked(realm_set_clear(strings.get()));
2✔
3861
                    });
2✔
3862

1✔
3863
                    size_t num_deletions, num_insertions, num_modifications;
2✔
3864
                    bool collection_cleared = false;
2✔
3865
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
2✔
3866
                                                             &num_modifications, &num_moves, &collection_cleared,
2✔
3867
                                                             nullptr);
2✔
3868
                    CHECK(collection_cleared == true);
2!
3869
                }
2✔
3870
            }
4✔
3871
        }
26✔
3872

99✔
3873
        SECTION("dictionaries") {
198✔
3874
            SECTION("realm_get_dictionary() errors") {
28✔
3875
                CHECK(!realm_get_dictionary(obj1.get(), foo_properties["int"]));
2!
3876
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3877

1✔
3878
                CHECK(!realm_get_dictionary(obj1.get(), 123123123));
2!
3879
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
3880
            }
2✔
3881

14✔
3882
            SECTION("nullable strings") {
28✔
3883
                auto strings = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
8✔
3884
                CHECK(strings);
8!
3885
                CHECK(!realm_is_frozen(strings.get()));
8!
3886

4✔
3887
                realm_value_t a = rlm_str_val("a");
8✔
3888
                realm_value_t b = rlm_str_val("b");
8✔
3889
                realm_value_t c = rlm_null();
8✔
3890
                realm_value_t key_a = rlm_str_val("key_a");
8✔
3891
                realm_value_t key_b = rlm_str_val("key_b");
8✔
3892
                realm_value_t key_c = rlm_str_val("key_c");
8✔
3893

4✔
3894
                SECTION("realm_equals() type check") {
8✔
3895
                    CHECK(!realm_equals(strings.get(), obj1.get()));
2!
3896
                }
2✔
3897

4✔
3898
                SECTION("realm_clone()") {
8✔
3899
                    auto dict2 = clone_cptr(strings.get());
2✔
3900
                    CHECK(realm_equals(strings.get(), dict2.get()));
2!
3901
                    CHECK(strings.get() != dict2.get());
2!
3902
                }
2✔
3903

4✔
3904
                SECTION("insert, then get, then erase") {
8✔
3905
                    write([&]() {
2✔
3906
                        bool inserted = false;
2✔
3907
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_a, a, nullptr, &inserted)));
2!
3908
                        CHECK(inserted);
2!
3909
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_b, b, nullptr, &inserted)));
2!
3910
                        CHECK(inserted);
2!
3911
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_c, c, nullptr, &inserted)));
2!
3912
                        CHECK(inserted);
2!
3913

1✔
3914
                        realm_value_t a2, b2, c2;
2✔
3915
                        bool found = false;
2✔
3916
                        CHECK(checked(realm_dictionary_find(strings.get(), key_a, &a2, &found)));
2!
3917
                        CHECK(found);
2!
3918
                        CHECK(checked(realm_dictionary_find(strings.get(), key_b, &b2, &found)));
2!
3919
                        CHECK(found);
2!
3920
                        CHECK(checked(realm_dictionary_find(strings.get(), key_c, &c2, &found)));
2!
3921
                        CHECK(found);
2!
3922

1✔
3923
                        CHECK(rlm_stdstr(a2) == "a");
2!
3924
                        CHECK(rlm_stdstr(b2) == "b");
2!
3925
                        CHECK(c2.type == RLM_TYPE_NULL);
2!
3926

1✔
3927
                        bool erased = false;
2✔
3928
                        CHECK(checked(realm_dictionary_erase(strings.get(), key_a, &erased)));
2!
3929
                        CHECK(erased);
2!
3930
                        CHECK(checked(realm_dictionary_erase(strings.get(), rlm_int_val(987), &erased)));
2!
3931
                        CHECK(!erased);
2!
3932
                    });
2✔
3933
                }
2✔
3934

4✔
3935
                SECTION("equality") {
8✔
3936
                    auto strings2 =
2✔
3937
                        cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
2✔
3938
                    CHECK(strings2);
2!
3939
                    CHECK(realm_equals(strings.get(), strings2.get()));
2!
3940

1✔
3941
                    write([&]() {
2✔
3942
                        auto obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
3943
                        CHECK(obj3);
2!
3944
                        auto strings3 =
2✔
3945
                            cptr_checked(realm_get_dictionary(obj3.get(), foo_properties["nullable_string_dict"]));
2✔
3946
                        CHECK(!realm_equals(strings.get(), strings3.get()));
2!
3947
                    });
2✔
3948
                }
2✔
3949
            }
8✔
3950

14✔
3951
            SECTION("get/insert all property types") {
28✔
3952
                realm_value_t key = rlm_str_val("k");
2✔
3953
                realm_value_t key2 = rlm_str_val("k2");
2✔
3954

1✔
3955
                realm_value_t null = rlm_null();
2✔
3956
                realm_value_t integer = rlm_int_val(987);
2✔
3957
                realm_value_t boolean = rlm_bool_val(true);
2✔
3958
                realm_value_t string = rlm_str_val("My string");
2✔
3959
                const uint8_t binary_data[] = {0, 1, 2, 3, 4, 5, 6, 7};
2✔
3960
                realm_value_t binary = rlm_binary_val(binary_data, sizeof(binary_data));
2✔
3961
                realm_value_t timestamp = rlm_timestamp_val(1000000, 123123123);
2✔
3962
                realm_value_t fnum = rlm_float_val(123.f);
2✔
3963
                realm_value_t dnum = rlm_double_val(456.0);
2✔
3964
                realm_value_t decimal = rlm_decimal_val(999.0);
2✔
3965
                realm_value_t object_id = rlm_object_id_val("abc123abc123");
2✔
3966
                realm_value_t uuid = rlm_uuid_val("01234567-9abc-4def-9012-3456789abcde");
2✔
3967

1✔
3968
                auto int_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["int_dict"]));
2✔
3969
                auto bool_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["bool_dict"]));
2✔
3970
                auto string_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["string_dict"]));
2✔
3971
                auto binary_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["binary_dict"]));
2✔
3972
                auto timestamp_dict =
2✔
3973
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["timestamp_dict"]));
2✔
3974
                auto float_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["float_dict"]));
2✔
3975
                auto double_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["double_dict"]));
2✔
3976
                auto decimal_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["decimal_dict"]));
2✔
3977
                auto object_id_dict =
2✔
3978
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["object_id_dict"]));
2✔
3979
                auto uuid_dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["uuid_dict"]));
2✔
3980
                auto nullable_int_dict =
2✔
3981
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_int_dict"]));
2✔
3982
                auto nullable_bool_dict =
2✔
3983
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_bool_dict"]));
2✔
3984
                auto nullable_string_dict =
2✔
3985
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
2✔
3986
                auto nullable_binary_dict =
2✔
3987
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_binary_dict"]));
2✔
3988
                auto nullable_timestamp_dict =
2✔
3989
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_timestamp_dict"]));
2✔
3990
                auto nullable_float_dict =
2✔
3991
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_float_dict"]));
2✔
3992
                auto nullable_double_dict =
2✔
3993
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_double_dict"]));
2✔
3994
                auto nullable_decimal_dict =
2✔
3995
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_decimal_dict"]));
2✔
3996
                auto nullable_object_id_dict =
2✔
3997
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_object_id_dict"]));
2✔
3998
                auto nullable_uuid_dict =
2✔
3999
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_uuid_dict"]));
2✔
4000

1✔
4001
                write([&]() {
2✔
4002
                    size_t index;
2✔
4003
                    bool inserted;
2✔
4004
                    CHECK(!realm_dictionary_insert(int_dict.get(), rlm_int_val(987), integer, &index, &inserted));
2!
4005

1✔
4006
                    CHECK(realm_dictionary_insert(int_dict.get(), key, integer, &index, &inserted));
2!
4007
                    CHECK((inserted && index == 0));
2!
4008
                    CHECK(realm_dictionary_insert(bool_dict.get(), key, boolean, &index, &inserted));
2!
4009
                    CHECK((inserted && index == 0));
2!
4010
                    CHECK(realm_dictionary_insert(string_dict.get(), key, string, &index, &inserted));
2!
4011
                    CHECK((inserted && index == 0));
2!
4012
                    CHECK(realm_dictionary_insert(binary_dict.get(), key, binary, &index, &inserted));
2!
4013
                    CHECK((inserted && index == 0));
2!
4014
                    CHECK(realm_dictionary_insert(timestamp_dict.get(), key, timestamp, &index, &inserted));
2!
4015
                    CHECK((inserted && index == 0));
2!
4016
                    CHECK(realm_dictionary_insert(float_dict.get(), key, fnum, &index, &inserted));
2!
4017
                    CHECK((inserted && index == 0));
2!
4018
                    CHECK(realm_dictionary_insert(double_dict.get(), key, dnum, &index, &inserted));
2!
4019
                    CHECK((inserted && index == 0));
2!
4020
                    CHECK(realm_dictionary_insert(decimal_dict.get(), key, decimal, &index, &inserted));
2!
4021
                    CHECK((inserted && index == 0));
2!
4022
                    CHECK(realm_dictionary_insert(object_id_dict.get(), key, object_id, &index, &inserted));
2!
4023
                    CHECK((inserted && index == 0));
2!
4024
                    CHECK(realm_dictionary_insert(uuid_dict.get(), key, uuid, &index, &inserted));
2!
4025
                    CHECK((inserted && index == 0));
2!
4026

1✔
4027
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key, integer, &index, &inserted));
2!
4028
                    CHECK((inserted && index == 0));
2!
4029
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key, boolean, &index, &inserted));
2!
4030
                    CHECK((inserted && index == 0));
2!
4031
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key, string, &index, &inserted));
2!
4032
                    CHECK((inserted && index == 0));
2!
4033
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key, binary, &index, &inserted));
2!
4034
                    CHECK((inserted && index == 0));
2!
4035
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key, timestamp, &index, &inserted));
2!
4036
                    CHECK((inserted && index == 0));
2!
4037
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key, fnum, &index, &inserted));
2!
4038
                    CHECK((inserted && index == 0));
2!
4039
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key, dnum, &index, &inserted));
2!
4040
                    CHECK((inserted && index == 0));
2!
4041
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key, decimal, &index, &inserted));
2!
4042
                    CHECK((inserted && index == 0));
2!
4043
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key, object_id, &index, &inserted));
2!
4044
                    CHECK((inserted && index == 0));
2!
4045
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key, uuid, &index, &inserted));
2!
4046
                    CHECK((inserted && index == 0));
2!
4047

1✔
4048
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key2, null, &index, &inserted));
2!
4049
                    CHECK((inserted && index == 1));
2!
4050
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key2, null, &index, &inserted));
2!
4051
                    CHECK((inserted && index == 1));
2!
4052
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key2, null, &index, &inserted));
2!
4053
                    CHECK((inserted && index == 1));
2!
4054
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key2, null, &index, &inserted));
2!
4055
                    CHECK((inserted && index == 1));
2!
4056
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key2, null, &index, &inserted));
2!
4057
                    CHECK((inserted && index == 1));
2!
4058
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key2, null, &index, &inserted));
2!
4059
                    CHECK((inserted && index == 1));
2!
4060
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key2, null, &index, &inserted));
2!
4061
                    CHECK((inserted && index == 1));
2!
4062
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key2, null, &index, &inserted));
2!
4063
                    CHECK((inserted && index == 1));
2!
4064
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key2, null, &index, &inserted));
2!
4065
                    CHECK((inserted && index == 1));
2!
4066
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key2, null, &index, &inserted));
2!
4067
                    CHECK((inserted && index == 1));
2!
4068
                });
2✔
4069

1✔
4070
                realm_value_t k, value;
2✔
4071

1✔
4072
                CHECK(realm_dictionary_get(int_dict.get(), 0, &k, &value));
2!
4073
                CHECK(rlm_val_eq(k, key));
2!
4074
                CHECK(rlm_val_eq(value, integer));
2!
4075
                CHECK(realm_dictionary_get(bool_dict.get(), 0, &k, &value));
2!
4076
                CHECK(rlm_val_eq(k, key));
2!
4077
                CHECK(rlm_val_eq(value, boolean));
2!
4078
                CHECK(realm_dictionary_get(string_dict.get(), 0, &k, &value));
2!
4079
                CHECK(rlm_val_eq(k, key));
2!
4080
                CHECK(rlm_val_eq(value, string));
2!
4081
                CHECK(realm_dictionary_get(binary_dict.get(), 0, &k, &value));
2!
4082
                CHECK(rlm_val_eq(k, key));
2!
4083
                CHECK(rlm_val_eq(value, binary));
2!
4084
                CHECK(realm_dictionary_get(timestamp_dict.get(), 0, &k, &value));
2!
4085
                CHECK(rlm_val_eq(k, key));
2!
4086
                CHECK(rlm_val_eq(value, timestamp));
2!
4087
                CHECK(realm_dictionary_get(float_dict.get(), 0, &k, &value));
2!
4088
                CHECK(rlm_val_eq(k, key));
2!
4089
                CHECK(rlm_val_eq(value, fnum));
2!
4090
                CHECK(realm_dictionary_get(double_dict.get(), 0, &k, &value));
2!
4091
                CHECK(rlm_val_eq(k, key));
2!
4092
                CHECK(rlm_val_eq(value, dnum));
2!
4093
                CHECK(realm_dictionary_get(decimal_dict.get(), 0, &k, &value));
2!
4094
                CHECK(rlm_val_eq(k, key));
2!
4095
                CHECK(rlm_val_eq(value, decimal));
2!
4096
                CHECK(realm_dictionary_get(object_id_dict.get(), 0, &k, &value));
2!
4097
                CHECK(rlm_val_eq(k, key));
2!
4098
                CHECK(rlm_val_eq(value, object_id));
2!
4099
                CHECK(realm_dictionary_get(uuid_dict.get(), 0, &k, &value));
2!
4100
                CHECK(rlm_val_eq(k, key));
2!
4101
                CHECK(rlm_val_eq(value, uuid));
2!
4102
                CHECK(realm_dictionary_get(nullable_int_dict.get(), 0, &k, &value));
2!
4103
                CHECK(rlm_val_eq(k, key));
2!
4104
                CHECK(rlm_val_eq(value, integer));
2!
4105
                CHECK(realm_dictionary_get(nullable_bool_dict.get(), 0, &k, &value));
2!
4106
                CHECK(rlm_val_eq(k, key));
2!
4107
                CHECK(rlm_val_eq(value, boolean));
2!
4108
                CHECK(realm_dictionary_get(nullable_string_dict.get(), 0, &k, &value));
2!
4109
                CHECK(rlm_val_eq(k, key));
2!
4110
                CHECK(rlm_val_eq(value, string));
2!
4111
                CHECK(realm_dictionary_get(nullable_binary_dict.get(), 0, &k, &value));
2!
4112
                CHECK(rlm_val_eq(k, key));
2!
4113
                CHECK(rlm_val_eq(value, binary));
2!
4114
                CHECK(realm_dictionary_get(nullable_timestamp_dict.get(), 0, &k, &value));
2!
4115
                CHECK(rlm_val_eq(k, key));
2!
4116
                CHECK(rlm_val_eq(value, timestamp));
2!
4117
                CHECK(realm_dictionary_get(nullable_float_dict.get(), 0, &k, &value));
2!
4118
                CHECK(rlm_val_eq(k, key));
2!
4119
                CHECK(rlm_val_eq(value, fnum));
2!
4120
                CHECK(realm_dictionary_get(nullable_double_dict.get(), 0, &k, &value));
2!
4121
                CHECK(rlm_val_eq(k, key));
2!
4122
                CHECK(rlm_val_eq(value, dnum));
2!
4123
                CHECK(realm_dictionary_get(nullable_decimal_dict.get(), 0, &k, &value));
2!
4124
                CHECK(rlm_val_eq(k, key));
2!
4125
                CHECK(rlm_val_eq(value, decimal));
2!
4126
                CHECK(realm_dictionary_get(nullable_object_id_dict.get(), 0, &k, &value));
2!
4127
                CHECK(rlm_val_eq(k, key));
2!
4128
                CHECK(rlm_val_eq(value, object_id));
2!
4129
                CHECK(realm_dictionary_get(nullable_uuid_dict.get(), 0, &k, &value));
2!
4130
                CHECK(rlm_val_eq(k, key));
2!
4131
                CHECK(rlm_val_eq(value, uuid));
2!
4132

1✔
4133
                write([&]() {
2✔
4134
                    size_t index;
2✔
4135
                    bool inserted;
2✔
4136
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key2, null, &index, &inserted));
2!
4137
                    CHECK(!inserted);
2!
4138
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key2, null, &index, &inserted));
2!
4139
                    CHECK(!inserted);
2!
4140
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key2, null, &index, &inserted));
2!
4141
                    CHECK(!inserted);
2!
4142
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key2, null, &index, &inserted));
2!
4143
                    CHECK(!inserted);
2!
4144
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key2, null, &index, &inserted));
2!
4145
                    CHECK(!inserted);
2!
4146
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key2, null, &index, &inserted));
2!
4147
                    CHECK(!inserted);
2!
4148
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key2, null, &index, &inserted));
2!
4149
                    CHECK(!inserted);
2!
4150
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key2, null, &index, &inserted));
2!
4151
                    CHECK(!inserted);
2!
4152
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key2, null, &index, &inserted));
2!
4153
                    CHECK(!inserted);
2!
4154
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key2, null, &index, &inserted));
2!
4155
                    CHECK(!inserted);
2!
4156
                });
2✔
4157

1✔
4158
                CHECK(realm_dictionary_find(int_dict.get(), rlm_int_val(987), &value, &found));
2!
4159
                CHECK(!found);
2!
4160
                CHECK(realm_dictionary_find(int_dict.get(), rlm_str_val("Boogeyman"), &value, &found));
2!
4161
                CHECK(!found);
2!
4162
                CHECK(realm_dictionary_find(int_dict.get(), key, &value, &found));
2!
4163
                CHECK(found);
2!
4164
                CHECK(rlm_val_eq(value, integer));
2!
4165
                CHECK(realm_dictionary_find(bool_dict.get(), key, &value, &found));
2!
4166
                CHECK(found);
2!
4167
                CHECK(rlm_val_eq(value, boolean));
2!
4168
                CHECK(realm_dictionary_find(string_dict.get(), key, &value, &found));
2!
4169
                CHECK(found);
2!
4170
                CHECK(rlm_val_eq(value, string));
2!
4171
                CHECK(realm_dictionary_find(binary_dict.get(), key, &value, &found));
2!
4172
                CHECK(found);
2!
4173
                CHECK(rlm_val_eq(value, binary));
2!
4174
                CHECK(realm_dictionary_find(timestamp_dict.get(), key, &value, &found));
2!
4175
                CHECK(found);
2!
4176
                CHECK(rlm_val_eq(value, timestamp));
2!
4177
                CHECK(realm_dictionary_find(float_dict.get(), key, &value, &found));
2!
4178
                CHECK(found);
2!
4179
                CHECK(rlm_val_eq(value, fnum));
2!
4180
                CHECK(realm_dictionary_find(double_dict.get(), key, &value, &found));
2!
4181
                CHECK(found);
2!
4182
                CHECK(rlm_val_eq(value, dnum));
2!
4183
                CHECK(realm_dictionary_find(decimal_dict.get(), key, &value, &found));
2!
4184
                CHECK(found);
2!
4185
                CHECK(rlm_val_eq(value, decimal));
2!
4186
                CHECK(realm_dictionary_find(object_id_dict.get(), key, &value, &found));
2!
4187
                CHECK(found);
2!
4188
                CHECK(rlm_val_eq(value, object_id));
2!
4189
                CHECK(realm_dictionary_find(uuid_dict.get(), key, &value, &found));
2!
4190
                CHECK(found);
2!
4191
                CHECK(rlm_val_eq(value, uuid));
2!
4192
                CHECK(realm_dictionary_find(nullable_int_dict.get(), key, &value, &found));
2!
4193
                CHECK(found);
2!
4194
                CHECK(rlm_val_eq(value, integer));
2!
4195
                CHECK(realm_dictionary_find(nullable_bool_dict.get(), key, &value, &found));
2!
4196
                CHECK(found);
2!
4197
                CHECK(rlm_val_eq(value, boolean));
2!
4198
                CHECK(realm_dictionary_find(nullable_string_dict.get(), key, &value, &found));
2!
4199
                CHECK(found);
2!
4200
                CHECK(rlm_val_eq(value, string));
2!
4201
                CHECK(realm_dictionary_find(nullable_binary_dict.get(), key, &value, &found));
2!
4202
                CHECK(found);
2!
4203
                CHECK(rlm_val_eq(value, binary));
2!
4204
                CHECK(realm_dictionary_find(nullable_timestamp_dict.get(), key, &value, &found));
2!
4205
                CHECK(found);
2!
4206
                CHECK(rlm_val_eq(value, timestamp));
2!
4207
                CHECK(realm_dictionary_find(nullable_float_dict.get(), key, &value, &found));
2!
4208
                CHECK(found);
2!
4209
                CHECK(rlm_val_eq(value, fnum));
2!
4210
                CHECK(realm_dictionary_find(nullable_double_dict.get(), key, &value, &found));
2!
4211
                CHECK(found);
2!
4212
                CHECK(rlm_val_eq(value, dnum));
2!
4213
                CHECK(realm_dictionary_find(nullable_decimal_dict.get(), key, &value, &found));
2!
4214
                CHECK(found);
2!
4215
                CHECK(rlm_val_eq(value, decimal));
2!
4216
                CHECK(realm_dictionary_find(nullable_object_id_dict.get(), key, &value, &found));
2!
4217
                CHECK(found);
2!
4218
                CHECK(rlm_val_eq(value, object_id));
2!
4219
                CHECK(realm_dictionary_find(nullable_uuid_dict.get(), key, &value, &found));
2!
4220
                CHECK(found);
2!
4221
                CHECK(rlm_val_eq(value, uuid));
2!
4222

1✔
4223
                CHECK(realm_dictionary_find(nullable_int_dict.get(), key2, &value, &found));
2!
4224
                CHECK(found);
2!
4225
                CHECK(rlm_val_eq(value, null));
2!
4226
                CHECK(realm_dictionary_find(nullable_bool_dict.get(), key2, &value, &found));
2!
4227
                CHECK(found);
2!
4228
                CHECK(rlm_val_eq(value, null));
2!
4229
                CHECK(realm_dictionary_find(nullable_string_dict.get(), key2, &value, &found));
2!
4230
                CHECK(found);
2!
4231
                CHECK(rlm_val_eq(value, null));
2!
4232
                CHECK(realm_dictionary_find(nullable_binary_dict.get(), key2, &value, &found));
2!
4233
                CHECK(found);
2!
4234
                CHECK(rlm_val_eq(value, null));
2!
4235
                CHECK(realm_dictionary_find(nullable_timestamp_dict.get(), key2, &value, &found));
2!
4236
                CHECK(found);
2!
4237
                CHECK(rlm_val_eq(value, null));
2!
4238
                CHECK(realm_dictionary_find(nullable_float_dict.get(), key2, &value, &found));
2!
4239
                CHECK(found);
2!
4240
                CHECK(rlm_val_eq(value, null));
2!
4241
                CHECK(realm_dictionary_find(nullable_double_dict.get(), key2, &value, &found));
2!
4242
                CHECK(found);
2!
4243
                CHECK(rlm_val_eq(value, null));
2!
4244
                CHECK(realm_dictionary_find(nullable_decimal_dict.get(), key2, &value, &found));
2!
4245
                CHECK(found);
2!
4246
                CHECK(rlm_val_eq(value, null));
2!
4247
                CHECK(realm_dictionary_find(nullable_object_id_dict.get(), key2, &value, &found));
2!
4248
                CHECK(found);
2!
4249
                CHECK(rlm_val_eq(value, null));
2!
4250
                CHECK(realm_dictionary_find(nullable_uuid_dict.get(), key2, &value, &found));
2!
4251
                CHECK(found);
2!
4252
                CHECK(rlm_val_eq(value, null));
2!
4253
            }
2✔
4254

14✔
4255
            SECTION("links") {
28✔
4256
                CPtr<realm_dictionary_t> bars;
6✔
4257
                realm_value_t key = rlm_str_val("k");
6✔
4258

3✔
4259
                write([&]() {
6✔
4260
                    bars = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["link_dict"]));
6✔
4261
                    auto bar_link = realm_object_as_link(obj2.get());
6✔
4262
                    realm_value_t bar_link_val;
6✔
4263
                    bar_link_val.type = RLM_TYPE_LINK;
6✔
4264
                    bar_link_val.link = bar_link;
6✔
4265
                    size_t index;
6✔
4266
                    bool inserted;
6✔
4267
                    CHECK(checked(realm_dictionary_insert(bars.get(), key, bar_link_val, &index, &inserted)));
6!
4268
                    CHECK((index == 0 && inserted));
6!
4269
                    CHECK(checked(realm_dictionary_insert(bars.get(), key, bar_link_val, &index, &inserted)));
6!
4270
                    CHECK((index == 0 && !inserted));
6!
4271
                    size_t size;
6✔
4272
                    CHECK(checked(realm_dictionary_size(bars.get(), &size)));
6!
4273
                    CHECK(size == 1);
6!
4274
                });
6✔
4275

3✔
4276
                SECTION("get") {
6✔
4277
                    realm_value_t k, val;
2✔
4278
                    CHECK(checked(realm_dictionary_get(bars.get(), 0, &k, &val)));
2!
4279
                    CHECK(rlm_val_eq(k, key));
2!
4280
                    CHECK(val.type == RLM_TYPE_LINK);
2!
4281
                    CHECK(val.link.target_table == class_bar.key);
2!
4282
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
4283

1✔
4284
                    auto result = realm_dictionary_get(bars.get(), 1, &k, &val);
2✔
4285
                    CHECK(!result);
2!
4286
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
2!
4287
                }
2✔
4288

3✔
4289
                SECTION("insert wrong type") {
6✔
4290
                    write([&]() {
2✔
4291
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
2✔
4292
                        CHECK(foo2);
2!
4293
                        realm_value_t foo2_link_val;
2✔
4294
                        foo2_link_val.type = RLM_TYPE_LINK;
2✔
4295
                        foo2_link_val.link = realm_object_as_link(foo2.get());
2✔
4296

1✔
4297
                        CHECK(!realm_dictionary_insert(bars.get(), key, foo2_link_val, nullptr, nullptr));
2!
4298
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
4299
                    });
2✔
4300
                }
2✔
4301

3✔
4302
                SECTION("realm_dictionary_clear()") {
6✔
4303
                    write([&]() {
2✔
4304
                        CHECK(realm_dictionary_clear(bars.get()));
2!
4305
                    });
2✔
4306
                    size_t size;
2✔
4307
                    CHECK(realm_dictionary_size(bars.get(), &size));
2!
4308
                    CHECK(size == 0);
2!
4309

1✔
4310
                    size_t num_bars;
2✔
4311
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
4312
                    CHECK(num_bars != 0);
2!
4313
                }
2✔
4314
            }
6✔
4315

14✔
4316
            SECTION("notifications") {
28✔
4317
                struct State {
4✔
4318
                    CPtr<realm_collection_changes_t> changes;
4✔
4319
                    CPtr<realm_dictionary_changes_t> dictionary_changes;
4✔
4320
                    CPtr<realm_async_error_t> error;
4✔
4321
                    bool destroyed = false;
4✔
4322
                };
4✔
4323

2✔
4324
                State state;
4✔
4325
                auto on_dictionary_change = [](void* userdata, const realm_dictionary_changes_t* changes) {
6✔
4326
                    auto* state = static_cast<State*>(userdata);
6✔
4327
                    state->dictionary_changes = clone_cptr(changes);
6✔
4328
                };
6✔
4329

2✔
4330
                CPtr<realm_dictionary_t> strings =
4✔
4331
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
4✔
4332

2✔
4333
                auto str1 = rlm_str_val("a");
4✔
4334
                auto str2 = rlm_str_val("b");
4✔
4335
                auto null = rlm_null();
4✔
4336

2✔
4337

2✔
4338
                auto require_change = [&]() {
3✔
4339
                    auto token = cptr_checked(realm_dictionary_add_notification_callback(
2✔
4340
                        strings.get(), &state, nullptr, nullptr, on_dictionary_change));
2✔
4341
                    checked(realm_refresh(realm, nullptr));
2✔
4342
                    return token;
2✔
4343
                };
2✔
4344

2✔
4345
                SECTION("userdata is freed when the token is destroyed") {
4✔
4346
                    auto token = cptr_checked(realm_dictionary_add_notification_callback(
2✔
4347
                        strings.get(), &state,
2✔
4348
                        [](void* p) {
2✔
4349
                            static_cast<State*>(p)->destroyed = true;
2✔
4350
                        },
2✔
4351
                        nullptr, nullptr));
2✔
4352
                    CHECK(!state.destroyed);
2!
4353
                    token.reset();
2✔
4354
                    CHECK(state.destroyed);
2!
4355
                }
2✔
4356

2✔
4357
                SECTION("insertion, deletions sends a change callback") {
4✔
4358
                    write([&]() {
2✔
4359
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("a"), str1, nullptr, nullptr));
2✔
4360
                    });
2✔
4361
                    auto token = require_change();
2✔
4362
                    write([&]() {
2✔
4363
                        checked(realm_dictionary_erase(strings.get(), rlm_str_val("a"), nullptr));
2✔
4364
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("b"), str2, nullptr, nullptr));
2✔
4365
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("c"), null, nullptr, nullptr));
2✔
4366
                    });
2✔
4367
                    CHECK(!state.error);
2!
4368
                    CHECK(state.dictionary_changes);
2!
4369

1✔
4370
                    size_t num_deletions, num_insertions, num_modifications;
2✔
4371
                    realm_dictionary_get_changes(state.dictionary_changes.get(), &num_deletions, &num_insertions,
2✔
4372
                                                 &num_modifications, nullptr);
2✔
4373
                    CHECK(num_deletions == 1);
2!
4374
                    CHECK(num_insertions == 2);
2!
4375
                    CHECK(num_modifications == 0);
2!
4376
                    realm_value_t *deletions = nullptr, *insertions = nullptr, *modifications = nullptr;
2✔
4377
                    bool collection_cleared = false;
2✔
4378
                    deletions = (realm_value_t*)malloc(sizeof(realm_value_t) * num_deletions);
2✔
4379
                    insertions = (realm_value_t*)malloc(sizeof(realm_value_t) * num_insertions);
2✔
4380
                    realm_dictionary_get_changed_keys(state.dictionary_changes.get(), deletions, &num_deletions,
2✔
4381
                                                      insertions, &num_insertions, modifications, &num_modifications,
2✔
4382
                                                      &collection_cleared);
2✔
4383
                    CHECK(deletions != nullptr);
2!
4384
                    CHECK(insertions != nullptr);
2!
4385
                    CHECK(modifications == nullptr);
2!
4386
                    realm_free(deletions);
2✔
4387
                    realm_free(insertions);
2✔
4388
                    realm_free(modifications);
2✔
4389

1✔
4390
                    write([&]() {
2✔
4391
                        checked(realm_dictionary_clear(strings.get()));
2✔
4392
                    });
2✔
4393
                    realm_dictionary_get_changed_keys(state.dictionary_changes.get(), deletions, &num_deletions,
2✔
4394
                                                      insertions, &num_insertions, modifications, &num_modifications,
2✔
4395
                                                      &collection_cleared);
2✔
4396
                    CHECK(collection_cleared == true);
2!
4397
                }
2✔
4398
            }
4✔
4399

14✔
4400
            SECTION("realm_dictionary_content_checks") {
28✔
4401
                auto ints = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["int_dict"]));
6✔
4402
                CHECK(ints);
6!
4403
                CHECK(!realm_is_frozen(ints.get()));
6!
4404
                realm_value_t key1 = rlm_str_val("k");
6✔
4405
                realm_value_t key2 = rlm_str_val("k2");
6✔
4406
                realm_value_t integer1 = rlm_int_val(987);
6✔
4407
                realm_value_t integer2 = rlm_int_val(988);
6✔
4408

3✔
4409
                write([&]() {
6✔
4410
                    bool inserted = false;
6✔
4411
                    CHECK(checked(realm_dictionary_insert(ints.get(), key1, integer1, nullptr, &inserted)));
6!
4412
                    CHECK(inserted);
6!
4413
                    CHECK(checked(realm_dictionary_insert(ints.get(), key2, integer2, nullptr, &inserted)));
6!
4414
                    CHECK(inserted);
6!
4415
                });
6✔
4416

3✔
4417
                SECTION("realm_dictionary_get_keys") {
6✔
4418
                    size_t size = 0;
2✔
4419
                    realm_results_t* keys = nullptr;
2✔
4420
                    CHECK(checked(realm_dictionary_get_keys(ints.get(), &size, &keys)));
2!
4421
                    CHECK(keys);
2!
4422
                    CHECK((*keys).size() == size);
2!
4423
                    realm_release(keys);
2✔
4424
                }
2✔
4425

3✔
4426
                SECTION("realm_dictionary_contains_key") {
6✔
4427
                    bool found = false;
2✔
4428
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key1, &found)));
2!
4429
                    CHECK(found);
2!
4430
                    found = false;
2✔
4431
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key2, &found)));
2!
4432
                    CHECK(found);
2!
4433
                    realm_value_t key_no_present = rlm_str_val("kkkk");
2✔
4434
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key_no_present, &found)));
2!
4435
                    CHECK(!found);
2!
4436
                }
2✔
4437

3✔
4438
                SECTION("realm_dictionary_contains_value") {
6✔
4439
                    size_t index = -1;
2✔
4440
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer1, &index)));
2!
4441
                    CHECK(index == 0);
2!
4442
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer2, &index)));
2!
4443
                    CHECK(index == 1);
2!
4444
                    realm_value_t integer_no_present = rlm_int_val(678);
2✔
4445
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer_no_present, &index)));
2!
4446
                    CHECK(index == realm::npos);
2!
4447
                }
2✔
4448
            }
6✔
4449
        }
28✔
4450

99✔
4451
        SECTION("notifications") {
198✔
4452
            struct State {
6✔
4453
                CPtr<realm_object_changes_t> changes;
6✔
4454
                CPtr<realm_async_error_t> error;
6✔
4455
                bool called;
6✔
4456
            };
6✔
4457

3✔
4458
            State state;
6✔
4459

3✔
4460
            auto on_change = [](void* userdata, const realm_object_changes_t* changes) {
12✔
4461
                auto state = static_cast<State*>(userdata);
12✔
4462
                state->changes = clone_cptr(changes);
12✔
4463
                state->called = true;
12✔
4464
            };
12✔
4465

3✔
4466
            auto require_change = [&]() {
5✔
4467
                auto token =
4✔
4468
                    cptr(realm_object_add_notification_callback(obj1.get(), &state, nullptr, nullptr, on_change));
4✔
4469
                checked(realm_refresh(realm, nullptr));
4✔
4470
                return token;
4✔
4471
            };
4✔
4472

3✔
4473
            SECTION("deleting the object sends a change notification") {
6✔
4474
                auto token = require_change();
2✔
4475
                write([&]() {
2✔
4476
                    checked(realm_object_delete(obj1.get()));
2✔
4477
                });
2✔
4478
                CHECK(!state.error);
2!
4479
                CHECK(state.changes);
2!
4480
                bool deleted = realm_object_changes_is_deleted(state.changes.get());
2✔
4481
                CHECK(deleted);
2!
4482
            }
2✔
4483

3✔
4484
            SECTION("modifying the object sends a change notification for the object, and for the changed column") {
6✔
4485
                auto token = require_change();
2✔
4486
                write([&]() {
2✔
4487
                    checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
2✔
4488
                    checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("aaa"), false));
2✔
4489
                });
2✔
4490
                CHECK(!state.error);
2!
4491
                CHECK(state.changes);
2!
4492
                bool deleted = realm_object_changes_is_deleted(state.changes.get());
2✔
4493
                CHECK(!deleted);
2!
4494
                size_t num_modified = realm_object_changes_get_num_modified_properties(state.changes.get());
2✔
4495
                CHECK(num_modified == 2);
2!
4496
                realm_property_key_t modified_keys[2];
2✔
4497
                size_t n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 2);
2✔
4498
                CHECK(n == 2);
2!
4499
                CHECK(modified_keys[0] == foo_int_key);
2!
4500
                CHECK(modified_keys[1] == foo_str_key);
2!
4501

1✔
4502
                n = realm_object_changes_get_modified_properties(state.changes.get(), nullptr, 2);
2✔
4503
                CHECK(n == 2);
2!
4504

1✔
4505
                n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 0);
2✔
4506
                CHECK(n == 0);
2!
4507
            }
2✔
4508
            SECTION("modifying the object while observing a specific value") {
6✔
4509
                realm_key_path_elem_t origin_value[] = {{class_foo.key, foo_int_key}};
2✔
4510
                realm_key_path_t key_path_origin_value[] = {{1, origin_value}};
2✔
4511
                realm_key_path_array_t key_path_array = {1, key_path_origin_value};
2✔
4512
                auto token = cptr(
2✔
4513
                    realm_object_add_notification_callback(obj1.get(), &state, nullptr, &key_path_array, on_change));
2✔
4514
                checked(realm_refresh(realm, nullptr));
2✔
4515
                state.called = false;
2✔
4516
                write([&]() {
2✔
4517
                    checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
2✔
4518
                });
2✔
4519
                REQUIRE(state.called);
2!
4520
                CHECK(!state.error);
2!
4521
                CHECK(state.changes);
2!
4522
                realm_property_key_t modified_keys[2];
2✔
4523
                size_t n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 2);
2✔
4524
                CHECK(n == 1);
2!
4525
                CHECK(modified_keys[0] == foo_int_key);
2!
4526

1✔
4527
                state.called = false;
2✔
4528
                write([&]() {
2✔
4529
                    // checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
1✔
4530
                    checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("aaa"), false));
2✔
4531
                });
2✔
4532
                REQUIRE(!state.called);
2!
4533
            }
2✔
4534
        }
6✔
4535
    }
198✔
4536

149✔
4537
    SECTION("threads") {
298✔
4538
        CPtr<realm_object_t> foo_obj, bar_obj;
10✔
4539
        write([&]() {
10✔
4540
            foo_obj = cptr_checked(realm_object_create(realm, class_foo.key));
10✔
4541
            realm_set_value(foo_obj.get(), foo_int_key, rlm_int_val(123), false);
10✔
4542
            bar_obj = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123)));
10✔
4543
        });
10✔
4544

5✔
4545
        auto list = cptr_checked(realm_get_list(foo_obj.get(), foo_properties["int_list"]));
10✔
4546
        auto set = cptr_checked(realm_get_set(foo_obj.get(), foo_properties["int_set"]));
10✔
4547
        auto dictionary = cptr_checked(realm_get_dictionary(foo_obj.get(), foo_properties["int_dict"]));
10✔
4548
        auto results = cptr_checked(realm_object_find_all(realm, class_foo.key));
10✔
4549

5✔
4550
        SECTION("wrong thread") {
10✔
4551
            std::thread t{[&]() {
2✔
4552
                realm_value_t val;
2✔
4553
                CHECK(!realm_get_value(foo_obj.get(), foo_int_key, &val));
2!
4554
                CHECK_ERR(RLM_ERR_WRONG_THREAD);
2!
4555
            }};
2✔
4556

1✔
4557
            t.join();
2✔
4558
        }
2✔
4559

5✔
4560
        SECTION("thread-safe references") {
10✔
4561
            auto foo_obj_tsr = cptr_checked(realm_create_thread_safe_reference(foo_obj.get()));
8✔
4562
            auto bar_obj_tsr = cptr_checked(realm_create_thread_safe_reference(bar_obj.get()));
8✔
4563
            auto list_tsr = cptr_checked(realm_create_thread_safe_reference(list.get()));
8✔
4564
            auto set_tsr = cptr_checked(realm_create_thread_safe_reference(set.get()));
8✔
4565
            auto dict_tsr = cptr_checked(realm_create_thread_safe_reference(dictionary.get()));
8✔
4566
            auto results_tsr = cptr_checked(realm_create_thread_safe_reference(results.get()));
8✔
4567

4✔
4568
            SECTION("resolve") {
8✔
4569
                std::thread t{[&]() {
2✔
4570
                    auto config = make_config(test_file.path.c_str());
2✔
4571
                    auto realm2 = cptr_checked(realm_open(config.get()));
2✔
4572
                    auto foo_obj2 =
2✔
4573
                        cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), foo_obj_tsr.get()));
2✔
4574
                    auto bar_obj2 =
2✔
4575
                        cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), bar_obj_tsr.get()));
2✔
4576
                    auto results2 =
2✔
4577
                        cptr_checked(realm_results_from_thread_safe_reference(realm2.get(), results_tsr.get()));
2✔
4578
                    auto list2 = cptr_checked(realm_list_from_thread_safe_reference(realm2.get(), list_tsr.get()));
2✔
4579
                    auto set2 = cptr_checked(realm_set_from_thread_safe_reference(realm2.get(), set_tsr.get()));
2✔
4580
                    auto dict2 =
2✔
4581
                        cptr_checked(realm_dictionary_from_thread_safe_reference(realm2.get(), dict_tsr.get()));
2✔
4582

1✔
4583
                    realm_value_t foo_obj_int;
2✔
4584
                    CHECK(realm_get_value(foo_obj2.get(), foo_int_key, &foo_obj_int));
2!
4585
                    CHECK(rlm_val_eq(foo_obj_int, rlm_int_val(123)));
2!
4586

1✔
4587
                    size_t count;
2✔
4588
                    CHECK(realm_results_count(results2.get(), &count));
2!
4589
                    CHECK(count == 1);
2!
4590
                }};
2✔
4591

1✔
4592
                t.join();
2✔
4593
            }
2✔
4594

4✔
4595
            SECTION("resolve in frozen") {
8✔
4596
                auto realm2 = cptr_checked(realm_freeze(realm));
2✔
4597
                CHECK(realm_is_frozen(realm2.get()));
2!
4598
                CHECK(realm != realm2.get());
2!
4599

1✔
4600
                auto foo_obj2 =
2✔
4601
                    cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), foo_obj_tsr.get()));
2✔
4602
                CHECK(realm_is_frozen(foo_obj2.get()));
2!
4603
            }
2✔
4604

4✔
4605
            SECTION("type error") {
8✔
4606
                CHECK(!realm_object_from_thread_safe_reference(realm, list_tsr.get()));
2!
4607
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4608
                CHECK(!realm_list_from_thread_safe_reference(realm, foo_obj_tsr.get()));
2!
4609
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4610
                CHECK(!realm_set_from_thread_safe_reference(realm, list_tsr.get()));
2!
4611
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4612
                CHECK(!realm_dictionary_from_thread_safe_reference(realm, set_tsr.get()));
2!
4613
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4614
                CHECK(!realm_results_from_thread_safe_reference(realm, list_tsr.get()));
2!
4615
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4616
                CHECK(!realm_from_thread_safe_reference(list_tsr.get(), nullptr));
2!
4617
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4618
            }
2✔
4619

4✔
4620
            SECTION("non-sendable") {
8✔
4621
                auto c = cptr(realm_config_new());
2✔
4622
                CHECK(!realm_create_thread_safe_reference(c.get()));
2!
4623
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4624
            }
2✔
4625
        }
8✔
4626
    }
10✔
4627

149✔
4628
    SECTION("freeze and thaw") {
298✔
4629
        SECTION("realm") {
12✔
4630
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4631
            CHECK(!realm_is_frozen(realm));
2!
4632
            CHECK(realm_is_frozen(frozen_realm.get()));
2!
4633
        }
2✔
4634

6✔
4635
        SECTION("objects") {
12✔
4636
            CPtr<realm_object_t> obj1;
2✔
4637
            realm_value_t value;
2✔
4638

1✔
4639
            write([&]() {
2✔
4640
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4641
                CHECK(obj1);
2!
4642
            });
2✔
4643
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
2!
4644
            CHECK(value.type == RLM_TYPE_STRING);
2!
4645
            CHECK(strncmp(value.string.data, "", value.string.size) == 0);
2!
4646

1✔
4647
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4648
            realm_object_t* frozen_obj1;
2✔
4649
            CHECK(realm_object_resolve_in(obj1.get(), frozen_realm.get(), &frozen_obj1));
2!
4650

1✔
4651
            write([&]() {
2✔
4652
                CHECK(checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("Hello, World!"), false)));
2!
4653
            });
2✔
4654

1✔
4655
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
2!
4656
            CHECK(value.type == RLM_TYPE_STRING);
2!
4657
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
2!
4658

1✔
4659
            CHECK(checked(realm_get_value(frozen_obj1, foo_str_key, &value)));
2!
4660
            CHECK(value.type == RLM_TYPE_STRING);
2!
4661
            CHECK(strncmp(value.string.data, "", value.string.size) == 0);
2!
4662
            realm_object_t* thawed_obj1;
2✔
4663
            CHECK(realm_object_resolve_in(obj1.get(), realm, &thawed_obj1));
2!
4664
            CHECK(thawed_obj1);
2!
4665
            CHECK(checked(realm_get_value(thawed_obj1, foo_str_key, &value)));
2!
4666
            CHECK(value.type == RLM_TYPE_STRING);
2!
4667
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
2!
4668

1✔
4669
            write([&]() {
2✔
4670
                CHECK(checked(realm_object_delete(obj1.get())));
2!
4671
            });
2✔
4672
            realm_object_t* deleted_obj;
2✔
4673
            auto b = realm_object_resolve_in(frozen_obj1, realm, &deleted_obj);
2✔
4674
            CHECK(b);
2!
4675
            CHECK(deleted_obj == nullptr);
2!
4676
            realm_release(frozen_obj1);
2✔
4677
            realm_release(thawed_obj1);
2✔
4678
        }
2✔
4679

6✔
4680
        SECTION("results") {
12✔
4681
            auto results = cptr_checked(realm_object_find_all(realm, class_foo.key));
2✔
4682
            realm_results_delete_all(results.get());
2✔
4683

1✔
4684
            write([&]() {
2✔
4685
                // Ensure that we start from a known initial state
1✔
4686
                CHECK(realm_results_delete_all(results.get()));
2!
4687

1✔
4688
                auto obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4689
                CHECK(obj1);
2!
4690
            });
2✔
4691

1✔
4692
            size_t count;
2✔
4693
            realm_results_count(results.get(), &count);
2✔
4694
            CHECK(count == 1);
2!
4695

1✔
4696
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4697
            auto frozen_results = cptr_checked(realm_results_resolve_in(results.get(), frozen_realm.get()));
2✔
4698
            write([&]() {
2✔
4699
                auto obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4700
                CHECK(obj1);
2!
4701
            });
2✔
4702
            realm_results_count(frozen_results.get(), &count);
2✔
4703
            CHECK(count == 1);
2!
4704
            realm_results_count(results.get(), &count);
2✔
4705
            CHECK(count == 2);
2!
4706

1✔
4707
            auto thawed_results = cptr_checked(realm_results_resolve_in(frozen_results.get(), realm));
2✔
4708
            realm_results_count(thawed_results.get(), &count);
2✔
4709
            CHECK(count == 2);
2!
4710
        }
2✔
4711

6✔
4712
        SECTION("lists") {
12✔
4713
            CPtr<realm_object_t> obj1;
2✔
4714
            size_t count;
2✔
4715

1✔
4716
            write([&]() {
2✔
4717
                obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(1)));
2✔
4718
                CHECK(obj1);
2!
4719
            });
2✔
4720

1✔
4721
            auto list = cptr_checked(realm_get_list(obj1.get(), bar_properties["strings"]));
2✔
4722
            realm_list_size(list.get(), &count);
2✔
4723
            CHECK(count == 0);
2!
4724

1✔
4725
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4726
            realm_list_t* frozen_list;
2✔
4727
            CHECK(realm_list_resolve_in(list.get(), frozen_realm.get(), &frozen_list));
2!
4728
            realm_list_size(frozen_list, &count);
2✔
4729
            CHECK(count == 0);
2!
4730

1✔
4731
            write([&]() {
2✔
4732
                checked(realm_list_insert(list.get(), 0, rlm_str_val("Hello")));
2✔
4733
            });
2✔
4734

1✔
4735
            realm_list_size(frozen_list, &count);
2✔
4736
            CHECK(count == 0);
2!
4737
            realm_list_size(list.get(), &count);
2✔
4738
            CHECK(count == 1);
2!
4739

1✔
4740
            realm_list_t* thawed_list;
2✔
4741
            CHECK(realm_list_resolve_in(frozen_list, realm, &thawed_list));
2!
4742
            realm_list_size(thawed_list, &count);
2✔
4743
            CHECK(count == 1);
2!
4744

1✔
4745
            CHECK(realm_list_is_valid(thawed_list));
2!
4746
            write([&]() {
2✔
4747
                CHECK(checked(realm_object_delete(obj1.get())));
2!
4748
            });
2✔
4749
            CHECK(!realm_list_is_valid(thawed_list));
2!
4750
            realm_release(thawed_list);
2✔
4751
            CHECK(realm_list_resolve_in(frozen_list, realm, &thawed_list));
2!
4752
            CHECK(thawed_list == nullptr);
2!
4753
            realm_release(frozen_list);
2✔
4754
        }
2✔
4755

6✔
4756
        SECTION("sets") {
12✔
4757
            CPtr<realm_object_t> obj1;
2✔
4758
            size_t count;
2✔
4759

1✔
4760
            write([&]() {
2✔
4761
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4762
                CHECK(obj1);
2!
4763
            });
2✔
4764

1✔
4765
            auto set = cptr_checked(realm_get_set(obj1.get(), foo_properties["string_set"]));
2✔
4766
            realm_set_size(set.get(), &count);
2✔
4767
            CHECK(count == 0);
2!
4768

1✔
4769
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4770
            realm_set_t* frozen_set;
2✔
4771
            CHECK(realm_set_resolve_in(set.get(), frozen_realm.get(), &frozen_set));
2!
4772
            realm_set_size(frozen_set, &count);
2✔
4773
            CHECK(count == 0);
2!
4774

1✔
4775
            write([&]() {
2✔
4776
                checked(realm_set_insert(set.get(), rlm_str_val("Hello"), nullptr, nullptr));
2✔
4777
            });
2✔
4778

1✔
4779
            realm_set_size(frozen_set, &count);
2✔
4780
            CHECK(count == 0);
2!
4781
            realm_set_size(set.get(), &count);
2✔
4782
            CHECK(count == 1);
2!
4783

1✔
4784
            realm_set_t* thawed_set;
2✔
4785
            CHECK(realm_set_resolve_in(frozen_set, realm, &thawed_set));
2!
4786
            realm_set_size(thawed_set, &count);
2✔
4787
            CHECK(count == 1);
2!
4788

1✔
4789
            CHECK(realm_set_is_valid(thawed_set));
2!
4790
            write([&]() {
2✔
4791
                CHECK(checked(realm_object_delete(obj1.get())));
2!
4792
            });
2✔
4793
            CHECK(!realm_set_is_valid(thawed_set));
2!
4794
            realm_release(thawed_set);
2✔
4795
            CHECK(realm_set_resolve_in(frozen_set, realm, &thawed_set));
2!
4796
            CHECK(thawed_set == nullptr);
2!
4797
            realm_release(frozen_set);
2✔
4798
        }
2✔
4799

6✔
4800
        SECTION("dictionaries") {
12✔
4801
            CPtr<realm_object_t> obj1;
2✔
4802
            size_t count;
2✔
4803

1✔
4804
            write([&]() {
2✔
4805
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4806
                CHECK(obj1);
2!
4807
            });
2✔
4808

1✔
4809
            auto dictionary = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["string_dict"]));
2✔
4810
            realm_dictionary_size(dictionary.get(), &count);
2✔
4811
            CHECK(count == 0);
2!
4812

1✔
4813
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4814
            realm_dictionary_t* frozen_dictionary;
2✔
4815
            CHECK(realm_dictionary_resolve_in(dictionary.get(), frozen_realm.get(), &frozen_dictionary));
2!
4816
            realm_dictionary_size(frozen_dictionary, &count);
2✔
4817
            CHECK(count == 0);
2!
4818

1✔
4819
            write([&]() {
2✔
4820
                checked(realm_dictionary_insert(dictionary.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr,
2✔
4821
                                                nullptr));
2✔
4822
            });
2✔
4823

1✔
4824
            realm_dictionary_size(frozen_dictionary, &count);
2✔
4825
            CHECK(count == 0);
2!
4826
            realm_dictionary_size(dictionary.get(), &count);
2✔
4827
            CHECK(count == 1);
2!
4828

1✔
4829
            realm_dictionary_t* thawed_dictionary;
2✔
4830
            CHECK(realm_dictionary_resolve_in(frozen_dictionary, realm, &thawed_dictionary));
2!
4831
            realm_dictionary_size(thawed_dictionary, &count);
2✔
4832
            CHECK(count == 1);
2!
4833

1✔
4834
            CHECK(realm_dictionary_is_valid(thawed_dictionary));
2!
4835
            write([&]() {
2✔
4836
                CHECK(checked(realm_object_delete(obj1.get())));
2!
4837
            });
2✔
4838
            CHECK(!realm_dictionary_is_valid(thawed_dictionary));
2!
4839
            realm_release(thawed_dictionary);
2✔
4840
            CHECK(realm_dictionary_resolve_in(frozen_dictionary, realm, &thawed_dictionary));
2!
4841
            CHECK(thawed_dictionary == nullptr);
2!
4842
            realm_release(frozen_dictionary);
2✔
4843
        }
2✔
4844
    }
12✔
4845

149✔
4846
    realm_close(realm);
298✔
4847
    REQUIRE(realm_is_closed(realm));
298!
4848
    realm_release(realm);
298✔
4849
}
298✔
4850

4851
TEST_CASE("C API: nested collections", "[c_api]") {
16✔
4852
    TestFile test_file;
16✔
4853
    realm_t* realm;
16✔
4854
    ObjectSchema object_schema = {"Foo",
16✔
4855
                                  {
16✔
4856
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
16✔
4857
                                      {"any", PropertyType::Mixed | PropertyType::Nullable},
16✔
4858
                                  }};
16✔
4859

8✔
4860
    auto config = make_config(test_file.path.c_str(), false);
16✔
4861
    config->schema = Schema{object_schema};
16✔
4862
    config->schema_version = 0;
16✔
4863
    realm = realm_open(config.get());
16✔
4864

8✔
4865
    realm_class_info_t class_foo;
16✔
4866
    bool found = false;
16✔
4867
    CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
16!
4868
    REQUIRE(found);
16!
4869

8✔
4870
    realm_property_info_t info;
16✔
4871
    found = false;
16✔
4872
    REQUIRE(realm_find_property(realm, class_foo.key, "any", &found, &info));
16!
4873
    REQUIRE(found);
16!
4874
    CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
16!
4875
    realm_property_key_t foo_any_col_key = info.key;
16✔
4876

8✔
4877
    CPtr<realm_object_t> obj1;
16✔
4878
    checked(realm_begin_write(realm));
16✔
4879
    realm_value_t pk = rlm_int_val(42);
16✔
4880
    obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_foo.key, pk));
16✔
4881

8✔
4882
    auto write = [&](auto&& f) {
12✔
4883
        checked(realm_begin_write(realm));
8✔
4884
        f();
8✔
4885
        checked(realm_commit(realm));
8✔
4886
        checked(realm_refresh(realm, nullptr));
8✔
4887
    };
8✔
4888

8✔
4889
    SECTION("results of mixed") {
16✔
4890
        SECTION("dictionary") {
4✔
4891
            REQUIRE(realm_set_dictionary(obj1.get(), foo_any_col_key));
2!
4892
            realm_value_t value;
2✔
4893
            realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
4894
            REQUIRE(value.type == RLM_TYPE_DICTIONARY);
2!
4895
            auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
2✔
4896
            auto nlist = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("A")));
2✔
4897
            auto ndict = cptr_checked(realm_dictionary_insert_dictionary(dict.get(), rlm_str_val("B")));
2✔
4898
            auto nset = cptr_checked(realm_dictionary_insert_set(dict.get(), rlm_str_val("C")));
2✔
4899

1✔
4900
            // verify that we can fetch a collection from a result of mixed
1✔
4901
            auto results = cptr_checked(realm_dictionary_to_results(dict.get()));
2✔
4902
            const auto sz = results->size();
2✔
4903
            REQUIRE(sz == dict->size());
2!
4904
            REQUIRE(results->is_valid());
2!
4905
            realm_value_t val;
2✔
4906
            realm_results_get(results.get(), 0, &val);
2✔
4907
            REQUIRE(val.type == RLM_TYPE_LIST);
2!
4908
            realm_results_get(results.get(), 1, &val);
2✔
4909
            REQUIRE(val.type == RLM_TYPE_DICTIONARY);
2!
4910
            realm_results_get(results.get(), 2, &val);
2✔
4911
            REQUIRE(val.type == RLM_TYPE_SET);
2!
4912
            auto result_list = cptr_checked(realm_results_get_list(results.get(), 0));
2✔
4913
            REQUIRE(result_list);
2!
4914
            REQUIRE(result_list->size() == nlist->size());
2!
4915
            auto result_dictionary = cptr_checked(realm_results_get_dictionary(results.get(), 1));
2✔
4916
            REQUIRE(result_dictionary);
2!
4917
            REQUIRE(result_dictionary->size() == ndict->size());
2!
4918
            auto result_set = cptr_checked(realm_results_get_set(results.get(), 2));
2✔
4919
            REQUIRE(result_set);
2!
4920
            REQUIRE(result_set->size() == nset->size());
2!
4921
        }
2✔
4922
        SECTION("list") {
4✔
4923
            REQUIRE(realm_set_list(obj1.get(), foo_any_col_key));
2!
4924
            realm_value_t value;
2✔
4925
            realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
4926
            REQUIRE(value.type == RLM_TYPE_LIST);
2!
4927
            auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
2✔
4928
            auto nlist = cptr_checked(realm_list_insert_list(list.get(), 0));
2✔
4929
            auto ndict = cptr_checked(realm_list_insert_dictionary(list.get(), 1));
2✔
4930
            auto nset = cptr_checked(realm_list_insert_set(list.get(), 2));
2✔
4931

1✔
4932
            // verify that we can fetch a collection from a result of mixed
1✔
4933
            auto results = cptr_checked(realm_list_to_results(list.get()));
2✔
4934
            const auto sz = results->size();
2✔
4935
            REQUIRE(sz == list->size());
2!
4936
            REQUIRE(results->is_valid());
2!
4937
            realm_value_t val;
2✔
4938
            realm_results_get(results.get(), 0, &val);
2✔
4939
            REQUIRE(val.type == RLM_TYPE_LIST);
2!
4940
            realm_results_get(results.get(), 1, &val);
2✔
4941
            REQUIRE(val.type == RLM_TYPE_DICTIONARY);
2!
4942
            realm_results_get(results.get(), 2, &val);
2✔
4943
            REQUIRE(val.type == RLM_TYPE_SET);
2!
4944
            auto result_list = cptr_checked(realm_results_get_list(results.get(), 0));
2✔
4945
            REQUIRE(result_list);
2!
4946
            REQUIRE(result_list->size() == nlist->size());
2!
4947
            auto result_dictionary = cptr_checked(realm_results_get_dictionary(results.get(), 1));
2✔
4948
            REQUIRE(result_dictionary);
2!
4949
            REQUIRE(result_dictionary->size() == ndict->size());
2!
4950
            auto result_set = cptr_checked(realm_results_get_set(results.get(), 2));
2✔
4951
            REQUIRE(result_set);
2!
4952
            REQUIRE(result_set->size() == nset->size());
2!
4953
        }
2✔
4954
    }
4✔
4955

8✔
4956
    SECTION("dictionary") {
16✔
4957
        struct UserData {
2✔
4958
            size_t deletions;
2✔
4959
            size_t insertions;
2✔
4960
            size_t modifications;
2✔
4961
            bool was_deleted;
2✔
4962
            realm_dictionary_t* dict;
2✔
4963
        } user_data;
2✔
4964

1✔
4965
        REQUIRE(realm_set_dictionary(obj1.get(), foo_any_col_key));
2!
4966
        realm_value_t value;
2✔
4967
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
4968
        REQUIRE(value.type == RLM_TYPE_DICTIONARY);
2!
4969
        auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
2✔
4970
        checked(realm_dictionary_insert(dict.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr, nullptr));
2✔
4971
        // dict -> list
1✔
4972
        auto list = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("Goodbye")));
2✔
4973
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
2✔
4974
        realm_list_insert(list.get(), 0, rlm_str_val("42"));
2✔
4975
        realm_list_insert(list.get(), 0, rlm_int_val(42));
2✔
4976
        // dict -> dict
1✔
4977
        auto dict2 = cptr_checked(realm_dictionary_insert_dictionary(dict.get(), rlm_str_val("Hi")));
2✔
4978
        user_data.dict = dict2.get();
2✔
4979
        checked(realm_dictionary_insert(dict2.get(), rlm_str_val("Nested-Hello"), rlm_str_val("Nested-World"),
2✔
4980
                                        nullptr, nullptr));
2✔
4981
        checked(realm_commit(realm));
2✔
4982

1✔
4983
        auto on_dictionary_change = [](void* data, const realm_dictionary_changes_t* changes) {
6✔
4984
            auto* user_data = static_cast<UserData*>(data);
6✔
4985
            realm_dictionary_get_changes(changes, &user_data->deletions, &user_data->insertions,
6✔
4986
                                         &user_data->modifications, &user_data->was_deleted);
6✔
4987
            if (user_data->was_deleted) {
6✔
4988
                CHECK(!realm_dictionary_is_valid(user_data->dict));
2!
4989
            }
2✔
4990
        };
6✔
4991
        auto require_change = [&]() {
2✔
4992
            auto token = cptr_checked(realm_dictionary_add_notification_callback(dict2.get(), &user_data, nullptr,
2✔
4993
                                                                                 nullptr, on_dictionary_change));
2✔
4994
            checked(realm_refresh(realm, nullptr));
2✔
4995
            return token;
2✔
4996
        };
2✔
4997

1✔
4998
        auto token = require_change();
2✔
4999

1✔
5000
        write([&] {
2✔
5001
            checked(realm_dictionary_insert(dict2.get(), rlm_str_val("Nested-Godbye"),
2✔
5002
                                            rlm_str_val("Nested-CruelWorld"), nullptr, nullptr));
2✔
5003
        });
2✔
5004
        CHECK(user_data.insertions == 1);
2!
5005

1✔
5006
        write([&] {
2✔
5007
            realm_dictionary_insert(dict.get(), rlm_str_val("Hi"), rlm_str_val("Foo"), nullptr, nullptr);
2✔
5008
        });
2✔
5009
        CHECK(user_data.deletions == 2);
2!
5010
        CHECK(user_data.was_deleted);
2!
5011

1✔
5012
        checked(realm_begin_write(realm));
2✔
5013
        // dict -> set
1✔
5014
        auto set = cptr_checked(realm_dictionary_insert_set(dict.get(), rlm_str_val("Leaf-Set")));
2✔
5015
        bool inserted;
2✔
5016
        size_t index;
2✔
5017
        realm_set_insert(set.get(), rlm_str_val("Set-Hello"), &index, &inserted);
2✔
5018
        CHECK(index == 0);
2!
5019
        CHECK(inserted);
2!
5020
        size_t size;
2✔
5021
        checked(realm_dictionary_size(dict.get(), &size));
2✔
5022
        REQUIRE(size == 4);
2!
5023
        checked(realm_commit(realm));
2✔
5024
    }
2✔
5025

8✔
5026
    SECTION("list") {
16✔
5027
        struct UserData {
2✔
5028
            size_t deletions;
2✔
5029
            size_t insertions;
2✔
5030
            size_t modifications;
2✔
5031
            bool was_deleted;
2✔
5032
            realm_list_t* list;
2✔
5033
        } user_data;
2✔
5034

1✔
5035
        REQUIRE(realm_set_list(obj1.get(), foo_any_col_key));
2!
5036
        realm_value_t value;
2✔
5037
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5038
        REQUIRE(value.type == RLM_TYPE_LIST);
2!
5039
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
2✔
5040
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
2✔
5041
        realm_list_insert(list.get(), 1, rlm_str_val("World"));
2✔
5042
        // list -> dict
1✔
5043
        auto dict = cptr_checked(realm_list_insert_dictionary(list.get(), 1));
2✔
5044
        checked(realm_dictionary_insert(dict.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr, nullptr));
2✔
5045
        // list -> list
1✔
5046
        auto list2 = cptr_checked(realm_list_insert_list(list.get(), 2));
2✔
5047
        user_data.list = list2.get();
2✔
5048

1✔
5049
        checked(realm_commit(realm));
2✔
5050

1✔
5051
        auto on_list_change = [](void* data, const realm_collection_changes_t* changes) {
6✔
5052
            auto* user_data = static_cast<UserData*>(data);
6✔
5053
            realm_collection_changes_get_num_changes(changes, &user_data->deletions, &user_data->insertions,
6✔
5054
                                                     &user_data->modifications, nullptr, nullptr,
6✔
5055
                                                     &user_data->was_deleted);
6✔
5056
            if (user_data->was_deleted) {
6✔
5057
                CHECK(!realm_list_is_valid(user_data->list));
2!
5058
            }
2✔
5059
        };
6✔
5060
        auto require_change = [&]() {
2✔
5061
            auto token = cptr_checked(
2✔
5062
                realm_list_add_notification_callback(list2.get(), &user_data, nullptr, nullptr, on_list_change));
2✔
5063
            checked(realm_refresh(realm, nullptr));
2✔
5064
            return token;
2✔
5065
        };
2✔
5066

1✔
5067
        auto token = require_change();
2✔
5068

1✔
5069
        write([&] {
2✔
5070
            realm_list_insert(list2.get(), 0, rlm_str_val("Nested-Hello"));
2✔
5071
            realm_list_insert(list2.get(), 1, rlm_str_val("Nested-World"));
2✔
5072
        });
2✔
5073
        CHECK(user_data.insertions == 2);
2!
5074

1✔
5075
        write([&] {
2✔
5076
            realm_list_set(list.get(), 2, rlm_str_val("Foo"));
2✔
5077
        });
2✔
5078
        CHECK(user_data.deletions == 2);
2!
5079
        CHECK(user_data.was_deleted);
2!
5080

1✔
5081
        // list -> set
1✔
5082
        checked(realm_begin_write(realm));
2✔
5083
        auto set = cptr_checked(realm_list_insert_set(list.get(), 3));
2✔
5084
        bool inserted;
2✔
5085
        size_t index;
2✔
5086
        realm_set_insert(set.get(), rlm_str_val("Set-Hello"), &index, &inserted);
2✔
5087
        CHECK(index == 0);
2!
5088
        CHECK(inserted);
2!
5089
        size_t size;
2✔
5090
        checked(realm_list_size(list.get(), &size));
2✔
5091
        REQUIRE(size == 5);
2!
5092
    }
2✔
5093

8✔
5094
    SECTION("set list for collection in mixed, verify that previous reference is invalid") {
16✔
5095
        REQUIRE(realm_set_list(obj1.get(), foo_any_col_key));
2!
5096
        realm_value_t value;
2✔
5097
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5098
        REQUIRE(value.type == RLM_TYPE_LIST);
2!
5099
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
2✔
5100
        auto n_list = cptr_checked(realm_list_insert_list(list.get(), 0));
2✔
5101
        size_t size;
2✔
5102
        checked(realm_list_size(list.get(), &size));
2✔
5103
        REQUIRE(size == 1);
2!
5104
        realm_list_insert(n_list.get(), 0, rlm_str_val("Test1"));
2✔
5105
        auto n_dict = cptr_checked(realm_list_set_dictionary(list.get(), 0));
2✔
5106
        // accessor has become invalid
1✔
5107
        REQUIRE(!realm_list_insert(n_list.get(), 1, rlm_str_val("Test2")));
2!
5108
        CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
5109
        // try to get a dictionary should work
1✔
5110
        n_dict = cptr_checked(realm_list_get_dictionary(list.get(), 0));
2✔
5111
        bool inserted = false;
2✔
5112
        size_t ndx;
2✔
5113
        realm_value_t key = rlm_str_val("key");
2✔
5114
        realm_value_t val = rlm_str_val("value");
2✔
5115
        REQUIRE(realm_dictionary_insert(n_dict.get(), key, val, &ndx, &inserted));
2!
5116
        REQUIRE(ndx == 0);
2!
5117
        REQUIRE(inserted);
2!
5118
        auto n_set = cptr_checked(realm_list_set_set(list.get(), 0));
2✔
5119
        // accessor invalid
1✔
5120
        REQUIRE(!realm_dictionary_insert(n_dict.get(), key, val, &ndx, &inserted));
2!
5121
        CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
5122
        n_set = cptr_checked(realm_list_get_set(list.get(), 0));
2✔
5123
        REQUIRE(realm_set_insert(n_set.get(), val, &ndx, &inserted));
2!
5124
        REQUIRE(ndx == 0);
2!
5125
        REQUIRE(inserted);
2!
5126
        n_list = cptr_checked(realm_list_set_list(list.get(), 0));
2✔
5127
        // accessor invalid
1✔
5128
        REQUIRE(!realm_set_insert(n_set.get(), val, &ndx, &inserted));
2!
5129
        CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
5130
        // get a list should work
1✔
5131
        n_list = cptr_checked(realm_list_get_list(list.get(), 0));
2✔
5132
        REQUIRE(realm_list_insert(n_list.get(), 0, rlm_str_val("Test1")));
2!
5133
        // reset the collection type to the same type (nop)
1✔
5134
        n_list = cptr_checked(realm_list_set_list(list.get(), 0));
2✔
5135
        // accessor is still valid
1✔
5136
        REQUIRE(realm_list_insert(n_list.get(), 0, rlm_str_val("Test2")));
2!
5137
        checked(realm_list_size(n_list.get(), &size));
2✔
5138
        REQUIRE(size == 2);
2!
5139
    }
2✔
5140

8✔
5141
    SECTION("set") {
16✔
5142
        REQUIRE(realm_set_set(obj1.get(), foo_any_col_key));
2!
5143
        realm_value_t value;
2✔
5144
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5145
        REQUIRE(value.type == RLM_TYPE_SET);
2!
5146
        auto set = cptr_checked(realm_get_set(obj1.get(), foo_any_col_key));
2✔
5147
        size_t index;
2✔
5148
        bool inserted = false;
2✔
5149
        REQUIRE(realm_set_insert(set.get(), rlm_str_val("Hello"), &index, &inserted));
2!
5150
        CHECK(index == 0);
2!
5151
        CHECK(inserted);
2!
5152
        REQUIRE(realm_set_insert(set.get(), rlm_int_val(42), &index, &inserted));
2!
5153
        CHECK(index == 0);
2!
5154
        CHECK(inserted);
2!
5155
        size_t size;
2✔
5156
        checked(realm_set_size(set.get(), &size));
2✔
5157
        REQUIRE(size == 2);
2!
5158
        checked(realm_set_get(set.get(), 0, &value));
2✔
5159
        CHECK(value.type == RLM_TYPE_INT);
2!
5160
        CHECK(value.integer == 42);
2!
5161
        checked(realm_set_get(set.get(), 1, &value));
2✔
5162
        CHECK(value.type == RLM_TYPE_STRING);
2!
5163
        CHECK(strncmp(value.string.data, "Hello", value.string.size) == 0);
2!
5164
    }
2✔
5165

8✔
5166
    SECTION("json") {
16✔
5167
        REQUIRE(realm_set_json(
2!
5168
            obj1.get(), foo_any_col_key,
2✔
5169
            "[{\"Seven\":7, \"Six\":6}, \"Hello\", {\"Points\": [1.25, 4.5, 6.75], \"Hello\": \"World\"}]"));
2✔
5170
        realm_value_t value;
2✔
5171
        realm_get_value(obj1.get(), foo_any_col_key, &value);
2✔
5172
        REQUIRE(value.type == RLM_TYPE_LIST);
2!
5173
        auto list = cptr_checked(realm_get_list(obj1.get(), foo_any_col_key));
2✔
5174
        size_t size;
2✔
5175
        checked(realm_list_size(list.get(), &size));
2✔
5176
        CHECK(size == 3);
2!
5177
    }
2✔
5178

8✔
5179
    SECTION("freeze list") {
16✔
5180
        REQUIRE(realm_set_dictionary(obj1.get(), foo_any_col_key));
2!
5181
        auto dict = cptr_checked(realm_get_dictionary(obj1.get(), foo_any_col_key));
2✔
5182
        auto list = cptr_checked(realm_dictionary_insert_list(dict.get(), rlm_str_val("List")));
2✔
5183
        realm_list_insert(list.get(), 0, rlm_str_val("Hello"));
2✔
5184
        realm_list_insert(list.get(), 0, rlm_str_val("42"));
2✔
5185
        checked(realm_commit(realm));
2✔
5186
        size_t size;
2✔
5187
        checked(realm_list_size(list.get(), &size));
2✔
5188
        REQUIRE(size == 2);
2!
5189
        auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
5190

1✔
5191
        realm_list_t* frozen_list;
2✔
5192
        realm_list_resolve_in(list.get(), frozen_realm.get(), &frozen_list);
2✔
5193
        checked(realm_list_size(frozen_list, &size));
2✔
5194
        REQUIRE(size == 2);
2!
5195
        realm_release(frozen_list);
2✔
5196
    }
2✔
5197
    realm_release(realm);
16✔
5198
}
16✔
5199

5200
TEST_CASE("C API: convert", "[c_api]") {
8✔
5201
    TestFile test_file;
8✔
5202
    TestFile dest_test_file;
8✔
5203
    realm_t* realm;
8✔
5204
    ObjectSchema object_schema = {"Foo",
8✔
5205
                                  {
8✔
5206
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
8✔
5207
                                      {"string_value", PropertyType::String},
8✔
5208
                                  }};
8✔
5209

4✔
5210
    { // seed a Realm with an object
8✔
5211
        auto config = make_config(test_file.path.c_str(), false);
8✔
5212
        config->schema = Schema{object_schema};
8✔
5213
        config->schema_version = 0;
8✔
5214
        realm = realm_open(config.get());
8✔
5215
        REQUIRE(checked(realm));
8!
5216
        CHECK(!realm_equals(realm, nullptr));
8!
5217
        realm_class_info_t class_foo;
8✔
5218
        bool found = false;
8✔
5219
        CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
8!
5220
        REQUIRE(found);
8!
5221

4✔
5222
        realm_property_key_t foo_str_col_key;
8✔
5223
        realm_property_info_t info;
8✔
5224
        found = false;
8✔
5225
        REQUIRE(realm_find_property(realm, class_foo.key, "string_value", &found, &info));
8!
5226
        REQUIRE(found);
8!
5227
        CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
8!
5228
        foo_str_col_key = info.key;
8✔
5229

4✔
5230
        CPtr<realm_object_t> obj1;
8✔
5231
        checked(realm_begin_write(realm));
8✔
5232
        realm_value_t pk = rlm_int_val(42);
8✔
5233
        obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_foo.key, pk));
8✔
5234
        CHECK(obj1);
8!
5235
        CHECK(checked(realm_set_value(obj1.get(), foo_str_col_key, rlm_str_val("Hello, World!"), false)));
8!
5236
        checked(realm_commit(realm));
8✔
5237
        checked(realm_refresh(realm, nullptr));
8✔
5238

4✔
5239
        size_t foo_count;
8✔
5240
        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &foo_count)));
8!
5241
        REQUIRE(foo_count == 1);
8!
5242
    }
8✔
5243

4✔
5244
    CHECK(realm_get_num_classes(realm) == 1);
8!
5245

4✔
5246
    SECTION("convert with path") {
8✔
5247
        bool merge_with_existing = false;
4✔
5248
        realm_binary encryption_key{nullptr, 0};
4✔
5249

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

2✔
5252
        SECTION("convert again without merge should fail") {
4✔
5253
            REQUIRE_FALSE(
2!
5254
                realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
2✔
5255
        }
2✔
5256
        SECTION("convert again with merge should succeed") {
4✔
5257
            merge_with_existing = true;
2✔
5258
            REQUIRE(realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
2!
5259
        }
2✔
5260
    }
4✔
5261

4✔
5262
    SECTION("convert with config") {
8✔
5263
        auto dest_config = make_config(dest_test_file.path.c_str(), false);
4✔
5264
        dest_config->schema = Schema{object_schema};
4✔
5265
        dest_config->schema_version = 0;
4✔
5266
        bool merge_with_existing = false;
4✔
5267
        REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
4!
5268
        SECTION("convert again without merge should fail") {
4✔
5269
            REQUIRE_FALSE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
2!
5270
        }
2✔
5271
        SECTION("convert again with merge should succeed") {
4✔
5272
            merge_with_existing = true;
2✔
5273
            REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
2!
5274
        }
2✔
5275
    }
4✔
5276
    realm_close(realm);
8✔
5277
    REQUIRE(realm_is_closed(realm));
8!
5278
    realm_release(realm);
8✔
5279
}
8✔
5280

5281
struct Userdata {
5282
    std::atomic<bool> called{false};
5283
    bool has_error;
5284
    realm_error_t error;
5285
    realm_thread_safe_reference_t* realm_ref = nullptr;
5286
    std::string error_message;
5287
};
5288

5289
#if REALM_ENABLE_SYNC
5290

5291
static void task_completion_func(void* p, realm_thread_safe_reference_t* realm,
5292
                                 const realm_async_error_t* async_error)
5293
{
4✔
5294
    auto userdata_p = static_cast<Userdata*>(p);
4✔
5295

2✔
5296
    userdata_p->realm_ref = realm;
4✔
5297
    userdata_p->has_error = realm_get_async_error(async_error, &userdata_p->error);
4✔
5298
    userdata_p->called = true;
4✔
5299
}
4✔
5300

5301
static void task_init_subscription(realm_t* realm, void*)
UNCOV
5302
{
×
UNCOV
5303
    REQUIRE(realm);
×
UNCOV
5304
}
×
5305

5306
static void sync_error_handler(void* p, realm_sync_session_t*, const realm_sync_error_t error)
5307
{
2✔
5308
    auto userdata_p = static_cast<Userdata*>(p);
2✔
5309
    userdata_p->has_error = true;
2✔
5310
    userdata_p->error_message = error.status.message;
2✔
5311
    userdata_p->error.error = error.status.error;
2✔
5312
    userdata_p->error.categories = error.status.categories;
2✔
5313
    userdata_p->error.message = userdata_p->error_message.c_str();
2✔
5314
}
2✔
5315

5316
TEST_CASE("C API - async_open", "[sync][pbs][c_api]") {
4✔
5317
    TestSyncManager init_sync_manager;
4✔
5318
    SyncTestFile test_config(init_sync_manager.app(), "default");
4✔
5319
    test_config.cache = false;
4✔
5320
    ObjectSchema object_schema = {"object",
4✔
5321
                                  {
4✔
5322
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
4✔
5323
                                      {"value", PropertyType::Int},
4✔
5324
                                  }};
4✔
5325
    test_config.schema = Schema{object_schema};
4✔
5326

2✔
5327
    SECTION("can open synced Realms that don't already exist") {
4✔
5328
        realm_config_t* config = realm_config_new();
2✔
5329
        config->schema = Schema{object_schema};
2✔
5330
        realm_user user(init_sync_manager.app()->current_user());
2✔
5331
        realm_sync_config_t* sync_config = realm_sync_config_new(&user, "default");
2✔
5332
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
2✔
5333
                                                           nullptr);
2✔
5334
        realm_config_set_path(config, test_config.path.c_str());
2✔
5335
        realm_config_set_sync_config(config, sync_config);
2✔
5336
        realm_config_set_schema_version(config, 1);
2✔
5337
        realm_async_open_task_t* task = realm_open_synchronized(config);
2✔
5338
        REQUIRE(task);
2!
5339
        Userdata userdata;
2✔
5340
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
2✔
5341
        util::EventLoop::main().run_until([&] {
16,963✔
5342
            return userdata.called.load();
16,963✔
5343
        });
16,963✔
5344
        REQUIRE(userdata.called);
2!
5345
        REQUIRE(userdata.realm_ref);
2!
5346
        realm_release(task);
2✔
5347

1✔
5348
        realm_t* realm = realm_from_thread_safe_reference(userdata.realm_ref, nullptr);
2✔
5349
        realm_release(userdata.realm_ref);
2✔
5350

1✔
5351
        bool found;
2✔
5352
        realm_class_info_t class_info;
2✔
5353
        realm_find_class(realm, "object", &found, &class_info);
2✔
5354
        REQUIRE(found);
2!
5355
        realm_release(realm);
2✔
5356
        realm_release(config);
2✔
5357
        realm_release(sync_config);
2✔
5358
    }
2✔
5359

2✔
5360
    SECTION("cancels download and reports an error on auth error") {
4✔
5361
        // Create a token which can be parsed as a JWT but is not valid
1✔
5362
        std::string unencoded_body = nlohmann::json({{"exp", 123}, {"iat", 456}}).dump();
2✔
5363
        std::string encoded_body;
2✔
5364
        encoded_body.resize(util::base64_encoded_size(unencoded_body.size()));
2✔
5365
        util::base64_encode(unencoded_body.data(), unencoded_body.size(), &encoded_body[0], encoded_body.size());
2✔
5366
        auto invalid_token = "." + encoded_body + ".";
2✔
5367

1✔
5368

1✔
5369
        realm_config_t* config = realm_config_new();
2✔
5370
        config->schema = Schema{object_schema};
2✔
5371
        realm_user user(init_sync_manager.app()->current_user());
2✔
5372
        realm_sync_config_t* sync_config = realm_sync_config_new(&user, "realm");
2✔
5373
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
2✔
5374
                                                           nullptr);
2✔
5375
        sync_config->user->log_in(invalid_token, invalid_token);
2✔
5376

1✔
5377
        realm_config_set_path(config, test_config.path.c_str());
2✔
5378
        realm_config_set_schema_version(config, 1);
2✔
5379
        Userdata userdata;
2✔
5380
        realm_sync_config_set_error_handler(sync_config, sync_error_handler, &userdata, nullptr);
2✔
5381
        realm_config_set_sync_config(config, sync_config);
2✔
5382

1✔
5383
        realm_async_open_task_t* task = realm_open_synchronized(config);
2✔
5384
        REQUIRE(task);
2!
5385
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
2✔
5386
        init_sync_manager.network_callback(app::Response{403});
2✔
5387
        util::EventLoop::main().run_until([&] {
3✔
5388
            return userdata.called.load();
3✔
5389
        });
3✔
5390
        REQUIRE(userdata.called);
2!
5391
        REQUIRE(!userdata.realm_ref);
2!
5392
        REQUIRE(userdata.error.error == RLM_ERR_AUTH_ERROR);
2!
5393
        REQUIRE(userdata.error_message ==
2!
5394
                "Unable to refresh the user access token: http error code considered fatal. Client Error: 403");
2✔
5395
        realm_release(task);
2✔
5396
        realm_release(config);
2✔
5397
        realm_release(sync_config);
2✔
5398
    }
2✔
5399
}
4✔
5400

5401
struct BCTOState {
5402
    bool bcto_deleted = false;
5403
    bool thread_create_called = false;
5404
    bool thread_destroy_called = false;
5405
    std::string thread_on_error_message;
5406
    std::string id = "BTCO-STATE";
5407
};
5408

5409

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

1✔
5413
    auto bcto_free_userdata = [](realm_userdata_t userdata) {
2✔
5414
        REQUIRE(userdata);
2!
5415
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5416
        REQUIRE(user_data->bcto_deleted == false);
2!
5417
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5418
        user_data->id.clear();
2✔
5419
        user_data->bcto_deleted = true;
2✔
5420
    };
2✔
5421

1✔
5422
    auto bcto_on_thread_create = [](realm_userdata_t userdata) {
2✔
5423
        REQUIRE(userdata);
2!
5424
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5425
        REQUIRE(user_data->bcto_deleted == false);
2!
5426
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5427
        REQUIRE(!user_data->thread_create_called);
2!
5428
        user_data->thread_create_called = true;
2✔
5429
    };
2✔
5430

1✔
5431
    auto bcto_on_thread_destroy = [](realm_userdata_t userdata) {
2✔
5432
        REQUIRE(userdata);
2!
5433
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5434
        REQUIRE(user_data->bcto_deleted == false);
2!
5435
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5436
        REQUIRE(!user_data->thread_destroy_called);
2!
5437
        user_data->thread_destroy_called = true;
2✔
5438
    };
2✔
5439

1✔
5440
    auto bcto_on_thread_error = [](realm_userdata_t userdata, const char* err_message) {
2✔
5441
        REQUIRE(userdata);
2!
5442
        REQUIRE(err_message);
2!
5443
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5444
        REQUIRE(user_data->bcto_deleted == false);
2!
5445
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5446
        REQUIRE(user_data->thread_on_error_message.empty());
2!
5447
        user_data->thread_on_error_message = err_message;
2✔
5448
        return true;
2✔
5449
    };
2✔
5450

1✔
5451
    {
2✔
5452
        auto config = cptr(realm_sync_client_config_new());
2✔
5453
        realm_sync_client_config_set_default_binding_thread_observer(
2✔
5454
            config.get(), bcto_on_thread_create, bcto_on_thread_destroy, bcto_on_thread_error,
2✔
5455
            static_cast<realm_userdata_t>(&bcto_user_data), bcto_free_userdata);
2✔
5456
        REQUIRE(config->default_socket_provider_thread_observer);
2!
5457
        auto observer_ptr =
2✔
5458
            static_cast<CBindingThreadObserver*>(config->default_socket_provider_thread_observer.get());
2✔
5459
        REQUIRE(observer_ptr->test_get_create_callback_func() == bcto_on_thread_create);
2!
5460
        REQUIRE(observer_ptr->test_get_destroy_callback_func() == bcto_on_thread_destroy);
2!
5461
        REQUIRE(observer_ptr->test_get_error_callback_func() == bcto_on_thread_error);
2!
5462
        REQUIRE(observer_ptr->has_handle_error());
2!
5463
        REQUIRE(observer_ptr->test_get_userdata_ptr() == &bcto_user_data);
2!
5464

1✔
5465
        auto test_thread = std::thread([&]() {
2✔
5466
            auto bcto_ptr = std::static_pointer_cast<realm::BindingCallbackThreadObserver>(
2✔
5467
                config->default_socket_provider_thread_observer);
2✔
5468
            REQUIRE(bcto_ptr);
2!
5469
            auto will_destroy_thread = util::make_scope_exit([&bcto_ptr]() noexcept {
2✔
5470
                bcto_ptr->will_destroy_thread();
2✔
5471
            });
2✔
5472
            bcto_ptr->did_create_thread();
2✔
5473
            REQUIRE(bcto_ptr->handle_error(MultipleSyncAgents()));
2!
5474
        });
2✔
5475

1✔
5476
        // Wait for the thread to exit
1✔
5477
        test_thread.join();
2✔
5478

1✔
5479
        REQUIRE(bcto_user_data.thread_create_called);
2!
5480
        REQUIRE(bcto_user_data.thread_on_error_message.find(
2!
5481
                    "Multiple sync agents attempted to join the same session") != std::string::npos);
2✔
5482
        REQUIRE(bcto_user_data.thread_destroy_called);
2!
5483
    }
2✔
5484

1✔
5485
    REQUIRE(bcto_user_data.bcto_deleted == true);
2!
5486

1✔
5487
    {
2✔
5488
        auto config = cptr(realm_sync_client_config_new());
2✔
5489
        realm_sync_client_config_set_default_binding_thread_observer(config.get(), nullptr, nullptr, nullptr, nullptr,
2✔
5490
                                                                     nullptr);
2✔
5491
        auto no_handle_error_ptr =
2✔
5492
            static_cast<CBindingThreadObserver*>(config->default_socket_provider_thread_observer.get());
2✔
5493
        no_handle_error_ptr->did_create_thread();                          // should not crash
2✔
5494
        no_handle_error_ptr->will_destroy_thread();                        // should not crash
2✔
5495
        REQUIRE(!no_handle_error_ptr->has_handle_error());                 // no handler, returns false
2!
5496
        REQUIRE(!no_handle_error_ptr->handle_error(MultipleSyncAgents())); // no handler, returns false
2!
5497
        // No free_user_data function was provided and internal default should be used
1✔
5498
        // Should not crash at scope exit
1✔
5499
    }
2✔
5500
}
2✔
5501
#endif
5502

5503
#if REALM_ENABLE_AUTH_TESTS
5504

5505
std::atomic_bool baas_client_stop{false};
5506
std::atomic<std::size_t> error_handler_counter{0};
5507
std::atomic<std::size_t> before_client_reset_counter{0};
5508
std::atomic<std::size_t> after_client_reset_counter{0};
5509

5510
TEST_CASE("C API - client reset", "[sync][pbs][c_api][client reset][baas]") {
6✔
5511
    reset_utils::Partition partition{"realm_id", random_string(20)};
6✔
5512
    Property partition_prop = {partition.property_name, PropertyType::String | PropertyType::Nullable};
6✔
5513
    Schema schema{
6✔
5514
        {"object",
6✔
5515
         {
6✔
5516
             {"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
6✔
5517
             {"value", PropertyType::Int},
6✔
5518
             partition_prop,
6✔
5519
         }},
6✔
5520
    };
6✔
5521

3✔
5522
    std::string base_url = get_base_url();
6✔
5523
    REQUIRE(!base_url.empty());
6!
5524
    auto server_app_config = minimal_app_config(base_url, "c_api_client_reset_tests", schema);
6✔
5525
    server_app_config.partition_key = partition_prop;
6✔
5526
    TestAppSession test_app_session(create_app(server_app_config));
6✔
5527

3✔
5528
    auto app = test_app_session.app();
6✔
5529
    auto get_valid_config = [&]() -> SyncTestFile {
12✔
5530
        create_user_and_log_in(app);
12✔
5531
        return SyncTestFile(app->current_user(), partition.value, schema);
12✔
5532
    };
12✔
5533
    SyncTestFile local_config = get_valid_config();
6✔
5534
    SyncTestFile remote_config = get_valid_config();
6✔
5535

3✔
5536
    auto make_reset = [&](Realm::Config config_local,
6✔
5537
                          Realm::Config config_remote) -> std::unique_ptr<reset_utils::TestClientReset> {
6✔
5538
        return reset_utils::make_baas_client_reset(config_local, config_remote, test_app_session);
6✔
5539
    };
6✔
5540

3✔
5541
    local_config.sync_config = std::make_shared<realm_sync_config_t>(*local_config.sync_config);
6✔
5542
    realm_sync_config_t* local_sync_config = static_cast<realm_sync_config_t*>(local_config.sync_config.get());
6✔
5543

3✔
5544
    struct ResetRealmFiles {
6✔
5545
        void set_app(std::shared_ptr<realm::app::App> app)
6✔
5546
        {
6✔
5547
            m_app = app;
6✔
5548
        }
6✔
5549
        void reset_realm(const char* path)
6✔
5550
        {
5✔
5551
            realm_app_t realm_app{m_app};
4✔
5552
            bool did_run;
4✔
5553
            realm_sync_immediately_run_file_actions(&realm_app, path, &did_run);
4✔
5554
        }
4✔
5555
        static ResetRealmFiles& instance()
6✔
5556
        {
10✔
5557
            static ResetRealmFiles instance;
10✔
5558
            return instance;
10✔
5559
        }
10✔
5560
        std::shared_ptr<realm::app::App> m_app;
6✔
5561
    };
6✔
5562
    ResetRealmFiles::instance().set_app(app);
6✔
5563

3✔
5564
    SECTION("Manual reset") {
6✔
5565
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_MANUAL);
2✔
5566

1✔
5567
        realm_sync_config_set_error_handler(
2✔
5568
            local_sync_config,
2✔
5569
            [](realm_userdata_t, realm_sync_session_t*, const realm_sync_error_t sync_error) {
2✔
5570
                REQUIRE(sync_error.c_original_file_path_key);
2!
5571
                REQUIRE(sync_error.c_recovery_file_path_key);
2!
5572
                REQUIRE(sync_error.is_client_reset_requested);
2!
5573
                REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_CLIENT_RESET);
2!
5574
                ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
2✔
5575
                baas_client_stop.store(true);
2✔
5576
            },
2✔
5577
            nullptr, nullptr);
2✔
5578

1✔
5579
        make_reset(local_config, remote_config)
2✔
5580
            ->on_post_reset([&](SharedRealm) {
2✔
5581
                util::EventLoop::main().run_until([&] {
773✔
5582
                    return baas_client_stop.load();
773✔
5583
                });
773✔
5584
            })
2✔
5585
            ->run();
2✔
5586
    }
2✔
5587

3✔
5588
    SECTION("Local Discard") {
6✔
5589
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_DISCARD_LOCAL);
4✔
5590

2✔
5591
        SECTION("Before client reset success") {
4✔
5592
            realm_sync_config_set_before_client_reset_handler(
2✔
5593
                local_sync_config,
2✔
5594
                [](realm_userdata_t, realm_t*) -> bool {
2✔
5595
                    before_client_reset_counter.fetch_add(1);
2✔
5596
                    return true;
2✔
5597
                },
2✔
5598
                nullptr, nullptr);
2✔
5599

1✔
5600
            realm_sync_config_set_after_client_reset_handler(
2✔
5601
                local_sync_config,
2✔
5602
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
2✔
5603
                    after_client_reset_counter.fetch_add(1);
2✔
5604
                    baas_client_stop.store(true);
2✔
5605
                    return true;
2✔
5606
                },
2✔
5607
                nullptr, nullptr);
2✔
5608

1✔
5609
            make_reset(local_config, remote_config)
2✔
5610
                ->on_post_reset([&](SharedRealm) {
2✔
5611
                    util::EventLoop::main().run_until([&] {
3✔
5612
                        return baas_client_stop.load();
3✔
5613
                    });
3✔
5614
                })
2✔
5615
                ->run();
2✔
5616

1✔
5617
            REQUIRE(before_client_reset_counter.load() == 1);
2!
5618
            REQUIRE(after_client_reset_counter.load() == 1);
2!
5619
        }
2✔
5620

2✔
5621
        SECTION("Before client reset fails") {
4✔
5622
            baas_client_stop.store(false);
2✔
5623
            before_client_reset_counter.store(0);
2✔
5624
            after_client_reset_counter.store(0);
2✔
5625

1✔
5626
            realm_sync_config_set_error_handler(
2✔
5627
                local_sync_config,
2✔
5628
                [](realm_userdata_t, realm_sync_session_t*, const realm_sync_error_t sync_error) {
2✔
5629
                    REQUIRE(sync_error.c_original_file_path_key);
2!
5630
                    REQUIRE(sync_error.c_recovery_file_path_key);
2!
5631
                    REQUIRE(sync_error.is_client_reset_requested);
2!
5632
                    // Callback in `realm_sync_config_set_before_client_reset_handler` fails, so
1✔
5633
                    // a synthetic error is created with no action.
1✔
5634
                    REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_NO_ACTION);
2!
5635
                    ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
2✔
5636
                    error_handler_counter.fetch_add(1);
2✔
5637
                    baas_client_stop.store(true);
2✔
5638
                },
2✔
5639
                nullptr, nullptr);
2✔
5640

1✔
5641
            realm_sync_config_set_before_client_reset_handler(
2✔
5642
                local_sync_config,
2✔
5643
                [](realm_userdata_t, realm_t*) -> bool {
2✔
5644
                    before_client_reset_counter.fetch_add(1);
2✔
5645
                    return false;
2✔
5646
                },
2✔
5647
                nullptr, nullptr);
2✔
5648

1✔
5649
            realm_sync_config_set_after_client_reset_handler(
2✔
5650
                local_sync_config,
2✔
5651
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
1✔
UNCOV
5652
                    after_client_reset_counter.fetch_add(1);
×
UNCOV
5653
                    return true;
×
UNCOV
5654
                },
×
5655
                nullptr, nullptr);
2✔
5656

1✔
5657
            make_reset(local_config, remote_config)
2✔
5658
                ->on_post_reset([&](SharedRealm) {
2✔
5659
                    util::EventLoop::main().run_until([&] {
1,037✔
5660
                        return baas_client_stop.load();
1,037✔
5661
                    });
1,037✔
5662
                })
2✔
5663
                ->run();
2✔
5664

1✔
5665
            REQUIRE(error_handler_counter.load() == 1);
2!
5666
            REQUIRE(before_client_reset_counter.load() == 1);
2!
5667
            REQUIRE(after_client_reset_counter.load() == 0);
2!
5668
        }
2✔
5669
    }
4✔
5670
}
6✔
5671

5672
static void realm_app_void_completion(void*, const realm_app_error_t*) {}
14✔
5673

5674
static void realm_app_user1(void* p, realm_user_t* user, const realm_app_error_t*)
5675
{
10✔
5676
    auto clone_ptr = realm_clone(user);
10✔
5677
    CHECK(realm_equals(user, clone_ptr));
10!
5678
    *(static_cast<realm_user_t**>(p)) = static_cast<realm_user_t*>(clone_ptr);
10✔
5679
}
10✔
5680

5681
static void realm_app_user2(void* p, realm_user_t* user, const realm_app_error_t*)
5682
{
2✔
5683
    realm_user_identity_t idents[10];
2✔
5684
    size_t n;
2✔
5685
    realm_user_get_all_identities(user, idents, 10, &n);
2✔
5686
    *static_cast<bool*>(p) = n == 2;
2✔
5687
    for (size_t i = 0; i < n; i++) {
6✔
5688
        realm_free(idents[i].id);
4✔
5689
    }
4✔
5690
}
2✔
5691

5692
TEST_CASE("C API app: link_user integration w/c_api transport", "[sync][app][c_api][baas]") {
12✔
5693
    struct TestTransportUserData {
12✔
5694
        TestTransportUserData()
12✔
5695
            : logger(std::make_unique<util::StderrLogger>(realm::util::Logger::Level::TEST_LOGGING_LEVEL))
12✔
5696
            , transport(std::make_unique<SynchronousTestTransport>())
12✔
5697
        {
12✔
5698
        }
12✔
5699
        std::unique_ptr<util::Logger> logger;
12✔
5700
        std::unique_ptr<realm::app::GenericNetworkTransport> transport;
12✔
5701
    };
12✔
5702

6✔
5703
    auto send_request_to_server = [](realm_userdata_t userdata, const realm_http_request_t request,
12✔
5704
                                     void* request_context) {
88✔
5705
        using namespace realm::app;
88✔
5706

44✔
5707
        constexpr uint64_t default_timeout_ms = 60000;
88✔
5708
        REQUIRE(userdata != nullptr);
88!
5709
        TestTransportUserData* user_data(static_cast<TestTransportUserData*>(userdata));
88✔
5710
        REQUIRE(user_data != nullptr);
88!
5711
        REQUIRE(user_data->transport != nullptr);
88!
5712
        REQUIRE(user_data->logger != nullptr);
88!
5713
        REQUIRE(strlen(request.url) > 0);
88!
5714
        HttpHeaders headers;
88✔
5715
        for (size_t i = 0; i < request.num_headers; i++) {
272✔
5716
            REQUIRE(request.headers[i].name != nullptr);
184!
5717
            REQUIRE(request.headers[i].value != nullptr);
184!
5718
            std::string name(request.headers[i].name);
184✔
5719
            std::string value(request.headers[i].value);
184✔
5720
            REQUIRE(!name.empty());
184!
5721
            REQUIRE(!value.empty());
184!
5722
            headers.emplace(name, value);
184✔
5723
        }
184✔
5724
        REQUIRE(request_context != nullptr);
88!
5725
        auto new_request = Request{HttpMethod(request.method), request.url, default_timeout_ms, std::move(headers),
88✔
5726
                                   std::string(request.body, request.body_size)};
88✔
5727
        user_data->logger->trace("CAPI: Request URL (%1): %2", httpmethod_to_string(new_request.method),
88✔
5728
                                 new_request.url);
88✔
5729
        user_data->logger->trace("CAPI: Request body: %1", new_request.body);
88✔
5730
        user_data->transport->send_request_to_server(new_request, [&](const Response& response) mutable {
88✔
5731
            std::vector<realm_http_header_t> c_headers;
88✔
5732
            c_headers.reserve(response.headers.size());
88✔
5733
            for (auto&& header : response.headers) {
692✔
5734
                c_headers.push_back({header.first.c_str(), header.second.c_str()});
692✔
5735
            }
692✔
5736

44✔
5737
            auto c_response = std::make_unique<realm_http_response_t>();
88✔
5738
            c_response->status_code = response.http_status_code;
88✔
5739
            c_response->custom_status_code = response.custom_status_code;
88✔
5740
            c_response->headers = c_headers.data();
88✔
5741
            c_response->num_headers = c_headers.size();
88✔
5742
            c_response->body = response.body.data();
88✔
5743
            c_response->body_size = response.body.size();
88✔
5744
            user_data->logger->trace("CAPI: Response (%1): %2", c_response->status_code,
88✔
5745
                                     std::string(c_response->body, c_response->body_size));
88✔
5746
            realm_http_transport_complete_request(request_context, c_response.get());
88✔
5747
        });
88✔
5748
    };
88✔
5749

6✔
5750
    auto user_data_free = [](void* userdata) {
12✔
5751
        REQUIRE(userdata != nullptr);
12!
5752
        std::unique_ptr<TestTransportUserData> user_data(static_cast<TestTransportUserData*>(userdata));
12✔
5753
        REQUIRE(user_data != nullptr);
12!
5754
        REQUIRE(user_data->transport != nullptr);
12!
5755
        REQUIRE(user_data->logger != nullptr);
12!
5756
        user_data->logger->trace("CAPI: user_data free called");
12✔
5757
    };
12✔
5758

6✔
5759
    // user_data will be deleted when user_data_free() is called
6✔
5760
    auto user_data = new TestTransportUserData();
12✔
5761
    auto http_transport = realm_http_transport_new(send_request_to_server, user_data, user_data_free);
12✔
5762
    auto app_session = get_runtime_app_session(get_base_url());
12✔
5763
    TestAppSession session(app_session, *http_transport, DeleteApp{false});
12✔
5764
    realm_app app(session.app());
12✔
5765

6✔
5766
    SECTION("remove_user integration") {
12✔
5767
        AutoVerifiedEmailCredentials creds;
2✔
5768
        realm_user_t* sync_user_1 = nullptr;
2✔
5769
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5770
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5771
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
5772
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5773
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
2✔
5774

1✔
5775
        realm_app_remove_user(&app, sync_user_1, realm_app_void_completion, nullptr, nullptr);
2✔
5776
        auto state = realm_user_get_state(sync_user_1);
2✔
5777
        CHECK(state == RLM_USER_STATE_REMOVED);
2!
5778
        realm_release(sync_user_1);
2✔
5779
    }
2✔
5780
    SECTION("delete_user integration") {
12✔
5781
        AutoVerifiedEmailCredentials creds;
2✔
5782
        realm_user_t* sync_user = nullptr;
2✔
5783
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5784
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5785
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
5786
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5787
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user, nullptr);
2✔
5788

1✔
5789
        realm_app_delete_user(&app, sync_user, realm_app_void_completion, nullptr, nullptr);
2✔
5790
        auto state = realm_user_get_state(sync_user);
2✔
5791
        CHECK(state == RLM_USER_STATE_REMOVED);
2!
5792
        realm_release(sync_user);
2✔
5793
    }
2✔
5794
    SECTION("link_user integration") {
12✔
5795
        AutoVerifiedEmailCredentials creds;
2✔
5796
        bool processed = false;
2✔
5797
        realm_user_t* sync_user = nullptr;
2✔
5798

1✔
5799
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5800
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5801
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
5802

1✔
5803
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5804
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user, nullptr);
2✔
5805

1✔
5806
        realm_app_credentials email_creds(creds);
2✔
5807
        realm_app_link_user(&app, sync_user, &email_creds, realm_app_user2, &processed, nullptr);
2✔
5808

1✔
5809
        CHECK(processed);
2!
5810
        realm_release(sync_user);
2✔
5811
    }
2✔
5812
    SECTION("realm_app_get_all_users integration") {
12✔
5813

1✔
5814
        AutoVerifiedEmailCredentials creds;
2✔
5815
        realm_user_t* sync_user_1 = nullptr;
2✔
5816
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5817
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5818
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
5819
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5820
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
2✔
5821
        auto current_user = realm_app_get_current_user(&app);
2✔
5822
        CHECK(realm_equals(sync_user_1, current_user));
2!
5823
        realm_release(current_user);
2✔
5824

1✔
5825
        realm_user_t* sync_user_2;
2✔
5826
        realm_app_switch_user(&app, sync_user_1, &sync_user_2);
2✔
5827
        size_t out_n = 0;
2✔
5828

1✔
5829
        realm_app_get_all_users(&app, nullptr, 0, &out_n);
2✔
5830
        CHECK(out_n == 2);
2!
5831

1✔
5832
        std::vector<realm_user_t*> out_users;
2✔
5833
        out_users.resize(out_n);
2✔
5834
        size_t n = 0;
2✔
5835
        realm_app_get_all_users(&app, out_users.data(), out_n, &n);
2✔
5836
        CHECK(n == out_n);
2!
5837

1✔
5838
        for (size_t i = 0; i < out_n; ++i)
6✔
5839
            realm_release(out_users[i]);
4✔
5840
        realm_release(sync_user_1);
2✔
5841
        realm_release(sync_user_2);
2✔
5842
    }
2✔
5843
    SECTION("realm_app_user_apikey_provider_client_fetch_apikeys") {
12✔
5844
        SECTION("Failure") {
4✔
5845
            AutoVerifiedEmailCredentials creds;
2✔
5846
            realm_user_t* sync_user_1 = nullptr;
2✔
5847
            realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5848
            realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5849
                                                                    realm_app_void_completion, nullptr, nullptr);
2✔
5850
            realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5851
            realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
2✔
5852
            auto callback = [](realm_userdata_t, realm_app_user_apikey_t[], size_t count, realm_app_error_t* error) {
2✔
5853
                CHECK(error);
2!
5854
                CHECK(count == 0);
2!
5855
            };
2✔
5856
            // Should fail with 403
1✔
5857
            auto res =
2✔
5858
                realm_app_user_apikey_provider_client_fetch_apikeys(&app, sync_user_1, callback, nullptr, nullptr);
2✔
5859
            REQUIRE(res == true);
2!
5860
            realm_release(sync_user_1);
2✔
5861
        }
2✔
5862
        SECTION("Success") {
4✔
5863
            TestAppSession session;
2✔
5864
            realm_app app(session.app());
2✔
5865

1✔
5866
            class AppObject {
2✔
5867
            public:
2✔
5868
                static AppObject& create()
2✔
5869
                {
6✔
5870
                    static AppObject obj;
6✔
5871
                    return obj;
6✔
5872
                }
6✔
5873
                const char* key() const
2✔
5874
                {
6✔
5875
                    return api_key_name.c_str();
6✔
5876
                }
6✔
5877

1✔
5878
            private:
2✔
5879
                AppObject()
2✔
5880
                {
2✔
5881
                    api_key_name = util::format("%1", random_string(15));
2✔
5882
                }
2✔
5883
                std::string api_key_name;
2✔
5884
            };
2✔
5885

1✔
5886
            auto create_api_callback = [](realm_userdata_t, realm_app_user_apikey_t* api_key,
2✔
5887
                                          const realm_app_error_t* error) {
2✔
5888
                REQUIRE_FALSE(error);
2!
5889
                REQUIRE(api_key);
2!
5890
                CHECK(strcmp(api_key->name, AppObject::create().key()) == 0);
2!
5891
            };
2✔
5892
            auto current_user = realm_app_get_current_user(&app);
2✔
5893
            auto state = realm_app_user_apikey_provider_client_create_apikey(
2✔
5894
                &app, current_user, AppObject::create().key(), create_api_callback, nullptr, nullptr);
2✔
5895
            REQUIRE(state);
2!
5896

1✔
5897
            auto fetch_apikey_callback = [](realm_userdata_t, realm_app_user_apikey_t* keys, size_t count,
2✔
5898
                                            realm_app_error_t* error) {
2✔
5899
                REQUIRE_FALSE(error);
2!
5900
                REQUIRE(keys);
2!
5901
                REQUIRE(count == 1);
2!
5902
                CHECK(strcmp(keys[0].name, AppObject::create().key()) == 0);
2!
5903
            };
2✔
5904
            state = realm_app_user_apikey_provider_client_fetch_apikeys(&app, current_user, fetch_apikey_callback,
2✔
5905
                                                                        nullptr, nullptr);
2✔
5906
            REQUIRE(state);
2!
5907
            realm_release(current_user);
2✔
5908
        }
2✔
5909
    }
4✔
5910
    realm_release(http_transport);
12✔
5911
}
12✔
5912

5913
TEST_CASE("app: flx-sync compensating writes C API support", "[sync][flx][c_api][baas]") {
2✔
5914
    using namespace realm::app;
2✔
5915
    FLXSyncTestHarness harness("c_api_comp_writes");
2✔
5916
    create_user_and_log_in(harness.app());
2✔
5917
    SyncTestFile test_config(harness.app()->current_user(), harness.schema(), realm::SyncConfig::FLXSyncEnabled{});
2✔
5918
    test_config.sync_config = std::make_shared<realm_sync_config_t>(*test_config.sync_config);
2✔
5919
    realm_sync_config_t* sync_config = static_cast<realm_sync_config_t*>(test_config.sync_config.get());
2✔
5920

1✔
5921
    struct TestState {
2✔
5922
        std::mutex mutex;
2✔
5923
        std::condition_variable cond_var;
2✔
5924
        std::vector<sync::CompensatingWriteErrorInfo> compensating_writes;
2✔
5925
    };
2✔
5926
    auto state = std::make_unique<TestState>();
2✔
5927
    realm_sync_config_set_error_handler(
2✔
5928
        sync_config,
2✔
5929
        [](realm_userdata_t user_data, realm_sync_session_t*, const realm_sync_error_t error) {
2✔
5930
            auto state = reinterpret_cast<TestState*>(user_data);
2✔
5931
            REQUIRE(error.status.error == RLM_ERR_SYNC_COMPENSATING_WRITE);
2!
5932
            REQUIRE(error.compensating_writes_length > 0);
2!
5933

1✔
5934
            std::lock_guard<std::mutex> lk(state->mutex);
2✔
5935
            for (size_t i = 0; i < error.compensating_writes_length; ++i) {
6✔
5936
                sync::CompensatingWriteErrorInfo err_info;
4✔
5937
                err_info.object_name = error.compensating_writes[i].object_name;
4✔
5938
                err_info.reason = error.compensating_writes[i].reason;
4✔
5939
                Mixed pk(c_api::from_capi(error.compensating_writes[i].primary_key));
4✔
5940
                err_info.primary_key = pk;
4✔
5941
                state->compensating_writes.push_back(std::move(err_info));
4✔
5942
            }
4✔
5943

1✔
5944
            state->cond_var.notify_one();
2✔
5945
        },
2✔
5946
        state.get(), [](realm_userdata_t) {});
2✔
5947

1✔
5948
    auto realm = Realm::get_shared_realm(test_config);
2✔
5949

1✔
5950
    auto mut_subs = realm->get_latest_subscription_set().make_mutable_copy();
2✔
5951
    auto table = realm->read_group().get_table("class_TopLevel");
2✔
5952
    mut_subs.insert_or_assign(Query(table).equal(table->get_column_key("queryable_str_field"), "bizz"));
2✔
5953
    mut_subs.commit();
2✔
5954

1✔
5955
    CppContext c(realm);
2✔
5956
    realm->begin_transaction();
2✔
5957
    auto obj_1_id = ObjectId::gen();
2✔
5958
    auto obj_2_id = ObjectId::gen();
2✔
5959
    Object::create(c, realm, "TopLevel",
2✔
5960
                   std::any(AnyDict{
2✔
5961
                       {"_id", obj_1_id},
2✔
5962
                       {"queryable_str_field", std::string{"foo"}},
2✔
5963
                   }));
2✔
5964
    Object::create(c, realm, "TopLevel",
2✔
5965
                   std::any(AnyDict{
2✔
5966
                       {"_id", obj_2_id},
2✔
5967
                       {"queryable_str_field", std::string{"bar"}},
2✔
5968
                   }));
2✔
5969
    realm->commit_transaction();
2✔
5970

1✔
5971
    std::unique_lock<std::mutex> lk(state->mutex);
2✔
5972
    state->cond_var.wait_for(lk, std::chrono::seconds(30), [&] {
4✔
5973
        return state->compensating_writes.size() == 2;
4✔
5974
    });
4✔
5975

1✔
5976
    auto errors = std::move(state->compensating_writes);
2✔
5977
    lk.unlock();
2✔
5978

1✔
5979
    std::sort(errors.begin(), errors.end(), [](const auto& lhs, const auto& rhs) {
3✔
5980
        return lhs.primary_key < rhs.primary_key;
3✔
5981
    });
3✔
5982

1✔
5983
    REQUIRE(errors.size() == 2);
2!
5984
    REQUIRE(errors[0].primary_key == obj_1_id);
2!
5985
    REQUIRE(errors[0].object_name == "TopLevel");
2!
5986
    REQUIRE_THAT(errors[0].reason, Catch::Matchers::ContainsSubstring("object is outside of the current query view"));
2✔
5987
    REQUIRE(errors[1].primary_key == obj_2_id);
2!
5988
    REQUIRE(errors[1].object_name == "TopLevel");
2!
5989
    REQUIRE_THAT(errors[1].reason, Catch::Matchers::ContainsSubstring("object is outside of the current query view"));
2✔
5990
}
2✔
5991

5992
TEST_CASE("app: flx-sync basic tests", "[sync][flx][c_api][baas]") {
2✔
5993
    using namespace realm::app;
2✔
5994

1✔
5995
    auto make_schema = [] {
2✔
5996
        Schema schema{{"Obj",
2✔
5997
                       {{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
2✔
5998
                        {"name", PropertyType::String | PropertyType::Nullable},
2✔
5999
                        {"value", PropertyType::Int | PropertyType::Nullable}}}};
2✔
6000

1✔
6001
        return FLXSyncTestHarness::ServerSchema{std::move(schema), {"name", "value"}};
2✔
6002
    };
2✔
6003

1✔
6004
    FLXSyncTestHarness harness("c_api_flx_sync_test", make_schema());
2✔
6005
    auto foo_obj_id = ObjectId::gen();
2✔
6006
    auto bar_obj_id = ObjectId::gen();
2✔
6007

1✔
6008
    harness.load_initial_data([&](SharedRealm& realm) {
2✔
6009
        CppContext c(realm);
2✔
6010
        Object::create(
2✔
6011
            c, realm, "Obj",
2✔
6012
            std::any(AnyDict{{"_id", foo_obj_id}, {"name", std::string{"foo"}}, {"value", static_cast<int64_t>(5)}}));
2✔
6013
        Object::create(c, realm, "Obj",
2✔
6014
                       std::any(AnyDict{
2✔
6015
                           {"_id", bar_obj_id}, {"name", std::string{"bar"}}, {"value", static_cast<int64_t>(10)}}));
2✔
6016
    });
2✔
6017

1✔
6018
    harness.do_with_new_realm([&](SharedRealm realm) {
2✔
6019
        realm_t c_wrap_realm(realm);
2✔
6020

1✔
6021
        wait_for_download(*realm);
2✔
6022
        {
2✔
6023
            auto empty_subs = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6024
            CHECK(realm_sync_subscription_set_size(empty_subs) == 0);
2!
6025
            CHECK(realm_sync_subscription_set_version(empty_subs) == 0);
2!
6026
            realm_sync_on_subscription_set_state_change_wait(
2✔
6027
                empty_subs, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6028
            realm_release(empty_subs);
2✔
6029
        }
2✔
6030
        realm_class_info_t table_info;
2✔
6031
        bool found;
2✔
6032
        CHECK(realm_find_class(&c_wrap_realm, "Obj", &found, &table_info));
2!
6033
        auto c_wrap_query_foo = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'foo'", 0, nullptr);
2✔
6034
        auto c_wrap_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
2✔
6035
        {
2✔
6036
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6037
            CHECK(sub != nullptr);
2!
6038
            auto new_subs = realm_sync_make_subscription_set_mutable(sub);
2✔
6039
            std::size_t index = -1;
2✔
6040
            bool inserted = false;
2✔
6041
            // realm_results_t
1✔
6042
            auto res = realm_sync_subscription_set_insert_or_assign_query(new_subs, c_wrap_query_foo, nullptr, &index,
2✔
6043
                                                                          &inserted);
2✔
6044
            CHECK(inserted == true);
2!
6045
            CHECK(index == 0);
2!
6046
            CHECK(res);
2!
6047
            auto subs = realm_sync_subscription_set_commit(new_subs);
2✔
6048
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6049
                subs, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6050
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6051
            realm_release(sub);
2✔
6052
            realm_release(new_subs);
2✔
6053
            realm_release(subs);
2✔
6054
        }
2✔
6055

1✔
6056
        wait_for_download(*realm);
2✔
6057
        {
2✔
6058
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6059
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6060
            size_t count = 0;
2✔
6061
            realm_results_count(results, &count);
2✔
6062
            CHECK(count == 1);
2!
6063
            auto object = realm_results_get_object(results, 0);
2✔
6064
            REQUIRE(realm_object_is_valid(object));
2!
6065
            REQUIRE(object->get_column_value<ObjectId>("_id") == foo_obj_id);
2!
6066
            realm_release(object);
2✔
6067
            realm_release(results);
2✔
6068
        }
2✔
6069

1✔
6070
        {
2✔
6071
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6072
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6073
            std::size_t index = -1;
2✔
6074
            bool inserted = false;
2✔
6075
            realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_bar, nullptr, &index, &inserted);
2✔
6076
            CHECK(inserted);
2!
6077
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6078
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6079
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6080
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6081
            realm_release(sub);
2✔
6082
            realm_release(mut_sub);
2✔
6083
            realm_release(sub_c);
2✔
6084
        }
2✔
6085

1✔
6086
        {
2✔
6087
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6088
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6089
            size_t count = 0;
2✔
6090
            realm_results_count(results, &count);
2✔
6091
            CHECK(count == 2);
2!
6092
            realm_release(results);
2✔
6093
        }
2✔
6094

1✔
6095
        {
2✔
6096
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6097
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6098
            auto s = realm_sync_find_subscription_by_query(sub, c_wrap_query_foo);
2✔
6099
            CHECK(s != nullptr);
2!
6100
            auto cloned_s = realm_clone(s);
2✔
6101
            CHECK(realm_equals(s, cloned_s));
2!
6102
            bool erased = false;
2✔
6103
            auto result = realm_sync_subscription_set_erase_by_query(mut_sub, c_wrap_query_foo, &erased);
2✔
6104
            CHECK(erased);
2!
6105
            CHECK(result);
2!
6106
            auto c_wrap_new_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
2✔
6107
            std::size_t index = -1;
2✔
6108
            bool inserted = false;
2✔
6109
            auto results = realm_query_find_all(c_wrap_new_query_bar);
2✔
6110
            bool updated =
2✔
6111
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, nullptr, &index, &inserted);
2✔
6112
            CHECK(!inserted);
2!
6113
            CHECK(updated);
2!
6114
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6115
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6116
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6117
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6118
            realm_release(s);
2✔
6119
            realm_release(cloned_s);
2✔
6120
            realm_release(sub);
2✔
6121
            realm_release(mut_sub);
2✔
6122
            realm_release(sub_c);
2✔
6123
            realm_release(results);
2✔
6124
            realm_release(c_wrap_new_query_bar);
2✔
6125
        }
2✔
6126

1✔
6127
        {
2✔
6128
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6129
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6130
            size_t count = 0;
2✔
6131
            realm_results_count(results, &count);
2✔
6132
            CHECK(count == 1);
2!
6133
            auto object = realm_results_get_object(results, 0);
2✔
6134
            REQUIRE(realm_object_is_valid(object));
2!
6135
            REQUIRE(object->get_column_value<ObjectId>("_id") == bar_obj_id);
2!
6136
            realm_release(object);
2✔
6137
            realm_release(results);
2✔
6138
        }
2✔
6139

1✔
6140
        {
2✔
6141
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6142
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6143
            auto cleared = realm_sync_subscription_set_clear(mut_sub);
2✔
6144
            CHECK(cleared);
2!
6145
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6146
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6147
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6148
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6149
            realm_release(sub);
2✔
6150
            realm_release(mut_sub);
2✔
6151
            realm_release(sub_c);
2✔
6152
        }
2✔
6153

1✔
6154
        {
2✔
6155
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6156
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6157
            size_t count = std::numeric_limits<std::size_t>::max();
2✔
6158
            realm_results_count(results, &count);
2✔
6159
            CHECK(count == 0);
2!
6160
            realm_release(results);
2✔
6161
        }
2✔
6162

1✔
6163
        {
2✔
6164
            auto c_wrap_new_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
2✔
6165
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6166
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6167
            std::size_t index = -1;
2✔
6168
            bool inserted = false;
2✔
6169
            auto results = realm_query_find_all(c_wrap_new_query_bar);
2✔
6170
            bool success =
2✔
6171
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "bar", &index, &inserted);
2✔
6172
            CHECK(inserted);
2!
6173
            CHECK(success);
2!
6174
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6175
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6176
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6177
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6178
            realm_release(sub);
2✔
6179
            realm_release(mut_sub);
2✔
6180
            realm_release(sub_c);
2✔
6181
            realm_release(results);
2✔
6182
            realm_release(c_wrap_new_query_bar);
2✔
6183
        }
2✔
6184

1✔
6185
        {
2✔
6186
            realm->refresh();
2✔
6187
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6188
            size_t count = std::numeric_limits<std::size_t>::max();
2✔
6189
            realm_results_count(results, &count);
2✔
6190
            CHECK(count == 1);
2!
6191
            realm_release(results);
2✔
6192
        }
2✔
6193

1✔
6194
        {
2✔
6195
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6196
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6197
            bool erased = false;
2✔
6198
            realm_sync_subscription_set_erase_by_name(mut_sub, "bar", &erased);
2✔
6199
            CHECK(erased);
2!
6200
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6201
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
6202
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6203
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
6204
            realm_release(sub);
2✔
6205
            realm_release(mut_sub);
2✔
6206
            realm_release(sub_c);
2✔
6207
        }
2✔
6208

1✔
6209
        {
2✔
6210
            realm_refresh(&c_wrap_realm, nullptr);
2✔
6211
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
6212
            size_t count = std::numeric_limits<std::size_t>::max();
2✔
6213
            realm_results_count(results, &count);
2✔
6214
            CHECK(count == 0);
2!
6215
            realm_release(results);
2✔
6216
        }
2✔
6217

1✔
6218
        {
2✔
6219
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6220
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6221
            std::size_t index = -1;
2✔
6222
            bool inserted = false;
2✔
6223
            auto results = realm_query_find_all(c_wrap_query_bar);
2✔
6224
            bool success =
2✔
6225
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, nullptr, &index, &inserted);
2✔
6226
            CHECK(inserted);
2!
6227
            CHECK(success);
2!
6228
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6229
            // lambdas with state cannot be easily converted to function pointers, add a simple singleton that syncs
1✔
6230
            // the state among threads
1✔
6231
            struct SyncObject {
2✔
6232
                std::mutex m_mutex;
2✔
6233
                std::condition_variable m_cv;
2✔
6234
                realm_flx_sync_subscription_set_state_e m_state{RLM_SYNC_SUBSCRIPTION_UNCOMMITTED};
2✔
6235
                void* m_userdata;
2✔
6236

1✔
6237
                static SyncObject& create()
2✔
6238
                {
4✔
6239
                    static SyncObject sync_object;
4✔
6240
                    return sync_object;
4✔
6241
                }
4✔
6242

1✔
6243
                void set_state_and_notify(void* userdata, realm_flx_sync_subscription_set_state_e state)
2✔
6244
                {
2✔
6245
                    {
2✔
6246
                        std::lock_guard<std::mutex> guard{m_mutex};
2✔
6247
                        m_state = state;
2✔
6248
                        m_userdata = userdata;
2✔
6249
                    }
2✔
6250
                    m_cv.notify_one();
2✔
6251
                }
2✔
6252

1✔
6253
                realm_flx_sync_subscription_set_state_e wait_state()
2✔
6254
                {
2✔
6255
                    using namespace std::chrono_literals;
2✔
6256
                    std::unique_lock<std::mutex> lock{m_mutex};
2✔
6257
                    bool completed_within_time_limit = m_cv.wait_for(lock, 5s, [this]() {
4✔
6258
                        return m_state == RLM_SYNC_SUBSCRIPTION_COMPLETE && m_userdata != nullptr;
4✔
6259
                    });
4✔
6260
                    CHECK(completed_within_time_limit);
2!
6261
                    return m_state;
2✔
6262
                }
2✔
6263
            };
2✔
6264

1✔
6265
            auto callback = [](void* userdata, realm_flx_sync_subscription_set_state_e sub_state) {
2✔
6266
                SyncObject::create().set_state_and_notify(userdata, sub_state);
2✔
6267
            };
2✔
6268
            int userdata = 0;
2✔
6269
            realm_sync_on_subscription_set_state_change_async(
2✔
6270
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE, callback, &userdata,
2✔
6271
                nullptr);
2✔
6272
            CHECK(SyncObject::create().wait_state() ==
2!
6273
                  realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
6274

1✔
6275
            realm_release(sub);
2✔
6276
            realm_release(mut_sub);
2✔
6277
            realm_release(sub_c);
2✔
6278
            realm_release(results);
2✔
6279
        }
2✔
6280

1✔
6281
        {
2✔
6282
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6283
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6284
            std::size_t index = -1;
2✔
6285
            bool inserted = false;
2✔
6286
            auto results = realm_query_find_all(c_wrap_query_bar);
2✔
6287
            bool success =
2✔
6288
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "test_bar", &index, &inserted);
2✔
6289
            CHECK(inserted);
2!
6290
            CHECK(success);
2!
6291
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6292
            auto sub_by_res = realm_sync_find_subscription_by_results(sub_c, results);
2✔
6293
            CHECK(sub_by_res);
2!
6294
            const auto realm_flx_sync_sub = realm_sync_find_subscription_by_name(sub_c, "test_bar");
2✔
6295
            REQUIRE(realm_flx_sync_sub);
2!
6296

1✔
6297
            const realm_object_id_t& object_id = realm_sync_subscription_id(realm_flx_sync_sub);
2✔
6298
            CHECK(!realm::c_api::from_capi(object_id).to_string().empty());
2!
6299

1✔
6300
            realm_timestamp_t create_time = realm_sync_subscription_created_at(realm_flx_sync_sub);
2✔
6301
            realm_timestamp_t update_time = realm_sync_subscription_updated_at(realm_flx_sync_sub);
2✔
6302
            CHECK(create_time.seconds == update_time.seconds);
2!
6303
            CHECK(create_time.nanoseconds == update_time.nanoseconds);
2!
6304
            CHECK(create_time.seconds != 0);
2!
6305
            CHECK(create_time.seconds != 0);
2!
6306

1✔
6307
            const realm_string_t& name = realm_sync_subscription_name(realm_flx_sync_sub);
2✔
6308
            REQUIRE(name.size != 0);
2!
6309
            CHECK(std::string{name.data, name.size} == "test_bar");
2!
6310

1✔
6311
            auto mut_sub_1 = realm_sync_make_subscription_set_mutable(sub_c);
2✔
6312
            bool erased = false;
2✔
6313
            CHECK(realm_sync_subscription_set_erase_by_id(mut_sub_1, &object_id, &erased));
2!
6314
            CHECK(erased);
2!
6315
            auto sub_c_1 = realm_sync_subscription_set_commit(mut_sub_1);
2✔
6316

1✔
6317
            const auto sub_not_found = realm_sync_find_subscription_by_name(sub_c_1, "test_bar");
2✔
6318
            CHECK(!sub_not_found);
2!
6319

1✔
6320
            realm_release(sub);
2✔
6321
            realm_release(mut_sub);
2✔
6322
            realm_release(results);
2✔
6323
            realm_release(sub_c);
2✔
6324
            realm_release(sub_by_res);
2✔
6325
            realm_release(realm_flx_sync_sub);
2✔
6326
            realm_release(mut_sub_1);
2✔
6327
            realm_release(sub_c_1);
2✔
6328
            realm_release(sub_not_found);
2✔
6329
        }
2✔
6330

1✔
6331
        {
2✔
6332
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
6333
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
6334
            std::size_t index = -1;
2✔
6335
            bool inserted = false;
2✔
6336
            auto results = realm_query_find_all(c_wrap_query_bar);
2✔
6337
            bool success =
2✔
6338
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "test_bar", &index, &inserted);
2✔
6339
            CHECK(inserted);
2!
6340
            CHECK(success);
2!
6341
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
6342
            auto sub_by_res = realm_sync_find_subscription_by_results(sub_c, results);
2✔
6343
            CHECK(sub_by_res);
2!
6344

1✔
6345
            auto mut_sub_1 = realm_sync_make_subscription_set_mutable(sub_c);
2✔
6346
            bool erased = false;
2✔
6347
            realm_sync_subscription_set_erase_by_results(mut_sub_1, results, &erased);
2✔
6348
            CHECK(erased);
2!
6349
            auto sub_c_1 = realm_sync_subscription_set_commit(mut_sub_1);
2✔
6350

1✔
6351
            realm_release(sub);
2✔
6352
            realm_release(mut_sub);
2✔
6353
            realm_release(results);
2✔
6354
            realm_release(sub_c);
2✔
6355
            realm_release(sub_by_res);
2✔
6356
            realm_release(mut_sub_1);
2✔
6357
            realm_release(sub_c_1);
2✔
6358
        }
2✔
6359

1✔
6360
        realm_release(c_wrap_query_foo);
2✔
6361
        realm_release(c_wrap_query_bar);
2✔
6362
    });
2✔
6363
}
2✔
6364

6365
TEST_CASE("C API app: websocket provider", "[sync][app][c_api][baas]") {
2✔
6366
    using namespace realm::app;
2✔
6367
    using namespace realm::sync;
2✔
6368
    using namespace realm::sync::websocket;
2✔
6369

1✔
6370
    struct TestWebSocketObserverShim : sync::WebSocketObserver {
2✔
6371
    public:
2✔
6372
        explicit TestWebSocketObserverShim(std::shared_ptr<sync::WebSocketObserver> observer)
2✔
6373
            : m_observer(observer)
2✔
6374
        {
2✔
6375
        }
2✔
6376

1✔
6377
        void websocket_connected_handler(const std::string& protocol) override
2✔
6378
        {
2✔
6379
            return m_observer->websocket_connected_handler(protocol);
2✔
6380
        }
2✔
6381

1✔
6382
        void websocket_error_handler() override
2✔
6383
        {
1✔
UNCOV
6384
            m_observer->websocket_error_handler();
×
UNCOV
6385
        }
×
6386

1✔
6387
        bool websocket_binary_message_received(util::Span<const char> data) override
2✔
6388
        {
9✔
6389
            return m_observer->websocket_binary_message_received(data);
9✔
6390
        }
9✔
6391

1✔
6392
        bool websocket_closed_handler(bool was_clean, WebSocketError error, std::string_view msg) override
2✔
6393
        {
1✔
UNCOV
6394
            return m_observer->websocket_closed_handler(was_clean, error, msg);
×
UNCOV
6395
        }
×
6396

1✔
6397
    private:
2✔
6398
        std::shared_ptr<sync::WebSocketObserver> m_observer;
2✔
6399
    };
2✔
6400

1✔
6401
    struct TestWebSocket : realm::c_api::WrapC, WebSocketInterface {
2✔
6402
    public:
2✔
6403
        TestWebSocket(DefaultSocketProvider& socket_provider, realm_websocket_endpoint_t endpoint,
2✔
6404
                      realm_websocket_observer_t* realm_websocket_observer)
2✔
6405
        {
2✔
6406
            WebSocketEndpoint ws_endpoint;
2✔
6407
            ws_endpoint.address = endpoint.address;
2✔
6408
            ws_endpoint.port = endpoint.port;
2✔
6409
            ws_endpoint.path = endpoint.path;
2✔
6410
            for (size_t i = 0; i < endpoint.num_protocols; ++i) {
20✔
6411
                ws_endpoint.protocols.push_back(endpoint.protocols[i]);
18✔
6412
            }
18✔
6413
            ws_endpoint.is_ssl = endpoint.is_ssl;
2✔
6414

1✔
6415
            auto observer = std::make_unique<TestWebSocketObserverShim>(*realm_websocket_observer);
2✔
6416
            m_websocket = socket_provider.connect(std::move(observer), std::move(ws_endpoint));
2✔
6417
        }
2✔
6418

1✔
6419
        void async_write_binary(util::Span<const char> data, SyncSocketProvider::FunctionHandler&& handler) override
2✔
6420
        {
16✔
6421
            m_websocket->async_write_binary(data, std::move(handler));
16✔
6422
        }
16✔
6423

1✔
6424
    private:
2✔
6425
        std::unique_ptr<WebSocketInterface> m_websocket;
2✔
6426
    };
2✔
6427

1✔
6428
    struct TestSyncTimer : realm::c_api::WrapC, SyncSocketProvider::Timer {
2✔
6429
    public:
2✔
6430
        TestSyncTimer(DefaultSocketProvider& socket_provider, std::chrono::milliseconds delay,
2✔
6431
                      SyncSocketProvider::FunctionHandler&& handler)
2✔
6432
        {
14✔
6433
            m_timer = socket_provider.create_timer(delay, std::move(handler));
14✔
6434
        }
14✔
6435

1✔
6436
        void cancel() override
2✔
6437
        {
14✔
6438
            m_timer->cancel();
14✔
6439
        }
14✔
6440

1✔
6441
    private:
2✔
6442
        SyncSocketProvider::SyncTimer m_timer;
2✔
6443
    };
2✔
6444

1✔
6445
    struct TestData {
2✔
6446
        DefaultSocketProvider* socket_provider;
2✔
6447
        int free_count = 0;
2✔
6448
    };
2✔
6449

1✔
6450
    auto logger = std::make_shared<util::StderrLogger>();
2✔
6451
    DefaultSocketProvider default_socket_provider(logger, "SocketProvider");
2✔
6452

1✔
6453
    auto free_fn = [](realm_userdata_t user_ptr) {
2✔
6454
        auto test_data = static_cast<TestData*>(user_ptr);
2✔
6455
        REQUIRE(test_data);
2!
6456
        test_data->free_count++;
2✔
6457
    };
2✔
6458
    auto post_fn = [](realm_userdata_t userdata, realm_sync_socket_callback_t* callback) {
16✔
6459
        auto test_data = static_cast<TestData*>(userdata);
16✔
6460
        REQUIRE(test_data);
16!
6461
        auto cb = [callback_copy = callback](Status s) {
16✔
6462
            realm_sync_socket_callback_complete(callback_copy, static_cast<realm_errno_e>(s.code()),
16✔
6463
                                                s.reason().c_str());
16✔
6464
        };
16✔
6465
        test_data->socket_provider->post(std::move(cb));
16✔
6466
    };
16✔
6467
    auto create_timer_fn = [](realm_userdata_t userdata, uint64_t delay_ms,
2✔
6468
                              realm_sync_socket_callback_t* callback) -> realm_sync_socket_timer_t {
14✔
6469
        auto test_data = static_cast<TestData*>(userdata);
14✔
6470
        REQUIRE(test_data);
14!
6471
        return static_cast<realm_sync_socket_timer_t>(new TestSyncTimer(
14✔
6472
            *test_data->socket_provider, std::chrono::milliseconds(delay_ms), std::move(**callback)));
14✔
6473
    };
14✔
6474
    auto cancel_timer_fn = [](realm_userdata_t, realm_sync_socket_timer_t sync_timer) {
14✔
6475
        auto timer = static_cast<TestSyncTimer*>(sync_timer);
14✔
6476
        REQUIRE(timer);
14!
6477
        timer->cancel();
14✔
6478
    };
14✔
6479
    auto free_timer_fn = [](realm_userdata_t, realm_sync_socket_timer_t sync_timer) {
14✔
6480
        realm_release(sync_timer);
14✔
6481
    };
14✔
6482
    auto websocket_connect_fn =
2✔
6483
        [](realm_userdata_t userdata, realm_websocket_endpoint_t endpoint,
2✔
6484
           realm_websocket_observer_t* realm_websocket_observer) -> realm_sync_socket_websocket_t {
2✔
6485
        auto test_data = static_cast<TestData*>(userdata);
2✔
6486
        REQUIRE(test_data);
2!
6487
        return static_cast<realm_sync_socket_websocket_t>(
2✔
6488
            new TestWebSocket(*test_data->socket_provider, endpoint, realm_websocket_observer));
2✔
6489
    };
2✔
6490
    auto websocket_async_write_fn = [](realm_userdata_t, realm_sync_socket_websocket_t sync_websocket,
2✔
6491
                                       const char* data, size_t size, realm_sync_socket_callback_t* callback) {
16✔
6492
        auto websocket = static_cast<TestWebSocket*>(sync_websocket);
16✔
6493
        REQUIRE(websocket);
16!
6494
        websocket->async_write_binary(util::Span{data, size}, std::move(**callback));
16✔
6495
        realm_release(callback);
16✔
6496
    };
16✔
6497
    auto websocket_free_fn = [](realm_userdata_t, realm_sync_socket_websocket_t sync_websocket) {
2✔
6498
        realm_release(sync_websocket);
2✔
6499
    };
2✔
6500

1✔
6501
    // Test drive.
1✔
6502
    TestData test_data{&default_socket_provider};
2✔
6503
    {
2✔
6504
        auto socket_provider = realm_sync_socket_new(
2✔
6505
            static_cast<realm_userdata_t>(&test_data), free_fn, post_fn, create_timer_fn, cancel_timer_fn,
2✔
6506
            free_timer_fn, websocket_connect_fn, websocket_async_write_fn, websocket_free_fn);
2✔
6507

1✔
6508

1✔
6509
        FLXSyncTestHarness harness("c_api_websocket_provider", FLXSyncTestHarness::default_server_schema(),
2✔
6510
                                   instance_of<SynchronousTestTransport>, *socket_provider);
2✔
6511

1✔
6512
        SyncTestFile test_config(harness.app()->current_user(), harness.schema(),
2✔
6513
                                 realm::SyncConfig::FLXSyncEnabled{});
2✔
6514
        auto realm = Realm::get_shared_realm(test_config);
2✔
6515
        REQUIRE(!wait_for_download(*realm));
2!
6516

1✔
6517
        realm_release(socket_provider);
2✔
6518
    }
2✔
6519

1✔
6520
    default_socket_provider.stop(true);
2✔
6521
    REQUIRE(test_data.free_count == 1);
2!
6522
}
2✔
6523
#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