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

realm / realm-core / nicola.cabiddu_1040

26 Sep 2023 05:08PM UTC coverage: 91.056% (-1.9%) from 92.915%
nicola.cabiddu_1040

Pull #6766

Evergreen

nicola-cab
several fixes and final client reset algo for collection in mixed
Pull Request #6766: Client Reset for collections in mixed / nested collections

97128 of 178458 branches covered (0.0%)

1524 of 1603 new or added lines in 5 files covered. (95.07%)

4511 existing lines in 109 files now uncovered.

236619 of 259862 relevant lines covered (91.06%)

7169640.31 hits per line

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

99.48
/test/test_links.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
#ifdef TEST_LINKS
21

22

23
#include <realm.hpp>
24
#include <realm/util/file.hpp>
25
#include <realm/array_key.hpp>
26

27
#include "test.hpp"
28

29
using namespace realm;
30
using namespace realm::util;
31
using namespace realm::test_util;
32

33
namespace {
34

35
enum Days { Mon, Tue, Wed, Thu, Fri, Sat, Sun };
36

37
template <class T>
38
void test_table_add_columns(T t)
39
{
12✔
40
    t->add_column(type_String, "first");
12✔
41
    t->add_column(type_Int, "second");
12✔
42
    t->add_column(type_Bool, "third");
12✔
43
    t->add_column(type_Int, "fourth");
12✔
44
}
12✔
45

46
} // Anonymous namespace
47

48
TEST(Links_Columns)
49
{
2✔
50
    // Test adding and removing columns with links
1✔
51

1✔
52
    Group group;
2✔
53

1✔
54
    TableRef table1 = group.add_table("table1");
2✔
55
    TableRef table2 = group.add_table("table2");
2✔
56

1✔
57
    // table1 can link to table2
1✔
58
    table2->add_column(*table1, "link");
2✔
59

1✔
60
    // add some more columns to table1 and table2
1✔
61
    auto col_1 = table1->add_column(type_String, "col1");
2✔
62
    table2->add_column(type_String, "col2");
2✔
63

1✔
64
    // Use a key where the first has the the second most significant bit set.
1✔
65
    // When this is shifted up and down again, the most significant bit must
1✔
66
    // still be 0.
1✔
67
    ObjKeys table_1_keys({5, 1LL << 62});
2✔
68
    ObjKeys table_2_keys({2, 7});
2✔
69
    // add some rows
1✔
70
    table1->create_objects(table_1_keys);
2✔
71
    table2->create_objects(table_2_keys);
2✔
72

1✔
73
    table1->get_object(table_1_keys[0]).set<String>(col_1, "string1");
2✔
74
    auto col_link2 = table1->add_column(*table2, "link");
2✔
75

1✔
76
    // set some links
1✔
77
    table1->get_object(table_1_keys[0]).set(col_link2, table_2_keys[1]);
2✔
78
    Obj obj1 = table2->get_object(table_2_keys[1]);
2✔
79
    table1->get_object(table_1_keys[1]).set(col_link2, table_2_keys[0]);
2✔
80
    Obj obj0 = table2->get_object(table_2_keys[0]);
2✔
81

1✔
82
    CHECK_EQUAL(1, obj0.get_backlink_count(*table1, col_link2));
2✔
83
    CHECK_EQUAL(table_1_keys[1], obj0.get_backlink(*table1, col_link2, 0));
2✔
84
    CHECK_EQUAL(1, obj1.get_backlink_count(*table1, col_link2));
2✔
85
    CHECK_EQUAL(table_1_keys[0], obj1.get_backlink(*table1, col_link2, 0));
2✔
86

1✔
87
    auto tv = obj1.get_backlink_view(table1, col_link2);
2✔
88
    CHECK_EQUAL(tv.size(), 1);
2✔
89

1✔
90
    // remove a column (moving link column back)'
1✔
91
    table1->remove_column(col_1);
2✔
92
    CHECK_EQUAL(1, table2->get_object(table_2_keys[1]).get_backlink_count(*table1, col_link2));
2✔
93
    CHECK_EQUAL(table_1_keys[0], table2->get_object(table_2_keys[1]).get_backlink(*table1, col_link2, 0));
2✔
94

1✔
95
    table1->remove_column(col_link2);
2✔
96
    tv.sync_if_needed();
2✔
97
    CHECK_EQUAL(tv.size(), 0);
2✔
98
}
2✔
99

100

101
TEST(Links_Basic)
102
{
2✔
103
    GROUP_TEST_PATH(path);
2✔
104
    ObjKey key_origin;
2✔
105
    ObjKey key_target;
2✔
106
    ColKey col_link;
2✔
107

1✔
108
    // Test basic link operations
1✔
109
    {
2✔
110
        Group group;
2✔
111

1✔
112
        auto table1 = group.add_table("table1");
2✔
113
        table1->add_column(type_String, "first");
2✔
114
        table1->add_column(type_Int, "second");
2✔
115
        table1->add_column(type_Bool, "third");
2✔
116
        table1->add_column(type_Int, "fourth");
2✔
117

1✔
118
        Obj obj0 = table1->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
119
        Obj obj1 = table1->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
120
        Obj obj2 = table1->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
121
        ObjKey key0 = obj0.get_key();
2✔
122
        ObjKey key1 = obj1.get_key();
2✔
123
        ObjKey key2 = obj2.get_key();
2✔
124

1✔
125
        // create table with links to table1
1✔
126
        TableRef table2 = group.add_table("table2");
2✔
127
        col_link = table2->add_column(*table1, "link");
2✔
128
        CHECK_EQUAL(table1, table2->get_link_target(col_link));
2✔
129

1✔
130
        // add a few links
1✔
131
        Obj obj3 = table2->create_object(ObjKey{}, {{col_link, key1}});
2✔
132
        Obj obj4 = table2->create_object(ObjKey{}, {{col_link, key0}});
2✔
133
        ObjKey key3 = obj3.get_key();
2✔
134
        key_origin = obj4.get_key();
2✔
135

1✔
136
        // Verify that links were set correctly
1✔
137
        ObjKey link3 = obj3.get<ObjKey>(col_link);
2✔
138
        ObjKey link4 = obj4.get<ObjKey>(col_link);
2✔
139
        CHECK_EQUAL(key1, link3);
2✔
140
        CHECK_EQUAL(key0, link4);
2✔
141

1✔
142
        // Verify backlinks
1✔
143
        CHECK_EQUAL(1, obj0.get_backlink_count(*table2, col_link));
2✔
144
        CHECK_EQUAL(key_origin, obj0.get_backlink(*table2, col_link, 0));
2✔
145
        CHECK_EQUAL(1, obj1.get_backlink_count(*table2, col_link));
2✔
146
        CHECK_EQUAL(key3, obj1.get_backlink(*table2, col_link, 0));
2✔
147
        CHECK_EQUAL(0, obj2.get_backlink_count(*table2, col_link));
2✔
148

1✔
149
        // Change a link to point to a new location
1✔
150
        obj4.set(col_link, key2);
2✔
151

1✔
152
        link4 = obj4.get<ObjKey>(col_link);
2✔
153
        CHECK_EQUAL(key2, link4);
2✔
154
        CHECK_EQUAL(0, obj0.get_backlink_count(*table2, col_link));
2✔
155
        CHECK_EQUAL(1, obj2.get_backlink_count(*table2, col_link));
2✔
156
        CHECK_EQUAL(key_origin, obj2.get_backlink(*table2, col_link, 0));
2✔
157

1✔
158
        // Delete an object.
1✔
159
        table2->remove_object(key3);
2✔
160

1✔
161
        // Verify that delete went correctly
1✔
162
        CHECK_EQUAL(1, table2->size());
2✔
163
        CHECK_EQUAL(key2, obj4.get<ObjKey>(col_link));
2✔
164

1✔
165
        CHECK_EQUAL(0, obj0.get_backlink_count(*table2, col_link));
2✔
166
        CHECK_EQUAL(0, obj1.get_backlink_count(*table2, col_link));
2✔
167
        CHECK_EQUAL(1, obj2.get_backlink_count(*table2, col_link));
2✔
168
        CHECK_EQUAL(key_origin, obj2.get_backlink(*table2, col_link, 0));
2✔
169

1✔
170
        // Nullify a link
1✔
171
        obj4.set(col_link, null_key);
2✔
172
        CHECK(obj4.is_null(col_link));
2✔
173
        CHECK_EQUAL(0, obj2.get_backlink_count(*table2, col_link));
2✔
174

1✔
175
        // Add a new row to target table and verify that backlinks are
1✔
176
        // tracked for it as well
1✔
177
        Obj obj5 = table1->create_object().set_all("test4", 4, false, int64_t(Thu));
2✔
178
        key_target = obj5.get_key();
2✔
179
        CHECK_EQUAL(0, obj5.get_backlink_count(*table2, col_link));
2✔
180

1✔
181
        obj4.set(col_link, key_target);
2✔
182
        CHECK_EQUAL(1, obj5.get_backlink_count(*table2, col_link));
2✔
183

1✔
184
        group.write(path);
2✔
185
    }
2✔
186

1✔
187
    // Reopen same group from disk
1✔
188
    {
2✔
189
        Group group(path);
2✔
190

1✔
191
        TableRef table1 = group.get_table("table1");
2✔
192
        TableRef table2 = group.get_table("table2");
2✔
193

1✔
194
        // Verify that we are pointing to the right table
1✔
195
        CHECK_EQUAL(table1, table2->get_link_target(col_link));
2✔
196

1✔
197
        // Verify that links are still correct
1✔
198
        CHECK_EQUAL(key_target, table2->get_object(key_origin).get<ObjKey>(col_link));
2✔
199
    }
2✔
200
}
2✔
201

202
TEST(Group_LinksToSameTable)
203
{
2✔
204
    Group g;
2✔
205
    TableRef table = g.add_table("target");
2✔
206

1✔
207
    table->add_column(type_Int, "integers", true);
2✔
208
    auto link_col = table->add_column(*table, "links");
2✔
209

1✔
210
    // 3 rows linked together in a list
1✔
211
    std::vector<ObjKey> keys;
2✔
212
    table->create_objects(3, keys);
2✔
213
    table->get_object(keys[0]).set(link_col, keys[1]);
2✔
214
    table->get_object(keys[1]).set(link_col, keys[2]);
2✔
215
    table->remove_object(keys[0]);
2✔
216
    CHECK_EQUAL(table->size(), 2);
2✔
217
    CHECK_EQUAL(table->get_object(keys[1]).get_backlink_count(*table, link_col), 0);
2✔
218
    table->remove_object(keys[2]);
2✔
219
    CHECK_EQUAL(table->size(), 1);
2✔
220
    CHECK(table->get_object(keys[1]).is_null(link_col));
2✔
221
}
2✔
222

223
TEST(Links_SetLinkLogicErrors)
224
{
2✔
225
    Group group;
2✔
226
    TableRef origin = group.add_table("origin");
2✔
227
    TableRef target = group.add_table("target");
2✔
228
    auto col0 = origin->add_column(*target, "a");
2✔
229
    origin->add_column(type_Int, "b");
2✔
230
    Obj obj = origin->create_object();
2✔
231
    target->create_object(ObjKey(10));
2✔
232

1✔
233
    // FIXME: Not really possible with column keys?
1✔
234
    // CHECK_LOGIC_ERROR(obj.set(2, Key(10)), ErrorCodes::KeyNotFound);
1✔
235
    CHECK_LOGIC_ERROR(obj.set(col0, ObjKey(5)), ErrorCodes::KeyNotFound);
2✔
236

1✔
237
    // FIXME: Must also check that Logic::type_mismatch is thrown on column type mismatch, but Table::set_link() does
1✔
238
    // not properly check it yet.
1✔
239

1✔
240
    origin->remove_object(obj.get_key());
2✔
241
    CHECK_THROW(obj.set(col0, ObjKey(10)), KeyNotFound);
2✔
242

1✔
243
    group.remove_table("origin");
2✔
244
    CHECK_THROW(obj.set(col0, ObjKey(10)), realm::InvalidTableRef);
2✔
245
}
2✔
246

247

248
TEST(Links_Deletes)
249
{
2✔
250
    Group group;
2✔
251

1✔
252
    auto table1 = group.add_table("table1");
2✔
253
    table1->add_column(type_String, "first");
2✔
254
    table1->add_column(type_Int, "second");
2✔
255
    table1->add_column(type_Bool, "third");
2✔
256
    table1->add_column(type_Int, "fourth");
2✔
257

1✔
258
    // create table with links to table1
1✔
259
    TableRef table2 = group.add_table("table2");
2✔
260
    auto col_link = table2->add_column(*TableRef(table1), "link");
2✔
261
    CHECK_EQUAL(table1, table2->get_link_target(col_link));
2✔
262

1✔
263
    Obj obj0 = table1->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
264
    Obj obj1 = table1->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
265
    Obj obj2 = table1->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
266
    ObjKey key0 = obj0.get_key();
2✔
267
    ObjKey key1 = obj1.get_key();
2✔
268
    ObjKey key2 = obj2.get_key();
2✔
269

1✔
270
    {
2✔
271
        // add a few links
1✔
272
        Obj obj3 = table2->create_object().set(col_link, key1);
2✔
273
        Obj obj4 = table2->create_object().set(col_link, key0);
2✔
274
        ObjKey key3 = obj3.get_key();
2✔
275
        ObjKey key4 = obj4.get_key();
2✔
276

1✔
277
        table2->remove_object(key3);
2✔
278
        table2->remove_object(key4);
2✔
279
    }
2✔
280
    CHECK(table2->is_empty());
2✔
281
    CHECK_EQUAL(0, obj0.get_backlink_count(*table2, col_link));
2✔
282
    CHECK_EQUAL(0, obj1.get_backlink_count(*table2, col_link));
2✔
283

1✔
284
    // add links again
1✔
285
    table2->create_object().set(col_link, key1);
2✔
286
    table2->create_object().set(col_link, key0);
2✔
287

1✔
288
    // remove all rows in target table
1✔
289
    table1->remove_object(key0);
2✔
290
    table1->remove_object(key1);
2✔
291
    table1->remove_object(key2);
2✔
292

1✔
293
    // verify that originating links was nullified
1✔
294
    for (auto o : *table2) {
4✔
295
        CHECK(o.is_null(col_link));
4✔
296
    }
4✔
297

1✔
298
    // add target rows again with links
1✔
299
    table1->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
300
    table1->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
301
    table1->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
302

1✔
303
    auto it = table1->begin();
2✔
304
    for (auto o : *table2) {
4✔
305
        o.set(col_link, it->get_key());
4✔
306
        ++it;
4✔
307
    }
4✔
308

1✔
309
    // clear entire table and make sure backlinks are removed as well
1✔
310
    table2->clear();
2✔
311
    for (auto o : *table1) {
6✔
312
        CHECK_EQUAL(0, o.get_backlink_count(*table2, col_link));
6✔
313
    }
6✔
314

1✔
315
    // add links again
1✔
316
    for (auto o : *table1) {
6✔
317
        table2->create_object().set(col_link, o.get_key());
6✔
318
    }
6✔
319

1✔
320
    // clear target table and make sure links are nullified
1✔
321
    table1->clear();
2✔
322
    for (auto o : *table2) {
6✔
323
        CHECK(o.is_null(col_link));
6✔
324
    }
6✔
325
}
2✔
326

327
TEST(Links_Multi)
328
{
2✔
329
    // Multiple links to same rows
1✔
330
    Group group;
2✔
331

1✔
332
    auto table1 = group.add_table("target");
2✔
333
    test_table_add_columns(table1);
2✔
334
    table1->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
335
    Obj obj1 = table1->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
336
    Obj obj2 = table1->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
337
    ObjKey key1 = obj1.get_key();
2✔
338
    ObjKey key2 = obj2.get_key();
2✔
339

1✔
340
    // create table with links to table1
1✔
341
    TableRef table2 = group.add_table("table2");
2✔
342
    auto col_link = table2->add_column(*TableRef(table1), "link");
2✔
343
    CHECK_EQUAL(table1, table2->get_link_target(col_link));
2✔
344

1✔
345
    // add a few links pointing to same row
1✔
346
    auto k0 = table2->create_object().set(col_link, key1).get_key();
2✔
347
    auto k1 = table2->create_object().set(col_link, key1).get_key();
2✔
348
    auto k2 = table2->create_object().set(col_link, key1).get_key();
2✔
349

1✔
350
    CHECK_EQUAL(3, obj1.get_backlink_count(*table2, col_link));
2✔
351
    CHECK_EQUAL(k0, obj1.get_backlink(*table2, col_link, 0));
2✔
352
    CHECK_EQUAL(k1, obj1.get_backlink(*table2, col_link, 1));
2✔
353
    CHECK_EQUAL(k2, obj1.get_backlink(*table2, col_link, 2));
2✔
354

1✔
355
    // nullify a link
1✔
356
    table2->get_object(k1).set_null(col_link);
2✔
357
    CHECK_EQUAL(2, obj1.get_backlink_count(*table2, col_link));
2✔
358
    CHECK_EQUAL(k0, obj1.get_backlink(*table2, col_link, 0));
2✔
359
    CHECK_EQUAL(k2, obj1.get_backlink(*table2, col_link, 1));
2✔
360

1✔
361
    // nullify one more to reduce to one link (test re-inlining)
1✔
362
    table2->get_object(k0).set_null(col_link);
2✔
363
    CHECK_EQUAL(1, obj1.get_backlink_count(*table2, col_link));
2✔
364
    CHECK_EQUAL(k2, obj1.get_backlink(*table2, col_link, 0));
2✔
365

1✔
366
    // re-add links
1✔
367
    table2->get_object(k0).set(col_link, k1);
2✔
368
    table2->get_object(k1).set(col_link, k1);
2✔
369

1✔
370
    // remove a row
1✔
371
    table2->remove_object(k0);
2✔
372
    CHECK_EQUAL(2, obj1.get_backlink_count(*table2, col_link));
2✔
373
    CHECK_EQUAL(k2, obj1.get_backlink(*table2, col_link, 0));
2✔
374
    CHECK_EQUAL(k1, obj1.get_backlink(*table2, col_link, 1));
2✔
375

1✔
376
    // add some more links and see that they get nullified when the target
1✔
377
    // is removed
1✔
378
    auto k3 = table2->create_object().set(col_link, key2).get_key();
2✔
379
    auto k4 = table2->create_object().set(col_link, key2).get_key();
2✔
380
    CHECK_EQUAL(2, obj2.get_backlink_count(*table2, col_link));
2✔
381

1✔
382
    obj1.remove();
2✔
383
    CHECK(table2->get_object(k1).is_null(col_link));
2✔
384
    CHECK(table2->get_object(k2).is_null(col_link));
2✔
385
    CHECK_NOT(table2->get_object(k3).is_null(col_link));
2✔
386
    CHECK_NOT(table2->get_object(k4).is_null(col_link));
2✔
387

1✔
388
    // remove all rows from target and verify that links get nullified
1✔
389
    table1->clear();
2✔
390
    for (auto o : *table2) {
8✔
391
        CHECK(o.is_null(col_link));
8✔
392
    }
8✔
393
}
2✔
394

395

396
TEST(Links_MultiToSame)
397
{
2✔
398
    Group group;
2✔
399

1✔
400
    auto table1 = group.add_table("target");
2✔
401
    test_table_add_columns(table1);
2✔
402
    Obj obj0 = table1->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
403
    ObjKey key0 = obj0.get_key();
2✔
404

1✔
405
    // create table with multiple links to table1
1✔
406
    TableRef table2 = group.add_table("table2");
2✔
407
    auto col_link1 = table2->add_column(*table1, "link1");
2✔
408
    auto col_link2 = table2->add_column(*table1, "link2");
2✔
409
    CHECK_EQUAL(table1, table2->get_link_target(col_link1));
2✔
410
    CHECK_EQUAL(table1, table2->get_link_target(col_link2));
2✔
411

1✔
412
    table2->create_object().set_all(key0, key0);
2✔
413
    CHECK_EQUAL(1, obj0.get_backlink_count(*table2, col_link1));
2✔
414
    CHECK_EQUAL(1, obj0.get_backlink_count(*table2, col_link2));
2✔
415

1✔
416
    table2->begin()->remove();
2✔
417
    CHECK_EQUAL(0, obj0.get_backlink_count(*table2, col_link1));
2✔
418
    CHECK_EQUAL(0, obj0.get_backlink_count(*table2, col_link2));
2✔
419
}
2✔
420

421
TEST(Links_LinkList_TableOps)
422
{
2✔
423
    Group group;
2✔
424

1✔
425
    auto target = group.add_table("target");
2✔
426
    target->add_column(type_String, "first");
2✔
427
    target->add_column(type_Int, "second");
2✔
428
    target->add_column(type_Bool, "third");
2✔
429
    target->add_column(type_Int, "fourth");
2✔
430

1✔
431
    // create table with links to table1
1✔
432
    TableRef origin = group.add_table("origin");
2✔
433
    auto col_link = origin->add_column_list(*TableRef(target), "links");
2✔
434
    CHECK_EQUAL(target, origin->get_link_target(col_link));
2✔
435

1✔
436
    target->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
437
    target->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
438
    target->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
439

1✔
440
    const Obj obj1 = origin->create_object();
2✔
441
    CHECK(obj1.get_list<ObjKey>(col_link).is_empty());
2✔
442
    CHECK_EQUAL(0, obj1.get_link_count(col_link));
2✔
443

1✔
444
    // add some more rows and test that they can be deleted
1✔
445
    origin->create_object();
2✔
446
    origin->create_object();
2✔
447
    origin->create_object();
2✔
448

1✔
449
    while (!origin->is_empty()) {
10✔
450
        origin->remove_object(origin->begin()->get_key());
8✔
451
    }
8✔
452

1✔
453
    // add some more rows and clear
1✔
454
    origin->create_object();
2✔
455
    origin->create_object();
2✔
456
    origin->create_object();
2✔
457
    origin->clear();
2✔
458
}
2✔
459

460

461
TEST(Links_LinkList_Construction)
462
{
2✔
463
    Group group;
2✔
464

1✔
465
    auto table1 = group.add_table("target");
2✔
466

1✔
467
    // create table with links to table1
1✔
468
    TableRef origin = group.add_table("origin");
2✔
469
    auto col_link = origin->add_column_list(*table1, "links");
2✔
470
    CHECK_EQUAL(table1, origin->get_link_target(col_link));
2✔
471

1✔
472
    ObjKeys target_keys({4, 5, 6});
2✔
473
    table1->create_objects(target_keys);
2✔
474

1✔
475
    auto links0 = origin->create_object().get_linklist(col_link);
2✔
476
    auto obj1 = origin->create_object();
2✔
477
    auto links1 = obj1.get_linklist(col_link);
2✔
478

1✔
479
    // add several links to a single linklist
1✔
480
    obj1.set_list_values(col_link, target_keys);
2✔
481

1✔
482
    CHECK_EQUAL(0, links0.size());
2✔
483
    CHECK_EQUAL(3, links1.size());
2✔
484

1✔
485
    LnkLst default_list; // Default constructed list
2✔
486
    CHECK_EQUAL(0, default_list.size());
2✔
487

1✔
488
    LnkLst list0(links0); // Constructed from empty list
2✔
489
    CHECK_EQUAL(0, list0.size());
2✔
490
    list0.add(target_keys[0]); // Ensure usability
2✔
491
    list0.clear();             // Make it empty again
2✔
492

1✔
493
    links1.size();        // call update_if_needed()
2✔
494
    LnkLst list1(links1); // Constructed from not empty list
2✔
495
    CHECK_EQUAL(3, list1.size());
2✔
496
    list1.clear();
2✔
497
    CHECK_EQUAL(0, list1.size());
2✔
498
    obj1.set_list_values(col_link, target_keys);
2✔
499

1✔
500
    LnkLst list2 = default_list; // Constructed from default object
2✔
501
    CHECK_EQUAL(0, list2.size());
2✔
502

1✔
503
    list1 = default_list; // Assignment from default
2✔
504
    CHECK_EQUAL(0, list1.size());
2✔
505

1✔
506
    list1 = links0; // Assignment from empty list
2✔
507
    CHECK_EQUAL(0, list1.size());
2✔
508
    list1.add(target_keys[0]);
2✔
509
    list1.clear();
2✔
510

1✔
511
    list2 = links1; // Assignment from non empty list
2✔
512
    CHECK_EQUAL(3, list2.size());
2✔
513
    list2.clear();
2✔
514
    CHECK_EQUAL(0, list2.size());
2✔
515
}
2✔
516

517
TEST(Links_LinkList_Basics)
518
{
2✔
519
    Group group;
2✔
520

1✔
521
    auto target = group.add_table("target");
2✔
522
    target->add_column(type_String, "first");
2✔
523
    target->add_column(type_Int, "second");
2✔
524
    target->add_column(type_Bool, "third");
2✔
525
    auto day_col = target->add_column(type_Int, "fourth");
2✔
526

1✔
527
    // create table with links to table1
1✔
528
    TableRef origin = group.add_table("origin");
2✔
529
    auto col_link = origin->add_column_list(*TableRef(target), "links");
2✔
530
    origin->add_column(type_Int, "integers"); // Make sure the link column is not the only column
2✔
531
    CHECK_EQUAL(target, origin->get_link_target(col_link));
2✔
532

1✔
533
    Obj obj0 = target->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
534
    Obj obj1 = target->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
535
    Obj obj2 = target->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
536
    ObjKey key0 = obj0.get_key();
2✔
537
    ObjKey key1 = obj1.get_key();
2✔
538
    ObjKey key2 = obj2.get_key();
2✔
539

1✔
540
    Obj obj3 = origin->create_object(ObjKey(0));
2✔
541
    ObjKey key3 = obj3.get_key();
2✔
542
    auto links = obj3.get_linklist(col_link);
2✔
543

1✔
544
    // add several links to a single linklist
1✔
545
    links.add(key2);
2✔
546
    links.add(key1);
2✔
547
    links.add(key0);
2✔
548

1✔
549
    CHECK_EQUAL(3, obj3.get_link_count(col_link));
2✔
550
    CHECK_EQUAL(key2, links.get(0));
2✔
551
    CHECK_EQUAL(key1, links.get(1));
2✔
552
    CHECK_EQUAL(key0, links.get(2));
2✔
553
    CHECK_EQUAL(Wed, Days(links[0].get<Int>(day_col)));
2✔
554

1✔
555
    LnkLst links2 = links;
2✔
556

1✔
557
    CHECK_EQUAL(3, links2.size());
2✔
558
    ObjList* obj_list = &links2;
2✔
559
    CHECK_EQUAL(key2, obj_list->get_key(0));
2✔
560

1✔
561
    // verify that backlinks was set correctly
1✔
562
    CHECK_EQUAL(1, obj0.get_backlink_count(*origin, col_link));
2✔
563
    CHECK_EQUAL(key3, obj0.get_backlink(*origin, col_link, 0));
2✔
564
    CHECK_EQUAL(1, obj1.get_backlink_count(*origin, col_link));
2✔
565
    CHECK_EQUAL(key3, obj1.get_backlink(*origin, col_link, 0));
2✔
566
    CHECK_EQUAL(1, obj2.get_backlink_count(*origin, col_link));
2✔
567
    CHECK_EQUAL(key3, obj2.get_backlink(*origin, col_link, 0));
2✔
568

1✔
569
    // insert a link at a specific position in the linklist
1✔
570
    links.insert(1, key2);
2✔
571
    CHECK_EQUAL(4, links2.size());
2✔
572
    CHECK_EQUAL(4, obj3.get_link_count(col_link));
2✔
573
    CHECK_EQUAL(key2, links.get(0));
2✔
574
    CHECK_EQUAL(key2, links.get(1));
2✔
575
    CHECK_EQUAL(key1, links.get(2));
2✔
576
    CHECK_EQUAL(key0, links.get(3));
2✔
577

1✔
578
    CHECK_EQUAL(2, obj2.get_backlink_count(*origin, col_link));
2✔
579
    CHECK_EQUAL(key3, obj2.get_backlink(*origin, col_link, 0));
2✔
580
    CHECK_EQUAL(key3, obj2.get_backlink(*origin, col_link, 1));
2✔
581

1✔
582
    // change one link to another (replace key2 with key1)
1✔
583
    links.set(0, key1);
2✔
584
    CHECK_EQUAL(4, obj3.get_link_count(col_link));
2✔
585
    CHECK_EQUAL(key1, links.get(0));
2✔
586
    CHECK_EQUAL(key2, links.get(1));
2✔
587
    CHECK_EQUAL(key1, links.get(2));
2✔
588
    CHECK_EQUAL(key0, links.get(3));
2✔
589

1✔
590
    CHECK_EQUAL(4, links2.size());
2✔
591
    CHECK_EQUAL(key1, links2.get(0));
2✔
592
    CHECK_EQUAL(key2, links2.get(1));
2✔
593
    CHECK_EQUAL(key1, links2.get(2));
2✔
594
    CHECK_EQUAL(key0, links2.get(3));
2✔
595

1✔
596
    CHECK_EQUAL(1, obj0.get_backlink_count(*origin, col_link));
2✔
597
    CHECK_EQUAL(key3, obj0.get_backlink(*origin, col_link, 0));
2✔
598
    CHECK_EQUAL(2, obj1.get_backlink_count(*origin, col_link));
2✔
599
    CHECK_EQUAL(key3, obj1.get_backlink(*origin, col_link, 0));
2✔
600
    CHECK_EQUAL(key3, obj1.get_backlink(*origin, col_link, 1));
2✔
601
    CHECK_EQUAL(1, obj2.get_backlink_count(*origin, col_link));
2✔
602
    CHECK_EQUAL(key3, obj2.get_backlink(*origin, col_link, 0));
2✔
603

1✔
604
    // move a link
1✔
605
    links.move(3, 0);
2✔
606
    CHECK_EQUAL(4, obj3.get_link_count(col_link));
2✔
607
    CHECK_EQUAL(key0, links.get(0));
2✔
608
    CHECK_EQUAL(key1, links.get(1));
2✔
609
    CHECK_EQUAL(key2, links.get(2));
2✔
610
    CHECK_EQUAL(key1, links.get(3));
2✔
611

1✔
612
    CHECK_EQUAL(4, links2.size());
2✔
613
    CHECK_EQUAL(key0, links2.get(0));
2✔
614
    CHECK_EQUAL(key1, links2.get(1));
2✔
615
    CHECK_EQUAL(key2, links2.get(2));
2✔
616
    CHECK_EQUAL(key1, links2.get(3));
2✔
617

1✔
618
    links.move(0, 2);
2✔
619
    CHECK_EQUAL(4, obj3.get_link_count(col_link));
2✔
620
    CHECK_EQUAL(key1, links.get(0));
2✔
621
    CHECK_EQUAL(key2, links.get(1));
2✔
622
    CHECK_EQUAL(key0, links.get(2));
2✔
623
    CHECK_EQUAL(key1, links.get(3));
2✔
624

1✔
625
    CHECK_EQUAL(4, links2.size());
2✔
626
    CHECK_EQUAL(key1, links2.get(0));
2✔
627
    CHECK_EQUAL(key2, links2.get(1));
2✔
628
    CHECK_EQUAL(key0, links2.get(2));
2✔
629
    CHECK_EQUAL(key1, links2.get(3));
2✔
630

1✔
631
    links.move(2, 0);
2✔
632
    CHECK_EQUAL(4, obj3.get_link_count(col_link));
2✔
633
    CHECK_EQUAL(key0, links.get(0));
2✔
634
    CHECK_EQUAL(key1, links.get(1));
2✔
635
    CHECK_EQUAL(key2, links.get(2));
2✔
636
    CHECK_EQUAL(key1, links.get(3));
2✔
637

1✔
638
    CHECK_EQUAL(4, links2.size());
2✔
639
    CHECK_EQUAL(key0, links2.get(0));
2✔
640
    CHECK_EQUAL(key1, links2.get(1));
2✔
641
    CHECK_EQUAL(key2, links2.get(2));
2✔
642
    CHECK_EQUAL(key1, links2.get(3));
2✔
643

1✔
644
    links.move(2, 2);
2✔
645
    CHECK_EQUAL(4, obj3.get_link_count(col_link));
2✔
646
    CHECK_EQUAL(key0, links.get(0));
2✔
647
    CHECK_EQUAL(key1, links.get(1));
2✔
648
    CHECK_EQUAL(key2, links.get(2));
2✔
649
    CHECK_EQUAL(key1, links.get(3));
2✔
650

1✔
651
    CHECK_EQUAL(4, links2.size());
2✔
652
    CHECK_EQUAL(key0, links2.get(0));
2✔
653
    CHECK_EQUAL(key1, links2.get(1));
2✔
654
    CHECK_EQUAL(key2, links2.get(2));
2✔
655
    CHECK_EQUAL(key1, links2.get(3));
2✔
656

1✔
657
    // swap two links
1✔
658
    links.swap(1, 2);
2✔
659
    CHECK_EQUAL(4, obj3.get_link_count(col_link));
2✔
660
    CHECK_EQUAL(key0, links.get(0));
2✔
661
    CHECK_EQUAL(key2, links.get(1));
2✔
662
    CHECK_EQUAL(key1, links.get(2));
2✔
663
    CHECK_EQUAL(key1, links.get(3));
2✔
664

1✔
665
    CHECK_EQUAL(4, links2.size());
2✔
666
    CHECK_EQUAL(key0, links2.get(0));
2✔
667
    CHECK_EQUAL(key2, links2.get(1));
2✔
668
    CHECK_EQUAL(key1, links2.get(2));
2✔
669
    CHECK_EQUAL(key1, links2.get(3));
2✔
670

1✔
671
    // swap a link with itself
1✔
672
    links.swap(2, 2);
2✔
673
    CHECK_EQUAL(4, obj3.get_link_count(col_link));
2✔
674
    CHECK_EQUAL(key0, links.get(0));
2✔
675
    CHECK_EQUAL(key2, links.get(1));
2✔
676
    CHECK_EQUAL(key1, links.get(2));
2✔
677
    CHECK_EQUAL(key1, links.get(3));
2✔
678

1✔
679
    CHECK_EQUAL(4, links2.size());
2✔
680
    CHECK_EQUAL(key0, links2.get(0));
2✔
681
    CHECK_EQUAL(key2, links2.get(1));
2✔
682
    CHECK_EQUAL(key1, links2.get(2));
2✔
683
    CHECK_EQUAL(key1, links2.get(3));
2✔
684

1✔
685
    // remove a link
1✔
686
    links.remove(0);
2✔
687
    CHECK_EQUAL(3, links2.size());
2✔
688
    CHECK_EQUAL(3, obj3.get_link_count(col_link));
2✔
689
    CHECK_EQUAL(0, obj0.get_backlink_count(*origin, col_link));
2✔
690

1✔
691
    // remove all links
1✔
692
    links.clear();
2✔
693
    CHECK_EQUAL(0, links2.size());
2✔
694
    CHECK_EQUAL(0, obj3.get_link_count(col_link));
2✔
695
    CHECK_EQUAL(0, obj0.get_backlink_count(*origin, col_link));
2✔
696
    CHECK_EQUAL(0, obj1.get_backlink_count(*origin, col_link));
2✔
697
    CHECK_EQUAL(0, obj2.get_backlink_count(*origin, col_link));
2✔
698

1✔
699
    // Add links again
1✔
700
    links.add(key2);
2✔
701
    links.add(key1);
2✔
702
    links.add(key0);
2✔
703

1✔
704
    CHECK_EQUAL(3, links2.size());
2✔
705

1✔
706
    // verify that backlinks were set
1✔
707
    CHECK_EQUAL(1, obj0.get_backlink_count(*origin, col_link));
2✔
708
    CHECK_EQUAL(1, obj1.get_backlink_count(*origin, col_link));
2✔
709
    CHECK_EQUAL(1, obj2.get_backlink_count(*origin, col_link));
2✔
710

1✔
711
    origin->remove_object(key3);
2✔
712
    // verify that backlinks were removed
1✔
713
    CHECK_EQUAL(0, obj0.get_backlink_count(*origin, col_link));
2✔
714
    CHECK_EQUAL(0, obj1.get_backlink_count(*origin, col_link));
2✔
715
    CHECK_EQUAL(0, obj2.get_backlink_count(*origin, col_link));
2✔
716
}
2✔
717

718
TEST(ListList_Clear)
719
{
2✔
720
    SHARED_GROUP_TEST_PATH(path);
2✔
721
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
722
    DBRef db = DB::create(*hist, path);
2✔
723
    auto group = db->start_write();
2✔
724

1✔
725
    auto target = group->add_table("target");
2✔
726
    target->add_column(type_Int, "value");
2✔
727

1✔
728
    TableRef origin = group->add_table("origin");
2✔
729
    auto col_link = origin->add_column_list(*target, "links");
2✔
730

1✔
731
    ObjKey key0 = target->create_object().set_all(1).get_key();
2✔
732
    ObjKey key1 = target->create_object().set_all(2).get_key();
2✔
733

1✔
734
    Obj obj3 = origin->create_object(ObjKey(0));
2✔
735
    auto links = obj3.get_linklist(col_link);
2✔
736

1✔
737
    links.add(key0);
2✔
738
    links.add(key1);
2✔
739

1✔
740
    group->commit_and_continue_as_read();
2✔
741
    group->promote_to_write();
2✔
742

1✔
743
    if (links.size() > 1)
2✔
744
        links.set(1, key0);
2✔
745
    links.clear();
2✔
746

1✔
747
    group->commit_and_continue_as_read();
2✔
748
    group->promote_to_write();
2✔
749

1✔
750
    obj3.remove();
2✔
751
    auto links2 = links.clone();
2✔
752
    CHECK_EQUAL(links2->size(), 0);
2✔
753
}
2✔
754

755
TEST(Links_AddBacklinkToTableWithEnumColumns)
756
{
2✔
757
    Group g;
2✔
758
    auto table = g.add_table("fshno");
2✔
759
    auto col = table->add_column(type_String, "strings", false);
2✔
760
    table->create_object();
2✔
761
    table->add_column(*table, "link1");
2✔
762
    table->enumerate_string_column(col);
2✔
763
    table->add_column(*table, "link2");
2✔
764
}
2✔
765

766
TEST(Links_LinkList_Inserts)
767
{
2✔
768
    Group group;
2✔
769

1✔
770
    auto target = group.add_table("target");
2✔
771
    test_table_add_columns(target);
2✔
772
    auto col_date = target->get_column_key("fourth");
2✔
773
    Obj obj0 = target->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
774
    Obj obj1 = target->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
775
    Obj obj2 = target->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
776
    ObjKey key0 = obj0.get_key();
2✔
777
    ObjKey key1 = obj1.get_key();
2✔
778
    ObjKey key2 = obj2.get_key();
2✔
779

1✔
780
    // create table with links to target table
1✔
781
    TableRef origin = group.add_table("origin");
2✔
782
    auto col_link = origin->add_column_list(*target, "links");
2✔
783
    CHECK_EQUAL(target, origin->get_link_target(col_link));
2✔
784

1✔
785
    auto obj = origin->create_object();
2✔
786
    auto links = obj.get_linklist_ptr(col_link);
2✔
787
    auto links2 = obj.get_linklist_ptr(col_link);
2✔
788
    auto k0 = links->get_owner_key();
2✔
789

1✔
790
    CHECK_EQUAL(0, links->size());
2✔
791
    CHECK_EQUAL(0, links2->size());
2✔
792

1✔
793
    // add several links to a single linklist
1✔
794
    links->add(key2);
2✔
795
    links->add(key1);
2✔
796
    links->add(key0);
2✔
797

1✔
798
    CHECK_EQUAL(3, links->size());
2✔
799
    CHECK_EQUAL(key2, links->get(0));
2✔
800
    CHECK_EQUAL(key1, links->get(1));
2✔
801
    CHECK_EQUAL(key0, links->get(2));
2✔
802
    CHECK_EQUAL(Wed, Days((*links)[0].get<Int>(col_date)));
2✔
803

1✔
804
    CHECK_EQUAL(3, links2->size());
2✔
805
    CHECK_EQUAL(key2, links2->get(0));
2✔
806
    CHECK_EQUAL(key1, links2->get(1));
2✔
807
    CHECK_EQUAL(key0, links2->get(2));
2✔
808
    CHECK_EQUAL(Wed, Days((*links2)[0].get<Int>(col_date)));
2✔
809

1✔
810
    // verify that backlinks was set correctly
1✔
811
    CHECK_EQUAL(1, obj0.get_backlink_count(*origin, col_link));
2✔
812
    CHECK_EQUAL(k0, obj0.get_backlink(*origin, col_link, 0));
2✔
813
    CHECK_EQUAL(1, obj1.get_backlink_count(*origin, col_link));
2✔
814
    CHECK_EQUAL(k0, obj1.get_backlink(*origin, col_link, 0));
2✔
815
    CHECK_EQUAL(1, obj2.get_backlink_count(*origin, col_link));
2✔
816
    CHECK_EQUAL(k0, obj2.get_backlink(*origin, col_link, 0));
2✔
817
}
2✔
818

819
TEST(Links_LinkList_Backlinks)
820
{
2✔
821
    Group group;
2✔
822

1✔
823
    auto target = group.add_table("target");
2✔
824
    test_table_add_columns(target);
2✔
825
    Obj obj0 = target->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
826
    Obj obj1 = target->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
827
    Obj obj2 = target->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
828
    ObjKey key0 = obj0.get_key();
2✔
829
    ObjKey key1 = obj1.get_key();
2✔
830
    ObjKey key2 = obj2.get_key();
2✔
831

1✔
832
    // create table with links to target table
1✔
833
    TableRef origin = group.add_table("origin");
2✔
834
    auto col_link = origin->add_column_list(*target, "links");
2✔
835
    CHECK_EQUAL(target, origin->get_link_target(col_link));
2✔
836

1✔
837
    Obj origin_obj = origin->create_object();
2✔
838
    auto links = origin_obj.get_linklist_ptr(col_link);
2✔
839
    auto k0 = links->get_owner_key();
2✔
840

1✔
841
    // add several links to a single linklist
1✔
842
    links->add(key2);
2✔
843
    links->add(key1);
2✔
844
    links->add(key0);
2✔
845
    links->add(key2);
2✔
846
    links->add(key1);
2✔
847
    links->add(key0);
2✔
848

1✔
849
    group.verify();
2✔
850

1✔
851
    // remove a target row and check that origin links are removed as well
1✔
852
    target->remove_object(key1);
2✔
853
    CHECK_EQUAL(4, origin_obj.get_link_count(col_link));
2✔
854
    CHECK_EQUAL(key2, links->get(0));
2✔
855
    CHECK_EQUAL(key0, links->get(1));
2✔
856

1✔
857
    // remove all
1✔
858
    target->clear();
2✔
859
    CHECK_EQUAL(0, origin_obj.get_link_count(col_link));
2✔
860
    CHECK(links->is_empty());
2✔
861

1✔
862
    // re-add rows to target
1✔
863
    obj0 = target->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
864
    obj1 = target->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
865
    obj2 = target->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
866

1✔
867
    // add more rows with links
1✔
868
    links->add(obj2.get_key());
2✔
869
    auto links1 = origin->create_object().get_linklist_ptr(col_link);
2✔
870
    auto links2 = origin->create_object().get_linklist_ptr(col_link);
2✔
871
    links1->add(obj1.get_key());
2✔
872
    links2->add(obj0.get_key());
2✔
873
    auto k1 = links1->get_owner_key();
2✔
874
    auto k2 = links2->get_owner_key();
2✔
875

1✔
876
    // Verify backlinks
1✔
877
    CHECK_EQUAL(1, obj0.get_backlink_count(*origin, col_link));
2✔
878
    CHECK_EQUAL(k2, obj0.get_backlink(*origin, col_link, 0));
2✔
879
    CHECK_EQUAL(1, obj1.get_backlink_count(*origin, col_link));
2✔
880
    CHECK_EQUAL(k1, obj1.get_backlink(*origin, col_link, 0));
2✔
881
    CHECK_EQUAL(1, obj2.get_backlink_count(*origin, col_link));
2✔
882
    CHECK_EQUAL(k0, obj2.get_backlink(*origin, col_link, 0));
2✔
883

1✔
884
    // delete a row and make sure backlinks are updated
1✔
885
    origin->remove_object(k0);
2✔
886
    CHECK_EQUAL(1, obj0.get_backlink_count(*origin, col_link));
2✔
887
    CHECK_EQUAL(k2, obj0.get_backlink(*origin, col_link, 0));
2✔
888
    CHECK_EQUAL(1, obj1.get_backlink_count(*origin, col_link));
2✔
889
    CHECK_EQUAL(k1, obj1.get_backlink(*origin, col_link, 0));
2✔
890
    CHECK_EQUAL(0, obj2.get_backlink_count(*origin, col_link));
2✔
891

1✔
892
    // delete last row and make sure backlinks are updated
1✔
893
    origin->remove_object(k2);
2✔
894
    CHECK_EQUAL(0, obj0.get_backlink_count(*origin, col_link));
2✔
895
    CHECK_EQUAL(1, obj1.get_backlink_count(*origin, col_link));
2✔
896
    CHECK_EQUAL(k1, obj1.get_backlink(*origin, col_link, 0));
2✔
897
    CHECK_EQUAL(0, obj2.get_backlink_count(*origin, col_link));
2✔
898

1✔
899
    // remove all link lists and make sure backlinks are updated
1✔
900
    origin->clear();
2✔
901
    CHECK_EQUAL(0, obj0.get_backlink_count(*origin, col_link));
2✔
902
    CHECK_EQUAL(0, obj1.get_backlink_count(*origin, col_link));
2✔
903
    CHECK_EQUAL(0, obj2.get_backlink_count(*origin, col_link));
2✔
904
}
2✔
905

906

907
TEST(Links_LinkList_FindByOrigin)
908
{
2✔
909
    Group group;
2✔
910

1✔
911
    auto target = group.add_table("target");
2✔
912
    test_table_add_columns(target);
2✔
913
    Obj obj0 = target->create_object().set_all("test1", 1, true, int64_t(Mon));
2✔
914
    Obj obj1 = target->create_object().set_all("test2", 2, false, int64_t(Tue));
2✔
915
    Obj obj2 = target->create_object().set_all("test3", 3, true, int64_t(Wed));
2✔
916
    ObjKey key0 = obj0.get_key();
2✔
917
    ObjKey key1 = obj1.get_key();
2✔
918
    ObjKey key2 = obj2.get_key();
2✔
919

1✔
920
    // create table with links to target table
1✔
921
    TableRef origin = group.add_table("origin");
2✔
922
    auto col_link = origin->add_column_list(*target, "links");
2✔
923
    CHECK_EQUAL(target, origin->get_link_target(col_link));
2✔
924

1✔
925
    auto obj = origin->create_object();
2✔
926
    auto links = obj.get_linklist_ptr(col_link);
2✔
927
    auto links2 = obj.get_linklist_ptr(col_link);
2✔
928
    CHECK_EQUAL(not_found, links->find_first(key2));
2✔
929
    CHECK_EQUAL(not_found, links2->find_first(key2));
2✔
930

1✔
931
    links->find_all(key2, [&](size_t) {
1✔
UNCOV
932
        CHECK(false);
×
UNCOV
933
    });
×
934
    links2->find_all(key2, [&](size_t) {
1✔
UNCOV
935
        CHECK(false);
×
UNCOV
936
    });
×
937

1✔
938
    links->add(key2);
2✔
939
    links->add(key1);
2✔
940
    links->add(key0);
2✔
941

1✔
942
    CHECK_EQUAL(0, links->find_first(key2));
2✔
943
    CHECK_EQUAL(1, links->find_first(key1));
2✔
944
    CHECK_EQUAL(2, links->find_first(key0));
2✔
945
    CHECK_EQUAL(0, links2->find_first(key2));
2✔
946
    CHECK_EQUAL(1, links2->find_first(key1));
2✔
947
    CHECK_EQUAL(2, links2->find_first(key0));
2✔
948

1✔
949
    int calls = 0;
2✔
950
    links->find_all(key2, [&](size_t i) {
2✔
951
        CHECK_EQUAL(i, 0);
2✔
952
        ++calls;
2✔
953
    });
2✔
954
    CHECK_EQUAL(calls, 1);
2✔
955
    calls = 0;
2✔
956
    links2->find_all(key0, [&](size_t i) {
2✔
957
        CHECK_EQUAL(i, 2);
2✔
958
        ++calls;
2✔
959
    });
2✔
960
    CHECK_EQUAL(calls, 1);
2✔
961

1✔
962
    links->remove(0);
2✔
963

1✔
964
    CHECK_EQUAL(not_found, links->find_first(key2));
2✔
965
    CHECK_EQUAL(not_found, links2->find_first(key2));
2✔
966

1✔
967
    links->add(key0);
2✔
968
    links->add(key0);
2✔
969

1✔
970
    calls = 0;
2✔
971
    links->find_all(key0, [&](size_t i) {
6✔
972
        CHECK(i >= 1);
6✔
973
        ++calls;
6✔
974
    });
6✔
975
    CHECK_EQUAL(calls, 3);
2✔
976
}
2✔
977

978

979
TEST(Links_CircularAccessors)
980
{
2✔
981
    SHARED_GROUP_TEST_PATH(path);
2✔
982
    auto db = DB::create(path);
2✔
983
    ColKey col1;
2✔
984
    ColKey col2;
2✔
985
    {
2✔
986
        WriteTransaction wt(db);
2✔
987
        TableRef table1 = wt.add_table("table1");
2✔
988
        TableRef table2 = wt.add_table("table2");
2✔
989
        col1 = table1->add_column(*table2, "link");
2✔
990
        col2 = table2->add_column(*table1, "link");
2✔
991
        CHECK_EQUAL(table1, table2->get_link_target(col1));
2✔
992
        CHECK_EQUAL(table2, table1->get_link_target(col2));
2✔
993
        wt.commit();
2✔
994
    }
2✔
995
    {
2✔
996
        WriteTransaction wt(db);
2✔
997
        TableRef table1 = wt.get_table("table1");
2✔
998
        TableRef table2 = wt.get_table("table2");
2✔
999
        CHECK_EQUAL(table1, table2->get_link_target(col1));
2✔
1000
        CHECK_EQUAL(table2, table1->get_link_target(col2));
2✔
1001
    }
2✔
1002
}
2✔
1003

1004

1005
TEST(Links_Transactions)
1006
{
2✔
1007
    SHARED_GROUP_TEST_PATH(path);
2✔
1008
    auto hist = make_in_realm_history();
2✔
1009
    auto db = DB::create(*hist, path);
2✔
1010

1✔
1011
    ColKey dog_col;
2✔
1012
    ObjKey tim_key;
2✔
1013
    ObjKey harvey_key;
2✔
1014

1✔
1015
    {
2✔
1016
        WriteTransaction group(db);
2✔
1017

1✔
1018
        // Create dogs table
1✔
1019
        TableRef dogs = group.add_table("dogs");
2✔
1020
        dogs->add_column(type_String, "dogName");
2✔
1021

1✔
1022
        // Create owners table
1✔
1023
        TableRef owners = group.add_table("owners");
2✔
1024
        owners->add_column(type_String, "name");
2✔
1025
        dog_col = owners->add_column(*dogs, "dog");
2✔
1026

1✔
1027
        // Insert a single dog
1✔
1028
        harvey_key = dogs->create_object().set_all("Harvey").get_key();
2✔
1029

1✔
1030
        // Insert an owner with link to dog
1✔
1031
        tim_key = owners->create_object().set_all("Tim", harvey_key).get_key();
2✔
1032

1✔
1033
        group.commit();
2✔
1034
    }
2✔
1035

1✔
1036
    auto rt = db->start_read();
2✔
1037
    ConstTableRef owners = rt->get_table("owners");
2✔
1038
    ConstTableRef dogs = rt->get_table("dogs");
2✔
1039
    const Obj tim = owners->get_object(tim_key);
2✔
1040
    CHECK_NOT(tim.is_null(dog_col));
2✔
1041
    CHECK_EQUAL(harvey_key, tim.get<ObjKey>(dog_col));
2✔
1042
    const Obj harvey = dogs->get_object(harvey_key);
2✔
1043
    CHECK_EQUAL(harvey.get_backlink_count(), 1);
2✔
1044

1✔
1045
    {
2✔
1046
        // Add another another owner for Harvey
1✔
1047
        WriteTransaction wt(db);
2✔
1048
        wt.get_table("owners")->create_object().set_all("Tim", harvey_key);
2✔
1049
        wt.commit();
2✔
1050
    }
2✔
1051

1✔
1052
    rt->advance_read();
2✔
1053
    CHECK_EQUAL(harvey.get_backlink_count(), 2);
2✔
1054

1✔
1055
    {
2✔
1056
        // Delete dog
1✔
1057
        WriteTransaction wt(db);
2✔
1058
        wt.get_table("dogs")->remove_object(harvey_key);
2✔
1059
        wt.commit();
2✔
1060
    }
2✔
1061

1✔
1062
    // Verify that link from owner was nullified
1✔
1063
    rt->advance_read();
2✔
1064
    CHECK(tim.is_null(dog_col));
2✔
1065
}
2✔
1066

1067
#if !REALM_ANDROID // FIXME
1068
// When compiling for Android (armeabi-v7a) you will get this error:
1069
// internal compiler error: in possible_polymorphic_call_targets, at ipa-devirt.c:1556
1070
TEST(Links_RemoveTargetRows)
1071
{
2✔
1072
    Group group;
2✔
1073

1✔
1074
    auto target = group.add_table("target");
2✔
1075
    test_table_add_columns(target);
2✔
1076
    auto k0 = target->create_object().set_all("test1", 1, true, int(Mon)).get_key();
2✔
1077
    auto k1 = target->create_object().set_all("test2", 2, false, int(Tue)).get_key();
2✔
1078
    auto k2 = target->create_object().set_all("test3", 3, true, int(Wed)).get_key();
2✔
1079

1✔
1080
    // create table with links to target table
1✔
1081
    TableRef origin = group.add_table("origin");
2✔
1082
    auto col_link = origin->add_column_list(*target, "links");
2✔
1083

1✔
1084
    Obj obj = origin->create_object();
2✔
1085
    auto links = obj.get_linklist(col_link);
2✔
1086
    links.add(k2);
2✔
1087
    links.add(k1);
2✔
1088
    links.add(k0);
2✔
1089

1✔
1090
    // delete target rows through the links one at a time
1✔
1091
    links.remove_target_row(0);
2✔
1092
    CHECK_EQUAL(2, target->size());
2✔
1093
    CHECK_EQUAL(2, links.size());
2✔
1094

1✔
1095
    links.remove_target_row(1);
2✔
1096
    CHECK_EQUAL(1, target->size());
2✔
1097
    CHECK_EQUAL(1, links.size());
2✔
1098

1✔
1099
    links.remove_target_row(0);
2✔
1100
    CHECK_EQUAL(0, target->size());
2✔
1101
    CHECK_EQUAL(0, links.size());
2✔
1102

1✔
1103
    // re-add targets and links
1✔
1104
    k0 = target->create_object().set_all("test1", 1, true, int(Mon)).get_key();
2✔
1105
    k1 = target->create_object().set_all("test2", 2, false, int(Tue)).get_key();
2✔
1106
    k2 = target->create_object().set_all("test3", 3, true, int(Wed)).get_key();
2✔
1107
    links.add(k2);
2✔
1108
    links.add(k1);
2✔
1109
    links.add(k0);
2✔
1110

1✔
1111
    // Remove all targets through the links
1✔
1112
    links.remove_all_target_rows();
2✔
1113
    CHECK(target->is_empty());
2✔
1114
    CHECK(links.is_empty());
2✔
1115
}
2✔
1116
#endif
1117

1118
TEST(Links_ClearColumnWithTwoLevelBptree)
1119
{
2✔
1120
    Group group;
2✔
1121
    TableRef origin = group.add_table("origin");
2✔
1122
    TableRef target = group.add_table("target");
2✔
1123

1✔
1124
    // The extra columns beyond the first one increase the likelihood of
1✔
1125
    // getting unambiguously bad ref
1✔
1126
    target->add_column(type_Int, "i1");
2✔
1127
    target->add_column(type_Int, "i2");
2✔
1128
    target->add_column(type_Int, "i3");
2✔
1129
    target->add_column(type_Int, "i4");
2✔
1130
    target->add_column(type_Int, "i5");
2✔
1131
    Obj obj = target->create_object();
2✔
1132

1✔
1133
    auto col = origin->add_column_list(*target, "");
2✔
1134
    std::vector<ObjKey> keys;
2✔
1135
    origin->create_objects(REALM_MAX_BPNODE_SIZE + 1, keys);
2✔
1136
    origin->clear();
2✔
1137
    origin->create_object().get_linklist(col).add(obj.get_key());
2✔
1138
    group.verify();
2✔
1139
}
2✔
1140

1141

1142
TEST(Links_ClearLinkListWithTwoLevelBptree)
1143
{
2✔
1144
    Group group;
2✔
1145
    TableRef origin = group.add_table("origin");
2✔
1146
    TableRef target = group.add_table("target");
2✔
1147
    auto col_link = origin->add_column_list(*target, "");
2✔
1148
    ObjKey k = target->create_object().get_key();
2✔
1149
    auto ll = origin->create_object().get_linklist(col_link);
2✔
1150
    for (size_t i = 0; i < REALM_MAX_BPNODE_SIZE + 1; ++i)
2,004✔
1151
        ll.add(k);
2,002✔
1152
    ll.clear();
2✔
1153
    group.verify();
2✔
1154
}
2✔
1155

1156

1157
TEST(Links_FormerMemLeakCase)
1158
{
2✔
1159
    SHARED_GROUP_TEST_PATH(path);
2✔
1160
    auto sg_w = DB::create(path);
2✔
1161
    {
2✔
1162
        WriteTransaction wt(sg_w);
2✔
1163
        TableRef origin = wt.add_table("origin");
2✔
1164
        TableRef target = wt.add_table("target");
2✔
1165
        target->add_column(type_Int, "int");
2✔
1166
        auto k = target->create_object().get_key();
2✔
1167
        auto col = origin->add_column(*target, "link");
2✔
1168
        origin->create_object().set(col, k);
2✔
1169
        origin->create_object().set(col, k);
2✔
1170
        wt.commit();
2✔
1171
    }
2✔
1172
    {
2✔
1173
        WriteTransaction wt(sg_w);
2✔
1174
        TableRef target = wt.get_table("target");
2✔
1175
        target->begin()->remove();
2✔
1176
        wt.get_group().verify();
2✔
1177
        wt.commit();
2✔
1178
    }
2✔
1179
}
2✔
1180

1181
TEST(Links_CascadeRemove_ColumnLink)
1182
{
2✔
1183
    struct Fixture {
2✔
1184
        Group group;
2✔
1185
        TableRef origin = group.add_table("origin");
2✔
1186
        TableRef target = group.add_table("target", Table::Type::Embedded);
2✔
1187
        std::vector<ObjKey> origin_keys;
2✔
1188
        std::vector<ObjKey> target_keys;
2✔
1189
        ColKey col_link;
2✔
1190
        Fixture()
2✔
1191
        {
20✔
1192
            target->add_column(type_Int, "t_1");
20✔
1193
            col_link = origin->add_column(*target, "o_1");
20✔
1194
            for (int i = 0; i < 3; ++i) {
80✔
1195
                auto oo = origin->create_object();
60✔
1196
                auto to = oo.create_and_set_linked_object(col_link);
60✔
1197
                origin_keys.push_back(oo.get_key());
60✔
1198
                target_keys.push_back(to.get_key());
60✔
1199
            }
60✔
1200
        }
20✔
1201
        Obj get_origin_obj(int i)
2✔
1202
        {
54✔
1203
            return origin->get_object(origin_keys[i]);
54✔
1204
        }
54✔
1205
        Obj get_target_obj(int i)
2✔
1206
        {
1✔
UNCOV
1207
            return target->get_object(origin_keys[i]);
×
UNCOV
1208
        }
×
1209
    };
2✔
1210

1✔
1211
    // Break link by nullifying
1✔
1212
    {
2✔
1213
        Fixture f;
2✔
1214
        f.get_origin_obj(0).set(f.col_link, null_key); // origin[0].o_1 -> realm::null()
2✔
1215
        // Cascade: target->remove_object(key[0])
1✔
1216
        CHECK(!f.target->is_valid(f.target_keys[0]));
2✔
1217
        CHECK(f.target->is_valid(f.target_keys[1]) && f.target->is_valid(f.target_keys[2]));
2✔
1218
        CHECK_EQUAL(f.target_keys[1], f.get_origin_obj(1).get<ObjKey>(f.col_link));
2✔
1219
        CHECK_EQUAL(f.target_keys[2], f.get_origin_obj(2).get<ObjKey>(f.col_link));
2✔
1220
    }
2✔
1221
    {
2✔
1222
        Fixture f;
2✔
1223
        f.get_origin_obj(1).set(f.col_link, null_key); // origin[1].o_1 -> realm::null()
2✔
1224
        // Cascade: target->remove_object(key[1])
1✔
1225
        CHECK(!f.target->is_valid(f.target_keys[1]));
2✔
1226
        CHECK(f.target->is_valid(f.target_keys[0]) && f.target->is_valid(f.target_keys[2]));
2✔
1227
        CHECK_EQUAL(f.target_keys[0], f.get_origin_obj(0).get<ObjKey>(f.col_link));
2✔
1228
        CHECK_EQUAL(f.target_keys[2], f.get_origin_obj(2).get<ObjKey>(f.col_link));
2✔
1229
    }
2✔
1230
    {
2✔
1231
        Fixture f;
2✔
1232
        f.get_origin_obj(2).set(f.col_link, null_key); // origin[0].o_1 -> realm::null()
2✔
1233
        // Cascade: target->remove_object(key[2])
1✔
1234
        CHECK(!f.target->is_valid(f.target_keys[2]));
2✔
1235
        CHECK(f.target->is_valid(f.target_keys[0]) && f.target->is_valid(f.target_keys[1]));
2✔
1236
        CHECK_EQUAL(f.target_keys[0], f.get_origin_obj(0).get<ObjKey>(f.col_link));
2✔
1237
        CHECK_EQUAL(f.target_keys[1], f.get_origin_obj(1).get<ObjKey>(f.col_link));
2✔
1238
    }
2✔
1239
    // Break link by reassign
1✔
1240
    {
2✔
1241
        Fixture f;
2✔
1242
        f.get_origin_obj(0).create_and_set_linked_object(f.col_link);
2✔
1243
        // Cascade: target->remove_object(key[0])
1✔
1244
        CHECK(!f.target->is_valid(f.target_keys[0]));
2✔
1245
        CHECK(f.target->is_valid(f.target_keys[1]) && f.target->is_valid(f.target_keys[2]));
2✔
1246
        CHECK_EQUAL(f.target_keys[1], f.get_origin_obj(1).get<ObjKey>(f.col_link));
2✔
1247
        CHECK_EQUAL(f.target_keys[2], f.get_origin_obj(2).get<ObjKey>(f.col_link));
2✔
1248
    }
2✔
1249
    {
2✔
1250
        Fixture f;
2✔
1251
        f.get_origin_obj(1).create_and_set_linked_object(f.col_link);
2✔
1252
        // Cascade: target->remove_object(key[1])
1✔
1253
        CHECK(!f.target->is_valid(f.target_keys[1]));
2✔
1254
        CHECK(f.target->is_valid(f.target_keys[0]) && f.target->is_valid(f.target_keys[2]));
2✔
1255
        CHECK_EQUAL(f.target_keys[0], f.get_origin_obj(0).get<ObjKey>(f.col_link));
2✔
1256
        CHECK_EQUAL(f.target_keys[2], f.get_origin_obj(2).get<ObjKey>(f.col_link));
2✔
1257
    }
2✔
1258
    {
2✔
1259
        Fixture f;
2✔
1260
        f.get_origin_obj(2).create_and_set_linked_object(f.col_link);
2✔
1261
        // Cascade: target->remove_object(key[2])
1✔
1262
        CHECK(!f.target->is_valid(f.target_keys[2]));
2✔
1263
        CHECK(f.target->is_valid(f.target_keys[0]) && f.target->is_valid(f.target_keys[1]));
2✔
1264
        CHECK_EQUAL(f.target_keys[0], f.get_origin_obj(0).get<ObjKey>(f.col_link));
2✔
1265
        CHECK_EQUAL(f.target_keys[1], f.get_origin_obj(1).get<ObjKey>(f.col_link));
2✔
1266
    }
2✔
1267
    // Break link by explicit object removal
1✔
1268
    {
2✔
1269
        Fixture f;
2✔
1270
        f.get_origin_obj(0).remove(); // Cascade: target->remove_object(key[0])
2✔
1271
        CHECK(!f.target->is_valid(f.target_keys[0]));
2✔
1272
        CHECK(f.target->is_valid(f.target_keys[1]) && f.target->is_valid(f.target_keys[2]));
2✔
1273
        CHECK_EQUAL(f.target_keys[1], f.get_origin_obj(1).get<ObjKey>(f.col_link));
2✔
1274
        CHECK_EQUAL(f.target_keys[2], f.get_origin_obj(2).get<ObjKey>(f.col_link));
2✔
1275
    }
2✔
1276
    {
2✔
1277
        Fixture f;
2✔
1278
        f.get_origin_obj(1).remove(); // Cascade: target->remove_object(key[1])
2✔
1279
        CHECK(!f.target->is_valid(f.target_keys[1]));
2✔
1280
        CHECK(f.target->is_valid(f.target_keys[0]) && f.target->is_valid(f.target_keys[2]));
2✔
1281
        CHECK_EQUAL(f.target_keys[0], f.get_origin_obj(0).get<ObjKey>(f.col_link));
2✔
1282
        CHECK_EQUAL(f.target_keys[2], f.get_origin_obj(2).get<ObjKey>(f.col_link));
2✔
1283
    }
2✔
1284
    {
2✔
1285
        Fixture f;
2✔
1286
        f.get_origin_obj(2).remove(); // Cascade: target->remove_object(key[2])
2✔
1287
        CHECK(!f.target->is_valid(f.target_keys[2]));
2✔
1288
        CHECK(f.target->is_valid(f.target_keys[0]) && f.target->is_valid(f.target_keys[1]));
2✔
1289
        CHECK_EQUAL(f.target_keys[0], f.get_origin_obj(0).get<ObjKey>(f.col_link));
2✔
1290
        CHECK_EQUAL(f.target_keys[1], f.get_origin_obj(1).get<ObjKey>(f.col_link));
2✔
1291
    }
2✔
1292

1✔
1293
    // Break link by clearing table
1✔
1294
    {
2✔
1295
        Fixture f;
2✔
1296
        f.origin->clear();
2✔
1297
        CHECK(!f.target->is_valid(f.target_keys[0]));
2✔
1298
        CHECK(!f.target->is_valid(f.target_keys[1]));
2✔
1299
        CHECK(!f.target->is_valid(f.target_keys[2]));
2✔
1300
    }
2✔
1301
}
2✔
1302

1303

1304
TEST(Links_CascadeRemove_ColumnLinkList)
1305
{
2✔
1306
    struct Fixture {
2✔
1307
        Group group;
2✔
1308
        TableRef origin = group.add_table("origin");
2✔
1309
        TableRef target = group.add_table("target", Table::Type::Embedded);
2✔
1310
        std::vector<ObjKey> origin_keys;
2✔
1311
        std::vector<ObjKey> target_keys;
2✔
1312
        std::vector<LnkLstPtr> linklists;
2✔
1313
        ColKey col_link;
2✔
1314
        Fixture()
2✔
1315
        {
22✔
1316
            target->add_column(type_Int, "t_1");
22✔
1317
            col_link = origin->add_column_list(*target, "o_1");
22✔
1318
            origin->create_objects(3, origin_keys);
22✔
1319
            linklists.emplace_back(origin->get_object(origin_keys[0]).get_linklist_ptr(col_link));
22✔
1320
            linklists.emplace_back(origin->get_object(origin_keys[1]).get_linklist_ptr(col_link));
22✔
1321
            linklists.emplace_back(origin->get_object(origin_keys[2]).get_linklist_ptr(col_link));
22✔
1322
            target_keys.emplace_back(linklists[0]->create_and_insert_linked_object(0).get_key());
22✔
1323
            target_keys.emplace_back(linklists[1]->create_and_insert_linked_object(0).get_key());
22✔
1324
            target_keys.emplace_back(linklists[1]->create_and_insert_linked_object(1).get_key());
22✔
1325
            target_keys.emplace_back(linklists[2]->create_and_insert_linked_object(0).get_key());
22✔
1326
            target_keys.emplace_back(linklists[2]->create_and_insert_linked_object(1).get_key());
22✔
1327
            target_keys.emplace_back(linklists[2]->create_and_insert_linked_object(2).get_key());
22✔
1328
        }
22✔
1329
        Obj get_origin_obj(int i)
2✔
1330
        {
6✔
1331
            return origin->get_object(origin_keys[i]);
6✔
1332
        }
6✔
1333
        Obj get_target_obj(int i)
2✔
1334
        {
1✔
UNCOV
1335
            return target->get_object(target_keys[i]);
×
UNCOV
1336
        }
×
1337
    };
2✔
1338
    // Break links by clearing list
1✔
1339
    {
2✔
1340
        Fixture f;
2✔
1341
        f.linklists[0]->clear();
2✔
1342
        CHECK(!f.target->is_valid(f.target_keys[0]));
2✔
1343
        CHECK(f.target->is_valid(f.target_keys[1]));
2✔
1344
        CHECK(f.target->is_valid(f.target_keys[2]));
2✔
1345
        CHECK(f.target->is_valid(f.target_keys[3]));
2✔
1346
        CHECK(f.target->is_valid(f.target_keys[4]));
2✔
1347
        CHECK(f.target->is_valid(f.target_keys[5]));
2✔
1348
        CHECK_EQUAL(f.target_keys[1], f.linklists[1]->get(0));
2✔
1349
        CHECK_EQUAL(f.target_keys[2], f.linklists[1]->get(1));
2✔
1350
        CHECK_EQUAL(f.target_keys[3], f.linklists[2]->get(0));
2✔
1351
        CHECK_EQUAL(f.target_keys[4], f.linklists[2]->get(1));
2✔
1352
        CHECK_EQUAL(f.target_keys[5], f.linklists[2]->get(2));
2✔
1353
        CHECK_EQUAL(5, f.target->size());
2✔
1354
        f.group.verify();
2✔
1355
    }
2✔
1356
    {
2✔
1357
        Fixture f;
2✔
1358
        f.linklists[1]->clear();
2✔
1359
        CHECK(f.target->is_valid(f.target_keys[0]));
2✔
1360
        CHECK(!f.target->is_valid(f.target_keys[1]));
2✔
1361
        CHECK(!f.target->is_valid(f.target_keys[2]));
2✔
1362
        CHECK(f.target->is_valid(f.target_keys[3]));
2✔
1363
        CHECK(f.target->is_valid(f.target_keys[4]));
2✔
1364
        CHECK(f.target->is_valid(f.target_keys[5]));
2✔
1365
        CHECK_EQUAL(f.target_keys[0], f.linklists[0]->get(0));
2✔
1366
        CHECK_EQUAL(f.target_keys[3], f.linklists[2]->get(0));
2✔
1367
        CHECK_EQUAL(f.target_keys[4], f.linklists[2]->get(1));
2✔
1368
        CHECK_EQUAL(f.target_keys[5], f.linklists[2]->get(2));
2✔
1369
        CHECK_EQUAL(4, f.target->size());
2✔
1370
        f.group.verify();
2✔
1371
    }
2✔
1372
    {
2✔
1373
        Fixture f;
2✔
1374
        f.linklists[2]->clear(); // Cascade: Nothing
2✔
1375
        CHECK(f.target->is_valid(f.target_keys[0]));
2✔
1376
        CHECK(f.target->is_valid(f.target_keys[1]));
2✔
1377
        CHECK(f.target->is_valid(f.target_keys[2]));
2✔
1378
        CHECK(!f.target->is_valid(f.target_keys[3]));
2✔
1379
        CHECK(!f.target->is_valid(f.target_keys[4]));
2✔
1380
        CHECK(!f.target->is_valid(f.target_keys[5]));
2✔
1381
        CHECK_EQUAL(f.target_keys[0], f.linklists[0]->get(0));
2✔
1382
        CHECK_EQUAL(f.target_keys[1], f.linklists[1]->get(0));
2✔
1383
        CHECK_EQUAL(f.target_keys[2], f.linklists[1]->get(1));
2✔
1384
        CHECK_EQUAL(3, f.target->size());
2✔
1385
        f.group.verify();
2✔
1386
    }
2✔
1387
    // Break links by removal from list
1✔
1388
    {
2✔
1389
        Fixture f;
2✔
1390
        f.linklists[0]->remove(0); // Cascade: Nothing
2✔
1391
        CHECK(!f.target->is_valid(f.target_keys[0]));
2✔
1392
        CHECK(f.target->is_valid(f.target_keys[1]));
2✔
1393
        CHECK(f.target->is_valid(f.target_keys[2]));
2✔
1394
        CHECK(f.target->is_valid(f.target_keys[3]));
2✔
1395
        CHECK(f.target->is_valid(f.target_keys[4]));
2✔
1396
        CHECK(f.target->is_valid(f.target_keys[5]));
2✔
1397
        CHECK_EQUAL(f.target_keys[1], f.linklists[1]->get(0));
2✔
1398
        CHECK_EQUAL(f.target_keys[2], f.linklists[1]->get(1));
2✔
1399
        CHECK_EQUAL(f.target_keys[3], f.linklists[2]->get(0));
2✔
1400
        CHECK_EQUAL(f.target_keys[4], f.linklists[2]->get(1));
2✔
1401
        CHECK_EQUAL(f.target_keys[5], f.linklists[2]->get(2));
2✔
1402
        CHECK_EQUAL(5, f.target->size());
2✔
1403
        f.group.verify();
2✔
1404
    }
2✔
1405
    {
2✔
1406
        Fixture f;
2✔
1407
        f.linklists[1]->remove(0);
2✔
1408
        CHECK(f.target->is_valid(f.target_keys[0]));
2✔
1409
        CHECK(!f.target->is_valid(f.target_keys[1]));
2✔
1410
        CHECK(f.target->is_valid(f.target_keys[2]));
2✔
1411
        CHECK(f.target->is_valid(f.target_keys[3]));
2✔
1412
        CHECK(f.target->is_valid(f.target_keys[4]));
2✔
1413
        CHECK(f.target->is_valid(f.target_keys[5]));
2✔
1414
        CHECK_EQUAL(f.target_keys[0], f.linklists[0]->get(0));
2✔
1415
        CHECK_EQUAL(f.target_keys[2], f.linklists[1]->get(0));
2✔
1416
        CHECK_EQUAL(f.target_keys[3], f.linklists[2]->get(0));
2✔
1417
        CHECK_EQUAL(f.target_keys[4], f.linklists[2]->get(1));
2✔
1418
        CHECK_EQUAL(f.target_keys[5], f.linklists[2]->get(2));
2✔
1419
        CHECK_EQUAL(5, f.target->size());
2✔
1420
        f.group.verify();
2✔
1421
    }
2✔
1422

1✔
1423
    // Break links by reassign
1✔
1424
    {
2✔
1425
        Fixture f;
2✔
1426
        f.target_keys.emplace_back(f.linklists[0]->create_and_set_linked_object(0).get_key());
2✔
1427
        CHECK(!f.target->is_valid(f.target_keys[0]));
2✔
1428
        CHECK(f.target->is_valid(f.target_keys[6]));
2✔
1429
        CHECK(f.target->is_valid(f.target_keys[1]));
2✔
1430
        CHECK(f.target->is_valid(f.target_keys[2]));
2✔
1431
        CHECK(f.target->is_valid(f.target_keys[3]));
2✔
1432
        CHECK(f.target->is_valid(f.target_keys[4]));
2✔
1433
        CHECK(f.target->is_valid(f.target_keys[5]));
2✔
1434
        CHECK_EQUAL(f.target_keys[6], f.linklists[0]->get(0));
2✔
1435
        CHECK_EQUAL(f.target_keys[1], f.linklists[1]->get(0));
2✔
1436
        CHECK_EQUAL(f.target_keys[2], f.linklists[1]->get(1));
2✔
1437
        CHECK_EQUAL(f.target_keys[3], f.linklists[2]->get(0));
2✔
1438
        CHECK_EQUAL(f.target_keys[4], f.linklists[2]->get(1));
2✔
1439
        CHECK_EQUAL(f.target_keys[5], f.linklists[2]->get(2));
2✔
1440
        CHECK_EQUAL(6, f.target->size());
2✔
1441
        f.group.verify();
2✔
1442
    }
2✔
1443
    {
2✔
1444
        Fixture f;
2✔
1445
        f.target_keys.emplace_back(f.linklists[1]->create_and_set_linked_object(0).get_key());
2✔
1446
        CHECK(!f.target->is_valid(f.target_keys[1]));
2✔
1447
        CHECK(f.target->is_valid(f.target_keys[0]));
2✔
1448
        CHECK(f.target->is_valid(f.target_keys[6]));
2✔
1449
        CHECK(f.target->is_valid(f.target_keys[2]));
2✔
1450
        CHECK(f.target->is_valid(f.target_keys[3]));
2✔
1451
        CHECK(f.target->is_valid(f.target_keys[4]));
2✔
1452
        CHECK(f.target->is_valid(f.target_keys[5]));
2✔
1453
        CHECK_EQUAL(f.target_keys[0], f.linklists[0]->get(0));
2✔
1454
        CHECK_EQUAL(f.target_keys[6], f.linklists[1]->get(0));
2✔
1455
        CHECK_EQUAL(f.target_keys[2], f.linklists[1]->get(1));
2✔
1456
        CHECK_EQUAL(f.target_keys[3], f.linklists[2]->get(0));
2✔
1457
        CHECK_EQUAL(f.target_keys[4], f.linklists[2]->get(1));
2✔
1458
        CHECK_EQUAL(f.target_keys[5], f.linklists[2]->get(2));
2✔
1459
        CHECK_EQUAL(6, f.target->size());
2✔
1460
        f.group.verify();
2✔
1461
    }
2✔
1462

1✔
1463
    // Break links by explicit ordered row removal
1✔
1464
    {
2✔
1465
        Fixture f;
2✔
1466
        f.get_origin_obj(0).remove();
2✔
1467
        CHECK_EQUAL(2, f.origin->size());
2✔
1468
        CHECK_EQUAL(5, f.target->size());
2✔
1469
        CHECK(!f.target->is_valid(f.target_keys[0]));
2✔
1470
        CHECK(f.target->is_valid(f.target_keys[1]));
2✔
1471
        CHECK(f.target->is_valid(f.target_keys[2]));
2✔
1472
        CHECK(f.target->is_valid(f.target_keys[3]));
2✔
1473
        CHECK(f.target->is_valid(f.target_keys[4]));
2✔
1474
        CHECK(f.target->is_valid(f.target_keys[5]));
2✔
1475
        // CHECK_EQUAL(f.target_keys[0], f.linklists[0]->get(0));
1✔
1476
        CHECK_EQUAL(f.target_keys[1], f.linklists[1]->get(0));
2✔
1477
        CHECK_EQUAL(f.target_keys[2], f.linklists[1]->get(1));
2✔
1478
        CHECK_EQUAL(f.target_keys[3], f.linklists[2]->get(0));
2✔
1479
        CHECK_EQUAL(f.target_keys[4], f.linklists[2]->get(1));
2✔
1480
        CHECK_EQUAL(f.target_keys[5], f.linklists[2]->get(2));
2✔
1481
        f.group.verify();
2✔
1482
    }
2✔
1483
    {
2✔
1484
        Fixture f;
2✔
1485
        f.get_origin_obj(1).remove();
2✔
1486
        CHECK_EQUAL(2, f.origin->size());
2✔
1487
        CHECK_EQUAL(4, f.target->size());
2✔
1488
        CHECK(f.target->is_valid(f.target_keys[0]));
2✔
1489
        CHECK(!f.target->is_valid(f.target_keys[1]));
2✔
1490
        CHECK(!f.target->is_valid(f.target_keys[2]));
2✔
1491
        CHECK(f.target->is_valid(f.target_keys[3]));
2✔
1492
        CHECK(f.target->is_valid(f.target_keys[4]));
2✔
1493
        CHECK(f.target->is_valid(f.target_keys[5]));
2✔
1494
        CHECK_EQUAL(f.target_keys[0], f.linklists[0]->get(0));
2✔
1495
        // CHECK_EQUAL(f.target_keys[1], f.linklists[1]->get(0));
1✔
1496
        // CHECK_EQUAL(f.target_keys[2], f.linklists[1]->get(1));
1✔
1497
        CHECK_EQUAL(f.target_keys[3], f.linklists[2]->get(0));
2✔
1498
        CHECK_EQUAL(f.target_keys[4], f.linklists[2]->get(1));
2✔
1499
        CHECK_EQUAL(f.target_keys[5], f.linklists[2]->get(2));
2✔
1500
        f.group.verify();
2✔
1501
    }
2✔
1502
    {
2✔
1503
        Fixture f;
2✔
1504
        f.get_origin_obj(2).remove();
2✔
1505
        CHECK_EQUAL(2, f.origin->size());
2✔
1506
        CHECK_EQUAL(3, f.target->size());
2✔
1507
        CHECK(f.target->is_valid(f.target_keys[0]));
2✔
1508
        CHECK(f.target->is_valid(f.target_keys[1]));
2✔
1509
        CHECK(f.target->is_valid(f.target_keys[2]));
2✔
1510
        CHECK(!f.target->is_valid(f.target_keys[3]));
2✔
1511
        CHECK(!f.target->is_valid(f.target_keys[4]));
2✔
1512
        CHECK(!f.target->is_valid(f.target_keys[5]));
2✔
1513
        CHECK_EQUAL(f.target_keys[0], f.linklists[0]->get(0));
2✔
1514
        CHECK_EQUAL(f.target_keys[1], f.linklists[1]->get(0));
2✔
1515
        CHECK_EQUAL(f.target_keys[2], f.linklists[1]->get(1));
2✔
1516
        // CHECK_EQUAL(f.target_keys[3], f.linklists[2]->get(0));
1✔
1517
        // CHECK_EQUAL(f.target_keys[4], f.linklists[2]->get(1));
1✔
1518
        // CHECK_EQUAL(f.target_keys[5], f.linklists[2]->get(2));
1✔
1519
        f.group.verify();
2✔
1520
    }
2✔
1521
    // Break link by clearing table
1✔
1522
    {
2✔
1523
        Fixture f;
2✔
1524
        f.origin->clear();
2✔
1525
        CHECK(!f.target->is_valid(f.target_keys[0]));
2✔
1526
        CHECK(!f.target->is_valid(f.target_keys[1]));
2✔
1527
        CHECK(!f.target->is_valid(f.target_keys[2]));
2✔
1528
        CHECK(!f.target->is_valid(f.target_keys[3]));
2✔
1529
        CHECK(!f.target->is_valid(f.target_keys[4]));
2✔
1530
        CHECK(!f.target->is_valid(f.target_keys[5]));
2✔
1531
        CHECK_EQUAL(0, f.target->size());
2✔
1532
        f.group.verify();
2✔
1533
    }
2✔
1534
}
2✔
1535

1536
TEST(Links_LinkList_Swap)
1537
{
2✔
1538
    struct Fixture {
2✔
1539
        Group group;
2✔
1540
        TableRef origin = group.add_table("origin");
2✔
1541
        TableRef target = group.add_table("target");
2✔
1542
        LnkLstPtr link_list_1;
2✔
1543
        LnkLstPtr link_list_2;
2✔
1544
        ObjKeys okeys;
2✔
1545
        ObjKeys tkeys;
2✔
1546
        Fixture()
2✔
1547
        {
6✔
1548
            auto col_link = origin->add_column_list(*target, "");
6✔
1549
            target->add_column(type_Int, "");
6✔
1550
            origin->create_objects(2, okeys);
6✔
1551
            target->create_objects(2, tkeys);
6✔
1552
            link_list_1 = origin->get_object(okeys[0]).get_linklist_ptr(col_link);
6✔
1553
            link_list_1->add(tkeys[0]);
6✔
1554
            link_list_1->add(tkeys[1]);
6✔
1555
            link_list_2 = origin->get_object(okeys[1]).get_linklist_ptr(col_link); // Leave it empty
6✔
1556
        }
6✔
1557
    };
2✔
1558

1✔
1559
    // Sanity
1✔
1560
    {
2✔
1561
        Fixture f;
2✔
1562
        CHECK_EQUAL(2, f.link_list_1->size());
2✔
1563
        CHECK_EQUAL(f.tkeys[0], f.link_list_1->get(0));
2✔
1564
        CHECK_EQUAL(f.tkeys[1], f.link_list_1->get(1));
2✔
1565
        CHECK_EQUAL(0, f.link_list_2->size());
2✔
1566
        f.group.verify();
2✔
1567
    }
2✔
1568

1✔
1569
    // No-op
1✔
1570
    {
2✔
1571
        Fixture f;
2✔
1572
        f.link_list_1->swap(0, 0);
2✔
1573
        CHECK_EQUAL(2, f.link_list_1->size());
2✔
1574
        CHECK_EQUAL(f.tkeys[0], f.link_list_1->get(0));
2✔
1575
        CHECK_EQUAL(f.tkeys[1], f.link_list_1->get(1));
2✔
1576
        f.link_list_1->swap(1, 1);
2✔
1577
        CHECK_EQUAL(2, f.link_list_1->size());
2✔
1578
        CHECK_EQUAL(f.tkeys[0], f.link_list_1->get(0));
2✔
1579
        CHECK_EQUAL(f.tkeys[1], f.link_list_1->get(1));
2✔
1580
        f.group.verify();
2✔
1581
    }
2✔
1582

1✔
1583
    // Both orders of arguments mean the same this
1✔
1584
    {
2✔
1585
        Fixture f;
2✔
1586
        f.link_list_1->swap(0, 1);
2✔
1587
        CHECK_EQUAL(2, f.link_list_1->size());
2✔
1588
        CHECK_EQUAL(f.tkeys[1], f.link_list_1->get(0));
2✔
1589
        CHECK_EQUAL(f.tkeys[0], f.link_list_1->get(1));
2✔
1590
        f.link_list_1->swap(1, 0);
2✔
1591
        CHECK_EQUAL(2, f.link_list_1->size());
2✔
1592
        CHECK_EQUAL(f.tkeys[0], f.link_list_1->get(0));
2✔
1593
        CHECK_EQUAL(f.tkeys[1], f.link_list_1->get(1));
2✔
1594
        f.group.verify();
2✔
1595
    }
2✔
1596
}
2✔
1597

1598

1599
TEST(Links_DetachedAccessor)
1600
{
2✔
1601
    Group group;
2✔
1602
    TableRef table = group.add_table("table");
2✔
1603
    auto col = table->add_column_list(*table, "l");
2✔
1604
    Obj obj = table->create_object();
2✔
1605
    auto link_list = obj.get_linklist(col);
2✔
1606
    link_list.add(obj.get_key());
2✔
1607
    link_list.add(obj.get_key());
2✔
1608
    group.remove_table("table");
2✔
1609

1✔
1610
    CHECK_EQUAL(link_list.size(), 0);
2✔
1611
    CHECK_NOT(link_list.is_attached());
2✔
1612
}
2✔
1613

1614
TEST(Unresolved_Mixed_links)
1615
{
2✔
1616
    Group g;
2✔
1617

1✔
1618
    auto source_table = g.add_table_with_primary_key("source", type_Int, "source_id");
2✔
1619
    auto dest_table = g.add_table_with_primary_key("dest", type_Int, "dest_id");
2✔
1620
    auto list_mixed_col = source_table->add_column_list(type_Mixed, "list of mixed");
2✔
1621
    auto set_of_mixed_col = source_table->add_column_set(type_Mixed, "set of mixed");
2✔
1622

1✔
1623
    auto source_obj = source_table->create_object_with_primary_key(0);
2✔
1624
    auto dest_obj = dest_table->create_object_with_primary_key(1);
2✔
1625

1✔
1626
    Lst<Mixed> list_mixed = source_obj.get_list<Mixed>(list_mixed_col);
2✔
1627
    Set<Mixed> set_mixed = source_obj.get_set<Mixed>(set_of_mixed_col);
2✔
1628

1✔
1629
    list_mixed.add(ObjLink{dest_table->get_key(), dest_obj.get_key()});
2✔
1630
    set_mixed.insert(ObjLink{dest_table->get_key(), dest_obj.get_key()});
2✔
1631

1✔
1632
    size_t expected_size = 1;
2✔
1633
    CHECK_EQUAL(list_mixed.size(), expected_size);
2✔
1634
    CHECK_EQUAL(set_mixed.size(), expected_size);
2✔
1635

1✔
1636
    dest_obj.invalidate(); // send to graveyard, and transform all incoming links to be unresolved
2✔
1637

1✔
1638
    // difference between list/set of links and lst/set of Mixed, size returns the whole list of links also the
1✔
1639
    // null/unresolved ones
1✔
1640
    expected_size = 1;
2✔
1641
    CHECK_EQUAL(list_mixed.size(), expected_size);
2✔
1642
    CHECK_EQUAL(set_mixed.size(), expected_size);
2✔
1643

1✔
1644
    if (list_mixed.size()) {
2✔
1645
        auto link = list_mixed.get(0);
2✔
1646
        CHECK(link.is_null());
2✔
1647
    }
2✔
1648
    if (set_mixed.size()) {
2✔
1649
        auto link = set_mixed.get(0);
2✔
1650
        // set do not hide unresolved links
1✔
1651
        CHECK(!link.is_null());
2✔
1652
        CHECK(link.is_unresolved_link());
2✔
1653
    }
2✔
1654
}
2✔
1655

1656
// TODO: add tests here
1657

1658
#endif // TEST_LINKS
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