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

realm / realm-core / github_pull_request_312964

19 Feb 2025 07:31PM UTC coverage: 90.814% (-0.3%) from 91.119%
github_pull_request_312964

Pull #8071

Evergreen

web-flow
Bump serialize-javascript and mocha

Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) to 6.0.2 and updates ancestor dependency [mocha](https://github.com/mochajs/mocha). These dependencies need to be updated together.


Updates `serialize-javascript` from 6.0.0 to 6.0.2
- [Release notes](https://github.com/yahoo/serialize-javascript/releases)
- [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.0...v6.0.2)

Updates `mocha` from 10.2.0 to 10.8.2
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v10.2.0...v10.8.2)

---
updated-dependencies:
- dependency-name: serialize-javascript
  dependency-type: indirect
- dependency-name: mocha
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #8071: Bump serialize-javascript and mocha

96552 of 179126 branches covered (53.9%)

212672 of 234185 relevant lines covered (90.81%)

3115802.0 hits per line

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

86.85
/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/sync/sync_test_utils.hpp"
24
#include "../util/crypt_key.hpp"
25
#include "../util/test_path.hpp"
26
#include "util/sync/sync_test_utils.hpp"
27

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

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

46
#include <cstdlib>
47
#include <iostream>
48

49
#ifdef _WIN32
50
#include <io.h>
51
#include <fcntl.h>
52

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

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

68
using namespace realm;
69

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

91
    schema_version = 0;
4,998✔
92
}
4,998✔
93

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

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

116
InMemoryTestFile::InMemoryTestFile()
117
{
4,314✔
118
    in_memory = true;
4,314✔
119
    schema_version = 0;
4,314✔
120
    encryption_key = std::vector<char>();
4,314✔
121
}
4,314✔
122

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

130
#if REALM_ENABLE_SYNC
131

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

136
SyncTestFile::SyncTestFile(TestSyncManager& tsm, std::string name, std::string user_name)
137
    : SyncTestFile(tsm.fake_user(user_name), bson::Bson(name))
91✔
138
{
91✔
139
}
91✔
140

141
#if REALM_APP_SERVICES
142
SyncTestFile::SyncTestFile(OfflineAppSession& oas, std::string name)
143
    : SyncTestFile(oas.make_user(), bson::Bson(name))
1,829✔
144
{
1,829✔
145
}
1,829✔
146
#endif // REALM_APP_SERVICES
147

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

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

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

192
SyncTestFile::SyncTestFile(TestSyncManager& tsm, bson::Bson partition, Schema schema)
193
    : SyncTestFile(tsm.fake_user("test"), std::move(partition), std::move(schema))
×
194
{
×
195
}
×
196

197
// MARK: - SyncServer
198
SyncServer::SyncServer(const SyncServer::Config& config)
199
    : m_local_root_dir(config.local_dir.empty() ? util::make_temp_dir() : config.local_dir)
98✔
200
    , m_server(m_local_root_dir, util::none, ([&] {
98✔
201
                   using namespace std::literals::chrono_literals;
98✔
202

203
                   m_logger = util::Logger::get_default_logger();
98✔
204

205
                   sync::Server::Config c;
98✔
206
                   c.logger = m_logger;
98✔
207
                   c.token_expiration_clock = this;
98✔
208
                   c.listen_address = "127.0.0.1";
98✔
209
                   c.disable_sync_to_disk = true;
98✔
210
                   c.ssl = config.ssl;
98✔
211
                   if (c.ssl) {
98✔
212
                       c.ssl_certificate_path = test_util::get_test_resource_path() + "test_util_network_ssl_ca.pem";
1✔
213
                       c.ssl_certificate_key_path =
1✔
214
                           test_util::get_test_resource_path() + "test_util_network_ssl_key.pem";
1✔
215
                   }
1✔
216

217
                   return c;
98✔
218
               })())
98✔
219
{
98✔
220
    m_server.start();
98✔
221
    m_url = util::format("%1://127.0.0.1:%2", config.ssl ? "wss" : "ws", m_server.listen_endpoint().port());
98✔
222
    if (config.start_immediately)
98✔
223
        start();
69✔
224
}
98✔
225

226
SyncServer::~SyncServer()
227
{
98✔
228
    stop();
98✔
229
}
98✔
230

231
void SyncServer::start()
232
{
84✔
233
    REALM_ASSERT(!m_thread.joinable());
84✔
234
    m_thread = std::thread([this] {
84✔
235
        m_server.run();
84✔
236
    });
84✔
237
}
84✔
238

239
void SyncServer::stop()
240
{
98✔
241
    m_server.stop();
98✔
242
    if (m_thread.joinable())
98✔
243
        m_thread.join();
84✔
244
}
98✔
245

246
std::string SyncServer::url_for_realm(StringData realm_name) const
247
{
×
248
    return util::format("%1/%2", m_url, realm_name);
×
249
}
×
250

251
int SyncServer::port() const
252
{
10✔
253
    return static_cast<int>(m_server.listen_endpoint().port());
10✔
254
}
10✔
255

256
struct WaitForSessionState {
257
    std::condition_variable cv;
258
    std::mutex mutex;
259
    bool complete = false;
260
    Status status = Status::OK();
261
};
262

263
static Status wait_for_session(Realm& realm, void (SyncSession::*fn)(util::UniqueFunction<void(Status)>&&),
264
                               std::chrono::seconds timeout)
265
{
989✔
266
    auto shared_state = std::make_shared<WaitForSessionState>();
989✔
267
    auto& session = *realm.sync_session();
989✔
268
    auto delay = TEST_TIMEOUT_EXTRA > 0 ? timeout + std::chrono::seconds(TEST_TIMEOUT_EXTRA) : timeout;
989✔
269
    (session.*fn)([weak_state = std::weak_ptr<WaitForSessionState>(shared_state)](Status s) {
989✔
270
        auto shared_state = weak_state.lock();
989✔
271
        if (!shared_state) {
989✔
272
            return;
×
273
        }
×
274
        std::lock_guard<std::mutex> lock(shared_state->mutex);
989✔
275
        shared_state->complete = true;
989✔
276
        shared_state->status = s;
989✔
277
        shared_state->cv.notify_one();
989✔
278
    });
989✔
279
    std::unique_lock<std::mutex> lock(shared_state->mutex);
989✔
280
    bool completed = shared_state->cv.wait_for(lock, delay, [&]() {
1,978✔
281
        return shared_state->complete == true;
1,978✔
282
    });
1,978✔
283
    if (!completed) {
989✔
284
        throw std::runtime_error(util::format("wait_for_session() exceeded %1 s", delay.count()));
×
285
    }
×
286
    return shared_state->status;
989✔
287
}
989✔
288

289
bool wait_for_upload(Realm& realm, std::chrono::seconds timeout)
290
{
466✔
291
    return !wait_for_session(realm, &SyncSession::wait_for_upload_completion, timeout).is_ok();
466✔
292
}
466✔
293

294
bool wait_for_download(Realm& realm, std::chrono::seconds timeout)
295
{
523✔
296
    return !wait_for_session(realm, &SyncSession::wait_for_download_completion, timeout).is_ok();
523✔
297
}
523✔
298

299
#if REALM_APP_SERVICES
300
void set_app_config_defaults(app::AppConfig& app_config,
301
                             const std::shared_ptr<app::GenericNetworkTransport>& transport)
302
{
2,179✔
303
    if (!app_config.transport)
2,179✔
304
        app_config.transport = transport;
1,912✔
305
    if (app_config.device_info.platform_version.empty())
2,179✔
306
        app_config.device_info.platform_version = "Object Store Test Platform Version";
1,912✔
307
    if (app_config.device_info.sdk_version.empty())
2,179✔
308
        app_config.device_info.sdk_version = "SDK Version";
1,912✔
309
    if (app_config.device_info.sdk.empty())
2,179✔
310
        app_config.device_info.sdk = "SDK Name";
1,912✔
311
    if (app_config.device_info.device_name.empty())
2,179✔
312
        app_config.device_info.device_name = "Device Name";
1,912✔
313
    if (app_config.device_info.device_version.empty())
2,179✔
314
        app_config.device_info.device_version = "Device Version";
1,912✔
315
    if (app_config.device_info.framework_name.empty())
2,179✔
316
        app_config.device_info.framework_name = "Framework Name";
1,912✔
317
    if (app_config.device_info.framework_version.empty())
2,179✔
318
        app_config.device_info.framework_version = "Framework Version";
1,912✔
319
    if (app_config.device_info.bundle_id.empty())
2,179✔
320
        app_config.device_info.bundle_id = "Bundle Id";
1,912✔
321
    if (app_config.app_id.empty())
2,179✔
322
        app_config.app_id = "app_id";
1,912✔
323
    app_config.metadata_mode = app::AppConfig::MetadataMode::InMemory;
2,179✔
324
}
2,179✔
325
#endif // REALM_APP_SERVICES
326

327
// MARK: - TestAppSession
328

329
#if REALM_ENABLE_AUTH_TESTS
330

331
TestAppSession::TestAppSession()
332
    // Don't delete the global runtime app session
333
    : TestAppSession(get_runtime_app_session(), {}, DeleteApp{false})
65✔
334
{
65✔
335
}
65✔
336

337
TestAppSession::TestAppSession(AppSession session)
338
    : TestAppSession(session, {}, DeleteApp{true})
84✔
339
{
84✔
340
}
84✔
341

342
TestAppSession::TestAppSession(AppSession session, Config config, DeleteApp delete_app, bool delete_storage)
343
    : m_app_session(std::make_unique<AppSession>(session))
267✔
344
    , m_config(config)
267✔
345
    , m_delete_app(delete_app)
267✔
346
    , m_delete_storage(delete_storage)
267✔
347
{
267✔
348
    if (!m_config.storage_path || m_config.storage_path->empty()) {
267✔
349
        m_config.storage_path.emplace(util::make_temp_dir() + random_string(10));
266✔
350
    }
266✔
351
    REALM_ASSERT(m_config.storage_path);
267✔
352
    util::try_make_dir(*m_config.storage_path);
267✔
353

354
    if (!m_config.transport) {
267✔
355
        m_config.transport = instance_of<SynchronousTestTransport>;
152✔
356
    }
152✔
357
    realm::app::AppConfig app_config = get_config(m_config.transport, *m_app_session);
267✔
358
    set_app_config_defaults(app_config, m_config.transport);
267✔
359
    // If a base URL was provided, set it in the app config
360
    if (m_config.base_url) {
267✔
361
        app_config.base_url = *m_config.base_url;
×
362
    }
×
363
    app_config.base_file_path = *m_config.storage_path;
267✔
364
    app_config.metadata_mode = m_config.metadata_mode;
267✔
365
    app_config.sync_client_config.reconnect_mode = m_config.reconnect_mode;
267✔
366
    // With multiplexing enabled, the linger time controls how long a
367
    // connection is kept open for reuse. In tests, we want to shut
368
    // down sync clients immediately.
369
    app_config.sync_client_config.timeouts.connection_linger_time = 0;
267✔
370
    app_config.sync_client_config.socket_provider = m_config.socket_provider;
267✔
371
    if (m_config.logger) {
267✔
372
        app_config.sync_client_config.logger_factory = [logger = m_config.logger](util::Logger::Level) {
1✔
373
            return logger;
1✔
374
        };
1✔
375
    }
1✔
376

377
    m_app = app::App::get_app(app::App::CacheMode::Disabled, app_config);
267✔
378

379
    // initialize sync client
380
    m_app->sync_manager()->get_sync_client();
267✔
381
    // If no user creds are supplied, then create the user and log in
382
    if (!m_config.user_creds) {
267✔
383
        auto result = create_user_and_log_in();
266✔
384
        REALM_ASSERT(result.is_ok());
266✔
385
        m_config.user_creds = result.get_value();
266✔
386
    }
266✔
387
    // If creds are supplied, it is up to the caller to log in separately
388
}
267✔
389

390
TestAppSession::~TestAppSession()
391
{
267✔
392
    if (m_app) {
267✔
393
        m_app->sync_manager()->tear_down_for_testing();
267✔
394
        m_app.reset();
267✔
395
    }
267✔
396
    app::App::clear_cached_apps();
267✔
397
    // If the app session is being deleted or the config tells us to, delete the storage path
398
    if ((m_delete_app || m_delete_storage) && util::File::exists(*m_config.storage_path)) {
267✔
399
        try {
266✔
400
            util::try_remove_dir_recursive(*m_config.storage_path);
266✔
401
        }
266✔
402
        catch (const std::exception& ex) {
266✔
403
            std::cerr << "Error tearing down TestAppSession(" << m_app_session->config.app_name << "): " << ex.what()
×
404
                      << "\n";
×
405
        }
×
406
    }
266✔
407
    if (m_delete_app && m_app_session) {
267✔
408
        m_app_session->admin_api.delete_app(m_app_session->server_app_id);
187✔
409
    }
187✔
410
}
267✔
411

412
StatusWith<realm::app::AppCredentials> TestAppSession::create_user_and_log_in()
413
{
266✔
414
    REALM_ASSERT(m_app);
266✔
415
    AutoVerifiedEmailCredentials creds;
266✔
416
    auto pf = util::make_promise_future<void>();
266✔
417
    m_app->provider_client<app::App::UsernamePasswordProviderClient>().register_email(
266✔
418
        creds.email, creds.password,
266✔
419
        [this, &creds, promise = util::CopyablePromiseHolder<void>(std::move(pf.promise))](
266✔
420
            util::Optional<app::AppError> error) mutable {
266✔
421
            if (error) {
266✔
422
                promise.get_promise().set_error(error->to_status());
×
423
                return;
×
424
            }
×
425
            auto result = log_in_user(creds);
266✔
426
            if (!result.is_ok()) {
266✔
427
                promise.get_promise().set_error(result.get_status());
×
428
                return;
×
429
            }
×
430
            promise.get_promise().emplace_value();
266✔
431
        });
266✔
432
    auto result = pf.future.get_no_throw();
266✔
433
    if (!result.is_ok()) {
266✔
434
        return result;
×
435
    }
×
436
    return creds;
266✔
437
}
266✔
438

439
StatusWith<std::shared_ptr<realm::SyncUser>>
440
TestAppSession::log_in_user(std::optional<realm::app::AppCredentials> user_creds)
441
{
266✔
442
    REALM_ASSERT(m_app);
266✔
443
    REALM_ASSERT((user_creds || m_config.user_creds));
266!
444
    auto pf = util::make_promise_future<std::shared_ptr<realm::SyncUser>>();
266✔
445
    m_app->log_in_with_credentials(
266✔
446
        *user_creds, [promise = util::CopyablePromiseHolder<std::shared_ptr<realm::SyncUser>>(std::move(pf.promise))](
266✔
447
                         std::shared_ptr<realm::SyncUser> user, util::Optional<app::AppError> error) mutable {
266✔
448
            if (error) {
266✔
449
                promise.get_promise().set_error(error->to_status());
×
450
                return;
×
451
            }
×
452
            promise.get_promise().emplace_value(user);
266✔
453
        });
266✔
454
    return pf.future.get_no_throw();
266✔
455
}
266✔
456

457
std::vector<bson::BsonDocument> TestAppSession::get_documents(app::User& user, const std::string& object_type,
458
                                                              size_t expected_count) const
459
{
10✔
460
    app::MongoClient remote_client = user.mongo_client("BackingDB");
10✔
461
    app::MongoDatabase db = remote_client.db(m_app_session->config.mongo_dbname);
10✔
462
    app::MongoCollection collection = db[object_type];
10✔
463
    int sleep_time = 10;
10✔
464
    timed_wait_for(
10✔
465
        [&] {
29✔
466
            uint64_t count = 0;
29✔
467
            collection.count({}, [&](uint64_t c, util::Optional<app::AppError> error) {
29✔
468
                REQUIRE(!error);
29!
469
                count = c;
29✔
470
            });
29✔
471
            if (count < expected_count) {
29✔
472
                // querying the server too frequently makes it take longer to process the sync changesets we're
473
                // waiting for
474
                millisleep(sleep_time);
19✔
475
                if (sleep_time < 500) {
19✔
476
                    sleep_time *= 2;
17✔
477
                }
17✔
478
                return false;
19✔
479
            }
19✔
480
            return true;
10✔
481
        },
29✔
482
        std::chrono::minutes(5));
10✔
483

484
    std::vector<bson::BsonDocument> documents;
10✔
485
    collection.find({}, {}, [&](util::Optional<bson::BsonArray>&& result, util::Optional<app::AppError> error) {
10✔
486
        REQUIRE(result);
10!
487
        REQUIRE(!error);
10!
488
        REQUIRE(result->size() == expected_count);
10!
489
        documents.reserve(result->size());
10✔
490
        for (auto&& bson : *result) {
1,143✔
491
            REQUIRE(bson.type() == bson::Bson::Type::Document);
1,143!
492
            documents.push_back(std::move(static_cast<const bson::BsonDocument&>(bson)));
1,143✔
493
        }
1,143✔
494
    });
10✔
495
    return documents;
10✔
496
}
10✔
497
#endif // REALM_ENABLE_AUTH_TESTS
498

499
// MARK: - TestSyncManager
500

501
TestSyncManager::Config::Config() {}
95✔
502

503
TestSyncManager::TestSyncManager(const Config& config, const SyncServer::Config& sync_server_config)
504
    : m_sync_manager(SyncManager::create(SyncClientConfig()))
95✔
505
    , m_sync_server(sync_server_config)
95✔
506
    , m_base_file_path(config.base_path.empty() ? util::make_temp_dir() : config.base_path)
95✔
507
    , m_should_teardown_test_directory(config.should_teardown_test_directory)
95✔
508
{
95✔
509
    util::try_make_dir(m_base_file_path);
95✔
510

511
    m_sync_manager->set_sync_route(m_sync_server.base_url() + "/realm-sync", true);
95✔
512
    if (config.start_sync_client) {
95✔
513
        m_sync_manager->get_sync_client();
93✔
514
    }
93✔
515
}
95✔
516

517
TestSyncManager::~TestSyncManager()
518
{
95✔
519
    if (m_should_teardown_test_directory) {
95✔
520
        if (!m_base_file_path.empty() && util::File::exists(m_base_file_path)) {
95✔
521
            try {
95✔
522
                m_sync_manager->tear_down_for_testing();
95✔
523
                util::try_remove_dir_recursive(m_base_file_path);
95✔
524
            }
95✔
525
            catch (const std::exception& ex) {
95✔
526
                std::cerr << ex.what() << "\n";
×
527
            }
×
528
#if REALM_APP_SERVICES
95✔
529
            app::App::clear_cached_apps();
95✔
530
#endif // REALM_APP_SERVICES
95✔
531
        }
95✔
532
    }
95✔
533
}
95✔
534

535
std::shared_ptr<TestUser> TestSyncManager::fake_user(const std::string& name)
536
{
136✔
537
    auto user = std::make_shared<TestUser>(name, m_sync_manager);
136✔
538
    user->m_access_token = fake_access_token;
136✔
539
    user->m_refresh_token = fake_refresh_token;
136✔
540
    return user;
136✔
541
}
136✔
542

543
#if REALM_APP_SERVICES
544
OfflineAppSession::Config::Config(std::shared_ptr<realm::app::GenericNetworkTransport> t)
545
    : transport(t)
1,890✔
546
{
1,890✔
547
}
1,890✔
548

549
OfflineAppSession::OfflineAppSession(OfflineAppSession::Config config)
550
    : m_transport(std::move(config.transport))
1,897✔
551
    , m_delete_storage(config.delete_storage)
1,897✔
552
{
1,897✔
553
    REALM_ASSERT(m_transport);
1,897✔
554
    set_app_config_defaults(m_app_config, m_transport);
1,897✔
555

556
    if (config.storage_path) {
1,897✔
557
        m_base_file_path = *config.storage_path;
14✔
558
        util::try_make_dir(m_base_file_path);
14✔
559
    }
14✔
560
    else {
1,883✔
561
        m_base_file_path = util::make_temp_dir();
1,883✔
562
    }
1,883✔
563

564
    m_app_config.base_file_path = m_base_file_path;
1,897✔
565
    m_app_config.metadata_mode = config.metadata_mode;
1,897✔
566
    if (config.base_url) {
1,897✔
567
        m_app_config.base_url = *config.base_url;
21✔
568
    }
21✔
569
    if (config.app_id) {
1,897✔
570
        m_app_config.app_id = *config.app_id;
×
571
    }
×
572
    m_app_config.sync_client_config.socket_provider = config.socket_provider;
1,897✔
573
    m_app = app::App::get_app(app::App::CacheMode::Disabled, m_app_config);
1,897✔
574
}
1,897✔
575

576
OfflineAppSession::~OfflineAppSession()
577
{
1,897✔
578
    if (util::File::exists(m_base_file_path) && m_delete_storage) {
1,897✔
579
        try {
1,890✔
580
            m_app->sync_manager()->tear_down_for_testing();
1,890✔
581
            util::try_remove_dir_recursive(m_base_file_path);
1,890✔
582
        }
1,890✔
583
        catch (const std::exception& ex) {
1,890✔
584
            std::cerr << ex.what() << "\n";
×
585
        }
×
586
        app::App::clear_cached_apps();
1,890✔
587
    }
1,890✔
588
}
1,897✔
589

590
std::shared_ptr<realm::app::User> OfflineAppSession::make_user() const
591
{
1,863✔
592
    create_user_and_log_in(app());
1,863✔
593
    return app()->current_user();
1,863✔
594
}
1,863✔
595

596
#endif // REALM_APP_SERVICES
597
#endif // REALM_ENABLE_SYNC
598

599
#if REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
600
// MARK: - TsanNotifyWorker
601
// A helper which synchronously runs on_change() on a fixed background thread
602
// so that ThreadSanitizer can potentially detect issues
603
// This deliberately uses an unsafe spinlock for synchronization to ensure that
604
// the code being tested has to supply all required safety
605
static class TsanNotifyWorker {
606
public:
607
    TsanNotifyWorker()
608
    {
609
        m_thread = std::thread([&] {
610
            work();
611
        });
612
    }
613

614
    void work()
615
    {
616
        while (true) {
617
            auto value = m_signal.load(std::memory_order_relaxed);
618
            if (value == 0 || value == 1)
619
                continue;
620
            if (value == 2)
621
                return;
622

623
            if (value & 1) {
624
                // Synchronize on the first handover of a given coordinator.
625
                value &= ~1;
626
                m_signal.load();
627
            }
628

629
            auto c = reinterpret_cast<_impl::RealmCoordinator*>(value);
630
            c->on_change();
631
            m_signal.store(1, std::memory_order_relaxed);
632
        }
633
    }
634

635
    ~TsanNotifyWorker()
636
    {
637
        m_signal = 2;
638
        m_thread.join();
639
    }
640

641
    void on_change(const std::shared_ptr<_impl::RealmCoordinator>& c)
642
    {
643
        auto& it = m_published_coordinators[c.get()];
644
        if (it.lock()) {
645
            m_signal.store(reinterpret_cast<uintptr_t>(c.get()), std::memory_order_relaxed);
646
        }
647
        else {
648
            // Synchronize on the first handover of a given coordinator.
649
            it = c;
650
            m_signal = reinterpret_cast<uintptr_t>(c.get()) | 1;
651
        }
652

653
        while (m_signal.load(std::memory_order_relaxed) != 1)
654
            ;
655
    }
656

657
private:
658
    std::atomic<uintptr_t> m_signal{0};
659
    std::thread m_thread;
660
    std::map<_impl::RealmCoordinator*, std::weak_ptr<_impl::RealmCoordinator>> m_published_coordinators;
661
} s_worker;
662

663
void on_change_but_no_notify(Realm& realm)
664
{
665
    s_worker.on_change(_impl::RealmCoordinator::get_existing_coordinator(realm.config().path));
666
}
667

668
void advance_and_notify(Realm& realm)
669
{
670
    on_change_but_no_notify(realm);
671
    realm.notify();
672
}
673

674
#else // REALM_HAVE_CLANG_FEATURE(thread_sanitizer)
675

676
void on_change_but_no_notify(Realm& realm)
677
{
14,230✔
678
    _impl::RealmCoordinator::get_coordinator(realm.config().path)->on_change();
14,230✔
679
}
14,230✔
680

681
void advance_and_notify(Realm& realm)
682
{
8,660✔
683
    on_change_but_no_notify(realm);
8,660✔
684
    realm.notify();
8,660✔
685
}
8,660✔
686
#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