testjsonparser.pp 14 KB

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