testjsonparser.pp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. {
  2. This file is part of the Free Component Library
  3. JSON FPCUNit test for parser
  4. Copyright (c) 2007 by Michael Van Canneyt [email protected]
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$mode objfpc}
  12. {$h+}
  13. unit testjsonparser;
  14. interface
  15. uses
  16. Classes, SysUtils, fpcunit, testregistry,fpjson,
  17. jsonscanner,jsonParser,testjsondata;
  18. Const
  19. DefaultOpts = [joUTF8,joStrict];
  20. type
  21. { TTestParser }
  22. TTestParser = class(TTestJSON)
  23. private
  24. FOptions : TJSONOptions;
  25. procedure CallNoHandlerStream;
  26. procedure DoTestError(S: String; Options : TJSONOptions = DefaultOpts);
  27. procedure DoTestFloat(F: TJSONFloat); overload;
  28. procedure DoTestFloat(F: TJSONFloat; S: String); overload;
  29. procedure DoTestObject(S: String; const ElNames: array of String; DoJSONTest : Boolean = True);
  30. procedure DoTestString(S : String; AResult : String);
  31. procedure DoTestString(S : String);
  32. procedure DoTestArray(S: String; ACount: Integer; IgnoreJSON: Boolean=False);
  33. Procedure DoTestClass(S : String; AClass : TJSONDataClass);
  34. procedure CallNoHandler;
  35. procedure DoTrailingCommaErrorArray;
  36. procedure DoTrailingCommaErrorObject;
  37. Protected
  38. Procedure Setup; override;
  39. published
  40. procedure TestEmpty;
  41. procedure TestNull;
  42. procedure TestTrue;
  43. procedure TestFalse;
  44. procedure TestFloat;
  45. procedure TestInteger;
  46. procedure TestInt64;
  47. procedure TestString;
  48. procedure TestArray;
  49. procedure TestObject;
  50. procedure TestObjectError;
  51. procedure TestTrailingComma;
  52. procedure TestTrailingCommaErrorArray;
  53. procedure TestTrailingCommaErrorObject;
  54. procedure TestMixed;
  55. Procedure TestComment;
  56. procedure TestErrors;
  57. Procedure TestClasses;
  58. Procedure TestHandler;
  59. Procedure TestNoHandlerError;
  60. Procedure TestHandlerResult;
  61. Procedure TestHandlerResultStream;
  62. Procedure TestEmptyLine;
  63. procedure TestBug36037Part2;
  64. end;
  65. implementation
  66. procedure TTestParser.TestEmpty;
  67. Var
  68. P : TJSONParser;
  69. J : TJSONData;
  70. begin
  71. P:=TJSONParser.Create('',[joUTF8]);
  72. Try
  73. J:=P.Parse;
  74. If (J<>Nil) then
  75. Fail('Empty returns Nil');
  76. Finally
  77. FreeAndNil(J);
  78. FreeAndNil(P);
  79. end;
  80. end;
  81. procedure TTestParser.TestInteger;
  82. Var
  83. P : TJSONParser;
  84. J : TJSONData;
  85. begin
  86. P:=TJSONParser.Create('1',[joUTF8]);
  87. Try
  88. J:=P.Parse;
  89. If (J=Nil) then
  90. Fail('Parse of 1 fails');
  91. TestJSONType(J,jtNumber);
  92. TestAsInteger(J,1);
  93. Finally
  94. FreeAndNil(J);
  95. FreeAndNil(P);
  96. end;
  97. end;
  98. procedure TTestParser.TestInt64;
  99. Var
  100. P : TJSONParser;
  101. J : TJSONData;
  102. begin
  103. P:=TJSONParser.Create('123456789012345',[joUTF8]);
  104. Try
  105. J:=P.Parse;
  106. If (J=Nil) then
  107. Fail('Parse of 123456789012345 fails');
  108. TestJSONType(J,jtNumber);
  109. TestAsInt64(J,123456789012345);
  110. Finally
  111. FreeAndNil(J);
  112. FreeAndNil(P);
  113. end;
  114. end;
  115. procedure TTestParser.TestNull;
  116. Var
  117. P : TJSONParser;
  118. J : TJSONData;
  119. begin
  120. P:=TJSONParser.Create('null',[joUTF8]);
  121. Try
  122. J:=P.Parse;
  123. If (J=Nil) then
  124. Fail('Parse of null fails');
  125. TestJSONType(J,jtNull);
  126. Finally
  127. FreeAndNil(J);
  128. FreeAndNil(P);
  129. end;
  130. end;
  131. procedure TTestParser.TestTrue;
  132. Var
  133. P : TJSONParser;
  134. J : TJSONData;
  135. begin
  136. P:=TJSONParser.Create('true',[joUTF8]);
  137. Try
  138. J:=P.Parse;
  139. If (J=Nil) then
  140. Fail('Parse of True fails');
  141. TestJSONType(J,jtBoolean);
  142. TestAsBoolean(J,True);
  143. Finally
  144. FreeAndNil(J);
  145. FreeAndNil(P);
  146. end;
  147. end;
  148. procedure TTestParser.TestFalse;
  149. Var
  150. P : TJSONParser;
  151. J : TJSONData;
  152. begin
  153. P:=TJSONParser.Create('false',[joUTF8]);
  154. Try
  155. J:=P.Parse;
  156. If (J=Nil) then
  157. Fail('Parse of False fails');
  158. TestJSONType(J,jtBoolean);
  159. TestAsBoolean(J,False);
  160. Finally
  161. FreeAndNil(J);
  162. FreeAndNil(P);
  163. end;
  164. end;
  165. procedure TTestParser.TestFloat;
  166. begin
  167. DoTestFloat(1.2);
  168. DoTestFloat(-1.2);
  169. DoTestFloat(0);
  170. DoTestFloat(1.2e1);
  171. DoTestFloat(-1.2e1);
  172. DoTestFloat(0);
  173. DoTestFloat(1.2,'1.2');
  174. DoTestFloat(-1.2,'-1.2');
  175. DoTestFloat(0,'0.0');
  176. end;
  177. procedure TTestParser.TestString;
  178. Const
  179. // Glowing star in UTF8
  180. GlowingStar = #$F0#$9F#$8C#$9F;
  181. begin
  182. DoTestString('A string');
  183. DoTestString('');
  184. DoTestString('\"');
  185. DoTestString('\u00f8','ø'); // this is ø
  186. DoTestString('\u00f8\"','ø"'); // this is ø"
  187. // Writeln(GlowingStar);
  188. DoTestString('\ud83c\udf1f',GlowingStar);
  189. end;
  190. procedure TTestParser.TestArray;
  191. Var
  192. S1,S2,S3 : String;
  193. begin
  194. DoTestArray('[]',0);
  195. DoTestArray('[null]',1);
  196. DoTestArray('[true]',1);
  197. DoTestArray('[false]',1);
  198. DoTestArray('[1]',1);
  199. DoTestArray('[1, 2]',2);
  200. DoTestArray('[1, 2, 3]',3);
  201. DoTestArray('[1234567890123456]',1);
  202. DoTestArray('[1234567890123456, 2234567890123456]',2);
  203. DoTestArray('[1234567890123456, 2234567890123456, 3234567890123456]',3);
  204. Str(12/10,S1);
  205. Delete(S1,1,1);
  206. Str(34/10,S2);
  207. Delete(S2,1,1);
  208. Str(34/10,S3);
  209. Delete(S3,1,1);
  210. DoTestArray('['+S1+']',1,true);
  211. DoTestArray('['+S1+', '+S2+']',2,true);
  212. DoTestArray('['+S1+', '+S2+', '+S3+']',3,true);
  213. DoTestArray('["A string"]',1);
  214. DoTestArray('["A string", "Another string"]',2);
  215. DoTestArray('["A string", "Another string", "Yet another string"]',3);
  216. DoTestArray('[null, false]',2);
  217. DoTestArray('[true, false]',2);
  218. DoTestArray('[null, 1]',2);
  219. DoTestArray('[1, "A string"]',2);
  220. DoTestArray('[1, []]',2);
  221. DoTestArray('[1, [1, 2]]',2);
  222. end;
  223. procedure TTestParser.TestTrailingComma;
  224. begin
  225. FOptions:=[joIgnoreTrailingComma];
  226. DoTestArray('[1, 2,]',2,True);
  227. DoTestObject('{ "a" : 1, }',['a'],False);
  228. end;
  229. procedure TTestParser.TestTrailingCommaErrorArray;
  230. begin
  231. AssertException('Need joIgnoreTrailingComma in options to allow trailing comma',EJSONParser,@DoTrailingCommaErrorArray) ;
  232. end;
  233. procedure TTestParser.TestTrailingCommaErrorObject;
  234. begin
  235. AssertException('Need joIgnoreTrailingComma in options to allow trailing comma',EJSONParser,@DoTrailingCommaErrorObject);
  236. end;
  237. procedure TTestParser.DoTrailingCommaErrorArray;
  238. begin
  239. DoTestArray('[1, 2,]',2,True);
  240. end;
  241. procedure TTestParser.DoTrailingCommaErrorObject;
  242. begin
  243. DoTestObject('{ "a" : 1, }',['a'],False);
  244. end;
  245. procedure TTestParser.TestMixed;
  246. Const
  247. SAddr ='{ "addressbook": { "name": "Mary Lebow", '+
  248. ' "address": {'+
  249. ' "street": "5 Main Street",'+LineEnding+
  250. ' "city": "San Diego, CA",'+LineEnding+
  251. ' "zip": 91912'+LineEnding+
  252. ' },'+LineEnding+
  253. ' "phoneNumbers": [ '+LineEnding+
  254. ' "619 332-3452",'+LineEnding+
  255. ' "664 223-4667"'+LineEnding+
  256. ' ]'+LineEnding+
  257. ' }'+LineEnding+
  258. '}';
  259. begin
  260. DoTestArray('[1, {}]',2);
  261. DoTestArray('[1, { "a" : 1 }]',2);
  262. DoTestArray('[1, { "a" : 1 }, 1]',3);
  263. DoTestObject('{ "a" : [1, 2] }',['a']);
  264. DoTestObject('{ "a" : [1, 2], "B" : { "c" : "d" } }',['a','B']);
  265. DoTestObject(SAddr,['addressbook'],False);
  266. end;
  267. procedure TTestParser.TestComment;
  268. begin
  269. FOptions:=[joComments];
  270. DoTestArray('/* */ [1, {}]',2,True);
  271. DoTestArray('//'+sLineBreak+'[1, { "a" : 1 }]',2,True);
  272. DoTestArray('/* '+sLineBreak+' */ [1, {}]',2,True);
  273. DoTestArray('/*'+sLineBreak+'*/ [1, {}]',2,True);
  274. DoTestArray('/*'+sLineBreak+'*/ [1, {}]',2,True);
  275. DoTestArray('/*'+sLineBreak+'*'+sLineBreak+'*/ [1, {}]',2,True);
  276. DoTestArray('/**'+sLineBreak+'**'+sLineBreak+'**/ [1, {}]',2,True);
  277. DoTestArray('/* */ [1, {}]',2,True);
  278. DoTestArray('[1, { "a" : 1 }]//'+sLineBreak,2,True);
  279. DoTestArray('[1, {}]/* '+sLineBreak+' */ ',2,True);
  280. DoTestArray('[1, {}]/*'+sLineBreak+'*/ ',2,True);
  281. DoTestArray('[1, {}]/*'+sLineBreak+'*/ ',2,True);
  282. DoTestArray('[1, {}]/*'+sLineBreak+'*'+sLineBreak+'*/ ',2,True);
  283. DoTestArray(' [1, {}]/**'+sLineBreak+'**'+sLineBreak+'**/',2,True);
  284. end;
  285. procedure TTestParser.TestObject;
  286. begin
  287. DoTestObject('{}',[]);
  288. DoTestObject('{ "a" : 1 }',['a']);
  289. DoTestObject('{ "a" : 1, "B" : "String" }',['a','B']);
  290. DoTestObject('{ "a" : 1, "B" : {} }',['a','B']);
  291. DoTestObject('{ "a" : 1, "B" : { "c" : "d" } }',['a','B']);
  292. end;
  293. procedure TTestParser.TestObjectError;
  294. begin
  295. DoTestError('{ "name" : value }',[joUTF8]);
  296. end;
  297. procedure TTestParser.DoTestObject(S: String; const ElNames: array of String;
  298. DoJSONTest: Boolean);
  299. Var
  300. P : TJSONParser;
  301. J : TJSONData;
  302. O : TJSONObject;
  303. I : Integer;
  304. begin
  305. J:=Nil;
  306. P:=TJSONParser.Create(S,[joUTF8]);
  307. Try
  308. P.Options:=FOptions;
  309. J:=P.Parse;
  310. If (J=Nil) then
  311. Fail('Parse of object "'+S+'" fails');
  312. TestJSONType(J,jtObject);
  313. TestItemCount(J,High(ElNames)-Low(ElNames)+1);
  314. O:=TJSONObject(J);
  315. For I:=Low(ElNames) to High(ElNames) do
  316. AssertEquals(Format('Element %d name',[I-Low(Elnames)])
  317. ,ElNames[i], O.Names[I-Low(ElNames)]);
  318. If DoJSONTest then
  319. self.TestJSON(J,S);
  320. Finally
  321. FreeAndNil(J);
  322. FreeAndNil(P);
  323. end;
  324. end;
  325. procedure TTestParser.DoTestArray(S : String; ACount : Integer; IgnoreJSON : Boolean = False);
  326. Var
  327. P : TJSONParser;
  328. J : TJSONData;
  329. begin
  330. J:=Nil;
  331. P:=TJSONParser.Create(S,[joComments]);
  332. Try
  333. P.Options:=FOptions;
  334. J:=P.Parse;
  335. If (J=Nil) then
  336. Fail('Parse of array "'+S+'" fails');
  337. TestJSONType(J,jtArray);
  338. TestItemCount(J,ACount);
  339. if not IgnoreJSON then
  340. TestJSON(J,S);
  341. Finally
  342. FreeAndNil(J);
  343. FreeAndNil(P);
  344. end;
  345. end;
  346. procedure TTestParser.DoTestClass(S: String; AClass: TJSONDataClass);
  347. Var
  348. P : TJSONParser;
  349. D : TJSONData;
  350. begin
  351. P:=TJSONParser.Create(S,[joUTF8]);
  352. try
  353. D:=P.Parse;
  354. try
  355. AssertEquals('Correct class for '+S+' : ',AClass,D.ClassType);
  356. finally
  357. D.Free
  358. end;
  359. finally
  360. P.Free;
  361. end;
  362. end;
  363. procedure TTestParser.TestErrors;
  364. begin
  365. DoTestError('1Tru');
  366. DoTestError('a');
  367. DoTestError('"b');
  368. DoTestError('b"');
  369. DoTestError('{"a" : }');
  370. DoTestError('{"a" : ""');
  371. DoTestError('{"a : ""');
  372. DoTestError('[1,]');
  373. DoTestError('[,]');
  374. DoTestError('[,,]');
  375. DoTestError('[1,,]');
  376. end;
  377. procedure TTestParser.TestClasses;
  378. begin
  379. SetMyInstanceTypes;
  380. DoTestClass('null',TMyNull);
  381. DoTestClass('true',TMyBoolean);
  382. DoTestClass('1',TMyInteger);
  383. DoTestClass('1.2',TMyFloat);
  384. DoTestClass('123456789012345',TMyInt64);
  385. DoTestClass('"tata"',TMyString);
  386. DoTestClass('{}',TMyObject);
  387. DoTestClass('[]',TMyArray);
  388. end;
  389. procedure TTestParser.CallNoHandler;
  390. begin
  391. GetJSON('1',True).Free;
  392. end;
  393. procedure TTestParser.Setup;
  394. begin
  395. inherited Setup;
  396. FOptions:=[];
  397. end;
  398. procedure TTestParser.CallNoHandlerStream;
  399. Var
  400. S : TStringStream;
  401. begin
  402. S:=TstringStream.Create('1');
  403. try
  404. GetJSON(S,True).Free;
  405. finally
  406. S.Free;
  407. end;
  408. end;
  409. procedure TTestParser.TestHandler;
  410. begin
  411. AssertNotNull('Handler installed',GetJSONParserHandler);
  412. end;
  413. procedure TTestParser.TestNoHandlerError;
  414. Var
  415. H : TJSONParserHandler;
  416. HS : TJSONStringParserHandler;
  417. begin
  418. H:=GetJSONParserHandler;
  419. HS:=GetJSONStringParserHandler;
  420. try
  421. AssertSame('SetJSONParserHandler returns previous handler',H,SetJSONParserHandler(Nil));
  422. AssertSame('SetJSONStringParserHandler returns previous handler',HS,SetJSONStringParserHandler(Nil));
  423. AssertException('No handler raises exception',EJSON,@CallNoHandler);
  424. AssertException('No handler raises exception',EJSON,@CallNoHandlerStream);
  425. finally
  426. SetJSONParserHandler(H);
  427. SetJSONStringParserHandler(HS);
  428. end;
  429. end;
  430. procedure TTestParser.TestHandlerResult;
  431. Var
  432. D : TJSONData;
  433. begin
  434. D:=GetJSON('"123"');
  435. try
  436. AssertEquals('Have correct string','123',D.AsString);
  437. finally
  438. D.Free;
  439. end;
  440. end;
  441. procedure TTestParser.TestHandlerResultStream;
  442. Var
  443. D : TJSONData;
  444. S : TStream;
  445. begin
  446. S:=TStringStream.Create('"123"');
  447. try
  448. D:=GetJSON(S);
  449. try
  450. AssertEquals('Have correct string','123',D.AsString);
  451. finally
  452. D.Free;
  453. end;
  454. finally
  455. S.Free;
  456. end;
  457. end;
  458. procedure TTestParser.TestEmptyLine;
  459. // Bug report 36037
  460. Const
  461. MyJSON =
  462. ' {'+sLineBreak+
  463. ' "pylib__linux" : "libpython3.7m.so.1.0",'+sLineBreak+
  464. ' "ui_toolbar_theme": "default_24x24",'+sLineBreak+
  465. ' "ui_toolbar_show" : true,'+sLineBreak+
  466. ' "font_name__linux" : "DejaVu Sans Mono",'+sLineBreak+
  467. ' "font_size__linux" : 10,'+sLineBreak+
  468. ' "ui_listbox_fuzzy": false,'+sLineBreak+
  469. ' "ui_max_size_lexer": 5,'+sLineBreak+
  470. ' "find_separate_form": false,'+sLineBreak+sLineBreak+
  471. '}';
  472. var
  473. J : TJSONData;
  474. begin
  475. With TJSONParser.Create(MyJSON,[joUTF8,joIgnoreTrailingComma]) do
  476. Try
  477. J:=Parse;
  478. J.Free;
  479. Finally
  480. Free;
  481. end;
  482. end;
  483. procedure TTestParser.TestBug36037Part2;
  484. Const
  485. MyJSON =
  486. '{'+sLineBreak+
  487. ' "tab_spaces": true,'+sLineBreak+
  488. ' //auto-indent kind:'+sLineBreak+
  489. ' // 0: indent like in prev line'+sLineBreak+
  490. ' // 1: by spaces'+sLineBreak+
  491. ' // 2: by tabs+spaces'+sLineBreak+
  492. ' // 3: by tabs only'+sLineBreak+
  493. ' "indent_kind": 1,'+sLineBreak+
  494. ' "indent_size": 4,'+sLineBreak+
  495. ''+sLineBreak+
  496. ' "saving_trim_spaces": true,'+sLineBreak+
  497. ''+sLineBreak+
  498. '// "config_menus_from": "kv-menu JSON.json",'+sLineBreak+
  499. ' "find_hotkey_replace": "Alt+Enter",'+sLineBreak+
  500. ' "fold_style": 4,'+sLineBreak+
  501. '}'+sLineBreak;
  502. var
  503. J : TJSONData;
  504. begin
  505. With TJSONParser.Create(MyJSON,[joUTF8,joIgnoreTrailingComma,joComments]) do
  506. Try
  507. J:=Parse;
  508. J.Free;
  509. Finally
  510. Free;
  511. end;
  512. end;
  513. procedure TTestParser.DoTestError(S : String; Options : TJSONOptions = DefaultOpts);
  514. Var
  515. P : TJSONParser;
  516. J : TJSONData;
  517. ParseOK : Boolean;
  518. N : String;
  519. begin
  520. ParseOK:=False;
  521. P:=TJSONParser.Create(S,[joUTF8]);
  522. P.OPtions:=Options;
  523. J:=Nil;
  524. Try
  525. Try
  526. Repeat
  527. FreeAndNil(J);
  528. J:=P.Parse;
  529. ParseOK:=True;
  530. If (J<>Nil) then
  531. N:=J.ClassName;
  532. Until (J=Nil)
  533. Finally
  534. FreeAndNil(J);
  535. FreeAndNil(P);
  536. end;
  537. except
  538. ParseOk:=False;
  539. end;
  540. If ParseOK then
  541. Fail('Parse of JSON string "'+S+'" should fail, but returned '+N);
  542. end;
  543. procedure TTestParser.DoTestString(S: String);
  544. begin
  545. DoTestString(S,JSONStringToString(S));
  546. end;
  547. procedure TTestParser.DoTestString(S: String; AResult : String);
  548. Var
  549. P : TJSONParser;
  550. J : TJSONData;
  551. begin
  552. P:=TJSONParser.Create('"'+S+'"',[joUTF8]);
  553. Try
  554. J:=P.Parse;
  555. If (J=Nil) then
  556. Fail('Parse of string "'+S+'" fails');
  557. TestJSONType(J,jtString);
  558. TestAsString(J,aResult);
  559. if Pos('\u',S)=0 then
  560. TestJSON(J,'"'+S+'"');
  561. Finally
  562. FreeAndNil(J);
  563. FreeAndNil(P);
  564. end;
  565. end;
  566. procedure TTestParser.DoTestFloat(F : TJSONFloat);
  567. Var
  568. S : String;
  569. begin
  570. Str(F,S);
  571. DoTestFloat(F,S);
  572. end;
  573. procedure TTestParser.DoTestFloat(F : TJSONFloat; S : String);
  574. Var
  575. P : TJSONParser;
  576. J : TJSONData;
  577. begin
  578. P:=TJSONParser.Create(S,[joUTF8]);
  579. Try
  580. J:=P.Parse;
  581. If (J=Nil) then
  582. Fail('Parse of float '+S+' fails');
  583. TestJSONType(J,jtNumber);
  584. TestAsFloat(J,F);
  585. Finally
  586. FreeAndNil(J);
  587. FreeAndNil(P);
  588. end;
  589. end;
  590. initialization
  591. RegisterTest(TTestParser);
  592. end.