/*
* 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;
}
}