• 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.85
/test/test_table_view.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_TABLE_VIEW
21

22
#include <limits>
23
#include <string>
24
#include <sstream>
25
#include <ostream>
26
#include <cwchar>
27
#include <chrono>
28

29
#include <realm.hpp>
30

31
#include "util/misc.hpp"
32

33
#include "test.hpp"
34
#include "test_table_helper.hpp"
35

36
using namespace std::chrono;
37

38
using namespace realm;
39
using namespace test_util;
40

41
extern unsigned int unit_test_random_seed;
42

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

72
TEST(TableView_Json)
73
{
2✔
74
    Table table;
2✔
75
    auto col = table.add_column(type_Int, "first");
2✔
76

1✔
77
    table.create_object().set(col, 1);
2✔
78
    table.create_object().set(col, 2);
2✔
79
    table.create_object().set(col, 3);
2✔
80

1✔
81
    TableView v = table.where().find_all(2);
2✔
82
    std::stringstream ss;
2✔
83
    v.to_json(ss);
2✔
84
    const std::string json = ss.str();
2✔
85
    CHECK_EQUAL(true, json.length() > 0);
2✔
86
    CHECK_EQUAL("[{\"_key\":0,\"first\":1},{\"_key\":1,\"first\":2}]", json);
2✔
87
}
2✔
88

89

90
TEST(TableView_TimestampMaxMinCount)
91
{
2✔
92
    Table t;
2✔
93
    auto col = t.add_column(type_Timestamp, "ts", true);
2✔
94
    auto max_key = t.create_object().set_all(Timestamp(300, 300)).get_key();
2✔
95
    auto min_key = t.create_object().set_all(Timestamp(100, 100)).get_key();
2✔
96
    t.create_object().set_all(Timestamp(200, 200));
2✔
97

1✔
98
    // Add object with null. For max(), any non-null is greater, and for min() any non-null is less
1✔
99
    t.create_object();
2✔
100

1✔
101
    TableView tv = t.where().find_all();
2✔
102
    std::optional<Mixed> ts;
2✔
103

1✔
104
    ts = tv.max(col, nullptr);
2✔
105
    CHECK_EQUAL(ts, Timestamp(300, 300));
2✔
106
    ts = tv.min(col, nullptr);
2✔
107
    CHECK_EQUAL(ts, Timestamp(100, 100));
2✔
108

1✔
109
    ObjKey key;
2✔
110
    ts = tv.max(col, &key);
2✔
111
    CHECK_EQUAL(key, max_key);
2✔
112
    ts = tv.min(col, &key);
2✔
113
    CHECK_EQUAL(key, min_key);
2✔
114

1✔
115
    size_t cnt;
2✔
116
    cnt = tv.count_timestamp(col, Timestamp(100, 100));
2✔
117
    CHECK_EQUAL(cnt, 1);
2✔
118

1✔
119
    cnt = tv.count_timestamp(col, Timestamp{});
2✔
120
    CHECK_EQUAL(cnt, 1);
2✔
121
}
2✔
122

123

124
TEST(TableView_FloatsFindAndAggregations)
125
{
2✔
126
    Table table;
2✔
127
    auto col_float = table.add_column(type_Float, "1");
2✔
128
    auto col_double = table.add_column(type_Double, "2");
2✔
129
    auto col_int = table.add_column(type_Int, "3");
2✔
130

1✔
131
    float f_val[] = {1.2f, 2.1f, 3.1f, -1.1f, 2.1f, 0.0f};
2✔
132
    double d_val[] = {-1.2, 2.2, 3.2, -1.2, 2.3, 0.0};
2✔
133
    // v_some =       ^^^^            ^^^^
1✔
134
    double sum_f = 0.0;
2✔
135
    double sum_d = 0.0;
2✔
136
    std::vector<ObjKey> keys;
2✔
137
    table.create_objects(6, keys);
2✔
138
    for (int i = 0; i < 6; ++i) {
14✔
139
        table.get_object(keys[i]).set_all(f_val[i], d_val[i], 1);
12✔
140
        sum_d += d_val[i];
12✔
141
        sum_f += f_val[i];
12✔
142
    }
12✔
143

1✔
144
    // Test find_all()
1✔
145
    TableView v_all = table.find_all_int(col_int, 1);
2✔
146
    CHECK_EQUAL(6, v_all.size());
2✔
147

1✔
148
    TableView v_some = table.find_all_double(col_double, -1.2);
2✔
149
    CHECK_EQUAL(2, v_some.size());
2✔
150
    CHECK_EQUAL(ObjKey(0), v_some.get_key(0));
2✔
151
    CHECK_EQUAL(ObjKey(3), v_some.get_key(1));
2✔
152

1✔
153
    // Test find_first
1✔
154
    CHECK_EQUAL(0, v_all.find_first<Double>(col_double, -1.2));
2✔
155
    CHECK_EQUAL(5, v_all.find_first<Double>(col_double, 0.0));
2✔
156
    CHECK_EQUAL(2, v_all.find_first<Double>(col_double, 3.2));
2✔
157

1✔
158
    CHECK_EQUAL(1, v_all.find_first<float>(col_float, 2.1f));
2✔
159
    CHECK_EQUAL(5, v_all.find_first<float>(col_float, 0.0f));
2✔
160
    CHECK_EQUAL(2, v_all.find_first<float>(col_float, 3.1f));
2✔
161

1✔
162
    // TODO: add for float as well
1✔
163

1✔
164
    double epsilon = std::numeric_limits<double>::epsilon();
2✔
165

1✔
166
    // Test sum
1✔
167
    CHECK_APPROXIMATELY_EQUAL(sum_d, v_all.sum(col_double)->get_double(), 10 * epsilon);
2✔
168
    CHECK_APPROXIMATELY_EQUAL(sum_f, v_all.sum(col_float)->get_double(), 10 * epsilon);
2✔
169
    CHECK_APPROXIMATELY_EQUAL(-1.2 + -1.2, v_some.sum(col_double)->get_double(), 10 * epsilon);
2✔
170
    CHECK_APPROXIMATELY_EQUAL(double(1.2f) + double(-1.1f), v_some.sum(col_float)->get_double(), 10 * epsilon);
2✔
171

1✔
172
    ObjKey key;
2✔
173

1✔
174
    // Test max
1✔
175
    CHECK_EQUAL(3.2, v_all.max(col_double, &key)->get_double());
2✔
176
    CHECK_EQUAL(ObjKey(2), key);
2✔
177

1✔
178
    CHECK_EQUAL(-1.2, v_some.max(col_double, &key)->get_double());
2✔
179
    CHECK_EQUAL(ObjKey(0), key);
2✔
180

1✔
181
    CHECK_EQUAL(3.1f, v_all.max(col_float, &key)->get_float());
2✔
182
    CHECK_EQUAL(ObjKey(2), key);
2✔
183

1✔
184
    CHECK_EQUAL(1.2f, v_some.max(col_float, &key)->get_float());
2✔
185
    CHECK_EQUAL(ObjKey(0), key);
2✔
186

1✔
187
    // Max without ret_index
1✔
188
    CHECK_EQUAL(3.2, v_all.max(col_double)->get_double());
2✔
189
    CHECK_EQUAL(-1.2, v_some.max(col_double)->get_double());
2✔
190
    CHECK_EQUAL(3.1f, v_all.max(col_float)->get_float());
2✔
191
    CHECK_EQUAL(1.2f, v_some.max(col_float)->get_float());
2✔
192

1✔
193
    // Test min
1✔
194
    CHECK_EQUAL(-1.2, v_all.min(col_double)->get_double());
2✔
195
    CHECK_EQUAL(-1.2, v_some.min(col_double)->get_double());
2✔
196
    CHECK_EQUAL(-1.1f, v_all.min(col_float)->get_float());
2✔
197
    CHECK_EQUAL(-1.1f, v_some.min(col_float)->get_float());
2✔
198
    // min with ret_ndx
1✔
199
    CHECK_EQUAL(-1.2, v_all.min(col_double, &key)->get_double());
2✔
200
    CHECK_EQUAL(ObjKey(0), key);
2✔
201

1✔
202
    CHECK_EQUAL(-1.2, v_some.min(col_double, &key)->get_double());
2✔
203
    CHECK_EQUAL(ObjKey(0), key);
2✔
204

1✔
205
    CHECK_EQUAL(-1.1f, v_all.min(col_float, &key)->get_float());
2✔
206
    CHECK_EQUAL(ObjKey(3), key);
2✔
207

1✔
208
    CHECK_EQUAL(-1.1f, v_some.min(col_float, &key)->get_float());
2✔
209
    CHECK_EQUAL(ObjKey(3), key);
2✔
210

1✔
211
    // Test avg
1✔
212
    CHECK_APPROXIMATELY_EQUAL(sum_d / 6.0, v_all.avg(col_double)->get_double(), 10 * epsilon);
2✔
213
    CHECK_APPROXIMATELY_EQUAL((-1.2 + -1.2) / 2.0, v_some.avg(col_double)->get_double(), 10 * epsilon);
2✔
214
    CHECK_APPROXIMATELY_EQUAL(sum_f / 6.0, v_all.avg(col_float)->get_double(), 10 * epsilon);
2✔
215
    CHECK_APPROXIMATELY_EQUAL((double(1.2f) + double(-1.1f)) / 2, v_some.avg(col_float)->get_double(), 10 * epsilon);
2✔
216

1✔
217
    CHECK_EQUAL(1, v_some.count_float(col_float, 1.2f));
2✔
218
    CHECK_EQUAL(2, v_some.count_double(col_double, -1.2));
2✔
219
    CHECK_EQUAL(2, v_some.count_int(col_int, 1));
2✔
220

1✔
221
    CHECK_EQUAL(2, v_all.count_float(col_float, 2.1f));
2✔
222
    CHECK_EQUAL(2, v_all.count_double(col_double, -1.2));
2✔
223
    CHECK_EQUAL(6, v_all.count_int(col_int, 1));
2✔
224
}
2✔
225

226
TEST(TableView_Sum)
227
{
2✔
228
    Table table;
2✔
229
    auto c0 = table.add_column(type_Int, "1");
2✔
230

1✔
231
    table.create_object().set_all( 2);
2✔
232
    table.create_object().set_all( 2);
2✔
233
    table.create_object().set_all( 2);
2✔
234
    table.create_object().set_all( 2);
2✔
235
    table.create_object().set_all( 2);
2✔
236

1✔
237
    TableView v = table.find_all_int(c0, 2);
2✔
238
    CHECK_EQUAL(5, v.size());
2✔
239

1✔
240
    int64_t sum = v.sum(c0)->get_int();
2✔
241
    CHECK_EQUAL(10, sum);
2✔
242
}
2✔
243

244
TEST(TableView_Average)
245
{
2✔
246
    Table table;
2✔
247
    auto c0 = table.add_column(type_Int, "1");
2✔
248

1✔
249
    table.create_object().set_all( 2);
2✔
250
    table.create_object().set_all( 2);
2✔
251
    table.create_object().set_all( 2);
2✔
252
    table.create_object().set_all( 2);
2✔
253
    table.create_object().set_all( 2);
2✔
254

1✔
255
    TableView v = table.find_all_int(c0, 2);
2✔
256
    CHECK_EQUAL(5, v.size());
2✔
257

1✔
258
    double sum = v.avg(c0)->get_double();
2✔
259
    CHECK_APPROXIMATELY_EQUAL(2., sum, 0.00001);
2✔
260
}
2✔
261

262
TEST(TableView_SumNegative)
263
{
2✔
264
    Table table;
2✔
265
    auto c0 = table.add_column(type_Int, "1");
2✔
266

1✔
267
    table.create_object().set_all( 0);
2✔
268
    table.create_object().set_all( 0);
2✔
269
    table.create_object().set_all( 0);
2✔
270

1✔
271
    TableView v = table.find_all_int(c0, 0);
2✔
272
    v[0].set<Int>(c0, 11);
2✔
273
    v[2].set<Int>(c0, -20);
2✔
274

1✔
275
    int64_t sum = v.sum(c0)->get_int();
2✔
276
    CHECK_EQUAL(-9, sum);
2✔
277
}
2✔
278

279
TEST(TableView_IsAttached)
280
{
2✔
281
    Table table;
2✔
282
    auto c0 = table.add_column(type_Int, "1");
2✔
283

1✔
284
    table.create_object().set_all( 0);
2✔
285
    table.create_object().set_all( 0);
2✔
286
    table.create_object().set_all( 0);
2✔
287

1✔
288
    TableView v = table.find_all_int(c0, 0);
2✔
289
    TableView v2 = table.find_all_int(c0, 0);
2✔
290
    v[0].set<Int>(c0, 11);
2✔
291
    CHECK_EQUAL(true, v.is_attached());
2✔
292
    CHECK_EQUAL(true, v2.is_attached());
2✔
293
    v.clear();
2✔
294
    CHECK_EQUAL(true, v.is_attached());
2✔
295
    CHECK_EQUAL(true, v2.is_attached());
2✔
296
}
2✔
297

298
TEST(TableView_Max)
299
{
2✔
300
    Table table;
2✔
301
    auto c0 = table.add_column(type_Int, "1");
2✔
302

1✔
303
    table.create_object().set_all( 0);
2✔
304
    table.create_object().set_all( 0);
2✔
305
    table.create_object().set_all( 0);
2✔
306

1✔
307
    TableView v = table.find_all_int(c0, 0);
2✔
308
    v[0].set<Int>(c0, -1);
2✔
309
    v[1].set<Int>(c0, 2);
2✔
310
    v[2].set<Int>(c0, 1);
2✔
311

1✔
312
    int64_t max = v.max(c0)->get_int();
2✔
313
    CHECK_EQUAL(2, max);
2✔
314
}
2✔
315

316
TEST(TableView_Max2)
317
{
2✔
318
    Table table;
2✔
319
    auto c0 = table.add_column(type_Int, "1");
2✔
320

1✔
321
    table.create_object().set_all( 0);
2✔
322
    table.create_object().set_all( 0);
2✔
323
    table.create_object().set_all( 0);
2✔
324

1✔
325
    TableView v = table.find_all_int(c0, 0);
2✔
326
    v[0].set<Int>(c0, -1);
2✔
327
    v[1].set<Int>(c0, -2);
2✔
328
    v[2].set<Int>(c0, -3);
2✔
329

1✔
330
    int64_t max = v.max(c0, 0)->get_int();
2✔
331
    CHECK_EQUAL(-1, max);
2✔
332
}
2✔
333

334

335
TEST(TableView_Min)
336
{
2✔
337
    Table table;
2✔
338
    auto c0 = table.add_column(type_Int, "first");
2✔
339

1✔
340
    table.create_object().set_all( 0);
2✔
341
    table.create_object().set_all( 0);
2✔
342
    table.create_object().set_all( 0);
2✔
343

1✔
344
    TableView v = table.find_all_int(c0, 0);
2✔
345
    v[0].set<Int>(c0, -1);
2✔
346
    v[1].set<Int>(c0, 2);
2✔
347
    v[2].set<Int>(c0, 1);
2✔
348

1✔
349
    int64_t min = v.min(c0)->get_int();
2✔
350
    CHECK_EQUAL(-1, min);
2✔
351

1✔
352
    ObjKey key;
2✔
353
    min = v.min(c0, &key)->get_int();
2✔
354
    CHECK_EQUAL(-1, min);
2✔
355
    CHECK_EQUAL(v[0].get_key(), key);
2✔
356
}
2✔
357

358
TEST(TableView_Min2)
359
{
2✔
360
    Table table;
2✔
361
    auto c0 = table.add_column(type_Int, "first");
2✔
362

1✔
363
    table.create_object().set_all( 0);
2✔
364
    table.create_object().set_all( 0);
2✔
365
    table.create_object().set_all( 0);
2✔
366

1✔
367
    TableView v = table.find_all_int(c0, 0);
2✔
368
    v[0].set<Int>(c0, -1);
2✔
369
    v[1].set<Int>(c0, -2);
2✔
370
    v[2].set<Int>(c0, -3);
2✔
371

1✔
372
    int64_t min = v.min(c0)->get_int();
2✔
373
    CHECK_EQUAL(-3, min);
2✔
374

1✔
375
    ObjKey key;
2✔
376
    min = v.min(c0, &key)->get_int();
2✔
377
    CHECK_EQUAL(-3, min);
2✔
378
    CHECK_EQUAL(v[2].get_key(), key);
2✔
379
}
2✔
380

381

382
TEST(TableView_Find)
383
{
2✔
384
    Table table;
2✔
385
    auto col0 = table.add_column(type_Int, "int");
2✔
386
    auto col1 = table.add_column(type_Int, "int?", true);
2✔
387
    auto col2 = table.add_column(type_Bool, "bool");
2✔
388
    auto col3 = table.add_column(type_Bool, "bool?", true);
2✔
389
    auto col4 = table.add_column(type_Float, "float");
2✔
390
    auto col5 = table.add_column(type_Float, "float?", true);
2✔
391
    auto col6 = table.add_column(type_Double, "double");
2✔
392
    auto col7 = table.add_column(type_Double, "double?", true);
2✔
393
    auto col8 = table.add_column(type_Timestamp, "timestamp");
2✔
394
    auto col9 = table.add_column(type_Timestamp, "timestamp?", true);
2✔
395
    auto col10 = table.add_column(type_String, "string");
2✔
396
    auto col11 = table.add_column(type_String, "string?", true);
2✔
397
    auto col12 = table.add_column(type_Binary, "binary");
2✔
398
    auto col13 = table.add_column(type_Binary, "binary?", true);
2✔
399

1✔
400
    Obj obj0 = table.create_object();
2✔
401
    Obj obj1 = table.create_object();
2✔
402
    Obj obj2 = table.create_object();
2✔
403
    Obj obj3 = table.create_object();
2✔
404

1✔
405
    obj0.set(col0, 0);
2✔
406
    obj1.set_all(1, 1, false, false, 1.1f, 1.1f, 1.1, 1.1, Timestamp(1, 1), Timestamp(1, 1), "a", "a",
2✔
407
                 BinaryData("a", 1), BinaryData("a", 1));
2✔
408
    obj2.set(col0, 2);
2✔
409
    obj2.set(col2, true);
2✔
410
    obj2.set(col4, 2.2f);
2✔
411
    obj2.set(col6, 2.2);
2✔
412
    obj2.set(col8, Timestamp(2, 2));
2✔
413
    obj2.set(col10, "b");
2✔
414
    obj2.set(col12, BinaryData("b", 1));
2✔
415
    obj3.set(col0, -1);
2✔
416

1✔
417
    // TV where index in TV equals the index in the table
1✔
418
    TableView all = table.where().find_all();
2✔
419

1✔
420
    // Ensure the TVs have a detached ref to deal with
1✔
421
    obj3.remove();
2✔
422

1✔
423
    // Look for the values in the second row
1✔
424
    CHECK_EQUAL(1, all.find_first<Int>(col0, 1));
2✔
425
    CHECK_EQUAL(1, all.find_first(col1, util::Optional<int64_t>(1)));
2✔
426
    CHECK_EQUAL(0, all.find_first(col2, false));
2✔
427
    CHECK_EQUAL(1, all.find_first(col3, util::make_optional(false)));
2✔
428
    CHECK_EQUAL(1, all.find_first(col4, 1.1f));
2✔
429
    CHECK_EQUAL(1, all.find_first(col5, util::make_optional(1.1f)));
2✔
430
    CHECK_EQUAL(1, all.find_first(col6, 1.1));
2✔
431
    CHECK_EQUAL(1, all.find_first(col7, util::make_optional(1.1)));
2✔
432
    CHECK_EQUAL(1, all.find_first(col8, Timestamp(1, 1)));
2✔
433
    CHECK_EQUAL(1, all.find_first(col9, Timestamp(1, 1)));
2✔
434
    CHECK_EQUAL(1, all.find_first(col10, StringData("a")));
2✔
435
    CHECK_EQUAL(1, all.find_first(col11, StringData("a")));
2✔
436
    CHECK_EQUAL(1, all.find_first(col12, BinaryData("a", 1)));
2✔
437
    CHECK_EQUAL(1, all.find_first(col13, BinaryData("a", 1)));
2✔
438

1✔
439
    // Look for the values in the third row
1✔
440
    CHECK_EQUAL(2, all.find_first<Int>(col0, 2));
2✔
441
    CHECK_EQUAL(0, all.find_first(col1, util::Optional<int64_t>()));
2✔
442
    CHECK_EQUAL(2, all.find_first(col2, true));
2✔
443
    CHECK_EQUAL(0, all.find_first(col3, util::Optional<bool>()));
2✔
444
    CHECK_EQUAL(2, all.find_first(col4, 2.2f));
2✔
445
    CHECK_EQUAL(0, all.find_first(col5, util::Optional<float>()));
2✔
446
    CHECK_EQUAL(2, all.find_first(col6, 2.2));
2✔
447
    CHECK_EQUAL(0, all.find_first(col7, util::Optional<double>()));
2✔
448
    CHECK_EQUAL(2, all.find_first(col8, Timestamp(2, 2)));
2✔
449
    CHECK_EQUAL(0, all.find_first(col9, Timestamp()));
2✔
450
    CHECK_EQUAL(2, all.find_first(col10, StringData("b")));
2✔
451
    CHECK_EQUAL(0, all.find_first(col11, StringData()));
2✔
452
    CHECK_EQUAL(2, all.find_first(col12, BinaryData("b", 1)));
2✔
453
    CHECK_EQUAL(0, all.find_first(col13, BinaryData()));
2✔
454

1✔
455
    // Look for values that aren't present
1✔
456
    CHECK_EQUAL(npos, all.find_first<Int>(col0, 5));
2✔
457
    CHECK_EQUAL(npos, all.find_first(col1, util::Optional<int64_t>(5)));
2✔
458
    CHECK_EQUAL(npos, all.find_first(col4, 3.3f));
2✔
459
    CHECK_EQUAL(npos, all.find_first(col5, util::make_optional(3.3f)));
2✔
460
    CHECK_EQUAL(npos, all.find_first(col6, 3.3));
2✔
461
    CHECK_EQUAL(npos, all.find_first(col7, util::make_optional(3.3)));
2✔
462
    CHECK_EQUAL(npos, all.find_first(col8, Timestamp(3, 3)));
2✔
463
    CHECK_EQUAL(npos, all.find_first(col9, Timestamp(3, 3)));
2✔
464
    CHECK_EQUAL(npos, all.find_first(col10, StringData("c")));
2✔
465
    CHECK_EQUAL(npos, all.find_first(col11, StringData("c")));
2✔
466
    CHECK_EQUAL(npos, all.find_first(col12, BinaryData("c", 1)));
2✔
467
    CHECK_EQUAL(npos, all.find_first(col13, BinaryData("c", 1)));
2✔
468
}
2✔
469

470

471
TEST(TableView_Follows_Changes)
472
{
2✔
473
    Table table;
2✔
474
    auto col = table.add_column(type_Int, "first");
2✔
475
    Obj obj0 = table.create_object().set(col, 1);
2✔
476

1✔
477
    Query q = table.where().equal(col, 1);
2✔
478
    TableView v = q.find_all();
2✔
479
    CHECK_EQUAL(1, v.size());
2✔
480
    CHECK_EQUAL(1, v[0].get<Int>(col));
2✔
481

1✔
482
    // low level sanity check that we can copy a query and run the copy:
1✔
483
    Query q2 = q;
2✔
484
    TableView v2 = q2.find_all();
2✔
485

1✔
486
    // now the fun begins
1✔
487
    CHECK_EQUAL(1, v.size());
2✔
488
    Obj obj1 = table.create_object();
2✔
489
    CHECK_EQUAL(1, v.size());
2✔
490
    obj1.set<Int>(col, 1);
2✔
491
    v.sync_if_needed();
2✔
492
    CHECK_EQUAL(2, v.size());
2✔
493
    CHECK_EQUAL(1, v[0].get<Int>(col));
2✔
494
    CHECK_EQUAL(1, v[1].get<Int>(col));
2✔
495
    obj0.set<Int>(col, 7);
2✔
496
    v.sync_if_needed();
2✔
497
    CHECK_EQUAL(1, v.size());
2✔
498
    CHECK_EQUAL(1, v[0].get<Int>(col));
2✔
499
    obj1.set<Int>(col, 7);
2✔
500
    v.sync_if_needed();
2✔
501
    CHECK_EQUAL(0, v.size());
2✔
502
    obj1.set<Int>(col, 1);
2✔
503
    v.sync_if_needed();
2✔
504
    CHECK_EQUAL(1, v.size());
2✔
505
    CHECK_EQUAL(1, v[0].get<Int>(col));
2✔
506
}
2✔
507

508

509
TEST(TableView_Distinct_Follows_Changes)
510
{
2✔
511
    Table table;
2✔
512
    auto col_int = table.add_column(type_Int, "first");
2✔
513
    table.add_column(type_String, "second");
2✔
514
    table.add_search_index(col_int);
2✔
515

1✔
516
    for (int i = 0; i < 5; ++i) {
12✔
517
        table.create_object().set_all(i, "Foo");
10✔
518
    }
10✔
519

1✔
520
    DescriptorOrdering order;
2✔
521
    order.append_distinct(DistinctDescriptor({{col_int}}));
2✔
522
    TableView distinct_ints = table.where().find_all(order);
2✔
523
    CHECK_EQUAL(5, distinct_ints.size());
2✔
524
    CHECK(distinct_ints.is_in_sync());
2✔
525

1✔
526
    // Check that adding a value that doesn't actually impact the
1✔
527
    // view still invalidates the view (which is inspected for now).
1✔
528
    table.create_object().set_all(4, "Foo");
2✔
529
    CHECK(!distinct_ints.is_in_sync());
2✔
530
    distinct_ints.sync_if_needed();
2✔
531
    CHECK(distinct_ints.is_in_sync());
2✔
532
    CHECK_EQUAL(5, distinct_ints.size());
2✔
533

1✔
534
    // Check that adding a value that impacts the view invalidates the view.
1✔
535
    distinct_ints.sync_if_needed();
2✔
536
    table.create_object().set_all(6, "Foo");
2✔
537
    CHECK(!distinct_ints.is_in_sync());
2✔
538
    distinct_ints.sync_if_needed();
2✔
539
    CHECK(distinct_ints.is_in_sync());
2✔
540
    CHECK_EQUAL(6, distinct_ints.size());
2✔
541
}
2✔
542

543

544
TEST(TableView_SyncAfterCopy)
545
{
2✔
546
    Table table;
2✔
547
    auto col = table.add_column(type_Int, "first");
2✔
548
    table.create_object().set(col, 1);
2✔
549

1✔
550
    // do initial query
1✔
551
    Query q = table.where().equal(col, 1);
2✔
552
    TableView v = q.find_all();
2✔
553
    CHECK_EQUAL(1, v.size());
2✔
554
    CHECK_EQUAL(1, v[0].get<Int>(col));
2✔
555

1✔
556
    // move the tableview
1✔
557
    TableView v2 = v;
2✔
558
    CHECK_EQUAL(1, v2.size());
2✔
559

1✔
560
    // make a change
1✔
561
    table.create_object().set(col, 1);
2✔
562

1✔
563
    // verify that the copied view sees the change
1✔
564
    v2.sync_if_needed();
2✔
565
    CHECK_EQUAL(2, v2.size());
2✔
566
}
2✔
567

568
TEST(TableView_StringSort)
569
{
2✔
570
    // WARNING: Do not use the C++11 method (set_string_compare_method(1)) on Windows 8.1 because it has a bug that
1✔
571
    // takes length in count when sorting ("b" comes before "aaaa"). Bug is not present in Windows 7.
1✔
572

1✔
573
    // Test of handling of unicode takes place in test_utf8.cpp
1✔
574
    Table table;
2✔
575
    auto col = table.add_column(type_String, "1");
2✔
576

1✔
577
    table.create_object().set_all( "alpha");
2✔
578
    table.create_object().set_all( "zebra");
2✔
579
    table.create_object().set_all( "ALPHA");
2✔
580
    table.create_object().set_all( "ZEBRA");
2✔
581

1✔
582
    // Core-only is default comparer
1✔
583
    TableView v = table.where().find_all();
2✔
584
    v.sort(col);
2✔
585
    CHECK_EQUAL("alpha", v[0].get<String>(col));
2✔
586
    CHECK_EQUAL("ALPHA", v[1].get<String>(col));
2✔
587
    CHECK_EQUAL("zebra", v[2].get<String>(col));
2✔
588
    CHECK_EQUAL("ZEBRA", v[3].get<String>(col));
2✔
589

1✔
590
    // Test descending mode
1✔
591
    v = table.where().find_all();
2✔
592
    v.sort(col, false);
2✔
593
    CHECK_EQUAL("alpha", v[3].get<String>(col));
2✔
594
    CHECK_EQUAL("ALPHA", v[2].get<String>(col));
2✔
595
    CHECK_EQUAL("zebra", v[1].get<String>(col));
2✔
596
    CHECK_EQUAL("ZEBRA", v[0].get<String>(col));
2✔
597
}
2✔
598

599
TEST(TableView_BinarySort)
600
{
2✔
601
    Table t;
2✔
602
    auto col_bin = t.add_column(type_Binary, "bin", true);
2✔
603
    auto col_rank = t.add_column(type_Int, "rank");
2✔
604

1✔
605
    const char b1[] = {1, 2, 3, 4, 5};
2✔
606
    const char b2[] = {1, 2, 0, 4, 5};
2✔
607
    const char b3[] = {1, 2, 3, 4};
2✔
608
    const char b4[] = {1, 2, 3, 4, 5, 6};
2✔
609

1✔
610
    t.create_object(ObjKey{}, {{col_bin, BinaryData(b1, sizeof(b1))}, {col_rank, 4}});
2✔
611
    t.create_object(ObjKey{}, {{col_bin, BinaryData(b2, sizeof(b2))}, {col_rank, 2}});
2✔
612
    t.create_object(ObjKey{}, {{col_rank, 1}});
2✔
613
    t.create_object(ObjKey{}, {{col_bin, BinaryData(b3, sizeof(b3))}, {col_rank, 3}});
2✔
614
    t.create_object(ObjKey{}, {{col_bin, BinaryData(b4, sizeof(b4))}, {col_rank, 5}});
2✔
615

1✔
616
    TableView tv = t.where().find_all();
2✔
617
    tv.sort(col_bin);
2✔
618
    int64_t rank = 0;
2✔
619
    for (size_t n = 0; n < tv.size(); n++) {
12✔
620
        auto this_rank = tv.get_object(n).get<Int>(col_rank);
10✔
621
        CHECK_GREATER(this_rank, rank);
10✔
622
        rank = this_rank;
10✔
623
    }
10✔
624
}
2✔
625

626

627
TEST(TableView_FloatDoubleSort)
628
{
2✔
629
    Table t;
2✔
630
    auto col_float = t.add_column(type_Float, "1");
2✔
631
    auto col_double = t.add_column(type_Double, "2");
2✔
632

1✔
633
    t.create_object().set_all(1.0f, 10.0);
2✔
634
    t.create_object().set_all(3.0f, 30.0);
2✔
635
    t.create_object().set_all(2.0f, 20.0);
2✔
636
    t.create_object().set_all(0.0f, 5.0);
2✔
637

1✔
638
    TableView tv = t.where().find_all();
2✔
639
    tv.sort(col_float);
2✔
640

1✔
641
    CHECK_EQUAL(0.0f, tv[0].get<float>(col_float));
2✔
642
    CHECK_EQUAL(1.0f, tv[1].get<float>(col_float));
2✔
643
    CHECK_EQUAL(2.0f, tv[2].get<float>(col_float));
2✔
644
    CHECK_EQUAL(3.0f, tv[3].get<float>(col_float));
2✔
645

1✔
646
    tv.sort(col_double);
2✔
647
    CHECK_EQUAL(5.0f, tv[0].get<double>(col_double));
2✔
648
    CHECK_EQUAL(10.0f, tv[1].get<double>(col_double));
2✔
649
    CHECK_EQUAL(20.0f, tv[2].get<double>(col_double));
2✔
650
    CHECK_EQUAL(30.0f, tv[3].get<double>(col_double));
2✔
651
}
2✔
652

653
TEST(TableView_DoubleSortPrecision)
654
{
2✔
655
    // Detect if sorting algorithm accidentally casts doubles to float somewhere so that precision gets lost
1✔
656
    Table t;
2✔
657
    auto col_float = t.add_column(type_Float, "1");
2✔
658
    auto col_double = t.add_column(type_Double, "2");
2✔
659

1✔
660
    double d1 = 100000000000.0;
2✔
661
    double d2 = 100000000001.0;
2✔
662

1✔
663
    // When casted to float, they are equal
1✔
664
    float f1 = static_cast<float>(d1);
2✔
665
    float f2 = static_cast<float>(d2);
2✔
666

1✔
667
    // If this check fails, it's a bug in this unit test, not in Realm
1✔
668
    CHECK_EQUAL(f1, f2);
2✔
669

1✔
670
    // First verify that our unit is guaranteed to find such a bug; that is, test if such a cast is guaranteed to give
1✔
671
    // bad sorting order. This is not granted, because an unstable sorting algorithm could *by chance* give the
1✔
672
    // correct sorting order. Fortunatly we use std::stable_sort which must maintain order on draws.
1✔
673
    t.create_object().set_all(f2, d2);
2✔
674
    t.create_object().set_all(f1, d1);
2✔
675

1✔
676
    TableView tv = t.where().find_all();
2✔
677
    tv.sort(col_float);
2✔
678

1✔
679
    // Sort should be stable
1✔
680
    CHECK_EQUAL(f2, tv[0].get<float>(col_float));
2✔
681
    CHECK_EQUAL(f1, tv[1].get<float>(col_float));
2✔
682

1✔
683
    // If sort is stable, and compare makes a draw because the doubles are accidentally casted to float in Realm,
1✔
684
    // then
1✔
685
    // original order would be maintained. Check that it's not maintained:
1✔
686
    tv.sort(col_double);
2✔
687
    CHECK_EQUAL(d1, tv[0].get<double>(col_double));
2✔
688
    CHECK_EQUAL(d2, tv[1].get<double>(col_double));
2✔
689
}
2✔
690

691
TEST(TableView_SortNullString)
692
{
2✔
693
    Table t;
2✔
694
    auto col = t.add_column(type_String, "s", true);
2✔
695
    Obj obj = t.create_object().set(col, StringData("")); // empty string
2✔
696
    t.create_object().set(col, realm::null());            // realm::null()
2✔
697
    t.create_object().set(col, StringData(""));           // empty string
2✔
698
    t.create_object().set(col, realm::null());            // realm::null()
2✔
699

1✔
700
    TableView tv;
2✔
701

1✔
702
    tv = t.where().find_all();
2✔
703
    tv.sort(col);
2✔
704
    CHECK(tv[0].get<String>(col).is_null());
2✔
705
    CHECK(tv[1].get<String>(col).is_null());
2✔
706
    CHECK_NOT(tv[2].get<String>(col).is_null());
2✔
707
    CHECK_NOT(tv[3].get<String>(col).is_null());
2✔
708

1✔
709
    obj.set(col, StringData("medium medium medium medium"));
2✔
710

1✔
711
    tv = t.where().find_all();
2✔
712
    tv.sort(col);
2✔
713
    CHECK(tv[0].get<String>(col).is_null());
2✔
714
    CHECK(tv[1].get<String>(col).is_null());
2✔
715
    CHECK_NOT(tv[2].get<String>(col).is_null());
2✔
716
    CHECK_NOT(tv[3].get<String>(col).is_null());
2✔
717

1✔
718
    obj.set(col, StringData("long long long long long long long long long long long long long long"));
2✔
719

1✔
720
    tv = t.where().find_all();
2✔
721
    tv.sort(col);
2✔
722
    CHECK(tv[0].get<String>(col).is_null());
2✔
723
    CHECK(tv[1].get<String>(col).is_null());
2✔
724
    CHECK_NOT(tv[2].get<String>(col).is_null());
2✔
725
    CHECK_NOT(tv[3].get<String>(col).is_null());
2✔
726
}
2✔
727

728
TEST(TableView_Clear)
729
{
2✔
730
    Table table;
2✔
731
    auto col = table.add_column(type_Int, "first");
2✔
732

1✔
733
    table.create_object().set(col, 1);
2✔
734
    table.create_object().set(col, 2);
2✔
735
    table.create_object().set(col, 1);
2✔
736
    table.create_object().set(col, 3);
2✔
737
    table.create_object().set(col, 1);
2✔
738

1✔
739
    TableView v = table.find_all_int(col, 1);
2✔
740
    CHECK_EQUAL(3, v.size());
2✔
741

1✔
742
    v.clear();
2✔
743
    CHECK_EQUAL(0, v.size());
2✔
744

1✔
745
    CHECK_EQUAL(2, table.size());
2✔
746
    auto it = table.begin();
2✔
747
    CHECK_EQUAL(2, it->get<int64_t>(col));
2✔
748
    ++it;
2✔
749
    CHECK_EQUAL(3, it->get<int64_t>(col));
2✔
750
}
2✔
751

752

753
// Verify that TableView::clear() can handle a detached ref,
754
// so that it can be used in an imperative setting
755
TEST(TableView_Imperative_Clear)
756
{
2✔
757
    Table t;
2✔
758
    auto col = t.add_column(type_Int, "i1");
2✔
759
    t.create_object().set(col, 7);
2✔
760
    t.create_object().set(col, 13);
2✔
761
    t.create_object().set(col, 29);
2✔
762

1✔
763
    TableView v = t.where().less(col, 20).find_all();
2✔
764
    CHECK_EQUAL(2, v.size());
2✔
765
    // remove the underlying entry in the table, introducing a detached ref
1✔
766
    t.remove_object(v.get_key(0));
2✔
767
    // the detached ref still counts as an entry when calling size()
1✔
768
    CHECK_EQUAL(2, v.size());
2✔
769

1✔
770
    v.clear();
2✔
771
    CHECK_EQUAL(0, v.size());
2✔
772
    CHECK_EQUAL(1, t.size());
2✔
773
}
2✔
774

775
TEST(TableView_ClearNone)
776
{
2✔
777
    Table table;
2✔
778
    auto col = table.add_column(type_Int, "first");
2✔
779

1✔
780
    TableView v = table.find_all_int(col, 1);
2✔
781
    CHECK_EQUAL(0, v.size());
2✔
782

1✔
783
    v.clear();
2✔
784
}
2✔
785

786
TEST(TableView_MultiColSort)
787
{
2✔
788
    Table table;
2✔
789
    auto col_int = table.add_column(type_Int, "int");
2✔
790
    auto col_float = table.add_column(type_Float, "float");
2✔
791

1✔
792
    table.create_object().set_all(0, 0.f);
2✔
793
    table.create_object().set_all(1, 2.f);
2✔
794
    table.create_object().set_all(1, 1.f);
2✔
795

1✔
796
    TableView tv = table.where().find_all();
2✔
797

1✔
798
    std::vector<std::vector<ExtendedColumnKey>> v = {{col_int}, {col_float}};
2✔
799
    std::vector<bool> a = {true, true};
2✔
800

1✔
801
    tv.sort(SortDescriptor{v, a});
2✔
802

1✔
803
    CHECK_EQUAL(tv[0].get<float>(col_float), 0.f);
2✔
804
    CHECK_EQUAL(tv[1].get<float>(col_float), 1.f);
2✔
805
    CHECK_EQUAL(tv[2].get<float>(col_float), 2.f);
2✔
806

1✔
807
    std::vector<bool> a_descending = {false, false};
2✔
808
    tv = table.where().find_all();
2✔
809
    tv.sort(SortDescriptor{v, a_descending});
2✔
810

1✔
811
    CHECK_EQUAL(tv[0].get<float>(col_float), 2.f);
2✔
812
    CHECK_EQUAL(tv[1].get<float>(col_float), 1.f);
2✔
813
    CHECK_EQUAL(tv[2].get<float>(col_float), 0.f);
2✔
814

1✔
815
    std::vector<bool> a_ascdesc = {true, false};
2✔
816
    tv = table.where().find_all();
2✔
817
    tv.sort(SortDescriptor{v, a_ascdesc});
2✔
818

1✔
819
    CHECK_EQUAL(tv[0].get<float>(col_float), 0.f);
2✔
820
    CHECK_EQUAL(tv[1].get<float>(col_float), 2.f);
2✔
821
    CHECK_EQUAL(tv[2].get<float>(col_float), 1.f);
2✔
822
}
2✔
823

824
TEST(TableView_QueryCopy)
825
{
2✔
826
    Table table;
2✔
827
    auto col = table.add_column(type_Int, "");
2✔
828

1✔
829
    table.create_object().set_all(0);
2✔
830
    table.create_object().set_all(1);
2✔
831
    table.create_object().set_all(2);
2✔
832

1✔
833
    // Test if copy-assign of Query in TableView works
1✔
834
    TableView tv = table.where().find_all();
2✔
835

1✔
836
    Query q = table.where();
2✔
837

1✔
838
    q.group();
2✔
839
    q.equal(col, 1);
2✔
840
    q.Or();
2✔
841
    q.equal(col, 2);
2✔
842
    q.end_group();
2✔
843

1✔
844
    q.count();
2✔
845

1✔
846
    Query q2;
2✔
847
    q2 = table.where().equal(col, 1234);
2✔
848

1✔
849
    q2 = q;
2✔
850
    size_t t = q2.count();
2✔
851

1✔
852
    CHECK_EQUAL(t, 2);
2✔
853
}
2✔
854

855
TEST(TableView_QueryCopyStringOr)
856
{
2✔
857
    Table table;
2✔
858
    auto str_col_key = table.add_column(type_String, "str_col", true);
2✔
859
    table.create_object().set_all("one");
2✔
860
    table.create_object().set_all("two");
2✔
861
    table.create_object().set_all("three");
2✔
862
    table.create_object().set_all("");
2✔
863
    table.create_object().set_null(str_col_key);
2✔
864

1✔
865
    // Test if copy-assign of Query in TableView works
1✔
866
    TableView tv = table.where().find_all();
2✔
867

1✔
868
    Query q = table.where();
2✔
869

1✔
870
    q.group();
2✔
871
    q.equal(str_col_key, "one");
2✔
872
    q.Or();
2✔
873
    q.equal(str_col_key, "two");
2✔
874
    q.Or();
2✔
875
    q.equal(str_col_key, realm::null());
2✔
876
    q.Or();
2✔
877
    q.equal(str_col_key, "");
2✔
878
    q.end_group();
2✔
879

1✔
880
    size_t before_copy_count = q.count();
2✔
881
    CHECK_EQUAL(before_copy_count, 4);
2✔
882

1✔
883
    Query q2;
2✔
884
    q2 = table.where().equal(str_col_key, "not found");
2✔
885
    size_t q2_count = q2.count();
2✔
886
    CHECK_EQUAL(q2_count, 0);
2✔
887

1✔
888
    q2 = q;
2✔
889
    size_t after_copy_count = q2.count();
2✔
890
    CHECK_EQUAL(q.count(), 4);
2✔
891
    CHECK_EQUAL(after_copy_count, 4);
2✔
892
}
2✔
893

894
TEST(TableView_SortEnum)
895
{
2✔
896
    Table table;
2✔
897
    auto col = table.add_column(type_String, "str");
2✔
898

1✔
899
    table.create_object().set_all("foo");
2✔
900
    table.create_object().set_all("foo");
2✔
901
    table.create_object().set_all("foo");
2✔
902

1✔
903
    table.enumerate_string_column(col);
2✔
904

1✔
905
    table.create_object().set_all("bbb");
2✔
906
    table.create_object().set_all("aaa");
2✔
907
    table.create_object().set_all("baz");
2✔
908

1✔
909
    TableView tv = table.where().find_all();
2✔
910
    tv.sort(col);
2✔
911

1✔
912
    CHECK_EQUAL(tv[0].get<String>(col), "aaa");
2✔
913
    CHECK_EQUAL(tv[1].get<String>(col), "baz");
2✔
914
    CHECK_EQUAL(tv[2].get<String>(col), "bbb");
2✔
915
    CHECK_EQUAL(tv[3].get<String>(col), "foo");
2✔
916
    CHECK_EQUAL(tv[4].get<String>(col), "foo");
2✔
917
    CHECK_EQUAL(tv[5].get<String>(col), "foo");
2✔
918
}
2✔
919

920
TEST(TableView_Backlinks)
921
{
2✔
922
    Group group;
2✔
923

1✔
924
    TableRef source = group.add_table("source");
2✔
925
    source->add_column(type_Int, "int");
2✔
926

1✔
927
    TableRef links = group.add_table("links");
2✔
928
    auto col_link = links->add_column(*source, "link");
2✔
929
    auto col_linklist = links->add_column_list(*source, "link_list");
2✔
930

1✔
931
    std::vector<ObjKey> keys;
2✔
932
    source->create_objects(3, keys);
2✔
933
    ObjKey k(500);
2✔
934
    {
2✔
935
        // Links
1✔
936
        Obj obj = source->get_object(keys[2]);
2✔
937
        TableView tv = obj.get_backlink_view(links, col_link);
2✔
938

1✔
939
        CHECK_EQUAL(tv.size(), 0);
2✔
940

1✔
941
        links->create_object(k).set(col_link, keys[2]).get_key();
2✔
942

1✔
943
        tv.sync_if_needed();
2✔
944
        CHECK_EQUAL(tv.size(), 1);
2✔
945
        CHECK_EQUAL(tv[0].get_key(), k);
2✔
946
    }
2✔
947
    {
2✔
948
        // LinkViews
1✔
949
        Obj obj = source->get_object(keys[2]);
2✔
950
        TableView tv = obj.get_backlink_view(links, col_linklist);
2✔
951

1✔
952
        CHECK_EQUAL(tv.size(), 0);
2✔
953

1✔
954
        auto ll = links->get_object(k).get_linklist_ptr(col_linklist);
2✔
955
        ll->add(keys[2]);
2✔
956
        ll->add(keys[0]);
2✔
957
        ll->add(keys[2]);
2✔
958

1✔
959
        tv.sync_if_needed();
2✔
960
        CHECK_EQUAL(tv.size(), 2);
2✔
961
        CHECK_EQUAL(tv[0].get_key(), k);
2✔
962
        CHECK_EQUAL(tv[1].get_key(), k);
2✔
963
    }
2✔
964
}
2✔
965

966
// Verify that a TableView that represents backlinks to a row functions correctly
967
// after being move-assigned.
968
TEST(TableView_BacklinksAfterMoveAssign)
969
{
2✔
970
    Group group;
2✔
971

1✔
972
    TableRef source = group.add_table("source");
2✔
973
    source->add_column(type_Int, "int");
2✔
974

1✔
975
    TableRef links = group.add_table("links");
2✔
976
    auto col_link = links->add_column(*source, "link");
2✔
977
    auto col_linklist = links->add_column_list(*source, "link_list");
2✔
978

1✔
979
    std::vector<ObjKey> keys;
2✔
980
    source->create_objects(3, keys);
2✔
981
    ObjKey k(500);
2✔
982
    {
2✔
983
        // Links
1✔
984
        Obj obj = source->get_object(keys[2]);
2✔
985
        TableView tv_source = obj.get_backlink_view(links, col_link);
2✔
986
        TableView tv;
2✔
987
        tv = std::move(tv_source);
2✔
988

1✔
989
        CHECK_EQUAL(tv.size(), 0);
2✔
990

1✔
991
        links->create_object(k).set(col_link, keys[2]).get_key();
2✔
992

1✔
993
        tv.sync_if_needed();
2✔
994
        CHECK_EQUAL(tv.size(), 1);
2✔
995
        CHECK_EQUAL(tv[0].get_key(), k);
2✔
996
    }
2✔
997
    {
2✔
998
        // LinkViews
1✔
999
        Obj obj = source->get_object(keys[2]);
2✔
1000
        TableView tv_source = obj.get_backlink_view(links, col_linklist);
2✔
1001
        TableView tv;
2✔
1002
        tv = std::move(tv_source);
2✔
1003

1✔
1004
        CHECK_EQUAL(tv.size(), 0);
2✔
1005

1✔
1006
        auto ll = links->get_object(k).get_linklist_ptr(col_linklist);
2✔
1007
        ll->add(keys[2]);
2✔
1008
        ll->add(keys[0]);
2✔
1009
        ll->add(keys[2]);
2✔
1010

1✔
1011
        tv.sync_if_needed();
2✔
1012
        CHECK_EQUAL(tv.size(), 2);
2✔
1013
        CHECK_EQUAL(tv[0].get_key(), k);
2✔
1014
    }
2✔
1015
}
2✔
1016

1017
TEST(TableView_SortOverLink)
1018
{
2✔
1019
    Group g;
2✔
1020
    TableRef target = g.add_table("target");
2✔
1021
    TableRef origin = g.add_table("origin");
2✔
1022
    auto col_link = origin->add_column(*target, "link");
2✔
1023
    auto col_int = origin->add_column(type_Int, "int");
2✔
1024
    auto col_str = target->add_column(type_String, "s", true);
2✔
1025

1✔
1026
    target->create_object().set(col_str, StringData("bravo"));
2✔
1027
    target->create_object().set(col_str, StringData("alfa"));
2✔
1028
    target->create_object().set(col_str, StringData("delta"));
2✔
1029
    Obj obj = target->create_object().set(col_str, StringData("charley"));
2✔
1030

1✔
1031

1✔
1032
    int64_t i = 0;
2✔
1033
    for (auto it : *target) {
8✔
1034
        Obj o = origin->create_object();
8✔
1035
        o.set(col_int, i);
8✔
1036
        o.set(col_link, it.get_key());
8✔
1037
        i++;
8✔
1038
    }
8✔
1039

1✔
1040
    auto tv = origin->where().greater(col_int, 1).find_all();
2✔
1041
    CHECK_EQUAL(tv.size(), 2);
2✔
1042
    CHECK_EQUAL(tv[0].get<Int>(col_int), 2);
2✔
1043
    CHECK_EQUAL(tv[1].get<Int>(col_int), 3);
2✔
1044
    std::vector<std::vector<ExtendedColumnKey>> v = {{col_link, col_str}};
2✔
1045
    std::vector<bool> a = {true};
2✔
1046
    tv.sort(SortDescriptor{v, a});
2✔
1047
    CHECK_EQUAL(tv[0].get<Int>(col_int), 3);
2✔
1048
    CHECK_EQUAL(tv[1].get<Int>(col_int), 2);
2✔
1049

1✔
1050
    // Modifying origin table should trigger query - and sort
1✔
1051
    origin->begin()->set(col_int, 6);
2✔
1052
    tv.sync_if_needed();
2✔
1053
    CHECK_EQUAL(tv.size(), 3);
2✔
1054
    CHECK_EQUAL(tv[0].get<Int>(col_int), 6);
2✔
1055
    CHECK_EQUAL(tv[1].get<Int>(col_int), 3);
2✔
1056
    CHECK_EQUAL(tv[2].get<Int>(col_int), 2);
2✔
1057

1✔
1058
    // Modifying target table should trigger sort
1✔
1059
    obj.set(col_str, StringData("echo"));
2✔
1060
    tv.sync_if_needed();
2✔
1061
    CHECK_EQUAL(tv.size(), 3);
2✔
1062
    CHECK_EQUAL(tv[0].get<Int>(col_int), 6);
2✔
1063
    CHECK_EQUAL(tv[1].get<Int>(col_int), 2);
2✔
1064
    CHECK_EQUAL(tv[2].get<Int>(col_int), 3);
2✔
1065
}
2✔
1066

1067
TEST(TableView_SortOverMultiLink)
1068
{
2✔
1069
    Group g;
2✔
1070
    TableRef target = g.add_table("target");
2✔
1071
    TableRef between = g.add_table("between");
2✔
1072
    TableRef origin = g.add_table("origin");
2✔
1073
    auto col_link1 = origin->add_column(*between, "link");
2✔
1074
    auto col_link2 = between->add_column(*target, "link");
2✔
1075
    auto col_int = origin->add_column(type_Int, "int");
2✔
1076

1✔
1077
    auto col_str = target->add_column(type_String, "str");
2✔
1078

1✔
1079
    target->create_object().set(col_str, StringData("bravo"));
2✔
1080
    target->create_object().set(col_str, StringData("alfa"));
2✔
1081
    target->create_object().set(col_str, StringData("delta"));
2✔
1082
    target->create_object().set(col_str, StringData("charley"));
2✔
1083

1✔
1084
    int64_t i = 27;
2✔
1085
    for (auto it : *target) {
8✔
1086
        Obj o1 = origin->create_object();
8✔
1087
        ObjKey k(i);
8✔
1088
        Obj o2 = between->create_object(k);
8✔
1089
        o1.set(col_int, i);
8✔
1090
        o1.set(col_link1, k);
8✔
1091
        o2.set(col_link2, it.get_key());
8✔
1092
        i++;
8✔
1093
    }
8✔
1094

1✔
1095
    auto tv = origin->where().find_all();
2✔
1096
    CHECK_EQUAL(tv.size(), 4);
2✔
1097
    CHECK_EQUAL(tv[0].get<Int>(col_int), 27);
2✔
1098
    CHECK_EQUAL(tv[1].get<Int>(col_int), 28);
2✔
1099
    CHECK_EQUAL(tv[2].get<Int>(col_int), 29);
2✔
1100
    CHECK_EQUAL(tv[3].get<Int>(col_int), 30);
2✔
1101

1✔
1102
    std::vector<std::vector<ExtendedColumnKey>> v = {{col_link1, col_link2, col_str}};
2✔
1103
    std::vector<bool> a = {true};
2✔
1104
    tv.sort(SortDescriptor{v, a});
2✔
1105
    CHECK_EQUAL(tv.size(), 4);
2✔
1106
    CHECK_EQUAL(tv[0].get<Int>(col_int), 28);
2✔
1107
    CHECK_EQUAL(tv[1].get<Int>(col_int), 27);
2✔
1108
    CHECK_EQUAL(tv[2].get<Int>(col_int), 30);
2✔
1109
    CHECK_EQUAL(tv[3].get<Int>(col_int), 29);
2✔
1110

1✔
1111
    // swap first two links in between
1✔
1112
    auto it = target->begin();
2✔
1113
    between->get_object(1).set(col_link2, it->get_key());
2✔
1114
    ++it;
2✔
1115
    between->get_object(0).set(col_link2, it->get_key());
2✔
1116

1✔
1117
    tv.sync_if_needed();
2✔
1118
    CHECK_EQUAL(tv.size(), 4);
2✔
1119
    CHECK_EQUAL(tv[0].get<Int>(col_int), 27);
2✔
1120
    CHECK_EQUAL(tv[1].get<Int>(col_int), 28);
2✔
1121
    CHECK_EQUAL(tv[2].get<Int>(col_int), 30);
2✔
1122
    CHECK_EQUAL(tv[3].get<Int>(col_int), 29);
2✔
1123
}
2✔
1124

1125
TEST(TableView_IsInSync)
1126
{
2✔
1127
    SHARED_GROUP_TEST_PATH(path);
2✔
1128
    auto repl = make_in_realm_history();
2✔
1129
    DBRef db_ref = DB::create(*repl, path, DBOptions(DBOptions::Durability::MemOnly));
2✔
1130

1✔
1131
    auto tr = db_ref->start_write();
2✔
1132
    Table& table = *tr->add_table("source");
2✔
1133
    table.add_column(type_Int, "int");
2✔
1134
    tr->commit_and_continue_as_read();
2✔
1135
    auto initial_tr = tr->duplicate(); // Hold onto version
2✔
1136

1✔
1137
    // Add another column to advance transaction version
1✔
1138
    tr->promote_to_write();
2✔
1139
    table.add_column(type_Double, "double");
2✔
1140
    tr->commit_and_continue_as_read();
2✔
1141

1✔
1142
    VersionID src_v = tr->get_version_of_current_transaction();
2✔
1143
    VersionID initial_v = initial_tr->get_version_of_current_transaction();
2✔
1144
    CHECK_NOT_EQUAL(src_v.version, initial_v.version);
2✔
1145

1✔
1146
    TableView tv = table.where().find_all();
2✔
1147
    TableView ctv0 = TableView(tv, initial_tr.get(), PayloadPolicy::Copy);
2✔
1148
    TableView ctv1 = TableView(tv, tr.get(), PayloadPolicy::Copy);
2✔
1149

1✔
1150
    CHECK_NOT(ctv0.is_in_sync());
2✔
1151
    CHECK(ctv1.is_in_sync());
2✔
1152
}
2✔
1153

1154
namespace {
1155
struct DistinctDirect {
1156
    Table& table;
1157
    DistinctDirect(TableRef, TableRef t, ColKey)
1158
        : table(*t)
1159
    {
2✔
1160
    }
2✔
1161

1162
    SortDescriptor get_sort(std::initializer_list<ColKey> columns, std::vector<bool> ascending = {}) const
1163
    {
12✔
1164
        std::vector<std::vector<ExtendedColumnKey>> column_indices;
12✔
1165
        for (ColKey col : columns)
12✔
1166
            column_indices.push_back({col});
12✔
1167
        return SortDescriptor(column_indices, ascending);
12✔
1168
    }
12✔
1169

1170
    DistinctDescriptor get_distinct(std::initializer_list<ColKey> columns) const
1171
    {
14✔
1172
        std::vector<std::vector<ExtendedColumnKey>> column_indices;
14✔
1173
        for (ColKey col : columns)
14✔
1174
            column_indices.push_back({col});
20✔
1175
        return DistinctDescriptor(column_indices);
14✔
1176
    }
14✔
1177

1178
    ObjKey get_key(const TableView& tv, size_t ndx) const
1179
    {
54✔
1180
        return tv.get_key(ndx);
54✔
1181
    }
54✔
1182

1183
    StringData get_string(const TableView& tv, ColKey col, size_t row) const
1184
    {
14✔
1185
        return tv.TableView::get_object(row).get<String>(col);
14✔
1186
    }
14✔
1187

1188
    TableView find_all() const
1189
    {
14✔
1190
        return table.where().find_all();
14✔
1191
    }
14✔
1192
};
1193

1194
struct DistinctOverLink {
1195
    Table& table;
1196
    ColKey m_col_link;
1197
    DistinctOverLink(TableRef t, TableRef, ColKey col_link)
1198
        : table(*t)
1199
        , m_col_link(col_link)
1200
    {
2✔
1201
    }
2✔
1202

1203
    SortDescriptor get_sort(std::initializer_list<ColKey> columns, std::vector<bool> ascending = {}) const
1204
    {
12✔
1205
        std::vector<std::vector<ExtendedColumnKey>> column_indices;
12✔
1206
        for (ColKey col : columns)
12✔
1207
            column_indices.push_back({m_col_link, col});
12✔
1208
        return SortDescriptor(column_indices, ascending);
12✔
1209
    }
12✔
1210

1211
    DistinctDescriptor get_distinct(std::initializer_list<ColKey> columns) const
1212
    {
14✔
1213
        std::vector<std::vector<ExtendedColumnKey>> column_indices;
14✔
1214
        for (ColKey col : columns)
14✔
1215
            column_indices.push_back({m_col_link, col});
20✔
1216
        return DistinctDescriptor(column_indices);
14✔
1217
    }
14✔
1218

1219
    ObjKey get_key(const TableView& tv, size_t ndx) const
1220
    {
54✔
1221
        return tv.TableView::get_object(ndx).get<ObjKey>(m_col_link);
54✔
1222
    }
54✔
1223

1224
    StringData get_string(const TableView& tv, ColKey col, size_t ndx) const
1225
    {
14✔
1226
        return tv.TableView::get_object(ndx).get_linked_object(m_col_link).get<String>(col);
14✔
1227
    }
14✔
1228

1229
    TableView find_all() const
1230
    {
14✔
1231
        return table.where().find_all();
14✔
1232
    }
14✔
1233
};
1234
} // anonymous namespace
1235

1236
TEST_TYPES(TableView_Distinct, DistinctDirect, DistinctOverLink)
1237
{
4✔
1238
    // distinct() will preserve the original order of the row pointers, also if the order is a result of sort()
2✔
1239
    // If multiple rows are identical for the given set of distinct-columns, then only the first is kept.
2✔
1240
    // You can call sync_if_needed() to update the distinct view, just like you can for a sorted view.
2✔
1241
    // Each time you call distinct() it will compound on the previous call.
2✔
1242
    // Results of distinct are affected by a previously applied sort order.
2✔
1243

2✔
1244
    // distinct() is internally based on the existing sort() method which is well tested. Hence it's not required
2✔
1245
    // to test distinct() with all possible Realm data types.
2✔
1246

2✔
1247

2✔
1248
    Group g;
4✔
1249
    TableRef target = g.add_table("target");
4✔
1250
    TableRef origin = g.add_table("origin");
4✔
1251
    auto col_link = origin->add_column(*target, "link");
4✔
1252

2✔
1253
    Table& t = *target;
4✔
1254
    auto col_str = t.add_column(type_String, "s", true);
4✔
1255
    auto col_int = t.add_column(type_Int, "i", true);
4✔
1256
    t.add_column(type_Float, "f", true);
4✔
1257

2✔
1258
    ObjKey k0 = t.create_object().set_all(StringData(""), 100, 100.f).get_key();
4✔
1259
    ObjKey k1 = t.create_object().set_all(StringData(), 200, 200.f).get_key();
4✔
1260
    t.create_object().set_all(StringData(""), 100, 100.f).get_key();
4✔
1261
    t.create_object().set_all(StringData(), 200, 200.f).get_key();
4✔
1262
    ObjKey k4 = t.create_object().set_all(StringData("foo"), 300, 300.f).get_key();
4✔
1263
    ObjKey k5 = t.create_object().set_all(StringData("foo"), 400, 400.f).get_key();
4✔
1264
    ObjKey k6 = t.create_object().set_all(StringData("bar"), 500, 500.f).get_key();
4✔
1265

2✔
1266
    for (auto it : t) {
28✔
1267
        origin->create_object().set(col_link, it.get_key());
28✔
1268
    }
28✔
1269

2✔
1270
    TEST_TYPE h(origin, target, col_link);
4✔
1271

2✔
1272
    TableView tv;
4✔
1273
    tv = h.find_all();
4✔
1274
    tv.distinct(h.get_distinct({col_str}));
4✔
1275
    CHECK_EQUAL(tv.size(), 4);
4✔
1276
    CHECK_EQUAL(h.get_key(tv, 0), k0);
4✔
1277
    CHECK_EQUAL(h.get_key(tv, 1), k1);
4✔
1278
    CHECK_EQUAL(h.get_key(tv, 2), k4);
4✔
1279
    CHECK_EQUAL(h.get_key(tv, 3), k6);
4✔
1280

2✔
1281
    tv = h.find_all();
4✔
1282
    tv.distinct(h.get_distinct({col_str}));
4✔
1283
    tv.sort(h.get_sort({col_str}));
4✔
1284
    CHECK_EQUAL(tv.size(), 4);
4✔
1285
    CHECK_EQUAL(h.get_key(tv, 0), k1);
4✔
1286
    CHECK_EQUAL(h.get_key(tv, 1), k0);
4✔
1287
    CHECK_EQUAL(h.get_key(tv, 2), k6);
4✔
1288
    CHECK_EQUAL(h.get_key(tv, 3), k4);
4✔
1289

2✔
1290
    tv = h.find_all();
4✔
1291
    tv.distinct(h.get_distinct({col_str}));
4✔
1292
    tv.sort(h.get_sort({col_str}, {false}));
4✔
1293
    CHECK_EQUAL(h.get_key(tv, 0), k4);
4✔
1294
    CHECK_EQUAL(h.get_key(tv, 1), k6);
4✔
1295
    CHECK_EQUAL(h.get_key(tv, 2), k0);
4✔
1296
    CHECK_EQUAL(h.get_key(tv, 3), k1);
4✔
1297

2✔
1298
    // Note here that our stable sort will sort the two "foo"s like row {4, 5}
2✔
1299
    tv = h.find_all();
4✔
1300
    tv.distinct(h.get_distinct({col_str, col_int}));
4✔
1301
    tv.sort(h.get_sort({col_str}, {false}));
4✔
1302
    CHECK_EQUAL(tv.size(), 5);
4✔
1303
    CHECK_EQUAL(h.get_key(tv, 0), k4);
4✔
1304
    CHECK_EQUAL(h.get_key(tv, 1), k5);
4✔
1305
    CHECK_EQUAL(h.get_key(tv, 2), k6);
4✔
1306
    CHECK_EQUAL(h.get_key(tv, 3), k0);
4✔
1307
    CHECK_EQUAL(h.get_key(tv, 4), k1);
4✔
1308

2✔
1309

2✔
1310
    // Now try distinct on string+float column. The float column has the same values as the int column
2✔
1311
    // so the result should equal the test above
2✔
1312
    tv = h.find_all();
4✔
1313
    tv.distinct(h.get_distinct({col_str, col_int}));
4✔
1314
    tv.sort(h.get_sort({col_str}, {false}));
4✔
1315
    CHECK_EQUAL(tv.size(), 5);
4✔
1316
    CHECK_EQUAL(h.get_key(tv, 0), k4);
4✔
1317
    CHECK_EQUAL(h.get_key(tv, 1), k5);
4✔
1318
    CHECK_EQUAL(h.get_key(tv, 2), k6);
4✔
1319
    CHECK_EQUAL(h.get_key(tv, 3), k0);
4✔
1320
    CHECK_EQUAL(h.get_key(tv, 4), k1);
4✔
1321

2✔
1322

2✔
1323
    // Same as previous test, but with string column being Enum
2✔
1324
    t.enumerate_string_column(col_str);
4✔
1325
    tv = h.find_all();
4✔
1326
    tv.distinct(h.get_distinct({col_str, col_int}));
4✔
1327
    tv.sort(h.get_sort({col_str}, {false}));
4✔
1328
    CHECK_EQUAL(tv.size(), 5);
4✔
1329
    CHECK_EQUAL(h.get_key(tv, 0), k4);
4✔
1330
    CHECK_EQUAL(h.get_key(tv, 1), k5);
4✔
1331
    CHECK_EQUAL(h.get_key(tv, 2), k6);
4✔
1332
    CHECK_EQUAL(h.get_key(tv, 3), k0);
4✔
1333
    CHECK_EQUAL(h.get_key(tv, 4), k1);
4✔
1334

2✔
1335

2✔
1336
    // Now test sync_if_needed()
2✔
1337
    tv = h.find_all();
4✔
1338
    // "", null, "", null, "foo", "foo", "bar"
2✔
1339

2✔
1340
    tv.distinct(h.get_distinct({col_str}));
4✔
1341
    tv.sort(h.get_sort({col_str}, {false}));
4✔
1342
    // "foo", "bar", "", null
2✔
1343

2✔
1344
    CHECK_EQUAL(tv.size(), 4);
4✔
1345
    CHECK_EQUAL(h.get_string(tv, col_str, 0), "foo");
4✔
1346
    CHECK_EQUAL(h.get_string(tv, col_str, 1), "bar");
4✔
1347
    CHECK_EQUAL(h.get_string(tv, col_str, 2), "");
4✔
1348
    CHECK(h.get_string(tv, col_str, 3).is_null());
4✔
1349

2✔
1350
    // remove "bar"
2✔
1351
    target->remove_object(k6);
4✔
1352
    // access to tv undefined; may crash
2✔
1353

2✔
1354
    tv.sync_if_needed();
4✔
1355
    // "foo", "", null
2✔
1356

2✔
1357
    CHECK_EQUAL(tv.size(), 3);
4✔
1358
    CHECK_EQUAL(h.get_string(tv, col_str, 0), "foo");
4✔
1359
    CHECK_EQUAL(h.get_string(tv, col_str, 1), "");
4✔
1360
    CHECK(h.get_string(tv, col_str, 2).is_null());
4✔
1361
}
4✔
1362

1363
TEST(TableView_DistinctOverNullLink)
1364
{
2✔
1365
    Group g;
2✔
1366
    TableRef target = g.add_table("target");
2✔
1367
    auto col_int = target->add_column(type_Int, "value");
2✔
1368

1✔
1369
    ObjKey k0 = target->create_object().set(col_int, 0).get_key();
2✔
1370
    ObjKey k1 = target->create_object().set(col_int, 1).get_key();
2✔
1371

1✔
1372
    TableRef origin = g.add_table("origin");
2✔
1373
    auto col_link = origin->add_column(*target, "link");
2✔
1374

1✔
1375
    origin->create_object().set(col_link, k0);
2✔
1376
    origin->create_object().set(col_link, k1);
2✔
1377
    origin->create_object().set(col_link, k0);
2✔
1378
    origin->create_object().set(col_link, k1);
2✔
1379
    origin->create_object(); // link is null
2✔
1380

1✔
1381
    auto tv = origin->where().find_all();
2✔
1382
    tv.distinct(DistinctDescriptor({{col_link, col_int}}));
2✔
1383
    CHECK_EQUAL(tv.size(), 2);
2✔
1384
    CHECK_EQUAL(tv.get_object(0).get_linked_object(col_link).get<Int>(col_int), 0);
2✔
1385
    CHECK_EQUAL(tv.get_object(1).get_linked_object(col_link).get<Int>(col_int), 1);
2✔
1386
}
2✔
1387

1388
TEST(TableView_IsRowAttachedAfterClear)
1389
{
2✔
1390
    Table t;
2✔
1391
    auto col_id = t.add_column(type_Int, "id");
2✔
1392

1✔
1393
    t.create_object().set(col_id, 0);
2✔
1394
    t.create_object().set(col_id, 1);
2✔
1395

1✔
1396
    TableView tv = t.where().find_all();
2✔
1397
    CHECK_EQUAL(2, tv.size());
2✔
1398
    CHECK(tv.is_obj_valid(0));
2✔
1399
    CHECK(tv.is_obj_valid(1));
2✔
1400

1✔
1401
    t.get_object(1).remove();
2✔
1402
    CHECK_EQUAL(2, tv.size());
2✔
1403
    CHECK(tv.is_obj_valid(0));
2✔
1404
    CHECK(!tv.is_obj_valid(1));
2✔
1405

1✔
1406
    t.clear();
2✔
1407
    CHECK_EQUAL(2, tv.size());
2✔
1408
    CHECK(!tv.is_obj_valid(0));
2✔
1409
    CHECK(!tv.is_obj_valid(1));
2✔
1410
}
2✔
1411

1412
TEST(TableView_IsInTableOrder)
1413
{
2✔
1414
    Group g;
2✔
1415

1✔
1416
    TableRef source = g.add_table("source");
2✔
1417
    TableRef target = g.add_table("target");
2✔
1418

1✔
1419
    auto col_link = source->add_column_list(*target, "link");
2✔
1420
    source->add_column(type_String, "name");
2✔
1421
    auto col_id = target->add_column(type_Int, "id");
2✔
1422
    target->add_search_index(col_id);
2✔
1423

1✔
1424
    Obj obj7 = target->create_object(ObjKey(7));
2✔
1425
    Obj src_obj = source->create_object();
2✔
1426
    src_obj.get_list<ObjKey>(col_link).add(ObjKey(7));
2✔
1427

1✔
1428
    // Detached views are in table order.
1✔
1429
    TableView tv;
2✔
1430
    CHECK_EQUAL(false, tv.is_in_table_order());
2✔
1431

1✔
1432
    // Queries not restricted by views are in table order.
1✔
1433
    tv = target->where().find_all();
2✔
1434
    CHECK_EQUAL(true, tv.is_in_table_order());
2✔
1435

1✔
1436
    // Views that have a distinct filter remain in table order.
1✔
1437
    tv.distinct(col_id);
2✔
1438
    CHECK_EQUAL(true, tv.is_in_table_order());
2✔
1439

1✔
1440
    // Views that are sorted are not guaranteed to be in table order.
1✔
1441
    tv.sort(col_id, true);
2✔
1442
    CHECK_EQUAL(false, tv.is_in_table_order());
2✔
1443

1✔
1444
    // Queries restricted by views are not guaranteed to be in table order.
1✔
1445
    TableView restricting_view = target->where().equal(col_id, 0).find_all();
2✔
1446
    tv = target->where(&restricting_view).find_all();
2✔
1447
    CHECK_EQUAL(false, tv.is_in_table_order());
2✔
1448

1✔
1449
    // Backlinks are not guaranteed to be in table order.
1✔
1450
    tv = obj7.get_backlink_view(source, col_link);
2✔
1451
    CHECK_EQUAL(false, tv.is_in_table_order());
2✔
1452

1✔
1453
    // Views derived from a LinkView are not guaranteed to be in table order.
1✔
1454
    auto ll = src_obj.get_linklist_ptr(col_link);
2✔
1455
    tv = ll->get_sorted_view(col_id);
2✔
1456
    CHECK_EQUAL(false, tv.is_in_table_order());
2✔
1457

1✔
1458
    // … unless sorted.
1✔
1459
    tv = target->get_sorted_view(col_id);
2✔
1460
    CHECK_EQUAL(false, tv.is_in_table_order());
2✔
1461
}
2✔
1462

1463

1464
TEST(TableView_SortOrder_Core)
1465
{
2✔
1466
    Table table;
2✔
1467
    auto col = table.add_column(type_String, "1");
2✔
1468

1✔
1469
    // This tests the expected sorting order with STRING_COMPARE_CORE. See utf8_compare() in unicode.cpp. Only
1✔
1470
    // characters
1✔
1471
    // that have a visual representation are tested (control characters such as line feed are omitted).
1✔
1472
    //
1✔
1473
    // NOTE: Your editor must assume that Core source code is in utf8, and it must save as utf8, else this unit
1✔
1474
    // test will fail.
1✔
1475

1✔
1476
    table.create_object().set_all("'");
2✔
1477
    table.create_object().set_all("-");
2✔
1478
    table.create_object().set_all( " ");
2✔
1479
    table.create_object().set_all(" ");
2✔
1480
    table.create_object().set_all( "!");
2✔
1481
    table.create_object().set_all( "\"");
2✔
1482
    table.create_object().set_all( "#");
2✔
1483
    table.create_object().set_all("$");
2✔
1484
    table.create_object().set_all( "%");
2✔
1485
    table.create_object().set_all("&");
2✔
1486
    table.create_object().set_all( "(");
2✔
1487
    table.create_object().set_all( ")");
2✔
1488
    table.create_object().set_all("*");
2✔
1489
    table.create_object().set_all(",");
2✔
1490
    table.create_object().set_all( ".");
2✔
1491
    table.create_object().set_all( "/");
2✔
1492
    table.create_object().set_all( ":");
2✔
1493
    table.create_object().set_all(";");
2✔
1494
    table.create_object().set_all( "?");
2✔
1495
    table.create_object().set_all( "@");
2✔
1496
    table.create_object().set_all( "[");
2✔
1497
    table.create_object().set_all("\\");
2✔
1498
    table.create_object().set_all( "^");
2✔
1499
    table.create_object().set_all( "_");
2✔
1500
    table.create_object().set_all( "`");
2✔
1501
    table.create_object().set_all( "{");
2✔
1502
    table.create_object().set_all( "|");
2✔
1503
    table.create_object().set_all( "}");
2✔
1504
    table.create_object().set_all("~");
2✔
1505
    table.create_object().set_all( "¡");
2✔
1506
    table.create_object().set_all("¦");
2✔
1507
    table.create_object().set_all("¨");
2✔
1508
    table.create_object().set_all("¯");
2✔
1509
    table.create_object().set_all("´");
2✔
1510
    table.create_object().set_all("¸");
2✔
1511
    table.create_object().set_all( "¿");
2✔
1512
    table.create_object().set_all("ǃ");
2✔
1513
    table.create_object().set_all("¢");
2✔
1514
    table.create_object().set_all( "£");
2✔
1515
    table.create_object().set_all("¤");
2✔
1516
    table.create_object().set_all( "¥");
2✔
1517
    table.create_object().set_all("+");
2✔
1518
    table.create_object().set_all("<");
2✔
1519
    table.create_object().set_all("=");
2✔
1520
    table.create_object().set_all(">");
2✔
1521
    table.create_object().set_all("±");
2✔
1522
    table.create_object().set_all("«");
2✔
1523
    table.create_object().set_all("»");
2✔
1524
    table.create_object().set_all("×");
2✔
1525
    table.create_object().set_all("÷");
2✔
1526
    table.create_object().set_all("ǀ");
2✔
1527
    table.create_object().set_all("ǁ");
2✔
1528
    table.create_object().set_all("ǂ");
2✔
1529
    table.create_object().set_all("§");
2✔
1530
    table.create_object().set_all("©");
2✔
1531
    table.create_object().set_all("¬");
2✔
1532
    table.create_object().set_all("®");
2✔
1533
    table.create_object().set_all("°");
2✔
1534
    table.create_object().set_all("µ");
2✔
1535
    table.create_object().set_all("¶");
2✔
1536
    table.create_object().set_all("·");
2✔
1537
    table.create_object().set_all( "0");
2✔
1538
    table.create_object().set_all("¼");
2✔
1539
    table.create_object().set_all("½");
2✔
1540
    table.create_object().set_all("¾");
2✔
1541
    table.create_object().set_all( "1");
2✔
1542
    table.create_object().set_all("¹");
2✔
1543
    table.create_object().set_all( "2");
2✔
1544
    table.create_object().set_all("ƻ");
2✔
1545
    table.create_object().set_all( "²");
2✔
1546
    table.create_object().set_all( "3");
2✔
1547
    table.create_object().set_all("³");
2✔
1548
    table.create_object().set_all( "4");
2✔
1549
    table.create_object().set_all( "5");
2✔
1550
    table.create_object().set_all("ƽ");
2✔
1551
    table.create_object().set_all("Ƽ");
2✔
1552
    table.create_object().set_all( "6");
2✔
1553
    table.create_object().set_all( "7");
2✔
1554
    table.create_object().set_all( "8");
2✔
1555
    table.create_object().set_all( "9");
2✔
1556
    table.create_object().set_all( "a");
2✔
1557
    table.create_object().set_all( "A");
2✔
1558
    table.create_object().set_all( "ª");
2✔
1559
    table.create_object().set_all( "á");
2✔
1560
    table.create_object().set_all( "Á");
2✔
1561
    table.create_object().set_all( "à");
2✔
1562
    table.create_object().set_all( "À");
2✔
1563
    table.create_object().set_all("ȧ");
2✔
1564
    table.create_object().set_all("Ȧ");
2✔
1565
    table.create_object().set_all( "â");
2✔
1566
    table.create_object().set_all( "Â");
2✔
1567
    table.create_object().set_all( "ǎ");
2✔
1568
    table.create_object().set_all( "Ǎ");
2✔
1569
    table.create_object().set_all("ă");
2✔
1570
    table.create_object().set_all("Ă");
2✔
1571
    table.create_object().set_all("ā");
2✔
1572
    table.create_object().set_all("Ā");
2✔
1573
    table.create_object().set_all( "ã");
2✔
1574
    table.create_object().set_all("Ã");
2✔
1575
    table.create_object().set_all( "ą");
2✔
1576
    table.create_object().set_all( "Ą");
2✔
1577
    table.create_object().set_all("Ⱥ");
2✔
1578
    table.create_object().set_all("ǡ");
2✔
1579
    table.create_object().set_all("Ǡ");
2✔
1580
    table.create_object().set_all("ǻ");
2✔
1581
    table.create_object().set_all("Ǻ");
2✔
1582
    table.create_object().set_all("ǟ");
2✔
1583
    table.create_object().set_all("Ǟ");
2✔
1584
    table.create_object().set_all( "ȁ");
2✔
1585
    table.create_object().set_all( "Ȁ");
2✔
1586
    table.create_object().set_all( "ȃ");
2✔
1587
    table.create_object().set_all("Ȃ");
2✔
1588
    table.create_object().set_all( "ǽ");
2✔
1589
    table.create_object().set_all("Ǽ");
2✔
1590
    table.create_object().set_all( "b");
2✔
1591
    table.create_object().set_all( "B");
2✔
1592
    table.create_object().set_all( "ƀ");
2✔
1593
    table.create_object().set_all( "Ƀ");
2✔
1594
    table.create_object().set_all( "Ɓ");
2✔
1595
    table.create_object().set_all( "ƃ");
2✔
1596
    table.create_object().set_all( "Ƃ");
2✔
1597
    table.create_object().set_all("ƅ");
2✔
1598
    table.create_object().set_all("Ƅ");
2✔
1599
    table.create_object().set_all( "c");
2✔
1600
    table.create_object().set_all( "C");
2✔
1601
    table.create_object().set_all( "ć");
2✔
1602
    table.create_object().set_all( "Ć");
2✔
1603
    table.create_object().set_all("ċ");
2✔
1604
    table.create_object().set_all("Ċ");
2✔
1605
    table.create_object().set_all( "ĉ");
2✔
1606
    table.create_object().set_all( "Ĉ");
2✔
1607
    table.create_object().set_all( "č");
2✔
1608
    table.create_object().set_all("Č");
2✔
1609
    table.create_object().set_all( "ç");
2✔
1610
    table.create_object().set_all( "Ç");
2✔
1611
    table.create_object().set_all( "ȼ");
2✔
1612
    table.create_object().set_all( "Ȼ");
2✔
1613
    table.create_object().set_all( "ƈ");
2✔
1614
    table.create_object().set_all( "Ƈ");
2✔
1615
    table.create_object().set_all("Ɔ");
2✔
1616
    table.create_object().set_all( "d");
2✔
1617
    table.create_object().set_all( "D");
2✔
1618
    table.create_object().set_all( "ď");
2✔
1619
    table.create_object().set_all( "Ď");
2✔
1620
    table.create_object().set_all( "đ");
2✔
1621
    table.create_object().set_all( "Đ");
2✔
1622
    table.create_object().set_all("ƌ");
2✔
1623
    table.create_object().set_all("Ƌ");
2✔
1624
    table.create_object().set_all("Ɗ");
2✔
1625
    table.create_object().set_all( "ð");
2✔
1626
    table.create_object().set_all( "Ð");
2✔
1627
    table.create_object().set_all("ƍ");
2✔
1628
    table.create_object().set_all( "ȸ");
2✔
1629
    table.create_object().set_all( "dz");
2✔
1630
    table.create_object().set_all( "Dz");
2✔
1631
    table.create_object().set_all( "DZ");
2✔
1632
    table.create_object().set_all( "dž");
2✔
1633
    table.create_object().set_all( "Dž");
2✔
1634
    table.create_object().set_all( "DŽ");
2✔
1635
    table.create_object().set_all("Ɖ");
2✔
1636
    table.create_object().set_all( "ȡ");
2✔
1637
    table.create_object().set_all( "e");
2✔
1638
    table.create_object().set_all( "E");
2✔
1639
    table.create_object().set_all( "é");
2✔
1640
    table.create_object().set_all( "É");
2✔
1641
    table.create_object().set_all( "è");
2✔
1642
    table.create_object().set_all( "È");
2✔
1643
    table.create_object().set_all("ė");
2✔
1644
    table.create_object().set_all("Ė");
2✔
1645
    table.create_object().set_all( "ê");
2✔
1646
    table.create_object().set_all("Ê");
2✔
1647
    table.create_object().set_all( "ë");
2✔
1648
    table.create_object().set_all( "Ë");
2✔
1649
    table.create_object().set_all("ě");
2✔
1650
    table.create_object().set_all("Ě");
2✔
1651
    table.create_object().set_all("ĕ");
2✔
1652
    table.create_object().set_all("Ĕ");
2✔
1653
    table.create_object().set_all( "ē");
2✔
1654
    table.create_object().set_all( "Ē");
2✔
1655
    table.create_object().set_all("ę");
2✔
1656
    table.create_object().set_all("Ę");
2✔
1657
    table.create_object().set_all("ȩ");
2✔
1658
    table.create_object().set_all("Ȩ");
2✔
1659
    table.create_object().set_all("ɇ");
2✔
1660
    table.create_object().set_all("Ɇ");
2✔
1661
    table.create_object().set_all( "ȅ");
2✔
1662
    table.create_object().set_all( "Ȅ");
2✔
1663
    table.create_object().set_all( "ȇ");
2✔
1664
    table.create_object().set_all("Ȇ");
2✔
1665
    table.create_object().set_all( "ǝ");
2✔
1666
    table.create_object().set_all( "Ǝ");
2✔
1667
    table.create_object().set_all( "Ə");
2✔
1668
    table.create_object().set_all( "Ɛ");
2✔
1669
    table.create_object().set_all("ȝ");
2✔
1670
    table.create_object().set_all("Ȝ");
2✔
1671
    table.create_object().set_all( "f");
2✔
1672
    table.create_object().set_all( "F");
2✔
1673
    table.create_object().set_all( "ƒ");
2✔
1674
    table.create_object().set_all( "Ƒ");
2✔
1675
    table.create_object().set_all( "g");
2✔
1676
    table.create_object().set_all( "G");
2✔
1677
    table.create_object().set_all( "ǵ");
2✔
1678
    table.create_object().set_all( "Ǵ");
2✔
1679
    table.create_object().set_all("ġ");
2✔
1680
    table.create_object().set_all("Ġ");
2✔
1681
    table.create_object().set_all( "ĝ");
2✔
1682
    table.create_object().set_all( "Ĝ");
2✔
1683
    table.create_object().set_all( "ǧ");
2✔
1684
    table.create_object().set_all( "Ǧ");
2✔
1685
    table.create_object().set_all("ğ");
2✔
1686
    table.create_object().set_all("Ğ");
2✔
1687
    table.create_object().set_all( "ģ");
2✔
1688
    table.create_object().set_all( "Ģ");
2✔
1689
    table.create_object().set_all( "ǥ");
2✔
1690
    table.create_object().set_all( "Ǥ");
2✔
1691
    table.create_object().set_all( "Ɠ");
2✔
1692
    table.create_object().set_all("Ɣ");
2✔
1693
    table.create_object().set_all( "h");
2✔
1694
    table.create_object().set_all( "H");
2✔
1695
    table.create_object().set_all( "ĥ");
2✔
1696
    table.create_object().set_all( "Ĥ");
2✔
1697
    table.create_object().set_all( "ȟ");
2✔
1698
    table.create_object().set_all( "Ȟ");
2✔
1699
    table.create_object().set_all( "ħ");
2✔
1700
    table.create_object().set_all( "Ħ");
2✔
1701
    table.create_object().set_all( "ƕ");
2✔
1702
    table.create_object().set_all( "Ƕ");
2✔
1703
    table.create_object().set_all( "i");
2✔
1704
    table.create_object().set_all( "I");
2✔
1705
    table.create_object().set_all("ı");
2✔
1706
    table.create_object().set_all( "í");
2✔
1707
    table.create_object().set_all( "Í");
2✔
1708
    table.create_object().set_all( "ì");
2✔
1709
    table.create_object().set_all( "Ì");
2✔
1710
    table.create_object().set_all("İ");
2✔
1711
    table.create_object().set_all( "î");
2✔
1712
    table.create_object().set_all("Î");
2✔
1713
    table.create_object().set_all( "ï");
2✔
1714
    table.create_object().set_all( "Ï");
2✔
1715
    table.create_object().set_all("ǐ");
2✔
1716
    table.create_object().set_all("Ǐ");
2✔
1717
    table.create_object().set_all("ĭ");
2✔
1718
    table.create_object().set_all("Ĭ");
2✔
1719
    table.create_object().set_all("ī");
2✔
1720
    table.create_object().set_all("Ī");
2✔
1721
    table.create_object().set_all( "ĩ");
2✔
1722
    table.create_object().set_all("Ĩ");
2✔
1723
    table.create_object().set_all( "į");
2✔
1724
    table.create_object().set_all( "Į");
2✔
1725
    table.create_object().set_all("Ɨ");
2✔
1726
    table.create_object().set_all( "ȉ");
2✔
1727
    table.create_object().set_all( "Ȉ");
2✔
1728
    table.create_object().set_all( "ȋ");
2✔
1729
    table.create_object().set_all( "Ȋ");
2✔
1730
    table.create_object().set_all("Ɩ");
2✔
1731
    table.create_object().set_all( "ij");
2✔
1732
    table.create_object().set_all("IJ");
2✔
1733
    table.create_object().set_all( "j");
2✔
1734
    table.create_object().set_all( "J");
2✔
1735
    table.create_object().set_all("ȷ");
2✔
1736
    table.create_object().set_all( "ĵ");
2✔
1737
    table.create_object().set_all( "Ĵ");
2✔
1738
    table.create_object().set_all("ǰ");
2✔
1739
    table.create_object().set_all( "ɉ");
2✔
1740
    table.create_object().set_all( "Ɉ");
2✔
1741
    table.create_object().set_all( "k");
2✔
1742
    table.create_object().set_all( "K");
2✔
1743
    table.create_object().set_all( "ǩ");
2✔
1744
    table.create_object().set_all( "Ǩ");
2✔
1745
    table.create_object().set_all( "ķ");
2✔
1746
    table.create_object().set_all( "Ķ");
2✔
1747
    table.create_object().set_all( "ƙ");
2✔
1748
    table.create_object().set_all( "Ƙ");
2✔
1749
    table.create_object().set_all("l");
2✔
1750
    table.create_object().set_all("L");
2✔
1751
    table.create_object().set_all( "ĺ");
2✔
1752
    table.create_object().set_all( "Ĺ");
2✔
1753
    table.create_object().set_all("ŀ");
2✔
1754
    table.create_object().set_all("Ŀ");
2✔
1755
    table.create_object().set_all( "ľ");
2✔
1756
    table.create_object().set_all( "Ľ");
2✔
1757
    table.create_object().set_all( "ļ");
2✔
1758
    table.create_object().set_all( "Ļ");
2✔
1759
    table.create_object().set_all("ƚ");
2✔
1760
    table.create_object().set_all("Ƚ");
2✔
1761
    table.create_object().set_all( "ł");
2✔
1762
    table.create_object().set_all( "Ł");
2✔
1763
    table.create_object().set_all("ƛ");
2✔
1764
    table.create_object().set_all( "lj");
2✔
1765
    table.create_object().set_all( "Lj");
2✔
1766
    table.create_object().set_all("LJ");
2✔
1767
    table.create_object().set_all("ȴ");
2✔
1768
    table.create_object().set_all( "m");
2✔
1769
    table.create_object().set_all( "M");
2✔
1770
    table.create_object().set_all("Ɯ");
2✔
1771
    table.create_object().set_all( "n");
2✔
1772
    table.create_object().set_all( "N");
2✔
1773
    table.create_object().set_all( "ń");
2✔
1774
    table.create_object().set_all( "Ń");
2✔
1775
    table.create_object().set_all( "ǹ");
2✔
1776
    table.create_object().set_all( "Ǹ");
2✔
1777
    table.create_object().set_all( "ň");
2✔
1778
    table.create_object().set_all( "Ň");
2✔
1779
    table.create_object().set_all( "ñ");
2✔
1780
    table.create_object().set_all( "Ñ");
2✔
1781
    table.create_object().set_all( "ņ");
2✔
1782
    table.create_object().set_all("Ņ");
2✔
1783
    table.create_object().set_all( "Ɲ");
2✔
1784
    table.create_object().set_all("ʼn");
2✔
1785
    table.create_object().set_all( "ƞ");
2✔
1786
    table.create_object().set_all( "Ƞ");
2✔
1787
    table.create_object().set_all("nj");
2✔
1788
    table.create_object().set_all("Nj");
2✔
1789
    table.create_object().set_all("NJ");
2✔
1790
    table.create_object().set_all( "ȵ");
2✔
1791
    table.create_object().set_all( "ŋ");
2✔
1792
    table.create_object().set_all( "Ŋ");
2✔
1793
    table.create_object().set_all( "o");
2✔
1794
    table.create_object().set_all( "O");
2✔
1795
    table.create_object().set_all( "º");
2✔
1796
    table.create_object().set_all( "ó");
2✔
1797
    table.create_object().set_all( "Ó");
2✔
1798
    table.create_object().set_all( "ò");
2✔
1799
    table.create_object().set_all( "Ò");
2✔
1800
    table.create_object().set_all("ȯ");
2✔
1801
    table.create_object().set_all("Ȯ");
2✔
1802
    table.create_object().set_all( "ô");
2✔
1803
    table.create_object().set_all( "Ô");
2✔
1804
    table.create_object().set_all( "ǒ");
2✔
1805
    table.create_object().set_all( "Ǒ");
2✔
1806
    table.create_object().set_all("ŏ");
2✔
1807
    table.create_object().set_all("Ŏ");
2✔
1808
    table.create_object().set_all("ō");
2✔
1809
    table.create_object().set_all("Ō");
2✔
1810
    table.create_object().set_all( "õ");
2✔
1811
    table.create_object().set_all( "Õ");
2✔
1812
    table.create_object().set_all("ǫ");
2✔
1813
    table.create_object().set_all("Ǫ");
2✔
1814
    table.create_object().set_all("Ɵ");
2✔
1815
    table.create_object().set_all( "ȱ");
2✔
1816
    table.create_object().set_all( "Ȱ");
2✔
1817
    table.create_object().set_all("ȫ");
2✔
1818
    table.create_object().set_all("Ȫ");
2✔
1819
    table.create_object().set_all( "ǿ");
2✔
1820
    table.create_object().set_all( "Ǿ");
2✔
1821
    table.create_object().set_all("ȭ");
2✔
1822
    table.create_object().set_all("Ȭ");
2✔
1823
    table.create_object().set_all( "ǭ");
2✔
1824
    table.create_object().set_all("Ǭ");
2✔
1825
    table.create_object().set_all( "ȍ");
2✔
1826
    table.create_object().set_all( "Ȍ");
2✔
1827
    table.create_object().set_all( "ȏ");
2✔
1828
    table.create_object().set_all( "Ȏ");
2✔
1829
    table.create_object().set_all( "ơ");
2✔
1830
    table.create_object().set_all( "Ơ");
2✔
1831
    table.create_object().set_all("ƣ");
2✔
1832
    table.create_object().set_all("Ƣ");
2✔
1833
    table.create_object().set_all( "œ");
2✔
1834
    table.create_object().set_all("Œ");
2✔
1835
    table.create_object().set_all( "ȣ");
2✔
1836
    table.create_object().set_all( "Ȣ");
2✔
1837
    table.create_object().set_all( "p");
2✔
1838
    table.create_object().set_all( "P");
2✔
1839
    table.create_object().set_all( "ƥ");
2✔
1840
    table.create_object().set_all( "Ƥ");
2✔
1841
    table.create_object().set_all( "q");
2✔
1842
    table.create_object().set_all( "Q");
2✔
1843
    table.create_object().set_all("ĸ");
2✔
1844
    table.create_object().set_all( "ɋ");
2✔
1845
    table.create_object().set_all( "Ɋ");
2✔
1846
    table.create_object().set_all("ȹ");
2✔
1847
    table.create_object().set_all( "r");
2✔
1848
    table.create_object().set_all( "R");
2✔
1849
    table.create_object().set_all("Ʀ");
2✔
1850
    table.create_object().set_all( "ŕ");
2✔
1851
    table.create_object().set_all( "Ŕ");
2✔
1852
    table.create_object().set_all( "ř");
2✔
1853
    table.create_object().set_all( "Ř");
2✔
1854
    table.create_object().set_all( "ŗ");
2✔
1855
    table.create_object().set_all( "Ŗ");
2✔
1856
    table.create_object().set_all("ɍ");
2✔
1857
    table.create_object().set_all("Ɍ");
2✔
1858
    table.create_object().set_all( "ȑ");
2✔
1859
    table.create_object().set_all( "Ȑ");
2✔
1860
    table.create_object().set_all( "ȓ");
2✔
1861
    table.create_object().set_all("Ȓ");
2✔
1862
    table.create_object().set_all( "s");
2✔
1863
    table.create_object().set_all( "S");
2✔
1864
    table.create_object().set_all( "ś");
2✔
1865
    table.create_object().set_all( "Ś");
2✔
1866
    table.create_object().set_all( "ŝ");
2✔
1867
    table.create_object().set_all( "Ŝ");
2✔
1868
    table.create_object().set_all( "š");
2✔
1869
    table.create_object().set_all( "Š");
2✔
1870
    table.create_object().set_all( "ş");
2✔
1871
    table.create_object().set_all( "Ş");
2✔
1872
    table.create_object().set_all( "ș");
2✔
1873
    table.create_object().set_all("Ș");
2✔
1874
    table.create_object().set_all( "ȿ");
2✔
1875
    table.create_object().set_all( "Ʃ");
2✔
1876
    table.create_object().set_all("ƨ");
2✔
1877
    table.create_object().set_all("Ƨ");
2✔
1878
    table.create_object().set_all( "ƪ");
2✔
1879
    table.create_object().set_all("ß");
2✔
1880
    table.create_object().set_all("ſ");
2✔
1881
    table.create_object().set_all( "t");
2✔
1882
    table.create_object().set_all( "T");
2✔
1883
    table.create_object().set_all( "ť");
2✔
1884
    table.create_object().set_all( "Ť");
2✔
1885
    table.create_object().set_all( "ţ");
2✔
1886
    table.create_object().set_all( "Ţ");
2✔
1887
    table.create_object().set_all( "ƭ");
2✔
1888
    table.create_object().set_all( "Ƭ");
2✔
1889
    table.create_object().set_all( "ƫ");
2✔
1890
    table.create_object().set_all( "Ʈ");
2✔
1891
    table.create_object().set_all( "ț");
2✔
1892
    table.create_object().set_all( "Ț");
2✔
1893
    table.create_object().set_all( "Ⱦ");
2✔
1894
    table.create_object().set_all( "ȶ");
2✔
1895
    table.create_object().set_all( "þ");
2✔
1896
    table.create_object().set_all( "Þ");
2✔
1897
    table.create_object().set_all( "ŧ");
2✔
1898
    table.create_object().set_all( "Ŧ");
2✔
1899
    table.create_object().set_all( "u");
2✔
1900
    table.create_object().set_all( "U");
2✔
1901
    table.create_object().set_all( "ú");
2✔
1902
    table.create_object().set_all( "Ú");
2✔
1903
    table.create_object().set_all( "ù");
2✔
1904
    table.create_object().set_all( "Ù");
2✔
1905
    table.create_object().set_all( "û");
2✔
1906
    table.create_object().set_all( "Û");
2✔
1907
    table.create_object().set_all( "ǔ");
2✔
1908
    table.create_object().set_all( "Ǔ");
2✔
1909
    table.create_object().set_all( "ŭ");
2✔
1910
    table.create_object().set_all( "Ŭ");
2✔
1911
    table.create_object().set_all( "ū");
2✔
1912
    table.create_object().set_all( "Ū");
2✔
1913
    table.create_object().set_all( "ũ");
2✔
1914
    table.create_object().set_all( "Ũ");
2✔
1915
    table.create_object().set_all( "ů");
2✔
1916
    table.create_object().set_all( "Ů");
2✔
1917
    table.create_object().set_all( "ų");
2✔
1918
    table.create_object().set_all( "Ų");
2✔
1919
    table.create_object().set_all( "Ʉ");
2✔
1920
    table.create_object().set_all( "ǘ");
2✔
1921
    table.create_object().set_all( "Ǘ");
2✔
1922
    table.create_object().set_all( "ǜ");
2✔
1923
    table.create_object().set_all( "Ǜ");
2✔
1924
    table.create_object().set_all( "ǚ");
2✔
1925
    table.create_object().set_all( "Ǚ");
2✔
1926
    table.create_object().set_all( "ǖ");
2✔
1927
    table.create_object().set_all( "Ǖ");
2✔
1928
    table.create_object().set_all( "ȕ");
2✔
1929
    table.create_object().set_all( "Ȕ");
2✔
1930
    table.create_object().set_all( "ȗ");
2✔
1931
    table.create_object().set_all( "Ȗ");
2✔
1932
    table.create_object().set_all( "ư");
2✔
1933
    table.create_object().set_all( "Ư");
2✔
1934
    table.create_object().set_all( "Ʊ");
2✔
1935
    table.create_object().set_all( "v");
2✔
1936
    table.create_object().set_all( "V");
2✔
1937
    table.create_object().set_all( "Ʋ");
2✔
1938
    table.create_object().set_all( "Ʌ");
2✔
1939
    table.create_object().set_all( "w");
2✔
1940
    table.create_object().set_all( "W");
2✔
1941
    table.create_object().set_all( "ŵ");
2✔
1942
    table.create_object().set_all( "Ŵ");
2✔
1943
    table.create_object().set_all( "ƿ");
2✔
1944
    table.create_object().set_all( "Ƿ");
2✔
1945
    table.create_object().set_all( "x");
2✔
1946
    table.create_object().set_all( "X");
2✔
1947
    table.create_object().set_all( "y");
2✔
1948
    table.create_object().set_all( "Y");
2✔
1949
    table.create_object().set_all( "ý");
2✔
1950
    table.create_object().set_all( "Ý");
2✔
1951
    table.create_object().set_all( "ŷ");
2✔
1952
    table.create_object().set_all( "Ŷ");
2✔
1953
    table.create_object().set_all( "ÿ");
2✔
1954
    table.create_object().set_all( "Ÿ");
2✔
1955
    table.create_object().set_all( "ȳ");
2✔
1956
    table.create_object().set_all( "Ȳ");
2✔
1957
    table.create_object().set_all( "ű");
2✔
1958
    table.create_object().set_all( "Ű");
2✔
1959
    table.create_object().set_all( "ɏ");
2✔
1960
    table.create_object().set_all( "Ɏ");
2✔
1961
    table.create_object().set_all( "ƴ");
2✔
1962
    table.create_object().set_all( "Ƴ");
2✔
1963
    table.create_object().set_all( "ü");
2✔
1964
    table.create_object().set_all( "Ü");
2✔
1965
    table.create_object().set_all( "z");
2✔
1966
    table.create_object().set_all( "Z");
2✔
1967
    table.create_object().set_all( "ź");
2✔
1968
    table.create_object().set_all( "Ź");
2✔
1969
    table.create_object().set_all( "ż");
2✔
1970
    table.create_object().set_all( "Ż");
2✔
1971
    table.create_object().set_all( "ž");
2✔
1972
    table.create_object().set_all( "Ž");
2✔
1973
    table.create_object().set_all( "ƶ");
2✔
1974
    table.create_object().set_all( "Ƶ");
2✔
1975
    table.create_object().set_all( "ȥ");
2✔
1976
    table.create_object().set_all( "Ȥ");
2✔
1977
    table.create_object().set_all( "ɀ");
2✔
1978
    table.create_object().set_all( "æ");
2✔
1979
    table.create_object().set_all( "Æ");
2✔
1980
    table.create_object().set_all( "Ʒ");
2✔
1981
    table.create_object().set_all( "ǣ");
2✔
1982
    table.create_object().set_all( "Ǣ");
2✔
1983
    table.create_object().set_all( "ä");
2✔
1984
    table.create_object().set_all( "Ä");
2✔
1985
    table.create_object().set_all( "ǯ");
2✔
1986
    table.create_object().set_all( "Ǯ");
2✔
1987
    table.create_object().set_all( "ƹ");
2✔
1988
    table.create_object().set_all( "Ƹ");
2✔
1989
    table.create_object().set_all( "ƺ");
2✔
1990
    table.create_object().set_all( "ø");
2✔
1991
    table.create_object().set_all( "Ø");
2✔
1992
    table.create_object().set_all( "ö");
2✔
1993
    table.create_object().set_all( "Ö");
2✔
1994
    table.create_object().set_all( "ő");
2✔
1995
    table.create_object().set_all( "Ő");
2✔
1996
    table.create_object().set_all( "å");
2✔
1997
    table.create_object().set_all( "Å");
2✔
1998
    table.create_object().set_all( "ƾ");
2✔
1999
    table.create_object().set_all( "ɂ");
2✔
2000
    table.create_object().set_all( "Ɂ");
2✔
2001

1✔
2002
    // Core-only is default comparer
1✔
2003
    TableView v1 = table.where().find_all();
2✔
2004
    TableView v2 = table.where().find_all();
2✔
2005

1✔
2006
    v2.sort(col);
2✔
2007

1✔
2008
    for (size_t t = 0; t < v1.size(); t++) {
1,052✔
2009
        CHECK_EQUAL(v1.get_object(t).get_key(), v2.get_object(t).get_key());
1,050✔
2010
    }
1,050✔
2011
}
2✔
2012

2013
TEST(TableView_SortNull)
2014
{
2✔
2015
    // Verifies that NULL values will come first when sorting
1✔
2016
    Table table;
2✔
2017
    auto col_int = table.add_column(type_Int, "int", true);
2✔
2018
    auto col_bool = table.add_column(type_Bool, "bool", true);
2✔
2019
    auto col_float = table.add_column(type_Float, "float", true);
2✔
2020
    auto col_double = table.add_column(type_Double, "double", true);
2✔
2021
    auto col_str = table.add_column(type_String, "string", true);
2✔
2022
    auto col_date = table.add_column(type_Timestamp, "date", true);
2✔
2023
    auto col_oid = table.add_column(type_ObjectId, "oid", true);
2✔
2024
    auto col_decimal = table.add_column(type_Decimal, "decimal", true);
2✔
2025
    auto col_int2 = table.add_column(type_Int, "int2", true);
2✔
2026

1✔
2027
    std::vector<ObjKey> keys;
2✔
2028
    auto k = table.create_object()
2✔
2029
                 .set_all(1, false, 1.0f, 1.0, "1", Timestamp(1, 1), ObjectId("000000000000000000000001"),
2✔
2030
                          Decimal128("1"), 1)
2✔
2031
                 .get_key();
2✔
2032
    keys.push_back(k);
2✔
2033
    auto all_cols = table.get_column_keys();
2✔
2034
    int i = 0;
2✔
2035
    for (auto col : all_cols) {
18✔
2036
        Obj o = table.create_object();
18✔
2037
        std::string oid_init = "00000000000000000000000" + util::to_string(i);
18✔
2038
        o.set_all(int64_t(i), false, float(i), double(i), util::to_string(i), Timestamp(i, i),
18✔
2039
                  ObjectId(oid_init.c_str()), Decimal128(i), 1);
18✔
2040
        // Set one field to Null. This element must come first when sorting by this column
9✔
2041
        o.set_null(col);
18✔
2042
        keys.push_back(o.get_key());
18✔
2043
        i++;
18✔
2044
    }
18✔
2045

1✔
2046
    auto tv = table.where().find_all();
2✔
2047
    // Without sorting first object comes first
1✔
2048
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[0]);
2✔
2049
    tv.sort(col_int);
2✔
2050
    // Now second element should come first
1✔
2051
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[1]);
2✔
2052
    tv.sort(col_bool);
2✔
2053
    // Now third element should come first
1✔
2054
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[2]);
2✔
2055
    tv.sort(col_float);
2✔
2056
    // etc.
1✔
2057
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[3]);
2✔
2058
    tv.sort(col_double);
2✔
2059
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[4]);
2✔
2060
    tv.sort(col_str);
2✔
2061
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[5]);
2✔
2062
    tv.sort(col_date);
2✔
2063
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[6]);
2✔
2064
    tv.sort(col_oid);
2✔
2065
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[7]);
2✔
2066
    tv.sort(col_decimal);
2✔
2067
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[8]);
2✔
2068
    tv.sort(col_int2);
2✔
2069
    CHECK_EQUAL(tv.get_object(0).get_key(), keys[9]);
2✔
2070
}
2✔
2071

2072
// Verify that copy-constructed and copy-assigned TableViews work normally.
2073
TEST(TableView_Copy)
2074
{
2✔
2075
    Table table;
2✔
2076
    auto col_id = table.add_column(type_Int, "id");
2✔
2077

1✔
2078
    table.create_object().set(col_id, -1);
2✔
2079
    ObjKey k1 = table.create_object().set(col_id, 1).get_key();
2✔
2080
    ObjKey k2 = table.create_object().set(col_id, 2).get_key();
2✔
2081

1✔
2082
    TableView tv = (table.column<Int>(col_id) > 0).find_all();
2✔
2083
    CHECK_EQUAL(2, tv.size());
2✔
2084

1✔
2085
    TableView copy_1(tv);
2✔
2086
    TableView copy_2;
2✔
2087
    copy_2 = tv;
2✔
2088

1✔
2089
    CHECK_EQUAL(2, copy_1.size());
2✔
2090
    CHECK_EQUAL(k1, copy_1.get_key(0));
2✔
2091
    CHECK_EQUAL(k2, copy_1.get_key(1));
2✔
2092

1✔
2093
    CHECK_EQUAL(2, copy_2.size());
2✔
2094
    CHECK_EQUAL(k1, copy_2.get_key(0));
2✔
2095
    CHECK_EQUAL(k2, copy_2.get_key(1));
2✔
2096

1✔
2097
    table.remove_object(k1);
2✔
2098

1✔
2099
    CHECK(!copy_1.is_in_sync());
2✔
2100
    CHECK(!copy_2.is_in_sync());
2✔
2101

1✔
2102
    copy_1.sync_if_needed();
2✔
2103
    CHECK_EQUAL(1, copy_1.size());
2✔
2104
    CHECK_EQUAL(k2, copy_1.get_key(0));
2✔
2105

1✔
2106
    copy_2.sync_if_needed();
2✔
2107
    CHECK_EQUAL(1, copy_2.size());
2✔
2108
    CHECK_EQUAL(k2, copy_2.get_key(0));
2✔
2109
}
2✔
2110

2111
TEST(TableView_RemoveColumnsAfterSort)
2112
{
2✔
2113
    Table table;
2✔
2114
    auto col_str0 = table.add_column(type_String, "0");
2✔
2115
    auto col_str1 = table.add_column(type_String, "1");
2✔
2116
    auto col_int = table.add_column(type_Int, "value");
2✔
2117
    for (int i = 0; i < 10; ++i) {
22✔
2118
        table.create_object().set(col_int, i);
20✔
2119
    }
20✔
2120

1✔
2121
    SortDescriptor desc({{col_int}}, {false}); // sort by the one column in descending order
2✔
2122
    table.create_object();
2✔
2123
    table.remove_column(col_str0);
2✔
2124
    auto tv = table.get_sorted_view(desc);
2✔
2125
    CHECK_EQUAL(tv.get_object(0).get<Int>(col_int), 9);
2✔
2126
    CHECK_EQUAL(tv.get_object(9).get<Int>(col_int), 0);
2✔
2127

1✔
2128
    table.remove_column(col_str1);
2✔
2129
    table.create_object();
2✔
2130
    tv.sync_if_needed();
2✔
2131
    CHECK_EQUAL(tv.get_object(0).get<Int>(col_int), 9);
2✔
2132
    CHECK_EQUAL(tv.get_object(10).get<Int>(col_int), 0);
2✔
2133
}
2✔
2134

2135
TEST(TableView_TimestampMaxRemoveRow)
2136
{
2✔
2137
    Table table;
2✔
2138
    auto col_date = table.add_column(type_Timestamp, "time");
2✔
2139
    for (size_t i = 0; i < 10; ++i) {
22✔
2140
        table.create_object().set(col_date, Timestamp(i, 0));
20✔
2141
    }
20✔
2142

1✔
2143
    TableView tv = table.where().find_all();
2✔
2144
    CHECK_EQUAL(tv.size(), 10);
2✔
2145
    CHECK_EQUAL(tv.max(col_date)->get_timestamp(), Timestamp(9, 0));
2✔
2146

1✔
2147
    table.remove_object(ObjKey(9));
2✔
2148
    CHECK_EQUAL(tv.size(), 10);                            // not changed since sync_if_needed hasn't been called
2✔
2149
    CHECK_EQUAL(tv.max(col_date)->get_timestamp(), Timestamp(8, 0)); // but aggregate functions skip removed rows
2✔
2150

1✔
2151
    tv.sync_if_needed();
2✔
2152
    CHECK_EQUAL(tv.size(), 9);
2✔
2153
    CHECK_EQUAL(tv.max(col_date)->get_timestamp(), Timestamp(8, 0));
2✔
2154
}
2✔
2155

2156
TEST(TableView_UpdateQuery)
2157
{
2✔
2158
    Table table;
2✔
2159
    auto col = table.add_column(type_Int, "first");
2✔
2160
    table.create_object().set(col, 1);
2✔
2161
    table.create_object().set(col, 2);
2✔
2162
    table.create_object().set(col, 3);
2✔
2163
    table.create_object().set(col, 3);
2✔
2164

1✔
2165
    Query q = table.where().equal(col, 1);
2✔
2166
    TableView v = q.find_all();
2✔
2167
    CHECK_EQUAL(1, v.size());
2✔
2168
    CHECK_EQUAL(1, v[0].get<Int>(col));
2✔
2169

1✔
2170
    // Create new query and update tableview to show this instead
1✔
2171
    Query q2 = table.where().equal(col, 3);
2✔
2172
    v.update_query(q2);
2✔
2173
    CHECK_EQUAL(2, v.size());
2✔
2174
    CHECK_EQUAL(3, v[0].get<Int>(col));
2✔
2175
    CHECK_EQUAL(3, v[1].get<Int>(col));
2✔
2176
}
2✔
2177

2178
class TestTableView : public TableView {
2179
public:
2180
    using TableView::TableView;
2181

2182
    KeyColumn& get_keys()
UNCOV
2183
    {
×
UNCOV
2184
        return this->m_key_values;
×
UNCOV
2185
    }
×
2186
    void add_values()
2187
    {
2✔
2188
        m_key_values.create();
2✔
2189
        for (int i = 0; i < 10; i++) {
22✔
2190
            m_key_values.add(ObjKey(i));
20✔
2191
        }
20✔
2192
    }
2✔
2193
};
2194

2195
static TestTableView get_table_view(TestTableView val)
2196
{
2✔
2197
    return val;
2✔
2198
}
2✔
2199

2200
TEST(TableView_CopyKeyValues)
2201
{
2✔
2202
    TestTableView view;
2✔
2203

1✔
2204
    view.add_values();
2✔
2205

1✔
2206
    TestTableView another_view(view);
2✔
2207
    CHECK_EQUAL(another_view.size(), 10);
2✔
2208
    CHECK_EQUAL(another_view.get_key(0), ObjKey(0));
2✔
2209

1✔
2210
    TestTableView yet_another_view(get_table_view(view)); // Using move constructor
2✔
2211
    CHECK_EQUAL(yet_another_view.size(), 10);
2✔
2212
    CHECK_EQUAL(yet_another_view.get_key(0), ObjKey(0));
2✔
2213
}
2✔
2214

2215
TEST(TableView_SortFollowedByLimit)
2216
{
2✔
2217
    constexpr int limit = 100;
2✔
2218
    Table table;
2✔
2219
    auto col = table.add_column(type_Int, "first");
2✔
2220
    std::vector<int> values(10000);
2✔
2221
    std::iota(values.begin(), values.end(), 0);
2✔
2222
    std::shuffle(values.begin(), values.end(), std::mt19937(unit_test_random_seed));
2✔
2223

1✔
2224
    for (auto i : values) {
20,000✔
2225
        table.create_object().set(col, i);
20,000✔
2226
    }
20,000✔
2227

1✔
2228
    auto tv = table.where().find_all();
2✔
2229
    DescriptorOrdering ordering;
2✔
2230
    ordering.append_sort(SortDescriptor({{col}}));
2✔
2231
    ordering.append_limit(limit);
2✔
2232

1✔
2233
    auto t1 = steady_clock::now();
2✔
2234
    tv.apply_descriptor_ordering(ordering);
2✔
2235
    auto t2 = steady_clock::now();
2✔
2236

1✔
2237
    CHECK(t2 > t1);
2✔
2238
    // std::cout << duration_cast<microseconds>(t2 - t1).count() << " us" << std::endl;
1✔
2239

1✔
2240
    CHECK_EQUAL(tv.size(), limit);
2✔
2241
    for (int i = 0; i < limit; i++) {
202✔
2242
        CHECK_EQUAL(tv.get_object(i).get<Int>(col), i);
200✔
2243
    }
200✔
2244
}
2✔
2245

2246
#endif // TEST_TABLE_VIEW
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