| /* |
| * 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.gwt.resources.rg; |
| |
| import com.google.gwt.core.ext.GeneratorContext; |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.core.ext.UnableToCompleteException; |
| import com.google.gwt.core.ext.typeinfo.JClassType; |
| import com.google.gwt.resources.css.GenerateCssAst; |
| import com.google.gwt.resources.css.ast.CssNode; |
| import com.google.gwt.resources.css.ast.CssStylesheet; |
| import com.google.gwt.resources.css.ast.CssVisitor; |
| import com.google.gwt.resources.css.ast.HasNodes; |
| import com.google.gwt.resources.ext.ClientBundleRequirements; |
| import com.google.gwt.resources.ext.ResourceContext; |
| |
| import junit.framework.TestCase; |
| |
| import java.net.URL; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Contains functions for golden-output tests that are concerned with structural |
| * modifications to the CSS AST. |
| */ |
| public class CssTestCase extends TestCase { |
| /* |
| * NB: This class is in the resources.rg package so that it can access |
| * package-protected methods in CssResourceGenerator. |
| */ |
| |
| /** |
| * Triggers an assertion if a CssNode is traversed more than once. |
| * |
| * @see CssTestCase#assertNoAliasing(CssNode) |
| */ |
| public static class AliasDetector extends CssVisitor { |
| private final Map<CssNode, Void> seen = new IdentityHashMap<CssNode, Void>(); |
| |
| @Override |
| protected void doAccept(List<? extends CssNode> list) { |
| for (CssNode node : list) { |
| doAccept(node); |
| } |
| } |
| |
| @Override |
| protected <T extends CssNode> T doAccept(T node) { |
| assertFalse("Found repeated node " + node.toString(), |
| seen.containsKey(node)); |
| seen.put(node, null); |
| return super.doAccept(node); |
| } |
| |
| @Override |
| protected void doAcceptWithInsertRemove(List<? extends CssNode> list) { |
| for (CssNode node : list) { |
| doAccept(node); |
| } |
| } |
| } |
| |
| /** |
| * Total fake, no implementations. |
| */ |
| private static class FakeContext implements ResourceContext { |
| @Override |
| public String deploy(String suggestedFileName, String mimeType, |
| byte[] data, boolean forceExternal) throws UnableToCompleteException { |
| return null; |
| } |
| |
| @Override |
| @Deprecated |
| public String deploy(URL resource, boolean forceExternal) |
| throws UnableToCompleteException { |
| return null; |
| } |
| |
| @Override |
| public String deploy(URL resource, String mimeType, boolean forceExternal) |
| throws UnableToCompleteException { |
| return null; |
| } |
| |
| @Override |
| public <T> T getCachedData(String key, Class<T> clazz) { |
| return null; |
| } |
| |
| @Override |
| public JClassType getClientBundleType() { |
| return null; |
| } |
| |
| @Override |
| public GeneratorContext getGeneratorContext() { |
| return null; |
| } |
| |
| @Override |
| public String getImplementationSimpleSourceName() |
| throws IllegalStateException { |
| return null; |
| } |
| |
| @Override |
| public ClientBundleRequirements getRequirements() { |
| return null; |
| } |
| |
| @Override |
| public <T> boolean putCachedData(String key, T value) { |
| return false; |
| } |
| |
| @Override |
| public boolean supportsDataUrls() { |
| return true; |
| } |
| } |
| |
| /** |
| * Asserts that two CssNodes are identical. |
| */ |
| protected static <T extends CssNode & HasNodes> void assertEquals( |
| TreeLogger logger, T expected, T test) throws UnableToCompleteException { |
| String expectedCss = CssResourceGenerator.makeExpression(logger, |
| new FakeContext(), expected, false); |
| String testCss = CssResourceGenerator.makeExpression(logger, |
| new FakeContext(), test, false); |
| assertEquals(expectedCss, testCss); |
| } |
| |
| /** |
| * Ensure that no CssNode is traversed more than once due to AST errors. |
| */ |
| protected static void assertNoAliasing(CssNode node) { |
| (new AliasDetector()).accept(node); |
| } |
| |
| /** |
| * Compares the generated Java expressions for an input file, transformed in |
| * order by the specified visitors, and a golden-output file. |
| */ |
| private static void test(TreeLogger logger, URL test, URL expected, |
| CssVisitor... visitors) throws UnableToCompleteException { |
| |
| CssStylesheet expectedSheet = null; |
| CssStylesheet testSheet = null; |
| |
| try { |
| expectedSheet = GenerateCssAst.exec(logger, expected); |
| testSheet = GenerateCssAst.exec(logger, test); |
| } catch (UnableToCompleteException e) { |
| fail("Unable to parse stylesheet"); |
| } |
| |
| for (CssVisitor v : visitors) { |
| v.accept(testSheet); |
| } |
| |
| assertEquals(logger, expectedSheet, testSheet); |
| } |
| |
| /** |
| * Runs a test. |
| * |
| * @param testName is used to compute the test and expected resource paths. |
| * @param reversible if <code>true</code>, the test will attempt to transform |
| * the expected css into the test css |
| */ |
| protected void test(TreeLogger logger, String testName, boolean reversible, |
| CssVisitor... visitors) throws UnableToCompleteException { |
| String packagePath = getClass().getPackage().getName().replace('.', '/') |
| + "/"; |
| URL testUrl = getClass().getClassLoader().getResource( |
| packagePath + testName + "_test.css"); |
| assertNotNull("Could not find testUrl", testUrl); |
| URL expectedUrl = getClass().getClassLoader().getResource( |
| packagePath + testName + "_expected.css"); |
| assertNotNull("Could not find testUrl", expectedUrl); |
| |
| test(logger, testUrl, expectedUrl, visitors); |
| |
| if (reversible) { |
| test(logger, expectedUrl, testUrl, visitors); |
| } |
| } |
| } |