testjsonparser.pp 13 KB

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