001: /*
002: * Copyright (c) 2005 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.testcase;
054:
055: import freemarker.template.*;
056: import freemarker.ext.beans.*;
057: import freemarker.ext.dom.NodeModel;
058: import freemarker.ext.jdom.NodeListModel;
059: import freemarker.testcase.models.*;
060: import freemarker.template.utility.*;
061: import freemarker.template.utility.NormalizeNewlines;
062: import freemarker.testcase.models.TransformHashWrapper;
063: import junit.framework.*;
064: import java.util.*;
065: import java.io.*;
066: import java.net.URL;
067: import javax.xml.parsers.DocumentBuilder;
068: import javax.xml.parsers.DocumentBuilderFactory;
069: import org.jdom.input.SAXBuilder;
070: import org.xml.sax.InputSource;
071:
072: public class TemplateTestCase extends TestCase {
073:
074: Template template;
075: HashMap dataModel = new HashMap();
076:
077: String filename, testName;
078: String inputDir = "template";
079: String referenceDir = "reference";
080: File outputDir;
081:
082: Configuration conf = new Configuration();
083:
084: public TemplateTestCase(String name, String filename) {
085: super (name);
086: this .filename = filename;
087: this .testName = name;
088: }
089:
090: public void setTemplateDirectory(String dirname) throws IOException {
091: URL url = getClass().getResource("TemplateTestCase.class");
092: File parent = new File(url.getFile()).getParentFile();
093: File dir = new File(parent, dirname);
094: conf.setDirectoryForTemplateLoading(dir);
095: System.out.println("Setting loading directory as: " + dir);
096: }
097:
098: public void setOutputDirectory(String dirname) {
099: URL url = getClass().getResource("TemplateTestCase.class");
100: File parent = new File(url.getFile()).getParentFile();
101: this .outputDir = new File(parent, dirname);
102: System.out.println("Setting reference directory as: "
103: + outputDir);
104: }
105:
106: public void setConfigParam(String param, String value)
107: throws IOException {
108: if ("templatedir".equals(param)) {
109: setTemplateDirectory(value);
110: } else if ("auto_import".equals(param)) {
111: StringTokenizer st = new StringTokenizer(value);
112: if (!st.hasMoreTokens())
113: fail("Expecting libname");
114: String libname = st.nextToken();
115: if (!st.hasMoreTokens())
116: fail("Expecting 'as <alias>' in autoimport");
117: String as = st.nextToken();
118: if (!as.equals("as"))
119: fail("Expecting 'as <alias>' in autoimport");
120: if (!st.hasMoreTokens())
121: fail("Expecting alias after 'as' in autoimport");
122: String alias = st.nextToken();
123: conf.addAutoImport(alias, libname);
124: } else if ("clear_encoding_map".equals(param)) {
125: if (StringUtil.getYesNo(value)) {
126: conf.clearEncodingMap();
127: }
128: } else if ("input_encoding".equals(param)) {
129: conf.setDefaultEncoding(value);
130: } else if ("outputdir".equals(param)) {
131: setOutputDirectory(value);
132: } else if ("output_encoding".equals(param)) {
133: conf.setOutputEncoding(value);
134: } else if ("locale".equals(param)) {
135: String lang = "", country = "", variant = "";
136: StringTokenizer st = new StringTokenizer(value, "_", false);
137: if (st.hasMoreTokens()) {
138: lang = st.nextToken();
139: }
140: if (st.hasMoreTokens()) {
141: country = st.nextToken();
142: }
143: if (st.hasMoreTokens()) {
144: variant = st.nextToken();
145: }
146: if (lang != "") {
147: Locale loc = new Locale(lang, country, variant);
148: conf.setLocale(loc);
149: }
150: } else if ("object_wrapper".equals(param)) {
151: try {
152: Class cl = Class.forName(value);
153: ObjectWrapper ow = (ObjectWrapper) cl.newInstance();
154: conf.setObjectWrapper(ow);
155: } catch (Exception e) {
156: fail("Error setting object wrapper to " + value + "\n"
157: + e.getMessage());
158: }
159: } else if ("input_encoding".equals(param)) {
160: conf.setDefaultEncoding(value);
161: } else if ("output_encoding".equals(param)) {
162: conf.setOutputEncoding(value);
163: } else if ("strict_syntax".equals(param)) {
164: boolean b = StringUtil.getYesNo(value);
165: conf.setStrictSyntaxMode(b);
166: } else if ("url_escaping_charset".equals(param)) {
167: conf.setURLEscapingCharset(value);
168: }
169: }
170:
171: /*
172: * This method just contains all the code to seed the data model
173: * ported over from the individual classes. This seems ugly and unnecessary.
174: * We really might as well just expose pretty much
175: * the same tree to all our tests. (JR)
176: */
177:
178: public void setUp() throws Exception {
179: dataModel.put("message", "Hello, world!");
180:
181: if (testName.equals("bean-maps")) {
182: BeansWrapper w1 = new BeansWrapper();
183: BeansWrapper w2 = new BeansWrapper();
184: BeansWrapper w3 = new BeansWrapper();
185: BeansWrapper w4 = new BeansWrapper();
186: BeansWrapper w5 = new BeansWrapper();
187: BeansWrapper w6 = new BeansWrapper();
188: BeansWrapper w7 = new BeansWrapper();
189:
190: w1.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
191: w2.setExposureLevel(BeansWrapper.EXPOSE_PROPERTIES_ONLY);
192: w3.setExposureLevel(BeansWrapper.EXPOSE_NOTHING);
193: w4.setExposureLevel(BeansWrapper.EXPOSE_NOTHING);
194: w5.setExposureLevel(BeansWrapper.EXPOSE_ALL);
195: w6.setExposureLevel(BeansWrapper.EXPOSE_ALL);
196:
197: w1.setMethodsShadowItems(true);
198: w2.setMethodsShadowItems(false);
199: w3.setMethodsShadowItems(true);
200: w4.setMethodsShadowItems(false);
201: w5.setMethodsShadowItems(true);
202: w6.setMethodsShadowItems(false);
203:
204: w7.setSimpleMapWrapper(true);
205:
206: Object test = getTestBean();
207:
208: dataModel.put("m1", w1.wrap(test));
209: dataModel.put("m2", w2.wrap(test));
210: dataModel.put("m3", w3.wrap(test));
211: dataModel.put("m4", w4.wrap(test));
212: dataModel.put("m5", w5.wrap(test));
213: dataModel.put("m6", w6.wrap(test));
214: dataModel.put("m7", w7.wrap(test));
215:
216: dataModel.put("s1", w1.wrap("hello"));
217: dataModel.put("s2", w1.wrap("world"));
218: dataModel.put("s3", w5.wrap("hello"));
219: dataModel.put("s4", w5.wrap("world"));
220: }
221:
222: else if (testName.equals("beans")) {
223: dataModel.put("array",
224: new String[] { "array-0", "array-1" });
225: dataModel.put("list", Arrays.asList(new String[] {
226: "list-0", "list-1", "list-2" }));
227: Map tmap = new HashMap();
228: tmap.put("key", "value");
229: Object objKey = new Object();
230: tmap.put(objKey, "objValue");
231: dataModel.put("map", tmap);
232: dataModel.put("objKey", objKey);
233: dataModel.put("obj",
234: new freemarker.testcase.models.BeanTestClass());
235: dataModel
236: .put(
237: "resourceBundle",
238: new ResourceBundleModel(
239: ResourceBundle
240: .getBundle("freemarker.testcase.models.BeansTestResources"),
241: BeansWrapper.getDefaultInstance()));
242: dataModel.put("date", new GregorianCalendar(1974, 10, 14)
243: .getTime());
244: dataModel.put("statics", BeansWrapper.getDefaultInstance()
245: .getStaticModels());
246: }
247:
248: else if (testName.equals("boolean")) {
249: dataModel.put("boolean1", TemplateBooleanModel.FALSE);
250: dataModel.put("boolean2", TemplateBooleanModel.TRUE);
251: dataModel.put("boolean3", TemplateBooleanModel.TRUE);
252: dataModel.put("boolean4", TemplateBooleanModel.TRUE);
253: dataModel.put("boolean5", TemplateBooleanModel.FALSE);
254:
255: dataModel.put("list1", new BooleanList1());
256: dataModel.put("list2", new BooleanList2());
257:
258: dataModel.put("hash1", new BooleanHash1());
259: dataModel.put("hash2", new BooleanHash2());
260: }
261:
262: else if (testName.equals("dateformat")) {
263: GregorianCalendar cal = new GregorianCalendar(2002, 10, 15,
264: 14, 54, 13);
265: cal.setTimeZone(TimeZone.getTimeZone("GMT"));
266: dataModel.put("date", new SimpleDate(cal.getTime(),
267: TemplateDateModel.DATETIME));
268: dataModel.put("unknownDate", new SimpleDate(cal.getTime(),
269: TemplateDateModel.UNKNOWN));
270: } else if (testName.equals("default-xmlns")) {
271: InputSource is = new InputSource(getClass()
272: .getResourceAsStream("test-defaultxmlns1.xml"));
273: NodeModel nm = NodeModel.parse(is);
274: dataModel.put("doc", nm);
275: }
276:
277: else if (testName.equals("multimodels")) {
278: dataModel.put("test", "selftest");
279: dataModel.put("self", "self");
280: dataModel.put("zero", new Integer(0));
281: dataModel.put("data", new MultiModel1());
282: }
283:
284: else if (testName.equals("nodelistmodel")) {
285: org.jdom.Document doc = new SAXBuilder()
286: .build(new InputSource(getClass()
287: .getResourceAsStream("test-xml.xml")));
288: dataModel.put("doc", new NodeListModel(doc));
289: }
290:
291: else if (testName.equals("string-builtins3")) {
292: dataModel.put("multi", new TestBoolean());
293: }
294:
295: else if (testName.equals("type-builtins")) {
296: dataModel.put("testmethod", new TestMethod());
297: dataModel.put("testnode", new TestNode());
298: dataModel.put("testcollection", new SimpleCollection(
299: new ArrayList()));
300: }
301:
302: else if (testName.equals("var-layers")) {
303: dataModel.put("x", new Integer(4));
304: dataModel.put("z", new Integer(4));
305: conf.setSharedVariable("y", new Integer(7));
306: }
307:
308: else if (testName.equals("xml-fragment")) {
309: DocumentBuilderFactory f = DocumentBuilderFactory
310: .newInstance();
311: f.setNamespaceAware(true);
312: DocumentBuilder db = f.newDocumentBuilder();
313: org.w3c.dom.Document doc = db.parse(new InputSource(
314: getClass().getResourceAsStream(
315: "test-xmlfragment.xml")));
316: dataModel.put("node", NodeModel.wrap(doc
317: .getDocumentElement().getFirstChild()
318: .getFirstChild()));
319: }
320:
321: else if (testName.equals("xmlns1")) {
322: InputSource is = new InputSource(getClass()
323: .getResourceAsStream("test-xmlns.xml"));
324: NodeModel nm = NodeModel.parse(is);
325: dataModel.put("doc", nm);
326: }
327:
328: else if (testName.equals("xmlns2")) {
329: InputSource is = new InputSource(getClass()
330: .getResourceAsStream("test-xmlns2.xml"));
331: NodeModel nm = NodeModel.parse(is);
332: dataModel.put("doc", nm);
333: }
334:
335: else if (testName.equals("xmlns3") || testName.equals("xmlns4")) {
336: InputSource is = new InputSource(getClass()
337: .getResourceAsStream("test-xmlns3.xml"));
338: NodeModel nm = NodeModel.parse(is);
339: dataModel.put("doc", nm);
340: }
341:
342: else if (testName.equals("xmlns5")) {
343: InputSource is = new InputSource(getClass()
344: .getResourceAsStream("test-defaultxmlns1.xml"));
345: NodeModel nm = NodeModel.parse(is);
346: dataModel.put("doc", nm);
347: }
348: }
349:
350: public void runTest() {
351: try {
352: template = conf.getTemplate(filename);
353: } catch (Exception e) {
354: StringWriter sw = new StringWriter();
355: PrintWriter pw = new PrintWriter(sw);
356: e.printStackTrace(pw);
357: fail("Could not load template " + filename + "\n"
358: + sw.toString());
359: }
360: File refFile = new File(outputDir, filename);
361: File outFile = new File(outputDir, filename + ".out");
362: Writer out = null;
363: String encoding = conf.getOutputEncoding();
364: if (encoding == null)
365: encoding = "UTF-8";
366: try {
367: out = new OutputStreamWriter(new FileOutputStream(outFile),
368: encoding);
369: } catch (IOException ioe) {
370: fail("Cannot write to file: " + outFile + "\n"
371: + ioe.getMessage());
372: }
373: try {
374: template.process(dataModel, out);
375: out.close();
376: } catch (Exception e) {
377: StringWriter sw = new StringWriter();
378: PrintWriter pw = new PrintWriter(sw);
379: e.printStackTrace(pw);
380: fail("Could not process template " + filename + "\n"
381: + sw.toString());
382: }
383: try {
384: Reader ref = new InputStreamReader(new FileInputStream(
385: refFile), encoding);
386: Reader output = new InputStreamReader(new FileInputStream(
387: outFile), encoding);
388: compare(ref, output);
389: } catch (IOException e) {
390: StringWriter sw = new StringWriter();
391: PrintWriter pw = new PrintWriter(sw);
392: e.printStackTrace(pw);
393: fail("Error comparing files " + refFile + " and " + outFile
394: + "\n" + sw.toString());
395: }
396: outFile.delete();
397: }
398:
399: static public void compare(Reader reference, Reader output)
400: throws IOException {
401: LineNumberReader ref = new LineNumberReader(reference);
402: LineNumberReader out = new LineNumberReader(output);
403: String refLine = "", outLine = "";
404: while (refLine != null || outLine != null) {
405: if (refLine == null) {
406: fail("Output text is longer than reference text");
407: }
408: if (outLine == null) {
409: fail("Output text is shorter than reference text");
410: }
411: refLine = ref.readLine();
412: outLine = out.readLine();
413: if (refLine != null && outLine != null
414: & !refLine.equals(outLine)) {
415: fail("Difference found on line " + ref.getLineNumber()
416: + ".\nReference text is: " + refLine
417: + "\nOutput text is: " + outLine);
418: }
419: }
420: }
421:
422: static class TestBoolean implements TemplateBooleanModel,
423: TemplateScalarModel {
424: public boolean getAsBoolean() {
425: return true;
426: }
427:
428: public String getAsString() {
429: return "de";
430: }
431: }
432:
433: static class TestMethod implements TemplateMethodModel {
434: public Object exec(java.util.List arguments) {
435: return "x";
436: }
437: }
438:
439: static class TestNode implements TemplateNodeModel {
440:
441: public String getNodeName() {
442: return "name";
443: }
444:
445: public TemplateNodeModel getParentNode() {
446: return null;
447: }
448:
449: public String getNodeType() {
450: return "element";
451: }
452:
453: public TemplateSequenceModel getChildNodes() {
454: return null;
455: }
456:
457: public String getNodeNamespace() {
458: return null;
459: }
460: }
461:
462: public Object getTestBean() {
463: Map testBean = new TestBean();
464: testBean.put("name", "Chris");
465: testBean.put("location", "San Francisco");
466: testBean.put("age", new Integer(27));
467: return testBean;
468: }
469:
470: public static class TestBean extends HashMap {
471: public String getName() {
472: return "Christopher";
473: }
474:
475: public int getLuckyNumber() {
476: return 7;
477: }
478: }
479: }
|