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

realm / realm-core / github_pull_request_278228

04 Oct 2023 10:15PM UTC coverage: 91.582% (+0.007%) from 91.575%
github_pull_request_278228

Pull #7029

Evergreen

tgoyne
Use UNITTEST_LOG_LEVEL in objectstore tests

For historical reasons core and sync tests use the UNITTEST_LOG_LEVEL
environment variable to determine the test log level, while object store tests
used a build time setting. This brings them into alignment on using the env
variable, and applies it via setting the default log level on startup in a
single place.
Pull Request #7029: Use UNITTEST_LOG_LEVEL in objectstore tests

94218 of 173442 branches covered (0.0%)

46 of 54 new or added lines in 5 files covered. (85.19%)

51 existing lines in 12 files now uncovered.

230351 of 251523 relevant lines covered (91.58%)

6704577.96 hits per line

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

90.07
/test/object-store/util/test_file.cpp
1
////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2016 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

21
#include "util/test_utils.hpp"
22
#include "util/sync/baas_admin_api.hpp"
23
#include "../util/crypt_key.hpp"
24
#include "../util/test_path.hpp"
25

26
#include <realm/db.hpp>
27
#include <realm/disable_sync_to_disk.hpp>
28
#include <realm/history.hpp>
29
#include <realm/string_data.hpp>
30
#include <realm/object-store/impl/realm_coordinator.hpp>
31
#include <realm/util/base64.hpp>
32
#include <realm/util/file.hpp>
33

34
#if REALM_ENABLE_SYNC
35
#include <realm/object-store/sync/mongo_client.hpp>
36
#include <realm/object-store/sync/mongo_database.hpp>
37
#include <realm/object-store/sync/mongo_collection.hpp>
38
#include <realm/object-store/sync/sync_manager.hpp>
39
#include <realm/object-store/sync/sync_session.hpp>
40
#include <realm/object-store/sync/sync_user.hpp>
41
#include <realm/object-store/schema.hpp>
42
#endif
43

44
#include <cstdlib>
45
#include <iostream>
46

47
#ifdef _WIN32
48
#include <io.h>
49
#include <fcntl.h>
50

51
inline static int mkstemp(char* _template)
52
{
53
    return _open(_mktemp(_template), _O_CREAT | _O_TEMPORARY, _S_IREAD | _S_IWRITE);
54
}
55
#else
56
#include <unistd.h>
57
#endif
58

59
#if REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
60
#include <condition_variable>
61
#include <functional>
62
#include <thread>
63
#include <map>
64
#endif
65

66
using namespace realm;
67

68
TestFile::TestFile()
69
{
8,878✔
70
    disable_sync_to_disk();
8,878✔
71
    m_temp_dir = util::make_temp_dir();
8,878✔
72
    path = (fs::path(m_temp_dir) / "realm.XXXXXX").string();
8,878✔
73
    if (const char* crypt_key = test_util::crypt_key()) {
8,878✔
UNCOV
74
        encryption_key = std::vector<char>(crypt_key, crypt_key + 64);
×
75
    }
×
76
    int fd = mkstemp(path.data());
8,878✔
77
    if (fd < 0) {
8,878✔
78
        int err = errno;
×
79
        throw std::system_error(err, std::system_category());
×
80
    }
×
81
#ifdef _WIN32
82
    _close(fd);
83
    _unlink(path.c_str());
84
#else // POSIX
85
    close(fd);
8,878✔
86
    unlink(path.c_str());
8,878✔
87
#endif
8,878✔
88

4,400✔
89
    schema_version = 0;
8,878✔
90
}
8,878✔
91

92
TestFile::~TestFile()
93
{
8,878✔
94
    if (!m_persist) {
8,878✔
95
        try {
8,794✔
96
            util::Logger::get_default_logger()->detail("~TestFile() removing '%1' and '%2'", path, m_temp_dir);
8,794✔
97
            util::File::try_remove(path);
8,794✔
98
            util::try_remove_dir_recursive(m_temp_dir);
8,794✔
99
        }
8,794✔
100
        catch (const std::exception& e) {
4,360✔
101
            util::Logger::get_default_logger()->warn("~TestFile() cleanup failed for '%1': %2", path, e.what());
4✔
102
            // clean up is best effort, ignored.
2✔
103
        }
4✔
104
    }
8,794✔
105
}
8,878✔
106

107
DBOptions TestFile::options() const
108
{
26✔
109
    DBOptions options;
26✔
110
    options.durability = in_memory ? DBOptions::Durability::MemOnly : DBOptions::Durability::Full;
26✔
111
    return options;
26✔
112
}
26✔
113

114
InMemoryTestFile::InMemoryTestFile()
115
{
8,536✔
116
    in_memory = true;
8,536✔
117
    schema_version = 0;
8,536✔
118
    encryption_key = std::vector<char>();
8,536✔
119
}
8,536✔
120

121
DBOptions InMemoryTestFile::options() const
122
{
×
123
    DBOptions options;
×
124
    options.durability = DBOptions::Durability::MemOnly;
×
125
    return options;
×
126
}
×
127

128
#if REALM_ENABLE_SYNC
129

130
static const std::string fake_refresh_token = ENCODE_FAKE_JWT("not_a_real_token");
131
static const std::string fake_access_token = ENCODE_FAKE_JWT("also_not_real");
132
static const std::string fake_device_id = "123400000000000000000000";
133

134
static std::shared_ptr<SyncUser> get_fake_user(app::App& app, const std::string& user_name)
135
{
6,666✔
136
    return app.sync_manager()->get_user(user_name, fake_refresh_token, fake_access_token, fake_device_id);
6,666✔
137
}
6,666✔
138

139
SyncTestFile::SyncTestFile(std::shared_ptr<app::App> app, std::string name, std::string user_name)
140
    : SyncTestFile(get_fake_user(*app, user_name), bson::Bson(name))
141
{
6,662✔
142
}
6,662✔
143

144
SyncTestFile::SyncTestFile(std::shared_ptr<SyncUser> user, bson::Bson partition, util::Optional<Schema> schema)
145
{
7,112✔
146
    REALM_ASSERT(user);
7,112✔
147
    sync_config = std::make_shared<realm::SyncConfig>(user, partition);
7,112✔
148
    sync_config->stop_policy = SyncSessionStopPolicy::Immediately;
7,112✔
149
    sync_config->error_handler = [](std::shared_ptr<SyncSession>, SyncError error) {
3,520✔
150
        util::format(std::cerr, "An unexpected sync error was caught by the default SyncTestFile handler: '%1'\n",
×
151
                     error.status);
×
152
        abort();
×
153
    };
×
154
    schema_version = 1;
7,112✔
155
    this->schema = std::move(schema);
7,112✔
156
    schema_mode = SchemaMode::AdditiveExplicit;
7,112✔
157
}
7,112✔
158

159
SyncTestFile::SyncTestFile(std::shared_ptr<SyncUser> user, bson::Bson partition,
160
                           realm::util::Optional<realm::Schema> schema,
161
                           std::function<SyncSessionErrorHandler>&& error_handler)
162
{
2✔
163
    REALM_ASSERT(user);
2✔
164
    sync_config = std::make_shared<realm::SyncConfig>(user, partition);
2✔
165
    sync_config->stop_policy = SyncSessionStopPolicy::Immediately;
2✔
166
    sync_config->error_handler = std::move(error_handler);
2✔
167
    schema_version = 1;
2✔
168
    this->schema = std::move(schema);
2✔
169
    schema_mode = SchemaMode::AdditiveExplicit;
2✔
170
}
2✔
171

172
SyncTestFile::SyncTestFile(std::shared_ptr<realm::SyncUser> user, realm::Schema _schema, SyncConfig::FLXSyncEnabled)
173
{
295✔
174
    REALM_ASSERT(user);
295✔
175
    sync_config = std::make_shared<realm::SyncConfig>(user, SyncConfig::FLXSyncEnabled{});
295✔
176
    sync_config->stop_policy = SyncSessionStopPolicy::Immediately;
295✔
177
    sync_config->error_handler = [](std::shared_ptr<SyncSession> session, SyncError error) {
147✔
178
        util::format(std::cerr,
×
179
                     "An unexpected sync error was caught by the default SyncTestFile handler: '%1' for '%2'",
×
180
                     error.status, session->path());
×
181
        abort();
×
182
    };
×
183
    schema_version = 1;
295✔
184
    schema = _schema;
295✔
185
    schema_mode = SchemaMode::AdditiveExplicit;
295✔
186
}
295✔
187

188
SyncTestFile::SyncTestFile(std::shared_ptr<app::App> app, bson::Bson partition, Schema schema)
189
    : SyncTestFile(app->current_user(), std::move(partition), std::move(schema))
190
{
126✔
191
}
126✔
192

193
// MARK: - SyncServer
194
SyncServer::SyncServer(const SyncServer::Config& config)
195
    : m_local_root_dir(config.local_dir.empty() ? util::make_temp_dir() : config.local_dir)
196
    , m_server(m_local_root_dir, util::none, ([&] {
3,630✔
197
                   using namespace std::literals::chrono_literals;
3,630✔
198

1,788✔
199
                   sync::Server::Config c;
3,630✔
200
                   c.logger = util::Logger::get_default_logger();
3,630✔
201
                   c.token_expiration_clock = this;
3,630✔
202
                   c.listen_address = "127.0.0.1";
3,630✔
203
                   c.disable_sync_to_disk = true;
3,630✔
204
                   c.ssl = config.ssl;
3,630✔
205
                   if (c.ssl) {
3,630✔
206
                       c.ssl_certificate_path = test_util::get_test_resource_path() + "test_util_network_ssl_ca.pem";
2✔
207
                       c.ssl_certificate_key_path =
2✔
208
                           test_util::get_test_resource_path() + "test_util_network_ssl_key.pem";
2✔
209
                   }
2✔
210

1,788✔
211
                   return c;
3,630✔
212
               })())
3,630✔
213
{
3,630✔
214
    m_server.start();
3,630✔
215
    m_url = util::format("%1://127.0.0.1:%2", config.ssl ? "wss" : "ws", m_server.listen_endpoint().port());
3,629✔
216
    if (config.start_immediately)
3,630✔
217
        start();
3,573✔
218
}
3,630✔
219

220
SyncServer::~SyncServer()
221
{
3,630✔
222
    stop();
3,630✔
223
}
3,630✔
224

225
void SyncServer::start()
226
{
3,606✔
227
    REALM_ASSERT(!m_thread.joinable());
3,606✔
228
    m_thread = std::thread([this] {
3,606✔
229
        m_server.run();
3,606✔
230
    });
3,606✔
231
}
3,606✔
232

233
void SyncServer::stop()
234
{
3,680✔
235
    m_server.stop();
3,680✔
236
    if (m_thread.joinable())
3,680✔
237
        m_thread.join();
3,606✔
238
}
3,680✔
239

240
std::string SyncServer::url_for_realm(StringData realm_name) const
241
{
×
242
    return util::format("%1/%2", m_url, realm_name);
×
243
}
×
244

245
struct WaitForSessionState {
246
    std::condition_variable cv;
247
    std::mutex mutex;
248
    bool complete = false;
249
    Status status = Status::OK();
250
};
251

252
static Status wait_for_session(Realm& realm, void (SyncSession::*fn)(util::UniqueFunction<void(Status)>&&),
253
                               std::chrono::seconds timeout)
254
{
1,498✔
255
    auto shared_state = std::make_shared<WaitForSessionState>();
1,498✔
256
    auto& session = *realm.config().sync_config->user->session_for_on_disk_path(realm.config().path);
1,498✔
257
    (session.*fn)([weak_state = std::weak_ptr<WaitForSessionState>(shared_state)](Status s) {
1,498✔
258
        auto shared_state = weak_state.lock();
1,498✔
259
        if (!shared_state) {
1,498✔
260
            return;
×
261
        }
×
262
        std::lock_guard<std::mutex> lock(shared_state->mutex);
1,498✔
263
        shared_state->complete = true;
1,498✔
264
        shared_state->status = s;
1,498✔
265
        shared_state->cv.notify_one();
1,498✔
266
    });
1,498✔
267
    std::unique_lock<std::mutex> lock(shared_state->mutex);
1,498✔
268
    bool completed = shared_state->cv.wait_for(lock, timeout, [&]() {
2,996✔
269
        return shared_state->complete == true;
2,996✔
270
    });
2,996✔
271
    if (!completed) {
1,498✔
272
        throw std::runtime_error("wait_for_session() timed out");
×
273
    }
×
274
    return shared_state->status;
1,498✔
275
}
1,498✔
276

277
bool wait_for_upload(Realm& realm, std::chrono::seconds timeout)
278
{
728✔
279
    return !wait_for_session(realm, &SyncSession::wait_for_upload_completion, timeout).is_ok();
728✔
280
}
728✔
281

282
bool wait_for_download(Realm& realm, std::chrono::seconds timeout)
283
{
770✔
284
    return !wait_for_session(realm, &SyncSession::wait_for_download_completion, timeout).is_ok();
770✔
285
}
770✔
286

287
void set_app_config_defaults(app::App::Config& app_config,
288
                             const std::shared_ptr<app::GenericNetworkTransport>& transport)
289
{
4,153✔
290
    if (!app_config.transport)
4,153✔
291
        app_config.transport = transport;
3,632✔
292
    if (app_config.device_info.platform_version.empty())
4,153✔
293
        app_config.device_info.platform_version = "Object Store Test Platform Version";
3,632✔
294
    if (app_config.device_info.sdk_version.empty())
4,153✔
295
        app_config.device_info.sdk_version = "SDK Version";
3,632✔
296
    if (app_config.device_info.sdk.empty())
4,153✔
297
        app_config.device_info.sdk = "SDK Name";
3,632✔
298
    if (app_config.device_info.device_name.empty())
4,153✔
299
        app_config.device_info.device_name = "Device Name";
3,632✔
300
    if (app_config.device_info.device_version.empty())
4,153✔
301
        app_config.device_info.device_version = "Device Version";
3,632✔
302
    if (app_config.device_info.framework_name.empty())
4,153✔
303
        app_config.device_info.framework_name = "Framework Name";
3,632✔
304
    if (app_config.device_info.framework_version.empty())
4,153✔
305
        app_config.device_info.framework_version = "Framework Version";
3,632✔
306
    if (app_config.device_info.bundle_id.empty())
4,153✔
307
        app_config.device_info.bundle_id = "Bundle Id";
3,632✔
308
    if (app_config.app_id.empty())
4,153✔
309
        app_config.app_id = "app_id";
3,596✔
310
}
4,153✔
311

312
// MARK: - TestAppSession
313

314
#if REALM_ENABLE_AUTH_TESTS
315

316
TestAppSession::TestAppSession()
317
    : TestAppSession(get_runtime_app_session(get_base_url()), nullptr, DeleteApp{false})
318
{
138✔
319
}
138✔
320

321
TestAppSession::TestAppSession(AppSession session,
322
                               std::shared_ptr<realm::app::GenericNetworkTransport> custom_transport,
323
                               DeleteApp delete_app, ReconnectMode reconnect_mode,
324
                               std::shared_ptr<realm::sync::SyncSocketProvider> custom_socket_provider)
325
    : m_app_session(std::make_unique<AppSession>(session))
326
    , m_base_file_path(util::make_temp_dir() + random_string(10))
327
    , m_delete_app(delete_app)
328
    , m_transport(custom_transport)
329
{
465✔
330
    if (!m_transport)
465✔
331
        m_transport = instance_of<SynchronousTestTransport>;
312✔
332
    auto app_config = get_config(m_transport, *m_app_session);
465✔
333
    set_app_config_defaults(app_config, m_transport);
465✔
334

224✔
335
    util::try_make_dir(m_base_file_path);
465✔
336
    SyncClientConfig sc_config;
465✔
337
    sc_config.base_file_path = m_base_file_path;
465✔
338
    sc_config.metadata_mode = realm::SyncManager::MetadataMode::NoEncryption;
465✔
339
    sc_config.reconnect_mode = reconnect_mode;
465✔
340
    sc_config.socket_provider = custom_socket_provider;
465✔
341
    // With multiplexing enabled, the linger time controls how long a
224✔
342
    // connection is kept open for reuse. In tests, we want to shut
224✔
343
    // down sync clients immediately.
224✔
344
    sc_config.timeouts.connection_linger_time = 0;
465✔
345

224✔
346
    m_app = app::App::get_uncached_app(app_config, sc_config);
465✔
347

224✔
348
    // initialize sync client
224✔
349
    m_app->sync_manager()->get_sync_client();
465✔
350
    create_user_and_log_in(m_app);
465✔
351
}
465✔
352

353
TestAppSession::~TestAppSession()
354
{
465✔
355
    if (util::File::exists(m_base_file_path)) {
465✔
356
        try {
465✔
357
            m_app->sync_manager()->reset_for_testing();
465✔
358
            util::try_remove_dir_recursive(m_base_file_path);
465✔
359
        }
465✔
360
        catch (const std::exception& ex) {
224✔
361
            std::cerr << ex.what() << "\n";
×
362
        }
×
363
        app::App::clear_cached_apps();
465✔
364
    }
465✔
365
    if (m_delete_app) {
465✔
366
        m_app_session->admin_api.delete_app(m_app_session->server_app_id);
301✔
367
    }
301✔
368
}
465✔
369

370
std::vector<bson::BsonDocument> TestAppSession::get_documents(SyncUser& user, const std::string& object_type,
371
                                                              size_t expected_count) const
372
{
25✔
373
    app::MongoClient remote_client = user.mongo_client("BackingDB");
25✔
374
    app::MongoDatabase db = remote_client.db(m_app_session->config.mongo_dbname);
25✔
375
    app::MongoCollection collection = db[object_type];
25✔
376
    int sleep_time = 10;
25✔
377
    timed_wait_for(
25✔
378
        [&] {
117✔
379
            uint64_t count = 0;
117✔
380
            collection.count({}, [&](uint64_t c, util::Optional<app::AppError> error) {
117✔
381
                REQUIRE(!error);
117!
382
                count = c;
117✔
383
            });
117✔
384
            if (count < expected_count) {
117✔
385
                // querying the server too frequently makes it take longer to process the sync changesets we're
27✔
386
                // waiting for
27✔
387
                millisleep(sleep_time);
71✔
388
                if (sleep_time < 500) {
71✔
389
                    sleep_time *= 2;
55✔
390
                }
55✔
391
                return false;
71✔
392
            }
71✔
393
            return true;
46✔
394
        },
46✔
395
        std::chrono::minutes(5));
25✔
396

10✔
397
    std::vector<bson::BsonDocument> documents;
25✔
398
    collection.find({}, {},
25✔
399
                    [&](util::Optional<std::vector<bson::Bson>>&& result, util::Optional<app::AppError> error) {
25✔
400
                        REQUIRE(result);
25!
401
                        REQUIRE(!error);
25!
402
                        REQUIRE(result->size() == expected_count);
25!
403
                        documents.reserve(result->size());
25✔
404
                        for (auto&& bson : *result) {
2,294✔
405
                            REQUIRE(bson.type() == bson::Bson::Type::Document);
2,294!
406
                            documents.push_back(std::move(static_cast<bson::BsonDocument&>(bson)));
2,294✔
407
                        }
2,294✔
408
                    });
25✔
409
    return documents;
25✔
410
}
25✔
411
#endif // REALM_ENABLE_AUTH_TESTS
412

413
// MARK: - TestSyncManager
414

415
TestSyncManager::TestSyncManager(const Config& config, const SyncServer::Config& sync_server_config)
416
    : transport(config.transport ? config.transport : std::make_shared<Transport>(network_callback))
417
    , m_sync_server(sync_server_config)
418
    , m_should_teardown_test_directory(config.should_teardown_test_directory)
419
{
3,630✔
420
    app::App::Config app_config = config.app_config;
3,630✔
421
    set_app_config_defaults(app_config, transport);
3,630✔
422

1,788✔
423
    SyncClientConfig sc_config;
3,630✔
424
    m_base_file_path = config.base_path.empty() ? util::make_temp_dir() + random_string(10) : config.base_path;
3,609✔
425
    util::try_make_dir(m_base_file_path);
3,630✔
426
    sc_config.base_file_path = m_base_file_path;
3,630✔
427
    sc_config.metadata_mode = config.metadata_mode;
3,630✔
428

1,788✔
429
    m_app = app::App::get_uncached_app(app_config, sc_config);
3,630✔
430
    if (config.override_sync_route) {
3,630✔
431
        m_app->sync_manager()->set_sync_route(m_sync_server.base_url() + "/realm-sync");
3,628✔
432
    }
3,628✔
433

1,788✔
434
    if (config.start_sync_client) {
3,630✔
435
        // initialize sync client
1,786✔
436
        m_app->sync_manager()->get_sync_client();
3,626✔
437
    }
3,626✔
438
}
3,630✔
439

440
TestSyncManager::~TestSyncManager()
441
{
3,630✔
442
    if (m_should_teardown_test_directory) {
3,630✔
443
        if (!m_base_file_path.empty() && util::File::exists(m_base_file_path)) {
3,592✔
444
            try {
3,590✔
445
                m_app->sync_manager()->reset_for_testing();
3,590✔
446
                util::try_remove_dir_recursive(m_base_file_path);
3,590✔
447
            }
3,590✔
448
            catch (const std::exception& ex) {
1,768✔
449
                std::cerr << ex.what() << "\n";
×
450
            }
×
451
            app::App::clear_cached_apps();
3,590✔
452
        }
3,590✔
453
    }
3,592✔
454
}
3,630✔
455

456
std::shared_ptr<realm::SyncUser> TestSyncManager::fake_user(const std::string& name)
457
{
4✔
458
    return get_fake_user(*m_app, name);
4✔
459
}
4✔
460

461
#endif // REALM_ENABLE_SYNC
462

463
#if REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
464
// MARK: - TsanNotifyWorker
465
// A helper which synchronously runs on_change() on a fixed background thread
466
// so that ThreadSanitizer can potentially detect issues
467
// This deliberately uses an unsafe spinlock for synchronization to ensure that
468
// the code being tested has to supply all required safety
469
static class TsanNotifyWorker {
470
public:
471
    TsanNotifyWorker()
472
    {
473
        m_thread = std::thread([&] {
474
            work();
475
        });
476
    }
477

478
    void work()
479
    {
480
        while (true) {
481
            auto value = m_signal.load(std::memory_order_relaxed);
482
            if (value == 0 || value == 1)
483
                continue;
484
            if (value == 2)
485
                return;
486

487
            if (value & 1) {
488
                // Synchronize on the first handover of a given coordinator.
489
                value &= ~1;
490
                m_signal.load();
491
            }
492

493
            auto c = reinterpret_cast<_impl::RealmCoordinator*>(value);
494
            c->on_change();
495
            m_signal.store(1, std::memory_order_relaxed);
496
        }
497
    }
498

499
    ~TsanNotifyWorker()
500
    {
501
        m_signal = 2;
502
        m_thread.join();
503
    }
504

505
    void on_change(const std::shared_ptr<_impl::RealmCoordinator>& c)
506
    {
507
        auto& it = m_published_coordinators[c.get()];
508
        if (it.lock()) {
509
            m_signal.store(reinterpret_cast<uintptr_t>(c.get()), std::memory_order_relaxed);
510
        }
511
        else {
512
            // Synchronize on the first handover of a given coordinator.
513
            it = c;
514
            m_signal = reinterpret_cast<uintptr_t>(c.get()) | 1;
515
        }
516

517
        while (m_signal.load(std::memory_order_relaxed) != 1)
518
            ;
519
    }
520

521
private:
522
    std::atomic<uintptr_t> m_signal{0};
523
    std::thread m_thread;
524
    std::map<_impl::RealmCoordinator*, std::weak_ptr<_impl::RealmCoordinator>> m_published_coordinators;
525
} s_worker;
526

527
void on_change_but_no_notify(Realm& realm)
528
{
529
    s_worker.on_change(_impl::RealmCoordinator::get_existing_coordinator(realm.config().path));
530
}
531

532
void advance_and_notify(Realm& realm)
533
{
534
    on_change_but_no_notify(realm);
535
    realm.notify();
536
}
537

538
#else // REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
539

540
void on_change_but_no_notify(Realm& realm)
541
{
18,908✔
542
    _impl::RealmCoordinator::get_coordinator(realm.config().path)->on_change();
18,908✔
543
}
18,908✔
544

545
void advance_and_notify(Realm& realm)
546
{
16,154✔
547
    on_change_but_no_notify(realm);
16,154✔
548
    realm.notify();
16,154✔
549
}
16,154✔
550
#endif
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