testjsonparser.pp 13 KB

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