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

realm / realm-core / 1845

17 Nov 2023 10:56AM UTC coverage: 91.697% (+0.04%) from 91.661%
1845

push

Evergreen

web-flow
Merge pull request #7144 from realm/tg/set-fixes

Use typed comparisons for set algebra on strings and binary

92280 of 169116 branches covered (0.0%)

256 of 263 new or added lines in 4 files covered. (97.34%)

79 existing lines in 17 files now uncovered.

231307 of 252252 relevant lines covered (91.7%)

6180067.37 hits per line

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

99.55
/test/test_set.cpp
1
/*************************************************************************
2
 *
3
 * Copyright 2020 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
#include <realm/array_mixed.hpp>
23

24
#include "test.hpp"
25
#include "test_types_helper.hpp"
26

27
using namespace realm;
28
using namespace realm::util;
29
using namespace realm::test_util;
30

31
extern unsigned int unit_test_random_seed;
32

33
TEST(Set_Basics)
34
{
2✔
35
    Group g;
2✔
36

1✔
37
    auto t = g.add_table("foo");
2✔
38
    auto col_int = t->add_column_set(type_Int, "ints");
2✔
39
    auto col_str = t->add_column_set(type_String, "strings");
2✔
40
    auto col_any = t->add_column_set(type_Mixed, "any");
2✔
41
    CHECK(col_int.is_set());
2✔
42
    CHECK(col_str.is_set());
2✔
43
    CHECK(col_any.is_set());
2✔
44

1✔
45
    auto obj = t->create_object();
2✔
46
    {
2✔
47
        auto s = obj.get_set<Int>(col_int);
2✔
48
        s.insert(5);
2✔
49
        CHECK_EQUAL(s.size(), 1);
2✔
50
        s.insert(10);
2✔
51
        CHECK_EQUAL(s.size(), 2);
2✔
52
        s.insert(5);
2✔
53
        CHECK_EQUAL(s.size(), 2);
2✔
54
        auto ndx = s.find(5);
2✔
55
        CHECK_NOT_EQUAL(ndx, realm::npos);
2✔
56
        auto [erased_ndx, erased] = s.erase(5);
2✔
57
        CHECK(erased);
2✔
58
        CHECK_EQUAL(erased_ndx, 0);
2✔
59
        CHECK_EQUAL(s.size(), 1);
2✔
60
    }
2✔
61

1✔
62
    {
2✔
63
        auto s = obj.get_set<String>(col_str);
2✔
64
        s.insert("Hello");
2✔
65
        CHECK_EQUAL(s.size(), 1);
2✔
66
        s.insert("World");
2✔
67
        CHECK_EQUAL(s.size(), 2);
2✔
68
        s.insert("Hello");
2✔
69
        CHECK_EQUAL(s.size(), 2);
2✔
70
        CHECK_THROW_ANY(s.insert(StringData{}));
2✔
71
        auto ndx = s.find("Hello");
2✔
72
        CHECK_NOT_EQUAL(ndx, realm::npos);
2✔
73
        auto [erased_ndx, erased] = s.erase("Hello");
2✔
74
        CHECK(erased);
2✔
75
        CHECK_EQUAL(erased_ndx, 0);
2✔
76
        CHECK_EQUAL(s.size(), 1);
2✔
77
    }
2✔
78
    {
2✔
79
        auto s = obj.get_set<Mixed>(col_any);
2✔
80
        s.insert(Mixed("Hello"));
2✔
81
        CHECK_EQUAL(s.size(), 1);
2✔
82
        s.insert(Mixed(10));
2✔
83
        CHECK_EQUAL(s.size(), 2);
2✔
84
        s.insert(Mixed("Hello"));
2✔
85
        CHECK_EQUAL(s.size(), 2);
2✔
86
        s.insert(BinaryData("Hello", 5));
2✔
87
        CHECK_EQUAL(s.size(), 3);
2✔
88
        auto ndx = s.find(Mixed("Hello"));
2✔
89
        auto ndx2 = s.find(Mixed(BinaryData("Hello", 5)));
2✔
90
        CHECK_NOT_EQUAL(ndx, realm::npos);
2✔
91
        CHECK_NOT_EQUAL(ndx2, realm::npos);
2✔
92
        CHECK_NOT_EQUAL(ndx, ndx2);
2✔
93
        auto [erased_ndx, erased] = s.erase(Mixed("Hello"));
2✔
94
        CHECK(erased);
2✔
95
        CHECK_EQUAL(erased_ndx, 1);
2✔
96
        CHECK_EQUAL(s.size(), 2);
2✔
97
    }
2✔
98
}
2✔
99

100

101
TEST(Set_Mixed)
102
{
2✔
103
    Group g;
2✔
104

1✔
105
    Obj bar = g.add_table("bar")->create_object();
2✔
106
    auto t = g.add_table("foo");
2✔
107
    t->add_column_set(type_Mixed, "mixeds");
2✔
108
    auto obj = t->create_object();
2✔
109

1✔
110
    // Check that different typed with same numeric value are treated as the same
1✔
111
    auto set = obj.get_set<Mixed>("mixeds");
2✔
112
    set.insert(123);
2✔
113
    set.insert(123.f);
2✔
114
    set.insert(123.);
2✔
115
    set.insert(Decimal128("123"));
2✔
116
    CHECK_EQUAL(set.size(), 1);
2✔
117
    CHECK_EQUAL(set.get(0), Mixed(123));
2✔
118
    set.clear();
2✔
119

1✔
120
    std::vector<Mixed> ref_values{{},
2✔
121
                                  false,
2✔
122
                                  true,
2✔
123
                                  Decimal128("-123"),
2✔
124
                                  25,
2✔
125
                                  56.f,
2✔
126
                                  88.,
2✔
127
                                  "Hello, World!",
2✔
128
                                  "æbler", // Apples
2✔
129
                                  "ørken", // Dessert
2✔
130
                                  "ådsel", // Carrion
2✔
131
                                  Timestamp(1, 2),
2✔
132
                                  ObjectId::gen(),
2✔
133
                                  UUID("01234567-9abc-4def-9012-3456789abcde"),
2✔
134
                                  bar.get_link()};
2✔
135
    // Sets of Mixed should be ordered by the rules defined for Set<Mixed>. Refer to "realm/set.hpp".
1✔
136
    std::vector<size_t> indices(ref_values.size());
2✔
137
    std::iota(indices.begin(), indices.end(), 0);
2✔
138
    std::shuffle(indices.begin(), indices.end(), std::mt19937(unit_test_random_seed));
2✔
139
    for (auto i : indices) {
30✔
140
        set.insert(ref_values[i]);
30✔
141
    }
30✔
142
    std::vector<Mixed> actuals;
2✔
143
    std::for_each(set.begin(), set.end(), [&actuals](auto v) {
30✔
144
        actuals.push_back(v);
30✔
145
    });
30✔
146
    CHECK(ref_values == actuals);
2✔
147
    actuals.clear();
2✔
148

1✔
149
    // Sets of Mixed can be sorted. Should sort according to the comparison rules defined for Mixed, which
1✔
150
    // currently
1✔
151
    set.sort(indices);
2✔
152
    std::transform(begin(indices), end(indices), std::back_inserter(actuals), [&](size_t index) {
30✔
153
        return set.get(index);
30✔
154
    });
30✔
155
    std::sort(begin(ref_values), end(ref_values), [](auto v1, auto v2) {
42✔
156
        return v1 < v2;
42✔
157
    });
42✔
158
    CHECK(ref_values == actuals);
2✔
159
}
2✔
160

161
TEST(Set_Mixed_SortStringAndBinary)
162
{
2✔
163
    Group g;
2✔
164
    auto table = g.add_table("table");
2✔
165
    auto col = table->add_column_set(type_Mixed, "set");
2✔
166
    auto obj = table->create_object();
2✔
167
    auto set = obj.get_set<Mixed>(col);
2✔
168

1✔
169
    std::vector<size_t> indices = {1};
2✔
170

1✔
171
    // Empty set
1✔
172
    set.sort(indices);
2✔
173
    CHECK(indices.empty());
2✔
174

1✔
175
    // Strings only
1✔
176
    set.insert("c");
2✔
177
    set.insert("e");
2✔
178
    set.insert("a");
2✔
179
    set.sort(indices, true);
2✔
180
    CHECK_EQUAL(indices, (std::vector<size_t>{0, 1, 2}));
2✔
181
    set.sort(indices, false);
2✔
182
    CHECK_EQUAL(indices, (std::vector<size_t>{2, 1, 0}));
2✔
183

1✔
184
    // Non-strings surrounding the strings
1✔
185
    set.insert(0);
2✔
186
    set.insert(UUID());
2✔
187
    set.sort(indices, true);
2✔
188
    CHECK_EQUAL(indices, (std::vector<size_t>{0, 1, 2, 3, 4}));
2✔
189
    set.sort(indices, false);
2✔
190
    CHECK_EQUAL(indices, (std::vector<size_t>{4, 3, 2, 1, 0}));
2✔
191

1✔
192
    // Binary values which should be interleaved with the strings
1✔
193
    set.insert(BinaryData("b", 1));
2✔
194
    set.insert(BinaryData("d", 1));
2✔
195
    set.sort(indices, true);
2✔
196
    CHECK_EQUAL(indices, (std::vector<size_t>{0, 1, 4, 2, 5, 3, 6}));
2✔
197
    set.sort(indices, false);
2✔
198
    CHECK_EQUAL(indices, (std::vector<size_t>{6, 3, 5, 2, 4, 1, 0}));
2✔
199

1✔
200
    // Non-empty but no strings
1✔
201
    set.clear();
2✔
202
    set.insert(1);
2✔
203
    set.insert(2);
2✔
204
    set.sort(indices, true);
2✔
205
    CHECK_EQUAL(indices, (std::vector<size_t>{0, 1}));
2✔
206
    set.sort(indices, false);
2✔
207
    CHECK_EQUAL(indices, (std::vector<size_t>{1, 0}));
2✔
208
}
2✔
209

210
TEST(Set_LinksRemoveBacklinks)
211
{
2✔
212
    SHARED_GROUP_TEST_PATH(path);
2✔
213
    std::unique_ptr<Replication> hist(make_in_realm_history());
2✔
214
    DBRef sg = DB::create(*hist, path, DBOptions(crypt_key()));
2✔
215

1✔
216
    auto wt = sg->start_write();
2✔
217
    auto persons = wt->add_table_with_primary_key("class_person", type_String, "name");
2✔
218
    auto collections = wt->add_table("class_Collection");
2✔
219
    auto col_set = collections->add_column_set(*persons, "Link");
2✔
220
    wt->commit_and_continue_as_read();
2✔
221

1✔
222
    wt->promote_to_write();
2✔
223
    auto collection = collections->create_object();
2✔
224
    auto person = persons->create_object_with_primary_key("Per");
2✔
225
    wt->commit_and_continue_as_read();
2✔
226

1✔
227
    wt->promote_to_write();
2✔
228
    collection.get_linkset(col_set).insert(person.get_key());
2✔
229
    wt->commit_and_continue_as_read();
2✔
230

1✔
231
    wt->promote_to_write();
2✔
232
    collection.remove();
2✔
233
    wt->commit_and_continue_as_read();
2✔
234

1✔
235
    wt->promote_to_write();
2✔
236
    person.remove();
2✔
237
    wt->commit();
2✔
238
}
2✔
239

240
TEST(Set_Links)
241
{
2✔
242
    Group g;
2✔
243
    auto foos = g.add_table("class_Foo");
2✔
244
    auto bars = g.add_table("class_Bar");
2✔
245
    auto cabs = g.add_table("class_Cab");
2✔
246

1✔
247
    ColKey col_links = foos->add_column_set(*bars, "links");
2✔
248
    ColKey col_typed_links = foos->add_column_set(type_TypedLink, "typed_links");
2✔
249
    ColKey col_mixeds = foos->add_column_set(type_Mixed, "mixeds");
2✔
250

1✔
251
    auto foo = foos->create_object();
2✔
252

1✔
253
    auto bar1 = bars->create_object();
2✔
254
    auto bar2 = bars->create_object();
2✔
255
    auto bar3 = bars->create_object();
2✔
256
    auto bar4 = bars->create_object();
2✔
257

1✔
258
    auto cab1 = cabs->create_object();
2✔
259
    auto cab2 = cabs->create_object();
2✔
260
    cabs->create_object();
2✔
261

1✔
262
    auto set_links = foo.get_set<ObjKey>(col_links);
2✔
263
    auto lnkset_links = foo.get_setbase_ptr(col_links);
2✔
264
    auto set_typed_links = foo.get_set<ObjLink>(col_typed_links);
2✔
265
    auto set_mixeds = foo.get_set<Mixed>(col_mixeds);
2✔
266

1✔
267
    set_links.insert(bar1.get_key());
2✔
268
    set_links.insert(bar2.get_key());
2✔
269
    set_links.insert(bar3.get_key());
2✔
270
    set_links.insert(bar1.get_key());
2✔
271
    set_links.insert(bar2.get_key());
2✔
272
    set_links.insert(bar3.get_key());
2✔
273

1✔
274
    CHECK_EQUAL(set_links.size(), 3);
2✔
275
    CHECK_EQUAL(bar1.get_backlink_count(), 1);
2✔
276
    CHECK_NOT_EQUAL(set_links.find(bar1.get_key()), realm::npos);
2✔
277
    CHECK_NOT_EQUAL(set_links.find(bar2.get_key()), realm::npos);
2✔
278
    CHECK_NOT_EQUAL(set_links.find(bar3.get_key()), realm::npos);
2✔
279
    CHECK_EQUAL(set_links.find(bar4.get_key()), realm::npos);
2✔
280
    CHECK_THROW_ANY(set_links.insert({}));
2✔
281
    set_links.erase(bar1.get_key());
2✔
282
    CHECK_EQUAL(bar1.get_backlink_count(), 0);
2✔
283
    set_links.insert(bar1.get_key());
2✔
284

1✔
285
    set_typed_links.insert(bar1.get_link());
2✔
286
    set_typed_links.insert(bar2.get_link());
2✔
287
    set_typed_links.insert(cab1.get_link());
2✔
288
    set_typed_links.insert(cab2.get_link());
2✔
289
    CHECK_EQUAL(set_typed_links.size(), 4);
2✔
290

1✔
291
    set_typed_links.insert(bar1.get_link());
2✔
292
    CHECK_EQUAL(set_typed_links.size(), 4);
2✔
293
    set_typed_links.insert(bar2.get_link());
2✔
294
    CHECK_EQUAL(set_typed_links.size(), 4);
2✔
295
    set_typed_links.insert(cab1.get_link());
2✔
296
    CHECK_EQUAL(set_typed_links.size(), 4);
2✔
297
    set_typed_links.insert(cab2.get_link());
2✔
298
    CHECK_EQUAL(set_typed_links.size(), 4);
2✔
299

1✔
300
    CHECK_EQUAL(bar1.get_backlink_count(), 2);
2✔
301
    CHECK_NOT_EQUAL(set_typed_links.find(bar1.get_link()), realm::npos);
2✔
302
    CHECK_NOT_EQUAL(set_typed_links.find(bar2.get_link()), realm::npos);
2✔
303
    CHECK_NOT_EQUAL(set_typed_links.find(cab1.get_link()), realm::npos);
2✔
304
    CHECK_NOT_EQUAL(set_typed_links.find(cab2.get_link()), realm::npos);
2✔
305
    CHECK_EQUAL(set_typed_links.find(bar3.get_link()), realm::npos);
2✔
306

1✔
307
    set_mixeds.insert(bar1.get_link());
2✔
308
    set_mixeds.insert(bar2.get_link());
2✔
309
    set_mixeds.insert(cab1.get_link());
2✔
310
    set_mixeds.insert(cab2.get_link());
2✔
311
    set_mixeds.insert(bar1.get_link());
2✔
312
    set_mixeds.insert(bar2.get_link());
2✔
313
    set_mixeds.insert(cab1.get_link());
2✔
314
    set_mixeds.insert(cab2.get_link());
2✔
315

1✔
316
    CHECK_EQUAL(set_mixeds.size(), 4);
2✔
317
    CHECK_EQUAL(bar1.get_backlink_count(), 3);
2✔
318
    CHECK_NOT_EQUAL(set_mixeds.find(bar1.get_link()), realm::npos);
2✔
319
    CHECK_NOT_EQUAL(set_mixeds.find(bar2.get_link()), realm::npos);
2✔
320
    CHECK_NOT_EQUAL(set_mixeds.find(cab1.get_link()), realm::npos);
2✔
321
    CHECK_NOT_EQUAL(set_mixeds.find(cab2.get_link()), realm::npos);
2✔
322
    CHECK_EQUAL(set_mixeds.find(bar3.get_link()), realm::npos);
2✔
323

1✔
324
    bar1.remove();
2✔
325
    set_links.insert(bar4.get_key());
2✔
326

1✔
327
    CHECK_EQUAL(set_links.size(), 3);
2✔
328
    CHECK_EQUAL(set_typed_links.size(), 3);
2✔
329
    CHECK_EQUAL(set_mixeds.size(), 3);
2✔
330

1✔
331
    CHECK_EQUAL(set_links.find(bar1.get_key()), realm::npos);
2✔
332
    CHECK_EQUAL(set_typed_links.find(bar1.get_link()), realm::npos);
2✔
333
    CHECK_EQUAL(set_mixeds.find(bar1.get_link()), realm::npos);
2✔
334

1✔
335
    auto bar2_key = bar2.get_key();
2✔
336
    auto bar2_link = bar2.get_link();
2✔
337
    bar2.invalidate();
2✔
338

1✔
339
    CHECK_EQUAL(set_links.size(), 3);
2✔
340
    CHECK_EQUAL(lnkset_links->size(), 2); // Unresolved link was hidden from LnkSet
2✔
341
    CHECK_EQUAL(set_typed_links.size(), 3);
2✔
342
    CHECK_EQUAL(set_mixeds.size(), 3);
2✔
343

1✔
344
    CHECK_EQUAL(set_links.find(bar2_key), realm::npos);               // The original bar2 key is no longer in the set
2✔
345
    CHECK_NOT_EQUAL(set_links.find(bar2.get_key()), realm::npos);     // The unresolved bar2 key is in the set
2✔
346
    CHECK_EQUAL(lnkset_links->find_any(bar2.get_key()), realm::npos); // The unresolved bar2 key is hidden by LnkSet
2✔
347
    CHECK_EQUAL(lnkset_links->find_any(bar3.get_key()), 0);
2✔
348
    CHECK_EQUAL(lnkset_links->find_any(bar4.get_key()), 1);
2✔
349
    CHECK_EQUAL(set_typed_links.find(bar2_link), realm::npos);
2✔
350
    CHECK_EQUAL(set_mixeds.find(bar2_link), realm::npos);
2✔
351

1✔
352
    // g.to_json(std::cout);
1✔
353
    foos->clear();
2✔
354
    g.verify();
2✔
355
}
2✔
356

357
TEST_TYPES(Set_Types, Prop<Int>, Prop<String>, Prop<Float>, Prop<Double>, Prop<Timestamp>, Prop<UUID>, Prop<ObjectId>,
358
           Prop<Decimal128>, Prop<BinaryData>, Prop<Mixed>, Nullable<Int>, Nullable<String>, Nullable<Float>,
359
           Nullable<Double>, Nullable<Timestamp>, Nullable<UUID>, Nullable<ObjectId>, Nullable<Decimal128>,
360
           Nullable<BinaryData>)
361
{
38✔
362
    using type = typename TEST_TYPE::type;
38✔
363
    TestValueGenerator gen;
38✔
364
    Group g;
38✔
365

19✔
366
    auto t = g.add_table("foo");
38✔
367
    auto col = t->add_column_set(TEST_TYPE::data_type, "values", TEST_TYPE::is_nullable);
38✔
368
    auto col_list = t->add_column_list(TEST_TYPE::data_type, "list", TEST_TYPE::is_nullable);
38✔
369
    CHECK(col.is_set());
38✔
370

19✔
371
    auto obj = t->create_object();
38✔
372
    auto values = gen.values_from_int<type>({0, 1, 2, 3});
38✔
373

19✔
374
    auto l = obj.get_list<type>(col_list);
38✔
375
    for (auto&& v : values) {
152✔
376
        l.add(v);
152✔
377
    }
152✔
378

19✔
379
    auto s = obj.get_set<type>(col);
38✔
380
    auto populate_set = [&] {
152✔
381
        s.clear();
152✔
382
        for (auto&& v : values) {
608✔
383
            s.insert(v);
608✔
384
        }
608✔
385
    };
152✔
386

19✔
387
    populate_set();
38✔
388
    auto sz = values.size();
38✔
389
    CHECK_EQUAL(s.size(), sz);
38✔
390
    auto s1 = s;
38✔
391
    CHECK_EQUAL(s1.size(), sz);
38✔
392
    CHECK(s.set_equals(l));
38✔
393
    for (auto v : values) {
152✔
394
        auto ndx = s.find(v);
152✔
395
        CHECK_NOT_EQUAL(ndx, realm::npos);
152✔
396
    }
152✔
397

19✔
398
    auto [erased_ndx, erased] = s.erase(values[0]);
38✔
399
    CHECK(erased);
38✔
400
    CHECK_EQUAL(erased_ndx, 0);
38✔
401
    CHECK_EQUAL(s.size(), values.size() - 1);
38✔
402
    CHECK(s.is_subset_of(l));
38✔
403

19✔
404
    s.clear();
38✔
405
    CHECK_EQUAL(s.size(), 0);
38✔
406

19✔
407
    // Union and intersection with self is a no-op
19✔
408
    populate_set();
38✔
409
    s.assign_union(s);
38✔
410
    CHECK_EQUAL(s.size(), sz);
38✔
411
    s.assign_intersection(s);
38✔
412
    CHECK_EQUAL(s.size(), sz);
38✔
413

19✔
414
    // Difference with self is clear()
19✔
415
    populate_set();
38✔
416
    s.assign_difference(s);
38✔
417
    CHECK_EQUAL(s.size(), 0);
38✔
418

19✔
419
    populate_set();
38✔
420
    s.assign_symmetric_difference(s);
38✔
421
    CHECK_EQUAL(s.size(), 0);
38✔
422

19✔
423
    if (TEST_TYPE::is_nullable) {
38✔
424
        s.insert_null();
18✔
425
        CHECK_EQUAL(s.size(), 1);
18✔
426
        auto null_value = TEST_TYPE::default_value();
18✔
427
        CHECK(value_is_null(null_value));
18✔
428
        auto ndx = s.find(null_value);
18✔
429
        CHECK_NOT_EQUAL(ndx, realm::npos);
18✔
430
        s.erase_null();
18✔
431
        CHECK_EQUAL(s.size(), 0);
18✔
432
        ndx = s.find(null_value);
18✔
433
        CHECK_EQUAL(ndx, realm::npos);
18✔
434
    }
18✔
435
}
38✔
436

437
TEST(Set_BinarySets)
438
{
2✔
439
    SHARED_GROUP_TEST_PATH(path);
2✔
440
    DBRef sg = DB::create(path);
2✔
441

1✔
442
    {
2✔
443
        WriteTransaction wt{sg};
2✔
444
        TableRef foo = wt.add_table("class_foo");
2✔
445
        auto set1 = foo->add_column_set(type_Binary, "bin1");
2✔
446
        auto set2 = foo->add_column_set(type_Binary, "bin2");
2✔
447

1✔
448
        auto obj = foo->create_object();
2✔
449
        auto s1 = obj.get_set<Binary>(set1);
2✔
450
        auto s2 = obj.get_set<Binary>(set2);
2✔
451
        wt.commit();
2✔
452
    }
2✔
453
    {
2✔
454
        WriteTransaction wt{sg};
2✔
455
        TableRef foo = wt.get_or_add_table("class_foo");
2✔
456
        auto set1 = foo->get_column_key("bin1");
2✔
457
        auto set2 = foo->get_column_key("bin2");
2✔
458

1✔
459
        auto s1 = foo->begin()->get_set<Binary>(set1);
2✔
460
        auto s2 = foo->begin()->get_set<Binary>(set2);
2✔
461

1✔
462
        std::string str1(1024, 'a');
2✔
463
        std::string str2(256, 'b');
2✔
464
        std::string str3(64, 'c');
2✔
465
        std::string str4(256, 'd');
2✔
466
        std::string str5(1024, 'e');
2✔
467

1✔
468
        s1.insert(BinaryData(str1.data(), str1.size()));
2✔
469
        s1.insert(BinaryData(str2.data(), str2.size()));
2✔
470
        s1.insert(BinaryData(str3.data(), str3.size()));
2✔
471

1✔
472
        s2.insert(BinaryData(str3.data(), str3.size()));
2✔
473
        s2.insert(BinaryData(str4.data(), str4.size()));
2✔
474
        s2.insert(BinaryData(str5.data(), str5.size()));
2✔
475
        wt.commit();
2✔
476
    }
2✔
477
    {
2✔
478
        WriteTransaction wt{sg};
2✔
479
        TableRef foo = wt.get_or_add_table("class_foo");
2✔
480
        auto set1 = foo->get_column_key("bin1");
2✔
481
        auto set2 = foo->get_column_key("bin2");
2✔
482

1✔
483
        auto s1 = foo->begin()->get_set<Binary>(set1);
2✔
484
        auto s2 = foo->begin()->get_set<Binary>(set2);
2✔
485

1✔
486
        s1.assign_intersection(s2);
2✔
487
        wt.commit();
2✔
488
    }
2✔
489

1✔
490
    sg->start_read()->verify();
2✔
491
}
2✔
492

493
TEST(Set_TableClear)
494
{
2✔
495
    SHARED_GROUP_TEST_PATH(path);
2✔
496
    DBRef sg = DB::create(path);
2✔
497

1✔
498
    {
2✔
499
        WriteTransaction wt{sg};
2✔
500
        TableRef foo = wt.add_table("class_foo");
2✔
501
        TableRef origin = wt.add_table("class_origin");
2✔
502
        auto set = origin->add_column_set(*foo, "set");
2✔
503

1✔
504
        auto obj = foo->create_object();
2✔
505
        auto s1 = origin->create_object().get_linkset(set);
2✔
506
        s1.insert(obj.get_key());
2✔
507
        wt.commit();
2✔
508
    }
2✔
509
    {
2✔
510
        WriteTransaction wt{sg};
2✔
511
        TableRef origin = wt.get_or_add_table("class_origin");
2✔
512
        auto set = origin->get_column_key("set");
2✔
513
        auto s1 = origin->begin()->get_linkset(set);
2✔
514
        s1.clear();
2✔
515
        wt.commit();
2✔
516
    }
2✔
517
    {
2✔
518
        WriteTransaction wt{sg};
2✔
519
        TableRef foo = wt.get_or_add_table("class_foo");
2✔
520
        foo->clear();
2✔
521
        wt.commit();
2✔
522
    }
2✔
523

1✔
524
    sg->start_read()->verify();
2✔
525
}
2✔
526

527
TEST(Set_LnkSetUnresolved)
528
{
2✔
529
    Group g;
2✔
530
    auto foos = g.add_table("class_Foo");
2✔
531
    auto bars = g.add_table("class_Bar");
2✔
532

1✔
533
    ColKey col_links = foos->add_column_set(*bars, "links");
2✔
534
    auto foo = foos->create_object();
2✔
535
    auto bar1 = bars->create_object();
2✔
536
    auto bar2 = bars->create_object();
2✔
537
    auto bar3 = bars->create_object();
2✔
538

1✔
539
    auto key_set = foo.get_set<ObjKey>(col_links);
2✔
540
    auto link_set = foo.get_linkset(col_links);
2✔
541

1✔
542
    link_set.insert(bar1.get_key());
2✔
543
    link_set.insert(bar2.get_key());
2✔
544
    link_set.insert(bar1.get_key());
2✔
545
    link_set.insert(bar2.get_key());
2✔
546

1✔
547
    CHECK_EQUAL(key_set.size(), 2);
2✔
548
    CHECK_EQUAL(link_set.size(), 2);
2✔
549
    CHECK_EQUAL(key_set.find(bar1.get_key()), 0);
2✔
550
    CHECK_EQUAL(key_set.find(bar2.get_key()), 1);
2✔
551
    CHECK_EQUAL(link_set.find(bar1.get_key()), 0);
2✔
552
    CHECK_EQUAL(link_set.find(bar2.get_key()), 1);
2✔
553

1✔
554
    bar2.invalidate();
2✔
555

1✔
556
    CHECK_EQUAL(key_set.size(), 2);
2✔
557
    CHECK_EQUAL(link_set.size(), 1);
2✔
558
    CHECK_EQUAL(key_set.find(bar2.get_key()), 0);
2✔
559
    CHECK_EQUAL(key_set.find(bar1.get_key()), 1);
2✔
560
    CHECK_EQUAL(link_set.find(bar1.get_key()), 0);
2✔
561
    CHECK_EQUAL(link_set.find(bar2.get_key()), not_found);
2✔
562

1✔
563
    link_set.insert(bar3.get_key());
2✔
564

1✔
565
    CHECK_EQUAL(key_set.size(), 3);
2✔
566
    CHECK_EQUAL(link_set.size(), 2);
2✔
567

1✔
568
    CHECK_EQUAL(key_set.find(bar2.get_key()), 0);
2✔
569
    CHECK_EQUAL(key_set.find(bar1.get_key()), 1);
2✔
570
    CHECK_EQUAL(key_set.find(bar3.get_key()), 2);
2✔
571

1✔
572
    CHECK_EQUAL(link_set.find(bar1.get_key()), 0);
2✔
573
    CHECK_EQUAL(link_set.find(bar2.get_key()), not_found);
2✔
574
    CHECK_EQUAL(link_set.find(bar3.get_key()), 1);
2✔
575

1✔
576
    CHECK_EQUAL(link_set.get(0), bar1.get_key());
2✔
577
    CHECK_EQUAL(link_set.get(1), bar3.get_key());
2✔
578

1✔
579
    std::vector<size_t> found;
2✔
580
    link_set.find_all(bar3.get_key(), [&](size_t ndx) {
2✔
581
        found.push_back(ndx);
2✔
582
    });
2✔
583
    CHECK_EQUAL(found.size(), 1);
2✔
584
    CHECK_EQUAL(found[0], 1);
2✔
585
}
2✔
586

587
TEST(Set_Union)
588
{
2✔
589
    Group g;
2✔
590
    auto foos = g.add_table("class_Foo");
2✔
591
    ColKey col_set = foos->add_column_set(type_Int, "int_set");
2✔
592
    ColKey col_list = foos->add_column_list(type_Int, "int_list");
2✔
593

1✔
594
    auto obj1 = foos->create_object();
2✔
595
    auto obj2 = foos->create_object();
2✔
596

1✔
597
    auto set1 = obj1.get_set<int64_t>(col_set);
2✔
598
    auto set2 = obj2.get_set<int64_t>(col_set);
2✔
599

1✔
600
    for (int64_t x : {1, 2, 4, 5}) {
8✔
601
        set1.insert(x);
8✔
602
    }
8✔
603

1✔
604
    for (int64_t x : {3, 4, 5}) {
6✔
605
        set2.insert(x);
6✔
606
    }
6✔
607

1✔
608
    auto list = obj1.get_list<int64_t>(col_list);
2✔
609

1✔
610
    for (int64_t x : {11, 3, 7, 5, 14, 7}) {
12✔
611
        list.add(x);
12✔
612
    }
12✔
613

1✔
614
    set1.assign_union(set2);
2✔
615
    CHECK_EQUAL(set1.size(), 5);
2✔
616
    CHECK_EQUAL(set1.get(0), 1);
2✔
617
    CHECK_EQUAL(set1.get(1), 2);
2✔
618
    CHECK_EQUAL(set1.get(2), 3);
2✔
619
    CHECK_EQUAL(set1.get(3), 4);
2✔
620
    CHECK_EQUAL(set1.get(4), 5);
2✔
621
    set2.assign_union(list);
2✔
622
    CHECK_EQUAL(set2.size(), 6);
2✔
623
    CHECK_EQUAL(set2.get(0), 3);
2✔
624
    CHECK_EQUAL(set2.get(5), 14);
2✔
625
}
2✔
626

627
TEST(Set_UnionString)
628
{
2✔
629
    Group g;
2✔
630
    auto foos = g.add_table("class_Foo");
2✔
631
    ColKey col_set = foos->add_column_set(type_String, "string set", true);
2✔
632
    ColKey col_list = foos->add_column_list(type_String, "string list", true);
2✔
633

1✔
634
    auto obj1 = foos->create_object();
2✔
635
    auto obj2 = foos->create_object();
2✔
636

1✔
637
    auto set1 = obj1.get_set<String>(col_set);
2✔
638
    auto set2 = obj2.get_set<String>(col_set);
2✔
639

1✔
640
    set1.insert("FooBar");
2✔
641
    set1.insert("A");
2✔
642
    set1.insert("The fox jumps over the lazy dog");
2✔
643

1✔
644
    set2.insert("FooBar");
2✔
645
    set2.insert("World");
2✔
646
    set2.insert("Atomic");
2✔
647

1✔
648
    auto list1 = obj1.get_list<String>(col_list);
2✔
649
    list1.add("FooBar");
2✔
650
    list1.add("World");
2✔
651
    list1.add({});
2✔
652
    list1.add("Atomic");
2✔
653

1✔
654
    set1.assign_union(set2);
2✔
655
    CHECK_EQUAL(set1.size(), 5);
2✔
656
    CHECK_EQUAL(set1.get(0), "A");
2✔
657
    CHECK_EQUAL(set1.get(1), "Atomic");
2✔
658
    CHECK_EQUAL(set1.get(2), "FooBar");
2✔
659
    CHECK_EQUAL(set1.get(3), "The fox jumps over the lazy dog");
2✔
660
    CHECK_EQUAL(set1.get(4), "World");
2✔
661

1✔
662
    set1.assign_union(list1);
2✔
663
    CHECK_EQUAL(set1.size(), 6);
2✔
664
    CHECK_EQUAL(set1.get(0), StringData());
2✔
665
    CHECK_EQUAL(set1.get(1), "A");
2✔
666
    CHECK_EQUAL(set1.get(2), "Atomic");
2✔
667
    CHECK_EQUAL(set1.get(3), "FooBar");
2✔
668
    CHECK_EQUAL(set1.get(4), "The fox jumps over the lazy dog");
2✔
669
    CHECK_EQUAL(set1.get(5), "World");
2✔
670
}
2✔
671

672
TEST(Set_Intersection)
673
{
2✔
674
    Group g;
2✔
675
    auto foos = g.add_table("class_Foo");
2✔
676
    ColKey col_set = foos->add_column_set(type_Int, "int set", true);
2✔
677
    ColKey col_list = foos->add_column_list(type_Int, "int list", true);
2✔
678

1✔
679
    auto obj1 = foos->create_object();
2✔
680
    auto obj2 = foos->create_object();
2✔
681
    auto obj3 = foos->create_object();
2✔
682

1✔
683
    auto set1 = obj1.get_set<util::Optional<int64_t>>(col_set);
2✔
684
    auto set2 = obj2.get_set<util::Optional<int64_t>>(col_set);
2✔
685

1✔
686
    for (int64_t x : {1, 2, 4, 5}) {
8✔
687
        set1.insert(x);
8✔
688
    }
8✔
689

1✔
690
    for (int64_t x : {3, 4, 5}) {
6✔
691
        set2.insert(x);
6✔
692
    }
6✔
693

1✔
694
    auto superset = obj1.get_list<util::Optional<int64_t>>(col_list);
2✔
695
    auto subset = obj2.get_list<util::Optional<int64_t>>(col_list);
2✔
696
    auto same_set = obj3.get_list<util::Optional<int64_t>>(col_list);
2✔
697

1✔
698
    for (int64_t x : {3, 4, 5, 1, 2}) {
10✔
699
        superset.add(x);
10✔
700
    }
10✔
701

1✔
702
    for (int64_t x : {1, 2}) {
4✔
703
        subset.add(x);
4✔
704
    }
4✔
705

1✔
706
    for (int64_t x : {1, 4, 2, 5}) {
8✔
707
        same_set.add(x);
8✔
708
    }
8✔
709

1✔
710
    CHECK(set1.intersects(set2));
2✔
711
    CHECK(set2.intersects(set1));
2✔
712
    CHECK(!set1.is_subset_of(set2));
2✔
713
    CHECK(!set2.is_subset_of(set1));
2✔
714
    CHECK(!set1.is_superset_of(set2));
2✔
715
    CHECK(!set2.is_superset_of(set1));
2✔
716
    CHECK(!set1.is_strict_superset_of(set1));
2✔
717
    CHECK(!set1.is_strict_subset_of(set1));
2✔
718
    CHECK(set1.is_subset_of(superset));
2✔
719
    CHECK(set1.is_superset_of(subset));
2✔
720
    CHECK(set1.is_strict_superset_of(subset));
2✔
721
    CHECK(set1.is_strict_subset_of(superset));
2✔
722
    CHECK(set1.is_subset_of(same_set));
2✔
723
    CHECK(set1.is_superset_of(same_set));
2✔
724
    CHECK(!set1.is_strict_superset_of(same_set));
2✔
725
    CHECK(!set1.is_strict_subset_of(same_set));
2✔
726
    CHECK(set1.set_equals(set1));
2✔
727
    CHECK(set1.set_equals(same_set));
2✔
728
    CHECK(!set1.set_equals(superset));
2✔
729
    CHECK(!set1.set_equals(superset));
2✔
730
    CHECK(!set1.set_equals(subset));
2✔
731
    CHECK(!set1.set_equals(subset));
2✔
732

1✔
733
    set1.assign_intersection(set2);
2✔
734
    CHECK_EQUAL(set1.size(), 2);
2✔
735
    CHECK_EQUAL(set1.get(0), 4);
2✔
736
    CHECK_EQUAL(set1.get(1), 5);
2✔
737
}
2✔
738

739
TEST(Set_IntersectionString)
740
{
2✔
741
    Group g;
2✔
742
    auto foos = g.add_table("class_Foo");
2✔
743
    ColKey col_strings = foos->add_column_set(type_String, "strings");
2✔
744

1✔
745
    auto obj1 = foos->create_object();
2✔
746
    auto obj2 = foos->create_object();
2✔
747

1✔
748
    auto set1 = obj1.get_set<String>(col_strings);
2✔
749
    auto set2 = obj2.get_set<String>(col_strings);
2✔
750

1✔
751
    set1.insert("FooBar");
2✔
752
    set1.insert("A");
2✔
753
    set1.insert("Hello");
2✔
754
    set1.insert("The fox jumps over the lazy dog");
2✔
755

1✔
756
    set2.insert("FooBar");
2✔
757
    set2.insert("World");
2✔
758
    set2.insert("The fox jumps over the lazy dog");
2✔
759

1✔
760
    CHECK(set1.intersects(set2));
2✔
761
    CHECK(set2.intersects(set1));
2✔
762
    CHECK(!set1.is_subset_of(set2));
2✔
763
    CHECK(!set2.is_subset_of(set1));
2✔
764
    CHECK(!set1.is_superset_of(set2));
2✔
765
    CHECK(!set2.is_superset_of(set1));
2✔
766

1✔
767
    set1.assign_intersection(set2);
2✔
768
    CHECK_EQUAL(set1.size(), 2);
2✔
769
    CHECK_EQUAL(set1.get(0), "FooBar");
2✔
770
    CHECK_EQUAL(set1.get(1), "The fox jumps over the lazy dog");
2✔
771
}
2✔
772

773
TEST(Set_Difference)
774
{
2✔
775
    Group g;
2✔
776
    auto foos = g.add_table("class_Foo");
2✔
777
    ColKey col_set = foos->add_column_set(type_Int, "int set");
2✔
778
    ColKey col_set_bin = foos->add_column_set(type_Binary, "bin set");
2✔
779
    ColKey col_set_str = foos->add_column_set(type_String, "str set");
2✔
780
    ColKey col_list = foos->add_column_list(type_Int, "int list");
2✔
781

1✔
782
    auto obj1 = foos->create_object();
2✔
783
    auto obj2 = foos->create_object();
2✔
784

1✔
785
    auto set1 = obj1.get_set<int64_t>(col_set);
2✔
786
    auto set2 = obj2.get_set<int64_t>(col_set);
2✔
787

1✔
788
    for (int64_t x : {1, 2, 4, 5}) {
8✔
789
        set1.insert(x);
8✔
790
    }
8✔
791

1✔
792
    for (int64_t x : {3, 4, 5}) {
6✔
793
        set2.insert(x);
6✔
794
    }
6✔
795

1✔
796
    set1.assign_difference(set2);
2✔
797
    CHECK_EQUAL(set1.size(), 2);
2✔
798
    CHECK_EQUAL(set1.get(0), 1);
2✔
799
    CHECK_EQUAL(set1.get(1), 2);
2✔
800

1✔
801
    set1.assign_union(set2);
2✔
802
    auto list = obj2.get_list<int64_t>(col_list);
2✔
803
    for (int64_t x : {4, 5, 1, 27}) {
8✔
804
        list.add(x);
8✔
805
    }
8✔
806
    set1.assign_difference(list);
2✔
807
    CHECK_EQUAL(set1.size(), 2);
2✔
808
    CHECK_EQUAL(set1.get(0), 2);
2✔
809
    CHECK_EQUAL(set1.get(1), 3);
2✔
810

1✔
811
    auto set3 = obj1.get_set<BinaryData>(col_set_bin);
2✔
812
    auto set4 = obj2.get_set<BinaryData>(col_set_bin);
2✔
813

1✔
814
    char buffer[5];
2✔
815
    auto gen_bin = [&](char c) {
14✔
816
        for (auto& b : buffer) {
70✔
817
            b = c;
70✔
818
        }
70✔
819
        return BinaryData(buffer, 5);
14✔
820
    };
14✔
821
    for (char x : {1, 2}) {
4✔
822
        set3.insert(gen_bin(x));
4✔
823
    }
4✔
824
    for (char x : {255, 1, 2, 3, 2}) {
10✔
825
        set4.insert(gen_bin(x));
10✔
826
    }
10✔
827
    set3.assign_difference(set4);
2✔
828
    CHECK_EQUAL(set3.size(), 0);
2✔
829

1✔
830
    auto set5 = obj1.get_set<StringData>(col_set_str);
2✔
831
    auto set6 = obj2.get_set<StringData>(col_set_str);
2✔
832

1✔
833
    for (const char* x : {"Apple", "Banana"}) {
4✔
834
        set5.insert(x);
4✔
835
    }
4✔
836
    for (const char* x : {"Æbler", "Apple", "Banana", "Citrus", "Dade"}) {
10✔
837
        set6.insert(x);
10✔
838
    }
10✔
839
    set5.assign_difference(set6);
2✔
840
    CHECK_EQUAL(set5.size(), 0);
2✔
841
}
2✔
842

843
TEST(Set_SymmetricDifference)
844
{
2✔
845
    Group g;
2✔
846
    auto foos = g.add_table("class_Foo");
2✔
847
    ColKey col_set = foos->add_column_set(type_Int, "int set");
2✔
848
    ColKey col_list = foos->add_column_list(type_Int, "int list");
2✔
849

1✔
850
    auto obj1 = foos->create_object();
2✔
851
    auto obj2 = foos->create_object();
2✔
852

1✔
853
    auto set1 = obj1.get_set<int64_t>(col_set);
2✔
854
    auto set2 = obj2.get_set<int64_t>(col_set);
2✔
855

1✔
856
    for (int64_t x : {1, 2, 4, 5}) {
8✔
857
        set1.insert(x);
8✔
858
    }
8✔
859

1✔
860
    for (int64_t x : {3, 4, 5}) {
6✔
861
        set2.insert(x);
6✔
862
    }
6✔
863

1✔
864
    set1.assign_symmetric_difference(set2);
2✔
865
    CHECK_EQUAL(set1.size(), 3);
2✔
866
    CHECK_EQUAL(set1.get(0), 1);
2✔
867
    CHECK_EQUAL(set1.get(1), 2);
2✔
868
    CHECK_EQUAL(set1.get(2), 3);
2✔
869

1✔
870
    set1.assign_union(set2);
2✔
871
    auto list = obj2.get_list<int64_t>(col_list);
2✔
872
    for (int64_t x : {4, 5, 1, 27}) {
8✔
873
        list.add(x);
8✔
874
    }
8✔
875
    set1.assign_symmetric_difference(list);
2✔
876
    CHECK_EQUAL(set1.size(), 3);
2✔
877
    CHECK_EQUAL(set1.get(0), 2);
2✔
878
    CHECK_EQUAL(set1.get(1), 3);
2✔
879
    CHECK_EQUAL(set1.get(2), 27);
2✔
880
}
2✔
881

882
TEST(Set_SymmetricDifferenceString)
883
{
2✔
884
    Group g;
2✔
885
    auto foos = g.add_table("class_Foo");
2✔
886
    ColKey col_strings = foos->add_column_set(type_String, "strings");
2✔
887

1✔
888
    auto obj1 = foos->create_object();
2✔
889
    auto obj2 = foos->create_object();
2✔
890

1✔
891
    auto set1 = obj1.get_set<String>(col_strings);
2✔
892
    auto set2 = obj2.get_set<String>(col_strings);
2✔
893

1✔
894
    set1.insert("FooBar");
2✔
895
    set1.insert("A");
2✔
896
    set1.insert("The fox jumps over the lazy dog");
2✔
897

1✔
898
    set2.insert("FooBar");
2✔
899
    set2.insert("World");
2✔
900
    set2.insert("Cosmic love");
2✔
901

1✔
902
    set1.assign_symmetric_difference(set2);
2✔
903
    CHECK_EQUAL(set1.size(), 4);
2✔
904
    CHECK_EQUAL(set1.get(0), "A");
2✔
905
    CHECK_EQUAL(set1.get(1), "Cosmic love");
2✔
906
    CHECK_EQUAL(set1.get(2), "The fox jumps over the lazy dog");
2✔
907
    CHECK_EQUAL(set1.get(3), "World");
2✔
908
}
2✔
909

910
namespace realm {
911
template <typename T>
912
static std::ostream& operator<<(std::ostream& os, const Set<T>& set)
NEW
913
{
×
NEW
914
    os << "Set(" << set.get_table()->get_key() << ", " << set.get_obj().get_key() << ", " << set.get_col_key() << ")";
×
NEW
915
    return os;
×
NEW
916
}
×
917
} // namespace realm
918

919
TEST(Set_Equality)
920
{
2✔
921
    // Table tries to avoid ColKey collisions, but we specifically want to trigger
1✔
922
    // them to test that operator== handles them correctly
1✔
923
    auto set_next_col_key = [](Table& table, int64_t value) {
8✔
924
        table.set_col_key_sequence_number(value ^ table.get_key().value);
8✔
925
    };
8✔
926

1✔
927
    Group g_1;
2✔
928
    auto table_1 = g_1.add_table("table 1");
2✔
929
    set_next_col_key(*table_1, 5);
2✔
930
    table_1->add_column_set(type_Int, "set 1");
2✔
931
    set_next_col_key(*table_1, 10);
2✔
932
    table_1->add_column_set(type_Int, "set 2");
2✔
933
    auto table_2 = g_1.add_table("table 2");
2✔
934
    set_next_col_key(*table_2, 5);
2✔
935
    table_2->add_column_set(type_Int, "set 1");
2✔
936
    Group g_2;
2✔
937
    auto table_3 = g_2.add_table("table 1");
2✔
938
    set_next_col_key(*table_3, 5);
2✔
939
    table_3->add_column_set(type_Int, "set 1");
2✔
940

1✔
941
    auto obj_1 = table_1->create_object();
2✔
942
    auto obj_2 = table_1->create_object();
2✔
943
    auto obj_3 = table_2->create_object();
2✔
944
    auto obj_4 = table_3->create_object();
2✔
945

1✔
946
    // Validate our assumptions that we actually do have key overlaps
1✔
947
    CHECK_EQUAL(table_1->get_key(), table_3->get_key());
2✔
948
    CHECK_EQUAL(table_1->get_column_key("set 1"), table_2->get_column_key("set 1"));
2✔
949
    CHECK_EQUAL(obj_1.get_key(), obj_3.get_key());
2✔
950
    CHECK_EQUAL(obj_1.get_key(), obj_4.get_key());
2✔
951

1✔
952
    CHECK_EQUAL(obj_1.get_set<Int>("set 1"), obj_1.get_set<Int>("set 1"));
2✔
953
    // Same obj, different col
1✔
954
    CHECK_NOT_EQUAL(obj_1.get_set<Int>("set 1"), obj_1.get_set<Int>("set 2"));
2✔
955
    // Same col, different obj
1✔
956
    CHECK_NOT_EQUAL(obj_1.get_set<Int>("set 1"), obj_2.get_set<Int>("set 1"));
2✔
957
    // Same col, same obj, different table
1✔
958
    CHECK_NOT_EQUAL(obj_1.get_set<Int>("set 1"), obj_3.get_set<Int>("set 1"));
2✔
959
    // Same col, same obj, same table, different group
1✔
960
    CHECK_NOT_EQUAL(obj_1.get_set<Int>("set 1"), obj_4.get_set<Int>("set 1"));
2✔
961
}
2✔
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