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

realm / realm-core / thomas.goyne_416

12 Jun 2024 07:15PM UTC coverage: 90.941% (-0.006%) from 90.947%
thomas.goyne_416

Pull #7796

Evergreen

tgoyne
Make async_open_realm() return StatusWith<std::shared_ptr<Realm>>

This simplifies a lot of test code and eliminates some cases where the Realm
was being opened on a background thread, which is unsupported on linux.
Pull Request #7796: RCORE-2160 Make upload completion reporting multiprocess-compatible

102098 of 180310 branches covered (56.62%)

205 of 210 new or added lines in 10 files covered. (97.62%)

73 existing lines in 15 files now uncovered.

214551 of 235923 relevant lines covered (90.94%)

5646939.61 hits per line

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

97.08
/test/test_bplus_tree.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 "testsettings.hpp"
20

21
#include "realm.hpp"
22

23
#include "test.hpp"
24

25
#include <chrono>
26
// #include <valgrind/callgrind.h>
27

28
#ifndef CALLGRIND_START_INSTRUMENTATION
29
#define CALLGRIND_START_INSTRUMENTATION
30
#endif
31

32
#ifndef CALLGRIND_STOP_INSTRUMENTATION
33
#define CALLGRIND_STOP_INSTRUMENTATION
34
#endif
35

36
using namespace realm;
37
using namespace realm::test_util;
38
using namespace std::chrono;
39

40
// Test independence and thread-safety
41
// -----------------------------------
42
//
43
// All tests must be thread safe and independent of each other. This
44
// is required because it allows for both shuffling of the execution
45
// order and for parallelized testing.
46
//
47
// In particular, avoid using std::rand() since it is not guaranteed
48
// to be thread safe. Instead use the API offered in
49
// `test/util/random.hpp`.
50
//
51
// All files created in tests must use the TEST_PATH macro (or one of
52
// its friends) to obtain a suitable file system path. See
53
// `test/util/test_path.hpp`.
54
//
55
//
56
// Debugging and the ONLY() macro
57
// ------------------------------
58
//
59
// A simple way of disabling all tests except one called `Foo`, is to
60
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
61
// test suite. Note that you can also use filtering by setting the
62
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
63
// this.
64
//
65
// Another way to debug a particular test, is to copy that test into
66
// `experiments/testcase.cpp` and then run `sh build.sh
67
// check-testcase` (or one of its friends) from the command line.
68

69
#ifdef TEST_BPLUS_TREE
70

71
TEST(BPlusTree_Integer)
72
{
2✔
73
    BPlusTree<Int> tree(Allocator::get_default());
2✔
74

75
    CHECK_EQUAL(tree.size(), 0);
2✔
76

77
    tree.create();
2✔
78

79
    tree.add(5);
2✔
80
    CHECK_EQUAL(tree.get(0), 5);
2✔
81

82
    for (int i = 0; i < 16; i++) {
34✔
83
        tree.add(i);
32✔
84
    }
32✔
85
    CHECK_EQUAL(tree.get(1), 0);
2✔
86
    CHECK_EQUAL(tree.get(10), 9);
2✔
87
    CHECK_EQUAL(tree.find_first(7), 8);
2✔
88
    tree.erase(0);
2✔
89
    CHECK_EQUAL(tree.find_first(7), 7);
2✔
90
    CHECK_EQUAL(tree.find_first(100), realm::npos);
2✔
91

92
    std::vector<Int> all = tree.get_all();
2✔
93
    size_t sz = tree.size();
2✔
94
    CHECK_EQUAL(all.size(), sz);
2✔
95
    for (size_t i = 0; i < sz; i++) {
34✔
96
        CHECK_EQUAL(tree.get(i), all[i]);
32✔
97
    }
32✔
98

99
    while (sz) {
34✔
100
        sz--;
32✔
101
        tree.erase(sz);
32✔
102
    }
32✔
103
    tree.destroy();
2✔
104
}
2✔
105

106
TEST(BPlusTree_Timestamp)
107
{
2✔
108
    BPlusTree<Timestamp> tree(Allocator::get_default());
2✔
109

110
    tree.create();
2✔
111

112
    tree.add(Timestamp(5, 2));
2✔
113
    tree.add(Timestamp(7, 0));
2✔
114
    tree.add(Timestamp(7, 3));
2✔
115
    CHECK_EQUAL(tree.get(0), Timestamp(5, 2));
2✔
116
    CHECK_EQUAL(tree.find_first(Timestamp(7, 3)), 2);
2✔
117

118
    tree.clear();
2✔
119
    CHECK_EQUAL(tree.size(), 0);
2✔
120

121
    tree.destroy();
2✔
122
}
2✔
123

124
TEST(BPlusTree_Fuzz)
125
{
2✔
126
    const size_t iters = 500;
2✔
127
    std::vector<std::string> ref_arr;
2✔
128
    BPlusTree<StringData> tree(Allocator::get_default());
2✔
129

130
    tree.create();
2✔
131

132
    for (size_t iter = 0; iter < iters; iter++) {
1,002✔
133

134
        // Add
135
        if (fastrand(100) < ((iter < iters / 2) ? 60 : 10)) {
1,000✔
136
            std::string str = "foo ";
304✔
137
            str += util::to_string(iter);
304✔
138
            tree.add(str);
304✔
139
            ref_arr.push_back(str);
304✔
140
        }
304✔
141

142
        // Erase
143
        if (fastrand(100) < ((iter < iters / 2) ? 40 : 90) && tree.size() > 0) {
1,000✔
144
            size_t r = size_t(fastrand(tree.size() - 1));
628✔
145
            tree.erase(r);
628✔
146
            ref_arr.erase(ref_arr.begin() + r);
628✔
147
        }
628✔
148

149
        // Insert
150
        if (fastrand(100) < ((iter < iters / 2) ? 60 : 10)) {
1,000✔
151
            size_t r = size_t(fastrand(tree.size()));
324✔
152
            std::string str = "baa ";
324✔
153
            str += util::to_string(iter);
324✔
154
            tree.insert(r, str);
324✔
155
            ref_arr.insert(ref_arr.begin() + r, str);
324✔
156
        }
324✔
157

158
        // Set
159
        if (fastrand(100) < 20 && tree.size() > 0) {
1,000✔
160
            size_t r = size_t(fastrand(tree.size() - 1));
187✔
161
            std::string str = "hello cruel world ";
187✔
162
            str += util::to_string(iter);
187✔
163
            tree.set(r, str);
187✔
164
            ref_arr[r] = str;
187✔
165
        }
187✔
166

167
        size_t sz = tree.size();
1,000✔
168
        CHECK_EQUAL(sz, ref_arr.size());
1,000✔
169

170
        for (size_t i = 0; i < sz; i++) {
78,420✔
171
            CHECK_EQUAL(tree.get(i), ref_arr[i]);
77,420✔
172
        }
77,420✔
173
    }
1,000✔
174

175
    size_t sz = tree.size();
2✔
176
    while (sz) {
2✔
UNCOV
177
        tree.erase(sz - 1);
×
UNCOV
178
        ref_arr.pop_back();
×
UNCOV
179
        sz--;
×
UNCOV
180
        CHECK_EQUAL(sz, tree.size());
×
UNCOV
181
        for (size_t i = 0; i < sz; i++) {
×
UNCOV
182
            CHECK_EQUAL(tree.get(i), ref_arr[i]);
×
UNCOV
183
        }
×
UNCOV
184
    }
×
185

186
    tree.destroy();
2✔
187
}
2✔
188

189

190
TEST(BPlusTree_FuzzBinary)
191
{
2✔
192
    SHARED_GROUP_TEST_PATH(path);
2✔
193
    auto hist = make_in_realm_history();
2✔
194
    DBRef db = DB::create(*hist, path);
2✔
195
    const size_t iters = 2000;
2✔
196
    std::vector<std::string> ref_arr;
2✔
197

198
    auto tr = db->start_write();
2✔
199
    auto table = tr->add_table("table");
2✔
200
    auto col = table->add_column_list(type_Binary, "bin");
2✔
201
    table->create_object();
2✔
202
    tr->commit_and_continue_as_read();
2✔
203

204
    for (size_t iter = 0; iter < iters; iter++) {
4,002✔
205

206
        tr->promote_to_write();
4,000✔
207
        auto list = table->begin()->get_list<Binary>(col);
4,000✔
208

209
        // Add
210
        uint64_t nb_add = fastrand(10);
4,000✔
211
        for (uint64_t i = 0; i < nb_add; i++) {
23,784✔
212
            std::string str = "foo ";
19,784✔
213
            str += util::to_string(i);
19,784✔
214
            list.add(BinaryData(str));
19,784✔
215
            ref_arr.push_back(str);
19,784✔
216
        }
19,784✔
217

218
        // Erase
219
        uint64_t nb_erase = (list.size() > 2) ? fastrand(list.size() - 2) : 0;
4,000✔
220
        for (uint64_t i = 0; i < nb_erase; i++) {
23,770✔
221
            list.remove(0);
19,770✔
222
            ref_arr.erase(ref_arr.begin());
19,770✔
223
        }
19,770✔
224

225
        tr->commit_and_continue_as_read();
4,000✔
226
        tr->verify();
4,000✔
227

228
        size_t sz = list.size();
4,000✔
229
        CHECK_EQUAL(sz, ref_arr.size());
4,000✔
230

231
        for (size_t i = 0; i < sz; i++) {
31,691✔
232
            CHECK_EQUAL(list.get(i), BinaryData(ref_arr[i]));
27,691✔
233
        }
27,691✔
234
    }
4,000✔
235
}
2✔
236

237

238
// This test is designed to work with a node size of 4
239
TEST(BPlusTree_Initialization)
240
{
2✔
241
    Array parent_array(Allocator::get_default());
2✔
242
    parent_array.create(NodeHeader::type_HasRefs);
2✔
243
    parent_array.add(0);
2✔
244

245
    BPlusTree<Int> tree(Allocator::get_default());
2✔
246
    tree.set_parent(&parent_array, 0);
2✔
247
    tree.create();
2✔
248
    CHECK_EQUAL(tree.get_ref(), parent_array.get_as_ref(0));
2✔
249

250
    tree.add(5);
2✔
251
    CHECK_EQUAL(tree.get(0), 5);
2✔
252

253
    BPlusTree<Int> another_tree(Allocator::get_default());
2✔
254
    another_tree.set_parent(&parent_array, 0);
2✔
255

256
    // another_tree initialized from scratch with a single leaf
257
    another_tree.init_from_parent();
2✔
258

259
    CHECK_EQUAL(another_tree.get(0), 5);
2✔
260

261
    tree.erase(0);
2✔
262
    // expand tree
263
    for (int i = 0; i < 10; i++) {
22✔
264
        tree.add(i);
20✔
265
    }
20✔
266

267
    // another_tree re-initialized with an inner node - replace accessor
268
    another_tree.init_from_parent();
2✔
269
    CHECK_EQUAL(another_tree.get(5), 5);
2✔
270

271
    // expand tree further
272
    for (int i = 0; i < 10; i++) {
22✔
273
        tree.add(i + 10);
20✔
274
    }
20✔
275

276
    // another_tree re-initialized with an inner node - reuse accessor
277
    another_tree.init_from_parent();
2✔
278
    CHECK_EQUAL(another_tree.get(15), 15);
2✔
279
    CHECK_EQUAL(another_tree.size(), 20);
2✔
280

281
    tree.clear();
2✔
282

283
    another_tree.init_from_parent();
2✔
284
    CHECK_EQUAL(another_tree.size(), 0);
2✔
285

286
    tree.destroy();
2✔
287
    parent_array.destroy();
2✔
288
}
2✔
289

290
TEST(BPlusTree_Destruction)
291
{
2✔
292
    BPlusTree<Int> tree(Allocator::get_default());
2✔
293
    tree.create();
2✔
294

295
    for (int64_t i = 0; i < 100; i++) {
202✔
296
        tree.add(i);
200✔
297
    }
200✔
298
    for (int64_t i = 0; i < 100; i++) {
202✔
299
        CHECK_EQUAL(tree.get(0), i);
200✔
300
        tree.erase(0);
200✔
301
        tree.verify();
200✔
302
    }
200✔
303
    tree.destroy();
2✔
304
}
2✔
305

306
NONCONCURRENT_TEST(BPlusTree_Performance)
307
{
2✔
308
    // We try to optimize for add and sequential lookup
309
    int nb_rows = 5000;
2✔
310
    BPlusTree<Int> tree(Allocator::get_default());
2✔
311

312
    tree.create();
2✔
313

314
    CALLGRIND_START_INSTRUMENTATION;
2✔
315

316
    std::cout << nb_rows << " BPlusTree - sequential" << std::endl;
2✔
317

318
    {
2✔
319
        auto t1 = steady_clock::now();
2✔
320

321
        for (int i = 0; i < nb_rows; i++) {
10,002✔
322
            tree.add(i);
10,000✔
323
        }
10,000✔
324

325
        auto t2 = steady_clock::now();
2✔
326
        std::cout << "   insertion time: " << duration_cast<nanoseconds>(t2 - t1).count() / nb_rows << " ns/row"
2✔
327
                  << std::endl;
2✔
328

329
        CHECK_EQUAL(tree.size(), nb_rows);
2✔
330
    }
2✔
331

332
    {
2✔
333
        auto t1 = steady_clock::now();
2✔
334

335
        for (int i = 0; i < nb_rows; i++) {
10,002✔
336
            CHECK_EQUAL(i, tree.get(i));
10,000✔
337
        }
10,000✔
338

339
        auto t2 = steady_clock::now();
2✔
340

341
        std::cout << "   lookup time   : " << duration_cast<nanoseconds>(t2 - t1).count() / nb_rows << " ns/row"
2✔
342
                  << std::endl;
2✔
343
    }
2✔
344

345
    CALLGRIND_STOP_INSTRUMENTATION;
2✔
346

347
    tree.destroy();
2✔
348
}
2✔
349

350
TEST(BinaryColumn_get_at)
351
{
2✔
352
    BinaryData read;
2✔
353
    size_t get_pos;
2✔
354

355
    std::string hello = "Hello, world";
2✔
356
    std::string very_lazy_fox =
2✔
357
        "The lazy fox jumped over the quick brown dog. The quick fox jumped over the lazy brown dog. ";
2✔
358

359
    BinaryColumn c(Allocator::get_default());
2✔
360
    c.create();
2✔
361

362
    c.add(BinaryData());
2✔
363
    c.add(BinaryData(hello));
2✔
364

365
    // First one should be NULL
366
    CHECK(c.get(0).is_null());
2✔
367
    get_pos = 0;
2✔
368
    read = c.get_at(0, get_pos);
2✔
369
    CHECK(read.is_null());
2✔
370

371
    get_pos = 0;
2✔
372
    read = c.get_at(1, get_pos);
2✔
373
    CHECK_EQUAL(read.size(), hello.size());
2✔
374
    CHECK_EQUAL(std::string(read.data(), read.size()), hello);
2✔
375

376
    BinaryIterator it0;
2✔
377
    read = it0.get_next();
2✔
378
    CHECK(read.is_null());
2✔
379

380
    BinaryIterator it1(&c, 1);
2✔
381
    read = it1.get_next();
2✔
382
    CHECK_EQUAL(std::string(read.data(), read.size()), hello);
2✔
383
    read = it1.get_next();
2✔
384
    CHECK(read.is_null());
2✔
385

386
    BinaryIterator it2(c.get(1));
2✔
387
    read = it2.get_next();
2✔
388
    CHECK_EQUAL(std::string(read.data(), read.size()), hello);
2✔
389
    read = it2.get_next();
2✔
390
    CHECK(read.is_null());
2✔
391

392
    // Check BigBlob
393
    c.add(BinaryData(very_lazy_fox));
2✔
394

395
    get_pos = 0;
2✔
396
    read = c.get_at(2, get_pos);
2✔
397
    CHECK_EQUAL(read.size(), very_lazy_fox.size());
2✔
398
    CHECK_EQUAL(std::string(read.data(), read.size()), very_lazy_fox);
2✔
399

400
    // Split root
401
    for (unsigned i = 0; i < REALM_MAX_BPNODE_SIZE; i++) {
2,002✔
402
        c.add(BinaryData());
2,000✔
403
    }
2,000✔
404

405
    get_pos = 0;
2✔
406
    read = c.get_at(1, get_pos);
2✔
407
    CHECK_EQUAL(read.size(), hello.size());
2✔
408
    CHECK_EQUAL(std::string(read.data(), read.size()), hello);
2✔
409

410
    c.destroy();
2✔
411
}
2✔
412

413
TEST(BPlusTree_LeafCache)
414
{
2✔
415
    BPlusTree<Int> tree(Allocator::get_default());
2✔
416
    tree.create();
2✔
417
    for (int i = 0; i < 1001; i++) {
2,004✔
418
        tree.add(i);
2,002✔
419
    }
2,002✔
420

421
    CHECK_EQUAL(tree.get(0), 0); // Caches leaf 0..1000
2✔
422

423
    tree.clear(); // After the fix, the cache is invalidated here
2✔
424

425
    for (int i = 0; i < 1001; i++) {
2,004✔
426
        tree.add(i);
2,002✔
427
    }
2,002✔
428
    for (int i = 0; i < 1001; i++) {
2,004✔
429
        CHECK_EQUAL(tree.get(i), i);
2,002✔
430
    }
2,002✔
431
    tree.destroy();
2✔
432
}
2✔
433

434
TEST(BPlusTree_UpgradeFromArray)
435
{
2✔
436
    // This test is only effective when node size is 4
437
    for (auto sz : {15, 16, 17, 65, 256, 257}) {
12✔
438
        Array arr(Allocator::get_default());
12✔
439
        arr.create(Node::type_Normal, false, 0, 0);
12✔
440

441
        for (int i = 0; i < sz; i++) {
1,264✔
442
            arr.add(i);
1,252✔
443
        }
1,252✔
444

445
        BPlusTree<Int> tree(Allocator::get_default());
12✔
446
        tree.init_from_ref(arr.get_ref());
12✔
447
        tree.split_if_needed();
12✔
448

449
        tree.add(100);
12✔
450
        CHECK_EQUAL(tree.size(), sz + 1);
12✔
451
        CHECK_EQUAL(tree.get(sz), 100);
12✔
452
        tree.verify();
12✔
453

454
        tree.destroy();
12✔
455
    }
12✔
456
}
2✔
457

458
#endif // TEST_BPLUS_TREE
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