/* * Copyright 2009 Google 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.google.common.css.compiler.ast; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import com.google.common.collect.Lists; import com.google.common.css.SourceCode; import com.google.common.css.compiler.ast.CssAttributeSelectorNode.MatchType; import com.google.common.css.compiler.ast.DefaultVisitController.RootVisitAfterChildrenState; import com.google.common.css.compiler.ast.DefaultVisitController.RootVisitBeforeChildrenState; import com.google.common.css.compiler.ast.DefaultVisitController.RootVisitBodyState; import com.google.common.css.compiler.ast.DefaultVisitController.RootVisitCharsetState; import com.google.common.css.compiler.ast.DefaultVisitController.RootVisitImportBlockState; import com.google.common.css.compiler.ast.DefaultVisitController.VisitBlockChildrenState; import com.google.common.css.compiler.ast.DefaultVisitController.VisitDefinitionState; import com.google.common.css.compiler.ast.DefaultVisitController.VisitImportBlockChildrenState; import com.google.common.css.compiler.ast.DefaultVisitController.VisitImportRuleState; import com.google.common.css.compiler.ast.DefaultVisitController.VisitReplaceChildrenState; import junit.framework.TestCase; import org.easymock.EasyMock; import org.easymock.IMocksControl; import java.util.List; /** * Unit tests for {@link DefaultVisitController}. * * TODO(oana): Add more unit tests. * * @author oana@google.com (Oana Florescu) */ public class DefaultVisitControllerTest extends TestCase { public void testConstructor() { DefaultVisitController visitController = new DefaultVisitController( new CssTree((SourceCode) null), false); assertTrue(visitController.getStateStack().isEmpty()); } public void testVisitBlock() { CssLiteralNode literal = new CssLiteralNode(""); CssDefinitionNode def = new CssDefinitionNode(literal); CssBlockNode block = new CssBlockNode(false); block.addChildToBack(def); CssRootNode root = new CssRootNode(block); CssTree tree = new CssTree(null, root); IMocksControl mockControl = EasyMock.createStrictControl(); DefaultTreeVisitor testVisitor = mockControl.createMock(DefaultTreeVisitor.class); // Enter Tree gets the root - there is no enterRoot. expect(testVisitor.enterTree(eq(root))).andReturn(true); // There are blocks that get created even if you don't add them. expect(testVisitor.enterImportBlock((CssImportBlockNode)anyObject())).andReturn(true); testVisitor.leaveImportBlock((CssImportBlockNode)anyObject()); // Then we enter the block. expect(testVisitor.enterBlock(eq(block))).andReturn(true); // Then another node that we created. expect(testVisitor.enterDefinition(eq(def))).andReturn(true); // Exit the nodes. testVisitor.leaveDefinition(eq(def)); testVisitor.leaveBlock(eq(block)); testVisitor.leaveTree(eq(root)); mockControl.replay(); DefaultVisitController controller = new DefaultVisitController(tree, true); controller.startVisit(testVisitor); mockControl.verify(); } public void testVisitProperties() { CssValueNode first = new CssLiteralNode("one", null); CssValueNode second = new CssLiteralNode("two", null); CssValueNode third = new CssLiteralNode("three", null); CssPropertyNode propName = new CssPropertyNode("prop"); CssPropertyValueNode propValue = new CssPropertyValueNode(); propValue.addChildToBack(first); propValue.addChildToBack(second); propValue.addChildToBack(third); CssDeclarationNode decl = new CssDeclarationNode(propName, propValue); CssDeclarationBlockNode dBlock = new CssDeclarationBlockNode(); dBlock.addChildToBack(decl); CssClassSelectorNode classSelector = new CssClassSelectorNode("foo", null); CssIdSelectorNode idSelector = new CssIdSelectorNode("bar", null); CssPseudoClassNode pseudoClass = new CssPseudoClassNode("foo", null); CssPseudoElementNode pseudoElement = new CssPseudoElementNode("bar", null); CssAttributeSelectorNode attrSelector = new CssAttributeSelectorNode( MatchType.EXACT, "hreflang", new CssStringNode( CssStringNode.Type.DOUBLE_QUOTED_STRING, "en"), null); CssSelectorNode selector = new CssSelectorNode("name", null); selector.getRefiners().addChildToBack(classSelector); selector.getRefiners().addChildToBack(idSelector); selector.getRefiners().addChildToBack(pseudoClass); selector.getRefiners().addChildToBack(pseudoElement); selector.getRefiners().addChildToBack(attrSelector); CssRulesetNode ruleset = new CssRulesetNode(dBlock); ruleset.addSelector(selector); CssBlockNode block = new CssBlockNode(false); block.addChildToBack(ruleset); CssRootNode root = new CssRootNode(block); CssTree tree = new CssTree(null, root); IMocksControl mockControl = EasyMock.createStrictControl(); DefaultTreeVisitor testVisitor = mockControl.createMock(DefaultTreeVisitor.class); // After all of that to get to a multi-valued property list, we // visit the tree and check what we see. expect(testVisitor.enterTree(eq(root))).andReturn(true); expect(testVisitor.enterImportBlock((CssImportBlockNode)anyObject())).andReturn(true); testVisitor.leaveImportBlock((CssImportBlockNode)anyObject()); expect(testVisitor.enterBlock(eq(block))).andReturn(true); expect(testVisitor.enterRuleset(eq(ruleset))).andReturn(true); expect(testVisitor.enterSelectorBlock((CssSelectorListNode) anyObject())).andReturn(true); expect(testVisitor.enterSelector(eq(selector))).andReturn(true); expect(testVisitor.enterClassSelector(eq(classSelector))).andReturn(true); testVisitor.leaveClassSelector(eq(classSelector)); expect(testVisitor.enterIdSelector(eq(idSelector))).andReturn(true); testVisitor.leaveIdSelector(eq(idSelector)); expect(testVisitor.enterPseudoClass(eq(pseudoClass))).andReturn(true); testVisitor.leavePseudoClass(eq(pseudoClass)); expect(testVisitor.enterPseudoElement(eq(pseudoElement))).andReturn(true); testVisitor.leavePseudoElement(eq(pseudoElement)); expect(testVisitor.enterAttributeSelector(eq(attrSelector))).andReturn(true); testVisitor.leaveAttributeSelector(eq(attrSelector)); testVisitor.leaveSelector(eq(selector)); testVisitor.leaveSelectorBlock((CssSelectorListNode) anyObject()); expect(testVisitor.enterDeclarationBlock(eq(dBlock))).andReturn(true); expect(testVisitor.enterDeclaration(eq(decl))).andReturn(true); expect(testVisitor.enterPropertyValue(eq(propValue))).andReturn(true); expect(testVisitor.enterValueNode(eq(first))).andReturn(true); testVisitor.leaveValueNode(eq(first)); expect(testVisitor.enterValueNode(eq(second))).andReturn(true); testVisitor.leaveValueNode(eq(second)); expect(testVisitor.enterValueNode(eq(third))).andReturn(true); testVisitor.leaveValueNode(eq(third)); testVisitor.leavePropertyValue(eq(propValue)); testVisitor.leaveDeclaration(eq(decl)); testVisitor.leaveDeclarationBlock(eq(dBlock)); testVisitor.leaveRuleset(eq(ruleset)); testVisitor.leaveBlock(eq(block)); testVisitor.leaveTree(eq(root)); mockControl.replay(); DefaultVisitController controller = new DefaultVisitController(tree, true); controller.startVisit(testVisitor); mockControl.verify(); } public void testStateStack() { CssTree tree = new CssTree((SourceCode) null); DefaultVisitController visitController = new DefaultVisitController( tree, false); RootVisitBeforeChildrenState state = visitController.new RootVisitBeforeChildrenState(tree.getRoot()); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); assertEquals(1, visitController.getStateStack().size()); visitController.getStateStack().pop(); assertTrue(visitController.getStateStack().isEmpty()); } public void testRootVisitBeforeChildrenState() { CssTree tree = new CssTree((SourceCode) null); DefaultVisitController visitController = new DefaultVisitController( tree, false); RootVisitBeforeChildrenState state = visitController.new RootVisitBeforeChildrenState(tree.getRoot()); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); state.transitionToNextState(); assertEquals(1, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof RootVisitCharsetState); } public void testRootVisitCharsetState() { CssTree tree = new CssTree((SourceCode) null); DefaultVisitController visitController = new DefaultVisitController( tree, true); RootVisitCharsetState state = visitController.new RootVisitCharsetState(tree.getRoot(), tree.getRoot().getCharsetRule()); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); visitController.removeCurrentNode(); assertNull(tree.getRoot().getCharsetRule()); state.transitionToNextState(); assertEquals(1, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof RootVisitImportBlockState); } public void testRootVisitImportBlockState() { CssTree tree = new CssTree((SourceCode) null); DefaultVisitController visitController = new DefaultVisitController( tree, true); RootVisitImportBlockState state = visitController.new RootVisitImportBlockState(tree.getRoot(), tree.getRoot().getImportRules()); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); state.transitionToNextState(); assertEquals(2, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof VisitImportBlockChildrenState); state.transitionToNextState(); assertEquals(2, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof RootVisitBodyState); } public void testVisitImportBlockChildrenState() { CssTree tree = new CssTree((SourceCode) null); DefaultVisitController visitController = new DefaultVisitController( tree, true); visitController.visitor = new DefaultTreeVisitor(); CssImportBlockNode cssImportBlockNode = new CssImportBlockNode(); cssImportBlockNode.setChildren(Lists.newArrayList(new CssImportRuleNode())); VisitImportBlockChildrenState state = visitController.new VisitImportBlockChildrenState(cssImportBlockNode); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); state.transitionToNextState(); assertEquals(2, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof VisitImportRuleState); visitController.getStateStack().getTop().transitionToNextState(); assertEquals(1, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof VisitImportBlockChildrenState); state.transitionToNextState(); assertTrue(visitController.getStateStack().isEmpty()); } public void testVisitImportRuleState() { CssTree tree = new CssTree((SourceCode) null); DefaultVisitController visitController = new DefaultVisitController( tree, true); visitController.visitor = new DefaultTreeVisitor(); VisitImportRuleState state = visitController.new VisitImportRuleState(new CssImportRuleNode()); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); state.transitionToNextState(); assertTrue(visitController.getStateStack().isEmpty()); } public void testRootVisitBodyState() { CssTree tree = new CssTree((SourceCode) null); DefaultVisitController visitController = new DefaultVisitController( tree, true); visitController.visitor = new DefaultTreeVisitor(); RootVisitBodyState state = visitController.new RootVisitBodyState(tree.getRoot(), tree.getRoot().getBody()); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); state.transitionToNextState(); assertEquals(2, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof VisitBlockChildrenState); visitController.getStateStack().getTop().transitionToNextState(); assertEquals(1, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof RootVisitBodyState); state.transitionToNextState(); assertEquals(1, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof RootVisitAfterChildrenState); } public void testVisitBlockChildrenState1() { CssDefinitionNode def = new CssDefinitionNode(new CssLiteralNode("")); CssBlockNode block = new CssBlockNode(false); block.addChildToBack(def); CssRootNode root = new CssRootNode(block); CssTree tree = new CssTree(null, root); DefaultVisitController visitController = new DefaultVisitController( tree, true); visitController.visitor = new DefaultTreeVisitor(); VisitReplaceChildrenState<CssNode> state = visitController.new VisitBlockChildrenState(tree.getRoot().getBody()); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); assertEquals(-1, state.currentIndex); state.transitionToNextState(); assertEquals(2, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof VisitDefinitionState); assertEquals(0, state.currentIndex); visitController.getStateStack().getTop().transitionToNextState(); assertEquals(3, visitController.getStateStack().size()); state.transitionToNextState(); state.transitionToNextState(); state.transitionToNextState(); assertTrue(visitController.getStateStack().isEmpty()); } public void testVisitBlockChildrenState2() { CssDefinitionNode def = new CssDefinitionNode(new CssLiteralNode("")); CssBlockNode block = new CssBlockNode(false); block.addChildToBack(def); def = new CssDefinitionNode(new CssLiteralNode("")); block.addChildToBack(def); CssRootNode root = new CssRootNode(block); CssTree tree = new CssTree(null, root); DefaultVisitController visitController = new DefaultVisitController( tree, true); visitController.visitor = new DefaultTreeVisitor(); VisitReplaceChildrenState<CssNode> state = visitController.new VisitBlockChildrenState(tree.getRoot().getBody()); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); assertEquals(-1, state.currentIndex); state.transitionToNextState(); assertEquals(2, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof VisitDefinitionState); assertEquals(0, state.currentIndex); state.removeCurrentChild(); assertEquals(0, state.currentIndex); state.removeCurrentChild(); assertEquals(0, state.currentIndex); visitController.getStateStack().getTop().transitionToNextState(); assertEquals(3, visitController.getStateStack().size()); state.transitionToNextState(); state.transitionToNextState(); state.transitionToNextState(); assertTrue(visitController.getStateStack().isEmpty()); assertEquals(0, state.currentIndex); } public void testVisitBlockChildrenState3() { CssDefinitionNode def = new CssDefinitionNode(new CssLiteralNode("")); CssBlockNode block = new CssBlockNode(false); block.addChildToBack(def); def = new CssDefinitionNode(new CssLiteralNode("")); block.addChildToBack(def); CssRootNode root = new CssRootNode(block); CssTree tree = new CssTree(null, root); DefaultVisitController visitController = new DefaultVisitController( tree, true); visitController.visitor = new DefaultTreeVisitor(); VisitReplaceChildrenState<CssNode> state = visitController.new VisitBlockChildrenState(tree.getRoot().getBody()); visitController.getStateStack().push(state); assertEquals(state, visitController.getStateStack().getTop()); assertEquals(-1, state.currentIndex); state.transitionToNextState(); assertEquals(2, visitController.getStateStack().size()); assertTrue(visitController.getStateStack() .getTop() instanceof VisitDefinitionState); assertEquals(0, state.currentIndex); state.replaceCurrentBlockChildWith( Lists.<CssNode>newArrayList( new CssDefinitionNode(new CssLiteralNode("")), new CssDefinitionNode(new CssLiteralNode(""))), true); assertEquals(0, state.currentIndex); state.removeCurrentChild(); assertEquals(0, state.currentIndex); visitController.getStateStack().getTop().transitionToNextState(); assertEquals(3, visitController.getStateStack().size()); } public void testVisitSimpleUnknownAtRule() { CssLiteralNode defLit = new CssLiteralNode("def"); CssUnknownAtRuleNode atDef = new CssUnknownAtRuleNode(defLit, false); CssLiteralNode xLit = new CssLiteralNode("x"); CssLiteralNode yLit = new CssLiteralNode("y"); List<CssValueNode> defParameters = Lists.newArrayList((CssValueNode) xLit, (CssValueNode) yLit); atDef.setParameters(defParameters); CssBlockNode block = new CssBlockNode(false); block.addChildToBack(atDef); CssRootNode root = new CssRootNode(block); CssTree tree = new CssTree(null, root); IMocksControl mockControl = EasyMock.createStrictControl(); DefaultTreeVisitor testVisitor = mockControl.createMock(DefaultTreeVisitor.class); // Enter Tree gets the root - there is no enterRoot. expect(testVisitor.enterTree(eq(root))).andReturn(true); // There are blocks that get created even if you don't add them. expect(testVisitor.enterImportBlock((CssImportBlockNode)anyObject())).andReturn(true); testVisitor.leaveImportBlock((CssImportBlockNode)anyObject()); // Then we enter the block. expect(testVisitor.enterBlock(eq(block))).andReturn(true); // Then we enter the unknown at-rule node that we created. expect(testVisitor.enterUnknownAtRule(eq(atDef))).andReturn(true); // Then the media type list params for (int i = 0; i < defParameters.size(); ++i) { expect( testVisitor.enterValueNode(eq(defParameters.get(i)))).andReturn(true); testVisitor.leaveValueNode(eq(defParameters.get(i))); if (i < defParameters.size() - 1) { expect(testVisitor.enterMediaTypeListDelimiter(eq(atDef))) .andReturn(true); testVisitor.leaveMediaTypeListDelimiter(eq(atDef)); } } // We've got no block associated with this at rule. // Exit the nodes. testVisitor.leaveUnknownAtRule(eq(atDef)); testVisitor.leaveBlock(eq(block)); testVisitor.leaveTree(eq(root)); mockControl.replay(); DefaultVisitController controller = new DefaultVisitController(tree, true); controller.startVisit(testVisitor); mockControl.verify(); } public void testVisitComplexUnknownAtRule() { CssLiteralNode defLit = new CssLiteralNode("def"); CssUnknownAtRuleNode atDef = new CssUnknownAtRuleNode(defLit, false); CssLiteralNode xLit = new CssLiteralNode("x"); CssLiteralNode yLit = new CssLiteralNode("y"); List<CssValueNode> defParameters = Lists.newArrayList((CssValueNode) xLit, (CssValueNode) yLit); atDef.setParameters(defParameters); CssBlockNode defBlock = new CssBlockNode(true); defBlock.addChildToBack(atDef); CssLiteralNode ifLit = new CssLiteralNode("if"); CssUnknownAtRuleNode atIf = new CssUnknownAtRuleNode(ifLit, true); CssBooleanExpressionNode ifCondition = new CssBooleanExpressionNode( CssBooleanExpressionNode.Type.CONSTANT, "some condition", null, null); List<CssValueNode> ifParameters = Lists.newArrayList((CssValueNode) ifCondition); atIf.setParameters(ifParameters); atIf.setBlock(defBlock); CssBlockNode block = new CssBlockNode(false); block.addChildToBack(atIf); CssRootNode root = new CssRootNode(block); CssTree tree = new CssTree(null, root); IMocksControl mockControl = EasyMock.createStrictControl(); DefaultTreeVisitor testVisitor = mockControl.createMock(DefaultTreeVisitor.class); // Enter Tree gets the root - there is no enterRoot. expect(testVisitor.enterTree(eq(root))).andReturn(true); // There are blocks that get created even if you don't add them. expect(testVisitor.enterImportBlock((CssImportBlockNode)anyObject())).andReturn(true); testVisitor.leaveImportBlock((CssImportBlockNode)anyObject()); // Then we enter the block. expect(testVisitor.enterBlock(eq(block))).andReturn(true); // Then we enter the unknown 'if' at-rule node that we created. expect(testVisitor.enterUnknownAtRule(eq(atIf))).andReturn(true); // Then the media type list params for 'if' for (int i = 0; i < ifParameters.size(); ++i) { expect( testVisitor.enterValueNode(eq(ifParameters.get(i)))).andReturn(true); testVisitor.leaveValueNode(eq(ifParameters.get(i))); if (i < ifParameters.size() - 1) { expect(testVisitor.enterMediaTypeListDelimiter(eq(atIf))) .andReturn(true); testVisitor.leaveMediaTypeListDelimiter(eq(atIf)); } } // Then we enter the defBlock. expect(testVisitor.enterBlock(eq(defBlock))).andReturn(true); // Then we enter the unknown 'def' at-rule node within the 'if'. expect(testVisitor.enterUnknownAtRule(eq(atDef))).andReturn(true); // Then the media type list params for 'def' for (int i = 0; i < defParameters.size(); ++i) { expect( testVisitor.enterValueNode(eq(defParameters.get(i)))).andReturn(true); testVisitor.leaveValueNode(eq(defParameters.get(i))); if (i < defParameters.size() - 1) { expect(testVisitor.enterMediaTypeListDelimiter(eq(atDef))) .andReturn(true); testVisitor.leaveMediaTypeListDelimiter(eq(atDef)); } } // Exit the nodes. testVisitor.leaveUnknownAtRule(eq(atDef)); testVisitor.leaveBlock(eq(defBlock)); testVisitor.leaveUnknownAtRule(eq(atIf)); testVisitor.leaveBlock(eq(block)); testVisitor.leaveTree(eq(root)); mockControl.replay(); DefaultVisitController controller = new DefaultVisitController(tree, true); controller.startVisit(testVisitor); mockControl.verify(); } public void testVisitComponent() { CssLiteralNode x = new CssLiteralNode("FOO"); CssDefinitionNode def = new CssDefinitionNode(x); CssBlockNode compBlock = new CssBlockNode(true); compBlock.addChildToBack(def); CssLiteralNode compLit = new CssLiteralNode("CSS_BAR"); CssComponentNode comp = new CssComponentNode(compLit, null, false, CssComponentNode.PrefixStyle.LITERAL, compBlock); CssBlockNode block = new CssBlockNode(false); block.addChildToBack(comp); CssRootNode root = new CssRootNode(block); CssTree tree = new CssTree(null, root); IMocksControl mockControl = EasyMock.createStrictControl(); DefaultTreeVisitor testVisitor = mockControl.createMock(DefaultTreeVisitor.class); // Enter Tree gets the root - there is no enterRoot. expect(testVisitor.enterTree(eq(root))).andReturn(true); // There are blocks that get created even if you don't add them. expect(testVisitor.enterImportBlock((CssImportBlockNode)anyObject())).andReturn(true); testVisitor.leaveImportBlock((CssImportBlockNode)anyObject()); // Then we enter the block. expect(testVisitor.enterBlock(eq(block))).andReturn(true); // Then we enter the component that we created. expect(testVisitor.enterComponent(eq(comp))).andReturn(true); // Then we enter the definition within the component. expect(testVisitor.enterDefinition(eq(def))).andReturn(true); // Exit the nodes. testVisitor.leaveDefinition(eq(def)); testVisitor.leaveComponent(eq(comp)); testVisitor.leaveBlock(eq(block)); testVisitor.leaveTree(eq(root)); mockControl.replay(); DefaultVisitController controller = new DefaultVisitController(tree, true); controller.startVisit(testVisitor); mockControl.verify(); } public void testVisitValueNodes() { List<CssValueNode> simpleValues = Lists.newLinkedList(); for (String v : new String [] {"a", "b", "c"}) { simpleValues.add(new CssLiteralNode(v, null)); } CssCompositeValueNode parent = new CssCompositeValueNode( simpleValues, CssCompositeValueNode.Operator.COMMA, null); CssPropertyValueNode propValue = new CssPropertyValueNode(); propValue.addChildToBack(parent); CssDeclarationNode decl = new CssDeclarationNode( new CssPropertyNode("prop"), propValue); CssDeclarationBlockNode db = new CssDeclarationBlockNode(); db.addChildToBack(decl); CssRulesetNode ruleset = new CssRulesetNode(db); ruleset.addSelector(new CssSelectorNode("name", null)); CssBlockNode b = new CssBlockNode(false); b.addChildToBack(ruleset); CssTree t = new CssTree(null, new CssRootNode(b)); final List<CssValueNode> cNodes = Lists.newLinkedList(); final List<CssValueNode> evnNodes = Lists.newLinkedList(); DefaultTreeVisitor testVisitor = new DefaultTreeVisitor() { @Override public boolean enterCompositeValueNode(CssCompositeValueNode c) { cNodes.add(c); return true; } @Override public boolean enterValueNode(CssValueNode n) { evnNodes.add(n); return true; } }; DefaultVisitController controller = new DefaultVisitController(t, true); controller.startVisit(testVisitor); assertEquals(simpleValues.size(), evnNodes.size()); for (CssValueNode i : simpleValues) { assertTrue(evnNodes.contains(i)); } assertEquals(1, cNodes.size()); assertTrue(cNodes.contains(parent)); } public void testVisitCompositeValueNodeWithFunction() { List<CssValueNode> simpleValues = Lists.newLinkedList(); simpleValues.add( new CssFunctionNode(CssFunctionNode.Function.byName("url"), null)); simpleValues.add( new CssFunctionNode(CssFunctionNode.Function.byName("url"), null)); CssCompositeValueNode parent = new CssCompositeValueNode( simpleValues, CssCompositeValueNode.Operator.COMMA, null); CssPropertyValueNode propValue = new CssPropertyValueNode(); propValue.addChildToBack(parent); CssDeclarationNode decl = new CssDeclarationNode( new CssPropertyNode("prop"), propValue); CssDeclarationBlockNode db = new CssDeclarationBlockNode(); db.addChildToBack(decl); CssRulesetNode ruleset = new CssRulesetNode(db); ruleset.addSelector(new CssSelectorNode("name", null)); CssBlockNode b = new CssBlockNode(false); b.addChildToBack(ruleset); CssTree t = new CssTree(null, new CssRootNode(b)); final List<CssValueNode> compositeNode = Lists.newLinkedList(); final List<CssValueNode> functionNodes = Lists.newLinkedList(); DefaultTreeVisitor testVisitor = new DefaultTreeVisitor() { @Override public boolean enterCompositeValueNode(CssCompositeValueNode c) { compositeNode.add(c); return true; } @Override public boolean enterFunctionNode(CssFunctionNode n) { functionNodes.add(n); return true; } }; DefaultVisitController controller = new DefaultVisitController(t, true); controller.startVisit(testVisitor); assertEquals(2, functionNodes.size()); assertEquals(1, compositeNode.size()); assertTrue(compositeNode.contains(parent)); } private static class ValueDetector extends DefaultTreeVisitor { private final String quarry; private final boolean[] foundValue = {false}; @Override public boolean enterValueNode(CssValueNode node) { if (quarry.equals(node.getValue())) { foundValue[0] = true; } return true; } private ValueDetector(String quarry) { this.quarry = quarry; } public static boolean detect(CssTree t, String quarry) { ValueDetector detector = new ValueDetector(quarry); new DefaultVisitController( t, false /* allowMutating */).startVisit(detector); return detector.foundValue[0]; } } private static class FunctionDetector extends DefaultTreeVisitor { private final boolean[] foundValue = {false}; @Override public boolean enterFunctionNode(CssFunctionNode node) { foundValue[0] = true; return true; } public static boolean detect(CssTree t) { FunctionDetector detector = new FunctionDetector(); new DefaultVisitController( t, false /* allowMutating */).startVisit(detector); return detector.foundValue[0]; } } public void verifyRemoveablePropertyValueElement(String backgroundValue) { try { CssTree t = new GssParser( new com.google.common.css.SourceCode(null, String.format("p { background: %s; }", backgroundValue))) .parse(); assertTrue( "This test assumes we start with a stylesheet containing detectable " + "function nodes.", FunctionDetector.detect(t)); final DefaultVisitController vc = new DefaultVisitController(t, true /* allowMutating */); CssTreeVisitor functionRemover = new DefaultTreeVisitor() { @Override public boolean enterFunctionNode(CssFunctionNode node) { System.err.println(node.getParent().getClass().getName()); vc.removeCurrentNode(); return true; } }; vc.startVisit(functionRemover); assertFalse( "We should be able to remove function nodes that occur as property " + "values.", FunctionDetector.detect(t)); assertTrue( "Removing one composite element within a property value should not " + "affect its siblings.", ValueDetector.detect(t, "red")); assertTrue( "Removing one composite element within a property value should not " + "affect its siblings.", ValueDetector.detect(t, "fixed")); } catch (GssParserException e) { throw new RuntimeException(e); } } public void testRemoveCompositePropertyValueElement() { verifyRemoveablePropertyValueElement( "url(http://www.google.com/logo), red fixed"); } public void testRemoveCompositePropertyValueElementMiddle() { verifyRemoveablePropertyValueElement( "red, url(http://www.google.com/logo), fixed"); } public void testRemoveCompositePropertyValueElementEnd() { verifyRemoveablePropertyValueElement( "red fixed, url(http://www.google.com/logo)"); } public void testVisitForLoop() { CssLiteralNode x = new CssLiteralNode("FOO"); CssDefinitionNode def = new CssDefinitionNode(x); CssBlockNode loopBlock = new CssBlockNode(true); loopBlock.addChildToBack(def); CssValueNode from = new CssNumericNode("1", CssNumericNode.NO_UNITS); CssValueNode to = new CssNumericNode("5", CssNumericNode.NO_UNITS); CssValueNode step = new CssNumericNode("2", CssNumericNode.NO_UNITS); CssLiteralNode variableNode = new CssLiteralNode("for"); CssForLoopRuleNode loop = new CssForLoopRuleNode( variableNode, loopBlock, null, from, to, step, "i", 0, null); CssBlockNode block = new CssBlockNode(false); block.addChildToBack(loop); CssRootNode root = new CssRootNode(block); CssTree tree = new CssTree(null, root); IMocksControl mockControl = EasyMock.createStrictControl(); DefaultTreeVisitor testVisitor = mockControl.createMock(DefaultTreeVisitor.class); // Enter Tree gets the root - there is no enterRoot. expect(testVisitor.enterTree(eq(root))).andReturn(true); // There are blocks that get created even if you don't add them. expect(testVisitor.enterImportBlock((CssImportBlockNode) anyObject())).andReturn(true); testVisitor.leaveImportBlock((CssImportBlockNode) anyObject()); // Then we enter the block. expect(testVisitor.enterBlock(eq(block))).andReturn(true); // Then we enter the for loop node. expect(testVisitor.enterForLoop(eq(loop))).andReturn(true); // Then we enter the definition within the for loop. expect(testVisitor.enterDefinition(eq(def))).andReturn(true); // Exit the nodes. testVisitor.leaveDefinition(eq(def)); testVisitor.leaveForLoop(eq(loop)); testVisitor.leaveBlock(eq(block)); testVisitor.leaveTree(eq(root)); mockControl.replay(); DefaultVisitController controller = new DefaultVisitController(tree, true); controller.startVisit(testVisitor); mockControl.verify(); } }