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

realm / realm-core / thomas.goyne_396

06 Jun 2024 03:48PM UTC coverage: 90.956% (+0.1%) from 90.858%
thomas.goyne_396

Pull #7698

Evergreen

tgoyne
Fix UB in Tokenizer
Pull Request #7698: RCORE-2141 RCORE-2142 Clean up a bunch of old encryption cruft

101814 of 180058 branches covered (56.55%)

1048 of 1098 new or added lines in 27 files covered. (95.45%)

132 existing lines in 20 files now uncovered.

214536 of 235869 relevant lines covered (90.96%)

6051586.73 hits per line

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

95.4
/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
#include <external/bson/bson.h>
32

33
#include "util/misc.hpp"
34

35
#include "test.hpp"
36

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

41

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

71
namespace {
72

73
const bool generate_all = false;
74

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

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

106

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

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

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

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

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

172
    auto j = nlohmann::json::parse(json);
48✔
173

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

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

212

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

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

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

229
    std::stringstream ss;
2✔
230
    table.to_json(ss, output_mode_xjson);
2✔
231

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

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

241
    std::stringstream ss;
2✔
242
    table.to_json(ss, output_mode_xjson_plus);
2✔
243

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

309
    std::stringstream ss;
2✔
310

311
    table1->to_json(ss);
2✔
312
    CHECK(json_test(ss.str(), "expected_json_linklist1_1", generate_all));
2✔
313
}
2✔
314

315
TEST(Json_LinkListCycle)
316
{
2✔
317
    // Cycle in LinkList
318
    Group group;
2✔
319

320
    TableRef table1 = group.add_table("class_Foo");
2✔
321
    TableRef table2 = group.add_table("class_Bar");
2✔
322

323
    table1->add_column(type_String, "str1");
2✔
324
    table2->add_column(type_String, "str2");
2✔
325

326
    // add some rows
327
    auto t10 = table1->create_object().set_all("hello");
2✔
328
    auto t11 = table1->create_object().set_all("world");
2✔
329

330
    table2->create_object().set_all("bar");
2✔
331
    auto t20 = table2->create_object().set_all("foo");
2✔
332

333
    auto col_link1 = table1->add_column_list(*table2, "linkA");
2✔
334
    auto col_link2 = table2->add_column_list(*table1, "linkB");
2✔
335

336
    // set some links
337
    auto links1 = t10.get_linklist(col_link1);
2✔
338
    links1.add(t20.get_key());
2✔
339

340
    auto links2 = t20.get_linklist(col_link2);
2✔
341
    links2.add(t10.get_key());
2✔
342
    links2.add(t11.get_key());
2✔
343

344
    // create json
345

346
    std::stringstream ss;
2✔
347

348
    group.to_json(ss);
2✔
349
    CHECK(json_test(ss.str(), "expected_json_linklist_cycle1", generate_all));
2✔
350
}
2✔
351

352
TEST(Json_LinkListLong)
353
{
2✔
354
    Group group;
2✔
355

356
    TableRef foos = group.add_table("foo");
2✔
357
    TableRef bars = group.add_table("bar");
2✔
358

359
    auto col_str = foos->add_column(type_String, "str1");
2✔
360
    auto col_sub = foos->add_column(*foos, "sub");
2✔
361
    auto col_link = bars->add_column(*foos, "link");
2✔
362

363
    // add some rows
364
    ObjKey prev;
2✔
365
    for (int i = 0; i < 10; i++) {
22✔
366
        std::string str = "String_" + util::to_string(i);
20✔
367
        prev = foos->create_object().set(col_str, str).set(col_sub, prev).get_key();
20✔
368
    }
20✔
369
    bars->create_object().set(col_link, prev);
2✔
370

371
    // create json
372

373
    std::stringstream ss;
2✔
374

375
    group.to_json(ss);
2✔
376
    CHECK(json_test(ss.str(), "expected_json_linklist_long1", generate_all));
2✔
377
}
2✔
378

379
TEST(Json_LinkCycles)
380
{
2✔
381
    // Cycle in Link
382
    Group group;
2✔
383

384
    TableRef table1 = group.add_table("table1");
2✔
385
    TableRef table2 = group.add_table("table2");
2✔
386

387
    table1->add_column(type_String, "str1");
2✔
388
    table2->add_column(type_String, "str2");
2✔
389

390
    // add some rows
391
    auto t10 = table1->create_object().set_all("hello");
2✔
392
    table1->create_object().set_all("world");
2✔
393

394
    auto t20 = table2->create_object().set_all("foo");
2✔
395

396
    ColKey col_link1 = table1->add_column(*table2, "linkA");
2✔
397
    ColKey col_link2 = table2->add_column(*table1, "linkB");
2✔
398

399
    // set some links
400
    table1->begin()->set(col_link1, t20.get_key());
2✔
401
    table2->begin()->set(col_link2, t10.get_key());
2✔
402

403
    std::stringstream ss;
2✔
404

405
    table1->to_json(ss);
2✔
406
    CHECK(json_test(ss.str(), "expected_json_link_cycles1", generate_all));
2✔
407

408
    // Redo but from a TableView instead of the Table.
409
    auto tv = table1->where().find_all();
2✔
410

411
    ss.str("");
2✔
412
    tv.to_json(ss);
2✔
413
    CHECK(json_test(ss.str(), "expected_json_link_cycles1", generate_all));
2✔
414
}
2✔
415

416
TEST(Xjson_LinkList1)
417
{
2✔
418
    // Basic non-cyclic LinkList test that also tests column and table renaming
419
    Group group;
2✔
420

421
    TableRef table1 = group.add_table_with_primary_key("table1", type_String, "primaryKey");
2✔
422
    TableRef table2 = group.add_table_with_primary_key("table2", type_String, "primaryKey");
2✔
423

424
    // add some more columns to table1 and table2
425
    ColKey table1Coll = table1->add_column(type_Int, "int1");
2✔
426
    ColKey table2Coll = table2->add_column(type_Int, "int2");
2✔
427

428
    // add some rows
429
    auto obj0 = table1->create_object_with_primary_key("t1o1").set(table1Coll, 100);
2✔
430
    table1->create_object_with_primary_key("t1o3").set(table1Coll, 300);
2✔
431
    auto obj1 = table1->create_object_with_primary_key("t1o2").set(table1Coll, 200);
2✔
432

433

434
    auto k20 = table2->create_object_with_primary_key("t2o1").set(table2Coll, 400).get_key();
2✔
435
    auto k21 = table2->create_object_with_primary_key("t2o2").set(table2Coll, 500).get_key();
2✔
436
    auto k22 = table2->create_object_with_primary_key("t2o3").set(table2Coll, 600).get_key();
2✔
437

438
    ColKey col_link2 = table1->add_column_list(*table2, "linkA");
2✔
439
    ColKey col_link3 = table1->add_column(*table2, "linkB");
2✔
440

441
    // set some links
442
    obj0.set(col_link3, k20);
2✔
443
    auto ll0 = obj0.get_linklist(col_link2); // Links to table 2
2✔
444
    ll0.add(k21);
2✔
445

446
    auto ll1 = obj1.get_linklist(col_link2); // Links to table 2
2✔
447
    ll1.add(k21);
2✔
448
    ll1.add(k22);
2✔
449

450
    std::stringstream ss;
2✔
451

452
    // Now try different link_depth arguments
453
    table1->to_json(ss, output_mode_xjson);
2✔
454
    CHECK(json_test(ss.str(), "expected_xjson_linklist1", generate_all));
2✔
455

456
    ss.str("");
2✔
457
    table1->to_json(ss, output_mode_xjson_plus);
2✔
458
    CHECK(json_test(ss.str(), "expected_xjson_plus_linklist1", generate_all));
2✔
459
}
2✔
460

461
TEST(Xjson_LinkSet1)
462
{
2✔
463
    // Basic non-cyclic LinkList test that also tests column and table renaming
464
    Group group;
2✔
465

466
    TableRef table1 = group.add_table_with_primary_key("table1", type_String, "primaryKey");
2✔
467
    TableRef table2 = group.add_table_with_primary_key("table2", type_String, "primaryKey");
2✔
468

469
    // add some more columns to table1 and table2
470
    ColKey table1Coll = table1->add_column(type_Int, "int1");
2✔
471
    ColKey table2Coll = table2->add_column(type_Int, "int2");
2✔
472

473
    // add some rows
474
    auto obj0 = table1->create_object_with_primary_key("t1o1").set(table1Coll, 100);
2✔
475
    table1->create_object_with_primary_key("t1o3").set(table1Coll, 300);
2✔
476
    auto obj1 = table1->create_object_with_primary_key("t1o2").set(table1Coll, 200);
2✔
477

478

479
    table2->create_object_with_primary_key("t2o1").set(table2Coll, 400);
2✔
480
    auto k21 = table2->create_object_with_primary_key("t2o2").set(table2Coll, 500).get_key();
2✔
481
    auto k22 = table2->create_object_with_primary_key("t2o3").set(table2Coll, 600).get_key();
2✔
482

483
    ColKey col_link2 = table1->add_column_set(*table2, "linkA");
2✔
484

485
    // set some links
486
    auto ll0 = obj0.get_linkset(col_link2); // Links to table 2
2✔
487
    ll0.insert(k21);
2✔
488

489
    auto ll1 = obj1.get_linkset(col_link2); // Links to table 2
2✔
490
    ll1.insert(k21);
2✔
491
    ll1.insert(k22);
2✔
492

493
    std::stringstream ss;
2✔
494

495
    table1->to_json(ss, output_mode_xjson);
2✔
496
    CHECK(json_test(ss.str(), "expected_xjson_linkset1", generate_all));
2✔
497

498
    ss.str("");
2✔
499
    table1->to_json(ss, output_mode_xjson_plus);
2✔
500
    CHECK(json_test(ss.str(), "expected_xjson_plus_linkset1", generate_all));
2✔
501
}
2✔
502

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

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

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

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

520

521
    table2->create_object_with_primary_key("t2o1").set(table2Coll, 400);
2✔
522
    auto k21 = table2->create_object_with_primary_key("t2o2").set(table2Coll, 500).get_key();
2✔
523
    auto k22 = table2->create_object_with_primary_key("t2o3").set(table2Coll, 600).get_key();
2✔
524
    auto k_unres = table2->get_objkey_from_primary_key("t2o4");
2✔
525

526
    ColKey col_link2 = table1->add_column_dictionary(*table2, "dict");
2✔
527

528
    // set some links
529
    auto ll0 = obj0.get_dictionary(col_link2); // Links to table 2
2✔
530
    ll0.insert("key1", k21);
2✔
531

532
    auto ll1 = obj1.get_dictionary(col_link2); // Links to table 2
2✔
533
    ll1.insert("key2", k21);
2✔
534
    ll1.insert("key3", k22);
2✔
535
    ll1.insert("key4", k_unres);
2✔
536
    ll1.insert("key5", Mixed{});
2✔
537

538
    std::stringstream ss;
2✔
539

540
    table1->to_json(ss);
2✔
541
    CHECK(json_test(ss.str(), "expected_json_linkdict1", generate_all));
2✔
542

543
    ss.str("");
2✔
544
    table1->to_json(ss, output_mode_xjson);
2✔
545
    CHECK(json_test(ss.str(), "expected_xjson_linkdict1", generate_all));
2✔
546

547
    ss.str("");
2✔
548
    table1->to_json(ss, output_mode_xjson_plus);
2✔
549
    CHECK(json_test(ss.str(), "expected_xjson_plus_linkdict1", generate_all));
2✔
550
}
2✔
551

552
TEST(Xjson_DictionaryEmbeddedObject1)
553
{
2✔
554
    // Basic EmbeddedDictionary test
555
    Group group;
2✔
556

557
    TableRef table1 = group.add_table_with_primary_key("table1", type_String, "primaryKey");
2✔
558
    TableRef table2 = group.add_table("table2", Table::Type::Embedded);
2✔
559

560
    // add some columns to table1 and table2
561
    ColKey table1Coll = table1->add_column(type_Int, "int1");
2✔
562
    ColKey col_obj = table1->add_column(*table2, "embedded");
2✔
563
    ColKey col_dict = table1->add_column_dictionary(*table2, "dict");
2✔
564
    table2->add_column(type_Int, "int2");
2✔
565

566
    // add some rows
567
    auto obj0 = table1->create_object_with_primary_key("t1o1").set(table1Coll, 100);
2✔
568
    auto obj2 = table1->create_object_with_primary_key("t1o3").set(table1Coll, 300);
2✔
569
    auto obj1 = table1->create_object_with_primary_key("t1o2").set(table1Coll, 200);
2✔
570

571
    auto dict1 = obj0.get_dictionary(col_dict);
2✔
572
    dict1.create_and_insert_linked_object("key1").set("int2", 111);
2✔
573

574
    auto dict2 = obj1.get_dictionary(col_dict);
2✔
575
    dict2.create_and_insert_linked_object("key2").set("int2", 222);
2✔
576
    dict2.create_and_insert_linked_object("key3").set("int2", 333);
2✔
577

578
    obj2.create_and_set_linked_object(col_obj).set("int2", 123);
2✔
579

580
    std::stringstream ss;
2✔
581
    table1->to_json(ss, output_mode_json);
2✔
582
    CHECK(json_test(ss.str(), "expected_json_embeddeddict1", generate_all));
2✔
583

584
    ss.str("");
2✔
585
    table1->to_json(ss, output_mode_xjson);
2✔
586
    CHECK(json_test(ss.str(), "expected_xjson_embeddeddict1", generate_all));
2✔
587

588
    ss.str("");
2✔
589
    table1->to_json(ss, output_mode_xjson_plus);
2✔
590
    CHECK(json_test(ss.str(), "expected_xjson_plus_embeddeddict1", generate_all));
2✔
591
}
2✔
592

593
TEST(Xjson_Mixed)
594
{
2✔
595
    Group group;
2✔
596

597
    TableRef foos = group.add_table_with_primary_key("Foo", type_Int, "_id");
2✔
598
    TableRef bars = group.add_table_with_primary_key("Bar", type_Int, "_id");
2✔
599

600
    // add some more columns to table1 and table2
601
    ColKey col_any = foos->add_column(type_Mixed, "any");
2✔
602
    ColKey col_any_list = foos->add_column_list(type_Mixed, "any_list");
2✔
603
    ColKey col_any_dict = foos->add_column_dictionary(type_Mixed, "any_dict");
2✔
604
    ColKey col_int = bars->add_column(type_Int, "int");
2✔
605

606
    // add some rows
607
    for (int64_t i = 0; i < 10; i++) {
22✔
608
        bars->create_object_with_primary_key(i).set(col_int, 100 * i);
20✔
609
    }
20✔
610
    auto obj = foos->create_object_with_primary_key(999);
2✔
611

612
    auto it = bars->begin();
2✔
613
    obj.set(col_any, Mixed(it->get_link()));
2✔
614

615
    auto list = obj.get_list<Mixed>(col_any_list);
2✔
616
    list.add(Mixed((++it)->get_link()));
2✔
617
    list.add(Mixed((++it)->get_link()));
2✔
618

619
    auto dict = obj.get_dictionary(col_any_dict);
2✔
620
    dict.insert("key1", Mixed((++it)->get_link()));
2✔
621
    dict.insert("key2", Mixed((++it)->get_link()));
2✔
622

623
    std::stringstream ss;
2✔
624

625
    foos->to_json(ss);
2✔
626
    CHECK(json_test(ss.str(), "expected_json_mixed1", generate_all));
2✔
627

628
    ss.str("");
2✔
629
    foos->to_json(ss, output_mode_xjson);
2✔
630
    CHECK(json_test(ss.str(), "expected_xjson_mixed1", generate_all));
2✔
631

632
    ss.str("");
2✔
633
    foos->to_json(ss, output_mode_xjson_plus);
2✔
634
    CHECK(json_test(ss.str(), "expected_xjson_plus_mixed1", generate_all));
2✔
635
}
2✔
636

637
TEST(Xjson_LinkCycles)
638
{
2✔
639
    // Cycle in Link
640
    Group group;
2✔
641

642
    TableRef table1 = group.add_table_with_primary_key("table1", type_String, "primaryKey");
2✔
643
    TableRef table2 = group.add_table_with_primary_key("table2", type_String, "primaryKey");
2✔
644

645
    ColKey table1Coll = table1->add_column(type_String, "str1");
2✔
646
    ColKey table2Coll = table2->add_column(type_String, "str2");
2✔
647

648
    // add some rows
649
    auto t10 = table1->create_object_with_primary_key("t1o1").set(table1Coll, "hello");
2✔
650
    table1->create_object_with_primary_key("t1o2").set(table1Coll, "world");
2✔
651

652
    auto t20 = table2->create_object_with_primary_key("t2o1").set(table2Coll, "foo");
2✔
653

654
    ColKey col_link1 = table1->add_column(*table2, "linkA");
2✔
655
    ColKey col_link2 = table2->add_column(*table1, "linkB");
2✔
656

657
    // set some links
658
    table1->begin()->set(col_link1, t20.get_key());
2✔
659
    table2->begin()->set(col_link2, t10.get_key());
2✔
660

661
    std::stringstream ss;
2✔
662

663
    // Now try different link_depth arguments
664
    table1->to_json(ss, output_mode_xjson);
2✔
665
    CHECK(json_test(ss.str(), "expected_xjson_link", generate_all));
2✔
666

667
    ss.str("");
2✔
668
    table1->to_json(ss, output_mode_xjson_plus);
2✔
669
    CHECK(json_test(ss.str(), "expected_xjson_plus_link", generate_all));
2✔
670
}
2✔
671

672
TEST(Json_Nulls)
673
{
2✔
674
    Group group;
2✔
675

676
    TableRef table1 = group.add_table("table1");
2✔
677

678
    constexpr bool is_nullable = true;
2✔
679
    ColKey str_col_ndx = table1->add_column(type_String, "str_col", is_nullable);
2✔
680
    ColKey bool_col_ndx = table1->add_column(type_Bool, "bool_col", is_nullable);
2✔
681
    ColKey int_col_ndx = table1->add_column(type_Int, "int_col", is_nullable);
2✔
682
    ColKey timestamp_col_ndx = table1->add_column(type_Timestamp, "timestamp_col", is_nullable);
2✔
683

684
    // add one row, populated manually
685
    auto obj = table1->create_object();
2✔
686
    obj.set(str_col_ndx, "Hello");
2✔
687
    obj.set(bool_col_ndx, false);
2✔
688
    obj.set(int_col_ndx, 1);
2✔
689
    obj.set(timestamp_col_ndx, Timestamp{1, 1});
2✔
690
    // add one row with default null values
691
    table1->create_object();
2✔
692

693
    std::stringstream ss;
2✔
694
    table1->to_json(ss);
2✔
695
    CHECK(json_test(ss.str(), "expected_json_nulls", generate_all));
2✔
696
}
2✔
697

698
TEST(Json_Schema)
699
{
2✔
700
    Group group;
2✔
701

702
    TableRef persons = group.add_table("person");
2✔
703
    TableRef dogs = group.add_table("dog", Table::Type::Embedded);
2✔
704

705
    constexpr bool is_nullable = true;
2✔
706
    persons->add_column(type_String, "name");
2✔
707
    persons->add_column(type_Bool, "isMarried");
2✔
708
    persons->add_column(type_Int, "age", is_nullable);
2✔
709
    persons->add_column_list(type_Timestamp, "dates");
2✔
710
    persons->add_column_list(*dogs, "pet");
2✔
711
    persons->add_column_dictionary(type_Mixed, "dictionary_pet");
2✔
712
    dogs->add_column(type_String, "name");
2✔
713

714
    std::stringstream ss;
2✔
715
    group.schema_to_json(ss);
2✔
716
    const std::string json = ss.str();
2✔
717
    std::string expected =
2✔
718
        "[\n"
2✔
719
        "{\"name\":\"person\",\"tableType\":\"TopLevel\",\"properties\":["
2✔
720
        "{\"name\":\"name\",\"type\":\"string\"},"
2✔
721
        "{\"name\":\"isMarried\",\"type\":\"bool\"},"
2✔
722
        "{\"name\":\"age\",\"type\":\"int\",\"isOptional\":true},"
2✔
723
        "{\"name\":\"dates\",\"type\":\"timestamp\",\"isArray\":true},"
2✔
724
        "{\"name\":\"pet\",\"type\":\"object\",\"objectType\":\"dog\",\"isArray\":true},"
2✔
725
        "{\"name\":\"dictionary_pet\",\"type\":\"mixed\",\"isMap\":true,\"keyType\":\"string\",\"isOptional\":true}"
2✔
726
        "]},\n"
2✔
727
        "{\"name\":\"dog\",\"tableType\":\"Embedded\",\"properties\":[{\"name\":\"name\",\"type\":\"string\"}]}\n"
2✔
728
        "]\n";
2✔
729
    CHECK_EQUAL(expected, json);
2✔
730
}
2✔
731

732

733
using namespace std::chrono;
734

735
TEST(Json_Timestamp)
736
{
2✔
737
    std::array<char, 32> buffer1{};
2✔
738
    std::array<char, 32> buffer2{};
2✔
739
    Timestamp(-63549305085, 0).to_string(buffer1);
2✔
740
    CHECK(strcmp(buffer1.data(), "-0044-03-15 15:15:15") == 0);
2✔
741
    Timestamp(0, 0).to_string(buffer1);
2✔
742
    CHECK(strcmp(buffer1.data(), "1970-01-01 00:00:00") == 0);
2✔
743
    Timestamp(-1, 0).to_string(buffer1);
2✔
744
    CHECK(strcmp(buffer1.data(), "1969-12-31 23:59:59") == 0);
2✔
745
    Timestamp(-1, -100000000).to_string(buffer1);
2✔
746
    CHECK(strcmp(buffer1.data(), "1969-12-31 23:59:58.900000000") == 0);
2✔
747

748
    // Compare our own to_string with standard implementation
749
    // for years 1900 to 2050
750
    struct tm buf {};
2✔
751
    buf.tm_year = 1900 - 1900;
2✔
752
    buf.tm_mday = 1;
2✔
753
    auto start = mktime(&buf);
2✔
754
    buf.tm_year = 2050 - 1900;
2✔
755
    auto end = mktime(&buf);
2✔
756
    constexpr int64_t seconds_in_a_day = 24 * 60 * 60;
2✔
757

758
    for (auto d = start; d < end; d += (seconds_in_a_day - 1)) {
109,578✔
759
        Timestamp t(d, 0);
109,576✔
760
        t.to_string(buffer1);
109,576✔
761
        auto seconds = time_t(t.get_seconds());
109,576✔
762
#ifdef _MSC_VER
763
        gmtime_s(&buf, &seconds);
764
#else
765
        gmtime_r(&seconds, &buf);
109,576✔
766
#endif
109,576✔
767
        strftime(buffer2.data(), sizeof(buffer2), "%Y-%m-%d %H:%M:%S", &buf);
109,576✔
768
        CHECK(strcmp(buffer1.data(), buffer2.data()) == 0);
109,576✔
769
    }
109,576✔
770
    /*
771
    auto t1 = steady_clock::now();
772
    for (auto d = 0; d < 10000; d++) {
773
        Timestamp(d * seconds_in_a_day, 0).to_string(buffer1);
774
    }
775
    auto t2 = steady_clock::now();
776
    std::cout << "   time_to_string: " << duration_cast<microseconds>(t2 - t1).count() << " us" << std::endl;
777

778
    t1 = steady_clock::now();
779
    for (auto d = 0; d < 10000; d++) {
780
        auto seconds = time_t(d);
781
        gmtime_r(&seconds, &buf);
782
        // strftime(buffer2, sizeof(buffer2), "%Y-%m-%d %H:%M:%S", &buf);
783
    }
784
    t2 = steady_clock::now();
785
    std::cout << "   gm_time: " << duration_cast<microseconds>(t2 - t1).count() << " us" << std::endl;
786
    */
787
}
2✔
788

789
TEST(Bson_bson)
790
{
2✔
791
    bson_t bs[1];
2✔
792
    bson_t child[1];
2✔
793
    bson_init(bs);
2✔
794
    BSON_APPEND_ARRAY_BEGIN(bs, "Hello", child);
2✔
795
    BSON_APPEND_UTF8(child, "0", "awesome");
2✔
796
    BSON_APPEND_DOUBLE(child, "1", 5.125);
2✔
797
    BSON_APPEND_INT32(child, "2", 1986);
2✔
798
    bson_append_array_end(bs, child);
2✔
799
    BSON_APPEND_ARRAY_BEGIN(bs, "World", child);
2✔
800
    BSON_APPEND_UTF8(child, "0", "pink");
2✔
801
    bson_append_array_end(bs, child);
2✔
802

803
    size_t len;
2✔
804
    char* str = bson_as_canonical_extended_json(bs, &len);
2✔
805
    // std::cout << str << std::endl;
806
    CHECK(strstr(str, "awesome") != nullptr);
2✔
807
    bson_free(str);
2✔
808
    bson_destroy(bs);
2✔
809
}
2✔
810

811
} // anonymous namespace
812

813
#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