tccssparser.pp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. {
  2. This file is part of the Free Pascal Run time library.
  3. Copyright (c) 2022 by Michael Van Canneyt ([email protected])
  4. This file contains the tests for the CSS parser
  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. unit tcCSSParser;
  12. {$mode objfpc}{$H+}
  13. interface
  14. uses
  15. Classes, SysUtils, fpcunit, testregistry, fpcssparser, fpcsstree;
  16. type
  17. { TTestBaseCSSParser }
  18. TTestBaseCSSParser = class(TTestCase)
  19. Private
  20. FParseResult: TCSSElement;
  21. FSource : TStringStream;
  22. FParser : TCSSParser;
  23. FToFree: TCSSElement;
  24. procedure Clear;
  25. function GetRule: TCSSRuleElement;
  26. protected
  27. procedure SetUp; override;
  28. procedure TearDown; override;
  29. Procedure CreateParser(Const ASource : string);
  30. procedure Parse;
  31. procedure Parse(Const aSource : String);
  32. function ParseRule(Const aSource : String) : TCSSRuleElement;
  33. procedure AssertEquals(AMessage: String; AExpected, AActual: TCSSUnits); overload;
  34. procedure AssertEquals(AMessage: String; AExpected, AActual: TCSSBinaryOperation); overload;
  35. Function CheckClass(Const aMsg : String; aExpectedClass : TCSSElementClass; aActual : TCSSElement) : TCSSElement;
  36. Function CheckDeclaration(aRule : TCSSRuleElement; aIndex : Integer) : TCSSDeclarationElement;
  37. Function CheckDeclaration(aRule : TCSSRuleElement; aIndex : Integer; const AKey : String) : TCSSDeclarationElement;
  38. Function CheckSelector(aRule : TCSSRuleElement; aIndex : Integer) : TCSSElement;
  39. Function CheckSelector(aRule : TCSSRuleElement; aIndex : Integer; const aName : String) : TCSSElement;
  40. function CheckList(aList: TCSSListElement; aIndex: Integer): TCSSElement;
  41. function CheckList(aList: TCSSListElement; aIndex: Integer; const aName: String): TCSSElement;
  42. function CheckLiteral(Msg: String; aEl: TCSSelement; aValue: String) : TCSSStringElement; overload;
  43. function CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer) : TCSSIntegerElement; overload;
  44. function CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer; AUnits : TCSSUnits) : TCSSIntegerElement; overload;
  45. Function GetCalArg(aCall : TCSSCallElement; aIndex : Integer) : TCSSElement;
  46. Public
  47. Property ParseResult : TCSSElement read FParseResult;
  48. Property FirstRule : TCSSRuleElement Read GetRule;
  49. Property ToFree : TCSSElement Read FToFree Write FToFree;
  50. end;
  51. { TTestCSSParser }
  52. TTestCSSParser = class(TTestBaseCSSParser)
  53. private
  54. Published
  55. Procedure TestEmpty;
  56. Procedure TestEmptyRule;
  57. Procedure TestPrefixedEmptyRule;
  58. Procedure TestClassPrefixedEmptyRule;
  59. Procedure TestHashPrefixedEmptyRule;
  60. procedure TestDoublePrefixedEmptyRule;
  61. procedure TestDoubleMixedPrefixedEmptyRule;
  62. procedure TestAttributePrefixedEmptyRule;
  63. procedure TestPseudoPrefixedEmptyRule;
  64. procedure TestPseudoFunctionEmptyRule;
  65. procedure TestFuncPrefixedEmptyRule;
  66. procedure TestQueryPrefixedEmptyRule;
  67. Procedure TestCommaPrefixedEmptyRule;
  68. Procedure TestOneDeclarationIDValue;
  69. Procedure TestOneDeclarationIDValueAndEmpty;
  70. Procedure TestOneDeclarationIntValue;
  71. Procedure TestOneDeclarationStringValue;
  72. Procedure TestOneDeclarationHashValue;
  73. Procedure TestOneDeclarationURLValue;
  74. Procedure TestOneDeclarationMultiValue;
  75. Procedure TestOneDeclarationMultiListValue;
  76. Procedure TestOneDeclarationExprValue;
  77. Procedure TestOneDeclarationUnicodeRangeValue;
  78. Procedure TestOneDeclarationNoColon;
  79. Procedure TestTwoDeclarationNoColon;
  80. Procedure TestOneEmptyDeclaration;
  81. Procedure TestImportAtKeyWord;
  82. Procedure TestMediaPrint;
  83. Procedure TestSupportsFunction;
  84. end;
  85. { TTestCSSFilesParser }
  86. TTestCSSFilesParser = class(TTestBaseCSSParser)
  87. private
  88. FTestDir: String;
  89. procedure SetTestDir(AValue: String);
  90. Public
  91. Procedure SetUp;override;
  92. Procedure RunFileTest(aFile : String='');
  93. Property TestDir : String Read FTestDir Write SetTestDir;
  94. Published
  95. // lowercase name must match 'test'+filename
  96. Procedure Testabsolute;
  97. Procedure Testanimation;
  98. Procedure Testanon;
  99. Procedure Testbigbig;
  100. Procedure Testclass;
  101. Procedure Testcolor;
  102. Procedure Testfont_face;
  103. Procedure Testfont_face2;
  104. Procedure Testfont;
  105. Procedure Testhello;
  106. Procedure Testid;
  107. Procedure Testinput_type;
  108. Procedure Testmargin;
  109. Procedure Testmedia_query;
  110. Procedure Testmystyle;
  111. Procedure Testnews;
  112. Procedure Testpadding;
  113. Procedure Teststyle;
  114. Procedure Teststyle2;
  115. Procedure Teststyle_big;
  116. Procedure TesTwildcard;
  117. end;
  118. implementation
  119. uses inifiles, typinfo;
  120. { TTestCSSFilesParser }
  121. procedure TTestCSSFilesParser.SetTestDir(AValue: String);
  122. begin
  123. if FTestDir=AValue then Exit;
  124. FTestDir:=AValue;
  125. end;
  126. procedure TTestCSSFilesParser.SetUp;
  127. begin
  128. inherited SetUp;
  129. With TMemIniFile.Create(ChangeFileExt(Paramstr(0),'.ini')) do
  130. try
  131. TestDir:=ReadString('CSS','SourceDir','css');
  132. finally
  133. Free
  134. end;
  135. end;
  136. procedure TTestCSSFilesParser.RunFileTest(aFile: String);
  137. var
  138. fn : string;
  139. OK : Boolean;
  140. begin
  141. if Afile='' then
  142. begin
  143. aFile:=LowerCase(TestName);
  144. if Copy(aFile,1,4)='test' then
  145. Delete(aFile,1,4);
  146. end;
  147. OK:=False;
  148. With TStringList.Create do
  149. try
  150. fn:=IncludeTrailingPathDelimiter(TestDir)+aFile+'.css';
  151. fn:=ExpandFileName(FN);
  152. // Writeln('Analysing file ',FN);
  153. LoadFromFile(fn);
  154. Parse(Text);
  155. OK:=True;
  156. finally
  157. if not OK then
  158. begin
  159. Writeln('Source generating error: ',FN);
  160. Writeln(Text);
  161. end;
  162. Free;
  163. end;
  164. end;
  165. procedure TTestCSSFilesParser.Testabsolute;
  166. begin
  167. RunFileTest;
  168. end;
  169. procedure TTestCSSFilesParser.Testanimation;
  170. begin
  171. RunFileTest;
  172. end;
  173. procedure TTestCSSFilesParser.Testanon;
  174. begin
  175. RunFileTest;
  176. end;
  177. procedure TTestCSSFilesParser.Testbigbig;
  178. begin
  179. RunFileTest;
  180. end;
  181. procedure TTestCSSFilesParser.Testclass;
  182. begin
  183. RunFileTest;
  184. end;
  185. procedure TTestCSSFilesParser.Testcolor;
  186. begin
  187. RunFileTest;
  188. end;
  189. procedure TTestCSSFilesParser.Testfont_face;
  190. begin
  191. RunFileTest('font-face');
  192. end;
  193. procedure TTestCSSFilesParser.Testfont_face2;
  194. begin
  195. RunFileTest('font-face2');
  196. end;
  197. procedure TTestCSSFilesParser.Testfont;
  198. begin
  199. RunFileTest;
  200. end;
  201. procedure TTestCSSFilesParser.Testhello;
  202. begin
  203. RunFileTest;
  204. end;
  205. procedure TTestCSSFilesParser.Testid;
  206. begin
  207. RunFileTest;
  208. end;
  209. procedure TTestCSSFilesParser.Testinput_type;
  210. begin
  211. RunFileTest;
  212. end;
  213. procedure TTestCSSFilesParser.Testmargin;
  214. begin
  215. RunFileTest;
  216. end;
  217. procedure TTestCSSFilesParser.Testmedia_query;
  218. begin
  219. RunFileTest;
  220. end;
  221. procedure TTestCSSFilesParser.Testmystyle;
  222. begin
  223. RunFileTest;
  224. end;
  225. procedure TTestCSSFilesParser.Testnews;
  226. begin
  227. RunFileTest;
  228. end;
  229. procedure TTestCSSFilesParser.Testpadding;
  230. begin
  231. RunFileTest;
  232. end;
  233. procedure TTestCSSFilesParser.Teststyle;
  234. begin
  235. RunFileTest;
  236. end;
  237. procedure TTestCSSFilesParser.Teststyle2;
  238. begin
  239. RunFileTest;
  240. end;
  241. procedure TTestCSSFilesParser.Teststyle_big;
  242. begin
  243. RunFileTest;
  244. end;
  245. procedure TTestCSSFilesParser.TesTwildcard;
  246. begin
  247. RunFileTest;
  248. end;
  249. { TTestCSSParser }
  250. procedure TTestCSSParser.TestEmpty;
  251. var
  252. L : TCSSCompoundElement;
  253. begin
  254. Parse('');
  255. L:=TCSSCompoundElement(CheckClass('list',TCSSCompoundElement,ParseResult));
  256. AssertEquals('No children',0,L.ChildCount);
  257. end;
  258. procedure TTestCSSParser.TestEmptyRule;
  259. var
  260. R : TCSSRuleElement;
  261. begin
  262. R:=ParseRule('{}');
  263. AssertEquals('No rule children',0,R.ChildCount);
  264. end;
  265. procedure TTestCSSParser.TestPrefixedEmptyRule;
  266. var
  267. R : TCSSRuleElement;
  268. sel: TCSSIdentifierElement;
  269. begin
  270. ParseRule('a { }');
  271. R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
  272. AssertEquals('No rule children',0,R.ChildCount);
  273. AssertEquals('selector count',1,R.SelectorCount);
  274. sel:=TCSSIdentifierElement(CheckClass('Selector', TCSSIdentifierElement,R.Selectors[0]));
  275. AssertEquals('Sel name','a',Sel.Value);
  276. end;
  277. procedure TTestCSSParser.TestClassPrefixedEmptyRule;
  278. var
  279. R : TCSSRuleElement;
  280. sel: TCSSClassNameElement;
  281. begin
  282. ParseRule('.a { }');
  283. R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
  284. AssertEquals('No rule children',0,R.ChildCount);
  285. AssertEquals('selector count',1,R.SelectorCount);
  286. sel:=TCSSClassNameElement(CheckClass('Selector', TCSSClassNameElement,R.Selectors[0]));
  287. AssertEquals('Sel name','.a',Sel.Value);
  288. end;
  289. procedure TTestCSSParser.TestHashPrefixedEmptyRule;
  290. var
  291. R : TCSSRuleElement;
  292. sel: TCSSStringElement;
  293. begin
  294. ParseRule('#a { }');
  295. R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
  296. AssertEquals('No rule children',0,R.ChildCount);
  297. AssertEquals('selector count',1,R.SelectorCount);
  298. sel:=TCSSStringElement(CheckClass('Selector', TCSSStringElement,R.Selectors[0]));
  299. AssertEquals('Sel name','#a',Sel.Value);
  300. end;
  301. procedure TTestCSSParser.TestDoublePrefixedEmptyRule;
  302. var
  303. R : TCSSRuleElement;
  304. sel: TCSSIdentifierElement;
  305. List : TCSSListElement;
  306. begin
  307. ParseRule('a b { }');
  308. R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
  309. AssertEquals('No rule children',0,R.ChildCount);
  310. AssertEquals('selector count',1,R.SelectorCount);
  311. List:=TCSSListElement(CheckClass('Selector', TCSSListElement,R.Selectors[0]));
  312. AssertEquals('selector list count',2,List.ChildCount);
  313. sel:=TCSSIdentifierElement(CheckClass('Selector', TCSSIdentifierElement,List[0]));
  314. AssertEquals('Sel 1 name','a',Sel.Value);
  315. sel:=TCSSIdentifierElement(CheckClass('Selector', TCSSIdentifierElement,List[1]));
  316. AssertEquals('Sel 2 name','b',Sel.Value);
  317. end;
  318. procedure TTestCSSParser.TestDoubleMixedPrefixedEmptyRule;
  319. var
  320. R : TCSSRuleElement;
  321. sel: TCSSIdentifierElement;
  322. List : TCSSListElement;
  323. begin
  324. ParseRule('a .b { }');
  325. R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
  326. AssertEquals('No rule children',0,R.ChildCount);
  327. AssertEquals('selector count',1,R.SelectorCount);
  328. List:=TCSSListElement(CheckClass('Selector', TCSSListElement,R.Selectors[0]));
  329. AssertEquals('selector list count',2,List.ChildCount);
  330. sel:=TCSSIdentifierElement(CheckClass('Selector', TCSSIdentifierElement,List[0]));
  331. AssertEquals('Sel 1 name','a',Sel.Value);
  332. sel:=TCSSClassNameElement(CheckClass('Selector', TCSSClassNameElement,List[1]));
  333. AssertEquals('Sel 2 name','.b',Sel.Value);
  334. end;
  335. procedure TTestCSSParser.TestAttributePrefixedEmptyRule;
  336. var
  337. R : TCSSRuleElement;
  338. sel: TCSSArrayElement;
  339. id : TCSSIdentifierElement;
  340. bin : TCSSBinaryElement;
  341. begin
  342. ParseRule('a[b="c"] { }');
  343. R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
  344. AssertEquals('No rule children',0,R.ChildCount);
  345. AssertEquals('selector count',1,R.SelectorCount);
  346. sel:=TCSSArrayElement(CheckClass('Selector', TCSSArrayElement,R.Selectors[0]));
  347. Id:=TCSSIdentifierElement(CheckClass('Array prefix',TCSSIdentifierElement,Sel.Prefix));
  348. AssertEquals('Prefix name','a',Id.Value);
  349. AssertEquals('Array count',1,Sel.ChildCount);
  350. Bin:=TCSSBinaryElement(CheckClass('Bin',TCSSBinaryElement,sel.children[0]));
  351. AssertEquals('Binary op',boEquals,Bin.Operation);
  352. end;
  353. procedure TTestCSSParser.TestPseudoPrefixedEmptyRule;
  354. var
  355. R : TCSSRuleElement;
  356. Sel : TCSSPseudoClassElement;
  357. begin
  358. ParseRule(':a { }');
  359. R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
  360. AssertEquals('No rule children',0,R.ChildCount);
  361. AssertEquals('selector count',1,R.SelectorCount);
  362. sel:=TCSSPseudoClassElement(CheckClass('Selector', TCSSPseudoClassElement,R.Selectors[0]));
  363. AssertEquals('Pseudo name',':a',sel.Value);
  364. end;
  365. procedure TTestCSSParser.TestPseudoFunctionEmptyRule;
  366. var
  367. R : TCSSRuleElement;
  368. Sel : TCSSCallElement;
  369. Id : TCSSIdentifierElement;
  370. begin
  371. ParseRule(':a(b) { }');
  372. R:=TCSSRuleElement(CheckClass('Rule',TCSSRuleElement,FirstRule));
  373. AssertEquals('No rule children',0,R.ChildCount);
  374. AssertEquals('selector count',1,R.SelectorCount);
  375. sel:=TCSSCallElement(CheckClass('Selector', TCSSCallElement,R.Selectors[0]));
  376. AssertEquals('Pseudo name',':a',sel.Name);
  377. AssertEquals('argument count',1,Sel.ChildCount);
  378. Id:=TCSSIdentifierElement(CheckClass('Argument 1',TCSSIdentifierElement,Sel[0]));
  379. AssertEquals('Argument name','b',id.Name);
  380. end;
  381. procedure TTestCSSParser.TestFuncPrefixedEmptyRule;
  382. var
  383. R : TCSSRuleElement;
  384. List : TCSSListElement;
  385. begin
  386. R:=ParseRule('input:enabled:read-write:-webkit-any(:focus,:hover)::-webkit-clear-button { }');
  387. AssertEquals('No rule children',0,R.ChildCount);
  388. AssertEquals('selector count',1,R.SelectorCount);
  389. List:=TCSSListElement(CheckClass('List',TCSSListElement,R.Selectors[0]));
  390. CheckList(List,0,'input');
  391. CheckList(List,1,':enabled');
  392. CheckList(List,2,':read-write');
  393. CheckList(List,4,'::-webkit-clear-button');
  394. end;
  395. procedure TTestCSSParser.TestQueryPrefixedEmptyRule;
  396. begin
  397. ParseRule('@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 3) { }');
  398. end;
  399. procedure TTestCSSParser.TestCommaPrefixedEmptyRule;
  400. begin
  401. ParseRule('#facebox .tl,#facebox .tl { }');
  402. end;
  403. procedure TTestCSSParser.TestOneDeclarationIDValue;
  404. var
  405. R : TCSSRuleElement;
  406. D : TCSSDeclarationElement;
  407. Id : TCSSIdentifierElement;
  408. begin
  409. R:=ParseRule('{ a : b; }');
  410. AssertEquals('selector count',0,R.SelectorCount);
  411. D:=CheckDeclaration(R,0,'a');
  412. AssertEquals('Value count', 1, D.ChildCount);
  413. ID:=TCSSIdentifierElement(CheckClass('Value', TCSSIdentifierElement,D.Children[0]));
  414. AssertEquals('Value','b',id.Value);
  415. end;
  416. procedure TTestCSSParser.TestOneDeclarationIDValueAndEmpty;
  417. var
  418. R : TCSSRuleElement;
  419. D : TCSSDeclarationElement;
  420. Id : TCSSIdentifierElement;
  421. begin
  422. R:=ParseRule('{ a : b;; }');
  423. AssertEquals('selector count',0,R.SelectorCount);
  424. D:=CheckDeclaration(R,0,'a');
  425. AssertEquals('Value count', 1, D.ChildCount);
  426. ID:=TCSSIdentifierElement(CheckClass('Value', TCSSIdentifierElement,D.Children[0]));
  427. AssertEquals('Value','b',id.Value);
  428. end;
  429. procedure TTestCSSParser.TestOneDeclarationIntValue;
  430. var
  431. R : TCSSRuleElement;
  432. D : TCSSDeclarationElement;
  433. U : TCSSUnits;
  434. begin
  435. For U in TCSSUnits do
  436. begin
  437. R:=ParseRule('{ a : 1'+CSSUnitNames[U]+'; }');
  438. AssertEquals('selector count',0,R.SelectorCount);
  439. D:=CheckDeclaration(R,0,'a');
  440. AssertEquals('Value count', 1, D.ChildCount);
  441. CheckLiteral('Value for '+CSSUnitNames[U],D.Children[0],1,U);
  442. end;
  443. end;
  444. procedure TTestCSSParser.TestOneDeclarationStringValue;
  445. var
  446. R : TCSSRuleElement;
  447. D : TCSSDeclarationElement;
  448. begin
  449. R:=ParseRule('{ a : "b"; }');
  450. AssertEquals('selector count',0,R.SelectorCount);
  451. D:=CheckDeclaration(R,0,'a');
  452. AssertEquals('Value count', 1, D.ChildCount);
  453. CheckLiteral('Value',D.Children[0],'b');
  454. end;
  455. procedure TTestCSSParser.TestOneDeclarationHashValue;
  456. var
  457. R : TCSSRuleElement;
  458. D : TCSSDeclarationElement;
  459. S : TCSSStringElement;
  460. begin
  461. R:=ParseRule('{ a : #ABABAB; }');
  462. AssertEquals('selector count',0,R.SelectorCount);
  463. D:=CheckDeclaration(R,0,'a');
  464. AssertEquals('Value count', 1, D.ChildCount);
  465. S:=TCSSStringElement(CheckClass('Value', TCSSStringElement,D.Children[0]));
  466. AssertEquals('Value ','#ABABAB',S.Value);
  467. end;
  468. procedure TTestCSSParser.TestOneDeclarationURLValue;
  469. var
  470. R : TCSSRuleElement;
  471. D : TCSSDeclarationElement;
  472. U : TCSSURLElement;
  473. begin
  474. R:=ParseRule('{ a : url("b.c"); }');
  475. AssertEquals('selector count',0,R.SelectorCount);
  476. D:=CheckDeclaration(R,0,'a');
  477. AssertEquals('Value count', 1, D.ChildCount);
  478. U:=TCSSURLElement(CheckClass('Value', TCSSURLElement,D.Children[0]));
  479. AssertEquals('Value ','b.c',U.Value);
  480. end;
  481. procedure TTestCSSParser.TestOneDeclarationMultiValue;
  482. var
  483. R : TCSSRuleElement;
  484. D : TCSSDeclarationElement;
  485. L : TCSSListElement;
  486. begin
  487. R:=ParseRule('{ a : 1px 2px 3px 4px; }');
  488. AssertEquals('selector count',0,R.SelectorCount);
  489. D:=CheckDeclaration(R,0,'a');
  490. AssertEquals('Value count', 1, D.ChildCount);
  491. L:=TCSSListElement(CheckClass('List',TCSSListElement,D.Children[0]));
  492. AssertEquals('List element count', 4, L.ChildCount);
  493. CheckLiteral('Value 1 ',L.Children[0],1,cuPX);
  494. CheckLiteral('Value 2 ',L.Children[1],2,cuPX);
  495. CheckLiteral('Value 3 ',L.Children[2],3,cuPX);
  496. CheckLiteral('Value 4 ',L.Children[3],4,cuPX);
  497. end;
  498. procedure TTestCSSParser.TestOneDeclarationMultiListValue;
  499. var
  500. R : TCSSRuleElement;
  501. D : TCSSDeclarationElement;
  502. L : TCSSListElement;
  503. begin
  504. R:=ParseRule('{ a : 1px 2px, 3px 4px; }');
  505. AssertEquals('selector count',0,R.SelectorCount);
  506. D:=CheckDeclaration(R,0,'a');
  507. AssertEquals('Value count', 2, D.ChildCount);
  508. L:=TCSSListElement(CheckClass('List',TCSSListElement,D.Children[0]));
  509. AssertEquals('List element count', 2, L.ChildCount);
  510. CheckLiteral('Value 1 ',L.Children[0],1,cuPX);
  511. CheckLiteral('Value 2 ',L.Children[1],2,cuPX);
  512. L:=TCSSListElement(CheckClass('List',TCSSListElement,D.Children[1]));
  513. AssertEquals('List element count', 2, L.ChildCount);
  514. CheckLiteral('Value 3 ',L.Children[0],3,cuPX);
  515. CheckLiteral('Value 4 ',L.Children[1],4,cuPX);
  516. end;
  517. procedure TTestCSSParser.TestOneDeclarationExprValue;
  518. begin
  519. // Todo
  520. end;
  521. procedure TTestCSSParser.TestOneDeclarationUnicodeRangeValue;
  522. var
  523. R : TCSSRuleElement;
  524. D : TCSSDeclarationElement;
  525. begin
  526. R:=ParseRule('{ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }');
  527. D:=CheckDeclaration(R,0);
  528. AssertEquals('Count values', 4, D.ChildCount);
  529. CheckClass('Value 0',TCSSUnicodeRangeElement,D.Children[0]);
  530. CheckClass('Value 1',TCSSUnicodeRangeElement,D.Children[1]);
  531. CheckClass('Value 2',TCSSUnicodeRangeElement,D.Children[2]);
  532. CheckClass('Value 3',TCSSUnicodeRangeElement,D.Children[3]);
  533. end;
  534. procedure TTestCSSParser.TestOneDeclarationNoColon;
  535. begin
  536. ParseRule('@a b { 0% { d: e; } }');
  537. end;
  538. procedure TTestCSSParser.TestTwoDeclarationNoColon;
  539. begin
  540. ParseRule('@a b { 0% { d: e; } 100% { f : g; } }');
  541. end;
  542. procedure TTestCSSParser.TestOneEmptyDeclaration;
  543. var
  544. R : TCSSRuleElement;
  545. begin
  546. R:=ParseRule('{ ; }');
  547. AssertEquals('selector count',0,R.SelectorCount);
  548. AssertEquals('declaration count',0,R.ChildCount);
  549. end;
  550. procedure TTestCSSParser.TestImportAtKeyWord;
  551. var
  552. R : TCSSAtRuleElement;
  553. begin
  554. R:=TCSSAtRuleElement(CheckClass('at',TCSSAtRuleElement,ParseRule('@import url("abc.css");')));
  555. AssertEquals('selector count',1,R.SelectorCount);
  556. AssertEquals('declaration count',0,R.ChildCount);
  557. end;
  558. procedure TTestCSSParser.TestMediaPrint;
  559. begin
  560. ParseRule('@media print { *, *:before {} }');
  561. end;
  562. procedure TTestCSSParser.TestSupportsFunction;
  563. begin
  564. ParseRule('@supports ((position: -webkit-sticky) or (position: sticky)) {'+ sLineBreak+
  565. ' .sticky-top { '+ sLineBreak+
  566. ' position: -webkit-sticky; '+ sLineBreak+
  567. ' position: sticky; '+ sLineBreak+
  568. ' top: 0; '+ sLineBreak+
  569. ' z-index: 1020; '+ sLineBreak+
  570. ' } '+ sLineBreak+
  571. '} '
  572. );
  573. end;
  574. { TTestBaseCSSParser }
  575. function TTestBaseCSSParser.GetRule: TCSSRuleElement;
  576. var
  577. L : TCSSCompoundElement;
  578. begin
  579. L:=TCSSCompoundElement(CheckClass('list',TCSSCompoundElement,ParseResult));
  580. AssertTrue('Result has at least 1 child',L.ChildCount>0);
  581. if L.Children[0] is TCSSAtRuleElement then
  582. Result:=TCSSAtRuleElement(CheckClass('First element is rule',TCSSAtRuleElement,L.Children[0]))
  583. else
  584. Result:=TCSSRuleElement(CheckClass('First element is rule',TCSSRuleElement,L.Children[0]));
  585. end;
  586. procedure TTestBaseCSSParser.SetUp;
  587. begin
  588. inherited SetUp;
  589. FParser:=Nil;
  590. FSource:=Nil;
  591. end;
  592. procedure TTestBaseCSSParser.Clear;
  593. begin
  594. if FParseResult<>FToFree then
  595. FreeAndNil(FToFree);
  596. FreeAndNil(FParseResult);
  597. FreeAndNil(FParser);
  598. FReeAndNil(FSource);
  599. end;
  600. procedure TTestBaseCSSParser.TearDown;
  601. begin
  602. Clear;
  603. inherited TearDown;
  604. end;
  605. procedure TTestBaseCSSParser.CreateParser(const ASource: string);
  606. begin
  607. Clear;
  608. FSource:=TStringStream.Create(ASource);
  609. FParser:=TCSSParser.Create(FSource);
  610. end;
  611. procedure TTestBaseCSSParser.Parse;
  612. begin
  613. FParseResult:=FParser.Parse;
  614. FToFree:=FParseResult;
  615. end;
  616. procedure TTestBaseCSSParser.Parse(const aSource: String);
  617. begin
  618. CreateParser(aSource);
  619. Parse;
  620. end;
  621. function TTestBaseCSSParser.ParseRule(const aSource: String): TCSSRuleElement;
  622. begin
  623. Parse(aSource);
  624. if ParseResult is TCSSRuleElement then
  625. Result:=ParseResult as TCSSRuleElement
  626. else
  627. Result:=FirstRule;
  628. end;
  629. procedure TTestBaseCSSParser.AssertEquals(AMessage : String; AExpected, AActual: TCSSUnits);
  630. Var
  631. S,EN1,EN2 : String;
  632. begin
  633. If (AActual<>AExpected) then
  634. begin
  635. EN1:=GetEnumName(TypeINfo(TCSSUnits),Ord(AExpected));
  636. EN2:=GetEnumName(TypeINfo(TCSSUnits),Ord(AActual));
  637. S:=Format('%s : %s <> %s',[AMessage,EN1,EN2]);
  638. Fail(S);
  639. end;
  640. end;
  641. procedure TTestBaseCSSParser.AssertEquals(AMessage: String; AExpected, AActual: TCSSBinaryOperation);
  642. Var
  643. S,EN1,EN2 : String;
  644. begin
  645. If (AActual<>AExpected) then
  646. begin
  647. EN1:=GetEnumName(TypeINfo(TCSSBinaryOperation),Ord(AExpected));
  648. EN2:=GetEnumName(TypeINfo(TCSSBinaryOperation),Ord(AActual));
  649. S:=Format('%s : %s <> %s',[AMessage,EN1,EN2]);
  650. Fail(S);
  651. end;
  652. end;
  653. function TTestBaseCSSParser.CheckClass(const aMsg: String; aExpectedClass: TCSSElementClass; aActual: TCSSElement): TCSSElement;
  654. begin
  655. AssertNotNull(aMsg+': Not null element',aExpectedClass);
  656. AssertNotNull(aMsg+': Not null class',aActual);
  657. AssertEquals(aMsg,aExpectedClass,aActual.ClassType);
  658. Result:=aActual;
  659. end;
  660. function TTestBaseCSSParser.CheckDeclaration(aRule: TCSSRuleElement; aIndex: Integer): TCSSDeclarationElement;
  661. begin
  662. AssertTrue('Have rule child '+IntToStr(aIndex),aIndex<aRule.ChildCount);
  663. Result:=TCSSDeclarationElement(CheckClass('Decl', TCSSDeclarationElement,aRule.Children[aIndex]));
  664. end;
  665. function TTestBaseCSSParser.CheckDeclaration(aRule: TCSSRuleElement; aIndex: Integer; const AKey: String): TCSSDeclarationElement;
  666. var
  667. ID : TCSSIdentifierElement;
  668. begin
  669. Result:=CheckDeclaration(aRule,aIndex);
  670. AssertEquals('Key count', 1, Result.KeyCount);
  671. ID:=TCSSIdentifierElement(CheckClass('key 0', TCSSIdentifierElement,Result.Keys[0]));
  672. AssertEquals('Key 0 name',aKey,id.Value);
  673. end;
  674. function TTestBaseCSSParser.CheckSelector(aRule: TCSSRuleElement; aIndex: Integer): TCSSElement;
  675. begin
  676. AssertTrue('Have rule selector '+IntToStr(aIndex),aIndex<aRule.SelectorCount);
  677. Result:=aRule.Selectors[aIndex];
  678. AssertNotNull('Have selector non-nil',Result);
  679. end;
  680. function TTestBaseCSSParser.CheckSelector(aRule: TCSSRuleElement; aIndex: Integer; const aName: String): TCSSElement;
  681. begin
  682. Result:=CheckSelector(aRule,aIndex);
  683. if Result is TCSSIdentifierElement then
  684. AssertEquals('Selector '+IntToStr(aIndex)+'name',aName,TCSSIdentifierElement(Result).Name)
  685. else if Result is TCSSStringElement then
  686. AssertEquals('Selector '+IntToStr(aIndex)+'name',aName,TCSSStringElement(Result).Value)
  687. else
  688. Fail('Selector '+IntToStr(aIndex)+' has no known type')
  689. end;
  690. function TTestBaseCSSParser.CheckList(aList: TCSSListElement; aIndex: Integer): TCSSElement;
  691. begin
  692. AssertTrue('Have list index '+IntToStr(aIndex),aIndex<aList.ChildCount);
  693. Result:=aList[aIndex];
  694. AssertNotNull('Have element non-nil',Result);
  695. end;
  696. function TTestBaseCSSParser.CheckList(aList: TCSSListElement; aIndex: Integer; const aName: String): TCSSElement;
  697. begin
  698. Result:=CheckList(aList,aIndex);
  699. if Result is TCSSIdentifierElement then
  700. AssertEquals('List element '+IntToStr(aIndex)+'name',aName,TCSSIdentifierElement(Result).Name)
  701. else if Result is TCSSStringElement then
  702. AssertEquals('List element '+IntToStr(aIndex)+'name',aName,TCSSStringElement(Result).Value)
  703. else
  704. Fail('List element '+IntToStr(aIndex)+' has no known type')
  705. end;
  706. function TTestBaseCSSParser.CheckLiteral(Msg: String; aEl: TCSSelement; aValue: String): TCSSStringElement;
  707. begin
  708. Result:=TCSSStringElement(CheckClass(Msg+': class', TCSSStringElement,aEl));
  709. AssertEquals(Msg+': String Value',aValue,Result.Value);
  710. end;
  711. function TTestBaseCSSParser.CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer): TCSSIntegerElement;
  712. begin
  713. Result:=TCSSIntegerElement(CheckClass(Msg+': Class', TCSSIntegerElement,aEl));
  714. AssertEquals(Msg+': Value ',aValue,Result.Value);
  715. end;
  716. function TTestBaseCSSParser.CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer; AUnits: TCSSUnits): TCSSIntegerElement;
  717. begin
  718. Result:=CheckLiteral(Msg,aEl,aValue);
  719. AssertEquals('Units',aUnits,Result.Units);
  720. end;
  721. function TTestBaseCSSParser.GetCalArg(aCall: TCSSCallElement; aIndex: Integer): TCSSElement;
  722. begin
  723. AssertNotNull('Have call element',aCall);
  724. AssertTrue('Have argument '+IntToStr(aIndex),aIndex<aCall.ChildCount);
  725. Result:=aCall.Children[0];
  726. AssertNotNull('Have call argument',Result);
  727. end;
  728. initialization
  729. RegisterTests([TTestCSSParser,TTestCSSFilesParser]);
  730. end.