tccssparser.pp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  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, testutils, 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. var
  397. R : TCSSRuleElement;
  398. begin
  399. R:=ParseRule('@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 3) { }');
  400. end;
  401. procedure TTestCSSParser.TestCommaPrefixedEmptyRule;
  402. var
  403. R : TCSSRuleElement;
  404. sel: TCSSArrayElement;
  405. begin
  406. R:=ParseRule('#facebox .tl,#facebox .tl { }');
  407. end;
  408. procedure TTestCSSParser.TestOneDeclarationIDValue;
  409. var
  410. R : TCSSRuleElement;
  411. D : TCSSDeclarationElement;
  412. Id : TCSSIdentifierElement;
  413. begin
  414. R:=ParseRule('{ a : b; }');
  415. AssertEquals('selector count',0,R.SelectorCount);
  416. D:=CheckDeclaration(R,0,'a');
  417. AssertEquals('Value count', 1, D.ChildCount);
  418. ID:=TCSSIdentifierElement(CheckClass('Value', TCSSIdentifierElement,D.Children[0]));
  419. AssertEquals('Value','b',id.Value);
  420. end;
  421. procedure TTestCSSParser.TestOneDeclarationIDValueAndEmpty;
  422. var
  423. R : TCSSRuleElement;
  424. D : TCSSDeclarationElement;
  425. Id : TCSSIdentifierElement;
  426. begin
  427. R:=ParseRule('{ a : b;; }');
  428. AssertEquals('selector count',0,R.SelectorCount);
  429. D:=CheckDeclaration(R,0,'a');
  430. AssertEquals('Value count', 1, D.ChildCount);
  431. ID:=TCSSIdentifierElement(CheckClass('Value', TCSSIdentifierElement,D.Children[0]));
  432. AssertEquals('Value','b',id.Value);
  433. end;
  434. procedure TTestCSSParser.TestOneDeclarationIntValue;
  435. var
  436. R : TCSSRuleElement;
  437. D : TCSSDeclarationElement;
  438. U : TCSSUnits;
  439. begin
  440. For U in TCSSUnits do
  441. begin
  442. R:=ParseRule('{ a : 1'+CSSUnitNames[U]+'; }');
  443. AssertEquals('selector count',0,R.SelectorCount);
  444. D:=CheckDeclaration(R,0,'a');
  445. AssertEquals('Value count', 1, D.ChildCount);
  446. CheckLiteral('Value for '+CSSUnitNames[U],D.Children[0],1,U);
  447. end;
  448. end;
  449. procedure TTestCSSParser.TestOneDeclarationStringValue;
  450. var
  451. R : TCSSRuleElement;
  452. D : TCSSDeclarationElement;
  453. begin
  454. R:=ParseRule('{ a : "b"; }');
  455. AssertEquals('selector count',0,R.SelectorCount);
  456. D:=CheckDeclaration(R,0,'a');
  457. AssertEquals('Value count', 1, D.ChildCount);
  458. CheckLiteral('Value',D.Children[0],'b');
  459. end;
  460. procedure TTestCSSParser.TestOneDeclarationHashValue;
  461. var
  462. R : TCSSRuleElement;
  463. D : TCSSDeclarationElement;
  464. S : TCSSStringElement;
  465. begin
  466. R:=ParseRule('{ a : #ABABAB; }');
  467. AssertEquals('selector count',0,R.SelectorCount);
  468. D:=CheckDeclaration(R,0,'a');
  469. AssertEquals('Value count', 1, D.ChildCount);
  470. S:=TCSSStringElement(CheckClass('Value', TCSSStringElement,D.Children[0]));
  471. AssertEquals('Value ','#ABABAB',S.Value);
  472. end;
  473. procedure TTestCSSParser.TestOneDeclarationURLValue;
  474. var
  475. R : TCSSRuleElement;
  476. D : TCSSDeclarationElement;
  477. U : TCSSURLElement;
  478. begin
  479. R:=ParseRule('{ a : url("b.c"); }');
  480. AssertEquals('selector count',0,R.SelectorCount);
  481. D:=CheckDeclaration(R,0,'a');
  482. AssertEquals('Value count', 1, D.ChildCount);
  483. U:=TCSSURLElement(CheckClass('Value', TCSSURLElement,D.Children[0]));
  484. AssertEquals('Value ','b.c',U.Value);
  485. end;
  486. procedure TTestCSSParser.TestOneDeclarationMultiValue;
  487. var
  488. R : TCSSRuleElement;
  489. D : TCSSDeclarationElement;
  490. L : TCSSListElement;
  491. begin
  492. R:=ParseRule('{ a : 1px 2px 3px 4px; }');
  493. AssertEquals('selector count',0,R.SelectorCount);
  494. D:=CheckDeclaration(R,0,'a');
  495. AssertEquals('Value count', 1, D.ChildCount);
  496. L:=TCSSListElement(CheckClass('List',TCSSListElement,D.Children[0]));
  497. AssertEquals('List element count', 4, L.ChildCount);
  498. CheckLiteral('Value 1 ',L.Children[0],1,cuPX);
  499. CheckLiteral('Value 2 ',L.Children[1],2,cuPX);
  500. CheckLiteral('Value 3 ',L.Children[2],3,cuPX);
  501. CheckLiteral('Value 4 ',L.Children[3],4,cuPX);
  502. end;
  503. procedure TTestCSSParser.TestOneDeclarationMultiListValue;
  504. var
  505. R : TCSSRuleElement;
  506. D : TCSSDeclarationElement;
  507. L : TCSSListElement;
  508. begin
  509. R:=ParseRule('{ a : 1px 2px, 3px 4px; }');
  510. AssertEquals('selector count',0,R.SelectorCount);
  511. D:=CheckDeclaration(R,0,'a');
  512. AssertEquals('Value count', 2, D.ChildCount);
  513. L:=TCSSListElement(CheckClass('List',TCSSListElement,D.Children[0]));
  514. AssertEquals('List element count', 2, L.ChildCount);
  515. CheckLiteral('Value 1 ',L.Children[0],1,cuPX);
  516. CheckLiteral('Value 2 ',L.Children[1],2,cuPX);
  517. L:=TCSSListElement(CheckClass('List',TCSSListElement,D.Children[1]));
  518. AssertEquals('List element count', 2, L.ChildCount);
  519. CheckLiteral('Value 3 ',L.Children[0],3,cuPX);
  520. CheckLiteral('Value 4 ',L.Children[1],4,cuPX);
  521. end;
  522. procedure TTestCSSParser.TestOneDeclarationExprValue;
  523. begin
  524. // Todo
  525. end;
  526. procedure TTestCSSParser.TestOneDeclarationUnicodeRangeValue;
  527. var
  528. R : TCSSRuleElement;
  529. D : TCSSDeclarationElement;
  530. begin
  531. R:=ParseRule('{ unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; }');
  532. D:=CheckDeclaration(R,0);
  533. AssertEquals('Count values', 4, D.ChildCount);
  534. CheckClass('Value 0',TCSSUnicodeRangeElement,D.Children[0]);
  535. CheckClass('Value 1',TCSSUnicodeRangeElement,D.Children[1]);
  536. CheckClass('Value 2',TCSSUnicodeRangeElement,D.Children[2]);
  537. CheckClass('Value 3',TCSSUnicodeRangeElement,D.Children[3]);
  538. end;
  539. procedure TTestCSSParser.TestOneDeclarationNoColon;
  540. Var
  541. R : TCSSRuleElement;
  542. begin
  543. R:=ParseRule('@a b { 0% { d: e; } }');
  544. end;
  545. procedure TTestCSSParser.TestTwoDeclarationNoColon;
  546. Var
  547. R : TCSSRuleElement;
  548. begin
  549. R:=ParseRule('@a b { 0% { d: e; } 100% { f : g; } }');
  550. end;
  551. procedure TTestCSSParser.TestOneEmptyDeclaration;
  552. var
  553. R : TCSSRuleElement;
  554. begin
  555. R:=ParseRule('{ ; }');
  556. AssertEquals('selector count',0,R.SelectorCount);
  557. AssertEquals('declaration count',0,R.ChildCount);
  558. end;
  559. procedure TTestCSSParser.TestImportAtKeyWord;
  560. var
  561. R : TCSSAtRuleElement;
  562. // D : TCSSDeclarationElement;
  563. begin
  564. R:=TCSSAtRuleElement(CheckClass('at',TCSSAtRuleElement,ParseRule('@import url("abc.css");')));
  565. AssertEquals('selector count',1,R.SelectorCount);
  566. AssertEquals('declaration count',0,R.ChildCount);
  567. end;
  568. procedure TTestCSSParser.TestMediaPrint;
  569. begin
  570. ParseRule('@media print { *, *:before {} }');
  571. end;
  572. procedure TTestCSSParser.TestSupportsFunction;
  573. begin
  574. ParseRule('@supports ((position: -webkit-sticky) or (position: sticky)) {'+ sLineBreak+
  575. ' .sticky-top { '+ sLineBreak+
  576. ' position: -webkit-sticky; '+ sLineBreak+
  577. ' position: sticky; '+ sLineBreak+
  578. ' top: 0; '+ sLineBreak+
  579. ' z-index: 1020; '+ sLineBreak+
  580. ' } '+ sLineBreak+
  581. '} '
  582. );
  583. end;
  584. { TTestBaseCSSParser }
  585. function TTestBaseCSSParser.GetRule: TCSSRuleElement;
  586. var
  587. L : TCSSCompoundElement;
  588. begin
  589. L:=TCSSCompoundElement(CheckClass('list',TCSSCompoundElement,ParseResult));
  590. AssertTrue('Result has at least 1 child',L.ChildCount>0);
  591. if L.Children[0] is TCSSAtRuleElement then
  592. Result:=TCSSAtRuleElement(CheckClass('First element is rule',TCSSAtRuleElement,L.Children[0]))
  593. else
  594. Result:=TCSSRuleElement(CheckClass('First element is rule',TCSSRuleElement,L.Children[0]));
  595. end;
  596. procedure TTestBaseCSSParser.SetUp;
  597. begin
  598. inherited SetUp;
  599. FParser:=Nil;
  600. FSource:=Nil;
  601. end;
  602. procedure TTestBaseCSSParser.clear;
  603. begin
  604. if FParseResult<>FToFree then
  605. FreeAndNil(FToFree);
  606. FreeAndNil(FParseResult);
  607. FreeAndNil(FParser);
  608. FReeAndNil(FSource);
  609. end;
  610. procedure TTestBaseCSSParser.TearDown;
  611. begin
  612. Clear;
  613. inherited TearDown;
  614. end;
  615. procedure TTestBaseCSSParser.CreateParser(const ASource: string);
  616. begin
  617. Clear;
  618. FSource:=TStringStream.Create(ASource);
  619. FParser:=TCSSParser.Create(FSource);
  620. end;
  621. procedure TTestBaseCSSParser.Parse;
  622. begin
  623. FParseResult:=FParser.Parse;
  624. FToFree:=FParseResult;
  625. end;
  626. procedure TTestBaseCSSParser.Parse(const aSource: String);
  627. begin
  628. CreateParser(aSource);
  629. Parse;
  630. end;
  631. function TTestBaseCSSParser.ParseRule(const aSource: String): TCSSRuleElement;
  632. begin
  633. Parse(aSource);
  634. if ParseResult is TCSSRuleElement then
  635. Result:=ParseResult as TCSSRuleElement
  636. else
  637. Result:=FirstRule;
  638. end;
  639. procedure TTestBaseCSSParser.AssertEquals(AMessage : String; AExpected, AActual: TCSSUnits);
  640. Var
  641. S,EN1,EN2 : String;
  642. begin
  643. If (AActual<>AExpected) then
  644. begin
  645. EN1:=GetEnumName(TypeINfo(TCSSUnits),Ord(AExpected));
  646. EN2:=GetEnumName(TypeINfo(TCSSUnits),Ord(AActual));
  647. S:=Format('%s : %s <> %s',[AMessage,EN1,EN2]);
  648. Fail(S);
  649. end;
  650. end;
  651. procedure TTestBaseCSSParser.AssertEquals(AMessage: String; AExpected, AActual: TCSSBinaryOperation);
  652. Var
  653. S,EN1,EN2 : String;
  654. begin
  655. If (AActual<>AExpected) then
  656. begin
  657. EN1:=GetEnumName(TypeINfo(TCSSBinaryOperation),Ord(AExpected));
  658. EN2:=GetEnumName(TypeINfo(TCSSBinaryOperation),Ord(AActual));
  659. S:=Format('%s : %s <> %s',[AMessage,EN1,EN2]);
  660. Fail(S);
  661. end;
  662. end;
  663. function TTestBaseCSSParser.CheckClass(const aMsg: String; aExpectedClass: TCSSElementClass; aActual: TCSSElement): TCSSElement;
  664. begin
  665. AssertNotNull(aMsg+': Not null element',aExpectedClass);
  666. AssertNotNull(aMsg+': Not null class',aActual);
  667. AssertEquals(aMsg,aExpectedClass,aActual.ClassType);
  668. Result:=aActual;
  669. end;
  670. function TTestBaseCSSParser.CheckDeclaration(aRule: TCSSRuleElement; aIndex: Integer): TCSSDeclarationElement;
  671. begin
  672. AssertTrue('Have rule child '+IntToStr(aIndex),aIndex<aRule.ChildCount);
  673. Result:=TCSSDeclarationElement(CheckClass('Decl', TCSSDeclarationElement,aRule.Children[aIndex]));
  674. end;
  675. function TTestBaseCSSParser.CheckDeclaration(aRule: TCSSRuleElement; aIndex: Integer; const AKey: String): TCSSDeclarationElement;
  676. var
  677. ID : TCSSIdentifierElement;
  678. begin
  679. Result:=CheckDeclaration(aRule,aIndex);
  680. AssertEquals('Key count', 1, Result.KeyCount);
  681. ID:=TCSSIdentifierElement(CheckClass('key 0', TCSSIdentifierElement,Result.Keys[0]));
  682. AssertEquals('Key 0 name',aKey,id.Value);
  683. end;
  684. function TTestBaseCSSParser.CheckSelector(aRule: TCSSRuleElement; aIndex: Integer): TCSSElement;
  685. begin
  686. AssertTrue('Have rule selector '+IntToStr(aIndex),aIndex<aRule.SelectorCount);
  687. Result:=aRule.Selectors[aIndex];
  688. AssertNotNull('Have selector non-nil',Result);
  689. end;
  690. function TTestBaseCSSParser.CheckSelector(aRule: TCSSRuleElement; aIndex: Integer; const aName: String): TCSSElement;
  691. begin
  692. Result:=CheckSelector(aRule,aIndex);
  693. if Result is TCSSIdentifierElement then
  694. AssertEquals('Selector '+IntToStr(aIndex)+'name',aName,TCSSIdentifierElement(Result).Name)
  695. else if Result is TCSSStringElement then
  696. AssertEquals('Selector '+IntToStr(aIndex)+'name',aName,TCSSStringElement(Result).Value)
  697. else
  698. Fail('Selector '+IntToStr(aIndex)+' has no known type')
  699. end;
  700. function TTestBaseCSSParser.CheckList(aList: TCSSListElement; aIndex: Integer): TCSSElement;
  701. begin
  702. AssertTrue('Have list index '+IntToStr(aIndex),aIndex<aList.ChildCount);
  703. Result:=aList[aIndex];
  704. AssertNotNull('Have element non-nil',Result);
  705. end;
  706. function TTestBaseCSSParser.CheckList(aList: TCSSListElement; aIndex: Integer; const aName: String): TCSSElement;
  707. begin
  708. Result:=CheckList(aList,aIndex);
  709. if Result is TCSSIdentifierElement then
  710. AssertEquals('List element '+IntToStr(aIndex)+'name',aName,TCSSIdentifierElement(Result).Name)
  711. else if Result is TCSSStringElement then
  712. AssertEquals('List element '+IntToStr(aIndex)+'name',aName,TCSSStringElement(Result).Value)
  713. else
  714. Fail('List element '+IntToStr(aIndex)+' has no known type')
  715. end;
  716. function TTestBaseCSSParser.CheckLiteral(Msg: String; aEl: TCSSelement; aValue: String): TCSSStringElement;
  717. begin
  718. Result:=TCSSStringElement(CheckClass(Msg+': class', TCSSStringElement,aEl));
  719. AssertEquals(Msg+': String Value',aValue,Result.Value);
  720. end;
  721. function TTestBaseCSSParser.CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer): TCSSIntegerElement;
  722. begin
  723. Result:=TCSSIntegerElement(CheckClass(Msg+': Class', TCSSIntegerElement,aEl));
  724. AssertEquals(Msg+': Value ',aValue,Result.Value);
  725. end;
  726. function TTestBaseCSSParser.CheckLiteral(Msg: String; aEl: TCSSelement; aValue: Integer; AUnits: TCSSUnits): TCSSIntegerElement;
  727. begin
  728. Result:=CheckLiteral(Msg,aEl,aValue);
  729. AssertEquals('Units',aUnits,Result.Units);
  730. end;
  731. function TTestBaseCSSParser.GetCalArg(aCall: TCSSCallElement; aIndex: Integer): TCSSElement;
  732. begin
  733. AssertNotNull('Have call element',aCall);
  734. AssertTrue('Have argument '+IntToStr(aIndex),aIndex<aCall.ChildCount);
  735. Result:=aCall.Children[0];
  736. AssertNotNull('Have call argument',Result);
  737. end;
  738. initialization
  739. RegisterTests([TTestCSSParser,TTestCSSFilesParser]);
  740. end.