tccssparser.pp 30 KB

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