/*
* Copyright 2000-2016 Vaadin Ltd.
*
* 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.vaadin.tests.design;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.BooleanAttribute;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
import org.junit.Assert;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Component;
import com.vaadin.ui.declarative.Design;
import com.vaadin.ui.declarative.DesignContext;
import com.vaadin.ui.declarative.ShouldWriteDataDelegate;
public abstract class DeclarativeTestBaseBase<T extends Component> {
private static final class AlwaysWriteDelegate
implements ShouldWriteDataDelegate {
private static final long serialVersionUID = -6345914431997793599L;
@Override
public boolean shouldWriteData(Component component) {
return true;
}
}
public static final ShouldWriteDataDelegate ALWAYS_WRITE_DATA = new AlwaysWriteDelegate();
public interface EqualsAsserter<TT> {
public void assertObjectEquals(TT o1, TT o2);
}
protected T read(String design) {
try {
return (T) Design
.read(new ByteArrayInputStream(design.getBytes("UTF-8")));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
protected DesignContext readAndReturnContext(String design) {
try {
return Design.read(
new ByteArrayInputStream(design.getBytes("UTF-8")), null);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
protected String write(T object, boolean writeData) {
DesignContext dc = new DesignContext();
if (writeData) {
dc.setShouldWriteDataDelegate(
DeclarativeTestBaseBase.ALWAYS_WRITE_DATA);
}
return write(object, dc);
}
protected String write(T object, DesignContext context) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
context.setRootComponent(object);
Design.write(context, outputStream);
return outputStream.toString("UTF-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void assertEquals(Object o1, Object o2) {
assertEquals("", o1, o2);
}
protected void assertEquals(String message, Object o1, Object o2) {
if (o1 == null) {
Assert.assertNull(message, o2);
return;
}
if (o2 == null) {
Assert.assertNull(message, o1);
return;
}
if (!(o1 instanceof Collection && o2 instanceof Collection)) {
Assert.assertEquals(o1.getClass(), o2.getClass());
}
if (o1 instanceof Object[]) {
Object[] a1 = ((Object[]) o1);
Object[] a2 = ((Object[]) o2);
Assert.assertEquals(message + ": array length", a1.length,
a2.length);
for (int i = 0; i < a1.length; i++) {
assertEquals(message + ": element " + i, a1[i], a2[i]);
}
return;
}
List<EqualsAsserter<Object>> comparators = getComparators(o1);
if (!comparators.isEmpty()) {
for (EqualsAsserter<Object> ec : comparators) {
ec.assertObjectEquals(o1, o2);
}
} else {
Assert.assertEquals(message, o1, o2);
}
}
private List<EqualsAsserter<Object>> getComparators(Object o1) {
List<EqualsAsserter<Object>> result = new ArrayList<>();
getComparators(o1.getClass(), result);
return result;
}
private void getComparators(Class<?> c,
List<EqualsAsserter<Object>> result) {
if (c == null || !isVaadin(c)) {
return;
}
EqualsAsserter<Object> comparator = (EqualsAsserter<Object>) getComparator(
c);
if (c.getSuperclass() != Object.class) {
getComparators(c.getSuperclass(), result);
}
for (Class<?> i : c.getInterfaces()) {
getComparators(i, result);
}
if (!result.contains(comparator)) {
result.add(comparator);
}
}
protected abstract <TT> EqualsAsserter<TT> getComparator(Class<TT> c);
private boolean isVaadin(Class<?> c) {
return c.getPackage() != null
&& c.getPackage().getName().startsWith("com.vaadin");
}
public static class TestLogHandler {
final List<String> messages = new ArrayList<>();
Handler handler = new Handler() {
@Override
public void publish(LogRecord record) {
messages.add(record.getMessage());
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
};
public TestLogHandler() {
Logger.getLogger(AbstractComponent.class.getName()).getParent()
.addHandler(handler);
}
public String getMessages() {
if (messages.isEmpty()) {
return "";
}
String r = "";
for (String message : messages) {
r += message + "\n";
}
return r;
}
}
public T testRead(String design, T expected) {
TestLogHandler l = new TestLogHandler();
T read = read(design);
assertEquals(expected, read);
Assert.assertEquals("", l.getMessages());
return read;
}
public DesignContext readComponentAndCompare(String design, T expected) {
TestLogHandler l = new TestLogHandler();
DesignContext context = readAndReturnContext(design);
assertEquals(expected, context.getRootComponent());
Assert.assertEquals("", l.getMessages());
return context;
}
public void testWrite(String expected, T component) {
TestLogHandler l = new TestLogHandler();
testWrite(expected, component, false);
Assert.assertEquals("", l.getMessages());
}
public void testWrite(String expectedDesign, T component,
boolean writeData) {
String written = write(component, writeData);
Element producedElem = Jsoup.parse(written).body().child(0);
Element comparableElem = Jsoup.parse(expectedDesign).body().child(0);
String produced = elementToHtml(producedElem);
String comparable = elementToHtml(comparableElem);
Assert.assertEquals(comparable, produced);
}
public void testWrite(T component, String expected, DesignContext context) {
String written = write(component, context);
Element producedElem = Jsoup.parse(written).body().child(0);
Element comparableElem = Jsoup.parse(expected).body().child(0);
String produced = elementToHtml(producedElem);
String comparable = elementToHtml(comparableElem);
Assert.assertEquals(comparable, produced);
}
protected Element createElement(Component c) {
return new DesignContext().createElement(c);
}
private String elementToHtml(Element producedElem) {
StringBuilder stringBuilder = new StringBuilder();
elementToHtml(producedElem, stringBuilder);
return stringBuilder.toString();
}
/**
* Produce predictable html (attributes in alphabetical order), always
* include close tags
*/
private String elementToHtml(Element producedElem, StringBuilder sb) {
HashSet<String> booleanAttributes = new HashSet<>();
ArrayList<String> names = new ArrayList<>();
for (Attribute a : producedElem.attributes().asList()) {
names.add(a.getKey());
if (a instanceof BooleanAttribute) {
booleanAttributes.add(a.getKey());
}
}
Collections.sort(names);
sb.append("<").append(producedElem.tagName()).append("");
for (String attrName : names) {
sb.append(" ").append(attrName);
if (!booleanAttributes.contains(attrName)) {
sb.append("=").append("\'").append(producedElem.attr(attrName))
.append("\'");
}
}
sb.append(">");
for (Node child : producedElem.childNodes()) {
if (child instanceof Element) {
elementToHtml((Element) child, sb);
} else if (child instanceof TextNode) {
String text = ((TextNode) child).text();
sb.append(text.trim());
}
}
sb.append("</").append(producedElem.tagName()).append(">");
return sb.toString();
}
protected String stripOptionTags(String design) {
return design.replaceAll("[ \n]*<option(.*)</option>[ \n]*", "");
}
}