• 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

97.17
/test/test_json.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_JSON
21

22
#include <algorithm>
23
#include <limits>
24
#include <string>
25
#include <fstream>
26
#include <ostream>
27
#include <chrono>
28

29
#include <realm.hpp>
30
#include <external/json/json.hpp>
31

32
#include "util/misc.hpp"
33

34
#include "test.hpp"
35

36
using namespace realm;
37
using namespace realm::util;
38
using namespace realm::test_util;
39

40

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

70
namespace {
71

72
const bool generate_all = false;
73

74
// After modifying json methods in core, set above generate_all = true to
75
// make the unit tests output their results to files. Then inspect the
76
// files manually to see if the json is correct.
77
//
78
// Finally set generate_all = false and commit them to github which will
79
// make all successive runs compare their produced json with these files
80
//
81
// All produced json is automatically checked for syntax regardless of
82
// the setting of generate_all. This is done using the 'nlohmann::json' parser.
83

84
void setup_multi_table(Table& table, size_t rows)
85
{
6✔
86
    // Create table with all column types
3✔
87
    table.add_column(type_Int, "int");                                     //  0
6✔
88
    table.add_column(type_Bool, "bool");                                   //  1
6✔
89
    table.add_column(type_Timestamp, "date");                              //  2
6✔
90
    table.add_column(type_Float, "float");                                 //  3
6✔
91
    table.add_column(type_Double, "double");                               //  4
6✔
92
    table.add_column(type_String, "string");                               //  5
6✔
93
    table.add_column(type_String, "string_long");                          //  6
6✔
94
    ColKey col_string_big = table.add_column(type_String, "string_big_blobs"); //  7
6✔
95
    ColKey col_string_enum = table.add_column(type_String, "string_enum");     //  8 - becomes StringEnumColumn
6✔
96
    ColKey col_binary = table.add_column(type_Binary, "binary");               //  9
6✔
97
    ColKey col_oid = table.add_column(type_ObjectId, "oid");                   //  10
6✔
98
    ColKey col_decimal = table.add_column(type_Decimal, "decimal");            //  11
6✔
99
    ColKey col_int_list = table.add_column_list(type_Int, "integers");         //  12
6✔
100
    ColKey col_string_list = table.add_column_list(type_String, "strings");    //  13
6✔
101
    ColKey col_dict = table.add_column_dictionary(type_Int, "dictionary");     //  14
6✔
102
    ColKey col_set = table.add_column_set(type_Int, "set");                    //  15
6✔
103
    ColKey col_uuid = table.add_column(type_UUID, "uuid");                     //  16
6✔
104

3✔
105

3✔
106
    std::vector<std::string> strings;
6✔
107
    for (size_t i = 0; i < rows; ++i) {
96✔
108
        std::stringstream out;
90✔
109
        out << "string" << i;
90✔
110
        strings.push_back(out.str());
90✔
111
    }
90✔
112
    for (size_t i = 0; i < rows; ++i) {
96✔
113
        int64_t sign = (i % 2 == 0) ? 1 : -1;
69✔
114
        std::string long_str = strings[i] + " very long string.........";
90✔
115
        auto obj = table.create_object().set_all(int64_t(i * sign), (i % 2 ? true : false), Timestamp{12345, 0},
69✔
116
                                                 123.456f * sign, 9876.54321 * sign, strings[i], long_str);
90✔
117
        switch (i % 2) {
90✔
118
            case 0: {
48✔
119
                std::string s = strings[i];
48✔
120
                s += " very long string.........";
48✔
121
                for (int j = 0; j != 4; ++j)
240✔
122
                    s += " big blobs big blobs big blobs"; // +30
192✔
123
                obj.set(col_string_big, s);
48✔
124
                break;
48✔
125
            }
×
126
            case 1:
42✔
127
                obj.set(col_string_big, "");
42✔
128
                break;
42✔
129
        }
90✔
130
        switch (i % 3) {
90✔
131
            case 0:
30✔
132
                obj.set(col_string_enum, "enum1");
30✔
133
                break;
30✔
134
            case 1:
30✔
135
                obj.set(col_string_enum, "enum2");
30✔
136
                break;
30✔
137
            case 2:
30✔
138
                obj.set(col_string_enum, "enum3");
30✔
139
                break;
30✔
140
        }
90✔
141
        obj.set(col_binary, BinaryData("binary", 7));
90✔
142
        obj.set(col_oid, ObjectId());
90✔
143
        obj.set(col_decimal, Decimal128("1.2345"));
90✔
144
        obj.set(col_uuid, UUID());
90✔
145
        auto int_list = obj.get_list<Int>(col_int_list);
90✔
146
        auto str_list = obj.get_list<String>(col_string_list);
90✔
147
        for (size_t n = 0; n < i % 5; n++) {
270✔
148
            int64_t val = -123 + i * n * 1234 * sign;
180✔
149
            std::string str = "sub_" + util::to_string(val);
180✔
150
            int_list.add(val);
180✔
151
            str_list.add(str);
180✔
152
        }
180✔
153

45✔
154
        auto dict = obj.get_dictionary(col_dict);
90✔
155
        dict.insert("a", 2);
90✔
156

45✔
157
        auto set = obj.get_set<Int>(col_set);
90✔
158
        set.insert(123);
90✔
159
    }
90✔
160

3✔
161
    // We also want a StringEnumColumn
3✔
162
    table.enumerate_string_column(col_string_enum);
6✔
163
}
6✔
164

165
bool json_test(std::string json, std::string expected_file, bool generate)
166
{
102✔
167
    // std::cout << json << std::endl;
51✔
168
    std::string file_name = get_test_resource_path();
102✔
169
    file_name += expected_file + ".json";
102✔
170

51✔
171
    auto j = nlohmann::json::parse(json);
102✔
172

51✔
173
    if (generate) {
102✔
174
        // Generate the testdata to compare. After doing this,
175
        // verify that the output is correct with a json validator:
176
        // http://jsonformatter.curiousconcept.com/
177
        std::ofstream test_file(file_name.c_str(), std::ios::out | std::ios::binary);
×
178
        test_file << j;
×
179
        std::cerr << "\n----------------------------------------\n";
×
180
        std::cerr << "Generated " << expected_file << ":\n";
×
181
        std::cerr << json << "\n----------------------------------------\n";
×
182
        return true;
×
183
    }
×
184
    else {
102✔
185
        nlohmann::json expected;
102✔
186
        std::ifstream test_file(file_name.c_str(), std::ios::in | std::ios::binary);
102✔
187

51✔
188
        // fixme, find a way to use CHECK from a function
51✔
189
        if (!test_file.good())
102✔
190
            return false;
×
191
        if (test_file.fail())
102✔
192
            return false;
×
193
        test_file >> expected;
102✔
194
        if (j != expected) {
102✔
195
            std::cout << "Current: " << json << std::endl;
×
196
            std::cout << std::endl;
×
197
            std::cout << "Excpected " << expected << std::endl;
×
198
            std::cout << std::endl;
×
199
            std::string file_name = get_test_resource_path();
×
200
            std::string path = file_name + "bad_" + expected_file + ".json";
×
201
            std::string pathOld = "bad_" + file_name;
×
202
            File out(path, File::mode_Write);
×
203
            out.write(json);
×
204
            std::cerr << "\n error result in '" << std::string(path) << "'\n";
×
UNCOV
205
            return false;
×
UNCOV
206
        }
×
207
        return true;
102✔
208
    }
102✔
209
}
102✔
210

211

212
std::map<std::string, std::string> no_renames;
213

214
TEST(Json_NoLinks)
215
{
2✔
216
    Table table;
2✔
217
    setup_multi_table(table, 15);
2✔
218

1✔
219
    std::stringstream ss;
2✔
220
    table.to_json(ss, 0, no_renames);
2✔
221
    CHECK(json_test(ss.str(), "expect_json", generate_all));
2✔
222
    return;
2✔
223
}
2✔
224

225
TEST(Xjson_NoLinks)
226
{
2✔
227
    Table table;
2✔
228
    setup_multi_table(table, 15);
2✔
229

1✔
230
    std::stringstream ss;
2✔
231
    table.to_json(ss, 0, no_renames, output_mode_xjson);
2✔
232

1✔
233
    CHECK(json_test(ss.str(), "expect_xjson", generate_all));
2✔
234
    return;
2✔
235
}
2✔
236

237
TEST(Xjson_Plus_NoLinks)
238
{
2✔
239
    Table table;
2✔
240
    setup_multi_table(table, 15);
2✔
241

1✔
242
    std::stringstream ss;
2✔
243
    table.to_json(ss, 0, no_renames, output_mode_xjson_plus);
2✔
244

1✔
245
    CHECK(json_test(ss.str(), "expect_xjson_plus", generate_all));
2✔
246
    return;
2✔
247
}
2✔
248

249
/*
250
For tables with links, the link_depth argument in to_json() means following:
251

252
link_depth = -1:
253
    Follow links to infinite depth, but only follow each link exactly once. Not suitable if cycles exist because they
254
    make it complex to find out what link is being followed for a table that has multiple outgoing links
255

256
link_depth >= 0:
257
    Follow all possible permitations of link paths that are at most link_depth links deep. A link can be taken any
258
    number if times.
259

260
A link which isn't followed (bottom of link_depth has been met, or link has already been followed with
261
    link_depth = -1) is printed as a simple sequence of integers of row indexes in the link column.
262
*/
263
TEST(Json_LinkList1)
264
{
2✔
265
    // Basic non-cyclic LinkList test that also tests column and table renaming
1✔
266
    Group group;
2✔
267

1✔
268
    TableRef table1 = group.add_table("table1");
2✔
269
    TableRef table2 = group.add_table("table2");
2✔
270
    TableRef table3 = group.add_table("table3");
2✔
271

1✔
272
    // add some more columns to table1 and table2
1✔
273
    table1->add_column(type_Int, "col1");
2✔
274
    table1->add_column(type_String, "str1");
2✔
275

1✔
276
    table2->add_column(type_Int, "col1");
2✔
277
    table2->add_column(type_String, "str2");
2✔
278

1✔
279
    table3->add_column(type_Int, "col1");
2✔
280
    table3->add_column(type_String, "str2");
2✔
281

1✔
282
    // add some rows
1✔
283
    auto obj0 = table1->create_object().set_all(100, "foo");
2✔
284
    auto obj1 = table1->create_object().set_all(200, "!");
2✔
285
    table1->create_object().set_all(300, "bar");
2✔
286

1✔
287
    table2->create_object().set_all(400, "hello");
2✔
288
    auto k21 = table2->create_object().set_all(500, "world").get_key();
2✔
289
    auto k22 = table2->create_object().set_all(600, "!").get_key();
2✔
290

1✔
291
    auto k30 = table3->create_object().set_all(700, "baz").get_key();
2✔
292
    table3->create_object().set_all(800, "test");
2✔
293
    auto k32 = table3->create_object().set_all(900, "hi").get_key();
2✔
294

1✔
295
    ColKey col_link2 = table1->add_column_list(*table2, "linkA");
2✔
296
    ColKey col_link3 = table1->add_column_list(*table3, "linkB");
2✔
297

1✔
298
    // set some links
1✔
299
    auto ll0 = obj0.get_linklist(col_link2); // Links to table 2
2✔
300
    ll0.add(k21);
2✔
301

1✔
302
    auto ll1 = obj1.get_linklist(col_link2); // Links to table 2
2✔
303
    ll1.add(k21);
2✔
304
    ll1.add(k22);
2✔
305

1✔
306
    auto ll2 = obj0.get_linklist(col_link3); // Links to table 3
2✔
307
    ll2.add(k30);
2✔
308
    ll2.add(k32);
2✔
309

1✔
310
    std::stringstream ss;
2✔
311

1✔
312
    // Now try different link_depth arguments
1✔
313
    table1->to_json(ss, 0, no_renames);
2✔
314
    CHECK(json_test(ss.str(), "expected_json_linklist1_1", generate_all));
2✔
315

1✔
316
    ss.str("");
2✔
317
    table1->to_json(ss, -1, no_renames);
2✔
318
    CHECK(json_test(ss.str(), "expected_json_linklist1_2", generate_all));
2✔
319

1✔
320
    ss.str("");
2✔
321
    table1->to_json(ss, 0, no_renames);
2✔
322
    CHECK(json_test(ss.str(), "expected_json_linklist1_3", generate_all));
2✔
323

1✔
324
    ss.str("");
2✔
325
    table1->to_json(ss, 1, no_renames);
2✔
326
    CHECK(json_test(ss.str(), "expected_json_linklist1_4", generate_all));
2✔
327

1✔
328
    ss.str("");
2✔
329
    table1->to_json(ss, 2, no_renames);
2✔
330
    CHECK(json_test(ss.str(), "expected_json_linklist1_5", generate_all));
2✔
331

1✔
332
    // Column and table renaming
1✔
333
    std::map<std::string, std::string> m;
2✔
334
    m["str1"] = "STR1";
2✔
335
    m["linkA"] = "LINKA";
2✔
336
    m["table1"] = "TABLE1";
2✔
337
    ss.str("");
2✔
338
    table1->to_json(ss, 2, m);
2✔
339
    CHECK(json_test(ss.str(), "expected_json_linklist1_6", generate_all));
2✔
340
}
2✔
341

342
TEST(Json_LinkListCycle)
343
{
2✔
344
    // Cycle in LinkList
1✔
345
    Group group;
2✔
346

1✔
347
    TableRef table1 = group.add_table("table1");
2✔
348
    TableRef table2 = group.add_table("table2");
2✔
349

1✔
350
    table1->add_column(type_String, "str1");
2✔
351
    table2->add_column(type_String, "str2");
2✔
352

1✔
353
    // add some rows
1✔
354
    auto t10 = table1->create_object().set_all("hello");
2✔
355
    auto t11 = table1->create_object().set_all("world");
2✔
356

1✔
357
    table2->create_object().set_all("bar");
2✔
358
    auto t20 = table2->create_object().set_all("foo");
2✔
359

1✔
360
    auto col_link1 = table1->add_column_list(*table2, "linkA");
2✔
361
    auto col_link2 = table2->add_column_list(*table1, "linkB");
2✔
362

1✔
363
    // set some links
1✔
364
    auto links1 = t10.get_linklist(col_link1);
2✔
365
    links1.add(t20.get_key());
2✔
366

1✔
367
    auto links2 = t20.get_linklist(col_link2);
2✔
368
    links2.add(t10.get_key());
2✔
369
    links2.add(t11.get_key());
2✔
370

1✔
371
    // create json
1✔
372

1✔
373
    std::stringstream ss;
2✔
374

1✔
375
    // Now try different link_depth arguments
1✔
376
    table1->to_json(ss, 0, no_renames);
2✔
377
    CHECK(json_test(ss.str(), "expected_json_linklist_cycle1", generate_all));
2✔
378

1✔
379
    ss.str("");
2✔
380
    table1->to_json(ss, -1, no_renames);
2✔
381
    CHECK(json_test(ss.str(), "expected_json_linklist_cycle2", generate_all));
2✔
382

1✔
383
    ss.str("");
2✔
384
    table1->to_json(ss, 1, no_renames);
2✔
385
    CHECK(json_test(ss.str(), "expected_json_linklist_cycle4", generate_all));
2✔
386

1✔
387
    ss.str("");
2✔
388
    table1->to_json(ss, 2, no_renames);
2✔
389
    CHECK(json_test(ss.str(), "expected_json_linklist_cycle5", generate_all));
2✔
390

1✔
391
    ss.str("");
2✔
392
    table1->to_json(ss, 3, no_renames);
2✔
393
    CHECK(json_test(ss.str(), "expected_json_linklist_cycle6", generate_all));
2✔
394
}
2✔
395

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

1✔
400
    TableRef foos = group.add_table("foo");
2✔
401
    TableRef bars = group.add_table("bar");
2✔
402

1✔
403
    auto col_str = foos->add_column(type_String, "str1");
2✔
404
    auto col_sub = foos->add_column(*foos, "sub");
2✔
405
    auto col_link = bars->add_column(*foos, "link");
2✔
406

1✔
407
    // add some rows
1✔
408
    ObjKey prev;
2✔
409
    for (int i = 0; i < 10; i++) {
22✔
410
        std::string str = "String_" + util::to_string(i);
20✔
411
        prev = foos->create_object().set(col_str, str).set(col_sub, prev).get_key();
20✔
412
    }
20✔
413
    bars->create_object().set(col_link, prev);
2✔
414

1✔
415
    // create json
1✔
416

1✔
417
    std::stringstream ss;
2✔
418

1✔
419
    // Now try different link_depth arguments
1✔
420
    bars->to_json(ss, 0, no_renames);
2✔
421
    CHECK(json_test(ss.str(), "expected_json_linklist_long1", generate_all));
2✔
422

1✔
423
    ss.str("");
2✔
424
    bars->to_json(ss, -1, no_renames);
2✔
425
    CHECK(json_test(ss.str(), "expected_json_linklist_long2", generate_all));
2✔
426

1✔
427
    ss.str("");
2✔
428
    bars->to_json(ss, 5, no_renames);
2✔
429
    CHECK(json_test(ss.str(), "expected_json_linklist_long3", generate_all));
2✔
430
}
2✔
431

432
TEST(Json_LinkCycles)
433
{
2✔
434
    // Cycle in Link
1✔
435
    Group group;
2✔
436

1✔
437
    TableRef table1 = group.add_table("table1");
2✔
438
    TableRef table2 = group.add_table("table2");
2✔
439

1✔
440
    table1->add_column(type_String, "str1");
2✔
441
    table2->add_column(type_String, "str2");
2✔
442

1✔
443
    // add some rows
1✔
444
    auto t10 = table1->create_object().set_all("hello");
2✔
445
    table1->create_object().set_all("world");
2✔
446

1✔
447
    auto t20 = table2->create_object().set_all("foo");
2✔
448

1✔
449
    ColKey col_link1 = table1->add_column(*table2, "linkA");
2✔
450
    ColKey col_link2 = table2->add_column(*table1, "linkB");
2✔
451

1✔
452
    // set some links
1✔
453
    table1->begin()->set(col_link1, t20.get_key());
2✔
454
    table2->begin()->set(col_link2, t10.get_key());
2✔
455

1✔
456
    std::stringstream ss;
2✔
457

1✔
458
    // Now try different link_depth arguments
1✔
459
    table1->to_json(ss, 0, no_renames);
2✔
460
    CHECK(json_test(ss.str(), "expected_json_link_cycles1", generate_all));
2✔
461

1✔
462
    ss.str("");
2✔
463
    table1->to_json(ss, -1, no_renames);
2✔
464
    CHECK(json_test(ss.str(), "expected_json_link_cycles2", generate_all));
2✔
465

1✔
466
    ss.str("");
2✔
467
    table1->to_json(ss, 0, no_renames);
2✔
468
    CHECK(json_test(ss.str(), "expected_json_link_cycles3", generate_all));
2✔
469

1✔
470
    ss.str("");
2✔
471
    table1->to_json(ss, 1, no_renames);
2✔
472
    CHECK(json_test(ss.str(), "expected_json_link_cycles4", generate_all));
2✔
473

1✔
474
    ss.str("");
2✔
475
    table1->to_json(ss, 2, no_renames);
2✔
476
    CHECK(json_test(ss.str(), "expected_json_link_cycles5", generate_all));
2✔
477

1✔
478
    // Redo but from a TableView instead of the Table.
1✔
479
    auto tv = table1->where().find_all();
2✔
480
    // Now try different link_depth arguments
1✔
481
    ss.str("");
2✔
482
    tv.to_json(ss);
2✔
483
    CHECK(json_test(ss.str(), "expected_json_link_cycles1", generate_all));
2✔
484

1✔
485
    ss.str("");
2✔
486
    tv.to_json(ss, -1);
2✔
487
    CHECK(json_test(ss.str(), "expected_json_link_cycles2", generate_all));
2✔
488

1✔
489
    ss.str("");
2✔
490
    tv.to_json(ss, 0);
2✔
491
    CHECK(json_test(ss.str(), "expected_json_link_cycles3", generate_all));
2✔
492

1✔
493
    ss.str("");
2✔
494
    tv.to_json(ss, 1);
2✔
495
    CHECK(json_test(ss.str(), "expected_json_link_cycles4", generate_all));
2✔
496

1✔
497
    ss.str("");
2✔
498
    tv.to_json(ss, 2);
2✔
499
    CHECK(json_test(ss.str(), "expected_json_link_cycles5", generate_all));
2✔
500
}
2✔
501

502
TEST(Xjson_LinkList1)
503
{
2✔
504
    // Basic non-cyclic LinkList test that also tests column and table renaming
1✔
505
    Group group;
2✔
506

1✔
507
    TableRef table1 = group.add_table_with_primary_key("table1", type_String, "primaryKey");
2✔
508
    TableRef table2 = group.add_table_with_primary_key("table2", type_String, "primaryKey");
2✔
509

1✔
510
    // add some more columns to table1 and table2
1✔
511
    ColKey table1Coll = table1->add_column(type_Int, "int1");
2✔
512
    ColKey table2Coll = table2->add_column(type_Int, "int2");
2✔
513

1✔
514
    // add some rows
1✔
515
    auto obj0 = table1->create_object_with_primary_key("t1o1").set(table1Coll, 100);
2✔
516
    table1->create_object_with_primary_key("t1o3").set(table1Coll, 300);
2✔
517
    auto obj1 = table1->create_object_with_primary_key("t1o2").set(table1Coll, 200);
2✔
518

1✔
519

1✔
520
    auto k20 = table2->create_object_with_primary_key("t2o1").set(table2Coll, 400).get_key();
2✔
521
    auto k21 = table2->create_object_with_primary_key("t2o2").set(table2Coll, 500).get_key();
2✔
522
    auto k22 = table2->create_object_with_primary_key("t2o3").set(table2Coll, 600).get_key();
2✔
523

1✔
524
    ColKey col_link2 = table1->add_column_list(*table2, "linkA");
2✔
525
    ColKey col_link3 = table1->add_column(*table2, "linkB");
2✔
526

1✔
527
    // set some links
1✔
528
    obj0.set(col_link3, k20);
2✔
529
    auto ll0 = obj0.get_linklist(col_link2); // Links to table 2
2✔
530
    ll0.add(k21);
2✔
531

1✔
532
    auto ll1 = obj1.get_linklist(col_link2); // Links to table 2
2✔
533
    ll1.add(k21);
2✔
534
    ll1.add(k22);
2✔
535

1✔
536
    std::stringstream ss;
2✔
537

1✔
538
    // Now try different link_depth arguments
1✔
539
    table1->to_json(ss, 0, no_renames, output_mode_xjson);
2✔
540
    CHECK(json_test(ss.str(), "expected_xjson_linklist1", generate_all));
2✔
541

1✔
542
    ss.str("");
2✔
543
    table1->to_json(ss, 0, no_renames, output_mode_xjson_plus);
2✔
544
    CHECK(json_test(ss.str(), "expected_xjson_plus_linklist1", generate_all));
2✔
545

1✔
546
    // Column and table renaming
1✔
547
    std::map<std::string, std::string> m;
2✔
548
    m["str1"] = "STR1";
2✔
549
    m["linkA"] = "LINKA";
2✔
550
    m["table1"] = "TABLE1";
2✔
551
    ss.str("");
2✔
552
    table1->to_json(ss, 2, m, output_mode_xjson);
2✔
553
    CHECK(json_test(ss.str(), "expected_xjson_linklist2", generate_all));
2✔
554

1✔
555
    ss.str("");
2✔
556
    table1->to_json(ss, 2, m, output_mode_xjson_plus);
2✔
557
    CHECK(json_test(ss.str(), "expected_xjson_plus_linklist2", generate_all));
2✔
558
}
2✔
559

560
TEST(Xjson_LinkSet1)
561
{
2✔
562
    // Basic non-cyclic LinkList test that also tests column and table renaming
1✔
563
    Group group;
2✔
564

1✔
565
    TableRef table1 = group.add_table_with_primary_key("table1", type_String, "primaryKey");
2✔
566
    TableRef table2 = group.add_table_with_primary_key("table2", type_String, "primaryKey");
2✔
567

1✔
568
    // add some more columns to table1 and table2
1✔
569
    ColKey table1Coll = table1->add_column(type_Int, "int1");
2✔
570
    ColKey table2Coll = table2->add_column(type_Int, "int2");
2✔
571

1✔
572
    // add some rows
1✔
573
    auto obj0 = table1->create_object_with_primary_key("t1o1").set(table1Coll, 100);
2✔
574
    table1->create_object_with_primary_key("t1o3").set(table1Coll, 300);
2✔
575
    auto obj1 = table1->create_object_with_primary_key("t1o2").set(table1Coll, 200);
2✔
576

1✔
577

1✔
578
    table2->create_object_with_primary_key("t2o1").set(table2Coll, 400);
2✔
579
    auto k21 = table2->create_object_with_primary_key("t2o2").set(table2Coll, 500).get_key();
2✔
580
    auto k22 = table2->create_object_with_primary_key("t2o3").set(table2Coll, 600).get_key();
2✔
581

1✔
582
    ColKey col_link2 = table1->add_column_set(*table2, "linkA");
2✔
583

1✔
584
    // set some links
1✔
585
    auto ll0 = obj0.get_linkset(col_link2); // Links to table 2
2✔
586
    ll0.insert(k21);
2✔
587

1✔
588
    auto ll1 = obj1.get_linkset(col_link2); // Links to table 2
2✔
589
    ll1.insert(k21);
2✔
590
    ll1.insert(k22);
2✔
591

1✔
592
    std::stringstream ss;
2✔
593

1✔
594
    // Now try different link_depth arguments
1✔
595
    table1->to_json(ss, 0, no_renames, output_mode_xjson);
2✔
596
    CHECK(json_test(ss.str(), "expected_xjson_linkset1", generate_all));
2✔
597

1✔
598
    ss.str("");
2✔
599
    table1->to_json(ss, 0, no_renames, output_mode_xjson_plus);
2✔
600
    CHECK(json_test(ss.str(), "expected_xjson_plus_linkset1", generate_all));
2✔
601

1✔
602
    // Column and table renaming
1✔
603
    std::map<std::string, std::string> m;
2✔
604
    m["str1"] = "STR1";
2✔
605
    m["linkA"] = "LINKA";
2✔
606
    m["table1"] = "TABLE1";
2✔
607
    ss.str("");
2✔
608
    table1->to_json(ss, 2, m, output_mode_xjson);
2✔
609
    CHECK(json_test(ss.str(), "expected_xjson_linkset2", generate_all));
2✔
610

1✔
611
    ss.str("");
2✔
612
    table1->to_json(ss, 2, m, output_mode_xjson_plus);
2✔
613
    CHECK(json_test(ss.str(), "expected_xjson_plus_linkset2", generate_all));
2✔
614
}
2✔
615

616
TEST(Xjson_LinkDictionary1)
617
{
2✔
618
    // Basic non-cyclic LinkList test that also tests column and table renaming
1✔
619
    Group group;
2✔
620

1✔
621
    TableRef table1 = group.add_table_with_primary_key("table1", type_String, "primaryKey");
2✔
622
    TableRef table2 = group.add_table_with_primary_key("table2", type_String, "primaryKey");
2✔
623

1✔
624
    // add some more columns to table1 and table2
1✔
625
    ColKey table1Coll = table1->add_column(type_Int, "int1");
2✔
626
    ColKey table2Coll = table2->add_column(type_Int, "int2");
2✔
627

1✔
628
    // add some rows
1✔
629
    auto obj0 = table1->create_object_with_primary_key("t1o1").set(table1Coll, 100);
2✔
630
    table1->create_object_with_primary_key("t1o3").set(table1Coll, 300);
2✔
631
    auto obj1 = table1->create_object_with_primary_key("t1o2").set(table1Coll, 200);
2✔
632

1✔
633

1✔
634
    table2->create_object_with_primary_key("t2o1").set(table2Coll, 400);
2✔
635
    auto k21 = table2->create_object_with_primary_key("t2o2").set(table2Coll, 500).get_key();
2✔
636
    auto k22 = table2->create_object_with_primary_key("t2o3").set(table2Coll, 600).get_key();
2✔
637
    auto k_unres = table2->get_objkey_from_primary_key("t2o4");
2✔
638

1✔
639
    ColKey col_link2 = table1->add_column_dictionary(*table2, "linkA");
2✔
640

1✔
641
    // set some links
1✔
642
    auto ll0 = obj0.get_dictionary(col_link2); // Links to table 2
2✔
643
    ll0.insert("key1", k21);
2✔
644

1✔
645
    auto ll1 = obj1.get_dictionary(col_link2); // Links to table 2
2✔
646
    ll1.insert("key2", k21);
2✔
647
    ll1.insert("key3", k22);
2✔
648
    ll1.insert("key4", k_unres);
2✔
649
    ll1.insert("key5", Mixed{});
2✔
650

1✔
651
    std::stringstream ss;
2✔
652

1✔
653
    // Now try different link_depth arguments
1✔
654
    table1->to_json(ss, 0, no_renames);
2✔
655
    CHECK(json_test(ss.str(), "expected_json_linkdict1", generate_all));
2✔
656

1✔
657
    ss.str("");
2✔
658
    table1->to_json(ss, 0, no_renames, output_mode_xjson);
2✔
659
    CHECK(json_test(ss.str(), "expected_xjson_linkdict1", generate_all));
2✔
660

1✔
661
    ss.str("");
2✔
662
    table1->to_json(ss, 0, no_renames, output_mode_xjson_plus);
2✔
663
    CHECK(json_test(ss.str(), "expected_xjson_plus_linkdict1", generate_all));
2✔
664

1✔
665
    // Column and table renaming
1✔
666
    std::map<std::string, std::string> m;
2✔
667
    m["str1"] = "STR1";
2✔
668
    m["linkA"] = "LINKA";
2✔
669
    m["table1"] = "TABLE1";
2✔
670
    ss.str("");
2✔
671
    table1->to_json(ss, 2, m);
2✔
672
    CHECK(json_test(ss.str(), "expected_json_linkdict2", generate_all));
2✔
673

1✔
674
    ss.str("");
2✔
675
    table1->to_json(ss, 2, m, output_mode_xjson);
2✔
676
    CHECK(json_test(ss.str(), "expected_xjson_linkdict2", generate_all));
2✔
677

1✔
678
    ss.str("");
2✔
679
    table1->to_json(ss, 2, m, output_mode_xjson_plus);
2✔
680
    CHECK(json_test(ss.str(), "expected_xjson_plus_linkdict2", generate_all));
2✔
681
}
2✔
682

683
TEST(Xjson_DictionaryEmbeddedObject1)
684
{
2✔
685
    // Basic EmbeddedDictionary test
1✔
686
    Group group;
2✔
687

1✔
688
    TableRef table1 = group.add_table_with_primary_key("table1", type_String, "primaryKey");
2✔
689
    TableRef table2 = group.add_table("table2", Table::Type::Embedded);
2✔
690

1✔
691
    // add some columns to table1 and table2
1✔
692
    ColKey table1Coll = table1->add_column(type_Int, "int1");
2✔
693
    ColKey col_obj = table1->add_column(*table2, "embedded");
2✔
694
    ColKey col_dict = table1->add_column_dictionary(*table2, "linkA");
2✔
695
    table2->add_column(type_Int, "int2");
2✔
696

1✔
697
    // add some rows
1✔
698
    auto obj0 = table1->create_object_with_primary_key("t1o1").set(table1Coll, 100);
2✔
699
    auto obj2 = table1->create_object_with_primary_key("t1o3").set(table1Coll, 300);
2✔
700
    auto obj1 = table1->create_object_with_primary_key("t1o2").set(table1Coll, 200);
2✔
701

1✔
702
    auto dict1 = obj0.get_dictionary(col_dict);
2✔
703
    dict1.create_and_insert_linked_object("key1").set("int2", 111);
2✔
704

1✔
705
    auto dict2 = obj1.get_dictionary(col_dict);
2✔
706
    dict2.create_and_insert_linked_object("key2").set("int2", 222);
2✔
707
    dict2.create_and_insert_linked_object("key3").set("int2", 333);
2✔
708

1✔
709
    obj2.create_and_set_linked_object(col_obj).set("int2", 123);
2✔
710

1✔
711
    std::stringstream ss;
2✔
712
    table1->to_json(ss, 0, no_renames, output_mode_json);
2✔
713
    CHECK(json_test(ss.str(), "expected_json_embeddeddict1", generate_all));
2✔
714

1✔
715
    ss.str("");
2✔
716
    table1->to_json(ss, 0, no_renames, output_mode_xjson);
2✔
717
    CHECK(json_test(ss.str(), "expected_xjson_embeddeddict1", generate_all));
2✔
718

1✔
719
    ss.str("");
2✔
720
    table1->to_json(ss, 0, no_renames, output_mode_xjson_plus);
2✔
721
    CHECK(json_test(ss.str(), "expected_xjson_plus_embeddeddict1", generate_all));
2✔
722
}
2✔
723

724
TEST(Xjson_Mixed)
725
{
2✔
726
    Group group;
2✔
727

1✔
728
    TableRef foos = group.add_table_with_primary_key("Foo", type_Int, "_id");
2✔
729
    TableRef bars = group.add_table_with_primary_key("Bar", type_Int, "_id");
2✔
730

1✔
731
    // add some more columns to table1 and table2
1✔
732
    ColKey col_any = foos->add_column(type_Mixed, "any");
2✔
733
    ColKey col_any_list = foos->add_column_list(type_Mixed, "any_list");
2✔
734
    ColKey col_any_dict = foos->add_column_dictionary(type_Mixed, "any_dict");
2✔
735
    ColKey col_int = bars->add_column(type_Int, "int");
2✔
736

1✔
737
    // add some rows
1✔
738
    for (int64_t i = 0; i < 10; i++) {
22✔
739
        bars->create_object_with_primary_key(i).set(col_int, 100 * i);
20✔
740
    }
20✔
741
    auto obj = foos->create_object_with_primary_key(999);
2✔
742

1✔
743
    auto it = bars->begin();
2✔
744
    obj.set(col_any, Mixed(it->get_link()));
2✔
745

1✔
746
    auto list = obj.get_list<Mixed>(col_any_list);
2✔
747
    list.add(Mixed((++it)->get_link()));
2✔
748
    list.add(Mixed((++it)->get_link()));
2✔
749

1✔
750
    auto dict = obj.get_dictionary(col_any_dict);
2✔
751
    dict.insert("key1", Mixed((++it)->get_link()));
2✔
752
    dict.insert("key2", Mixed((++it)->get_link()));
2✔
753

1✔
754
    std::stringstream ss;
2✔
755

1✔
756
    foos->to_json(ss, 0, no_renames);
2✔
757
    CHECK(json_test(ss.str(), "expected_json_mixed1", generate_all));
2✔
758

1✔
759
    ss.str("");
2✔
760
    foos->to_json(ss, realm::npos, no_renames);
2✔
761
    CHECK(json_test(ss.str(), "expected_json_mixed2", generate_all));
2✔
762

1✔
763
    ss.str("");
2✔
764
    foos->to_json(ss, 0, no_renames, output_mode_xjson);
2✔
765
    CHECK(json_test(ss.str(), "expected_xjson_mixed1", generate_all));
2✔
766

1✔
767
    ss.str("");
2✔
768
    foos->to_json(ss, 0, no_renames, output_mode_xjson_plus);
2✔
769
    CHECK(json_test(ss.str(), "expected_xjson_plus_mixed1", generate_all));
2✔
770
}
2✔
771

772
TEST(Xjson_LinkCycles)
773
{
2✔
774
    // Cycle in Link
1✔
775
    Group group;
2✔
776

1✔
777
    TableRef table1 = group.add_table_with_primary_key("table1", type_String, "primaryKey");
2✔
778
    TableRef table2 = group.add_table_with_primary_key("table2", type_String, "primaryKey");
2✔
779

1✔
780
    ColKey table1Coll = table1->add_column(type_String, "str1");
2✔
781
    ColKey table2Coll = table2->add_column(type_String, "str2");
2✔
782

1✔
783
    // add some rows
1✔
784
    auto t10 = table1->create_object_with_primary_key("t1o1").set(table1Coll, "hello");
2✔
785
    table1->create_object_with_primary_key("t1o2").set(table1Coll, "world");
2✔
786

1✔
787
    auto t20 = table2->create_object_with_primary_key("t2o1").set(table2Coll, "foo");
2✔
788

1✔
789
    ColKey col_link1 = table1->add_column(*table2, "linkA");
2✔
790
    ColKey col_link2 = table2->add_column(*table1, "linkB");
2✔
791

1✔
792
    // set some links
1✔
793
    table1->begin()->set(col_link1, t20.get_key());
2✔
794
    table2->begin()->set(col_link2, t10.get_key());
2✔
795

1✔
796
    std::stringstream ss;
2✔
797

1✔
798
    // Now try different link_depth arguments
1✔
799
    table1->to_json(ss, 0, no_renames, output_mode_xjson);
2✔
800
    CHECK(json_test(ss.str(), "expected_xjson_link", generate_all));
2✔
801

1✔
802
    ss.str("");
2✔
803
    table1->to_json(ss, 0, no_renames, output_mode_xjson_plus);
2✔
804
    CHECK(json_test(ss.str(), "expected_xjson_plus_link", generate_all));
2✔
805
}
2✔
806

807
TEST(Json_Nulls)
808
{
2✔
809
    Group group;
2✔
810

1✔
811
    TableRef table1 = group.add_table("table1");
2✔
812

1✔
813
    constexpr bool is_nullable = true;
2✔
814
    ColKey str_col_ndx = table1->add_column(type_String, "str_col", is_nullable);
2✔
815
    ColKey bool_col_ndx = table1->add_column(type_Bool, "bool_col", is_nullable);
2✔
816
    ColKey int_col_ndx = table1->add_column(type_Int, "int_col", is_nullable);
2✔
817
    ColKey timestamp_col_ndx = table1->add_column(type_Timestamp, "timestamp_col", is_nullable);
2✔
818

1✔
819
    // add one row, populated manually
1✔
820
    auto obj = table1->create_object();
2✔
821
    obj.set(str_col_ndx, "Hello");
2✔
822
    obj.set(bool_col_ndx, false);
2✔
823
    obj.set(int_col_ndx, 1);
2✔
824
    obj.set(timestamp_col_ndx, Timestamp{1, 1});
2✔
825
    // add one row with default null values
1✔
826
    table1->create_object();
2✔
827

1✔
828
    std::stringstream ss;
2✔
829
    table1->to_json(ss, 0, no_renames);
2✔
830
    CHECK(json_test(ss.str(), "expected_json_nulls", generate_all));
2✔
831
}
2✔
832

833
TEST(Json_Schema)
834
{
2✔
835
    Group group;
2✔
836

1✔
837
    TableRef persons = group.add_table("person");
2✔
838
    TableRef dogs = group.add_table("dog", Table::Type::Embedded);
2✔
839

1✔
840
    constexpr bool is_nullable = true;
2✔
841
    persons->add_column(type_String, "name");
2✔
842
    persons->add_column(type_Bool, "isMarried");
2✔
843
    persons->add_column(type_Int, "age", is_nullable);
2✔
844
    persons->add_column_list(type_Timestamp, "dates");
2✔
845
    persons->add_column_list(*dogs, "pet");
2✔
846
    persons->add_column_dictionary(type_Mixed, "dictionary_pet");
2✔
847
    dogs->add_column(type_String, "name");
2✔
848

1✔
849
    std::stringstream ss;
2✔
850
    group.schema_to_json(ss);
2✔
851
    const std::string json = ss.str();
2✔
852
    std::string expected =
2✔
853
        "[\n"
2✔
854
        "{\"name\":\"person\",\"tableType\":\"TopLevel\",\"properties\":["
2✔
855
        "{\"name\":\"name\",\"type\":\"string\"},"
2✔
856
        "{\"name\":\"isMarried\",\"type\":\"bool\"},"
2✔
857
        "{\"name\":\"age\",\"type\":\"int\",\"isOptional\":true},"
2✔
858
        "{\"name\":\"dates\",\"type\":\"timestamp\",\"isArray\":true},"
2✔
859
        "{\"name\":\"pet\",\"type\":\"object\",\"objectType\":\"dog\",\"isArray\":true},"
2✔
860
        "{\"name\":\"dictionary_pet\",\"type\":\"mixed\",\"isMap\":true,\"keyType\":\"string\",\"isOptional\":true}"
2✔
861
        "]},\n"
2✔
862
        "{\"name\":\"dog\",\"tableType\":\"Embedded\",\"properties\":[{\"name\":\"name\",\"type\":\"string\"}]}\n"
2✔
863
        "]\n";
2✔
864
    CHECK_EQUAL(expected, json);
2✔
865
}
2✔
866

867

868
using namespace std::chrono;
869

870
TEST(Json_Timestamp)
871
{
2✔
872
    char buffer1[31];
2✔
873
    char buffer2[31];
2✔
874
    Timestamp(-63549305085, 0).to_string(buffer1);
2✔
875
    CHECK(strcmp(buffer1, "-0044-03-15 15:15:15") == 0);
2✔
876
    Timestamp(0, 0).to_string(buffer1);
2✔
877
    CHECK(strcmp(buffer1, "1970-01-01 00:00:00") == 0);
2✔
878
    Timestamp(-1, 0).to_string(buffer1);
2✔
879
    CHECK(strcmp(buffer1, "1969-12-31 23:59:59") == 0);
2✔
880
    Timestamp(-1, -100000000).to_string(buffer1);
2✔
881
    CHECK(strcmp(buffer1, "1969-12-31 23:59:58.900000000") == 0);
2✔
882

1✔
883
    // Compare our own to_string with standard implementation
1✔
884
    // for years 1900 to 2050
1✔
885
    struct tm buf {};
2✔
886
    buf.tm_year = 1900 - 1900;
2✔
887
    buf.tm_mday = 1;
2✔
888
    auto start = mktime(&buf);
2✔
889
    buf.tm_year = 2050 - 1900;
2✔
890
    auto end = mktime(&buf);
2✔
891
    constexpr int64_t seconds_in_a_day = 24 * 60 * 60;
2✔
892

1✔
893
    for (auto d = start; d < end; d += (seconds_in_a_day - 1)) {
109,578✔
894
        Timestamp t(d, 0);
109,576✔
895
        t.to_string(buffer1);
109,576✔
896
        auto seconds = time_t(t.get_seconds());
109,576✔
897
#ifdef _MSC_VER
898
        gmtime_s(&buf, &seconds);
899
#else
900
        gmtime_r(&seconds, &buf);
109,576✔
901
#endif
109,576✔
902
        strftime(buffer2, sizeof(buffer2), "%Y-%m-%d %H:%M:%S", &buf);
109,576✔
903
        CHECK(strcmp(buffer1, buffer2) == 0);
109,576✔
904
    }
109,576✔
905
    /*
1✔
906
    auto t1 = steady_clock::now();
1✔
907
    for (auto d = 0; d < 10000; d++) {
1✔
908
        Timestamp(d * seconds_in_a_day, 0).to_string(buffer1);
1✔
909
    }
1✔
910
    auto t2 = steady_clock::now();
1✔
911
    std::cout << "   time_to_string: " << duration_cast<microseconds>(t2 - t1).count() << " us" << std::endl;
1✔
912

1✔
913
    t1 = steady_clock::now();
1✔
914
    for (auto d = 0; d < 10000; d++) {
1✔
915
        auto seconds = time_t(d);
1✔
916
        gmtime_r(&seconds, &buf);
1✔
917
        // strftime(buffer2, sizeof(buffer2), "%Y-%m-%d %H:%M:%S", &buf);
1✔
918
    }
1✔
919
    t2 = steady_clock::now();
1✔
920
    std::cout << "   gm_time: " << duration_cast<microseconds>(t2 - t1).count() << " us" << std::endl;
1✔
921
    */
1✔
922
}
2✔
923

924
} // anonymous namespace
925

926
#endif // TEST_TABLE
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