/*
* Copyright (C) 2008 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.gxp.compiler.reparent;
import com.google.gxp.compiler.GxpcTestCase;
import com.google.gxp.compiler.alerts.Alert;
import com.google.gxp.compiler.alerts.AlertSet;
import com.google.gxp.compiler.alerts.AlertSetBuilder;
import com.google.gxp.compiler.alerts.SourcePosition;
import com.google.gxp.compiler.alerts.WarningAlert;
import com.google.gxp.compiler.alerts.common.BadNodePlacementError;
import com.google.gxp.compiler.alerts.common.MissingAttributeError;
import com.google.gxp.compiler.alerts.common.UnknownAttributeError;
import com.google.gxp.compiler.base.ClassImport;
import com.google.gxp.compiler.base.CollapseExpression;
import com.google.gxp.compiler.base.Concatenation;
import com.google.gxp.compiler.base.Expression;
import com.google.gxp.compiler.base.Parameter;
import com.google.gxp.compiler.base.Import;
import com.google.gxp.compiler.base.NativeExpression;
import com.google.gxp.compiler.base.NativeType;
import com.google.gxp.compiler.base.Node;
import com.google.gxp.compiler.base.NullRoot;
import com.google.gxp.compiler.base.OutputElement;
import com.google.gxp.compiler.base.OutputLanguage;
import com.google.gxp.compiler.base.PackageImport;
import com.google.gxp.compiler.base.Root;
import com.google.gxp.compiler.base.StringConstant;
import com.google.gxp.compiler.base.Template;
import com.google.gxp.compiler.base.TemplateName;
import com.google.gxp.compiler.ifexpand.IfExpandedTree;
import com.google.gxp.compiler.parser.GxpNamespace;
import com.google.gxp.compiler.parser.Namespace;
import com.google.gxp.compiler.parser.NullNamespace;
import com.google.gxp.compiler.parser.OutputNamespace;
import com.google.gxp.compiler.parser.ParsedAttribute;
import com.google.gxp.compiler.parser.ParsedElement;
import com.google.gxp.compiler.parser.TextElement;
import com.google.gxp.compiler.schema.BuiltinSchemaFactory;
import java.util.*;
/**
* Tests for {@link Reparenter}.
*/
public class ReparenterTest extends GxpcTestCase {
private static final Namespace GXP_NS = GxpNamespace.INSTANCE;
private static final Namespace HTML_NS =
new OutputNamespace(BuiltinSchemaFactory.INSTANCE.fromContentTypeName("text/html"));
private static final TemplateName TEMPLATE_NAME =
TemplateName.create("pkg", "Test");
private static final List<ParsedAttribute> NO_ATTRS = Collections.emptyList();
private final AlertSetBuilder expectedAlerts = new AlertSetBuilder();
private AlertSet actualAlerts = AlertSet.EMPTY;
/**
* We test the alerts in tearDown. Individual tests can put Alerts they
* expect to see in expectedAlerts. actualAlerts is automatically set by
* reparent().
*/
public void tearDown() throws Exception {
assertEquals(expectedAlerts.buildAndClear(), actualAlerts);
}
private void assertEmptyValue(Expression childNode) {
if (childNode instanceof CollapseExpression) {
childNode = ((CollapseExpression) childNode).getSubexpression();
}
assertTrue(childNode + " is not a StringConstant.",
childNode instanceof StringConstant);
StringConstant stringConstant = (StringConstant) childNode;
assertEquals("", stringConstant.evaluate());
}
private ReparentedTree reparent(IfExpandedTree parseTree) {
ReparentedTree result = new Reparenter(BuiltinSchemaFactory.INSTANCE,
TEMPLATE_NAME.toString())
.apply(parseTree);
actualAlerts = result.getAlerts();
assertEquals(parseTree.getSourcePosition(), result.getSourcePosition());
return result;
}
/*
* To make it easier to programmatically create parse trees, there are a
* bunch of helper methods to create various types of parsed nodes.
*/
/**
* AlertSetBuilder used as AlertSink by all parsed node helper methods.
*/
private final AlertSetBuilder parseAlerts = new AlertSetBuilder();
private final String filename = "[" + getName() + ".gxp]";
int line = 0;
/**
* @return a unique but deterministic source position for each location in
* the simulated parse tree.
*/
private SourcePosition pos(Node... children) {
line++;
int column = 1;
for (Node node : children) {
column = Math.max(column, node.getSourcePosition().getColumn() + 1);
}
return new SourcePosition(filename, line, column);
}
/**
* Creates a parse tree.
*/
private IfExpandedTree tree(ParsedElement... children) {
return new IfExpandedTree(new SourcePosition(filename),
parseAlerts.buildAndClear(), list(children));
}
/**
* Creates a template element.
*/
private ParsedElement template(List<ParsedAttribute> attrs,
ParsedElement... children) {
return GXP_NS.createElement(parseAlerts, pos(children), "<gxp:template>",
"template", attrs, list(children));
}
/**
* Creates an HTML element.
*/
private ParsedElement tag(String tagName,
List<ParsedAttribute> attrs,
ParsedElement... children) {
return HTML_NS.createElement(parseAlerts, pos(children),
"<" + tagName + ">", tagName, attrs,
list(children));
}
/**
* Creates a text element.
*/
private TextElement text(String value) {
return new TextElement(pos(), value);
}
/**
* Creates an import element.
*/
private ParsedElement imp(List<ParsedAttribute> attrs,
ParsedElement... children) {
return GXP_NS.createElement(parseAlerts, pos(children), "<gxp:import>",
"import", attrs, list(children));
}
/**
* Creates a param element.
*/
private ParsedElement param(List<ParsedAttribute> attrs,
ParsedElement... children) {
return GXP_NS.createElement(parseAlerts, pos(children), "<gxp:param>",
"param", attrs, list(children));
}
private ParsedAttribute attr(String name, String value) {
return new ParsedAttribute(pos(), NullNamespace.INSTANCE, name, value,
name);
}
public void testBaseCase() throws Exception {
IfExpandedTree input = tree();
ReparentedTree output = reparent(input);
assertTrue(output.getRoot() instanceof NullRoot);
}
public void testWithAlerts() throws Exception {
Alert parseWarning = new WarningAlert(new SourcePosition("baz", 1, 2),
"test warning"){};
parseAlerts.add(parseWarning);
expectedAlerts.add(parseWarning);
ReparentedTree output = reparent(tree());
assertTrue(output.getRoot() instanceof NullRoot);
}
private void assertOneRoot(ReparentedTree tree) {
assertNotNull(tree.getRoot());
assertEquals(1, tree.getChildren().size());
assertEquals(tree.getRoot(), tree.getChildren().get(0));
}
private Template getTemplate(Root root) {
assertTrue(root + " is not a Template",
root instanceof Template);
return (Template)root;
}
public void testEmptyNamelessTemplate() throws Exception {
ParsedElement template = template(NO_ATTRS);
expectedAlerts.add(new MissingAttributeError(template, "name"));
IfExpandedTree parseTree = tree(template);
ReparentedTree output = reparent(parseTree);
assertOneRoot(output);
Template root = getTemplate(output.getRoot());
assertEquals(TEMPLATE_NAME.toString(), root.getName().toString());
assertEquals(0, root.getImports().size());
assertEquals(0, root.getParameters().size());
assertEmptyValue(root.getContent());
}
public void testNamedTemplate() throws Exception {
IfExpandedTree parseTree = tree(template(list(attr("name", "pkg.Test"))));
ReparentedTree output = reparent(parseTree);
assertOneRoot(output);
Template root = getTemplate(output.getRoot());
assertEquals(TEMPLATE_NAME, root.getName());
assertEquals(0, root.getImports().size());
assertEquals(0, root.getParameters().size());
assertEmptyValue(root.getContent());
}
public void testTemplateWithBadAttr() throws Exception {
ParsedAttribute spaz = attr("spaz", "I don't exist!");
ParsedElement template = template(list(attr("name", "pkg.Test"), spaz));
expectedAlerts.add(new UnknownAttributeError(template, spaz));
IfExpandedTree parseTree = tree(template);
ReparentedTree output = reparent(parseTree);
assertOneRoot(output);
Template root = getTemplate(output.getRoot());
assertEquals(TEMPLATE_NAME, root.getName());
assertEquals(0, root.getImports().size());
assertEquals(0, root.getParameters().size());
assertEmptyValue(root.getContent());
}
public void testTemplateWithImports() throws Exception {
IfExpandedTree parseTree =
tree(template(list(attr("name", "pkg.Test")),
imp(list(attr("class", "com.google.Foo"))),
imp(list(attr("package", "com.google.bar"))),
imp(list(attr("class", "com.google.Baz")))));
ReparentedTree output = reparent(parseTree);
assertOneRoot(output);
Template root = getTemplate(output.getRoot());
assertEquals(TEMPLATE_NAME, root.getName());
List<Import> imports = root.getImports();
assertEquals(3, imports.size());
assertEquals(templateName("com.google.Foo"),
((ClassImport) imports.get(0)).getClassName());
assertEquals("com.google.bar",
((PackageImport) imports.get(1)).getPackageName());
assertEquals(templateName("com.google.Baz"),
((ClassImport) imports.get(2)).getClassName());
assertEquals(0, root.getParameters().size());
assertEmptyValue(root.getContent());
}
public void testImportWithText() throws Exception {
TextElement textNode = text("gremlin");
ParsedElement badImp = imp(list(attr("class", "com.google.Foo")),
textNode);
IfExpandedTree parseTree =
tree(template(list(attr("name", "pkg.Test")),
badImp,
imp(list(attr("package", "com.google.bar"))),
imp(list(attr("class", "com.google.Baz")))));
ReparentedTree output = reparent(parseTree);
expectedAlerts.add(new BadNodePlacementError(textNode, badImp));
assertOneRoot(output);
Template root = getTemplate(output.getRoot());
assertEquals(TEMPLATE_NAME, root.getName());
List<Import> imports = root.getImports();
assertEquals(3, imports.size());
assertEquals(templateName("com.google.Foo"),
((ClassImport) imports.get(0)).getClassName());
assertEquals("com.google.bar",
((PackageImport) imports.get(1)).getPackageName());
assertEquals(templateName("com.google.Baz"),
((ClassImport) imports.get(2)).getClassName());
assertEquals(0, root.getParameters().size());
assertEmptyValue(root.getContent());
}
public void testTemplateWithParams() throws Exception {
IfExpandedTree parseTree =
tree(template(list(attr("name", "pkg.Test")),
param(list(attr("name", "x"),
attr("type", "int"))),
param(list(attr("type", "String"),
attr("default", "\"xyzzy\""),
attr("name", "s")))));
ReparentedTree output = reparent(parseTree);
assertOneRoot(output);
Template root = getTemplate(output.getRoot());
assertEquals(TEMPLATE_NAME, root.getName());
assertEquals(0, root.getImports().size());
List<Parameter> params = root.getParameters();
assertEquals(2, params.size());
assertEquals("x", params.get(0).getPrimaryName());
assertEquals("int",
((NativeType) params.get(0).getType()).getNativeType(OutputLanguage.JAVA));
assertEquals(null, params.get(0).getDefaultValue());
assertEquals("s", params.get(1).getPrimaryName());
assertEquals("String",
((NativeType) params.get(1).getType()).getNativeType(OutputLanguage.JAVA));
Expression defValue = params.get(1).getDefaultValue();
assertEquals("\"xyzzy\"", ((NativeExpression) defValue).getDefaultNativeCode());
assertEmptyValue(root.getContent());
}
public void testTemplateWithKids() throws Exception {
IfExpandedTree parseTree =
tree(template(list(attr("name", "pkg.Test")),
tag("img", list(attr("src", "Uno"))),
tag("p", list(attr("class", "Dos"))),
tag("div", list(attr("id", "Windows")))));
ReparentedTree output = reparent(parseTree);
assertOneRoot(output);
Template root = getTemplate(output.getRoot());
assertEquals(TEMPLATE_NAME, root.getName());
assertEquals(0, root.getImports().size());
assertEquals(0, root.getParameters().size());
CollapseExpression collapse = (CollapseExpression) root.getContent();
Concatenation kids = (Concatenation) collapse.getSubexpression();
assertEquals(3, kids.getValues().size());
assertEquals("img",
((OutputElement) kids.getValues().get(0)).getLocalName());
assertEquals("p",
((OutputElement) kids.getValues().get(1)).getLocalName());
assertEquals("div",
((OutputElement) kids.getValues().get(2)).getLocalName());
// TODO(laurence): test all other node types
}
}