blob: 75a3e072872ef429d86da03b36607171d17e228c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 freemarker.test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.junit.Ignore;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.StringTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.utility.StringUtil;
import freemarker.test.templatesuite.TemplateTestSuite;
/**
* Superclass of JUnit tests that process templates but aren't practical to implement via {@link TemplateTestSuite}.
*/
@Ignore
public abstract class TemplateTest {
private Configuration configuration;
private boolean dataModelCreated;
private Object dataModel;
protected final Configuration getConfiguration() {
if (configuration == null) {
try {
configuration = createConfiguration();
} catch (Exception e) {
throw new RuntimeException("Failed to set up configuration for the test", e);
}
}
return configuration;
}
protected final void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
protected void assertOutput(String ftl, String expectedOut) throws IOException, TemplateException {
assertOutput(createTemplate(ftl), expectedOut, false);
}
private Template createTemplate(String ftl) throws IOException {
Template t = new Template(null, ftl, getConfiguration());
return t;
}
protected void assertOutputForNamed(String name, String expectedOut) throws IOException, TemplateException {
assertOutput(getConfiguration().getTemplate(name), expectedOut, false);
}
@SuppressFBWarnings(value="UI_INHERITANCE_UNSAFE_GETRESOURCE", justification="By design relative to subclass")
protected void assertOutputForNamed(String name) throws IOException, TemplateException {
String expectedOut;
{
String resName = name + ".out";
InputStream in = this.getClass().getResourceAsStream(resName);
if (in == null) {
throw new IOException("Reference output resource not found: " + this.getClass() + ", " + resName);
}
try {
expectedOut = TestUtil.removeTxtCopyrightComment(IOUtils.toString(in, "utf-8"));
} finally {
in.close();
}
}
assertOutput(getConfiguration().getTemplate(name), expectedOut, true);
}
protected void assertOutput(Template t, String expectedOut) throws TemplateException, IOException {
assertOutput(t, expectedOut, false);
}
protected void assertOutput(Template t, String expectedOut, boolean normalizeNewlines)
throws TemplateException, IOException {
String actualOut = getOutput(t);
if (normalizeNewlines) {
expectedOut = normalizeNewLines(expectedOut);
actualOut = normalizeNewLines(actualOut);
}
assertEquals(expectedOut, actualOut);
}
protected String getOutput(String ftl) throws IOException, TemplateException {
return getOutput(createTemplate(ftl));
}
protected String getOutput(Template t) throws TemplateException, IOException {
StringWriter out = new StringWriter();
t.process(getDataModel(), out);
String actualOut = out.toString();
return actualOut;
}
protected Configuration createConfiguration() throws Exception {
return new Configuration(Configuration.VERSION_2_3_0);
}
protected Object getDataModel() {
if (!dataModelCreated) {
dataModel = createDataModel();
dataModelCreated = true;
}
return dataModel;
}
protected Object createDataModel() {
return null;
}
@SuppressWarnings("boxing")
protected Map<String, Object> createCommonTestValuesDataModel() {
Map<String, Object> dataModel = new HashMap<String, Object>();
dataModel.put("map", Collections.singletonMap("key", "value"));
dataModel.put("list", Collections.singletonList("item"));
dataModel.put("s", "text");
dataModel.put("n", 1);
dataModel.put("b", true);
dataModel.put("bean", new TestBean());
return dataModel;
}
protected void addTemplate(String name, String content) {
Configuration cfg = getConfiguration();
TemplateLoader tl = cfg.getTemplateLoader();
StringTemplateLoader stl;
if (tl != null) {
stl = extractStringTemplateLoader(tl);
} else {
stl = new StringTemplateLoader();
cfg.setTemplateLoader(stl);
}
stl.putTemplate(name, content);
}
private StringTemplateLoader extractStringTemplateLoader(TemplateLoader tl) {
if (tl instanceof MultiTemplateLoader) {
MultiTemplateLoader mtl = (MultiTemplateLoader) tl;
for (int i = 0; i < mtl.getTemplateLoaderCount(); i++) {
TemplateLoader tli = mtl.getTemplateLoader(i);
if (tli instanceof StringTemplateLoader) {
return (StringTemplateLoader) tli;
}
}
throw new IllegalStateException(
"The template loader was a MultiTemplateLoader that didn't contain StringTemplateLoader: "
+ tl);
} else if (tl instanceof StringTemplateLoader) {
return (StringTemplateLoader) tl;
} else {
throw new IllegalStateException(
"The template loader was already set to a non-StringTemplateLoader non-MultiTemplateLoader: "
+ tl);
}
}
protected void addToDataModel(String name, Object value) {
Object dm = getDataModel();
if (dm == null) {
dm = new HashMap<String, Object>();
dataModel = dm;
}
if (dm instanceof Map) {
((Map) dm).put(name, value);
} else {
throw new IllegalStateException("Can't add to non-Map data-model: " + dm);
}
}
protected Throwable assertErrorContains(String ftl, String... expectedSubstrings) {
return assertErrorContains(null, ftl, null, expectedSubstrings);
}
protected Throwable assertErrorContains(String ftl, Class<? extends Throwable> exceptionClass,
String... expectedSubstrings) {
return assertErrorContains(null, ftl, exceptionClass, expectedSubstrings);
}
protected void assertErrorContainsForNamed(String name, String... expectedSubstrings) {
assertErrorContains(name, null, null, expectedSubstrings);
}
protected void assertErrorContainsForNamed(String name, Class<? extends Throwable> exceptionClass,
String... expectedSubstrings) {
assertErrorContains(name, null, exceptionClass, expectedSubstrings);
}
private Throwable assertErrorContains(String name, String ftl, Class<? extends Throwable> exceptionClass,
String... expectedSubstrings) {
try {
Template t;
if (ftl == null) {
t = getConfiguration().getTemplate(name);
} else {
t = new Template("adhoc", ftl, getConfiguration());
}
t.process(getDataModel(), new StringWriter());
fail("The tempalte had to fail");
return null;
} catch (TemplateException e) {
if (exceptionClass != null) {
assertThat(e, instanceOf(exceptionClass));
}
assertContainsAll(e.getMessageWithoutStackTop(), expectedSubstrings);
return e;
} catch (ParseException e) {
if (exceptionClass != null) {
assertThat(e, instanceOf(exceptionClass));
}
assertContainsAll(e.getEditorMessage(), expectedSubstrings);
return e;
} catch (Exception e) {
if (exceptionClass != null) {
assertThat(e, instanceOf(exceptionClass));
return e;
} else {
throw new RuntimeException("Unexpected exception class: " + e.getClass().getName(), e);
}
}
}
private void assertContainsAll(String msg, String... expectedSubstrings) {
for (String needle: expectedSubstrings) {
if (needle.startsWith("\\!")) {
String netNeedle = needle.substring(2);
if (msg.contains(netNeedle)) {
fail("The message shouldn't contain substring " + StringUtil.jQuote(netNeedle) + ":\n" + msg);
}
} else if (!msg.contains(needle)) {
fail("The message didn't contain substring " + StringUtil.jQuote(needle) + ":\n" + msg);
}
}
}
private String normalizeNewLines(String s) {
return StringUtil.replace(s, "\r\n", "\n").replace('\r', '\n');
}
public static class TestBean {
private int x;
private boolean b;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public boolean isB() {
return b;
}
public void setB(boolean b) {
this.b = b;
}
public int intM() {
return 1;
}
public int intMP(int x) {
return x;
}
public void voidM() {
}
}
}