testjsonparser.pp 13 KB

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