]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // ---------------------------------------------------------------------------- |
2 | // Copyright (C) 2002-2006 Marcin Kalicinski | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. | |
5 | // (See accompanying file LICENSE_1_0.txt or copy at | |
6 | // http://www.boost.org/LICENSE_1_0.txt) | |
7 | // | |
8 | // For more information, see www.boost.org | |
9 | // ---------------------------------------------------------------------------- | |
10 | ||
11 | #include "test_utils.hpp" | |
12 | #include <boost/property_tree/json_parser.hpp> | |
13 | ||
14 | /////////////////////////////////////////////////////////////////////////////// | |
15 | // Test data | |
16 | ||
17 | const char *ok_data_1 = | |
18 | "{}"; | |
19 | ||
20 | const char *ok_data_2 = | |
21 | " \t {\n" | |
22 | " \t \"name 0\" : \"value\", \t \n" | |
23 | " \t \"name 1\" : \"\", \n" | |
24 | " \t \"name 2\" : true, \n" | |
25 | " \t \"name 3\" : false, \n" | |
26 | " \t \"name 4\" : null, \n" | |
27 | " \t \"name 5\" : 0, \n" | |
28 | " \t \"name 6\" : -5, \n" | |
29 | " \t \"name 7\" : 1.1, \n" | |
30 | " \t \"name 8\" : -956.45e+4, \n" | |
31 | " \t \"name 8\" : 5956.45E-11, \n" | |
32 | " \t \"name 9\" : [1,2,3,4], \n" | |
33 | " \t \"name 10\" : {\"a\":\"b\"} \n" | |
34 | " \t } \n"; | |
35 | ||
36 | const char *ok_data_3 = | |
37 | "{\"a\":{\"b\":\"c\"}}"; | |
38 | ||
39 | const char *ok_data_4 = | |
40 | "{\"a\":[{\"b\":\"c\"},{\"d\":\"e\"},1,2,3,4],\"f\":null}"; | |
41 | ||
42 | const char *ok_data_6 = | |
43 | "{\"menu\": {\n" | |
44 | " \"header\": \"SVG Viewer\",\n" | |
45 | " \"items\": [\n" | |
46 | " {\"id\": \"Open\"},\n" | |
47 | " {\"id\": \"OpenNew\", \"label\": \"Open New\"},\n" | |
48 | " null,\n" | |
49 | " {\"id\": \"ZoomIn\", \"label\": \"Zoom In\"},\n" | |
50 | " {\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"},\n" | |
51 | " {\"id\": \"OriginalView\", \"label\": \"Original View\"},\n" | |
52 | " null,\n" | |
53 | " {\"id\": \"Quality\"},\n" | |
54 | " {\"id\": \"Pause\"},\n" | |
55 | " {\"id\": \"Mute\"},\n" | |
56 | " null,\n" | |
57 | " {\"id\": \"Find\", \"label\": \"Find...\"},\n" | |
58 | " {\"id\": \"FindAgain\", \"label\": \"Find Again\"},\n" | |
59 | " {\"id\": \"Copy\"},\n" | |
60 | " {\"id\": \"CopyAgain\", \"label\": \"Copy Again\"},\n" | |
61 | " {\"id\": \"CopySVG\", \"label\": \"Copy SVG\"},\n" | |
62 | " {\"id\": \"ViewSVG\", \"label\": \"View SVG\"},\n" | |
63 | " {\"id\": \"ViewSource\", \"label\": \"View Source\"},\n" | |
64 | " {\"id\": \"SaveAs\", \"label\": \"Save As\"},\n" | |
65 | " null,\n" | |
66 | " {\"id\": \"Help\"},\n" | |
67 | " {\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}\n" | |
68 | " ]\n" | |
69 | "}}\n"; | |
70 | ||
71 | const char *ok_data_7 = | |
72 | "{\"web-app\": {\n" | |
73 | " \"servlet\": [ \n" | |
74 | " {\n" | |
75 | " \"servlet-name\": \"cofaxCDS\",\n" | |
76 | " \"servlet-class\": \"org.cofax.cds.CDSServlet\",\n" | |
77 | " \"init-param\": {\n" | |
78 | " \"configGlossary:installationAt\": \"Philadelphia, PA\",\n" | |
79 | " \"configGlossary:adminEmail\": \"ksm@pobox.com\",\n" | |
80 | " \"configGlossary:poweredBy\": \"Cofax\",\n" | |
81 | " \"configGlossary:poweredByIcon\": \"/images/cofax.gif\",\n" | |
82 | " \"configGlossary:staticPath\": \"/content/static\",\n" | |
83 | " \"templateProcessorClass\": \"org.cofax.WysiwygTemplate\",\n" | |
84 | " \"templateLoaderClass\": \"org.cofax.FilesTemplateLoader\",\n" | |
85 | " \"templatePath\": \"templates\",\n" | |
86 | " \"templateOverridePath\": \"\",\n" | |
87 | " \"defaultListTemplate\": \"listTemplate.htm\",\n" | |
88 | " \"defaultFileTemplate\": \"articleTemplate.htm\",\n" | |
89 | " \"useJSP\": false,\n" | |
90 | " \"jspListTemplate\": \"listTemplate.jsp\",\n" | |
91 | " \"jspFileTemplate\": \"articleTemplate.jsp\",\n" | |
92 | " \"cachePackageTagsTrack\": 200,\n" | |
93 | " \"cachePackageTagsStore\": 200,\n" | |
94 | " \"cachePackageTagsRefresh\": 60,\n" | |
95 | " \"cacheTemplatesTrack\": 100,\n" | |
96 | " \"cacheTemplatesStore\": 50,\n" | |
97 | " \"cacheTemplatesRefresh\": 15,\n" | |
98 | " \"cachePagesTrack\": 200,\n" | |
99 | " \"cachePagesStore\": 100,\n" | |
100 | " \"cachePagesRefresh\": 10,\n" | |
101 | " \"cachePagesDirtyRead\": 10,\n" | |
102 | " \"searchEngineListTemplate\": \"forSearchEnginesList.htm\",\n" | |
103 | " \"searchEngineFileTemplate\": \"forSearchEngines.htm\",\n" | |
104 | " \"searchEngineRobotsDb\": \"WEB-INF/robots.db\",\n" | |
105 | " \"useDataStore\": true,\n" | |
106 | " \"dataStoreClass\": \"org.cofax.SqlDataStore\",\n" | |
107 | " \"redirectionClass\": \"org.cofax.SqlRedirection\",\n" | |
108 | " \"dataStoreName\": \"cofax\",\n" | |
109 | " \"dataStoreDriver\": \"com.microsoft.jdbc.sqlserver.SQLServerDriver\",\n" | |
110 | " \"dataStoreUrl\": \"jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon\",\n" | |
111 | " \"dataStoreUser\": \"sa\",\n" | |
112 | " \"dataStorePassword\": \"dataStoreTestQuery\",\n" | |
113 | " \"dataStoreTestQuery\": \"SET NOCOUNT ON;select test='test';\",\n" | |
114 | " \"dataStoreLogFile\": \"/usr/local/tomcat/logs/datastore.log\",\n" | |
115 | " \"dataStoreInitConns\": 10,\n" | |
116 | " \"dataStoreMaxConns\": 100,\n" | |
117 | " \"dataStoreConnUsageLimit\": 100,\n" | |
118 | " \"dataStoreLogLevel\": \"debug\",\n" | |
119 | " \"maxUrlLength\": 500}},\n" | |
120 | " {\n" | |
121 | " \"servlet-name\": \"cofaxEmail\",\n" | |
122 | "\n \"servlet-class\": \"org.cofax.cds.EmailServlet\",\n" | |
123 | " \"init-param\": {\n" | |
124 | " \"mailHost\": \"mail1\",\n" | |
125 | " \"mailHostOverride\": \"mail2\"}},\n" | |
126 | " {\n" | |
127 | " \"servlet-name\": \"cofaxAdmin\",\n" | |
128 | " \"servlet-class\": \"org.cofax.cds.AdminServlet\"},\n" | |
129 | " \n" | |
130 | " {\n" | |
131 | " \"servlet-name\": \"fileServlet\",\n" | |
132 | " \"servlet-class\": \"org.cofax.cds.FileServlet\"},\n" | |
133 | " {\n" | |
134 | " \"servlet-name\": \"cofaxTools\",\n" | |
135 | " \"servlet-class\": \"org.cofax.cms.CofaxToolsServlet\",\n" | |
136 | " \"init-param\": {\n" | |
137 | " \"templatePath\": \"toolstemplates/\",\n" | |
138 | " \"log\": 1,\n" | |
139 | " \"logLocation\": \"/usr/local/tomcat/logs/CofaxTools.log\",\n" | |
140 | " \"logMaxSize\": \"\",\n" | |
141 | " \"dataLog\": 1,\n" | |
142 | " \"dataLogLocation\": \"/usr/local/tomcat/logs/dataLog.log\",\n" | |
143 | " \"dataLogMaxSize\": \"\",\n" | |
144 | " \"removePageCache\": \"/content/admin/remove?cache=pages&id=\",\n" | |
145 | " \"removeTemplateCache\": \"/content/admin/remove?cache=templates&id=\",\n" | |
146 | " \"fileTransferFolder\": \"/usr/local/tomcat/webapps/content/fileTransferFolder\",\n" | |
147 | " \"lookInContext\": 1,\n" | |
148 | " \"adminGroupID\": 4,\n" | |
149 | " \"betaServer\": true}}],\n" | |
150 | " \"servlet-mapping\": {\n" | |
151 | " \"cofaxCDS\": \"/\",\n" | |
152 | " \"cofaxEmail\": \"/cofaxutil/aemail/*\",\n" | |
153 | " \"cofaxAdmin\": \"/admin/*\",\n" | |
154 | " \"fileServlet\": \"/static/*\",\n" | |
155 | " \"cofaxTools\": \"/tools/*\"},\n" | |
156 | " \n" | |
157 | " \"taglib\": {\n" | |
158 | " \"taglib-uri\": \"cofax.tld\",\n" | |
159 | " \"taglib-location\": \"/WEB-INF/tlds/cofax.tld\"}}}\n"; | |
160 | ||
161 | const char *ok_data_8 = | |
162 | "{\"widget\": {\n" | |
163 | " \"debug\": \"on\",\n" | |
164 | " \"window\": {\n" | |
165 | " \"title\": \"Sample Konfabulator Widget\", \"name\": \"main_window\", \"width\": 500, \"height\": 500\n" | |
166 | " }, \"image\": { \n" | |
167 | " \"src\": \"Images/Sun.png\",\n" | |
168 | " \"name\": \"sun1\", \"hOffset\": 250, \"vOffset\": 250, \"alignment\": \"center\"\n" | |
169 | " }, \"text\": {\n" | |
170 | " \"data\": \"Click Here\",\n" | |
171 | " \"size\": 36,\n" | |
172 | " \"style\": \"bold\", \"name\": \"text1\", \"hOffset\": 250, \"vOffset\": 100, \"alignment\": \"center\",\n" | |
173 | " \"onMouseUp\": \"sun1.opacity = (sun1.opacity / 100) * 90;\"\n" | |
174 | " }\n" | |
175 | "}} \n"; | |
176 | ||
177 | const char *ok_data_9 = | |
178 | "{\"menu\": {\n" | |
179 | " \"id\": \"file\",\n" | |
180 | " \"value\": \"File:\",\n" | |
181 | " \"popup\": {\n" | |
182 | " \"menuitem\": [\n" | |
183 | " {\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"},\n" | |
184 | " {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"},\n" | |
185 | "\n {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}\n" | |
186 | " ]\n" | |
187 | " }\n" | |
188 | "}}\n"; | |
189 | ||
190 | const char *ok_data_10 = | |
191 | "{\n" | |
192 | " \"glossary\": {\n" | |
193 | " \"title\": \"example glossary\",\n" | |
194 | " \"GlossDiv\": {\n" | |
195 | " \"title\": \"S\",\n" | |
196 | " \"GlossList\": [{\n" | |
197 | " \"ID\": \"SGML\",\n" | |
198 | " \"SortAs\": \"SGML\",\n" | |
199 | " \"GlossTerm\": \"Standard Generalized Markup Language\",\n" | |
200 | " \"Acronym\": \"SGML\",\n" | |
201 | " \"Abbrev\": \"ISO 8879:1986\",\n" | |
202 | " \"GlossDef\": \n" | |
203 | "\"A meta-markup language, used to create markup languages such as DocBook.\",\n" | |
204 | " \"GlossSeeAlso\": [\"GML\", \"XML\", \"markup\"]\n" | |
205 | " }]\n" | |
206 | " }\n" | |
207 | " }\n" | |
208 | "}\n"; | |
209 | ||
210 | const char *ok_data_11 = | |
211 | "{\n" | |
212 | " \"data\" : [\n" | |
213 | " {\n" | |
214 | " \"First Name\" : \"Bob\",\n" | |
215 | " \"Last Name\" : \"Smith\",\n" | |
216 | " \"Email\" : \"bsmith@someurl.com\",\n" | |
217 | " \"Phone\" : \"(555) 555-1212\"\n" | |
218 | " },\n" | |
219 | " {\n" | |
220 | " \"First Name\" : \"Jan\",\n" | |
221 | " \"Last Name\" : \"Smith\",\n" | |
222 | " \"Email\" : \"jsmith@someurl.com\",\n" | |
223 | " \"Phone\" : \"(555) 555-3434\"\n" | |
224 | " },\n" | |
225 | " {\n" | |
226 | " \"First Name\" : \"Sue\",\n" | |
227 | " \"Last Name\" : \"Smith\",\n" | |
228 | " \"Email\" : \"ssmith@someurl.com\",\n" | |
229 | " \"Phone\" : \"(555) 555-5656\"\n" | |
230 | " }\n" | |
231 | " ]\n" | |
232 | "}\n"; | |
233 | ||
234 | const char *ok_data_12 = | |
235 | "{\" \\\" \\\\ \\b \\f \\n \\r \\t \" : \"\"}"; | |
236 | ||
237 | const char *error_data_1 = | |
238 | ""; // No root object | |
239 | ||
240 | const char *error_data_2 = | |
241 | "{\n\"a\":1\n"; // Unclosed object brace | |
242 | ||
243 | const char *error_data_3 = | |
244 | "{\n\"a\":\n[1,2,3,4\n}"; // Unclosed array brace | |
245 | ||
246 | const char *error_data_4 = | |
247 | "{\n\"a\"\n}"; // No object | |
248 | ||
249 | const char *bug_data_pr4387 = | |
250 | "[1, 2, 3]"; // Root array | |
251 | ||
252 | const char *bug_data_pr7180_1 = | |
253 | "{\n" | |
254 | " \"a\": [\n" | |
255 | " \"1\",\n" | |
256 | " \"2\"\n" | |
257 | " ]\n" | |
258 | "}\n"; | |
259 | const char *bug_data_pr7180_2 = | |
260 | "{\"a\":[\"1\",\"2\"]}\n"; | |
261 | ||
262 | struct ReadFunc | |
263 | { | |
264 | template<class Ptree> | |
265 | void operator()(const std::string &filename, Ptree &pt) const | |
266 | { | |
267 | boost::property_tree::read_json(filename, pt); | |
268 | } | |
269 | template <class Ptree> | |
270 | void operator()( | |
271 | std::basic_istream<typename Ptree::key_type::value_type>& is, | |
272 | Ptree& pt) const | |
273 | { | |
274 | boost::property_tree::read_json(is, pt); | |
275 | } | |
276 | }; | |
277 | ||
278 | struct WriteFunc | |
279 | { | |
280 | WriteFunc(bool pretty = false) : pretty(pretty) {} | |
281 | ||
282 | template<class Ptree> | |
283 | void operator()(const std::string &filename, const Ptree &pt) const | |
284 | { | |
285 | boost::property_tree::write_json(filename, pt, std::locale(), pretty); | |
286 | } | |
287 | ||
288 | template<class Ptree> | |
289 | void operator()( | |
290 | std::basic_ostream<typename Ptree::key_type::value_type>& os, | |
291 | Ptree& pt) const | |
292 | { | |
293 | boost::property_tree::write_json(os, pt, pretty); | |
294 | } | |
295 | ||
296 | bool pretty; | |
297 | }; | |
298 | ||
299 | template<class Ptree> | |
300 | void test_json_parser() | |
301 | { | |
302 | ||
303 | using namespace boost::property_tree; | |
304 | ||
305 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
306 | ( | |
307 | ReadFunc(), WriteFunc(false), ok_data_1, NULL, | |
308 | "testok1.json", NULL, "testok1out.json", 1, 0, 0 | |
309 | ); | |
310 | ||
311 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
312 | ( | |
313 | ReadFunc(), WriteFunc(false), ok_data_2, NULL, | |
314 | "testok2.json", NULL, "testok2out.json", 18, 50, 74 | |
315 | ); | |
316 | ||
317 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
318 | ( | |
319 | ReadFunc(), WriteFunc(false), ok_data_3, NULL, | |
320 | "testok3.json", NULL, "testok3out.json", 3, 1, 2 | |
321 | ); | |
322 | ||
323 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
324 | ( | |
325 | ReadFunc(), WriteFunc(false), ok_data_4, NULL, | |
326 | "testok4.json", NULL, "testok4out.json", 11, 10, 4 | |
327 | ); | |
328 | ||
329 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
330 | ( | |
331 | ReadFunc(), WriteFunc(false), ok_data_6, NULL, | |
332 | "testok6.json", NULL, "testok6out.json", 56, 265, 111 | |
333 | ); | |
334 | ||
335 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
336 | ( | |
337 | ReadFunc(), WriteFunc(false), ok_data_7, NULL, | |
338 | "testok7.json", NULL, "testok7out.json", 87, 1046, 1216 | |
339 | ); | |
340 | ||
341 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
342 | ( | |
343 | ReadFunc(), WriteFunc(false), ok_data_8, NULL, | |
344 | "testok8.json", NULL, "testok8out.json", 23, 149, 125 | |
345 | ); | |
346 | ||
347 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
348 | ( | |
349 | ReadFunc(), WriteFunc(false), ok_data_9, NULL, | |
350 | "testok9.json", NULL, "testok9out.json", 15, 54, 60 | |
351 | ); | |
352 | ||
353 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
354 | ( | |
355 | ReadFunc(), WriteFunc(false), ok_data_10, NULL, | |
356 | "testok10.json", NULL, "testok10out.json", 17, 162, 85 | |
357 | ); | |
358 | ||
359 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
360 | ( | |
361 | ReadFunc(), WriteFunc(false), ok_data_11, NULL, | |
362 | "testok11.json", NULL, "testok11out.json", 17, 120, 91 | |
363 | ); | |
364 | ||
365 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
366 | ( | |
367 | ReadFunc(), WriteFunc(false), ok_data_12, NULL, | |
368 | "testok12.json", NULL, "testok12out.json", 2, 0, 17 | |
369 | ); | |
370 | ||
371 | generic_parser_test_error<ptree, ReadFunc, WriteFunc, json_parser_error> | |
372 | ( | |
373 | ReadFunc(), WriteFunc(false), error_data_1, NULL, | |
374 | "testerr1.json", NULL, "testerr1out.json", 1 | |
375 | ); | |
376 | ||
377 | generic_parser_test_error<ptree, ReadFunc, WriteFunc, json_parser_error> | |
378 | ( | |
379 | ReadFunc(), WriteFunc(false), error_data_2, NULL, | |
380 | "testerr2.json", NULL, "testerr2out.json", 3 | |
381 | ); | |
382 | ||
383 | generic_parser_test_error<ptree, ReadFunc, WriteFunc, json_parser_error> | |
384 | ( | |
385 | ReadFunc(), WriteFunc(false), error_data_3, NULL, | |
386 | "testerr3.json", NULL, "testerr3out.json", 4 | |
387 | ); | |
388 | ||
389 | generic_parser_test_error<ptree, ReadFunc, WriteFunc, json_parser_error> | |
390 | ( | |
391 | ReadFunc(), WriteFunc(false), error_data_4, NULL, | |
392 | "testerr4.json", NULL, "testerr4out.json", 3 | |
393 | ); | |
394 | ||
395 | generic_parser_test_ok<Ptree, ReadFunc, WriteFunc> | |
396 | ( | |
397 | ReadFunc(), WriteFunc(false), bug_data_pr4387, NULL, | |
398 | "testpr4387.json", NULL, "testpr4387out.json", 4, 3, 0 | |
399 | ); | |
400 | ||
401 | check_exact_roundtrip<Ptree>(ReadFunc(), WriteFunc(true), | |
402 | bug_data_pr7180_1); | |
403 | check_exact_roundtrip<Ptree>(ReadFunc(), WriteFunc(false), | |
404 | bug_data_pr7180_2); | |
405 | ||
406 | } | |
407 | ||
408 | void test_escaping_utf8() | |
409 | { | |
410 | // This is cyrillic text encoded as UTF-8 | |
411 | std::string str = "\xD0\x9C\xD0\xB0\xD0\xBC\xD0\xB0 " | |
412 | "\xD0\xBC\xD1\x8B\xD0\xBB\xD0\xB0 \xD1\x80\xD0\xB0\xD0\xBC\xD1\x83"; | |
413 | // Should NOT escape UTF-8 | |
1e59de90 | 414 | BOOST_TEST(boost::property_tree::json_parser::create_escapes(str) == str); |
7c673cae FG |
415 | } |
416 | ||
417 | void test_escaping_wide() | |
418 | { | |
419 | // Should NOT escape characters within ASCII range. | |
420 | std::wstring str1 = L"I am wstring with ASCII"; | |
1e59de90 | 421 | BOOST_TEST(boost::property_tree::json_parser::create_escapes(str1) == str1); |
7c673cae FG |
422 | // Should escape characters outside ASCII range - this is NOT utf-8 |
423 | // This is cyrillic text | |
424 | std::wstring str2 = L"\u041C\u0430\u043C\u0430 " | |
425 | L"\u043C\u044B\u043B\u0430 \u0440\u0430\u043C\u0443"; | |
1e59de90 | 426 | BOOST_TEST(boost::property_tree::json_parser::create_escapes(str2) == |
7c673cae FG |
427 | L"\\u041C\\u0430\\u043C\\u0430 " |
428 | L"\\u043C\\u044B\\u043B\\u0430 \\u0440\\u0430\\u043C\\u0443"); | |
429 | } | |
430 | ||
1e59de90 | 431 | int main(int , char *[]) |
7c673cae FG |
432 | { |
433 | using namespace boost::property_tree; | |
434 | test_json_parser<ptree>(); | |
435 | test_json_parser<iptree>(); | |
436 | test_escaping_utf8(); | |
437 | #ifndef BOOST_NO_CWCHAR | |
438 | test_json_parser<wptree>(); | |
439 | test_json_parser<wiptree>(); | |
440 | test_escaping_wide(); | |
441 | #endif | |
1e59de90 | 442 | return boost::report_errors(); |
7c673cae | 443 | } |