File: test_json_pointer.cpp

package info (click to toggle)
valijson 1.0.3%2Brepack-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 2,756 kB
  • sloc: cpp: 19,769; sh: 134; makefile: 24
file content (309 lines) | stat: -rw-r--r-- 12,373 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#include <memory>

#include <gtest/gtest.h>

#include <valijson/internal/json_pointer.hpp>

#include <valijson/adapters/rapidjson_adapter.hpp>

using valijson::adapters::RapidJsonAdapter;
using valijson::internal::json_pointer::resolveJsonPointer;

typedef rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> RapidJsonCrtAllocator;

class TestJsonPointer : public testing::Test
{

};

struct JsonPointerTestCase
{
    /// Description of test case
    std::string description;

    /// Document to traverse when resolving the JSON Pointer
    rapidjson::Value value;

    /// JSON Pointer that should guide traversal of the document
    std::string jsonPointer;

    /// Optional reference to the expected result from the original document
    rapidjson::Value *expectedValue;
};

std::vector<std::shared_ptr<JsonPointerTestCase> >
        testCasesForSingleLevelObjectPointers(
                RapidJsonCrtAllocator &allocator)
{
    typedef std::shared_ptr<JsonPointerTestCase> TestCase;

    std::vector<TestCase> testCases;

    TestCase testCase = std::make_shared<JsonPointerTestCase>();
    testCase->description = "Resolving '#' should cause an exception to be thrown";
    testCase->value.SetNull();
    testCase->jsonPointer = "#";
    testCase->expectedValue = nullptr;
    testCases.push_back(testCase);

    testCase = std::make_shared<JsonPointerTestCase>();
    testCase->description = "Resolving an empty string should return the root node";
    testCase->value.SetNull();
    testCase->jsonPointer = "";
    testCase->expectedValue = &testCase->value;
    testCases.push_back(testCase);

    testCase = std::make_shared<JsonPointerTestCase>();
    testCase->description = "Resolving '/' should return the root node";
    testCase->value.SetNull();
    testCase->jsonPointer = "/";
    testCase->expectedValue = &testCase->value;
    testCases.push_back(testCase);

    testCase = std::make_shared<JsonPointerTestCase>();
    testCase->description = "Resolving '//' should return the root node";
    testCase->value.SetNull();
    testCase->jsonPointer = "//";
    testCase->expectedValue = &testCase->value;
    testCases.push_back(testCase);

    testCase = std::make_shared<JsonPointerTestCase>();
    testCase->description = "Resolve '/test' in object containing one member named 'test'";
    testCase->value.SetObject();
    testCase->value.AddMember("test", "test", allocator);
    testCase->jsonPointer = "/test";
    testCase->expectedValue = &testCase->value.FindMember("test")->value;
    testCases.push_back(testCase);

    testCase = std::make_shared<JsonPointerTestCase>();
    testCase->description = "Resolve '/test/' in object containing one member named 'test'";
    testCase->value.SetObject();
    testCase->value.AddMember("test", "test", allocator);
    testCase->jsonPointer = "/test/";
    testCase->expectedValue = &testCase->value.FindMember("test")->value;
    testCases.push_back(testCase);

    testCase = std::make_shared<JsonPointerTestCase>();
    testCase->description = "Resolve '//test//' in object containing one member named 'test'";
    testCase->value.SetObject();
    testCase->value.AddMember("test", "test", allocator);
    testCase->jsonPointer = "//test//";
    testCase->expectedValue = &testCase->value.FindMember("test")->value;
    testCases.push_back(testCase);

    testCase = std::make_shared<JsonPointerTestCase>();
    testCase->description = "Resolve '/missing' in object containing one member name 'test'";
    testCase->value.SetObject();
    testCase->value.AddMember("test", "test", allocator);
    testCase->jsonPointer = "/missing";
    testCase->expectedValue = nullptr;
    testCases.push_back(testCase);

    {
        rapidjson::Value nonemptyString;
        nonemptyString.SetString("hello, world");

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolve '/value/foo' fails because 'value' is not an object (but a non empty string)";
        testCase->value.SetObject();
        testCase->value.AddMember("value", nonemptyString, allocator);
        testCase->jsonPointer = "/value/bar";
        testCase->expectedValue = &testCase->value;
        testCase->expectedValue = nullptr;
        testCases.push_back(testCase);
    }

    {
        rapidjson::Value emptyString;
        emptyString.SetString("");

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolve '/empty/after_empty' fails because 'empty' is an empty string";
        testCase->value.SetObject();
        testCase->value.AddMember("empty", emptyString, allocator);
        testCase->jsonPointer = "/empty/after_empty";
        testCase->expectedValue = nullptr;
        testCases.push_back(testCase);
    }

    {
        rapidjson::Value testArray;
        testArray.SetArray();
        testArray.PushBack("test0", allocator);
        testArray.PushBack("test1", allocator);
        testArray.PushBack("test2", allocator);

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolve '/test/0' in object containing one member containing an array with 3 elements";
        testCase->value.SetObject();
        testCase->value.AddMember("test", testArray, allocator);
        testCase->jsonPointer = "/test/0";
        testCase->expectedValue = &testCase->value.FindMember("test")->value[rapidjson::SizeType(0)];
        testCases.push_back(testCase);
    }

    {
        rapidjson::Value testArray;
        testArray.SetArray();
        testArray.PushBack("test0", allocator);
        testArray.PushBack("test1", allocator);
        testArray.PushBack("test2", allocator);

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolve '/test/1' in object containing one member containing an array with 3 elements";
        testCase->value.SetObject();
        testCase->value.AddMember("test", testArray, allocator);
        testCase->jsonPointer = "/test/1";
        testCase->expectedValue = &testCase->value.FindMember("test")->value[rapidjson::SizeType(1)];
        testCases.push_back(testCase);
    }

    {
        rapidjson::Value testArray;
        testArray.SetArray();
        testArray.PushBack("test0", allocator);
        testArray.PushBack("test1", allocator);
        testArray.PushBack("test2", allocator);

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolve '/test/2' in object containing one member containing an array with 3 elements";
        testCase->value.SetObject();
        testCase->value.AddMember("test", testArray, allocator);
        testCase->jsonPointer = "/test/2";
        testCase->expectedValue = &testCase->value.FindMember("test")->value[rapidjson::SizeType(2)];
        testCases.push_back(testCase);
    }

    {
        rapidjson::Value testArray;
        testArray.SetArray();
        testArray.PushBack("test0", allocator);
        testArray.PushBack("test1", allocator);
        testArray.PushBack("test2", allocator);

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolving '/test/3' in object containing one member containing "
                "an array with 3 elements should throw an exception";
        testCase->value.SetObject();
        testCase->value.AddMember("test", testArray, allocator);
        testCase->jsonPointer = "/test/3";
        testCase->expectedValue = nullptr;
        testCases.push_back(testCase);
    }

    //
    // Allow the "-" character is not useful within the context of this library,
    // there is an explicit check for it, so that a custom error message can
    // be included in the exception that is thrown.
    //
    // From the JSON Pointer specification (RFC 6901, April 2013):
    //
    //    Note that the use of the "-" character to index an array will always
    //    result in such an error condition because by definition it refers to
    //    a nonexistent array element.  Thus, applications of JSON Pointer need
    //    to specify how that character is to be handled, if it is to be
    //    useful.
    //

    {
        rapidjson::Value testArray;
        testArray.SetArray();
        testArray.PushBack("test0", allocator);
        testArray.PushBack("test1", allocator);
        testArray.PushBack("test2", allocator);

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolving '/test/-' in object containing one member containing "
                "an array with 3 elements should throw an exception";
        testCase->value.SetNull();
        testCase->jsonPointer = "/test/-";
        testCase->expectedValue = nullptr;
        testCases.push_back(testCase);
    }

    //
    // The following tests ensure that escape sequences are handled correctly.
    //
    // From the JSON Pointer specification (RFC 6901, April 2013):
    //
    //    Evaluation of each reference token begins by decoding any escaped
    //    character sequence.  This is performed by first transforming any
    //    occurrence of the sequence '~1' to '/', and then transforming any
    //    occurrence of the sequence '~0' to '~'.  By performing the
    //    substitutions in this order, an implementation avoids the error of
    //    turning '~01' first into '~1' and then into '/', which would be
    //    incorrect (the string '~01' correctly becomes '~1' after
    //    transformation).
    //

    {
        rapidjson::Value value;
        value.SetDouble(10.);

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolving '/hello~1world' in object containing one member named "
                "'hello/world' should return the associated value";
        testCase->value.SetObject();
        testCase->value.AddMember("hello/world", value, allocator);
        testCase->jsonPointer = "/hello~1world";
        testCase->expectedValue = &testCase->value.FindMember("hello/world")->value;
        testCases.push_back(testCase);
    }

    {
        rapidjson::Value value;
        value.SetDouble(10.);

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolving '/hello~0world' in object containing one member named "
                "'hello~world' should return the associated value";
        testCase->value.SetObject();
        testCase->value.AddMember("hello~world", value, allocator);
        testCase->jsonPointer = "/hello~0world";
        testCase->expectedValue = &testCase->value.FindMember("hello~world")->value;
        testCases.push_back(testCase);
    }

    {
        rapidjson::Value value;
        value.SetDouble(10.);

        testCase = std::make_shared<JsonPointerTestCase>();
        testCase->description = "Resolving '/hello~01world' in object containing one member named "
                "'hello~1world' should return the associated value";
        testCase->value.SetObject();
        testCase->value.AddMember("hello~1world", value, allocator);
        testCase->jsonPointer = "/hello~01world";
        testCase->expectedValue = &testCase->value.FindMember("hello~1world")->value;
        testCases.push_back(testCase);
    }

    return testCases;
}

TEST_F(TestJsonPointer, JsonPointerTestCases)
{
    typedef std::vector<std::shared_ptr<JsonPointerTestCase> > TestCases;

    // Ensure memory used for test cases is freed when test function completes
    rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> allocator;

    TestCases testCases = testCasesForSingleLevelObjectPointers(allocator);

    for (const auto & testCase : testCases) {
        const std::string &jsonPointer = testCase->jsonPointer;
        const RapidJsonAdapter valueAdapter(testCase->value);
        if (testCase->expectedValue) {
            const RapidJsonAdapter expectedAdapter(*(testCase->expectedValue));
            const RapidJsonAdapter actualAdapter = resolveJsonPointer(valueAdapter, jsonPointer);
            EXPECT_TRUE(actualAdapter.equalTo(expectedAdapter, true)) << testCase->description;
        } else {
            // Since the tests with throwing disabled will abort, we can't
            // do anything here.

#if VALIJSON_USE_EXCEPTIONS
            EXPECT_THROW(resolveJsonPointer(valueAdapter, jsonPointer), std::runtime_error) << testCase->description;
#endif
        }
    }
}