VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstRTJson.cpp@ 86728

Last change on this file since 86728 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: tstRTJson.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * IPRT Testcase - JSON API.
4 */
5
6/*
7 * Copyright (C) 2016-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/json.h>
32
33#include <iprt/err.h>
34#include <iprt/string.h>
35#include <iprt/test.h>
36
37
38/*********************************************************************************************************************************
39* Global Variables *
40*********************************************************************************************************************************/
41static const char g_szJson[] =
42 "{\n"
43 " \"integer\": 100,\n"
44 " \"number\": 22.22,\n"
45 " \"string\": \"test\",\n"
46 " \"array\": [1, 2, 3, 4, 5, \"6\"],\n"
47 " \"subobject\":\n"
48 " {\n"
49 " \"false\": false,\n"
50 " \"true\": true,\n"
51 " \"null\": null\n"
52 " }\n"
53 "}\n";
54
55/**
56 * Some basic tests to detect malformed JSON.
57 */
58static void tstBasic(RTTEST hTest)
59{
60 RTTestSub(hTest, "Basic valid/malformed tests");
61 static struct
62 {
63 const char *pszJson;
64 int iRcResult;
65 } const aTests[] =
66 {
67 { "", VERR_JSON_MALFORMED },
68 { ",", VERR_JSON_MALFORMED },
69 { ":", VERR_JSON_MALFORMED },
70 { " \n\t{", VERR_JSON_MALFORMED },
71 { "}", VERR_JSON_MALFORMED },
72 { "[", VERR_JSON_MALFORMED },
73 { "]", VERR_JSON_MALFORMED },
74 { "[ \"test\" : ", VERR_JSON_MALFORMED },
75 { "null", VINF_SUCCESS },
76 { "true", VINF_SUCCESS },
77 { "false", VINF_SUCCESS },
78 { "100", VINF_SUCCESS },
79 { "\"test\"", VINF_SUCCESS },
80 { "{ }", VINF_SUCCESS },
81 { "[ ]", VINF_SUCCESS },
82 { "[ 100, 200 ]", VINF_SUCCESS },
83 { "{ \"1\": 1 }", VINF_SUCCESS },
84 { "{ \"1\": 1, \"2\": 2 }", VINF_SUCCESS },
85 { "20", VINF_SUCCESS },
86 { "-20", VINF_SUCCESS },
87 { "{\"positive\":20}", VINF_SUCCESS },
88 { "{\"negative\":-20}", VINF_SUCCESS },
89 { "\"\\u0001\"", VINF_SUCCESS },
90 { "\"\\u000\"", VERR_JSON_INVALID_UTF16_ESCAPE_SEQUENCE },
91 { "\"\\u00\"", VERR_JSON_INVALID_UTF16_ESCAPE_SEQUENCE },
92 { "\"\\u0\"", VERR_JSON_INVALID_UTF16_ESCAPE_SEQUENCE },
93 { "\"\\u\"", VERR_JSON_INVALID_UTF16_ESCAPE_SEQUENCE },
94 { "\"\\uGhKl\"", VERR_JSON_INVALID_UTF16_ESCAPE_SEQUENCE },
95 { "\"\\u0000z\"", VERR_JSON_INVALID_CODEPOINT },
96 { "\"\\uffff\"", VERR_JSON_INVALID_CODEPOINT },
97 { "\"\\ufffe\"", VERR_JSON_INVALID_CODEPOINT },
98 { "\"\\ufffd\"", VINF_SUCCESS},
99 { "\"\\ufffd1\"", VINF_SUCCESS},
100 { "\"\\ufffd12\"", VINF_SUCCESS},
101 { "\"\\uD801\\udC37\\ud852\\uDf62\"", VINF_SUCCESS }, /* U+10437 U+24B62 */
102 { "\"\\uD801 \\udC37\"", VERR_JSON_MISSING_SURROGATE_PAIR },
103 { "\"\\uD801udC37\"", VERR_JSON_MISSING_SURROGATE_PAIR },
104 { "\"\\uD801\"", VERR_JSON_MISSING_SURROGATE_PAIR },
105 { "\"\\uD801\\\"", VERR_JSON_MISSING_SURROGATE_PAIR },
106 { "\"\\uD801\\u\"", VERR_JSON_INVALID_UTF16_ESCAPE_SEQUENCE },
107 { "\"\\uD801\\ud\"", VERR_JSON_INVALID_UTF16_ESCAPE_SEQUENCE },
108 { "\"\\uD801\\udc\"", VERR_JSON_INVALID_UTF16_ESCAPE_SEQUENCE },
109 { "\"\\uD801\\udc3\"", VERR_JSON_INVALID_UTF16_ESCAPE_SEQUENCE },
110 { "\"\\uD801\\uDc37\"", VINF_SUCCESS},
111 { "\"\\uDbff\\uDfff\"", VINF_SUCCESS},
112 { "\"\\t\\n\\b\\f\\r\\\\\\/\"", VINF_SUCCESS},
113 };
114 for (unsigned iTest = 0; iTest < RT_ELEMENTS(aTests); iTest++)
115 {
116 RTERRINFOSTATIC ErrInfo;
117 RTJSONVAL hJsonVal = NIL_RTJSONVAL;
118 int rc = RTJsonParseFromString(&hJsonVal, aTests[iTest].pszJson, RTErrInfoInitStatic(&ErrInfo));
119 if (rc != aTests[iTest].iRcResult)
120 {
121 if (RTErrInfoIsSet(&ErrInfo.Core))
122 RTTestFailed(hTest, "RTJsonParseFromString() for \"%s\" failed, expected %Rrc got %Rrc\n%s",
123 aTests[iTest].pszJson, aTests[iTest].iRcResult, rc, ErrInfo.Core.pszMsg);
124 else
125 RTTestFailed(hTest, "RTJsonParseFromString() for \"%s\" failed, expected %Rrc got %Rrc",
126 aTests[iTest].pszJson, aTests[iTest].iRcResult, rc);
127 }
128 else if (rc == VERR_JSON_MALFORMED && !RTErrInfoIsSet(&ErrInfo.Core))
129 RTTestFailed(hTest, "RTJsonParseFromString() did not return error info for \"%s\" failed", aTests[iTest].pszJson);
130 if (RT_SUCCESS(rc))
131 {
132 if (hJsonVal != NIL_RTJSONVAL)
133 RTJsonValueRelease(hJsonVal);
134 else
135 RTTestFailed(hTest, "RTJsonParseFromString() returned success but no value\n");
136 }
137 else if (hJsonVal != NIL_RTJSONVAL)
138 RTTestFailed(hTest, "RTJsonParseFromString() failed but a JSON value was returned\n");
139 }
140}
141
142/**
143 * Checks that methods not indended for the given type return the correct error.
144 */
145static void tstCorrectnessRcForInvalidType(RTTEST hTest, RTJSONVAL hJsonVal, RTJSONVALTYPE enmType)
146{
147 bool fSavedMayPanic = RTAssertSetMayPanic(false);
148 bool fSavedQuiet = RTAssertSetQuiet(true);
149
150 if ( enmType != RTJSONVALTYPE_OBJECT
151 && enmType != RTJSONVALTYPE_ARRAY)
152 {
153 /* The iterator API should return errors. */
154 RTJSONIT hJsonIt = NIL_RTJSONIT;
155 RTTEST_CHECK_RC(hTest, RTJsonIteratorBegin(hJsonVal, &hJsonIt), VERR_JSON_VALUE_INVALID_TYPE);
156 }
157
158 if (enmType != RTJSONVALTYPE_ARRAY)
159 {
160 /* The Array access methods should return errors. */
161 uint32_t cItems = 0;
162 RTJSONVAL hJsonValItem = NIL_RTJSONVAL;
163 RTTEST_CHECK(hTest, RTJsonValueGetArraySize(hJsonVal) == 0);
164 RTTEST_CHECK_RC(hTest, RTJsonValueQueryArraySize(hJsonVal, &cItems), VERR_JSON_VALUE_INVALID_TYPE);
165 RTTEST_CHECK_RC(hTest, RTJsonValueQueryByIndex(hJsonVal, 0, &hJsonValItem), VERR_JSON_VALUE_INVALID_TYPE);
166 }
167
168 if (enmType != RTJSONVALTYPE_OBJECT)
169 {
170 /* The object access methods should return errors. */
171 RTJSONVAL hJsonValMember = NIL_RTJSONVAL;
172 RTTEST_CHECK_RC(hTest, RTJsonValueQueryByName(hJsonVal, "test", &hJsonValMember), VERR_JSON_VALUE_INVALID_TYPE);
173 }
174
175 if (enmType != RTJSONVALTYPE_INTEGER)
176 {
177 int64_t i64Num = 0;
178 RTTEST_CHECK_RC(hTest, RTJsonValueQueryInteger(hJsonVal, &i64Num), VERR_JSON_VALUE_INVALID_TYPE);
179 }
180
181 if (enmType != RTJSONVALTYPE_NUMBER)
182 {
183 double rdNum = 0.0;
184 RTTEST_CHECK_RC(hTest, RTJsonValueQueryNumber(hJsonVal, &rdNum), VERR_JSON_VALUE_INVALID_TYPE);
185 }
186
187 if (enmType != RTJSONVALTYPE_STRING)
188 {
189 const char *psz = NULL;
190 RTTEST_CHECK(hTest, RTJsonValueGetString(hJsonVal) == NULL);
191 RTTEST_CHECK_RC(hTest, RTJsonValueQueryString(hJsonVal, &psz), VERR_JSON_VALUE_INVALID_TYPE);
192 }
193
194 RTAssertSetMayPanic(fSavedMayPanic);
195 RTAssertSetQuiet(fSavedQuiet);
196}
197
198/**
199 * Tests the array accessors.
200 */
201static void tstArray(RTTEST hTest, RTJSONVAL hJsonVal)
202{
203 uint32_t cItems = 0;
204 RTTEST_CHECK(hTest, RTJsonValueGetArraySize(hJsonVal) == 6);
205 RTTEST_CHECK_RC_OK(hTest, RTJsonValueQueryArraySize(hJsonVal, &cItems));
206 RTTEST_CHECK(hTest, cItems == RTJsonValueGetArraySize(hJsonVal));
207
208 for (uint32_t i = 1; i <= 5; i++)
209 {
210 int64_t i64Num = 0;
211 RTJSONVAL hJsonValItem = NIL_RTJSONVAL;
212 RTTEST_CHECK_RC_OK_RETV(hTest, RTJsonValueQueryByIndex(hJsonVal, i - 1, &hJsonValItem));
213 RTTEST_CHECK(hTest, RTJsonValueGetType(hJsonValItem) == RTJSONVALTYPE_INTEGER);
214 RTTEST_CHECK_RC_OK_RETV(hTest, RTJsonValueQueryInteger(hJsonValItem, &i64Num));
215 RTTEST_CHECK(hTest, i64Num == (int64_t)i);
216 RTTEST_CHECK(hTest, RTJsonValueRelease(hJsonValItem) == 1);
217 }
218
219 /* Last should be string. */
220 const char *pszStr = NULL;
221 RTJSONVAL hJsonValItem = NIL_RTJSONVAL;
222 RTTEST_CHECK_RC_OK_RETV(hTest, RTJsonValueQueryByIndex(hJsonVal, 5, &hJsonValItem));
223 RTTEST_CHECK(hTest, RTJsonValueGetType(hJsonValItem) == RTJSONVALTYPE_STRING);
224 RTTEST_CHECK_RC_OK_RETV(hTest, RTJsonValueQueryString(hJsonValItem, &pszStr));
225 RTTEST_CHECK(hTest, RTJsonValueGetString(hJsonValItem) == pszStr);
226 RTTEST_CHECK(hTest, strcmp(pszStr, "6") == 0);
227 RTTEST_CHECK(hTest, RTJsonValueRelease(hJsonValItem) == 1);
228}
229
230/**
231 * Tests the iterator API for the given JSON array or object value.
232 */
233static void tstIterator(RTTEST hTest, RTJSONVAL hJsonVal)
234{
235 RTJSONIT hJsonIt = NIL_RTJSONIT;
236 int rc = RTJsonIteratorBegin(hJsonVal, &hJsonIt);
237 RTTEST_CHECK(hTest, RT_SUCCESS(rc));
238 if (RT_SUCCESS(rc))
239 {
240 const char *pszName = NULL;
241 RTJSONVAL hJsonValMember = NIL_RTJSONVAL;
242 rc = RTJsonIteratorQueryValue(hJsonIt, &hJsonValMember, &pszName);
243 RTTEST_CHECK(hTest, RT_SUCCESS(rc));
244 RTTEST_CHECK(hTest, pszName != NULL);
245 RTTEST_CHECK(hTest, hJsonValMember != NIL_RTJSONVAL);
246 while (RT_SUCCESS(rc))
247 {
248 RTJSONVALTYPE enmTypeMember = RTJsonValueGetType(hJsonValMember);
249 tstCorrectnessRcForInvalidType(hTest, hJsonValMember, enmTypeMember);
250
251 switch (enmTypeMember)
252 {
253 case RTJSONVALTYPE_OBJECT:
254 RTTEST_CHECK(hTest, strcmp(pszName, "subobject") == 0);
255 tstIterator(hTest, hJsonValMember);
256 break;
257 case RTJSONVALTYPE_ARRAY:
258 RTTEST_CHECK(hTest, strcmp(pszName, "array") == 0);
259 tstArray(hTest, hJsonValMember);
260 break;
261 case RTJSONVALTYPE_STRING:
262 {
263 RTTEST_CHECK(hTest, strcmp(pszName, "string") == 0);
264 const char *pszStr = NULL;
265 RTTEST_CHECK_RC_OK(hTest, RTJsonValueQueryString(hJsonValMember, &pszStr));
266 RTTEST_CHECK(hTest, strcmp(pszStr, "test") == 0);
267 break;
268 }
269 case RTJSONVALTYPE_INTEGER:
270 {
271 RTTEST_CHECK(hTest, strcmp(pszName, "integer") == 0);
272 int64_t i64Num = 0;
273 RTTEST_CHECK_RC_OK(hTest, RTJsonValueQueryInteger(hJsonValMember, &i64Num));
274 RTTEST_CHECK(hTest, i64Num == 100);
275 break;
276 }
277 case RTJSONVALTYPE_NUMBER:
278 {
279 RTTEST_CHECK(hTest, strcmp(pszName, "number") == 0);
280 double rdNum = 0.0;
281 RTTEST_CHECK_RC_OK(hTest, RTJsonValueQueryNumber(hJsonValMember, &rdNum));
282 double const rdExpect = 22.22;
283 RTTEST_CHECK(hTest, rdNum == rdExpect);
284 break;
285 }
286 case RTJSONVALTYPE_NULL:
287 RTTEST_CHECK(hTest, strcmp(pszName, "null") == 0);
288 break;
289 case RTJSONVALTYPE_TRUE:
290 RTTEST_CHECK(hTest, strcmp(pszName, "true") == 0);
291 break;
292 case RTJSONVALTYPE_FALSE:
293 RTTEST_CHECK(hTest, strcmp(pszName, "false") == 0);
294 break;
295 default:
296 RTTestFailed(hTest, "Invalid JSON value type %u returned\n", enmTypeMember);
297 }
298
299 RTTEST_CHECK(hTest, RTJsonValueRelease(hJsonValMember) == 1);
300 rc = RTJsonIteratorNext(hJsonIt);
301 RTTEST_CHECK(hTest, rc == VINF_SUCCESS || rc == VERR_JSON_ITERATOR_END);
302 if (RT_SUCCESS(rc))
303 RTTEST_CHECK_RC_OK(hTest, RTJsonIteratorQueryValue(hJsonIt, &hJsonValMember, &pszName));
304 }
305 RTJsonIteratorFree(hJsonIt);
306 }
307}
308
309/**
310 * Test that the parser returns the correct values for a valid JSON.
311 */
312static void tstCorrectness(RTTEST hTest)
313{
314 RTTestSub(hTest, "Correctness");
315
316 RTJSONVAL hJsonVal = NIL_RTJSONVAL;
317 RTTEST_CHECK_RC_OK_RETV(hTest, RTJsonParseFromString(&hJsonVal, g_szJson, NULL));
318
319 if (hJsonVal != NIL_RTJSONVAL)
320 {
321 RTJSONVALTYPE enmType = RTJsonValueGetType(hJsonVal);
322 if (enmType == RTJSONVALTYPE_OBJECT)
323 {
324 /* Excercise the other non object APIs to return VERR_JSON_VALUE_INVALID_TYPE. */
325 tstCorrectnessRcForInvalidType(hTest, hJsonVal, enmType);
326 tstIterator(hTest, hJsonVal);
327 }
328 else
329 RTTestFailed(hTest, "RTJsonParseFromString() returned an invalid JSON value, expected OBJECT got %u\n", enmType);
330 RTTEST_CHECK(hTest, RTJsonValueRelease(hJsonVal) == 0);
331 }
332 else
333 RTTestFailed(hTest, "RTJsonParseFromString() returned success but no value\n");
334}
335
336int main(int argc, char **argv)
337{
338 RTTEST hTest;
339 int rc = RTTestInitExAndCreate(argc, &argv, 0, "tstRTJson", &hTest);
340 if (rc)
341 return rc;
342 RTTestBanner(hTest);
343
344 tstBasic(hTest);
345 tstCorrectness(hTest);
346 for (int i = 1; i < argc; i++)
347 {
348 RTTestSubF(hTest, "file %Rbn", argv[i]);
349 RTERRINFOSTATIC ErrInfo;
350 RTJSONVAL hFileValue = NIL_RTJSONVAL;
351 rc = RTJsonParseFromFile(&hFileValue, argv[i], RTErrInfoInitStatic(&ErrInfo));
352 if (RT_SUCCESS(rc))
353 RTJsonValueRelease(hFileValue);
354 else if (RTErrInfoIsSet(&ErrInfo.Core))
355 RTTestFailed(hTest, "%Rrc - %s", rc, ErrInfo.Core.pszMsg);
356 else
357 RTTestFailed(hTest, "%Rrc", rc);
358 }
359
360 /*
361 * Summary.
362 */
363 return RTTestSummaryAndDestroy(hTest);
364}
365
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette