/* * Copyright 2012 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 com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.css.SourceCode; import com.google.common.css.compiler.ast.AccessibleErrorManager; import com.google.common.css.compiler.ast.CssCompositeValueNode; import com.google.common.css.compiler.ast.CssDeclarationNode; import com.google.common.css.compiler.ast.CssPropertyValueNode; import com.google.common.css.compiler.ast.CssRootNode; import com.google.common.css.compiler.ast.CssRulesetNode; import com.google.common.css.compiler.ast.CssTree; import com.google.common.css.compiler.ast.GssError; import com.google.common.css.compiler.ast.GssParser; import com.google.common.css.compiler.ast.GssParserException; import com.google.common.css.compiler.passes.testing.AstPrinter; import junit.framework.TestCase; /** * Unit tests for {@link FixupFontDeclarations}. * */ public class FixupFontDeclarationsTest extends TestCase { private AccessibleErrorManager errorManager; public void testIdSequenceFontFamilies() throws Exception { CssTree t = runPass(parse("a { font-family: a, b c d, e f g h i, 'jkl'; }")); CssRootNode r = t.getRoot(); CssRulesetNode rules = (CssRulesetNode) r.getBody().getChildAt(0); CssDeclarationNode decl = (CssDeclarationNode) rules.getDeclarations().getChildAt(0); assertEquals("font-family", decl.getPropertyName().getPropertyName()); CssPropertyValueNode valueNode = decl.getPropertyValue(); assertEquals(1, valueNode.numChildren()); CssCompositeValueNode alternatives = (CssCompositeValueNode) valueNode.getChildAt(0); assertEquals(4, alternatives.getValues().size()); assertEquals("a", alternatives.getValues().get(0).getValue()); assertEquals("b c d", alternatives.getValues().get(1).getValue()); assertEquals("e f g h i", alternatives.getValues().get(2).getValue()); assertEquals("jkl", alternatives.getValues().get(3).getValue()); } // TODO(user): when we have string parsing, test that this pass // rejects string sequences e.g. // font-family: Helvetica, "Adobe Garamond" "Pro", Times New Roman, serif // ^ // error: font alternatives must be strings or identifier-sequences public void testFontFamilyAssociativity() throws Exception { testTree("div { font-family: a b, c; }", "[[div]{[font-family:[[[a b],[c]]];]}]"); } public void testFontFamilyInherit() throws Exception { testValid("div { font-family: inherit; }"); } public void testFontShorthandGssUse() throws Exception { testTree(FixupFontDeclarations.InputMode.GSS, "div { font: FG_COLOR; }", "[[div]{[font:[[FG_COLOR]];]}]"); testTree(FixupFontDeclarations.InputMode.GSS, "div { font: FGCOLOR }", "[[div]{[font:[[FGCOLOR]];]}]"); testTree(FixupFontDeclarations.InputMode.GSS, "div { font: FGCOLOR }", "[[div]{[font:[[FGCOLOR]];]}]"); } public void testFontShorthandSystem() throws Exception { testTree("div { font: status-bar; }", "[[div]{[font:[[status-bar]];]}]"); testTree("div { font: status-bar }", "[[div]{[font:[[status-bar]];]}]"); } public void testFontJustInherit() throws Exception { testValid("div { font: inherit; }"); } public void testIdSeqInFontShorthand() throws Exception { testTree("div { font: bold 12px arial, serif; }", "[[div]{[font:[[bold][12px][[arial],[serif]]];]}]"); } public void testFontShorthandSizeFamily() throws Exception { testTree("div { font: 100% \"Adobe Garamond Premier Pro\"; }", "[[div]{[font:[[100%][[\"Adobe Garamond Premier Pro\"]]];]}]"); } public void testFontShorthandSizeFamilyNoSemi() throws Exception { testTree("div { font: 100% \"Adobe Garamond Premier Pro\" }", "[[div]{[font:[[100%][[\"Adobe Garamond Premier Pro\"]]];]}]"); } public void testFontShorthandPresize() throws Exception { testTree("div { font:normal 13px Frutiger; }", "[[div]{[font:[[normal][13px][[Frutiger]]];]}]"); } public void testFontShorthandVarFirst() throws Exception { testValid(FixupFontDeclarations.InputMode.GSS, "div { font: FONT_STYLE 14px arial,sans-serif; }"); } public void testFontShorthandNoTailSwizzle() throws Exception { testTree(FixupFontDeclarations.InputMode.GSS, "div { font: bold APP_MENU_FONT; }", "[[div]{[font:[[bold][APP_MENU_FONT]];]}]"); } public void testFontShorthandSizeAndLineHeight() throws Exception { testTree("div { font: 1em/1.3em \"Roboto\"; }", "[[div]{[font:[[1em][/][1.3em][[\"Roboto\"]]];]}]"); } public void testFontShorthandSizeAndLineHeight2() throws Exception { testValid("a { font: 10px/12px Arial; }"); } public void testFontShorthandImportant() throws Exception { testValid(FixupFontDeclarations.InputMode.GSS, "div { font: normal SEARCHAC !important; }"); } public void testFontShorthandImportant2() throws Exception { testTree("a { font: medium \"Computer Modern\" !important; }", "[[a]{[font:[[medium][[\"Computer Modern\"]][!important]];]}]"); } public void testFontFamilyImportant() throws Exception { testTree("a { font-family: \"Computer Modern\" !important; }", "[[a]{[font-family:[[[\"Computer Modern\"]][!important]];]}]"); } public void testFontInheritFamily() throws Exception { // this isn't allowed by the CSS 2.1 grammar but it's common enough // and we support it anyway. testValid("div { font: normal 95% inherit; }"); } public void testFontWithNumericWeight() throws Exception { testValid("div { font: normal normal 400 13px arial,sans,sans-serif; }"); } public void testFontVariantSizeFamily() throws Exception { testValid("div { font: small-caps 13px serif; }"); } public void testIdSequenceInShorthand() throws Exception { testTree("div { font: 13px Computer Modern, Adobe Caslon Pro, serif; }", "[[div]{[font:[[13px][[Computer Modern]," + "[Adobe Caslon Pro],[serif]]];]}]"); } public void testSizeAndFamilyRequired() throws Exception { testError(FixupFontDeclarations.SIZE_AND_FAMILY_REQUIRED, "a { font: 12px; }"); testError(FixupFontDeclarations.SIZE_AND_FAMILY_REQUIRED, "a { font: serif; }"); } public void testTooManyLineHeights() throws Exception { testError(FixupFontDeclarations.TOO_MANY.get( FixupFontDeclarations.FontProperty.LINE_HEIGHT), "a { font: 1ex/2pt/4px serif; }"); } public void testTooManyLineHeightPairs() throws Exception { testError(FixupFontDeclarations.TOO_MANY.get( FixupFontDeclarations.FontProperty.LINE_HEIGHT), "a { font: 1ex/2pt 4px/8em serif; }"); } public void testTooManyFontSizes() throws Exception { testError(FixupFontDeclarations.TOO_MANY.get( FixupFontDeclarations.FontProperty.SIZE), "a { font: large 12pt serif; }"); } public void testTooManyStyles() throws Exception { testError(FixupFontDeclarations.TOO_MANY.get( FixupFontDeclarations.FontProperty.STYLE), "a { font: italic oblique 10px helvetica; }"); } public void testTooManyVariants() throws Exception { testError(FixupFontDeclarations.TOO_MANY.get( FixupFontDeclarations.FontProperty.VARIANT), "a { font: small-caps small-caps 10px helvetica; }"); } public void testTooManyWeights() throws Exception { testError(FixupFontDeclarations.TOO_MANY.get( FixupFontDeclarations.FontProperty.WEIGHT), "a { font: bold 800 72pt serif; }"); } public void testTooManyNormals() throws Exception { testError(FixupFontDeclarations.TOO_MANY_NORMALS, "a { font: normal normal normal normal 8px sans-serif; }"); } public void testNormalTooLate() throws Exception { testError(FixupFontDeclarations.TOO_MANY_PRE_SIZE, "a { font: italic bold normal normal 12pt serif; }"); } public void testNormalAsAFontName() throws Exception { testValid("a { font: italic 3.14em normal; }"); } public void testNormalAsAFontName2() throws Exception { testValid("a { font: italic bold 12pt normal; }"); } public void testSizeHeightSize() throws Exception { testError(FixupFontDeclarations.TOO_MANY.get( FixupFontDeclarations.FontProperty.SIZE), "a { font: 1em/2em 3em serif; }"); } public void testSizeSizeHeight() throws Exception { testError(FixupFontDeclarations.TOO_MANY.get( FixupFontDeclarations.FontProperty.SIZE), "a { font: 3em 1em/2em serif; }"); } public void testTooManyPreLineHeight() throws Exception { testError(FixupFontDeclarations.TOO_MANY_PRE_SIZE, "a { font: normal italic small-caps bold 18px/20px serif; }"); } public void testTooManyPreSize() throws Exception { testError(FixupFontDeclarations.TOO_MANY_PRE_SIZE, "a { font: normal italic small-caps bold 18px serif; }"); } public void testPreSizeInterloperSize() throws Exception { testError(FixupFontDeclarations.PRE_SIZE_INTERLOPER_SIZE, "a { font: normal normal normal serif 12px sans-serif; }"); } private CssTree parse(String source) throws GssParserException { return new GssParser(new SourceCode(null, source)).parse(); } private CssTree runPass(CssTree input) { return runPass(FixupFontDeclarations.InputMode.CSS, input); } private CssTree runPass( FixupFontDeclarations.InputMode mode, CssTree input) { CssTree result = new CssTree(input.getSourceCode(), input.getRoot().deepCopy()); System.err.println(SExprPrinter.print(result)); errorManager = new AccessibleErrorManager(); new FixupFontDeclarations(mode, errorManager, result).runPass(); // this will be helpful for debugging tests System.err.println(SExprPrinter.print(result)); return result; } private CssTree testTree(String css, String output) throws Exception { return testTree(FixupFontDeclarations.InputMode.CSS, css, output); } private CssTree testTree( FixupFontDeclarations.InputMode mode, String gss, String output) throws Exception { CssTree tree = testValid(mode, gss); assertEquals(output.replace("[", "(").replace("]", ")"), AstPrinter.print(tree).replace("[", "(").replace("]", ")")); //assertEquals(output, AstPrinter.print(tree)); return tree; } private CssTree testValid(String css) throws Exception { return testValid(FixupFontDeclarations.InputMode.CSS, css); } private CssTree testValid( FixupFontDeclarations.InputMode mode, String css) throws Exception { CssTree tree = runPass(mode, parse(css)); assertNotNull(tree); assertFalse(Joiner.on("\n").join( Iterables.transform(errorManager.getErrors(), new Function<GssError, String>() { @Override public String apply(GssError e) { return e.format(); } })), errorManager.hasErrors()); return tree; } private CssTree testError(String expectedError, String css) throws Exception { return testError(FixupFontDeclarations.InputMode.CSS, expectedError, css); } private CssTree testError( FixupFontDeclarations.InputMode mode, final String expectedError, String css) throws Exception { CssTree tree = runPass(mode, parse(css)); assertNotNull(tree); assertTrue(errorManager.hasErrors()); assertTrue( Iterables.any(errorManager.getErrors(), new Predicate<GssError>() { @Override public boolean apply(GssError e) { return expectedError.equals(e.getMessage()); } })); return tree; } }