tccssparser.pp 30 KB

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