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

realm / realm-core / 2104

06 Mar 2024 05:28PM UTC coverage: 90.902% (-0.05%) from 90.949%
2104

push

Evergreen

web-flow
Add * wildcard matching for benchmark filters and fix a crash (#7413)

* add * wildcard matching for benchmark filters and fix a crash

* Use full regex as a filter for benchmark names

---------

Co-authored-by: Kirill Burtsev <kirill.burtsev@mongodb.com>

93884 of 173072 branches covered (54.25%)

238284 of 262132 relevant lines covered (90.9%)

6142248.78 hits per line

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

89.2
/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
static std::vector<std::pair<std::string_view, realm::util::Logger::Level>> default_log_levels = {
69
    {"Realm", realm::util::Logger::Level::TEST_LOGGING_LEVEL},
70
#ifdef TEST_LOGGING_LEVEL_STORAGE
71
    {"Realm.Storage", realm::util::Logger::Level::TEST_LOGGING_LEVEL_STORAGE},
72
#endif
73
#ifdef TEST_LOGGING_LEVEL_TRANSACTION
74
    {"Realm.Storage.Transaction", realm::util::Logger::Level::TEST_LOGGING_LEVEL_TRANSACTION},
75
#endif
76
#ifdef TEST_LOGGING_LEVEL_QUERY
77
    {"Realm.Storage.Query", realm::util::Logger::Level::TEST_LOGGING_LEVEL_QUERY},
78
#endif
79
#ifdef TEST_LOGGING_LEVEL_OBJECT
80
    {"Realm.Storage.Object", realm::util::Logger::Level::TEST_LOGGING_LEVEL_OBJECT},
81
#endif
82
#ifdef TEST_LOGGING_LEVEL_NOTIFICATION
83
    {"Realm.Storage.Notification", realm::util::Logger::Level::TEST_LOGGING_LEVEL_NOTIFICATION},
84
#endif
85
#ifdef TEST_LOGGING_LEVEL_SYNC
86
    {"Realm.Sync", realm::util::Logger::Level::TEST_LOGGING_LEVEL_SYNC},
87
#endif
88
#ifdef TEST_LOGGING_LEVEL_CLIENT
89
    {"Realm.Sync.Client", realm::util::Logger::Level::TEST_LOGGING_LEVEL_CLIENT},
90
#endif
91
#ifdef TEST_LOGGING_LEVEL_SESSION
92
    {"Realm.Sync.Client.Session", realm::util::Logger::Level::TEST_LOGGING_LEVEL_SESSION},
93
#endif
94
#ifdef TEST_LOGGING_LEVEL_CHANGESET
95
    {"Realm.Sync.Client.Changeset", realm::util::Logger::Level::TEST_LOGGING_LEVEL_CHANGESET},
96
#endif
97
#ifdef TEST_LOGGING_LEVEL_NETWORK
98
    {"Realm.Sync.Client.Network", realm::util::Logger::Level::TEST_LOGGING_LEVEL_NETWORK},
99
#endif
100
#ifdef TEST_LOGGING_LEVEL_RESET
101
    {"Realm.Sync.Client.Reset", realm::util::Logger::Level::TEST_LOGGING_LEVEL_RESET},
102
#endif
103
#ifdef TEST_LOGGING_LEVEL_SERVER
104
    {"Realm.Sync.Server", realm::util::Logger::Level::TEST_LOGGING_LEVEL_SERVER},
105
#endif
106
#ifdef TEST_LOGGING_LEVEL_APP
107
    {"Realm.App", realm::util::Logger::Level::TEST_LOGGING_LEVEL_APP},
108
#endif
109
};
110

111
static void set_default_level_thresholds()
112
{
22,503✔
113
    for (auto [cat, level] : default_log_levels) {
67,509✔
114
        realm::util::LogCategory::get_category(cat).set_default_level_threshold(level);
67,509✔
115
    }
67,509✔
116
}
22,503✔
117

118
TestFile::TestFile()
119
{
9,512✔
120
    disable_sync_to_disk();
9,512✔
121
    m_temp_dir = util::make_temp_dir();
9,512✔
122
    path = (fs::path(m_temp_dir) / "realm.XXXXXX").string();
9,512✔
123
    set_default_level_thresholds();
9,512✔
124
    if (const char* crypt_key = test_util::crypt_key()) {
9,512✔
125
        encryption_key = std::vector<char>(crypt_key, crypt_key + 64);
×
126
    }
×
127
    int fd = mkstemp(path.data());
9,512✔
128
    if (fd < 0) {
9,512✔
129
        int err = errno;
×
130
        throw std::system_error(err, std::system_category());
×
131
    }
×
132
#ifdef _WIN32
133
    _close(fd);
134
    _unlink(path.c_str());
135
#else // POSIX
136
    close(fd);
9,512✔
137
    unlink(path.c_str());
9,512✔
138
#endif
9,512✔
139

4,717✔
140
    schema_version = 0;
9,512✔
141
}
9,512✔
142

143
TestFile::~TestFile()
144
{
9,512✔
145
    if (!m_persist) {
9,512✔
146
        try {
9,426✔
147
            util::Logger::get_default_logger()->debug("~TestFile() removing '%1' and '%2'", path, m_temp_dir);
9,426✔
148
            util::File::try_remove(path);
9,426✔
149
            util::try_remove_dir_recursive(m_temp_dir);
9,426✔
150
        }
9,426✔
151
        catch (const std::exception& e) {
4,674✔
152
            util::Logger::get_default_logger()->warn("~TestFile() cleanup failed for '%1': %2", path, e.what());
×
153
            // clean up is best effort, ignored.
154
        }
×
155
    }
9,426✔
156
}
9,512✔
157

158
DBOptions TestFile::options() const
159
{
26✔
160
    DBOptions options;
26✔
161
    options.durability = in_memory ? DBOptions::Durability::MemOnly : DBOptions::Durability::Full;
26✔
162
    return options;
26✔
163
}
26✔
164

165
InMemoryTestFile::InMemoryTestFile()
166
{
8,590✔
167
    in_memory = true;
8,590✔
168
    schema_version = 0;
8,590✔
169
    encryption_key = std::vector<char>();
8,590✔
170
    set_default_level_thresholds();
8,590✔
171
}
8,590✔
172

173
DBOptions InMemoryTestFile::options() const
174
{
×
175
    DBOptions options;
×
176
    options.durability = DBOptions::Durability::MemOnly;
×
177
    return options;
×
178
}
×
179

180
#if REALM_ENABLE_SYNC
181

182
static const std::string fake_refresh_token = ENCODE_FAKE_JWT("not_a_real_token");
183
static const std::string fake_access_token = ENCODE_FAKE_JWT("also_not_real");
184
static const std::string fake_device_id = "123400000000000000000000";
185

186
static std::shared_ptr<SyncUser> get_fake_user(SyncManager& sync_manager, const std::string& user_name)
187
{
3,806✔
188
    return sync_manager.get_user(user_name, fake_refresh_token, fake_access_token, fake_device_id);
3,806✔
189
}
3,806✔
190

191
SyncTestFile::SyncTestFile(TestSyncManager& tsm, std::string name, std::string user_name)
192
    : SyncTestFile(tsm.fake_user(user_name), bson::Bson(name))
193
{
198✔
194
}
198✔
195

196
SyncTestFile::SyncTestFile(OfflineAppSession& oas, std::string name)
197
    : SyncTestFile(oas.make_user(), bson::Bson(name))
198
{
3,480✔
199
}
3,480✔
200

201
SyncTestFile::SyncTestFile(std::shared_ptr<SyncUser> user, bson::Bson partition, util::Optional<Schema> schema)
202
{
7,636✔
203
    REALM_ASSERT(user);
7,636✔
204
    sync_config = std::make_shared<realm::SyncConfig>(user, partition);
7,636✔
205
    sync_config->stop_policy = SyncSessionStopPolicy::Immediately;
7,636✔
206
    sync_config->error_handler = [](std::shared_ptr<SyncSession>, SyncError error) {
3,782✔
207
        util::format(std::cerr, "An unexpected sync error was caught by the default SyncTestFile handler: '%1'\n",
×
208
                     error.status);
×
209
        abort();
×
210
    };
×
211
    schema_version = 1;
7,636✔
212
    this->schema = std::move(schema);
7,636✔
213
    schema_mode = SchemaMode::AdditiveExplicit;
7,636✔
214
}
7,636✔
215

216
SyncTestFile::SyncTestFile(std::shared_ptr<SyncUser> user, bson::Bson partition,
217
                           realm::util::Optional<realm::Schema> schema,
218
                           std::function<SyncSessionErrorHandler>&& error_handler)
219
{
2✔
220
    REALM_ASSERT(user);
2✔
221
    sync_config = std::make_shared<realm::SyncConfig>(user, partition);
2✔
222
    sync_config->stop_policy = SyncSessionStopPolicy::Immediately;
2✔
223
    sync_config->error_handler = std::move(error_handler);
2✔
224
    schema_version = 1;
2✔
225
    this->schema = std::move(schema);
2✔
226
    schema_mode = SchemaMode::AdditiveExplicit;
2✔
227
}
2✔
228

229
SyncTestFile::SyncTestFile(std::shared_ptr<realm::SyncUser> user, realm::Schema _schema, SyncConfig::FLXSyncEnabled)
230
{
373✔
231
    REALM_ASSERT(user);
373✔
232
    sync_config = std::make_shared<realm::SyncConfig>(user, SyncConfig::FLXSyncEnabled{});
373✔
233
    sync_config->stop_policy = SyncSessionStopPolicy::Immediately;
373✔
234
    sync_config->error_handler = [](std::shared_ptr<SyncSession> session, SyncError error) {
186✔
235
        util::format(std::cerr,
×
236
                     "An unexpected sync error was caught by the default SyncTestFile handler: '%1' for '%2'",
×
237
                     error.status, session->path());
×
238
        abort();
×
239
    };
×
240
    schema_version = 0;
373✔
241
    schema = _schema;
373✔
242
    schema_mode = SchemaMode::AdditiveExplicit;
373✔
243
}
373✔
244

245
SyncTestFile::SyncTestFile(std::shared_ptr<app::App> app, bson::Bson partition, Schema schema)
246
    : SyncTestFile(app->current_user(), std::move(partition), std::move(schema))
247
{
126✔
248
}
126✔
249

250
// MARK: - SyncServer
251
SyncServer::SyncServer(const SyncServer::Config& config)
252
    : m_local_root_dir(config.local_dir.empty() ? util::make_temp_dir() : config.local_dir)
253
    , m_server(m_local_root_dir, util::none, ([&] {
344✔
254
                   using namespace std::literals::chrono_literals;
344✔
255

145✔
256
                   m_logger = util::Logger::get_default_logger();
344✔
257

145✔
258
                   sync::Server::Config c;
344✔
259
                   c.logger = m_logger;
344✔
260
                   c.token_expiration_clock = this;
344✔
261
                   c.listen_address = "127.0.0.1";
344✔
262
                   c.disable_sync_to_disk = true;
344✔
263
                   c.ssl = config.ssl;
344✔
264
                   if (c.ssl) {
344✔
265
                       c.ssl_certificate_path = test_util::get_test_resource_path() + "test_util_network_ssl_ca.pem";
2✔
266
                       c.ssl_certificate_key_path =
2✔
267
                           test_util::get_test_resource_path() + "test_util_network_ssl_key.pem";
2✔
268
                   }
2✔
269

145✔
270
                   return c;
344✔
271
               })())
344✔
272
{
344✔
273
    m_server.start();
344✔
274
    m_url = util::format("%1://127.0.0.1:%2", config.ssl ? "wss" : "ws", m_server.listen_endpoint().port());
343✔
275
    if (config.start_immediately)
344✔
276
        start();
283✔
277
}
344✔
278

279
SyncServer::~SyncServer()
280
{
344✔
281
    stop();
344✔
282
}
344✔
283

284
void SyncServer::start()
285
{
316✔
286
    REALM_ASSERT(!m_thread.joinable());
316✔
287
    m_thread = std::thread([this] {
316✔
288
        m_server.run();
316✔
289
    });
316✔
290
}
316✔
291

292
void SyncServer::stop()
293
{
394✔
294
    m_server.stop();
394✔
295
    if (m_thread.joinable())
394✔
296
        m_thread.join();
316✔
297
}
394✔
298

299
std::string SyncServer::url_for_realm(StringData realm_name) const
300
{
×
301
    return util::format("%1/%2", m_url, realm_name);
×
302
}
×
303

304
struct WaitForSessionState {
305
    std::condition_variable cv;
306
    std::mutex mutex;
307
    bool complete = false;
308
    Status status = Status::OK();
309
};
310

311
static Status wait_for_session(Realm& realm, void (SyncSession::*fn)(util::UniqueFunction<void(Status)>&&),
312
                               std::chrono::seconds timeout)
313
{
1,642✔
314
    auto shared_state = std::make_shared<WaitForSessionState>();
1,642✔
315
    auto& session = *realm.config().sync_config->user->session_for_on_disk_path(realm.config().path);
1,642✔
316
    auto delay = TEST_TIMEOUT_EXTRA > 0 ? timeout + std::chrono::seconds(TEST_TIMEOUT_EXTRA) : timeout;
1,642✔
317
    (session.*fn)([weak_state = std::weak_ptr<WaitForSessionState>(shared_state)](Status s) {
1,642✔
318
        auto shared_state = weak_state.lock();
1,642✔
319
        if (!shared_state) {
1,642✔
320
            return;
×
321
        }
×
322
        std::lock_guard<std::mutex> lock(shared_state->mutex);
1,642✔
323
        shared_state->complete = true;
1,642✔
324
        shared_state->status = s;
1,642✔
325
        shared_state->cv.notify_one();
1,642✔
326
    });
1,642✔
327
    std::unique_lock<std::mutex> lock(shared_state->mutex);
1,642✔
328
    bool completed = shared_state->cv.wait_for(lock, delay, [&]() {
3,284✔
329
        return shared_state->complete == true;
3,284✔
330
    });
3,284✔
331
    if (!completed) {
1,642✔
332
        throw std::runtime_error(util::format("wait_for_session() exceeded %1 s", delay.count()));
×
333
    }
×
334
    return shared_state->status;
1,642✔
335
}
1,642✔
336

337
bool wait_for_upload(Realm& realm, std::chrono::seconds timeout)
338
{
792✔
339
    return !wait_for_session(realm, &SyncSession::wait_for_upload_completion, timeout).is_ok();
792✔
340
}
792✔
341

342
bool wait_for_download(Realm& realm, std::chrono::seconds timeout)
343
{
850✔
344
    return !wait_for_session(realm, &SyncSession::wait_for_download_completion, timeout).is_ok();
850✔
345
}
850✔
346

347
void set_app_config_defaults(app::App::Config& app_config,
348
                             const std::shared_ptr<app::GenericNetworkTransport>& transport)
349
{
4,151✔
350
    if (!app_config.transport)
4,151✔
351
        app_config.transport = transport;
3,572✔
352
    if (app_config.device_info.platform_version.empty())
4,151✔
353
        app_config.device_info.platform_version = "Object Store Test Platform Version";
3,572✔
354
    if (app_config.device_info.sdk_version.empty())
4,151✔
355
        app_config.device_info.sdk_version = "SDK Version";
3,572✔
356
    if (app_config.device_info.sdk.empty())
4,151✔
357
        app_config.device_info.sdk = "SDK Name";
3,572✔
358
    if (app_config.device_info.device_name.empty())
4,151✔
359
        app_config.device_info.device_name = "Device Name";
3,572✔
360
    if (app_config.device_info.device_version.empty())
4,151✔
361
        app_config.device_info.device_version = "Device Version";
3,572✔
362
    if (app_config.device_info.framework_name.empty())
4,151✔
363
        app_config.device_info.framework_name = "Framework Name";
3,572✔
364
    if (app_config.device_info.framework_version.empty())
4,151✔
365
        app_config.device_info.framework_version = "Framework Version";
3,572✔
366
    if (app_config.device_info.bundle_id.empty())
4,151✔
367
        app_config.device_info.bundle_id = "Bundle Id";
3,572✔
368
    if (app_config.app_id.empty())
4,151✔
369
        app_config.app_id = "app_id";
3,556✔
370
}
4,151✔
371

372
// MARK: - TestAppSession
373

374
#if REALM_ENABLE_AUTH_TESTS
375

376
TestAppSession::TestAppSession()
377
    : TestAppSession(get_runtime_app_session(), nullptr, DeleteApp{false})
378
{
140✔
379
}
140✔
380

381
TestAppSession::TestAppSession(AppSession session,
382
                               std::shared_ptr<realm::app::GenericNetworkTransport> custom_transport,
383
                               DeleteApp delete_app, ReconnectMode reconnect_mode,
384
                               std::shared_ptr<realm::sync::SyncSocketProvider> custom_socket_provider)
385
    : m_app_session(std::make_unique<AppSession>(session))
386
    , m_base_file_path(util::make_temp_dir() + random_string(10))
387
    , m_delete_app(delete_app)
388
    , m_transport(custom_transport)
389
{
521✔
390
    if (!m_transport)
521✔
391
        m_transport = instance_of<SynchronousTestTransport>;
322✔
392
    auto app_config = get_config(m_transport, *m_app_session);
521✔
393
    set_default_level_thresholds();
521✔
394
    set_app_config_defaults(app_config, m_transport);
521✔
395

252✔
396
    util::try_make_dir(m_base_file_path);
521✔
397
    SyncClientConfig sc_config;
521✔
398
    sc_config.base_file_path = m_base_file_path;
521✔
399
    sc_config.metadata_mode = realm::SyncManager::MetadataMode::NoEncryption;
521✔
400
    sc_config.reconnect_mode = reconnect_mode;
521✔
401
    sc_config.socket_provider = custom_socket_provider;
521✔
402
    // With multiplexing enabled, the linger time controls how long a
252✔
403
    // connection is kept open for reuse. In tests, we want to shut
252✔
404
    // down sync clients immediately.
252✔
405
    sc_config.timeouts.connection_linger_time = 0;
521✔
406

252✔
407
    m_app = app::App::get_app(app::App::CacheMode::Disabled, app_config, sc_config);
521✔
408

252✔
409
    // initialize sync client
252✔
410
    m_app->sync_manager()->get_sync_client();
521✔
411
    create_user_and_log_in(m_app);
521✔
412
}
521✔
413

414
TestAppSession::~TestAppSession()
415
{
521✔
416
    if (util::File::exists(m_base_file_path)) {
521✔
417
        try {
521✔
418
            m_app->sync_manager()->tear_down_for_testing();
521✔
419
            util::try_remove_dir_recursive(m_base_file_path);
521✔
420
        }
521✔
421
        catch (const std::exception& ex) {
252✔
422
            std::cerr << ex.what() << "\n";
×
423
        }
×
424
        app::App::clear_cached_apps();
521✔
425
    }
521✔
426
    if (m_delete_app) {
521✔
427
        m_app_session->admin_api.delete_app(m_app_session->server_app_id);
355✔
428
    }
355✔
429
}
521✔
430

431
std::vector<bson::BsonDocument> TestAppSession::get_documents(SyncUser& user, const std::string& object_type,
432
                                                              size_t expected_count) const
433
{
25✔
434
    app::MongoClient remote_client = user.mongo_client("BackingDB");
25✔
435
    app::MongoDatabase db = remote_client.db(m_app_session->config.mongo_dbname);
25✔
436
    app::MongoCollection collection = db[object_type];
25✔
437
    int sleep_time = 10;
25✔
438
    timed_wait_for(
25✔
439
        [&] {
102✔
440
            uint64_t count = 0;
102✔
441
            collection.count({}, [&](uint64_t c, util::Optional<app::AppError> error) {
102✔
442
                REQUIRE(!error);
102!
443
                count = c;
102✔
444
            });
102✔
445
            if (count < expected_count) {
102✔
446
                // querying the server too frequently makes it take longer to process the sync changesets we're
20✔
447
                // waiting for
20✔
448
                millisleep(sleep_time);
51✔
449
                if (sleep_time < 500) {
51✔
450
                    sleep_time *= 2;
47✔
451
                }
47✔
452
                return false;
51✔
453
            }
51✔
454
            return true;
51✔
455
        },
51✔
456
        std::chrono::minutes(5));
25✔
457

10✔
458
    std::vector<bson::BsonDocument> documents;
25✔
459
    collection.find({}, {}, [&](util::Optional<bson::BsonArray>&& result, util::Optional<app::AppError> error) {
25✔
460
        REQUIRE(result);
25!
461
        REQUIRE(!error);
25!
462
        REQUIRE(result->size() == expected_count);
25!
463
        documents.reserve(result->size());
25✔
464
        for (auto&& bson : *result) {
2,294✔
465
            REQUIRE(bson.type() == bson::Bson::Type::Document);
2,294!
466
            documents.push_back(std::move(static_cast<const bson::BsonDocument&>(bson)));
2,294✔
467
        }
2,294✔
468
    });
25✔
469
    return documents;
25✔
470
}
25✔
471
#endif // REALM_ENABLE_AUTH_TESTS
472

473
// MARK: - TestSyncManager
474

475
TestSyncManager::Config::Config()
476
{
336✔
477
    set_default_level_thresholds();
336✔
478
}
336✔
479

480
TestSyncManager::TestSyncManager(const Config& config, const SyncServer::Config& sync_server_config)
481
    : m_sync_server(sync_server_config)
482
    , m_base_file_path(config.base_path.empty() ? util::make_temp_dir() : config.base_path)
483
    , m_should_teardown_test_directory(config.should_teardown_test_directory)
484
{
344✔
485
    util::try_make_dir(m_base_file_path);
344✔
486
    SyncClientConfig sc_config;
344✔
487
    sc_config.base_file_path = m_base_file_path;
344✔
488
    sc_config.metadata_mode = config.metadata_mode;
344✔
489
    m_sync_manager = SyncManager::create(nullptr, m_sync_server.base_url() + "/realm-sync", sc_config, "app_id");
344✔
490

145✔
491
    if (config.start_sync_client) {
344✔
492
        m_sync_manager->get_sync_client();
340✔
493
    }
340✔
494
}
344✔
495

496
TestSyncManager::~TestSyncManager()
497
{
344✔
498
    if (m_should_teardown_test_directory) {
344✔
499
        if (!m_base_file_path.empty() && util::File::exists(m_base_file_path)) {
314✔
500
            try {
312✔
501
                m_sync_manager->tear_down_for_testing();
312✔
502
                util::try_remove_dir_recursive(m_base_file_path);
312✔
503
            }
312✔
504
            catch (const std::exception& ex) {
129✔
505
                std::cerr << ex.what() << "\n";
×
506
            }
×
507
            app::App::clear_cached_apps();
312✔
508
        }
312✔
509
    }
314✔
510
}
344✔
511

512
std::shared_ptr<realm::SyncUser> TestSyncManager::fake_user(const std::string& name)
513
{
316✔
514
    return get_fake_user(*m_sync_manager, name);
316✔
515
}
316✔
516

517
OfflineAppSession::Config::Config(std::shared_ptr<realm::app::GenericNetworkTransport> t)
518
    : transport(t)
519
{
3,542✔
520
}
3,542✔
521

522
OfflineAppSession::OfflineAppSession(OfflineAppSession::Config config)
523
    : m_transport(std::move(config.transport))
524
    , m_delete_storage(config.delete_storage)
525
{
3,544✔
526
    REALM_ASSERT(m_transport);
3,544✔
527
    if (config.storage_path) {
3,544✔
528
        m_base_file_path = *config.storage_path;
4✔
529
        util::try_make_dir(m_base_file_path);
4✔
530
    }
4✔
531
    else {
3,540✔
532
        m_base_file_path = util::make_temp_dir();
3,540✔
533
    }
3,540✔
534

1,772✔
535
    app::App::Config app_config;
3,544✔
536
    set_app_config_defaults(app_config, m_transport);
3,544✔
537
    if (config.base_url) {
3,544✔
538
        app_config.base_url = *config.base_url;
×
539
    }
×
540
    if (config.app_id) {
3,544✔
541
        app_config.app_id = *config.app_id;
×
542
    }
×
543

1,772✔
544
    SyncClientConfig sc_config;
3,544✔
545
    sc_config.base_file_path = m_base_file_path;
3,544✔
546
    sc_config.metadata_mode = config.metadata_mode;
3,544✔
547
    sc_config.socket_provider = config.socket_provider;
3,544✔
548

1,772✔
549
    set_default_level_thresholds();
3,544✔
550

1,772✔
551
    m_app = app::App::get_app(app::App::CacheMode::Disabled, app_config, sc_config);
3,544✔
552
}
3,544✔
553

554
OfflineAppSession::~OfflineAppSession()
555
{
3,544✔
556
    if (util::File::exists(m_base_file_path) && m_delete_storage) {
3,544✔
557
        try {
3,542✔
558
            m_app->sync_manager()->tear_down_for_testing();
3,542✔
559
            util::try_remove_dir_recursive(m_base_file_path);
3,542✔
560
        }
3,542✔
561
        catch (const std::exception& ex) {
1,771✔
562
            std::cerr << ex.what() << "\n";
×
563
        }
×
564
        app::App::clear_cached_apps();
3,542✔
565
    }
3,542✔
566
}
3,544✔
567

568
std::shared_ptr<realm::SyncUser> OfflineAppSession::make_user() const
569
{
3,490✔
570
    return get_fake_user(*m_app->sync_manager(), "test user");
3,490✔
571
}
3,490✔
572

573
#endif // REALM_ENABLE_SYNC
574

575
#if REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
576
// MARK: - TsanNotifyWorker
577
// A helper which synchronously runs on_change() on a fixed background thread
578
// so that ThreadSanitizer can potentially detect issues
579
// This deliberately uses an unsafe spinlock for synchronization to ensure that
580
// the code being tested has to supply all required safety
581
static class TsanNotifyWorker {
582
public:
583
    TsanNotifyWorker()
584
    {
585
        m_thread = std::thread([&] {
586
            work();
587
        });
588
    }
589

590
    void work()
591
    {
592
        while (true) {
593
            auto value = m_signal.load(std::memory_order_relaxed);
594
            if (value == 0 || value == 1)
595
                continue;
596
            if (value == 2)
597
                return;
598

599
            if (value & 1) {
600
                // Synchronize on the first handover of a given coordinator.
601
                value &= ~1;
602
                m_signal.load();
603
            }
604

605
            auto c = reinterpret_cast<_impl::RealmCoordinator*>(value);
606
            c->on_change();
607
            m_signal.store(1, std::memory_order_relaxed);
608
        }
609
    }
610

611
    ~TsanNotifyWorker()
612
    {
613
        m_signal = 2;
614
        m_thread.join();
615
    }
616

617
    void on_change(const std::shared_ptr<_impl::RealmCoordinator>& c)
618
    {
619
        auto& it = m_published_coordinators[c.get()];
620
        if (it.lock()) {
621
            m_signal.store(reinterpret_cast<uintptr_t>(c.get()), std::memory_order_relaxed);
622
        }
623
        else {
624
            // Synchronize on the first handover of a given coordinator.
625
            it = c;
626
            m_signal = reinterpret_cast<uintptr_t>(c.get()) | 1;
627
        }
628

629
        while (m_signal.load(std::memory_order_relaxed) != 1)
630
            ;
631
    }
632

633
private:
634
    std::atomic<uintptr_t> m_signal{0};
635
    std::thread m_thread;
636
    std::map<_impl::RealmCoordinator*, std::weak_ptr<_impl::RealmCoordinator>> m_published_coordinators;
637
} s_worker;
638

639
void on_change_but_no_notify(Realm& realm)
640
{
641
    s_worker.on_change(_impl::RealmCoordinator::get_existing_coordinator(realm.config().path));
642
}
643

644
void advance_and_notify(Realm& realm)
645
{
646
    on_change_but_no_notify(realm);
647
    realm.notify();
648
}
649

650
#else // REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
651

652
void on_change_but_no_notify(Realm& realm)
653
{
19,431✔
654
    _impl::RealmCoordinator::get_coordinator(realm.config().path)->on_change();
19,431✔
655
}
19,431✔
656

657
void advance_and_notify(Realm& realm)
658
{
16,802✔
659
    on_change_but_no_notify(realm);
16,802✔
660
    realm.notify();
16,802✔
661
}
16,802✔
662
#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

© 2026 Coveralls, Inc