/******************************************************************************* * Copyright 2012-present Pixate, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.pixate.pxengine.styling.parsing; import java.util.List; import android.test.AndroidTestCase; import android.util.Log; import com.pixate.pxengine.cg.paints.PXLinearGradient; import com.pixate.pxengine.cg.paints.PXPaint; import com.pixate.pxengine.styling.PXDeclaration; import com.pixate.pxengine.styling.PXRuleSet; import com.pixate.pxengine.styling.PXStylesheet; import com.pixate.pxengine.styling.PXStylesheet.PXStyleSheetOrigin; import com.pixate.pxengine.styling.animation.PXKeyframe; import com.pixate.pxengine.styling.animation.PXKeyframeBlock; import com.pixate.pxengine.styling.media.PXMediaExpression; import com.pixate.pxengine.styling.media.PXMediaExpressionGroup; import com.pixate.pxengine.styling.media.PXMediaGroup; import com.pixate.pxengine.styling.media.PXNamedMediaExpression; import com.pixate.pxengine.styling.selectors.PXSelector; import com.pixate.util.CollectionUtil; import com.pixate.util.ObjectUtil; /** * {@link PXStylesheetParser} tests. */ public class PXStylesheetParserTests extends AndroidTestCase { private PXStylesheetParser parser; // TODO: Write assertStyleSheet method to remove code duplication in these // tests @Override protected void setUp() throws Exception { super.setUp(); parser = new PXStylesheetParser(); parser.setContext(getContext()); } public void testSimpleDeclaration() { String source = "button { abc: def }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse errors encountered", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXDeclaration> declarations = ruleSet.getDeclarations(); assertTrue("Expected a single declaration", declarations.size() == 1); PXDeclaration declaration = declarations.get(0); assertEquals("Expected 'abc' declaration", "abc", declaration.getName()); } public void testSimpleDeclarations() { String source = "button { abc: def; ghi: jkl }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse errors encountered", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXDeclaration> declarations = ruleSet.getDeclarations(); assertTrue("Expected two declarations", declarations.size() == 2); PXDeclaration declaration = declarations.get(0); assertEquals("Expected 'abc' declaration", "abc", declaration.getName()); declaration = declarations.get(1); assertEquals("Expected 'abc' declaration", "ghi", declaration.getName()); } public void testSimpleDeclarationsMissingSemicolon() { String source = "button { abc: def ghi: jkl }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse errors encountered", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXDeclaration> declarations = ruleSet.getDeclarations(); assertTrue("Expected two declarations", declarations.size() == 2); PXDeclaration declaration = declarations.get(0); assertEquals("Expected 'abc' declaration", "abc", declaration.getName()); declaration = declarations.get(1); assertEquals("Expected 'abc' declaration", "ghi", declaration.getName()); } public void testMissingSelector() { String source = "{ abc: def }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Expected one parse error", !CollectionUtil.isEmpty(errors) && errors.size() == 1); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", !CollectionUtil.isEmpty(ruleSets) && ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXDeclaration> declarations = ruleSet.getDeclarations(); assertTrue("Expected one declaration", declarations.size() == 1); PXDeclaration declaration = declarations.get(0); assertEquals("Expected 'abc' declaration", "abc", declaration.getName()); } public void testBadDeclarationName() { String source = "button { 10: def; ghi: jkl }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Expected one parse error", !CollectionUtil.isEmpty(errors) && errors.size() == 1); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", !CollectionUtil.isEmpty(ruleSets) && ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXDeclaration> declarations = ruleSet.getDeclarations(); assertTrue("Expected one declaration", declarations.size() == 1); PXDeclaration declaration = declarations.get(0); assertEquals("Expected 'ghi' declaration", "ghi", declaration.getName()); } public void testColonInValue() { String source = "button { abc: :; }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", !CollectionUtil.isEmpty(ruleSets) && ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXDeclaration> declarations = ruleSet.getDeclarations(); assertTrue("Expected one declaration", declarations.size() == 1); PXDeclaration declaration = declarations.get(0); assertEquals("Expected 'ghi' declaration", "abc", declaration.getName()); } // Selector Tests public void testTypeSelector() { String source = "button {}"; String expected = "(button)"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = %s\nactual = %s", expected, selector.getSource()), expected, selector.getSource()); } public void testAdjacentSiblingCombinator() { String source = "view + button {}"; String expected = "(ADJACENT_SIBLING_COMBINATOR\n" + " (view)\n" + " (button))"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", !CollectionUtil.isEmpty(ruleSets) && ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected, selector.getSource()), expected, selector.getSource()); } public void testSiblingCombinator() { String source = "view ~ button {}"; String expected = "(GENERAL_SIBLING_COMBINATOR\n" + " (view)\n" + " (button))"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected, selector.getSource()), expected, selector.getSource()); } public void testChildCombinator() { String source = "view > button {}"; String expected = "(CHILD_COMBINATOR\n" + " (view)\n" + " (button))"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", !CollectionUtil.isEmpty(ruleSets) && ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected, selector.getSource()), expected, selector.getSource()); } public void testDescendantCombinator() { String source = "view button {}"; String expected = "(DESCENDANT_COMBINATOR\n" + " (view)\n" + " (button))"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected, selector.getSource()), expected, selector.getSource()); } public void testDescendantCombinator2() { String source = "view button label {}"; String expected = "(DESCENDANT_COMBINATOR\n" + " (DESCENDANT_COMBINATOR\n" + " (view)\n" + " (button))\n" + " (label))"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected, selector.getSource()), expected, selector.getSource()); } public void testDescendantCombinator3() { String source = ".t1 :only-of-type {}"; String expected = "(DESCENDANT_COMBINATOR\n" + " (*\n" + " (CLASS t1))\n" + " (*\n" + " (PSEUDO_CLASS_PREDICATE :only-of-type)))"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected a single rule set", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected, selector.getSource()), expected, selector.getSource()); } public void testSelectorSequence() { String source = "button, slider {}"; String expected1 = "(button)"; String expected2 = "(slider)"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected two rule sets", ruleSets.size() == 2); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected1, selector.getSource()), expected1, selector.getSource()); ruleSet = ruleSets.get(1); selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected2, selector.getSource()), expected2, selector.getSource()); } public void testPseudoElement() { // NOTE: we don't place psuedo-elements in the tree yet String source = "button::before {}"; String expected = "(button\n" + " (PSEUDO_ELEMENT before))"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected one rule set", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected, selector.getSource()), expected, selector.getSource()); } public void testPseudoElements() { // NOTE: we don't place psuedo-elements in the tree yet String source = "button::before, slider::before {}"; String expected1 = "(button\n" + " (PSEUDO_ELEMENT before))"; String expected2 = "(slider\n" + " (PSEUDO_ELEMENT before))"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected two rule sets", ruleSets.size() == 2); PXRuleSet ruleSet = ruleSets.get(0); List<PXSelector> selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); PXSelector selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertNotNull("Unexpected null selector", selector); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected1, selector.getSource()), expected1, selector.getSource()); ruleSet = ruleSets.get(1); selectors = ruleSet.getSelectors(); assertTrue("Expected one selector", selectors.size() == 1); selector = CollectionUtil.isEmpty(selectors) ? null : selectors.get(0); assertEquals(String.format("Selector trees do not match:\nexpected = \n%s\nactual = \n%s", expected2, selector.getSource()), expected2, selector.getSource()); } // At-keyword Tests public void testDefaultNamespace() { String url = "http://www.pixate.com"; String source = String.format("@namespace \"%s\";", url); PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); String defaultNamespace = stylesheet.getNamespaceForPrefix(null); assertTrue(String.format("Default namespace, '%s', does not equal '%s'", defaultNamespace, url), ObjectUtil.areEqual(url, defaultNamespace)); } public void testNamespacePrefix() { String prefix = "px"; String url = "http://www.pixate.com"; String source = String.format("@namespace %s \"%s\";", prefix, url); PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); String prefixNamespace = stylesheet.getNamespaceForPrefix(prefix); assertTrue(String.format("'%s' prefix namespace, '%s', does not equal '%s'", prefix, prefixNamespace, url), ObjectUtil.areEqual(url, prefixNamespace)); } // Failures from W3C Selector tests public void testNot() { String source = "div.test *:not(a|p) { background-color : lime }"; parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue( String.format("Found %d unexpected parse error(s): %s", CollectionUtil.isEmpty(errors) ? 0 : errors.size(), CollectionUtil.toString(errors, "\n")), CollectionUtil.isEmpty(errors)); } public void testNthOfType() { String source = "line:nth-of-type(odd) { background: lime; }"; parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue( String.format("Found %d unexpected parse error(s): %s", CollectionUtil.isEmpty(errors) ? 0 : errors.size(), CollectionUtil.toString(errors, "\n")), CollectionUtil.isEmpty(errors)); } public void testNthChildWithoutSpaces() { String source = "line:nth-child(2n+1) { background: red; }"; parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue( String.format("Found %d unexpected parse error(s): %s", CollectionUtil.isEmpty(errors) ? 0 : errors.size(), CollectionUtil.toString(errors, "\n")), CollectionUtil.isEmpty(errors)); } public void testNthChildWithoutSpaces2() { String source = "line:nth-child(2n-1) { background: red; }"; parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue( String.format("Found %d unexpected parse error(s): %s", CollectionUtil.isEmpty(errors) ? 0 : errors.size(), CollectionUtil.toString(errors, "\n")), CollectionUtil.isEmpty(errors)); } public void testNthChildWithSpaces() { String source = "line:nth-child(2n + 1) { background: red; }"; parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue( String.format("Found %d unexpected parse error(s): %s", CollectionUtil.isEmpty(errors) ? 0 : errors.size(), CollectionUtil.toString(errors, "\n")), CollectionUtil.isEmpty(errors)); } public void testNthLastChild() { String source = "line:nth-last-child(n) { background: red; }"; parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue( String.format("Found %d unexpected parse error(s): %s", CollectionUtil.isEmpty(errors) ? 0 : errors.size(), CollectionUtil.toString(errors, "\n")), CollectionUtil.isEmpty(errors)); } public void testNthLastOfType() { String source = "line:nth-last-of-type(1) { background: red; }"; parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue( String.format("Found %d unexpected parse error(s): %s", CollectionUtil.isEmpty(errors) ? 0 : errors.size(), CollectionUtil.toString(errors, "\n")), CollectionUtil.isEmpty(errors)); } public void testImportant() { String source = "* { background: red !important; }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected two rule sets", ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); List<PXDeclaration> declarations = ruleSet.getDeclarations(); assertTrue("Expected one declaration", declarations.size() == 1); PXDeclaration declaration = declarations.get(0); assertTrue("declaration should be marked as important", declaration.isImportant()); String stringValue = declaration.getStringValue(); String expectedValue = "red"; assertTrue(String.format("declaration string value does not match: '%s' != '%s'", stringValue, expectedValue), ObjectUtil.areEqual(stringValue, expectedValue)); } public void testImportWithString() { String source = "@import \"test.css\";"; parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue( String.format("Found %d unexpected parse error(s): %s", CollectionUtil.isEmpty(errors) ? 0 : errors.size(), CollectionUtil.toString(errors, "\n")), CollectionUtil.isEmpty(errors)); } public void testImportWithURL() { String source = "@import url(test.css);"; parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue( String.format("Found %d unexpected parse error(s): %s", CollectionUtil.isEmpty(errors) ? 0 : errors.size(), CollectionUtil.toString(errors, "\n")), CollectionUtil.isEmpty(errors)); } // Keyframes Tests public void testEmptyKeyframes() { String source = "@keyframes test {}"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); PXKeyframe keyframe = stylesheet.getKeyframeForName("test"); assertNotNull("Expected a 'test' keyframe to be defined in the stylesheet", keyframe); List<PXKeyframeBlock> blocks = keyframe.getBlocks(); assertTrue(String.format("Expected no keyframe blocks but found %d", blocks == null ? 0 : blocks.size()), CollectionUtil.isEmpty(blocks)); } public void testKeyframesWithTo() { String source = "@keyframes test { to { color: blue; } }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); PXKeyframe keyframe = stylesheet.getKeyframeForName("test"); assertNotNull("Expected a 'test' keyframe to be defined in the stylesheet", keyframe); List<PXKeyframeBlock> blocks = keyframe.getBlocks(); assertTrue( String.format("Expected 1 keyframe block but found %d", blocks == null ? 0 : blocks.size()), !CollectionUtil.isEmpty(blocks) && blocks.size() == 1); PXKeyframeBlock block = blocks.get(0); assertTrue(String.format("Expected offset to be 1.0, but found %s", block.getOffset()), block.getOffset() == 1.0f); assertTrue(String.format("Expected 1 declaration but found %d", block.getDeclarations() .size()), block.getDeclarations().size() == 1); } public void testKeyframesWithFrom() { String source = "@keyframes test { from { color: blue; } }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); PXKeyframe keyframe = stylesheet.getKeyframeForName("test"); assertNotNull("Expected a 'test' keyframe to be defined in the stylesheet", keyframe); List<PXKeyframeBlock> blocks = keyframe.getBlocks(); assertTrue( String.format("Expected 1 keyframe block but found %d", blocks == null ? 0 : blocks.size()), !CollectionUtil.isEmpty(blocks) && blocks.size() == 1); PXKeyframeBlock block = blocks.get(0); assertTrue(String.format("Expected offset to be 0.0, but found %s", block.getOffset()), block.getOffset() == 0.0f); assertTrue(String.format("Expected 1 declaration but found %d", block.getDeclarations() .size()), block.getDeclarations().size() == 1); } public void testKeyframesWithPercentage() { String source = "@keyframes test { 50% { color: blue; } }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); PXKeyframe keyframe = stylesheet.getKeyframeForName("test"); assertNotNull("Expected a 'test' keyframe to be defined in the stylesheet", keyframe); List<PXKeyframeBlock> blocks = keyframe.getBlocks(); assertTrue( String.format("Expected 1 keyframe block but found %d", blocks == null ? 0 : blocks.size()), !CollectionUtil.isEmpty(blocks) && blocks.size() == 1); PXKeyframeBlock block = blocks.get(0); assertTrue(String.format("Expected offset to be 0.5, but found %s", block.getOffset()), block.getOffset() == 0.5f); assertTrue(String.format("Expected 1 declaration but found %d", block.getDeclarations() .size()), block.getDeclarations().size() == 1); } public void testKeyframesWithMultipleBlocks() { String source = "@keyframes test { from { color: red; } 50% { color: blue; } to { color: green; } }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); PXKeyframe keyframe = stylesheet.getKeyframeForName("test"); assertNotNull("Expected a 'test' keyframe to be defined in the stylesheet", keyframe); List<PXKeyframeBlock> blocks = keyframe.getBlocks(); assertTrue( String.format("Expected 3 keyframe block but found %d", blocks == null ? 0 : blocks.size()), !CollectionUtil.isEmpty(blocks) && blocks.size() == 3); PXKeyframeBlock block = blocks.get(0); assertTrue(String.format("Expected offset to be 0.0, but found %s", block.getOffset()), block.getOffset() == 0.0f); assertTrue(String.format("Expected 1 declaration but found %d", block.getDeclarations() .size()), block.getDeclarations().size() == 1); block = blocks.get(1); assertTrue(String.format("Expected offset to be 0.5, but found %s", block.getOffset()), block.getOffset() == 0.5f); assertTrue(String.format("Expected 1 declaration but found %d", block.getDeclarations() .size()), block.getDeclarations().size() == 1); block = blocks.get(2); assertTrue(String.format("Expected offset to be 1.0, but found %s", block.getOffset()), block.getOffset() == 1.0f); assertTrue(String.format("Expected 1 declaration but found %d", block.getDeclarations() .size()), block.getDeclarations().size() == 1); } public void testKeyframesWithMultipleOffsets() { String source = "@keyframes test { from, to { color: red; } }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); PXKeyframe keyframe = stylesheet.getKeyframeForName("test"); assertNotNull("Expected a 'test' keyframe to be defined in the stylesheet", keyframe); List<PXKeyframeBlock> blocks = keyframe.getBlocks(); assertTrue( String.format("Expected 2 keyframe block but found %d", blocks == null ? 0 : blocks.size()), !CollectionUtil.isEmpty(blocks) && blocks.size() == 2); PXKeyframeBlock block = blocks.get(0); assertTrue(String.format("Expected offset to be 0.0, but found %s", block.getOffset()), block.getOffset() == 0.0f); assertTrue(String.format("Expected 1 declaration but found %d", block.getDeclarations() .size()), block.getDeclarations().size() == 1); block = blocks.get(1); assertTrue(String.format("Expected offset to be 1.0, but found %s", block.getOffset()), block.getOffset() == 1.0f); assertTrue(String.format("Expected 1 declaration but found %d", block.getDeclarations() .size()), block.getDeclarations().size() == 1); } // @media Tests public void testMediaQuery() { String source = "@media (orientation:portrait) { button { background-color: red; } }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<PXMediaGroup> mediaGroups = stylesheet.getMediaGroups(); assertTrue("Expected one media group", !CollectionUtil.isEmpty(mediaGroups) && mediaGroups.size() == 1); // check expressions PXMediaGroup mediaGroup = mediaGroups.get(0); PXNamedMediaExpression expression = (PXNamedMediaExpression) mediaGroup.getQuery(); assertTrue("Expected name to be 'orientation'", "orientation".equals(expression.getName())); assertTrue("Expected value to be 'portrait'", "portrait".equals(expression.getValue())); // check rule sets List<PXRuleSet> ruleSets = mediaGroup.getRuleSets(); assertTrue("Expected one rule set", ruleSets.size() == 1); } public void testMediaQueries() { String source = "@media (orientation:portrait) and (min-device-width:100) { button { background-color: red; } }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<PXMediaGroup> mediaGroups = stylesheet.getMediaGroups(); assertTrue("Expected one media group", !CollectionUtil.isEmpty(mediaGroups) && mediaGroups.size() == 1); // check expressions PXMediaGroup mediaGroup = mediaGroups.get(0); PXMediaExpressionGroup expressionGroup = (PXMediaExpressionGroup) mediaGroup.getQuery(); List<PXMediaExpression> expressions = expressionGroup.getExpressions(); assertTrue("Expected two query expressions", !CollectionUtil.isEmpty(expressions) && expressions.size() == 2); PXNamedMediaExpression expression = (PXNamedMediaExpression) expressions.get(0); assertTrue("Expected name to be 'orientation'", "orientation".equals(expression.getName())); assertTrue("Expected value to be 'portrait'", "portrait".equals(expression.getValue())); expression = (PXNamedMediaExpression) expressions.get(1); assertTrue("Expected name to be 'orientation'", "min-device-width".equals(expression.getName())); assertTrue("Expected value to be 100", "100".equals(expression.getValue())); // check rule sets List<PXRuleSet> ruleSets = mediaGroup.getRuleSets(); assertTrue("Expected one rule set", !CollectionUtil.isEmpty(ruleSets) && ruleSets.size() == 1); } public void testNoMediaQuery() { String source = "button { background-color: red; }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<PXMediaGroup> mediaGroups = stylesheet.getMediaGroups(); assertTrue("Expected one media group", !CollectionUtil.isEmpty(mediaGroups) && mediaGroups.size() == 1); // check expressions PXMediaGroup mediaGroup = mediaGroups.get(0); PXNamedMediaExpression expression = (PXNamedMediaExpression) mediaGroup.getQuery(); assertNull("Expected the media group query to be null", expression); // check rule sets List<PXRuleSet> ruleSets = mediaGroup.getRuleSets(); assertTrue("Expected one rule set", !CollectionUtil.isEmpty(ruleSets) && ruleSets.size() == 1); } public void testMediaWithLeadingAnd() { String source = "@media screen and (min-width: 768px) { button { color: red; } }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<String> errors = parser.getErrors(); assertTrue("Unexpected parse error", CollectionUtil.isEmpty(errors)); List<PXMediaGroup> mediaGroups = stylesheet.getMediaGroups(); assertTrue(String.format("Expected 1 media group, found %d", mediaGroups == null ? 0 : mediaGroups.size()), !CollectionUtil.isEmpty(mediaGroups) && mediaGroups.size() == 1); } public void testMediaWithRatio() { String source = "@media (device-aspect-ratio: 4/5) { button { color: red; } }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); List<PXMediaGroup> mediaGroups = stylesheet.getMediaGroups(); assertTrue("Expected one media group, got " + (mediaGroups == null ? 0 : mediaGroups.size()), !CollectionUtil.isEmpty(mediaGroups) && mediaGroups.size() == 1); // check expressions PXMediaGroup mediaGroup = mediaGroups.get(0); PXNamedMediaExpression expression = (PXNamedMediaExpression) mediaGroup.getQuery(); Object value = expression.getValue(); assertTrue("Expected device aspect ratio to be an NSNumber", value instanceof Number); Number number = (Number) value; assertEquals( String.format("Expected device aspect ratio to be 0.8, but was %f", number.floatValue()), 0.8f, number.floatValue()); // check rule sets List<PXRuleSet> ruleSets = mediaGroup.getRuleSets(); assertTrue("Expected one rule set", !CollectionUtil.isEmpty(ruleSets) && ruleSets.size() == 1); } // inline CSS Tests public void testInlineCssWithHexColor() { String source = "text: hello; size: 100 50; color: red; border-radius: 10; border-width: 1px; background-color: linear-gradient(red, #d97410);"; PXStylesheet stylesheet = parser.parseInlineCSS(source); List<String> errors = parser.getErrors(); assertTrue(String.format("Unexpected parse error: %d", errors == null ? 0 : errors.size()), CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected one rule set", !CollectionUtil.isEmpty(ruleSets) && ruleSets.size() == 1); PXRuleSet ruleSet = ruleSets.get(0); PXDeclaration backgroundColor = ruleSet.getDeclarationForName("background-color"); assertNotNull("Expected a 'background-color' declaration", backgroundColor); PXPaint paint = backgroundColor.getPaintValue(); assertNotNull("Expected a paint value", paint); assertTrue(String.format("Expected 'PXLinearGradient' class but found %s", paint.getClass() .getSimpleName()), paint instanceof PXLinearGradient); } // Performance Tests public void testLargeCSS() { float start = System.currentTimeMillis(); PXStylesheet stylesheet = PXStylesheet.getStyleSheetFromFilePath(getContext(), "stylesheetParsing/large.css", PXStyleSheetOrigin.APPLICATION); float diff = System.currentTimeMillis() - start; assertNotNull("Expected a stylesheet", stylesheet); Log.i(PXStylesheetParserTests.class.getSimpleName(), String.format("Elapsed time = %f", diff * 1000)); } // Bug Fixes public void testCrashWithHexPaint() { String source = "button { background-color: #D4D4D; }"; PXStylesheet stylesheet = parser.parse(source, PXStyleSheetOrigin.APPLICATION); // NOTE: If we get this far, we didn't crash :) List<String> errors = parser.getErrors(); assertTrue(String.format("Unexpected parse error: %d", errors == null ? 0 : errors.size()), CollectionUtil.isEmpty(errors)); List<PXRuleSet> ruleSets = stylesheet.getRuleSets(getContext()); assertTrue("Expected one rule set", ruleSets.size() == 1); } public void testCrashOnImport() { PXStylesheet stylesheet = PXStylesheet.getStyleSheetFromFilePath(getContext(), "stylesheetParsing/crashOnImport.css", PXStyleSheetOrigin.APPLICATION); assertNotNull("Expected a stylesheet after parsing crashOnImport.css", stylesheet); } }