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

realm / realm-core / 1772

20 Oct 2023 07:34PM UTC coverage: 91.61% (+0.04%) from 91.567%
1772

push

Evergreen

web-flow
Added sync socket result enum for sync socket callback handlers in C API (#7015)

* Added sync socket result enum for sync socket callback handlers in C API
* Updated changelog
* CAPI: timer callbacks are now released by canceled/complete function
* Updated c_api tests to use all sync socket c_api functions
* Additional updates to sync socket c api test
* Added CAPI write callback manager to manage async write callbacks
* Pass error codes up to default socket provider for async_write_binary() callbacks
* Removed async write callback manager from CAPI
* Updated changelog after release
* clang format and updates from review
* Update async_write_binary() error handling to not throw exception
* Updated a few comments
* Another comment update
* Updates from review

94360 of 173622 branches covered (0.0%)

111 of 150 new or added lines in 8 files covered. (74.0%)

66 existing lines in 17 files now uncovered.

230703 of 251832 relevant lines covered (91.61%)

6730695.32 hits per line

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

99.45
/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,124✔
68
    if (!x) {
8,124✔
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,124✔
75
}
8,124✔
76

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

85
realm_value_t rlm_int_val(int64_t n)
86
{
784✔
87
    realm_value_t val;
784✔
88
    val.type = RLM_TYPE_INT;
784✔
89
    val.integer = n;
784✔
90
    return val;
784✔
91
}
784✔
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
            return true;
90✔
213
        case RLM_TYPE_INT:
86✔
214
            return lhs.integer == rhs.integer;
86✔
215
        case RLM_TYPE_BOOL:
20✔
216
            return lhs.boolean == rhs.boolean;
20✔
217
        case RLM_TYPE_STRING:
62✔
218
            return strncmp(lhs.string.data, rhs.string.data, lhs.string.size) == 0;
62✔
219
        case RLM_TYPE_BINARY:
20✔
220
            return memcmp(lhs.binary.data, rhs.binary.data, lhs.binary.size) == 0;
20✔
221
        case RLM_TYPE_TIMESTAMP:
20✔
222
            return lhs.timestamp.seconds == rhs.timestamp.seconds &&
20✔
223
                   lhs.timestamp.nanoseconds == rhs.timestamp.nanoseconds;
20✔
224
        case RLM_TYPE_FLOAT:
20✔
225
            return lhs.fnum == rhs.fnum;
20✔
226
        case RLM_TYPE_DOUBLE:
20✔
227
            return lhs.dnum == rhs.dnum;
20✔
228
        case RLM_TYPE_DECIMAL128:
20✔
229
            return lhs.decimal128.w[0] == rhs.decimal128.w[0] && lhs.decimal128.w[1] == rhs.decimal128.w[1];
20✔
230
        case RLM_TYPE_OBJECT_ID:
20✔
231
            return memcmp(lhs.object_id.bytes, rhs.object_id.bytes, 12) == 0;
20✔
232
        case RLM_TYPE_LINK:
4✔
233
            return lhs.link.target_table == rhs.link.target_table && lhs.link.target == rhs.link.target;
4✔
234
        case RLM_TYPE_UUID:
20✔
235
            return memcmp(lhs.uuid.bytes, rhs.uuid.bytes, 16) == 0;
20✔
236
    }
×
237
    REALM_TERMINATE("");
×
238
}
×
239

240
struct RealmReleaseDeleter {
241
    void operator()(void* ptr)
242
    {
2,660✔
243
        realm_release(ptr);
2,660✔
244
    }
2,660✔
245
};
246

247
template <class T>
248
using CPtr = std::unique_ptr<T, RealmReleaseDeleter>;
249

250
template <class T>
251
CPtr<T> cptr(T* ptr)
252
{
2,618✔
253
    return CPtr<T>{ptr};
2,618✔
254
}
2,618✔
255

256
template <class T>
257
CPtr<T> cptr_checked(T* ptr)
258
{
1,546✔
259
    return cptr(checked(ptr));
1,546✔
260
}
1,546✔
261

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

269
template <class T>
270
CPtr<T> clone_cptr(const T* ptr)
271
{
48✔
272
    void* clone = realm_clone(ptr);
48✔
273
    return CPtr<T>{static_cast<T*>(clone)};
48✔
274
}
48✔
275
} // anonymous namespace
276

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

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

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

314
TEST_CASE("C API (non-database)", "[c_api]") {
48✔
315
    SECTION("realm_get_library_version_numbers()") {
48✔
316
        int major, minor, patch;
2✔
317
        const char* extra;
2✔
318
        realm_get_library_version_numbers(&major, &minor, &patch, &extra);
2✔
319

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

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

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

24✔
336
    SECTION("realm_get_last_error()") {
48✔
337
        CHECK(!realm_get_last_error(nullptr));
2!
338
        CHECK(!realm_clear_last_error());
2!
339

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

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

24✔
352
    SECTION("realm_get_last_error_as_async_error()") {
48✔
353
        CHECK(!realm_get_last_error_as_async_error());
4!
354

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

2✔
360
        realm_async_error_t* async_err = realm_get_last_error_as_async_error();
4✔
361
        CHECK(async_err);
4!
362

2✔
363
        realm_error_t err;
4✔
364
        CHECK(realm_get_async_error(async_err, &err));
4!
365
        CHECK_FALSE(realm_get_async_error(nullptr, &err));
4!
366

2✔
367
        CHECK(err.error == RLM_ERR_RUNTIME);
4!
368
        CHECK(std::string{err.message} == "Synthetic error");
4!
369

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

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

2✔
385
        realm_release(async_err);
4✔
386
        realm_clear_last_error();
4✔
387
    }
4✔
388

24✔
389
    SECTION("realm_clear_last_error()") {
48✔
390
        auto synthetic = []() {
2✔
391
            throw std::runtime_error("Synthetic error");
2✔
392
        };
2✔
393
        CHECK(!realm_wrap_exceptions(synthetic));
2!
394

1✔
395
        CHECK(realm_clear_last_error());
2!
396
        CHECK(!realm_get_last_error(nullptr));
2!
397
    }
2✔
398

24✔
399
    SECTION("realm_clone() error") {
48✔
400
        // realm_config_t is not clonable
1✔
401
        auto config = cptr(realm_config_new());
2✔
402
        CHECK(!realm_clone(config.get()));
2!
403
        CHECK_ERR(RLM_ERR_NOT_CLONABLE);
2!
404
    }
2✔
405

24✔
406
    SECTION("realm_create_thread_safe_reference() error") {
48✔
407
        // realm_config_t is not sendable between threads
1✔
408
        auto config = cptr(realm_config_new());
2✔
409
        CHECK(!realm_create_thread_safe_reference(config.get()));
2!
410
        CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
411
    }
2✔
412

24✔
413
    SECTION("realm_is_frozen() false by default") {
48✔
414
        // realm_config_t cannot be frozen, so is never frozen
1✔
415
        auto config = cptr(realm_config_new());
2✔
416
        CHECK(!realm_is_frozen(config.get()));
2!
417
    }
2✔
418

24✔
419
    SECTION("realm_equals() with different object types returns false") {
48✔
420
        auto config = cptr(realm_config_new());
2✔
421
        auto schema = cptr(realm_schema_new(nullptr, 0, nullptr));
2✔
422
        CHECK(!realm_equals(config.get(), schema.get()));
2!
423
        CHECK(!realm_equals(schema.get(), config.get()));
2!
424
    }
2✔
425

24✔
426
    SECTION("realm_config_t") {
48✔
427
        auto config = cptr(realm_config_new());
24✔
428

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

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

1✔
439
            uint8_t buffer[64];
2✔
440
            size_t len = realm_config_get_encryption_key(config.get(), buffer);
2✔
441
            CHECK(len == 64);
2!
442

1✔
443
            CHECK(!realm_config_set_encryption_key(config.get(), key, 63));
2!
444
            CHECK_ERR(RLM_ERR_INVALID_ENCRYPTION_KEY);
2!
445
        }
2✔
446

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

12✔
457
        SECTION("realm_config_set_schema_version()") {
24✔
458
            realm_config_set_schema_version(config.get(), 26);
2✔
459
            CHECK(realm_config_get_schema_version(config.get()) == 26);
2!
460
        }
2✔
461

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

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

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

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

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

12✔
509
        SECTION("realm_config_set_max_number_of_active_versions()") {
24✔
510
            realm_config_set_max_number_of_active_versions(config.get(), 999);
2✔
511
            CHECK(realm_config_get_max_number_of_active_versions(config.get()) == 999);
2!
512
        }
2✔
513

12✔
514
        SECTION("realm_config_set_in_memory()") {
24✔
515
            realm_config_set_in_memory(config.get(), true);
2✔
516
            CHECK(realm_config_get_in_memory(config.get()) == true);
2!
517
        }
2✔
518

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

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

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

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

1✔
552
        realm_app_config_set_default_request_timeout(app_config.get(), request_timeout);
2✔
553
        CHECK(app_config->default_request_timeout_ms == request_timeout);
2!
554

1✔
555
        realm_app_config_set_platform_version(app_config.get(), "some_platform_version");
2✔
556
        CHECK(app_config->device_info.platform_version == "some_platform_version");
2!
557

1✔
558
        realm_app_config_set_sdk_version(app_config.get(), "some_sdk_version");
2✔
559
        CHECK(app_config->device_info.sdk_version == "some_sdk_version");
2!
560

1✔
561
        realm_app_config_set_sdk(app_config.get(), "some_sdk_name");
2✔
562
        CHECK(app_config->device_info.sdk == "some_sdk_name");
2!
563

1✔
564
        realm_app_config_set_device_name(app_config.get(), "some_device_name");
2✔
565
        CHECK(app_config->device_info.device_name == "some_device_name");
2!
566

1✔
567
        realm_app_config_set_device_version(app_config.get(), "some_device_version");
2✔
568
        CHECK(app_config->device_info.device_version == "some_device_version");
2!
569

1✔
570
        realm_app_config_set_framework_name(app_config.get(), "some_framework_name");
2✔
571
        CHECK(app_config->device_info.framework_name == "some_framework_name");
2!
572

1✔
573
        realm_app_config_set_framework_version(app_config.get(), "some_framework_version");
2✔
574
        CHECK(app_config->device_info.framework_version == "some_framework_version");
2!
575

1✔
576
        realm_app_config_set_bundle_id(app_config.get(), "some_bundle_id");
2✔
577
        CHECK(app_config->device_info.bundle_id == "some_bundle_id");
2!
578

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

590
namespace {
591

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

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

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

309✔
647
    REALM_ASSERT(num_names == num_types);
618✔
648
    REALM_ASSERT(num_nullable_names == num_types);
618✔
649
    REALM_ASSERT(num_list_names == num_types);
618✔
650
    REALM_ASSERT(num_nullable_list_names == num_types);
618✔
651
    REALM_ASSERT(num_set_names == num_types);
618✔
652
    REALM_ASSERT(num_nullable_set_names == num_types);
618✔
653
    REALM_ASSERT(num_dict_names == num_types);
618✔
654
    REALM_ASSERT(num_nullable_dict_names == num_types);
618✔
655

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

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

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

309✔
724
    properties.push_back(link);
618✔
725
    properties.push_back(link_list);
618✔
726
    properties.push_back(link_set);
618✔
727
    properties.push_back(link_dict);
618✔
728

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

309✔
738
    properties.push_back(mixed);
618✔
739
    // properties.push_back(mixed_list);
309✔
740

309✔
741
    return properties;
618✔
742
}
618✔
743

744
CPtr<realm_schema_t> make_schema()
745
{
320✔
746
    auto foo_properties = all_property_types("Bar");
320✔
747

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

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

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

160✔
850
    return cptr(realm_schema_new(classes, 3, class_properties));
320✔
851
}
320✔
852

853
CPtr<realm_config_t> make_config(const char* filename, bool set_schema = true)
854
{
320✔
855
    auto config = cptr(realm_config_new());
320✔
856
    realm_config_set_path(config.get(), filename);
320✔
857
    realm_config_set_schema_mode(config.get(), RLM_SCHEMA_MODE_AUTOMATIC);
320✔
858

160✔
859
    if (set_schema) {
320✔
860
        auto schema = make_schema();
2✔
861
        CHECK(checked(schema.get()));
2!
862
        REQUIRE(checked(realm_schema_validate(schema.get(), RLM_SCHEMA_VALIDATION_BASIC)));
2!
863
        realm_config_set_schema(config.get(), schema.get());
2✔
864
        realm_config_set_schema_version(config.get(), 0);
2✔
865
    }
2✔
866

160✔
867
    realm_config_set_automatic_change_notifications(config.get(), true);
320✔
868
    realm_config_set_max_number_of_active_versions(config.get(), 1000);
320✔
869

160✔
870
    return config;
320✔
871
}
320✔
872

873
struct ConfigUserdata {
874
    size_t num_initializations = 0;
875
    size_t num_migrations = 0;
876
    size_t num_compact_on_launch = 0;
877
};
878

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

886
void free_data(void* userdata_p)
887
{
2✔
888
    free(userdata_p);
2✔
889
}
2✔
890

891
void delete_data(void* userdata_p)
892
{
4✔
893
    delete static_cast<ConfigUserdata*>(userdata_p);
4✔
894
}
4✔
895

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

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

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

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

935
struct LogUserData {
936
    std::vector<std::string> log;
937
};
938

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

945
} // anonymous namespace
946

947
TEST_CASE("C API", "[c_api]") {
298✔
948
    TestFile test_file;
298✔
949

149✔
950
    SECTION("schema in config") {
298✔
951
        TestFile test_file_2;
20✔
952

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

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

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

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

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

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

10✔
1011
        SECTION("migrate schema and delete old table") {
20✔
1012
            TestFile test_file_3;
2✔
1013
            ConfigUserdata userdata;
2✔
1014

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

10✔
1075
        SECTION("migration callback rename property") {
20✔
1076
            TestFile test_file_3;
2✔
1077
            ConfigUserdata userdata;
2✔
1078

1✔
1079
            realm_config_set_migration_function(config.get(), migrate_schema_rename_prop, &userdata, nullptr);
2✔
1080

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

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

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

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

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

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

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

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

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

1✔
1200
            struct MyExceptionWrapper {
2✔
1201
                std::exception_ptr m_ptr{nullptr};
2✔
1202
            };
2✔
1203

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

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

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

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

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

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

149✔
1274
        realm = realm_open(config.get());
298✔
1275
        REQUIRE(checked(realm));
298!
1276
        REQUIRE(checked(realm_update_schema(realm, schema.get())));
298!
1277

149✔
1278
        CHECK(!realm_equals(realm, nullptr));
298!
1279

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

149✔
1286
    CHECK(realm_get_num_classes(realm) == 3);
298!
1287

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

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

1✔
1304
        auto realm2 = cptr_checked(_realm_from_native_ptr(&native, sizeof(native)));
2✔
1305
        CHECK(realm_equals(realm2.get(), realm));
2!
1306
    }
2✔
1307

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

1✔
1317
        realm_begin_write(realm);
2✔
1318
        realm_commit(realm);
2✔
1319
        CHECK(realm_changed_callback_called);
2!
1320
    }
2✔
1321

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

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

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

1✔
1353
        realm_begin_write(realm);
2✔
1354
        realm_begin_read(realm2.get());
2✔
1355
        realm_commit(realm);
2✔
1356

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

1✔
1364
        realm_refresh(realm2.get(), nullptr);
2✔
1365
        REQUIRE(token);
2!
1366
        CHECK(realm_refresh_callback_called);
2!
1367
    }
2✔
1368

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

1✔
1374
        realm_begin_write(realm);
2✔
1375
        realm_begin_read(realm2.get());
2✔
1376
        realm_commit(realm);
2✔
1377

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

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

1✔
1394
        auto realm2 = cptr_checked(realm_freeze(realm));
2✔
1395
        CHECK(!realm_is_frozen(realm));
2!
1396
        CHECK(realm_is_frozen(realm2.get()));
2!
1397
        CHECK(realm != realm2.get());
2!
1398

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

1✔
1406
        realm_refresh(realm, nullptr);
2✔
1407
        CHECK_FALSE(realm_refresh_callback_called);
2!
1408
    }
2✔
1409

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

1✔
1414
        auto realm2 = cptr_checked(realm_freeze(realm));
2✔
1415
        CHECK(!realm_is_frozen(realm));
2!
1416
        CHECK(realm_is_frozen(realm2.get()));
2!
1417
        CHECK(realm != realm2.get());
2!
1418

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

1✔
1426
        realm_refresh(realm, nullptr);
2✔
1427
        CHECK(token == nullptr);
2!
1428
        CHECK_FALSE(realm_refresh_callback_called);
2!
1429
    }
2✔
1430

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

1✔
1441
        auto int_property = realm_property_info_t{
2✔
1442
            "int", "", RLM_PROPERTY_TYPE_INT,    RLM_COLLECTION_TYPE_NONE,
2✔
1443
            "",    "", RLM_INVALID_PROPERTY_KEY, RLM_PROPERTY_NORMAL,
2✔
1444
        };
2✔
1445
        realm_property_info_t* baz_properties = &int_property;
2✔
1446

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

1✔
1467
        properties[num_classes] = baz_properties;
2✔
1468

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

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

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

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

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

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

1✔
1513
        auto schema2 = realm_get_schema(realm);
2✔
1514
        CHECK(checked(schema2));
2!
1515
        CHECK(realm_equals(schema, schema2));
2!
1516
        realm_release(schema2);
2✔
1517
        realm_release(schema);
2✔
1518
    }
2✔
1519

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

149✔
1527
    auto write = [&](auto&& f) {
394✔
1528
        checked(realm_begin_write(realm));
394✔
1529
        f();
394✔
1530
        checked(realm_commit(realm));
394✔
1531
        checked(realm_refresh(realm, nullptr));
394✔
1532
    };
394✔
1533

149✔
1534
    bool found = false;
298✔
1535

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

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

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

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

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

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

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

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

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

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

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

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

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

1✔
1630
        bool found;
2✔
1631
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", nullptr, nullptr)));
2!
1632
        CHECK(checked(realm_find_property(realm, class_foo.key, "int", &found, nullptr)));
2!
1633
        CHECK(found);
2!
1634

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

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

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

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

149✔
1656
    SECTION("realm_get_property_keys()") {
298✔
1657
        size_t num_found = 0;
2✔
1658
        size_t properties_found = 0;
2✔
1659

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

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

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

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

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

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

1✔
1700
        CHECK(!realm_get_property(realm, class_bar.key, 123123123, &prop));
2!
1701
        CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
1702
    }
2✔
1703

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

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

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

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

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

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

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

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

149✔
1765

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

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

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

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

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

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

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

1✔
1823
            CHECK(checked(realm_get_num_objects(realm, class_bar.key, nullptr)));
2!
1824
            CHECK(!realm_get_num_objects(realm, 123123123, nullptr));
2!
1825
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1826
        }
2✔
1827

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

1✔
1834
            realm_object_key_t invalid_key = 123123123;
2✔
1835
            CHECK(!realm_get_object(realm, class_foo.key, invalid_key));
2!
1836
            CHECK_ERR(RLM_ERR_NO_SUCH_OBJECT);
2!
1837

1✔
1838
            realm_class_key_t invalid_class_key = 123123123;
2✔
1839
            CHECK(!realm_get_object(realm, invalid_class_key, obj1_key));
2!
1840
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
1841
        }
2✔
1842

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

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

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

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

1✔
1863
            CHECK(!realm_get_value(obj1.get(), 123123123, &value));
2!
1864
            CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
1865

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

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

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

99✔
1880
        SECTION("realm_get_values()") {
198✔
1881
            realm_value_t values[3];
2✔
1882

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

1✔
1886
            CHECK(values[0].type == RLM_TYPE_INT);
2!
1887
            CHECK(values[1].type == RLM_TYPE_STRING);
2!
1888
            CHECK(values[2].type == RLM_TYPE_INT);
2!
1889

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

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

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

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

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

1✔
1913
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_str_val("a"), false));
2!
1914
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
1915

1✔
1916
                CHECK(!realm_set_value(obj1.get(), 123123123, rlm_int_val(123), false));
2!
1917
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
1918
            });
2✔
1919
        }
2✔
1920

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

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

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

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

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

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

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

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

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

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

1✔
2017
                CHECK(realm_set_value(obj1.get(), foo_properties["link"], link, false));
2!
2018
            });
2✔
2019

1✔
2020
            realm_value_t value;
2✔
2021

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

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

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

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

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

99✔
2124

99✔
2125
        SECTION("find with primary key") {
198✔
2126
            bool found = false;
2✔
2127

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

1✔
2135
            CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(2), &found));
2!
2136
            CHECK(!found);
2!
2137
            CHECK(!realm_object_find_with_primary_key(realm, class_bar.key, rlm_int_val(2), nullptr));
2!
2138

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

1✔
2146
            // Invalid class key
1✔
2147
            CHECK(!realm_object_find_with_primary_key(realm, 123123123, rlm_int_val(1), nullptr));
2!
2148
            CHECK_ERR(RLM_ERR_NO_SUCH_TABLE);
2!
2149
        }
2✔
2150

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

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

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

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

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

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

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

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

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

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

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

1✔
2234
                static const size_t num_args = 1;
2✔
2235
                realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &str}};
2✔
2236
                realm_query_arg_t* arg_list_simple = &args[0];
2✔
2237

1✔
2238
                realm_query_arg_t args_in_list[num_args] = {realm_query_arg_t{2, true, &list_arg[0]}};
2✔
2239
                realm_query_arg_t* arg_list = &args_in_list[0];
2✔
2240

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

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

1✔
2254
                CHECK(checked(realm_query_count(q_string_single_param.get(), &count)));
2!
2255
                CHECK(1 == count);
2!
2256
                CHECK(checked(realm_query_count(q_string_in_list.get(), &count_list)));
2!
2257
                CHECK(1 == count_list);
2!
2258
            }
2✔
2259

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

1✔
2267
                static const size_t num_args = 1;
2✔
2268
                realm_query_arg_t args[num_args] = {realm_query_arg_t{1, false, &link_value}};
2✔
2269
                realm_query_arg_t* arg = &args[0];
2✔
2270

1✔
2271
                realm_value_t list_arg[num_args] = {link_value};
2✔
2272
                realm_query_arg_t args_in_list[num_args] = {realm_query_arg_t{num_args, true, &list_arg[0]}};
2✔
2273
                realm_query_arg_t* arg_list = &args_in_list[0];
2✔
2274

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

1✔
2280
                size_t count, count_list;
2✔
2281

1✔
2282
                // change the link
1✔
2283
                link = rlm_null();
2✔
2284

1✔
2285
                CHECK(checked(realm_query_count(q_link_single_param.get(), &count)));
2!
2286
                CHECK(1 == count);
2!
2287
                CHECK(checked(realm_query_count(q_link_in_list.get(), &count_list)));
2!
2288
                CHECK(1 == count_list);
2!
2289
            }
2✔
2290

36✔
2291
            SECTION("decimal NaN") {
72✔
2292
                realm_value_t decimal = rlm_decimal_nan();
2✔
2293

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

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

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

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

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

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

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

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

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

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

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

36✔
2461
            SECTION("results") {
72✔
2462
                auto r = cptr_checked(realm_query_find_all(q.get()));
52✔
2463
                CHECK(!realm_is_frozen(r.get()));
52!
2464

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

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

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

1✔
2487
                    realm_value_t a = rlm_str_val("a");
2✔
2488
                    realm_value_t b = rlm_str_val("b");
2✔
2489
                    realm_value_t c = rlm_null();
2✔
2490

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

1✔
2501
                    write([&] {
2✔
2502
                        CHECK(checked(realm_object_delete(obj2.get())));
2!
2503
                    });
2✔
2504

1✔
2505
                    CHECK(checked(realm_results_is_valid(results.get(), &valid)));
2!
2506
                    CHECK_FALSE(valid);
2!
2507
                    size_t count;
2✔
2508

1✔
2509
                    CHECK_FALSE(realm_results_count(results.get(), &count));
2!
2510
                    CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
2!
2511

1✔
2512
                    CHECK_FALSE(realm_results_resolve_in(results.get(), realm));
2!
2513
                    CHECK_ERR(RLM_ERR_STALE_ACCESSOR);
2!
2514
                }
2✔
2515

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1✔
2662
                    CHECK(!realm_results_min(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
2663
                    CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2664
                }
2✔
2665

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

1✔
2673
                    CHECK(!realm_results_max(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
2674
                    CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2675
                }
2✔
2676

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

1✔
2684
                    size_t count;
2✔
2685
                    realm_results_count(r.get(), &count);
2✔
2686
                    CHECK(count == 1);
2!
2687

1✔
2688
                    CHECK(!realm_results_sum(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
2689
                    CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2690
                }
2✔
2691

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

1✔
2699
                    CHECK(!realm_results_average(r.get(), RLM_INVALID_PROPERTY_KEY, nullptr, nullptr));
2!
2700
                    CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2701
                }
2✔
2702

26✔
2703
                SECTION("realm_results_delete_all()") {
52✔
2704
                    CHECK(!realm_results_delete_all(r.get()));
2!
2705
                    CHECK_ERR(RLM_ERR_WRONG_TRANSACTION_STATE);
2!
2706

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

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

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

1✔
2725
                    auto bar_link = realm_object_as_link(obj2.get());
2✔
2726
                    realm_value_t bar_link_val;
2✔
2727
                    bar_link_val.type = RLM_TYPE_LINK;
2✔
2728
                    bar_link_val.link = bar_link;
2✔
2729

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

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

1✔
2744
                    write([&]() {
2✔
2745
                        realm_list_clear(list.get());
2✔
2746
                    });
2✔
2747
                }
2✔
2748

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

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

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

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

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

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

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

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

1✔
2814
                CHECK(checked(realm_object_delete(obj1.get())));
2!
2815
                CHECK(!realm_object_is_valid(obj1.get()));
2!
2816

1✔
2817
                realm_clear_last_error();
2✔
2818
                CHECK(!realm_object_delete(obj1.get()));
2!
2819
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2820

1✔
2821
                realm_clear_last_error();
2✔
2822
                CHECK(!realm_set_value(obj1.get(), foo_int_key, rlm_int_val(123), false));
2!
2823
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2824

1✔
2825
                realm_clear_last_error();
2✔
2826
                auto list2 = realm_get_list(obj1.get(), foo_links_key);
2✔
2827
                CHECK(!list2);
2!
2828
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2829

1✔
2830
                size_t size;
2✔
2831
                CHECK(!realm_list_size(list.get(), &size));
2!
2832
                CHECK_ERR(RLM_ERR_INVALIDATED_OBJECT);
2!
2833
            });
2✔
2834
        }
2✔
2835

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

1✔
2841
                CHECK(!realm_get_list(obj2.get(), 123123123));
2!
2842
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
2843
            }
2✔
2844

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

4✔
2850
                realm_value_t a = rlm_str_val("a");
8✔
2851
                realm_value_t b = rlm_str_val("b");
8✔
2852
                realm_value_t c = rlm_null();
8✔
2853

4✔
2854
                SECTION("realm_equals() type check") {
8✔
2855
                    CHECK(!realm_equals(strings.get(), obj1.get()));
2!
2856
                }
2✔
2857

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

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

1✔
2870
                        realm_value_t a2, b2, c2;
2✔
2871
                        CHECK(checked(realm_list_get(strings.get(), 0, &a2)));
2!
2872
                        CHECK(checked(realm_list_get(strings.get(), 1, &b2)));
2!
2873
                        CHECK(checked(realm_list_get(strings.get(), 2, &c2)));
2!
2874

1✔
2875
                        CHECK(rlm_stdstr(a2) == "a");
2!
2876
                        CHECK(rlm_stdstr(b2) == "b");
2!
2877
                        CHECK(c2.type == RLM_TYPE_NULL);
2!
2878

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1✔
3127
                write([&]() {
2✔
3128
                    CHECK(realm_list_move(int_list.get(), 0, 1));
2!
3129
                });
2✔
3130

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

1✔
3137
                write([&]() {
2✔
3138
                    CHECK(realm_list_move(int_list.get(), 3, 2));
2!
3139
                });
2✔
3140

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

16✔
3148
            SECTION("links") {
32✔
3149
                CPtr<realm_list_t> bars;
8✔
3150

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

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

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

4✔
3174
                    CHECK(checked(realm_list_insert(bars.get(), 0, bar_link_val)));
8!
3175
                    CHECK(checked(realm_list_insert(bars.get(), 1, bar_link_val)));
8!
3176
                });
8✔
3177

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

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

1✔
3190
                    auto result = realm_list_get(bars.get(), 2, &val);
2✔
3191
                    CHECK(!result);
2!
3192
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
2!
3193
                }
2✔
3194

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

1✔
3203
                        CHECK(!realm_list_set(bars.get(), 0, foo2_link_val));
2!
3204
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3205
                    });
2✔
3206
                }
2✔
3207

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

1✔
3216
                    size_t num_bars;
2✔
3217
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3218
                    CHECK(num_bars != 0);
2!
3219
                }
2✔
3220

4✔
3221
                SECTION("realm_list_remove_all()") {
8✔
3222
                    size_t num_bars;
2✔
3223
                    size_t size;
2✔
3224

1✔
3225
                    write([&]() {
2✔
3226
                        CHECK(checked(realm_list_remove_all(bars.get())));
2!
3227
                    });
2✔
3228

1✔
3229
                    CHECK(realm_list_size(bars.get(), &size));
2!
3230
                    CHECK(size == 0);
2!
3231

1✔
3232
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3233
                    CHECK(num_bars == 0);
2!
3234
                }
2✔
3235
            }
8✔
3236

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

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

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

4✔
3261
                State state;
8✔
3262

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

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

4✔
3271
                auto str1 = rlm_str_val("a");
8✔
3272
                auto str2 = rlm_str_val("b");
8✔
3273
                auto null = rlm_null();
8✔
3274

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

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

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

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

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

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

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

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

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

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

1✔
3361
                    auto token = require_change();
2✔
3362

1✔
3363
                    write([&]() {
2✔
3364
                        checked(realm_list_erase(strings.get(), 1));
2✔
3365
                        checked(realm_list_insert(strings.get(), 0, null));
2✔
3366
                        checked(realm_list_insert(strings.get(), 1, null));
2✔
3367

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

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

1✔
3383
                    size_t num_deletions, num_insertions, num_modifications;
2✔
3384
                    bool collection_cleared = false;
2✔
3385
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
2✔
3386
                                                             &num_modifications, &num_moves, &collection_cleared);
2✔
3387
                    CHECK(num_deletions == 1);
2!
3388
                    CHECK(num_insertions == 2);
2!
3389
                    CHECK(num_modifications == 1);
2!
3390
                    CHECK(collection_cleared == false);
2!
3391

1✔
3392
                    realm_index_range_t deletions, insertions, modifications, modifications_after;
2✔
3393
                    realm_collection_move_t moves;
2✔
3394
                    realm_collection_changes_get_ranges(state.changes.get(), &deletions, 1, &insertions, 1,
2✔
3395
                                                        &modifications, 1, &modifications_after, 1, &moves, 1);
2✔
3396
                    CHECK(deletions.from == 1);
2!
3397
                    CHECK(deletions.to == 2);
2!
3398

1✔
3399
                    CHECK(insertions.from == 0);
2!
3400
                    CHECK(insertions.to == 2);
2!
3401

1✔
3402
                    CHECK(modifications.from == 0);
2!
3403
                    CHECK(modifications.to == 1);
2!
3404

1✔
3405
                    CHECK(modifications_after.from == 2);
2!
3406
                    CHECK(modifications_after.to == 3);
2!
3407

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

1✔
3428
                    write([&]() {
2✔
3429
                        checked(realm_list_clear(strings.get()));
2✔
3430
                    });
2✔
3431

1✔
3432
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
2✔
3433
                                                             &num_modifications, &num_moves, &collection_cleared);
2✔
3434
                    CHECK(collection_cleared == true);
2!
3435
                }
2✔
3436
            }
8✔
3437
        }
32✔
3438

99✔
3439
        SECTION("sets") {
198✔
3440
            SECTION("realm_get_set() errors") {
26✔
3441
                CHECK(!realm_get_set(obj1.get(), foo_properties["int"]));
2!
3442
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3443

1✔
3444
                CHECK(!realm_get_set(obj1.get(), 123123123));
2!
3445
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
3446
            }
2✔
3447

13✔
3448
            SECTION("nullable strings") {
26✔
3449
                auto strings = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
10✔
3450
                CHECK(strings);
10!
3451
                CHECK(!realm_is_frozen(strings.get()));
10!
3452

5✔
3453
                realm_value_t a = rlm_str_val("a");
10✔
3454
                realm_value_t b = rlm_str_val("b");
10✔
3455
                realm_value_t c = rlm_null();
10✔
3456

5✔
3457
                SECTION("realm_equals() type check") {
10✔
3458
                    CHECK(!realm_equals(strings.get(), obj1.get()));
2!
3459
                }
2✔
3460

5✔
3461
                SECTION("realm_clone()") {
10✔
3462
                    auto set2 = clone_cptr(strings.get());
2✔
3463
                    CHECK(realm_equals(strings.get(), set2.get()));
2!
3464
                    CHECK(strings.get() != set2.get());
2!
3465
                }
2✔
3466

5✔
3467
                SECTION("insert, then get, then erase") {
10✔
3468
                    write([&]() {
2✔
3469
                        bool inserted = false;
2✔
3470
                        CHECK(checked(realm_set_insert(strings.get(), a, nullptr, &inserted)));
2!
3471
                        CHECK(inserted);
2!
3472
                        CHECK(checked(realm_set_insert(strings.get(), b, nullptr, &inserted)));
2!
3473
                        CHECK(inserted);
2!
3474
                        CHECK(checked(realm_set_insert(strings.get(), c, nullptr, &inserted)));
2!
3475
                        CHECK(inserted);
2!
3476

1✔
3477
                        size_t a_index, b_index, c_index;
2✔
3478
                        bool found = false;
2✔
3479
                        CHECK(checked(realm_set_find(strings.get(), a, &a_index, &found)));
2!
3480
                        CHECK(found);
2!
3481
                        CHECK(checked(realm_set_find(strings.get(), b, &b_index, &found)));
2!
3482
                        CHECK(found);
2!
3483
                        CHECK(checked(realm_set_find(strings.get(), c, &c_index, &found)));
2!
3484
                        CHECK(found);
2!
3485

1✔
3486
                        realm_value_t a2, b2, c2;
2✔
3487
                        CHECK(checked(realm_set_get(strings.get(), a_index, &a2)));
2!
3488
                        CHECK(checked(realm_set_get(strings.get(), b_index, &b2)));
2!
3489
                        CHECK(checked(realm_set_get(strings.get(), c_index, &c2)));
2!
3490

1✔
3491
                        CHECK(rlm_stdstr(a2) == "a");
2!
3492
                        CHECK(rlm_stdstr(b2) == "b");
2!
3493
                        CHECK(c2.type == RLM_TYPE_NULL);
2!
3494

1✔
3495
                        bool erased = false;
2✔
3496
                        CHECK(checked(realm_set_erase(strings.get(), a2, &erased)));
2!
3497
                        CHECK(erased);
2!
3498
                        CHECK(checked(realm_set_erase(strings.get(), rlm_int_val(987), &erased)));
2!
3499
                        CHECK(!erased);
2!
3500
                    });
2✔
3501
                }
2✔
3502

5✔
3503
                SECTION("equality") {
10✔
3504
                    auto strings2 = cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
2✔
3505
                    CHECK(strings2);
2!
3506
                    CHECK(realm_equals(strings.get(), strings2.get()));
2!
3507

1✔
3508
                    write([&]() {
2✔
3509
                        auto obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
3510
                        CHECK(obj3);
2!
3511
                        auto strings3 =
2✔
3512
                            cptr_checked(realm_get_set(obj3.get(), foo_properties["nullable_string_set"]));
2✔
3513
                        CHECK(!realm_equals(strings.get(), strings3.get()));
2!
3514
                    });
2✔
3515
                }
2✔
3516

5✔
3517
                SECTION("parse query for sets") {
10✔
3518
                    auto links = cptr_checked(realm_get_set(obj1.get(), foo_properties["link_set"]));
2✔
3519
                    CHECK(links);
2!
3520
                    cptr_checked(realm_query_parse_for_set(links.get(), "TRUEPREDICATE", 0, nullptr));
2✔
3521
                }
2✔
3522
            }
10✔
3523

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

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

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

1✔
3578
                    CHECK(realm_set_insert(nullable_int_set.get(), integer, nullptr, nullptr));
2!
3579
                    CHECK(realm_set_insert(nullable_bool_set.get(), boolean, nullptr, nullptr));
2!
3580
                    CHECK(realm_set_insert(nullable_string_set.get(), string, nullptr, nullptr));
2!
3581
                    CHECK(realm_set_insert(nullable_binary_set.get(), binary, nullptr, nullptr));
2!
3582
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), timestamp, nullptr, nullptr));
2!
3583
                    CHECK(realm_set_insert(nullable_float_set.get(), fnum, nullptr, nullptr));
2!
3584
                    CHECK(realm_set_insert(nullable_double_set.get(), dnum, nullptr, nullptr));
2!
3585
                    CHECK(realm_set_insert(nullable_decimal_set.get(), decimal, nullptr, nullptr));
2!
3586
                    CHECK(realm_set_insert(nullable_object_id_set.get(), object_id, nullptr, nullptr));
2!
3587
                    CHECK(realm_set_insert(nullable_uuid_set.get(), uuid, nullptr, nullptr));
2!
3588

1✔
3589
                    CHECK(realm_set_insert(nullable_int_set.get(), null, nullptr, nullptr));
2!
3590
                    CHECK(realm_set_insert(nullable_bool_set.get(), null, nullptr, nullptr));
2!
3591
                    CHECK(realm_set_insert(nullable_string_set.get(), null, nullptr, nullptr));
2!
3592
                    CHECK(realm_set_insert(nullable_binary_set.get(), null, nullptr, nullptr));
2!
3593
                    CHECK(realm_set_insert(nullable_timestamp_set.get(), null, nullptr, nullptr));
2!
3594
                    CHECK(realm_set_insert(nullable_float_set.get(), null, nullptr, nullptr));
2!
3595
                    CHECK(realm_set_insert(nullable_double_set.get(), null, nullptr, nullptr));
2!
3596
                    CHECK(realm_set_insert(nullable_decimal_set.get(), null, nullptr, nullptr));
2!
3597
                    CHECK(realm_set_insert(nullable_object_id_set.get(), null, nullptr, nullptr));
2!
3598
                    CHECK(realm_set_insert(nullable_uuid_set.get(), null, nullptr, nullptr));
2!
3599
                });
2✔
3600

1✔
3601
                realm_value_t value;
2✔
3602

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

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

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

13✔
3693
            SECTION("links") {
26✔
3694
                CPtr<realm_set_t> bars;
8✔
3695

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

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

4✔
3723
                SECTION("get") {
8✔
3724
                    realm_value_t val;
2✔
3725
                    CHECK(checked(realm_set_get(bars.get(), 0, &val)));
2!
3726
                    CHECK(val.type == RLM_TYPE_LINK);
2!
3727
                    CHECK(val.link.target_table == class_bar.key);
2!
3728
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
3729

1✔
3730
                    auto result = realm_set_get(bars.get(), 1, &val);
2✔
3731
                    CHECK(!result);
2!
3732
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
2!
3733
                }
2✔
3734

4✔
3735
                SECTION("insert wrong type") {
8✔
3736
                    write([&]() {
2✔
3737
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
2✔
3738
                        CHECK(foo2);
2!
3739
                        realm_value_t foo2_link_val;
2✔
3740
                        foo2_link_val.type = RLM_TYPE_LINK;
2✔
3741
                        foo2_link_val.link = realm_object_as_link(foo2.get());
2✔
3742

1✔
3743
                        CHECK(!realm_set_insert(bars.get(), foo2_link_val, nullptr, nullptr));
2!
3744
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3745
                    });
2✔
3746
                }
2✔
3747

4✔
3748
                SECTION("realm_set_clear()") {
8✔
3749
                    write([&]() {
2✔
3750
                        CHECK(realm_set_clear(bars.get()));
2!
3751
                    });
2✔
3752
                    size_t size;
2✔
3753
                    CHECK(realm_set_size(bars.get(), &size));
2!
3754
                    CHECK(size == 0);
2!
3755

1✔
3756
                    size_t num_bars;
2✔
3757
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3758
                    CHECK(num_bars != 0);
2!
3759
                }
2✔
3760

4✔
3761
                SECTION("realm_set_remove_all()") {
8✔
3762
                    realm_value_t val;
2✔
3763
                    CHECK(checked(realm_set_get(bars.get(), 0, &val)));
2!
3764
                    CHECK(val.type == RLM_TYPE_LINK);
2!
3765
                    CHECK(val.link.target_table == class_bar.key);
2!
3766
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
3767

1✔
3768
                    size_t num_bars;
2✔
3769
                    size_t size;
2✔
3770

1✔
3771
                    write([&]() {
2✔
3772
                        CHECK(checked(realm_set_remove_all(bars.get())));
2!
3773
                    });
2✔
3774

1✔
3775
                    CHECK(realm_set_size(bars.get(), &size));
2!
3776
                    CHECK(size == 0);
2!
3777

1✔
3778
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
3779
                    CHECK(num_bars == 0);
2!
3780
                }
2✔
3781
            }
8✔
3782

13✔
3783
            SECTION("notifications") {
26✔
3784
                struct State {
4✔
3785
                    CPtr<realm_collection_changes_t> changes;
4✔
3786
                    CPtr<realm_async_error_t> error;
4✔
3787
                    bool destroyed = false;
4✔
3788
                };
4✔
3789

2✔
3790
                State state;
4✔
3791

2✔
3792
                auto on_change = [](void* userdata, const realm_collection_changes_t* changes) {
6✔
3793
                    auto* state = static_cast<State*>(userdata);
6✔
3794
                    state->changes = clone_cptr(changes);
6✔
3795
                };
6✔
3796

2✔
3797
                CPtr<realm_set_t> strings =
4✔
3798
                    cptr_checked(realm_get_set(obj1.get(), foo_properties["nullable_string_set"]));
4✔
3799

2✔
3800
                auto str1 = rlm_str_val("a");
4✔
3801
                auto str2 = rlm_str_val("b");
4✔
3802
                auto null = rlm_null();
4✔
3803

2✔
3804
                auto require_change = [&]() {
3✔
3805
                    auto token = cptr_checked(
2✔
3806
                        realm_set_add_notification_callback(strings.get(), &state, nullptr, nullptr, on_change));
2✔
3807
                    checked(realm_refresh(realm, nullptr));
2✔
3808
                    return token;
2✔
3809
                };
2✔
3810

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

2✔
3823
                SECTION("insertion,deletion sends a change callback") {
4✔
3824
                    write([&]() {
2✔
3825
                        checked(realm_set_insert(strings.get(), str1, nullptr, nullptr));
2✔
3826
                    });
2✔
3827

1✔
3828
                    auto token = require_change();
2✔
3829
                    write([&]() {
2✔
3830
                        checked(realm_set_erase(strings.get(), str1, nullptr));
2✔
3831
                        checked(realm_set_insert(strings.get(), str2, nullptr, nullptr));
2✔
3832
                        checked(realm_set_insert(strings.get(), null, nullptr, nullptr));
2✔
3833
                    });
2✔
3834
                    CHECK(!state.error);
2!
3835
                    CHECK(state.changes);
2!
3836

1✔
3837
                    size_t num_deletion_ranges, num_insertion_ranges, num_modification_ranges, num_moves;
2✔
3838
                    realm_collection_changes_get_num_ranges(state.changes.get(), &num_deletion_ranges,
2✔
3839
                                                            &num_insertion_ranges, &num_modification_ranges,
2✔
3840
                                                            &num_moves);
2✔
3841
                    CHECK(num_deletion_ranges == 1);
2!
3842
                    CHECK(num_insertion_ranges == 1);
2!
3843
                    CHECK(num_modification_ranges == 0);
2!
3844
                    CHECK(num_moves == 0);
2!
3845

1✔
3846
                    realm_index_range_t insertion_range, deletion_range;
2✔
3847
                    realm_collection_changes_get_ranges(state.changes.get(), &deletion_range, 1, &insertion_range, 1,
2✔
3848
                                                        nullptr, 0, nullptr, 0, nullptr, 0);
2✔
3849
                    CHECK(deletion_range.from == 0);
2!
3850
                    CHECK(deletion_range.to == 1);
2!
3851
                    CHECK(insertion_range.from == 0);
2!
3852
                    CHECK(insertion_range.to == 2);
2!
3853

1✔
3854
                    write([&]() {
2✔
3855
                        checked(realm_set_clear(strings.get()));
2✔
3856
                    });
2✔
3857

1✔
3858
                    size_t num_deletions, num_insertions, num_modifications;
2✔
3859
                    bool collection_cleared = false;
2✔
3860
                    realm_collection_changes_get_num_changes(state.changes.get(), &num_deletions, &num_insertions,
2✔
3861
                                                             &num_modifications, &num_moves, &collection_cleared);
2✔
3862
                    CHECK(collection_cleared == true);
2!
3863
                }
2✔
3864
            }
4✔
3865
        }
26✔
3866

99✔
3867
        SECTION("dictionaries") {
198✔
3868
            SECTION("realm_get_dictionary() errors") {
28✔
3869
                CHECK(!realm_get_dictionary(obj1.get(), foo_properties["int"]));
2!
3870
                CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
3871

1✔
3872
                CHECK(!realm_get_dictionary(obj1.get(), 123123123));
2!
3873
                CHECK_ERR(RLM_ERR_INVALID_PROPERTY);
2!
3874
            }
2✔
3875

14✔
3876
            SECTION("nullable strings") {
28✔
3877
                auto strings = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
8✔
3878
                CHECK(strings);
8!
3879
                CHECK(!realm_is_frozen(strings.get()));
8!
3880

4✔
3881
                realm_value_t a = rlm_str_val("a");
8✔
3882
                realm_value_t b = rlm_str_val("b");
8✔
3883
                realm_value_t c = rlm_null();
8✔
3884
                realm_value_t key_a = rlm_str_val("key_a");
8✔
3885
                realm_value_t key_b = rlm_str_val("key_b");
8✔
3886
                realm_value_t key_c = rlm_str_val("key_c");
8✔
3887

4✔
3888
                SECTION("realm_equals() type check") {
8✔
3889
                    CHECK(!realm_equals(strings.get(), obj1.get()));
2!
3890
                }
2✔
3891

4✔
3892
                SECTION("realm_clone()") {
8✔
3893
                    auto dict2 = clone_cptr(strings.get());
2✔
3894
                    CHECK(realm_equals(strings.get(), dict2.get()));
2!
3895
                    CHECK(strings.get() != dict2.get());
2!
3896
                }
2✔
3897

4✔
3898
                SECTION("insert, then get, then erase") {
8✔
3899
                    write([&]() {
2✔
3900
                        bool inserted = false;
2✔
3901
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_a, a, nullptr, &inserted)));
2!
3902
                        CHECK(inserted);
2!
3903
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_b, b, nullptr, &inserted)));
2!
3904
                        CHECK(inserted);
2!
3905
                        CHECK(checked(realm_dictionary_insert(strings.get(), key_c, c, nullptr, &inserted)));
2!
3906
                        CHECK(inserted);
2!
3907

1✔
3908
                        realm_value_t a2, b2, c2;
2✔
3909
                        bool found = false;
2✔
3910
                        CHECK(checked(realm_dictionary_find(strings.get(), key_a, &a2, &found)));
2!
3911
                        CHECK(found);
2!
3912
                        CHECK(checked(realm_dictionary_find(strings.get(), key_b, &b2, &found)));
2!
3913
                        CHECK(found);
2!
3914
                        CHECK(checked(realm_dictionary_find(strings.get(), key_c, &c2, &found)));
2!
3915
                        CHECK(found);
2!
3916

1✔
3917
                        CHECK(rlm_stdstr(a2) == "a");
2!
3918
                        CHECK(rlm_stdstr(b2) == "b");
2!
3919
                        CHECK(c2.type == RLM_TYPE_NULL);
2!
3920

1✔
3921
                        bool erased = false;
2✔
3922
                        CHECK(checked(realm_dictionary_erase(strings.get(), key_a, &erased)));
2!
3923
                        CHECK(erased);
2!
3924
                        CHECK(checked(realm_dictionary_erase(strings.get(), rlm_int_val(987), &erased)));
2!
3925
                        CHECK(!erased);
2!
3926
                    });
2✔
3927
                }
2✔
3928

4✔
3929
                SECTION("equality") {
8✔
3930
                    auto strings2 =
2✔
3931
                        cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
2✔
3932
                    CHECK(strings2);
2!
3933
                    CHECK(realm_equals(strings.get(), strings2.get()));
2!
3934

1✔
3935
                    write([&]() {
2✔
3936
                        auto obj3 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
3937
                        CHECK(obj3);
2!
3938
                        auto strings3 =
2✔
3939
                            cptr_checked(realm_get_dictionary(obj3.get(), foo_properties["nullable_string_dict"]));
2✔
3940
                        CHECK(!realm_equals(strings.get(), strings3.get()));
2!
3941
                    });
2✔
3942
                }
2✔
3943
            }
8✔
3944

14✔
3945
            SECTION("get/insert all property types") {
28✔
3946
                realm_value_t key = rlm_str_val("k");
2✔
3947
                realm_value_t key2 = rlm_str_val("k2");
2✔
3948

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

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

1✔
3995
                write([&]() {
2✔
3996
                    size_t index;
2✔
3997
                    bool inserted;
2✔
3998
                    CHECK(!realm_dictionary_insert(int_dict.get(), rlm_int_val(987), integer, &index, &inserted));
2!
3999

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

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

1✔
4042
                    CHECK(realm_dictionary_insert(nullable_int_dict.get(), key2, null, &index, &inserted));
2!
4043
                    CHECK((inserted && index == 1));
2!
4044
                    CHECK(realm_dictionary_insert(nullable_bool_dict.get(), key2, null, &index, &inserted));
2!
4045
                    CHECK((inserted && index == 1));
2!
4046
                    CHECK(realm_dictionary_insert(nullable_string_dict.get(), key2, null, &index, &inserted));
2!
4047
                    CHECK((inserted && index == 1));
2!
4048
                    CHECK(realm_dictionary_insert(nullable_binary_dict.get(), key2, null, &index, &inserted));
2!
4049
                    CHECK((inserted && index == 1));
2!
4050
                    CHECK(realm_dictionary_insert(nullable_timestamp_dict.get(), key2, null, &index, &inserted));
2!
4051
                    CHECK((inserted && index == 1));
2!
4052
                    CHECK(realm_dictionary_insert(nullable_float_dict.get(), key2, null, &index, &inserted));
2!
4053
                    CHECK((inserted && index == 1));
2!
4054
                    CHECK(realm_dictionary_insert(nullable_double_dict.get(), key2, null, &index, &inserted));
2!
4055
                    CHECK((inserted && index == 1));
2!
4056
                    CHECK(realm_dictionary_insert(nullable_decimal_dict.get(), key2, null, &index, &inserted));
2!
4057
                    CHECK((inserted && index == 1));
2!
4058
                    CHECK(realm_dictionary_insert(nullable_object_id_dict.get(), key2, null, &index, &inserted));
2!
4059
                    CHECK((inserted && index == 1));
2!
4060
                    CHECK(realm_dictionary_insert(nullable_uuid_dict.get(), key2, null, &index, &inserted));
2!
4061
                    CHECK((inserted && index == 1));
2!
4062
                });
2✔
4063

1✔
4064
                realm_value_t k, value;
2✔
4065

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

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

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

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

14✔
4249
            SECTION("links") {
28✔
4250
                CPtr<realm_dictionary_t> bars;
6✔
4251
                realm_value_t key = rlm_str_val("k");
6✔
4252

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

3✔
4270
                SECTION("get") {
6✔
4271
                    realm_value_t k, val;
2✔
4272
                    CHECK(checked(realm_dictionary_get(bars.get(), 0, &k, &val)));
2!
4273
                    CHECK(rlm_val_eq(k, key));
2!
4274
                    CHECK(val.type == RLM_TYPE_LINK);
2!
4275
                    CHECK(val.link.target_table == class_bar.key);
2!
4276
                    CHECK(val.link.target == realm_object_get_key(obj2.get()));
2!
4277

1✔
4278
                    auto result = realm_dictionary_get(bars.get(), 1, &k, &val);
2✔
4279
                    CHECK(!result);
2!
4280
                    CHECK_ERR(RLM_ERR_INDEX_OUT_OF_BOUNDS);
2!
4281
                }
2✔
4282

3✔
4283
                SECTION("insert wrong type") {
6✔
4284
                    write([&]() {
2✔
4285
                        auto foo2 = cptr(realm_object_create(realm, class_foo.key));
2✔
4286
                        CHECK(foo2);
2!
4287
                        realm_value_t foo2_link_val;
2✔
4288
                        foo2_link_val.type = RLM_TYPE_LINK;
2✔
4289
                        foo2_link_val.link = realm_object_as_link(foo2.get());
2✔
4290

1✔
4291
                        CHECK(!realm_dictionary_insert(bars.get(), key, foo2_link_val, nullptr, nullptr));
2!
4292
                        CHECK_ERR(RLM_ERR_PROPERTY_TYPE_MISMATCH);
2!
4293
                    });
2✔
4294
                }
2✔
4295

3✔
4296
                SECTION("realm_dictionary_clear()") {
6✔
4297
                    write([&]() {
2✔
4298
                        CHECK(realm_dictionary_clear(bars.get()));
2!
4299
                    });
2✔
4300
                    size_t size;
2✔
4301
                    CHECK(realm_dictionary_size(bars.get(), &size));
2!
4302
                    CHECK(size == 0);
2!
4303

1✔
4304
                    size_t num_bars;
2✔
4305
                    CHECK(realm_get_num_objects(realm, class_bar.key, &num_bars));
2!
4306
                    CHECK(num_bars != 0);
2!
4307
                }
2✔
4308
            }
6✔
4309

14✔
4310
            SECTION("notifications") {
28✔
4311
                struct State {
4✔
4312
                    CPtr<realm_collection_changes_t> changes;
4✔
4313
                    CPtr<realm_dictionary_changes_t> dictionary_changes;
4✔
4314
                    CPtr<realm_async_error_t> error;
4✔
4315
                    bool destroyed = false;
4✔
4316
                };
4✔
4317

2✔
4318
                State state;
4✔
4319
                auto on_dictionary_change = [](void* userdata, const realm_dictionary_changes_t* changes) {
4✔
4320
                    auto* state = static_cast<State*>(userdata);
4✔
4321
                    state->dictionary_changes = clone_cptr(changes);
4✔
4322
                };
4✔
4323

2✔
4324
                CPtr<realm_dictionary_t> strings =
4✔
4325
                    cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["nullable_string_dict"]));
4✔
4326

2✔
4327
                auto str1 = rlm_str_val("a");
4✔
4328
                auto str2 = rlm_str_val("b");
4✔
4329
                auto null = rlm_null();
4✔
4330

2✔
4331
                auto require_change = [&]() {
3✔
4332
                    auto token = cptr_checked(realm_dictionary_add_notification_callback(
2✔
4333
                        strings.get(), &state, nullptr, nullptr, on_dictionary_change));
2✔
4334
                    checked(realm_refresh(realm, nullptr));
2✔
4335
                    return token;
2✔
4336
                };
2✔
4337

2✔
4338
                SECTION("userdata is freed when the token is destroyed") {
4✔
4339
                    auto token = cptr_checked(realm_dictionary_add_notification_callback(
2✔
4340
                        strings.get(), &state,
2✔
4341
                        [](void* p) {
2✔
4342
                            static_cast<State*>(p)->destroyed = true;
2✔
4343
                        },
2✔
4344
                        nullptr, nullptr));
2✔
4345
                    CHECK(!state.destroyed);
2!
4346
                    token.reset();
2✔
4347
                    CHECK(state.destroyed);
2!
4348
                }
2✔
4349

2✔
4350
                SECTION("insertion, deletions sends a change callback") {
4✔
4351
                    write([&]() {
2✔
4352
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("a"), str1, nullptr, nullptr));
2✔
4353
                    });
2✔
4354
                    auto token = require_change();
2✔
4355
                    write([&]() {
2✔
4356
                        checked(realm_dictionary_erase(strings.get(), rlm_str_val("a"), nullptr));
2✔
4357
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("b"), str2, nullptr, nullptr));
2✔
4358
                        checked(realm_dictionary_insert(strings.get(), rlm_str_val("c"), null, nullptr, nullptr));
2✔
4359
                    });
2✔
4360
                    CHECK(!state.error);
2!
4361
                    CHECK(state.dictionary_changes);
2!
4362

1✔
4363
                    size_t num_deletions, num_insertions, num_modifications;
2✔
4364
                    realm_dictionary_get_changes(state.dictionary_changes.get(), &num_deletions, &num_insertions,
2✔
4365
                                                 &num_modifications);
2✔
4366
                    CHECK(num_deletions == 1);
2!
4367
                    CHECK(num_insertions == 2);
2!
4368
                    CHECK(num_modifications == 0);
2!
4369
                    realm_value_t *deletions = nullptr, *insertions = nullptr, *modifications = nullptr;
2✔
4370
                    deletions = (realm_value_t*)malloc(sizeof(realm_value_t) * num_deletions);
2✔
4371
                    insertions = (realm_value_t*)malloc(sizeof(realm_value_t) * num_insertions);
2✔
4372
                    realm_dictionary_get_changed_keys(state.dictionary_changes.get(), deletions, &num_deletions,
2✔
4373
                                                      insertions, &num_insertions, modifications, &num_modifications);
2✔
4374
                    CHECK(deletions != nullptr);
2!
4375
                    CHECK(insertions != nullptr);
2!
4376
                    CHECK(modifications == nullptr);
2!
4377
                    realm_free(deletions);
2✔
4378
                    realm_free(insertions);
2✔
4379
                    realm_free(modifications);
2✔
4380
                }
2✔
4381
            }
4✔
4382

14✔
4383
            SECTION("realm_dictionary_content_checks") {
28✔
4384
                auto ints = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["int_dict"]));
6✔
4385
                CHECK(ints);
6!
4386
                CHECK(!realm_is_frozen(ints.get()));
6!
4387
                realm_value_t key1 = rlm_str_val("k");
6✔
4388
                realm_value_t key2 = rlm_str_val("k2");
6✔
4389
                realm_value_t integer1 = rlm_int_val(987);
6✔
4390
                realm_value_t integer2 = rlm_int_val(988);
6✔
4391

3✔
4392
                write([&]() {
6✔
4393
                    bool inserted = false;
6✔
4394
                    CHECK(checked(realm_dictionary_insert(ints.get(), key1, integer1, nullptr, &inserted)));
6!
4395
                    CHECK(inserted);
6!
4396
                    CHECK(checked(realm_dictionary_insert(ints.get(), key2, integer2, nullptr, &inserted)));
6!
4397
                    CHECK(inserted);
6!
4398
                });
6✔
4399

3✔
4400
                SECTION("realm_dictionary_get_keys") {
6✔
4401
                    size_t size = 0;
2✔
4402
                    realm_results_t* keys = nullptr;
2✔
4403
                    CHECK(checked(realm_dictionary_get_keys(ints.get(), &size, &keys)));
2!
4404
                    CHECK(keys);
2!
4405
                    CHECK((*keys).size() == size);
2!
4406
                    realm_release(keys);
2✔
4407
                }
2✔
4408

3✔
4409
                SECTION("realm_dictionary_contains_key") {
6✔
4410
                    bool found = false;
2✔
4411
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key1, &found)));
2!
4412
                    CHECK(found);
2!
4413
                    found = false;
2✔
4414
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key2, &found)));
2!
4415
                    CHECK(found);
2!
4416
                    realm_value_t key_no_present = rlm_str_val("kkkk");
2✔
4417
                    CHECK(checked(realm_dictionary_contains_key(ints.get(), key_no_present, &found)));
2!
4418
                    CHECK(!found);
2!
4419
                }
2✔
4420

3✔
4421
                SECTION("realm_dictionary_contains_value") {
6✔
4422
                    size_t index = -1;
2✔
4423
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer1, &index)));
2!
4424
                    CHECK(index == 0);
2!
4425
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer2, &index)));
2!
4426
                    CHECK(index == 1);
2!
4427
                    realm_value_t integer_no_present = rlm_int_val(678);
2✔
4428
                    CHECK(checked(realm_dictionary_contains_value(ints.get(), integer_no_present, &index)));
2!
4429
                    CHECK(index == realm::npos);
2!
4430
                }
2✔
4431
            }
6✔
4432
        }
28✔
4433

99✔
4434
        SECTION("notifications") {
198✔
4435
            struct State {
6✔
4436
                CPtr<realm_object_changes_t> changes;
6✔
4437
                CPtr<realm_async_error_t> error;
6✔
4438
                bool called;
6✔
4439
            };
6✔
4440

3✔
4441
            State state;
6✔
4442

3✔
4443
            auto on_change = [](void* userdata, const realm_object_changes_t* changes) {
12✔
4444
                auto state = static_cast<State*>(userdata);
12✔
4445
                state->changes = clone_cptr(changes);
12✔
4446
                state->called = true;
12✔
4447
            };
12✔
4448

3✔
4449
            auto require_change = [&]() {
5✔
4450
                auto token =
4✔
4451
                    cptr(realm_object_add_notification_callback(obj1.get(), &state, nullptr, nullptr, on_change));
4✔
4452
                checked(realm_refresh(realm, nullptr));
4✔
4453
                return token;
4✔
4454
            };
4✔
4455

3✔
4456
            SECTION("deleting the object sends a change notification") {
6✔
4457
                auto token = require_change();
2✔
4458
                write([&]() {
2✔
4459
                    checked(realm_object_delete(obj1.get()));
2✔
4460
                });
2✔
4461
                CHECK(!state.error);
2!
4462
                CHECK(state.changes);
2!
4463
                bool deleted = realm_object_changes_is_deleted(state.changes.get());
2✔
4464
                CHECK(deleted);
2!
4465
            }
2✔
4466

3✔
4467
            SECTION("modifying the object sends a change notification for the object, and for the changed column") {
6✔
4468
                auto token = require_change();
2✔
4469
                write([&]() {
2✔
4470
                    checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
2✔
4471
                    checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("aaa"), false));
2✔
4472
                });
2✔
4473
                CHECK(!state.error);
2!
4474
                CHECK(state.changes);
2!
4475
                bool deleted = realm_object_changes_is_deleted(state.changes.get());
2✔
4476
                CHECK(!deleted);
2!
4477
                size_t num_modified = realm_object_changes_get_num_modified_properties(state.changes.get());
2✔
4478
                CHECK(num_modified == 2);
2!
4479
                realm_property_key_t modified_keys[2];
2✔
4480
                size_t n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 2);
2✔
4481
                CHECK(n == 2);
2!
4482
                CHECK(modified_keys[0] == foo_int_key);
2!
4483
                CHECK(modified_keys[1] == foo_str_key);
2!
4484

1✔
4485
                n = realm_object_changes_get_modified_properties(state.changes.get(), nullptr, 2);
2✔
4486
                CHECK(n == 2);
2!
4487

1✔
4488
                n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 0);
2✔
4489
                CHECK(n == 0);
2!
4490
            }
2✔
4491
            SECTION("modifying the object while observing a specific value") {
6✔
4492
                realm_key_path_elem_t origin_value[] = {{class_foo.key, foo_int_key}};
2✔
4493
                realm_key_path_t key_path_origin_value[] = {{1, origin_value}};
2✔
4494
                realm_key_path_array_t key_path_array = {1, key_path_origin_value};
2✔
4495
                auto token = cptr(
2✔
4496
                    realm_object_add_notification_callback(obj1.get(), &state, nullptr, &key_path_array, on_change));
2✔
4497
                checked(realm_refresh(realm, nullptr));
2✔
4498
                state.called = false;
2✔
4499
                write([&]() {
2✔
4500
                    checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
2✔
4501
                });
2✔
4502
                REQUIRE(state.called);
2!
4503
                CHECK(!state.error);
2!
4504
                CHECK(state.changes);
2!
4505
                realm_property_key_t modified_keys[2];
2✔
4506
                size_t n = realm_object_changes_get_modified_properties(state.changes.get(), modified_keys, 2);
2✔
4507
                CHECK(n == 1);
2!
4508
                CHECK(modified_keys[0] == foo_int_key);
2!
4509

1✔
4510
                state.called = false;
2✔
4511
                write([&]() {
2✔
4512
                    // checked(realm_set_value(obj1.get(), foo_int_key, rlm_int_val(999), false));
1✔
4513
                    checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("aaa"), false));
2✔
4514
                });
2✔
4515
                REQUIRE(!state.called);
2!
4516
            }
2✔
4517
        }
6✔
4518
    }
198✔
4519

149✔
4520
    SECTION("threads") {
298✔
4521
        CPtr<realm_object_t> foo_obj, bar_obj;
10✔
4522
        write([&]() {
10✔
4523
            foo_obj = cptr_checked(realm_object_create(realm, class_foo.key));
10✔
4524
            realm_set_value(foo_obj.get(), foo_int_key, rlm_int_val(123), false);
10✔
4525
            bar_obj = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(123)));
10✔
4526
        });
10✔
4527

5✔
4528
        auto list = cptr_checked(realm_get_list(foo_obj.get(), foo_properties["int_list"]));
10✔
4529
        auto set = cptr_checked(realm_get_set(foo_obj.get(), foo_properties["int_set"]));
10✔
4530
        auto dictionary = cptr_checked(realm_get_dictionary(foo_obj.get(), foo_properties["int_dict"]));
10✔
4531
        auto results = cptr_checked(realm_object_find_all(realm, class_foo.key));
10✔
4532

5✔
4533
        SECTION("wrong thread") {
10✔
4534
            std::thread t{[&]() {
2✔
4535
                realm_value_t val;
2✔
4536
                CHECK(!realm_get_value(foo_obj.get(), foo_int_key, &val));
2!
4537
                CHECK_ERR(RLM_ERR_WRONG_THREAD);
2!
4538
            }};
2✔
4539

1✔
4540
            t.join();
2✔
4541
        }
2✔
4542

5✔
4543
        SECTION("thread-safe references") {
10✔
4544
            auto foo_obj_tsr = cptr_checked(realm_create_thread_safe_reference(foo_obj.get()));
8✔
4545
            auto bar_obj_tsr = cptr_checked(realm_create_thread_safe_reference(bar_obj.get()));
8✔
4546
            auto list_tsr = cptr_checked(realm_create_thread_safe_reference(list.get()));
8✔
4547
            auto set_tsr = cptr_checked(realm_create_thread_safe_reference(set.get()));
8✔
4548
            auto dict_tsr = cptr_checked(realm_create_thread_safe_reference(dictionary.get()));
8✔
4549
            auto results_tsr = cptr_checked(realm_create_thread_safe_reference(results.get()));
8✔
4550

4✔
4551
            SECTION("resolve") {
8✔
4552
                std::thread t{[&]() {
2✔
4553
                    auto config = make_config(test_file.path.c_str());
2✔
4554
                    auto realm2 = cptr_checked(realm_open(config.get()));
2✔
4555
                    auto foo_obj2 =
2✔
4556
                        cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), foo_obj_tsr.get()));
2✔
4557
                    auto bar_obj2 =
2✔
4558
                        cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), bar_obj_tsr.get()));
2✔
4559
                    auto results2 =
2✔
4560
                        cptr_checked(realm_results_from_thread_safe_reference(realm2.get(), results_tsr.get()));
2✔
4561
                    auto list2 = cptr_checked(realm_list_from_thread_safe_reference(realm2.get(), list_tsr.get()));
2✔
4562
                    auto set2 = cptr_checked(realm_set_from_thread_safe_reference(realm2.get(), set_tsr.get()));
2✔
4563
                    auto dict2 =
2✔
4564
                        cptr_checked(realm_dictionary_from_thread_safe_reference(realm2.get(), dict_tsr.get()));
2✔
4565

1✔
4566
                    realm_value_t foo_obj_int;
2✔
4567
                    CHECK(realm_get_value(foo_obj2.get(), foo_int_key, &foo_obj_int));
2!
4568
                    CHECK(rlm_val_eq(foo_obj_int, rlm_int_val(123)));
2!
4569

1✔
4570
                    size_t count;
2✔
4571
                    CHECK(realm_results_count(results2.get(), &count));
2!
4572
                    CHECK(count == 1);
2!
4573
                }};
2✔
4574

1✔
4575
                t.join();
2✔
4576
            }
2✔
4577

4✔
4578
            SECTION("resolve in frozen") {
8✔
4579
                auto realm2 = cptr_checked(realm_freeze(realm));
2✔
4580
                CHECK(realm_is_frozen(realm2.get()));
2!
4581
                CHECK(realm != realm2.get());
2!
4582

1✔
4583
                auto foo_obj2 =
2✔
4584
                    cptr_checked(realm_object_from_thread_safe_reference(realm2.get(), foo_obj_tsr.get()));
2✔
4585
                CHECK(realm_is_frozen(foo_obj2.get()));
2!
4586
            }
2✔
4587

4✔
4588
            SECTION("type error") {
8✔
4589
                CHECK(!realm_object_from_thread_safe_reference(realm, list_tsr.get()));
2!
4590
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4591
                CHECK(!realm_list_from_thread_safe_reference(realm, foo_obj_tsr.get()));
2!
4592
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4593
                CHECK(!realm_set_from_thread_safe_reference(realm, list_tsr.get()));
2!
4594
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4595
                CHECK(!realm_dictionary_from_thread_safe_reference(realm, set_tsr.get()));
2!
4596
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4597
                CHECK(!realm_results_from_thread_safe_reference(realm, list_tsr.get()));
2!
4598
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4599
                CHECK(!realm_from_thread_safe_reference(list_tsr.get(), nullptr));
2!
4600
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4601
            }
2✔
4602

4✔
4603
            SECTION("non-sendable") {
8✔
4604
                auto c = cptr(realm_config_new());
2✔
4605
                CHECK(!realm_create_thread_safe_reference(c.get()));
2!
4606
                CHECK_ERR(RLM_ERR_ILLEGAL_OPERATION);
2!
4607
            }
2✔
4608
        }
8✔
4609
    }
10✔
4610

149✔
4611
    SECTION("freeze and thaw") {
298✔
4612
        SECTION("realm") {
12✔
4613
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4614
            CHECK(!realm_is_frozen(realm));
2!
4615
            CHECK(realm_is_frozen(frozen_realm.get()));
2!
4616
        }
2✔
4617

6✔
4618
        SECTION("objects") {
12✔
4619
            CPtr<realm_object_t> obj1;
2✔
4620
            realm_value_t value;
2✔
4621

1✔
4622
            write([&]() {
2✔
4623
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4624
                CHECK(obj1);
2!
4625
            });
2✔
4626
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
2!
4627
            CHECK(value.type == RLM_TYPE_STRING);
2!
4628
            CHECK(strncmp(value.string.data, "", value.string.size) == 0);
2!
4629

1✔
4630
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4631
            realm_object_t* frozen_obj1;
2✔
4632
            CHECK(realm_object_resolve_in(obj1.get(), frozen_realm.get(), &frozen_obj1));
2!
4633

1✔
4634
            write([&]() {
2✔
4635
                CHECK(checked(realm_set_value(obj1.get(), foo_str_key, rlm_str_val("Hello, World!"), false)));
2!
4636
            });
2✔
4637

1✔
4638
            CHECK(checked(realm_get_value(obj1.get(), foo_str_key, &value)));
2!
4639
            CHECK(value.type == RLM_TYPE_STRING);
2!
4640
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
2!
4641

1✔
4642
            CHECK(checked(realm_get_value(frozen_obj1, foo_str_key, &value)));
2!
4643
            CHECK(value.type == RLM_TYPE_STRING);
2!
4644
            CHECK(strncmp(value.string.data, "", value.string.size) == 0);
2!
4645
            realm_object_t* thawed_obj1;
2✔
4646
            CHECK(realm_object_resolve_in(obj1.get(), realm, &thawed_obj1));
2!
4647
            CHECK(thawed_obj1);
2!
4648
            CHECK(checked(realm_get_value(thawed_obj1, foo_str_key, &value)));
2!
4649
            CHECK(value.type == RLM_TYPE_STRING);
2!
4650
            CHECK(strncmp(value.string.data, "Hello, World!", value.string.size) == 0);
2!
4651

1✔
4652
            write([&]() {
2✔
4653
                CHECK(checked(realm_object_delete(obj1.get())));
2!
4654
            });
2✔
4655
            realm_object_t* deleted_obj;
2✔
4656
            auto b = realm_object_resolve_in(frozen_obj1, realm, &deleted_obj);
2✔
4657
            CHECK(b);
2!
4658
            CHECK(deleted_obj == nullptr);
2!
4659
            realm_release(frozen_obj1);
2✔
4660
            realm_release(thawed_obj1);
2✔
4661
        }
2✔
4662

6✔
4663
        SECTION("results") {
12✔
4664
            auto results = cptr_checked(realm_object_find_all(realm, class_foo.key));
2✔
4665
            realm_results_delete_all(results.get());
2✔
4666

1✔
4667
            write([&]() {
2✔
4668
                // Ensure that we start from a known initial state
1✔
4669
                CHECK(realm_results_delete_all(results.get()));
2!
4670

1✔
4671
                auto obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4672
                CHECK(obj1);
2!
4673
            });
2✔
4674

1✔
4675
            size_t count;
2✔
4676
            realm_results_count(results.get(), &count);
2✔
4677
            CHECK(count == 1);
2!
4678

1✔
4679
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4680
            auto frozen_results = cptr_checked(realm_results_resolve_in(results.get(), frozen_realm.get()));
2✔
4681
            write([&]() {
2✔
4682
                auto obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4683
                CHECK(obj1);
2!
4684
            });
2✔
4685
            realm_results_count(frozen_results.get(), &count);
2✔
4686
            CHECK(count == 1);
2!
4687
            realm_results_count(results.get(), &count);
2✔
4688
            CHECK(count == 2);
2!
4689

1✔
4690
            auto thawed_results = cptr_checked(realm_results_resolve_in(frozen_results.get(), realm));
2✔
4691
            realm_results_count(thawed_results.get(), &count);
2✔
4692
            CHECK(count == 2);
2!
4693
        }
2✔
4694

6✔
4695
        SECTION("lists") {
12✔
4696
            CPtr<realm_object_t> obj1;
2✔
4697
            size_t count;
2✔
4698

1✔
4699
            write([&]() {
2✔
4700
                obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_bar.key, rlm_int_val(1)));
2✔
4701
                CHECK(obj1);
2!
4702
            });
2✔
4703

1✔
4704
            auto list = cptr_checked(realm_get_list(obj1.get(), bar_properties["strings"]));
2✔
4705
            realm_list_size(list.get(), &count);
2✔
4706
            CHECK(count == 0);
2!
4707

1✔
4708
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4709
            realm_list_t* frozen_list;
2✔
4710
            CHECK(realm_list_resolve_in(list.get(), frozen_realm.get(), &frozen_list));
2!
4711
            realm_list_size(frozen_list, &count);
2✔
4712
            CHECK(count == 0);
2!
4713

1✔
4714
            write([&]() {
2✔
4715
                checked(realm_list_insert(list.get(), 0, rlm_str_val("Hello")));
2✔
4716
            });
2✔
4717

1✔
4718
            realm_list_size(frozen_list, &count);
2✔
4719
            CHECK(count == 0);
2!
4720
            realm_list_size(list.get(), &count);
2✔
4721
            CHECK(count == 1);
2!
4722

1✔
4723
            realm_list_t* thawed_list;
2✔
4724
            CHECK(realm_list_resolve_in(frozen_list, realm, &thawed_list));
2!
4725
            realm_list_size(thawed_list, &count);
2✔
4726
            CHECK(count == 1);
2!
4727

1✔
4728
            CHECK(realm_list_is_valid(thawed_list));
2!
4729
            write([&]() {
2✔
4730
                CHECK(checked(realm_object_delete(obj1.get())));
2!
4731
            });
2✔
4732
            CHECK(!realm_list_is_valid(thawed_list));
2!
4733
            realm_release(thawed_list);
2✔
4734
            CHECK(realm_list_resolve_in(frozen_list, realm, &thawed_list));
2!
4735
            CHECK(thawed_list == nullptr);
2!
4736
            realm_release(frozen_list);
2✔
4737
        }
2✔
4738

6✔
4739
        SECTION("sets") {
12✔
4740
            CPtr<realm_object_t> obj1;
2✔
4741
            size_t count;
2✔
4742

1✔
4743
            write([&]() {
2✔
4744
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4745
                CHECK(obj1);
2!
4746
            });
2✔
4747

1✔
4748
            auto set = cptr_checked(realm_get_set(obj1.get(), foo_properties["string_set"]));
2✔
4749
            realm_set_size(set.get(), &count);
2✔
4750
            CHECK(count == 0);
2!
4751

1✔
4752
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4753
            realm_set_t* frozen_set;
2✔
4754
            CHECK(realm_set_resolve_in(set.get(), frozen_realm.get(), &frozen_set));
2!
4755
            realm_set_size(frozen_set, &count);
2✔
4756
            CHECK(count == 0);
2!
4757

1✔
4758
            write([&]() {
2✔
4759
                checked(realm_set_insert(set.get(), rlm_str_val("Hello"), nullptr, nullptr));
2✔
4760
            });
2✔
4761

1✔
4762
            realm_set_size(frozen_set, &count);
2✔
4763
            CHECK(count == 0);
2!
4764
            realm_set_size(set.get(), &count);
2✔
4765
            CHECK(count == 1);
2!
4766

1✔
4767
            realm_set_t* thawed_set;
2✔
4768
            CHECK(realm_set_resolve_in(frozen_set, realm, &thawed_set));
2!
4769
            realm_set_size(thawed_set, &count);
2✔
4770
            CHECK(count == 1);
2!
4771

1✔
4772
            CHECK(realm_set_is_valid(thawed_set));
2!
4773
            write([&]() {
2✔
4774
                CHECK(checked(realm_object_delete(obj1.get())));
2!
4775
            });
2✔
4776
            CHECK(!realm_set_is_valid(thawed_set));
2!
4777
            realm_release(thawed_set);
2✔
4778
            CHECK(realm_set_resolve_in(frozen_set, realm, &thawed_set));
2!
4779
            CHECK(thawed_set == nullptr);
2!
4780
            realm_release(frozen_set);
2✔
4781
        }
2✔
4782

6✔
4783
        SECTION("dictionaries") {
12✔
4784
            CPtr<realm_object_t> obj1;
2✔
4785
            size_t count;
2✔
4786

1✔
4787
            write([&]() {
2✔
4788
                obj1 = cptr_checked(realm_object_create(realm, class_foo.key));
2✔
4789
                CHECK(obj1);
2!
4790
            });
2✔
4791

1✔
4792
            auto dictionary = cptr_checked(realm_get_dictionary(obj1.get(), foo_properties["string_dict"]));
2✔
4793
            realm_dictionary_size(dictionary.get(), &count);
2✔
4794
            CHECK(count == 0);
2!
4795

1✔
4796
            auto frozen_realm = cptr_checked(realm_freeze(realm));
2✔
4797
            realm_dictionary_t* frozen_dictionary;
2✔
4798
            CHECK(realm_dictionary_resolve_in(dictionary.get(), frozen_realm.get(), &frozen_dictionary));
2!
4799
            realm_dictionary_size(frozen_dictionary, &count);
2✔
4800
            CHECK(count == 0);
2!
4801

1✔
4802
            write([&]() {
2✔
4803
                checked(realm_dictionary_insert(dictionary.get(), rlm_str_val("Hello"), rlm_str_val("world"), nullptr,
2✔
4804
                                                nullptr));
2✔
4805
            });
2✔
4806

1✔
4807
            realm_dictionary_size(frozen_dictionary, &count);
2✔
4808
            CHECK(count == 0);
2!
4809
            realm_dictionary_size(dictionary.get(), &count);
2✔
4810
            CHECK(count == 1);
2!
4811

1✔
4812
            realm_dictionary_t* thawed_dictionary;
2✔
4813
            CHECK(realm_dictionary_resolve_in(frozen_dictionary, realm, &thawed_dictionary));
2!
4814
            realm_dictionary_size(thawed_dictionary, &count);
2✔
4815
            CHECK(count == 1);
2!
4816

1✔
4817
            CHECK(realm_dictionary_is_valid(thawed_dictionary));
2!
4818
            write([&]() {
2✔
4819
                CHECK(checked(realm_object_delete(obj1.get())));
2!
4820
            });
2✔
4821
            CHECK(!realm_dictionary_is_valid(thawed_dictionary));
2!
4822
            realm_release(thawed_dictionary);
2✔
4823
            CHECK(realm_dictionary_resolve_in(frozen_dictionary, realm, &thawed_dictionary));
2!
4824
            CHECK(thawed_dictionary == nullptr);
2!
4825
            realm_release(frozen_dictionary);
2✔
4826
        }
2✔
4827
    }
12✔
4828

149✔
4829
    realm_close(realm);
298✔
4830
    REQUIRE(realm_is_closed(realm));
298!
4831
    realm_release(realm);
298✔
4832
}
298✔
4833

4834
TEST_CASE("C API: convert", "[c_api]") {
8✔
4835
    TestFile test_file;
8✔
4836
    TestFile dest_test_file;
8✔
4837
    realm_t* realm;
8✔
4838
    ObjectSchema object_schema = {"Foo",
8✔
4839
                                  {
8✔
4840
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
8✔
4841
                                      {"string_value", PropertyType::String},
8✔
4842
                                  }};
8✔
4843

4✔
4844
    { // seed a Realm with an object
8✔
4845
        auto config = make_config(test_file.path.c_str(), false);
8✔
4846
        config->schema = Schema{object_schema};
8✔
4847
        config->schema_version = 0;
8✔
4848
        realm = realm_open(config.get());
8✔
4849
        REQUIRE(checked(realm));
8!
4850
        CHECK(!realm_equals(realm, nullptr));
8!
4851
        realm_class_info_t class_foo;
8✔
4852
        bool found = false;
8✔
4853
        CHECK(checked(realm_find_class(realm, "Foo", &found, &class_foo)));
8!
4854
        REQUIRE(found);
8!
4855

4✔
4856
        realm_property_key_t foo_str_col_key;
8✔
4857
        realm_property_info_t info;
8✔
4858
        found = false;
8✔
4859
        REQUIRE(realm_find_property(realm, class_foo.key, "string_value", &found, &info));
8!
4860
        REQUIRE(found);
8!
4861
        CHECK(info.key != RLM_INVALID_PROPERTY_KEY);
8!
4862
        foo_str_col_key = info.key;
8✔
4863

4✔
4864
        CPtr<realm_object_t> obj1;
8✔
4865
        checked(realm_begin_write(realm));
8✔
4866
        realm_value_t pk = rlm_int_val(42);
8✔
4867
        obj1 = cptr_checked(realm_object_create_with_primary_key(realm, class_foo.key, pk));
8✔
4868
        CHECK(obj1);
8!
4869
        CHECK(checked(realm_set_value(obj1.get(), foo_str_col_key, rlm_str_val("Hello, World!"), false)));
8!
4870
        checked(realm_commit(realm));
8✔
4871
        checked(realm_refresh(realm, nullptr));
8✔
4872

4✔
4873
        size_t foo_count;
8✔
4874
        CHECK(checked(realm_get_num_objects(realm, class_foo.key, &foo_count)));
8!
4875
        REQUIRE(foo_count == 1);
8!
4876
    }
8✔
4877

4✔
4878
    CHECK(realm_get_num_classes(realm) == 1);
8!
4879

4✔
4880
    SECTION("convert with path") {
8✔
4881
        bool merge_with_existing = false;
4✔
4882
        realm_binary encryption_key{nullptr, 0};
4✔
4883

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

2✔
4886
        SECTION("convert again without merge should fail") {
4✔
4887
            REQUIRE_FALSE(
2!
4888
                realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
2✔
4889
        }
2✔
4890
        SECTION("convert again with merge should succeed") {
4✔
4891
            merge_with_existing = true;
2✔
4892
            REQUIRE(realm_convert_with_path(realm, dest_test_file.path.c_str(), encryption_key, merge_with_existing));
2!
4893
        }
2✔
4894
    }
4✔
4895

4✔
4896
    SECTION("convert with config") {
8✔
4897
        auto dest_config = make_config(dest_test_file.path.c_str(), false);
4✔
4898
        dest_config->schema = Schema{object_schema};
4✔
4899
        dest_config->schema_version = 0;
4✔
4900
        bool merge_with_existing = false;
4✔
4901
        REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
4!
4902
        SECTION("convert again without merge should fail") {
4✔
4903
            REQUIRE_FALSE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
2!
4904
        }
2✔
4905
        SECTION("convert again with merge should succeed") {
4✔
4906
            merge_with_existing = true;
2✔
4907
            REQUIRE(realm_convert_with_config(realm, dest_config.get(), merge_with_existing));
2!
4908
        }
2✔
4909
    }
4✔
4910
    realm_close(realm);
8✔
4911
    REQUIRE(realm_is_closed(realm));
8!
4912
    realm_release(realm);
8✔
4913
}
8✔
4914

4915
struct Userdata {
4916
    std::atomic<bool> called{false};
4917
    bool has_error;
4918
    realm_error_t error;
4919
    realm_thread_safe_reference_t* realm_ref = nullptr;
4920
    std::string error_message;
4921
};
4922

4923
#if REALM_ENABLE_SYNC
4924

4925
static void task_completion_func(void* p, realm_thread_safe_reference_t* realm,
4926
                                 const realm_async_error_t* async_error)
4927
{
4✔
4928
    auto userdata_p = static_cast<Userdata*>(p);
4✔
4929

2✔
4930
    userdata_p->realm_ref = realm;
4✔
4931
    userdata_p->has_error = realm_get_async_error(async_error, &userdata_p->error);
4✔
4932
    userdata_p->called = true;
4✔
4933
}
4✔
4934

4935
static void task_init_subscription(realm_t* realm, void*)
4936
{
×
4937
    REQUIRE(realm);
×
4938
}
×
4939

4940
static void sync_error_handler(void* p, realm_sync_session_t*, const realm_sync_error_t error)
4941
{
2✔
4942
    auto userdata_p = static_cast<Userdata*>(p);
2✔
4943
    userdata_p->has_error = true;
2✔
4944
    userdata_p->error_message = error.status.message;
2✔
4945
    userdata_p->error.error = error.status.error;
2✔
4946
    userdata_p->error.categories = error.status.categories;
2✔
4947
    userdata_p->error.message = userdata_p->error_message.c_str();
2✔
4948
}
2✔
4949

4950
TEST_CASE("C API - async_open", "[sync][pbs][c_api]") {
4✔
4951
    TestSyncManager init_sync_manager;
4✔
4952
    SyncTestFile test_config(init_sync_manager.app(), "default");
4✔
4953
    test_config.cache = false;
4✔
4954
    ObjectSchema object_schema = {"object",
4✔
4955
                                  {
4✔
4956
                                      {"_id", PropertyType::Int, Property::IsPrimary{true}},
4✔
4957
                                      {"value", PropertyType::Int},
4✔
4958
                                  }};
4✔
4959
    test_config.schema = Schema{object_schema};
4✔
4960

2✔
4961
    SECTION("can open synced Realms that don't already exist") {
4✔
4962
        realm_config_t* config = realm_config_new();
2✔
4963
        config->schema = Schema{object_schema};
2✔
4964
        realm_user user(init_sync_manager.app()->current_user());
2✔
4965
        realm_sync_config_t* sync_config = realm_sync_config_new(&user, "default");
2✔
4966
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
2✔
4967
                                                           nullptr);
2✔
4968
        realm_config_set_path(config, test_config.path.c_str());
2✔
4969
        realm_config_set_sync_config(config, sync_config);
2✔
4970
        realm_config_set_schema_version(config, 1);
2✔
4971
        realm_async_open_task_t* task = realm_open_synchronized(config);
2✔
4972
        REQUIRE(task);
2!
4973
        Userdata userdata;
2✔
4974
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
2✔
4975
        util::EventLoop::main().run_until([&] {
10,186✔
4976
            return userdata.called.load();
10,186✔
4977
        });
10,186✔
4978
        REQUIRE(userdata.called);
2!
4979
        REQUIRE(userdata.realm_ref);
2!
4980
        realm_release(task);
2✔
4981

1✔
4982
        realm_t* realm = realm_from_thread_safe_reference(userdata.realm_ref, nullptr);
2✔
4983
        realm_release(userdata.realm_ref);
2✔
4984

1✔
4985
        bool found;
2✔
4986
        realm_class_info_t class_info;
2✔
4987
        realm_find_class(realm, "object", &found, &class_info);
2✔
4988
        REQUIRE(found);
2!
4989
        realm_release(realm);
2✔
4990
        realm_release(config);
2✔
4991
        realm_release(sync_config);
2✔
4992
    }
2✔
4993

2✔
4994
    SECTION("cancels download and reports an error on auth error") {
4✔
4995
        // Create a token which can be parsed as a JWT but is not valid
1✔
4996
        std::string unencoded_body = nlohmann::json({{"exp", 123}, {"iat", 456}}).dump();
2✔
4997
        std::string encoded_body;
2✔
4998
        encoded_body.resize(util::base64_encoded_size(unencoded_body.size()));
2✔
4999
        util::base64_encode(unencoded_body.data(), unencoded_body.size(), &encoded_body[0], encoded_body.size());
2✔
5000
        auto invalid_token = "." + encoded_body + ".";
2✔
5001

1✔
5002

1✔
5003
        realm_config_t* config = realm_config_new();
2✔
5004
        config->schema = Schema{object_schema};
2✔
5005
        realm_user user(init_sync_manager.app()->current_user());
2✔
5006
        realm_sync_config_t* sync_config = realm_sync_config_new(&user, "realm");
2✔
5007
        realm_sync_config_set_initial_subscription_handler(sync_config, task_init_subscription, false, nullptr,
2✔
5008
                                                           nullptr);
2✔
5009
        sync_config->user->log_in(invalid_token, invalid_token);
2✔
5010

1✔
5011
        realm_config_set_path(config, test_config.path.c_str());
2✔
5012
        realm_config_set_schema_version(config, 1);
2✔
5013
        Userdata userdata;
2✔
5014
        realm_sync_config_set_error_handler(sync_config, sync_error_handler, &userdata, nullptr);
2✔
5015
        realm_config_set_sync_config(config, sync_config);
2✔
5016

1✔
5017
        realm_async_open_task_t* task = realm_open_synchronized(config);
2✔
5018
        REQUIRE(task);
2!
5019
        realm_async_open_task_start(task, task_completion_func, &userdata, nullptr);
2✔
5020
        init_sync_manager.network_callback(app::Response{403});
2✔
5021
        util::EventLoop::main().run_until([&] {
3✔
5022
            return userdata.called.load();
3✔
5023
        });
3✔
5024
        REQUIRE(userdata.called);
2!
5025
        REQUIRE(!userdata.realm_ref);
2!
5026
        REQUIRE(userdata.error.error == RLM_ERR_AUTH_ERROR);
2!
5027
        REQUIRE(userdata.error_message ==
2!
5028
                "Unable to refresh the user access token: http error code considered fatal. Client Error: 403");
2✔
5029
        realm_release(task);
2✔
5030
        realm_release(config);
2✔
5031
        realm_release(sync_config);
2✔
5032
    }
2✔
5033
}
4✔
5034

5035
struct BCTOState {
5036
    bool bcto_deleted = false;
5037
    bool thread_create_called = false;
5038
    bool thread_destroy_called = false;
5039
    std::string thread_on_error_message;
5040
    std::string id = "BTCO-STATE";
5041
};
5042

5043

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

1✔
5047
    auto bcto_free_userdata = [](realm_userdata_t userdata) {
2✔
5048
        REQUIRE(userdata);
2!
5049
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5050
        REQUIRE(user_data->bcto_deleted == false);
2!
5051
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5052
        user_data->id.clear();
2✔
5053
        user_data->bcto_deleted = true;
2✔
5054
    };
2✔
5055

1✔
5056
    auto bcto_on_thread_create = [](realm_userdata_t userdata) {
2✔
5057
        REQUIRE(userdata);
2!
5058
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5059
        REQUIRE(user_data->bcto_deleted == false);
2!
5060
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5061
        REQUIRE(!user_data->thread_create_called);
2!
5062
        user_data->thread_create_called = true;
2✔
5063
    };
2✔
5064

1✔
5065
    auto bcto_on_thread_destroy = [](realm_userdata_t userdata) {
2✔
5066
        REQUIRE(userdata);
2!
5067
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5068
        REQUIRE(user_data->bcto_deleted == false);
2!
5069
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5070
        REQUIRE(!user_data->thread_destroy_called);
2!
5071
        user_data->thread_destroy_called = true;
2✔
5072
    };
2✔
5073

1✔
5074
    auto bcto_on_thread_error = [](realm_userdata_t userdata, const char* err_message) {
2✔
5075
        REQUIRE(userdata);
2!
5076
        REQUIRE(err_message);
2!
5077
        auto user_data = static_cast<BCTOState*>(userdata);
2✔
5078
        REQUIRE(user_data->bcto_deleted == false);
2!
5079
        REQUIRE((user_data && user_data->id == "BTCO-STATE"));
2!
5080
        REQUIRE(user_data->thread_on_error_message.empty());
2!
5081
        user_data->thread_on_error_message = err_message;
2✔
5082
        return true;
2✔
5083
    };
2✔
5084

1✔
5085
    {
2✔
5086
        auto config = cptr(realm_sync_client_config_new());
2✔
5087
        realm_sync_client_config_set_default_binding_thread_observer(
2✔
5088
            config.get(), bcto_on_thread_create, bcto_on_thread_destroy, bcto_on_thread_error,
2✔
5089
            static_cast<realm_userdata_t>(&bcto_user_data), bcto_free_userdata);
2✔
5090
        REQUIRE(config->default_socket_provider_thread_observer);
2!
5091
        auto observer_ptr =
2✔
5092
            static_cast<CBindingThreadObserver*>(config->default_socket_provider_thread_observer.get());
2✔
5093
        REQUIRE(observer_ptr->test_get_create_callback_func() == bcto_on_thread_create);
2!
5094
        REQUIRE(observer_ptr->test_get_destroy_callback_func() == bcto_on_thread_destroy);
2!
5095
        REQUIRE(observer_ptr->test_get_error_callback_func() == bcto_on_thread_error);
2!
5096
        REQUIRE(observer_ptr->has_handle_error());
2!
5097
        REQUIRE(observer_ptr->test_get_userdata_ptr() == &bcto_user_data);
2!
5098

1✔
5099
        auto test_thread = std::thread([&]() {
2✔
5100
            auto bcto_ptr = std::static_pointer_cast<realm::BindingCallbackThreadObserver>(
2✔
5101
                config->default_socket_provider_thread_observer);
2✔
5102
            REQUIRE(bcto_ptr);
2!
5103
            auto will_destroy_thread = util::make_scope_exit([&bcto_ptr]() noexcept {
2✔
5104
                bcto_ptr->will_destroy_thread();
2✔
5105
            });
2✔
5106
            bcto_ptr->did_create_thread();
2✔
5107
            REQUIRE(bcto_ptr->handle_error(MultipleSyncAgents()));
2!
5108
        });
2✔
5109

1✔
5110
        // Wait for the thread to exit
1✔
5111
        test_thread.join();
2✔
5112

1✔
5113
        REQUIRE(bcto_user_data.thread_create_called);
2!
5114
        REQUIRE(bcto_user_data.thread_on_error_message.find(
2!
5115
                    "Multiple sync agents attempted to join the same session") != std::string::npos);
2✔
5116
        REQUIRE(bcto_user_data.thread_destroy_called);
2!
5117
    }
2✔
5118

1✔
5119
    REQUIRE(bcto_user_data.bcto_deleted == true);
2!
5120

1✔
5121
    {
2✔
5122
        auto config = cptr(realm_sync_client_config_new());
2✔
5123
        realm_sync_client_config_set_default_binding_thread_observer(config.get(), nullptr, nullptr, nullptr, nullptr,
2✔
5124
                                                                     nullptr);
2✔
5125
        auto no_handle_error_ptr =
2✔
5126
            static_cast<CBindingThreadObserver*>(config->default_socket_provider_thread_observer.get());
2✔
5127
        no_handle_error_ptr->did_create_thread();                          // should not crash
2✔
5128
        no_handle_error_ptr->will_destroy_thread();                        // should not crash
2✔
5129
        REQUIRE(!no_handle_error_ptr->has_handle_error());                 // no handler, returns false
2!
5130
        REQUIRE(!no_handle_error_ptr->handle_error(MultipleSyncAgents())); // no handler, returns false
2!
5131
        // No free_user_data function was provided and internal default should be used
1✔
5132
        // Should not crash at scope exit
1✔
5133
    }
2✔
5134
}
2✔
5135
#endif
5136

5137
#if REALM_ENABLE_AUTH_TESTS
5138

5139
std::atomic_bool baas_client_stop{false};
5140
std::atomic<std::size_t> error_handler_counter{0};
5141
std::atomic<std::size_t> before_client_reset_counter{0};
5142
std::atomic<std::size_t> after_client_reset_counter{0};
5143

5144
TEST_CASE("C API - client reset", "[sync][pbs][c_api][client reset][baas]") {
6✔
5145
    reset_utils::Partition partition{"realm_id", random_string(20)};
6✔
5146
    Property partition_prop = {partition.property_name, PropertyType::String | PropertyType::Nullable};
6✔
5147
    Schema schema{
6✔
5148
        {"object",
6✔
5149
         {
6✔
5150
             {"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
6✔
5151
             {"value", PropertyType::Int},
6✔
5152
             partition_prop,
6✔
5153
         }},
6✔
5154
    };
6✔
5155

3✔
5156
    std::string base_url = get_base_url();
6✔
5157
    REQUIRE(!base_url.empty());
6!
5158
    auto server_app_config = minimal_app_config(base_url, "c_api_client_reset_tests", schema);
6✔
5159
    server_app_config.partition_key = partition_prop;
6✔
5160
    TestAppSession test_app_session(create_app(server_app_config));
6✔
5161

3✔
5162
    auto app = test_app_session.app();
6✔
5163
    auto get_valid_config = [&]() -> SyncTestFile {
12✔
5164
        create_user_and_log_in(app);
12✔
5165
        return SyncTestFile(app->current_user(), partition.value, schema);
12✔
5166
    };
12✔
5167
    SyncTestFile local_config = get_valid_config();
6✔
5168
    SyncTestFile remote_config = get_valid_config();
6✔
5169

3✔
5170
    auto make_reset = [&](Realm::Config config_local,
6✔
5171
                          Realm::Config config_remote) -> std::unique_ptr<reset_utils::TestClientReset> {
6✔
5172
        return reset_utils::make_baas_client_reset(config_local, config_remote, test_app_session);
6✔
5173
    };
6✔
5174

3✔
5175
    local_config.sync_config = std::make_shared<realm_sync_config_t>(*local_config.sync_config);
6✔
5176
    realm_sync_config_t* local_sync_config = static_cast<realm_sync_config_t*>(local_config.sync_config.get());
6✔
5177

3✔
5178
    struct ResetRealmFiles {
6✔
5179
        void set_app(std::shared_ptr<realm::app::App> app)
6✔
5180
        {
6✔
5181
            m_app = app;
6✔
5182
        }
6✔
5183
        void reset_realm(const char* path)
6✔
5184
        {
5✔
5185
            realm_app_t realm_app{m_app};
4✔
5186
            bool did_run;
4✔
5187
            realm_sync_immediately_run_file_actions(&realm_app, path, &did_run);
4✔
5188
        }
4✔
5189
        static ResetRealmFiles& instance()
6✔
5190
        {
10✔
5191
            static ResetRealmFiles instance;
10✔
5192
            return instance;
10✔
5193
        }
10✔
5194
        std::shared_ptr<realm::app::App> m_app;
6✔
5195
    };
6✔
5196
    ResetRealmFiles::instance().set_app(app);
6✔
5197

3✔
5198
    SECTION("Manual reset") {
6✔
5199
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_MANUAL);
2✔
5200

1✔
5201
        realm_sync_config_set_error_handler(
2✔
5202
            local_sync_config,
2✔
5203
            [](realm_userdata_t, realm_sync_session_t*, const realm_sync_error_t sync_error) {
2✔
5204
                REQUIRE(sync_error.c_original_file_path_key);
2!
5205
                REQUIRE(sync_error.c_recovery_file_path_key);
2!
5206
                REQUIRE(sync_error.is_client_reset_requested);
2!
5207
                REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_CLIENT_RESET);
2!
5208
                ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
2✔
5209
                baas_client_stop.store(true);
2✔
5210
            },
2✔
5211
            nullptr, nullptr);
2✔
5212

1✔
5213
        make_reset(local_config, remote_config)
2✔
5214
            ->on_post_reset([&](SharedRealm) {
2✔
5215
                util::EventLoop::main().run_until([&] {
778✔
5216
                    return baas_client_stop.load();
778✔
5217
                });
778✔
5218
            })
2✔
5219
            ->run();
2✔
5220
    }
2✔
5221

3✔
5222
    SECTION("Local Discard") {
6✔
5223
        realm_sync_config_set_resync_mode(local_sync_config, RLM_SYNC_SESSION_RESYNC_MODE_DISCARD_LOCAL);
4✔
5224

2✔
5225
        SECTION("Before client reset success") {
4✔
5226
            realm_sync_config_set_before_client_reset_handler(
2✔
5227
                local_sync_config,
2✔
5228
                [](realm_userdata_t, realm_t*) -> bool {
2✔
5229
                    before_client_reset_counter.fetch_add(1);
2✔
5230
                    return true;
2✔
5231
                },
2✔
5232
                nullptr, nullptr);
2✔
5233

1✔
5234
            realm_sync_config_set_after_client_reset_handler(
2✔
5235
                local_sync_config,
2✔
5236
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
2✔
5237
                    after_client_reset_counter.fetch_add(1);
2✔
5238
                    baas_client_stop.store(true);
2✔
5239
                    return true;
2✔
5240
                },
2✔
5241
                nullptr, nullptr);
2✔
5242

1✔
5243
            make_reset(local_config, remote_config)
2✔
5244
                ->on_post_reset([&](SharedRealm) {
2✔
5245
                    util::EventLoop::main().run_until([&] {
3✔
5246
                        return baas_client_stop.load();
3✔
5247
                    });
3✔
5248
                })
2✔
5249
                ->run();
2✔
5250

1✔
5251
            REQUIRE(before_client_reset_counter.load() == 1);
2!
5252
            REQUIRE(after_client_reset_counter.load() == 1);
2!
5253
        }
2✔
5254

2✔
5255
        SECTION("Before client reset fails") {
4✔
5256
            baas_client_stop.store(false);
2✔
5257
            before_client_reset_counter.store(0);
2✔
5258
            after_client_reset_counter.store(0);
2✔
5259

1✔
5260
            realm_sync_config_set_error_handler(
2✔
5261
                local_sync_config,
2✔
5262
                [](realm_userdata_t, realm_sync_session_t*, const realm_sync_error_t sync_error) {
2✔
5263
                    REQUIRE(sync_error.c_original_file_path_key);
2!
5264
                    REQUIRE(sync_error.c_recovery_file_path_key);
2!
5265
                    REQUIRE(sync_error.is_client_reset_requested);
2!
5266
                    // Callback in `realm_sync_config_set_before_client_reset_handler` fails, so
1✔
5267
                    // a synthetic error is created with no action.
1✔
5268
                    REQUIRE(sync_error.server_requests_action == RLM_SYNC_ERROR_ACTION_NO_ACTION);
2!
5269
                    ResetRealmFiles::instance().reset_realm(sync_error.c_original_file_path_key);
2✔
5270
                    error_handler_counter.fetch_add(1);
2✔
5271
                    baas_client_stop.store(true);
2✔
5272
                },
2✔
5273
                nullptr, nullptr);
2✔
5274

1✔
5275
            realm_sync_config_set_before_client_reset_handler(
2✔
5276
                local_sync_config,
2✔
5277
                [](realm_userdata_t, realm_t*) -> bool {
2✔
5278
                    before_client_reset_counter.fetch_add(1);
2✔
5279
                    return false;
2✔
5280
                },
2✔
5281
                nullptr, nullptr);
2✔
5282

1✔
5283
            realm_sync_config_set_after_client_reset_handler(
2✔
5284
                local_sync_config,
2✔
5285
                [](realm_userdata_t, realm_t*, realm_thread_safe_reference_t*, bool) -> bool {
1✔
5286
                    after_client_reset_counter.fetch_add(1);
×
5287
                    return true;
×
5288
                },
×
5289
                nullptr, nullptr);
2✔
5290

1✔
5291
            make_reset(local_config, remote_config)
2✔
5292
                ->on_post_reset([&](SharedRealm) {
2✔
5293
                    util::EventLoop::main().run_until([&] {
752✔
5294
                        return baas_client_stop.load();
752✔
5295
                    });
752✔
5296
                })
2✔
5297
                ->run();
2✔
5298

1✔
5299
            REQUIRE(error_handler_counter.load() == 1);
2!
5300
            REQUIRE(before_client_reset_counter.load() == 1);
2!
5301
            REQUIRE(after_client_reset_counter.load() == 0);
2!
5302
        }
2✔
5303
    }
4✔
5304
}
6✔
5305

5306
static void realm_app_void_completion(void*, const realm_app_error_t*) {}
14✔
5307

5308
static void realm_app_user1(void* p, realm_user_t* user, const realm_app_error_t*)
5309
{
10✔
5310
    auto clone_ptr = realm_clone(user);
10✔
5311
    CHECK(realm_equals(user, clone_ptr));
10!
5312
    *(static_cast<realm_user_t**>(p)) = static_cast<realm_user_t*>(clone_ptr);
10✔
5313
}
10✔
5314

5315
static void realm_app_user2(void* p, realm_user_t* user, const realm_app_error_t*)
5316
{
2✔
5317
    realm_user_identity_t idents[10];
2✔
5318
    size_t n;
2✔
5319
    realm_user_get_all_identities(user, idents, 10, &n);
2✔
5320
    *static_cast<bool*>(p) = n == 2;
2✔
5321
    for (size_t i = 0; i < n; i++) {
6✔
5322
        realm_free(idents[i].id);
4✔
5323
    }
4✔
5324
}
2✔
5325

5326
TEST_CASE("C API app: link_user integration w/c_api transport", "[sync][app][c_api][baas]") {
12✔
5327
    struct TestTransportUserData {
12✔
5328
        TestTransportUserData()
12✔
5329
            : logger(util::Logger::get_default_logger())
12✔
5330
            , transport(std::make_unique<SynchronousTestTransport>())
12✔
5331
        {
12✔
5332
        }
12✔
5333
        std::shared_ptr<util::Logger> logger;
12✔
5334
        std::unique_ptr<realm::app::GenericNetworkTransport> transport;
12✔
5335
    };
12✔
5336

6✔
5337
    auto send_request_to_server = [](realm_userdata_t userdata, const realm_http_request_t request,
12✔
5338
                                     void* request_context) {
88✔
5339
        using namespace realm::app;
88✔
5340

44✔
5341
        constexpr uint64_t default_timeout_ms = 60000;
88✔
5342
        REQUIRE(userdata != nullptr);
88!
5343
        TestTransportUserData* user_data(static_cast<TestTransportUserData*>(userdata));
88✔
5344
        REQUIRE(user_data != nullptr);
88!
5345
        REQUIRE(user_data->transport != nullptr);
88!
5346
        REQUIRE(user_data->logger != nullptr);
88!
5347
        REQUIRE(strlen(request.url) > 0);
88!
5348
        HttpHeaders headers;
88✔
5349
        for (size_t i = 0; i < request.num_headers; i++) {
272✔
5350
            REQUIRE(request.headers[i].name != nullptr);
184!
5351
            REQUIRE(request.headers[i].value != nullptr);
184!
5352
            std::string name(request.headers[i].name);
184✔
5353
            std::string value(request.headers[i].value);
184✔
5354
            REQUIRE(!name.empty());
184!
5355
            REQUIRE(!value.empty());
184!
5356
            headers.emplace(name, value);
184✔
5357
        }
184✔
5358
        REQUIRE(request_context != nullptr);
88!
5359
        auto new_request = Request{HttpMethod(request.method), request.url, default_timeout_ms, std::move(headers),
88✔
5360
                                   std::string(request.body, request.body_size)};
88✔
5361
        user_data->logger->trace("CAPI: Request URL (%1): %2", httpmethod_to_string(new_request.method),
88✔
5362
                                 new_request.url);
88✔
5363
        user_data->logger->trace("CAPI: Request body: %1", new_request.body);
88✔
5364
        user_data->transport->send_request_to_server(new_request, [&](const Response& response) mutable {
88✔
5365
            std::vector<realm_http_header_t> c_headers;
88✔
5366
            c_headers.reserve(response.headers.size());
88✔
5367
            for (auto&& header : response.headers) {
692✔
5368
                c_headers.push_back({header.first.c_str(), header.second.c_str()});
692✔
5369
            }
692✔
5370

44✔
5371
            auto c_response = std::make_unique<realm_http_response_t>();
88✔
5372
            c_response->status_code = response.http_status_code;
88✔
5373
            c_response->custom_status_code = response.custom_status_code;
88✔
5374
            c_response->headers = c_headers.data();
88✔
5375
            c_response->num_headers = c_headers.size();
88✔
5376
            c_response->body = response.body.data();
88✔
5377
            c_response->body_size = response.body.size();
88✔
5378
            user_data->logger->trace("CAPI: Response (%1): %2", c_response->status_code,
88✔
5379
                                     std::string(c_response->body, c_response->body_size));
88✔
5380
            realm_http_transport_complete_request(request_context, c_response.get());
88✔
5381
        });
88✔
5382
    };
88✔
5383

6✔
5384
    auto user_data_free = [](void* userdata) {
12✔
5385
        REQUIRE(userdata != nullptr);
12!
5386
        std::unique_ptr<TestTransportUserData> user_data(static_cast<TestTransportUserData*>(userdata));
12✔
5387
        REQUIRE(user_data != nullptr);
12!
5388
        REQUIRE(user_data->transport != nullptr);
12!
5389
        REQUIRE(user_data->logger != nullptr);
12!
5390
        user_data->logger->trace("CAPI: user_data free called");
12✔
5391
    };
12✔
5392

6✔
5393
    // user_data will be deleted when user_data_free() is called
6✔
5394
    auto user_data = new TestTransportUserData();
12✔
5395
    auto http_transport = realm_http_transport_new(send_request_to_server, user_data, user_data_free);
12✔
5396
    auto app_session = get_runtime_app_session(get_base_url());
12✔
5397
    TestAppSession session(app_session, *http_transport, DeleteApp{false});
12✔
5398
    realm_app app(session.app());
12✔
5399

6✔
5400
    SECTION("remove_user integration") {
12✔
5401
        AutoVerifiedEmailCredentials creds;
2✔
5402
        realm_user_t* sync_user_1 = nullptr;
2✔
5403
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5404
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5405
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
5406
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5407
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
2✔
5408

1✔
5409
        realm_app_remove_user(&app, sync_user_1, realm_app_void_completion, nullptr, nullptr);
2✔
5410
        auto state = realm_user_get_state(sync_user_1);
2✔
5411
        CHECK(state == RLM_USER_STATE_REMOVED);
2!
5412
        realm_release(sync_user_1);
2✔
5413
    }
2✔
5414
    SECTION("delete_user integration") {
12✔
5415
        AutoVerifiedEmailCredentials creds;
2✔
5416
        realm_user_t* sync_user = nullptr;
2✔
5417
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5418
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5419
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
5420
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5421
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user, nullptr);
2✔
5422

1✔
5423
        realm_app_delete_user(&app, sync_user, realm_app_void_completion, nullptr, nullptr);
2✔
5424
        auto state = realm_user_get_state(sync_user);
2✔
5425
        CHECK(state == RLM_USER_STATE_REMOVED);
2!
5426
        realm_release(sync_user);
2✔
5427
    }
2✔
5428
    SECTION("link_user integration") {
12✔
5429
        AutoVerifiedEmailCredentials creds;
2✔
5430
        bool processed = false;
2✔
5431
        realm_user_t* sync_user = nullptr;
2✔
5432

1✔
5433
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5434
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5435
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
5436

1✔
5437
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5438
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user, nullptr);
2✔
5439

1✔
5440
        realm_app_credentials email_creds(creds);
2✔
5441
        realm_app_link_user(&app, sync_user, &email_creds, realm_app_user2, &processed, nullptr);
2✔
5442

1✔
5443
        CHECK(processed);
2!
5444
        realm_release(sync_user);
2✔
5445
    }
2✔
5446
    SECTION("realm_app_get_all_users integration") {
12✔
5447

1✔
5448
        AutoVerifiedEmailCredentials creds;
2✔
5449
        realm_user_t* sync_user_1 = nullptr;
2✔
5450
        realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5451
        realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5452
                                                                realm_app_void_completion, nullptr, nullptr);
2✔
5453
        realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5454
        realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
2✔
5455
        auto current_user = realm_app_get_current_user(&app);
2✔
5456
        CHECK(realm_equals(sync_user_1, current_user));
2!
5457
        realm_release(current_user);
2✔
5458

1✔
5459
        realm_user_t* sync_user_2;
2✔
5460
        realm_app_switch_user(&app, sync_user_1, &sync_user_2);
2✔
5461
        size_t out_n = 0;
2✔
5462

1✔
5463
        realm_app_get_all_users(&app, nullptr, 0, &out_n);
2✔
5464
        CHECK(out_n == 2);
2!
5465

1✔
5466
        std::vector<realm_user_t*> out_users;
2✔
5467
        out_users.resize(out_n);
2✔
5468
        size_t n = 0;
2✔
5469
        realm_app_get_all_users(&app, out_users.data(), out_n, &n);
2✔
5470
        CHECK(n == out_n);
2!
5471

1✔
5472
        for (size_t i = 0; i < out_n; ++i)
6✔
5473
            realm_release(out_users[i]);
4✔
5474
        realm_release(sync_user_1);
2✔
5475
        realm_release(sync_user_2);
2✔
5476
    }
2✔
5477
    SECTION("realm_app_user_apikey_provider_client_fetch_apikeys") {
12✔
5478
        SECTION("Failure") {
4✔
5479
            AutoVerifiedEmailCredentials creds;
2✔
5480
            realm_user_t* sync_user_1 = nullptr;
2✔
5481
            realm_string_t password{creds.password.c_str(), creds.password.length()};
2✔
5482
            realm_app_email_password_provider_client_register_email(&app, creds.email.c_str(), password,
2✔
5483
                                                                    realm_app_void_completion, nullptr, nullptr);
2✔
5484
            realm_app_credentials anonymous(app::AppCredentials::anonymous());
2✔
5485
            realm_app_log_in_with_credentials(&app, &anonymous, realm_app_user1, &sync_user_1, nullptr);
2✔
5486
            auto callback = [](realm_userdata_t, realm_app_user_apikey_t[], size_t count, realm_app_error_t* error) {
2✔
5487
                CHECK(error);
2!
5488
                CHECK(count == 0);
2!
5489
            };
2✔
5490
            // Should fail with 403
1✔
5491
            auto res =
2✔
5492
                realm_app_user_apikey_provider_client_fetch_apikeys(&app, sync_user_1, callback, nullptr, nullptr);
2✔
5493
            REQUIRE(res == true);
2!
5494
            realm_release(sync_user_1);
2✔
5495
        }
2✔
5496
        SECTION("Success") {
4✔
5497
            TestAppSession session;
2✔
5498
            realm_app app(session.app());
2✔
5499

1✔
5500
            class AppObject {
2✔
5501
            public:
2✔
5502
                static AppObject& create()
2✔
5503
                {
6✔
5504
                    static AppObject obj;
6✔
5505
                    return obj;
6✔
5506
                }
6✔
5507
                const char* key() const
2✔
5508
                {
6✔
5509
                    return api_key_name.c_str();
6✔
5510
                }
6✔
5511

1✔
5512
            private:
2✔
5513
                AppObject()
2✔
5514
                {
2✔
5515
                    api_key_name = util::format("%1", random_string(15));
2✔
5516
                }
2✔
5517
                std::string api_key_name;
2✔
5518
            };
2✔
5519

1✔
5520
            auto create_api_callback = [](realm_userdata_t, realm_app_user_apikey_t* api_key,
2✔
5521
                                          const realm_app_error_t* error) {
2✔
5522
                REQUIRE_FALSE(error);
2!
5523
                REQUIRE(api_key);
2!
5524
                CHECK(strcmp(api_key->name, AppObject::create().key()) == 0);
2!
5525
            };
2✔
5526
            auto current_user = realm_app_get_current_user(&app);
2✔
5527
            auto state = realm_app_user_apikey_provider_client_create_apikey(
2✔
5528
                &app, current_user, AppObject::create().key(), create_api_callback, nullptr, nullptr);
2✔
5529
            REQUIRE(state);
2!
5530

1✔
5531
            auto fetch_apikey_callback = [](realm_userdata_t, realm_app_user_apikey_t* keys, size_t count,
2✔
5532
                                            realm_app_error_t* error) {
2✔
5533
                REQUIRE_FALSE(error);
2!
5534
                REQUIRE(keys);
2!
5535
                REQUIRE(count == 1);
2!
5536
                CHECK(strcmp(keys[0].name, AppObject::create().key()) == 0);
2!
5537
            };
2✔
5538
            state = realm_app_user_apikey_provider_client_fetch_apikeys(&app, current_user, fetch_apikey_callback,
2✔
5539
                                                                        nullptr, nullptr);
2✔
5540
            REQUIRE(state);
2!
5541
            realm_release(current_user);
2✔
5542
        }
2✔
5543
    }
4✔
5544
    realm_release(http_transport);
12✔
5545
}
12✔
5546

5547
TEST_CASE("app: flx-sync compensating writes C API support", "[sync][flx][c_api][baas]") {
2✔
5548
    using namespace realm::app;
2✔
5549
    FLXSyncTestHarness harness("c_api_comp_writes");
2✔
5550
    create_user_and_log_in(harness.app());
2✔
5551
    SyncTestFile test_config(harness.app()->current_user(), harness.schema(), realm::SyncConfig::FLXSyncEnabled{});
2✔
5552
    test_config.sync_config = std::make_shared<realm_sync_config_t>(*test_config.sync_config);
2✔
5553
    realm_sync_config_t* sync_config = static_cast<realm_sync_config_t*>(test_config.sync_config.get());
2✔
5554

1✔
5555
    struct TestState {
2✔
5556
        std::mutex mutex;
2✔
5557
        std::condition_variable cond_var;
2✔
5558
        std::vector<sync::CompensatingWriteErrorInfo> compensating_writes;
2✔
5559
    };
2✔
5560
    auto state = std::make_unique<TestState>();
2✔
5561
    realm_sync_config_set_error_handler(
2✔
5562
        sync_config,
2✔
5563
        [](realm_userdata_t user_data, realm_sync_session_t*, const realm_sync_error_t error) {
2✔
5564
            auto state = reinterpret_cast<TestState*>(user_data);
2✔
5565
            REQUIRE(error.status.error == RLM_ERR_SYNC_COMPENSATING_WRITE);
2!
5566
            REQUIRE(error.compensating_writes_length > 0);
2!
5567

1✔
5568
            std::lock_guard<std::mutex> lk(state->mutex);
2✔
5569
            for (size_t i = 0; i < error.compensating_writes_length; ++i) {
6✔
5570
                sync::CompensatingWriteErrorInfo err_info;
4✔
5571
                err_info.object_name = error.compensating_writes[i].object_name;
4✔
5572
                err_info.reason = error.compensating_writes[i].reason;
4✔
5573
                Mixed pk(c_api::from_capi(error.compensating_writes[i].primary_key));
4✔
5574
                err_info.primary_key = pk;
4✔
5575
                state->compensating_writes.push_back(std::move(err_info));
4✔
5576
            }
4✔
5577

1✔
5578
            state->cond_var.notify_one();
2✔
5579
        },
2✔
5580
        state.get(), [](realm_userdata_t) {});
2✔
5581

1✔
5582
    auto realm = Realm::get_shared_realm(test_config);
2✔
5583

1✔
5584
    auto mut_subs = realm->get_latest_subscription_set().make_mutable_copy();
2✔
5585
    auto table = realm->read_group().get_table("class_TopLevel");
2✔
5586
    mut_subs.insert_or_assign(Query(table).equal(table->get_column_key("queryable_str_field"), "bizz"));
2✔
5587
    mut_subs.commit();
2✔
5588

1✔
5589
    CppContext c(realm);
2✔
5590
    realm->begin_transaction();
2✔
5591
    auto obj_1_id = ObjectId::gen();
2✔
5592
    auto obj_2_id = ObjectId::gen();
2✔
5593
    Object::create(c, realm, "TopLevel",
2✔
5594
                   std::any(AnyDict{
2✔
5595
                       {"_id", obj_1_id},
2✔
5596
                       {"queryable_str_field", std::string{"foo"}},
2✔
5597
                   }));
2✔
5598
    Object::create(c, realm, "TopLevel",
2✔
5599
                   std::any(AnyDict{
2✔
5600
                       {"_id", obj_2_id},
2✔
5601
                       {"queryable_str_field", std::string{"bar"}},
2✔
5602
                   }));
2✔
5603
    realm->commit_transaction();
2✔
5604

1✔
5605
    std::unique_lock<std::mutex> lk(state->mutex);
2✔
5606
    state->cond_var.wait_for(lk, std::chrono::seconds(30), [&] {
4✔
5607
        return state->compensating_writes.size() == 2;
4✔
5608
    });
4✔
5609

1✔
5610
    auto errors = std::move(state->compensating_writes);
2✔
5611
    lk.unlock();
2✔
5612

1✔
5613
    std::sort(errors.begin(), errors.end(), [](const auto& lhs, const auto& rhs) {
3✔
5614
        return lhs.primary_key < rhs.primary_key;
3✔
5615
    });
3✔
5616

1✔
5617
    REQUIRE(errors.size() == 2);
2!
5618
    REQUIRE(errors[0].primary_key == obj_1_id);
2!
5619
    REQUIRE(errors[0].object_name == "TopLevel");
2!
5620
    REQUIRE_THAT(errors[0].reason, Catch::Matchers::ContainsSubstring("object is outside of the current query view"));
2✔
5621
    REQUIRE(errors[1].primary_key == obj_2_id);
2!
5622
    REQUIRE(errors[1].object_name == "TopLevel");
2!
5623
    REQUIRE_THAT(errors[1].reason, Catch::Matchers::ContainsSubstring("object is outside of the current query view"));
2✔
5624
}
2✔
5625

5626
TEST_CASE("app: flx-sync basic tests", "[sync][flx][c_api][baas]") {
2✔
5627
    using namespace realm::app;
2✔
5628

1✔
5629
    auto make_schema = [] {
2✔
5630
        Schema schema{{"Obj",
2✔
5631
                       {{"_id", PropertyType::ObjectId, Property::IsPrimary{true}},
2✔
5632
                        {"name", PropertyType::String | PropertyType::Nullable},
2✔
5633
                        {"value", PropertyType::Int | PropertyType::Nullable}}}};
2✔
5634

1✔
5635
        return FLXSyncTestHarness::ServerSchema{std::move(schema), {"name", "value"}};
2✔
5636
    };
2✔
5637

1✔
5638
    FLXSyncTestHarness harness("c_api_flx_sync_test", make_schema());
2✔
5639
    auto foo_obj_id = ObjectId::gen();
2✔
5640
    auto bar_obj_id = ObjectId::gen();
2✔
5641

1✔
5642
    harness.load_initial_data([&](SharedRealm& realm) {
2✔
5643
        CppContext c(realm);
2✔
5644
        Object::create(
2✔
5645
            c, realm, "Obj",
2✔
5646
            std::any(AnyDict{{"_id", foo_obj_id}, {"name", std::string{"foo"}}, {"value", static_cast<int64_t>(5)}}));
2✔
5647
        Object::create(c, realm, "Obj",
2✔
5648
                       std::any(AnyDict{
2✔
5649
                           {"_id", bar_obj_id}, {"name", std::string{"bar"}}, {"value", static_cast<int64_t>(10)}}));
2✔
5650
    });
2✔
5651

1✔
5652
    harness.do_with_new_realm([&](SharedRealm realm) {
2✔
5653
        realm_t c_wrap_realm(realm);
2✔
5654

1✔
5655
        wait_for_download(*realm);
2✔
5656
        {
2✔
5657
            auto empty_subs = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5658
            CHECK(realm_sync_subscription_set_size(empty_subs) == 0);
2!
5659
            CHECK(realm_sync_subscription_set_version(empty_subs) == 0);
2!
5660
            realm_sync_on_subscription_set_state_change_wait(
2✔
5661
                empty_subs, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
5662
            realm_release(empty_subs);
2✔
5663
        }
2✔
5664
        realm_class_info_t table_info;
2✔
5665
        bool found;
2✔
5666
        CHECK(realm_find_class(&c_wrap_realm, "Obj", &found, &table_info));
2!
5667
        auto c_wrap_query_foo = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'foo'", 0, nullptr);
2✔
5668
        auto c_wrap_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
2✔
5669
        {
2✔
5670
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5671
            CHECK(sub != nullptr);
2!
5672
            auto new_subs = realm_sync_make_subscription_set_mutable(sub);
2✔
5673
            std::size_t index = -1;
2✔
5674
            bool inserted = false;
2✔
5675
            // realm_results_t
1✔
5676
            auto res = realm_sync_subscription_set_insert_or_assign_query(new_subs, c_wrap_query_foo, nullptr, &index,
2✔
5677
                                                                          &inserted);
2✔
5678
            CHECK(inserted == true);
2!
5679
            CHECK(index == 0);
2!
5680
            CHECK(res);
2!
5681
            auto subs = realm_sync_subscription_set_commit(new_subs);
2✔
5682
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
5683
                subs, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
5684
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
5685
            realm_release(sub);
2✔
5686
            realm_release(new_subs);
2✔
5687
            realm_release(subs);
2✔
5688
        }
2✔
5689

1✔
5690
        wait_for_download(*realm);
2✔
5691
        {
2✔
5692
            realm_refresh(&c_wrap_realm, nullptr);
2✔
5693
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
5694
            size_t count = 0;
2✔
5695
            realm_results_count(results, &count);
2✔
5696
            CHECK(count == 1);
2!
5697
            auto object = realm_results_get_object(results, 0);
2✔
5698
            REQUIRE(realm_object_is_valid(object));
2!
5699
            REQUIRE(object->get_column_value<ObjectId>("_id") == foo_obj_id);
2!
5700
            realm_release(object);
2✔
5701
            realm_release(results);
2✔
5702
        }
2✔
5703

1✔
5704
        {
2✔
5705
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5706
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
5707
            std::size_t index = -1;
2✔
5708
            bool inserted = false;
2✔
5709
            realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_bar, nullptr, &index, &inserted);
2✔
5710
            CHECK(inserted);
2!
5711
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
5712
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
5713
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
5714
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
5715
            realm_release(sub);
2✔
5716
            realm_release(mut_sub);
2✔
5717
            realm_release(sub_c);
2✔
5718
        }
2✔
5719

1✔
5720
        {
2✔
5721
            realm_refresh(&c_wrap_realm, nullptr);
2✔
5722
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
5723
            size_t count = 0;
2✔
5724
            realm_results_count(results, &count);
2✔
5725
            CHECK(count == 2);
2!
5726
            realm_release(results);
2✔
5727
        }
2✔
5728

1✔
5729
        {
2✔
5730
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5731
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
5732
            auto s = realm_sync_find_subscription_by_query(sub, c_wrap_query_foo);
2✔
5733
            CHECK(s != nullptr);
2!
5734
            auto cloned_s = realm_clone(s);
2✔
5735
            CHECK(realm_equals(s, cloned_s));
2!
5736
            bool erased = false;
2✔
5737
            auto result = realm_sync_subscription_set_erase_by_query(mut_sub, c_wrap_query_foo, &erased);
2✔
5738
            CHECK(erased);
2!
5739
            CHECK(result);
2!
5740
            auto c_wrap_new_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
2✔
5741
            std::size_t index = -1;
2✔
5742
            bool inserted = false;
2✔
5743
            auto results = realm_query_find_all(c_wrap_new_query_bar);
2✔
5744
            bool updated =
2✔
5745
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, nullptr, &index, &inserted);
2✔
5746
            CHECK(!inserted);
2!
5747
            CHECK(updated);
2!
5748
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
5749
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
5750
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
5751
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
5752
            realm_release(s);
2✔
5753
            realm_release(cloned_s);
2✔
5754
            realm_release(sub);
2✔
5755
            realm_release(mut_sub);
2✔
5756
            realm_release(sub_c);
2✔
5757
            realm_release(results);
2✔
5758
            realm_release(c_wrap_new_query_bar);
2✔
5759
        }
2✔
5760

1✔
5761
        {
2✔
5762
            realm_refresh(&c_wrap_realm, nullptr);
2✔
5763
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
5764
            size_t count = 0;
2✔
5765
            realm_results_count(results, &count);
2✔
5766
            CHECK(count == 1);
2!
5767
            auto object = realm_results_get_object(results, 0);
2✔
5768
            REQUIRE(realm_object_is_valid(object));
2!
5769
            REQUIRE(object->get_column_value<ObjectId>("_id") == bar_obj_id);
2!
5770
            realm_release(object);
2✔
5771
            realm_release(results);
2✔
5772
        }
2✔
5773

1✔
5774
        {
2✔
5775
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5776
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
5777
            auto cleared = realm_sync_subscription_set_clear(mut_sub);
2✔
5778
            CHECK(cleared);
2!
5779
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
5780
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
5781
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
5782
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
5783
            realm_release(sub);
2✔
5784
            realm_release(mut_sub);
2✔
5785
            realm_release(sub_c);
2✔
5786
        }
2✔
5787

1✔
5788
        {
2✔
5789
            realm_refresh(&c_wrap_realm, nullptr);
2✔
5790
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
5791
            size_t count = std::numeric_limits<std::size_t>::max();
2✔
5792
            realm_results_count(results, &count);
2✔
5793
            CHECK(count == 0);
2!
5794
            realm_release(results);
2✔
5795
        }
2✔
5796

1✔
5797
        {
2✔
5798
            auto c_wrap_new_query_bar = realm_query_parse(&c_wrap_realm, table_info.key, "name = 'bar'", 0, nullptr);
2✔
5799
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5800
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
5801
            std::size_t index = -1;
2✔
5802
            bool inserted = false;
2✔
5803
            auto results = realm_query_find_all(c_wrap_new_query_bar);
2✔
5804
            bool success =
2✔
5805
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "bar", &index, &inserted);
2✔
5806
            CHECK(inserted);
2!
5807
            CHECK(success);
2!
5808
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
5809
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
5810
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
5811
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
5812
            realm_release(sub);
2✔
5813
            realm_release(mut_sub);
2✔
5814
            realm_release(sub_c);
2✔
5815
            realm_release(results);
2✔
5816
            realm_release(c_wrap_new_query_bar);
2✔
5817
        }
2✔
5818

1✔
5819
        {
2✔
5820
            realm->refresh();
2✔
5821
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
5822
            size_t count = std::numeric_limits<std::size_t>::max();
2✔
5823
            realm_results_count(results, &count);
2✔
5824
            CHECK(count == 1);
2!
5825
            realm_release(results);
2✔
5826
        }
2✔
5827

1✔
5828
        {
2✔
5829
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5830
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
5831
            bool erased = false;
2✔
5832
            realm_sync_subscription_set_erase_by_name(mut_sub, "bar", &erased);
2✔
5833
            CHECK(erased);
2!
5834
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
5835
            auto state = realm_sync_on_subscription_set_state_change_wait(
2✔
5836
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
5837
            CHECK(state == realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2!
5838
            realm_release(sub);
2✔
5839
            realm_release(mut_sub);
2✔
5840
            realm_release(sub_c);
2✔
5841
        }
2✔
5842

1✔
5843
        {
2✔
5844
            realm_refresh(&c_wrap_realm, nullptr);
2✔
5845
            auto results = realm_object_find_all(&c_wrap_realm, table_info.key);
2✔
5846
            size_t count = std::numeric_limits<std::size_t>::max();
2✔
5847
            realm_results_count(results, &count);
2✔
5848
            CHECK(count == 0);
2!
5849
            realm_release(results);
2✔
5850
        }
2✔
5851

1✔
5852
        {
2✔
5853
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5854
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
5855
            std::size_t index = -1;
2✔
5856
            bool inserted = false;
2✔
5857
            auto results = realm_query_find_all(c_wrap_query_bar);
2✔
5858
            bool success =
2✔
5859
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, nullptr, &index, &inserted);
2✔
5860
            CHECK(inserted);
2!
5861
            CHECK(success);
2!
5862
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
5863
            // lambdas with state cannot be easily converted to function pointers, add a simple singleton that syncs
1✔
5864
            // the state among threads
1✔
5865
            struct SyncObject {
2✔
5866
                std::mutex m_mutex;
2✔
5867
                std::condition_variable m_cv;
2✔
5868
                realm_flx_sync_subscription_set_state_e m_state{RLM_SYNC_SUBSCRIPTION_UNCOMMITTED};
2✔
5869
                void* m_userdata;
2✔
5870

1✔
5871
                static SyncObject& create()
2✔
5872
                {
4✔
5873
                    static SyncObject sync_object;
4✔
5874
                    return sync_object;
4✔
5875
                }
4✔
5876

1✔
5877
                void set_state_and_notify(void* userdata, realm_flx_sync_subscription_set_state_e state)
2✔
5878
                {
2✔
5879
                    {
2✔
5880
                        std::lock_guard<std::mutex> guard{m_mutex};
2✔
5881
                        m_state = state;
2✔
5882
                        m_userdata = userdata;
2✔
5883
                    }
2✔
5884
                    m_cv.notify_one();
2✔
5885
                }
2✔
5886

1✔
5887
                realm_flx_sync_subscription_set_state_e wait_state()
2✔
5888
                {
2✔
5889
                    using namespace std::chrono_literals;
2✔
5890
                    std::unique_lock<std::mutex> lock{m_mutex};
2✔
5891
                    bool completed_within_time_limit = m_cv.wait_for(lock, 5s, [this]() {
4✔
5892
                        return m_state == RLM_SYNC_SUBSCRIPTION_COMPLETE && m_userdata != nullptr;
4✔
5893
                    });
4✔
5894
                    CHECK(completed_within_time_limit);
2!
5895
                    return m_state;
2✔
5896
                }
2✔
5897
            };
2✔
5898

1✔
5899
            auto callback = [](void* userdata, realm_flx_sync_subscription_set_state_e sub_state) {
2✔
5900
                SyncObject::create().set_state_and_notify(userdata, sub_state);
2✔
5901
            };
2✔
5902
            int userdata = 0;
2✔
5903
            realm_sync_on_subscription_set_state_change_async(
2✔
5904
                sub_c, realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE, callback, &userdata,
2✔
5905
                nullptr);
2✔
5906
            CHECK(SyncObject::create().wait_state() ==
2!
5907
                  realm_flx_sync_subscription_set_state_e::RLM_SYNC_SUBSCRIPTION_COMPLETE);
2✔
5908

1✔
5909
            realm_release(sub);
2✔
5910
            realm_release(mut_sub);
2✔
5911
            realm_release(sub_c);
2✔
5912
            realm_release(results);
2✔
5913
        }
2✔
5914

1✔
5915
        {
2✔
5916
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5917
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
5918
            std::size_t index = -1;
2✔
5919
            bool inserted = false;
2✔
5920
            auto results = realm_query_find_all(c_wrap_query_bar);
2✔
5921
            bool success =
2✔
5922
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "test_bar", &index, &inserted);
2✔
5923
            CHECK(inserted);
2!
5924
            CHECK(success);
2!
5925
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
5926
            auto sub_by_res = realm_sync_find_subscription_by_results(sub_c, results);
2✔
5927
            CHECK(sub_by_res);
2!
5928
            const auto realm_flx_sync_sub = realm_sync_find_subscription_by_name(sub_c, "test_bar");
2✔
5929
            REQUIRE(realm_flx_sync_sub);
2!
5930

1✔
5931
            const realm_object_id_t& object_id = realm_sync_subscription_id(realm_flx_sync_sub);
2✔
5932
            CHECK(!realm::c_api::from_capi(object_id).to_string().empty());
2!
5933

1✔
5934
            realm_timestamp_t create_time = realm_sync_subscription_created_at(realm_flx_sync_sub);
2✔
5935
            realm_timestamp_t update_time = realm_sync_subscription_updated_at(realm_flx_sync_sub);
2✔
5936
            CHECK(create_time.seconds == update_time.seconds);
2!
5937
            CHECK(create_time.nanoseconds == update_time.nanoseconds);
2!
5938
            CHECK(create_time.seconds != 0);
2!
5939
            CHECK(create_time.seconds != 0);
2!
5940

1✔
5941
            const realm_string_t& name = realm_sync_subscription_name(realm_flx_sync_sub);
2✔
5942
            REQUIRE(name.size != 0);
2!
5943
            CHECK(std::string{name.data, name.size} == "test_bar");
2!
5944

1✔
5945
            auto mut_sub_1 = realm_sync_make_subscription_set_mutable(sub_c);
2✔
5946
            bool erased = false;
2✔
5947
            CHECK(realm_sync_subscription_set_erase_by_id(mut_sub_1, &object_id, &erased));
2!
5948
            CHECK(erased);
2!
5949
            auto sub_c_1 = realm_sync_subscription_set_commit(mut_sub_1);
2✔
5950

1✔
5951
            const auto sub_not_found = realm_sync_find_subscription_by_name(sub_c_1, "test_bar");
2✔
5952
            CHECK(!sub_not_found);
2!
5953

1✔
5954
            realm_release(sub);
2✔
5955
            realm_release(mut_sub);
2✔
5956
            realm_release(results);
2✔
5957
            realm_release(sub_c);
2✔
5958
            realm_release(sub_by_res);
2✔
5959
            realm_release(realm_flx_sync_sub);
2✔
5960
            realm_release(mut_sub_1);
2✔
5961
            realm_release(sub_c_1);
2✔
5962
            realm_release(sub_not_found);
2✔
5963
        }
2✔
5964

1✔
5965
        {
2✔
5966
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5967
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
5968
            std::size_t index = -1;
2✔
5969
            bool inserted = false;
2✔
5970
            auto results = realm_query_find_all(c_wrap_query_bar);
2✔
5971
            bool success =
2✔
5972
                realm_sync_subscription_set_insert_or_assign_results(mut_sub, results, "test_bar", &index, &inserted);
2✔
5973
            CHECK(inserted);
2!
5974
            CHECK(success);
2!
5975
            auto sub_c = realm_sync_subscription_set_commit(mut_sub);
2✔
5976
            auto sub_by_res = realm_sync_find_subscription_by_results(sub_c, results);
2✔
5977
            CHECK(sub_by_res);
2!
5978

1✔
5979
            auto mut_sub_1 = realm_sync_make_subscription_set_mutable(sub_c);
2✔
5980
            bool erased = false;
2✔
5981
            realm_sync_subscription_set_erase_by_results(mut_sub_1, results, &erased);
2✔
5982
            CHECK(erased);
2!
5983
            auto sub_c_1 = realm_sync_subscription_set_commit(mut_sub_1);
2✔
5984

1✔
5985
            realm_release(sub);
2✔
5986
            realm_release(mut_sub);
2✔
5987
            realm_release(results);
2✔
5988
            realm_release(sub_c);
2✔
5989
            realm_release(sub_by_res);
2✔
5990
            realm_release(mut_sub_1);
2✔
5991
            realm_release(sub_c_1);
2✔
5992
        }
2✔
5993

1✔
5994
        {
2✔
5995
            auto sub = realm_sync_get_latest_subscription_set(&c_wrap_realm);
2✔
5996
            auto mut_sub = realm_sync_make_subscription_set_mutable(sub);
2✔
5997
            std::size_t index = -1;
2✔
5998
            bool inserted = false;
2✔
5999
            CHECK(realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_bar, nullptr, &index,
2!
6000
                                                                     &inserted));
2✔
6001
            CHECK(inserted);
2!
6002
            CHECK(realm_sync_subscription_set_insert_or_assign_query(mut_sub, c_wrap_query_foo, nullptr, &index,
2!
6003
                                                                     &inserted));
2✔
6004
            CHECK(inserted);
2!
6005
            bool erased = false;
2✔
6006
            CHECK(realm_sync_subscription_set_erase_by_class_name(mut_sub, "Obj", &erased));
2!
6007
            CHECK(erased);
2!
6008
            // Nothing to remove when trying again.
1✔
6009
            CHECK(realm_sync_subscription_set_erase_by_class_name(mut_sub, "Obj", &erased));
2!
6010
            CHECK_FALSE(erased);
2!
6011
            realm_release(sub);
2✔
6012
            realm_release(mut_sub);
2✔
6013
        }
2✔
6014

1✔
6015
        realm_release(c_wrap_query_foo);
2✔
6016
        realm_release(c_wrap_query_bar);
2✔
6017
    });
2✔
6018
}
2✔
6019

6020
TEST_CASE("C API app: websocket provider", "[sync][app][c_api][baas]") {
2✔
6021
    using namespace realm::app;
2✔
6022
    using namespace realm::sync;
2✔
6023
    using namespace realm::sync::websocket;
2✔
6024

1✔
6025
    struct TestWebSocketObserverShim : sync::WebSocketObserver {
2✔
6026
    public:
2✔
6027
        explicit TestWebSocketObserverShim(realm_websocket_observer_t* observer)
2✔
6028
            : m_observer(observer)
2✔
6029
        {
2✔
6030
        }
2✔
6031

1✔
6032
        void websocket_connected_handler(const std::string& protocol) override
2✔
6033
        {
2✔
6034
            REALM_ASSERT(m_observer);
2✔
6035
            realm_sync_socket_websocket_connected(m_observer, protocol.c_str());
2✔
6036
        }
2✔
6037

1✔
6038
        void websocket_error_handler() override
2✔
6039
        {
1✔
NEW
6040
            REALM_ASSERT(m_observer);
×
NEW
6041
            realm_sync_socket_websocket_error(m_observer);
×
UNCOV
6042
        }
×
6043

1✔
6044
        bool websocket_binary_message_received(util::Span<const char> data) override
2✔
6045
        {
9✔
6046
            REALM_ASSERT(m_observer);
9✔
6047
            return realm_sync_socket_websocket_message(m_observer, data.data(), data.size());
9✔
6048
        }
9✔
6049

1✔
6050
        bool websocket_closed_handler(bool was_clean, WebSocketError error, std::string_view msg) override
2✔
6051
        {
1✔
NEW
6052
            REALM_ASSERT(m_observer);
×
NEW
6053
            return realm_sync_socket_websocket_closed(m_observer, was_clean,
×
NEW
6054
                                                      static_cast<realm_web_socket_errno_e>(error), msg.data());
×
UNCOV
6055
        }
×
6056

1✔
6057
    private:
2✔
6058
        realm_websocket_observer_t* m_observer;
2✔
6059
    };
2✔
6060

1✔
6061
    struct TestWebSocket : realm::c_api::WrapC, WebSocketInterface {
2✔
6062
    public:
2✔
6063
        TestWebSocket(DefaultSocketProvider& socket_provider, realm_websocket_endpoint_t endpoint,
2✔
6064
                      realm_websocket_observer_t* realm_websocket_observer)
2✔
6065
        {
2✔
6066
            WebSocketEndpoint ws_endpoint;
2✔
6067
            ws_endpoint.address = endpoint.address;
2✔
6068
            ws_endpoint.port = endpoint.port;
2✔
6069
            ws_endpoint.path = endpoint.path;
2✔
6070
            for (size_t i = 0; i < endpoint.num_protocols; ++i) {
20✔
6071
                ws_endpoint.protocols.push_back(endpoint.protocols[i]);
18✔
6072
            }
18✔
6073
            ws_endpoint.is_ssl = endpoint.is_ssl;
2✔
6074

1✔
6075
            auto observer = std::make_unique<TestWebSocketObserverShim>(realm_websocket_observer);
2✔
6076
            m_websocket = socket_provider.connect(std::move(observer), std::move(ws_endpoint));
2✔
6077
        }
2✔
6078

1✔
6079
        void async_write_binary(util::Span<const char> data, SyncSocketProvider::FunctionHandler&& handler) override
2✔
6080
        {
16✔
6081
            m_websocket->async_write_binary(data, std::move(handler));
16✔
6082
        }
16✔
6083

1✔
6084
    private:
2✔
6085
        std::unique_ptr<WebSocketInterface> m_websocket;
2✔
6086
    };
2✔
6087

1✔
6088
    struct TestSyncTimer : realm::c_api::WrapC, SyncSocketProvider::Timer {
2✔
6089
    public:
2✔
6090
        TestSyncTimer(DefaultSocketProvider& socket_provider, std::chrono::milliseconds delay,
2✔
6091
                      realm_sync_socket_timer_callback_t* callback)
2✔
6092
        {
14✔
6093
            m_timer = socket_provider.create_timer(delay, [capi_callback = callback](Status s) {
14✔
6094
                if (s.code() == ErrorCodes::Error::OperationAborted) {
14✔
6095
                    return realm_sync_socket_timer_canceled(capi_callback);
9✔
6096
                }
9✔
6097
                realm_sync_socket_timer_complete(
5✔
6098
                    capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()), s.reason().c_str());
5✔
6099
            });
5✔
6100
        }
14✔
6101

1✔
6102
        void cancel() override
2✔
6103
        {
14✔
6104
            m_timer->cancel();
14✔
6105
        }
14✔
6106

1✔
6107
    private:
2✔
6108
        SyncSocketProvider::SyncTimer m_timer;
2✔
6109
    };
2✔
6110

1✔
6111
    struct TestData {
2✔
6112
        DefaultSocketProvider* socket_provider;
2✔
6113
        int free_count = 0;
2✔
6114
    };
2✔
6115

1✔
6116
    DefaultSocketProvider default_socket_provider(util::Logger::get_default_logger(), "SocketProvider");
2✔
6117

1✔
6118
    static std::mutex catch_mutex;
2✔
6119
#define LOCKED_REQUIRE(...)                                                                                          \
2✔
6120
    do {                                                                                                             \
228✔
6121
        std::lock_guard lock(catch_mutex);                                                                           \
228✔
6122
        REQUIRE(__VA_ARGS__);                                                                                        \
228✔
6123
    } while (0)
228✔
6124

1✔
6125
    auto userdata_free_fn = [](realm_userdata_t user_ptr) {
2✔
6126
        auto test_data = static_cast<TestData*>(user_ptr);
2✔
6127
        LOCKED_REQUIRE(test_data);
2!
6128
        test_data->free_count++;
2✔
6129
    };
2✔
6130
    auto post_fn = [](realm_userdata_t userdata, realm_sync_socket_post_callback_t* callback) {
34✔
6131
        auto test_data = static_cast<TestData*>(userdata);
34✔
6132
        LOCKED_REQUIRE(test_data);
34!
6133
        LOCKED_REQUIRE(test_data->socket_provider);
34!
6134
        test_data->socket_provider->post([capi_callback = callback](Status s) {
34✔
6135
            realm_sync_socket_post_complete(capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()),
34✔
6136
                                            s.reason().c_str());
34✔
6137
        });
34✔
6138
    };
34✔
6139
    auto create_timer_fn = [](realm_userdata_t userdata, uint64_t delay_ms,
2✔
6140
                              realm_sync_socket_timer_callback_t* callback) -> realm_sync_socket_timer_t {
14✔
6141
        auto test_data = static_cast<TestData*>(userdata);
14✔
6142
        LOCKED_REQUIRE(test_data);
14!
6143
        LOCKED_REQUIRE(test_data->socket_provider);
14!
6144
        return static_cast<realm_sync_socket_timer_t>(
14✔
6145
            new TestSyncTimer(*test_data->socket_provider, std::chrono::milliseconds(delay_ms), callback));
14✔
6146
    };
14✔
6147
    auto cancel_timer_fn = [](realm_userdata_t userdata, realm_sync_socket_timer_t sync_timer) {
14✔
6148
        auto test_data = static_cast<TestData*>(userdata);
14✔
6149
        LOCKED_REQUIRE(test_data);
14!
6150
        LOCKED_REQUIRE(test_data->socket_provider);
14!
6151
        auto timer = static_cast<TestSyncTimer*>(sync_timer);
14✔
6152
        LOCKED_REQUIRE(timer);
14!
6153
        timer->cancel();
14✔
6154
    };
14✔
6155
    auto free_timer_fn = [](realm_userdata_t userdata, realm_sync_socket_timer_t sync_timer) {
14✔
6156
        auto test_data = static_cast<TestData*>(userdata);
14✔
6157
        LOCKED_REQUIRE(test_data);
14!
6158
        LOCKED_REQUIRE(test_data->socket_provider);
14!
6159
        realm_release(sync_timer);
14✔
6160
    };
14✔
6161
    auto websocket_connect_fn =
2✔
6162
        [](realm_userdata_t userdata, realm_websocket_endpoint_t endpoint,
2✔
6163
           realm_websocket_observer_t* realm_websocket_observer) -> realm_sync_socket_websocket_t {
2✔
6164
        auto test_data = static_cast<TestData*>(userdata);
2✔
6165
        LOCKED_REQUIRE(test_data);
2!
6166
        LOCKED_REQUIRE(test_data->socket_provider);
2!
6167
        return static_cast<realm_sync_socket_websocket_t>(
2✔
6168
            new TestWebSocket(*test_data->socket_provider, endpoint, realm_websocket_observer));
2✔
6169
    };
2✔
6170
    auto websocket_async_write_fn = [](realm_userdata_t userdata, realm_sync_socket_websocket_t sync_websocket,
2✔
6171
                                       const char* data, size_t size, realm_sync_socket_write_callback_t* callback) {
16✔
6172
        auto test_data = static_cast<TestData*>(userdata);
16✔
6173
        LOCKED_REQUIRE(test_data);
16!
6174
        LOCKED_REQUIRE(test_data->socket_provider);
16!
6175
        auto websocket = static_cast<TestWebSocket*>(sync_websocket);
16✔
6176
        LOCKED_REQUIRE(websocket);
16!
6177
        websocket->async_write_binary(util::Span{data, size}, [capi_callback = callback](Status s) {
16✔
6178
            realm_sync_socket_write_complete(
16✔
6179
                capi_callback, static_cast<realm_sync_socket_callback_result_e>(s.code()), s.reason().c_str());
16✔
6180
        });
16✔
6181
    };
16✔
6182
    auto websocket_free_fn = [](realm_userdata_t userdata, realm_sync_socket_websocket_t sync_websocket) {
2✔
6183
        auto test_data = static_cast<TestData*>(userdata);
2✔
6184
        LOCKED_REQUIRE(test_data);
2!
6185
        LOCKED_REQUIRE(test_data->socket_provider);
2!
6186
        realm_release(sync_websocket);
2✔
6187
    };
2✔
6188

1✔
6189
    // Test drive.
1✔
6190
    TestData test_data{&default_socket_provider};
2✔
6191
    auto socket_provider = realm_sync_socket_new(static_cast<realm_userdata_t>(&test_data), userdata_free_fn, post_fn,
2✔
6192
                                                 create_timer_fn, cancel_timer_fn, free_timer_fn,
2✔
6193
                                                 websocket_connect_fn, websocket_async_write_fn, websocket_free_fn);
2✔
6194
    {
2✔
6195
        FLXSyncTestHarness harness("c_api_websocket_provider", FLXSyncTestHarness::default_server_schema(),
2✔
6196
                                   instance_of<SynchronousTestTransport>, *socket_provider);
2✔
6197

1✔
6198
        SyncTestFile test_config(harness.app()->current_user(), harness.schema(),
2✔
6199
                                 realm::SyncConfig::FLXSyncEnabled{});
2✔
6200
        auto realm = Realm::get_shared_realm(test_config);
2✔
6201
        bool wait_success = wait_for_download(*realm);
2✔
6202
        LOCKED_REQUIRE(!wait_success);
2!
6203
    }
2✔
6204
    realm_release(socket_provider);
2✔
6205
    default_socket_provider.stop(true);
2✔
6206
    LOCKED_REQUIRE(test_data.free_count == 1);
2!
6207
}
2✔
6208
#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