/* * 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.passes; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import com.google.common.collect.Sets; import com.google.common.css.compiler.ast.CssDeclarationNode; import com.google.common.css.compiler.ast.CssRulesetNode; import com.google.common.css.compiler.ast.CssTree; import com.google.common.css.compiler.ast.CssTree.RulesetNodesToRemove; import com.google.common.css.compiler.ast.VisitController; import com.google.common.css.compiler.passes.testing.PassesTestBase; import java.util.Iterator; import java.util.Set; /** * Unit tests for {@link MarkRemovableRulesetNodes}. * * @author oana@google.com (Oana Florescu) */ public class MarkRemovableRulesetNodesTest extends PassesTestBase { public void testRunPass() { VisitController visitController = createMock( VisitController.class); tree = createMock(CssTree.class); expect(tree.getVisitController()).andReturn(visitController) .anyTimes(); replay(tree); MarkRemovableRulesetNodes pass = new MarkRemovableRulesetNodes(tree); visitController.startVisit(pass); replay(visitController); pass.runPass(); verify(visitController); } public void testMarkRemovableRulesetNode1() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " border: 2px;", "}", ".CSS_RULE {", " border: 3px;", "}"), null, null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_RULE]{[border:[[2px]];]}]", rule); } public void testMarkRemovableRulesetNode2() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " display: inline-box;", "}", ".CSS_RULE {", " display: -moz-inline-box;", "}"), null, null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertTrue(rules.getRulesetNodes().isEmpty()); } public void testMarkRemovableRulesetNode3() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE_1 {", " border: 2px;", "}", ".CSS_RULE_2 {", " margin: 3px;", "}"), Sets.newHashSet("CSS_RULE_1"), "CSS_"); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_RULE_2]{[margin:[[3px]];]}]", rule); } public void testMarkRemovableRulesetNode4() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE_1 .CSS_RULE_3 {", " border: 2px;", "}", ".CSS_RULE_2 {", " margin: 3px;", "}"), Sets.newHashSet("CSS_RULE_1"), "CSS_"); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(2, rules.getRulesetNodes().size()); Iterator<CssRulesetNode> iter = rules.getRulesetNodes().iterator(); CssRulesetNode rule = iter.next(); checkRuleset("[[.CSS_RULE_2]{[margin:[[3px]];]}]", rule); rule = iter.next(); checkRuleset("[[.CSS_RULE_1 .CSS_RULE_3]{[border:[[2px]];]}]", rule); } public void testMarkRemovableRulesetNode5() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE_1 .CSS_RULE_3 {", " border: 2px;", "}", ".CSS_RULE_2 {", " margin: 3px;", "}"), Sets.newHashSet("CSS_RULE_1", "CSS_RULE_3"), "CSS_"); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_RULE_2]{[margin:[[3px]];]}]", rule); } public void testMarkRemovableRulesetNode6() { collectRemovableRulesetNodes( linesToString( "div {", " border: 2px;", "}", ".CSS_RULE_2 {", " margin: 3px;", "}"), Sets.newHashSet("CSS_RULE_2"), "CSS_"); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertTrue(rules.getRulesetNodes().isEmpty()); } public void testMarkRemovableRulesetNode7() { collectRemovableRulesetNodes( linesToString( "div .CSS_RULE_1 {", " border: 2px;", "}", ".CSS_RULE_2 {", " margin: 3px;", "}"), Sets.newHashSet("CSS_RULE_2"), "CSS_"); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[div .CSS_RULE_1]{[border:[[2px]];]}]", rule); } public void testMarkRemovableRulesetNode8() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE_1 div {", " border: 2px;", "}", ".CSS_RULE_2 {", " margin: 3px;", "}"), Sets.newHashSet("CSS_RULE_2"), "CSS_"); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_RULE_1 div]{[border:[[2px]];]}]", rule); } public void testMarkRemovableRulesetNode9() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " border: 2px;", "}", ".CSS_RULE {", " border: 3px;", "}"), Sets.<String>newHashSet(), null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_RULE]{[border:[[2px]];]}]", rule); } public void testMarkRemovableRulesetNode10() { collectRemovableRulesetNodes( linesToString( "div {", " border: 2px;", "}", "html.CSS_RULE {", " border: 3px;", "}"), Sets.newHashSet(".CSS_RULE_1"), "CSS_"); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[html.CSS_RULE]{[border:[[3px]];]}]", rule); } public void testMarkRemovableRulesetNode11() { collectRemovableRulesetNodes( linesToString( "selector1 {", " *height : 11px;", "}", "selector2 {", " *height : 12px;", "}", "selector1 {", " *height : 13px;", "}"), Sets.newHashSet("selector1", "selector2"), null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[selector1]{[*height:[[11px]];]}]", rule); } public void testMarkRemovableRulesetNode12() { collectRemovableRulesetNodes( linesToString( "selector1 {", " *foo : 11px;", "}", "selector1 {", " foo : 13px;", "}"), Sets.newHashSet("selector1"), null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertTrue(rules.getRulesetNodes().isEmpty()); } public void testMarkRemovableRulesetNode13() { collectRemovableRulesetNodes( linesToString( "selector1 {", " foo : 11px;", "}", "selector1 {", " *foo : 13px;", "}", "selector2 {", " *foo : 15px;", "}"), Sets.newHashSet("selector1", "selector2"), null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertTrue(rules.getRulesetNodes().isEmpty()); } public void testMarkRemovableRulesetNode14() { collectRemovableRulesetNodes( linesToString( "selector {", " foo : 10px;", "}", "selector {", " *foo : 11px;", // to be erased (second) "}", "selector {", " *bar : 12px;", "}", "selector {", " *foo : 13px;", "}", "selector {", " bar : 14px;", // to be erased (first) "}", "selector {", " bar : 15px;", "}"), Sets.newHashSet("selector"), null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(2, rules.getRulesetNodes().size()); Iterator<CssRulesetNode> iter = rules.getRulesetNodes().iterator(); CssRulesetNode rule1 = iter.next(); checkRuleset("[[selector]{[bar:[[14px]];]}]", rule1); CssRulesetNode rule2 = iter.next(); checkRuleset("[[selector]{[*foo:[[11px]];]}]", rule2); } public void testMarkRemovableRulesetNode15() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " cusrsor: pointer;", "}", ".CSS_RULE {", " cursor: hand;", "}"), null, null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertTrue(rules.getRulesetNodes().isEmpty()); } public void testMarkRemovableRulesetNode16() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " width: 12px;", " display: inline;", "}", ".CSS_RULE {", " width: 10px;", "}"), null, null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertTrue(rules.getRulesetNodes().isEmpty()); } public void testMarkRemovableRulesetNodeImportant() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " font: 12px blue !important;", "}", ".CSS_RULE {", " font: 10px;", "}"), null, null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule1 = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_RULE]{[font:[[10px]];]}]", rule1); } public void testMarkRemovableRulesetNodeComponents() { collectRemovableRulesetNodes( linesToString( ".CSS_C1-CSS_E {", " border: 2px;", "}", ".CSS_C2-CSS_E {", " margin: 3px;", "}"), Sets.newHashSet("CSS_C1", "CSS_E"), "CSS_"); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_C2-CSS_E]{[margin:[[3px]];]}]", rule); } public void testMarkRemovableRulesetNodeShorthand() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " border-color: red;", "}", ".CSS_RULE {", " border: 3px;", "}"), null, null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_RULE]{[border-color:[[red]];]}]", rule); } public void testMarkRemovableRulesetNodeShorthandWithImportant() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " border-color: red !important;", "}", ".CSS_RULE {", " border: 3px;", "}"), null, null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertTrue(rules.getRulesetNodes().isEmpty()); } public void testMarkRemovableRulesetNodeShorthandWithAllImportant() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " border-color: red !important;", "}", ".CSS_RULE {", " border: 3px !important;", "}"), null, null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_RULE]{[border-color:[[red][!important]];]}]", rule); } public void testMarkRemovableRulesetNodeDontBarfOnNonRuleset() { collectRemovableRulesetNodes( linesToString( "@media print {}", ".CSS_RULE {", " border: 2px;", "}", ".CSS_RULE {", " border: 3px;", "}"), null, null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertFalse(rules.getRulesetNodes().isEmpty()); assertEquals(1, rules.getRulesetNodes().size()); CssRulesetNode rule = rules.getRulesetNodes().iterator().next(); checkRuleset("[[.CSS_RULE]{[border:[[2px]];]}]", rule); } public void testMarkRemovableRulesetNodeAlternate1() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " border: 2px;", "}", ".CSS_RULE {", " /* @alternate */ border: 3px;", "}"), Sets.<String>newHashSet(), null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertEquals(0, rules.getRulesetNodes().size()); } public void testMarkRemovableRulesetNodeAlternate2() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " border: 0;", "}", ".CSS_RULE {", " /* @alternate */ border: 1px;", "}", // The previous two are superseded by the next non-alternate. ".CSS_RULE {", " border: 2px;", "}"), Sets.<String>newHashSet(), null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertEquals(2, rules.getRulesetNodes().size()); Iterator<CssRulesetNode> iterator = rules.getRulesetNodes().iterator(); CssRulesetNode alternateRule = iterator.next(); assertEquals(1, alternateRule.getDeclarations().numChildren()); CssDeclarationNode alternateDeclaration = (CssDeclarationNode) alternateRule.getDeclarations().getChildAt(0); checkRuleset("[[.CSS_RULE]{[[/* @alternate */]border:[[1px]];]}]", alternateRule); assertTrue(alternateDeclaration.hasComment("/* @alternate */")); checkRuleset("[[.CSS_RULE]{[border:[[0]];]}]", iterator.next()); } public void testMarkRemovableRulesetNodeAlternate3() { collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " border: 1px;", "}", // The rule before this is superseded. ".CSS_RULE {", " border: 2px;", "}", ".CSS_RULE {", " /* @alternate */ border: 3px;", "}"), Sets.<String>newHashSet(), null); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertEquals(1, rules.getRulesetNodes().size()); checkRuleset("[[.CSS_RULE]{[border:[[1px]];]}]", rules.getRulesetNodes().iterator().next()); } public void testMarkRemovableRulesetNodeDeadCode() { // The "display" property is typically guarded by // SkippingTreeProperty.canModify because it causes layout in IE. For // the purposes of dead code removal this is irrelevant so make sure that // we can remove it anyway. collectRemovableRulesetNodes( linesToString( ".CSS_RULE {", " display: 1px;", "}"), Sets.<String>newHashSet(".some_other_thing"), ""); final RulesetNodesToRemove rules = tree.getRulesetNodesToRemove(); assertEquals(1, rules.getRulesetNodes().size()); checkRuleset("[[.CSS_RULE]{[display:[[1px]];]}]", rules.getRulesetNodes().iterator().next()); } private void collectRemovableRulesetNodes(String source, Set<String> referredRules, String prefix) { parseAndBuildTree(source); MarkRemovableRulesetNodes pass = new MarkRemovableRulesetNodes(tree, true); pass.setReferencedRules(referredRules, prefix); pass.runPass(); } }