1   package org.apache.velocity.test;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.StringWriter;
25  
26  import junit.framework.TestCase;
27  import org.apache.velocity.VelocityContext;
28  import org.apache.velocity.app.Velocity;
29  import org.apache.velocity.app.VelocityEngine;
30  import org.apache.velocity.runtime.RuntimeConstants;
31  import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
32  import org.apache.velocity.runtime.resource.util.StringResourceRepository;
33  import org.apache.velocity.test.misc.TestLogChute;
34  import org.apache.velocity.util.StringUtils;
35  
36  /**
37   * Base test case that provides utility methods for
38   * the rest of the tests.
39   *
40   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
41   * @author Nathan Bubna
42   * @version $Id: BaseTestCase.java 1053539 2010-12-29 06:08:17Z nbubna $
43   */
44  public abstract class BaseTestCase extends TestCase implements TemplateTestBase
45  {
46      protected VelocityEngine engine;
47      protected VelocityContext context;
48      protected boolean DEBUG = false;
49      protected TestLogChute log;
50      protected String stringRepoName = "string.repo";
51  
52      public BaseTestCase(String name)
53      {
54          super(name);
55  
56          // if we're just running one case, then have DEBUG
57          // automatically set to true
58          String test = System.getProperty("test");
59          if (test != null)
60          {
61              DEBUG = test.equals(getClass().getSimpleName());
62          }
63      }
64  
65      protected void setUp() throws Exception
66      {
67          engine = new VelocityEngine();
68  
69          //by default, make the engine's log output go to the test-report
70          log = new TestLogChute(false, false);
71          log.setEnabledLevel(TestLogChute.INFO_ID);
72          log.setSystemErrLevel(TestLogChute.WARN_ID);
73          engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, log);
74  
75          // use string resource loader by default, instead of file
76          engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "file,string");
77          engine.addProperty("string.resource.loader.class", StringResourceLoader.class.getName());
78          engine.addProperty("string.resource.loader.repository.name", stringRepoName);
79          engine.addProperty("string.resource.loader.repository.static", "false");
80  
81          setUpEngine(engine);
82  
83          context = new VelocityContext();
84          setUpContext(context);
85      }
86  
87      protected void setUpEngine(VelocityEngine engine)
88      {
89          // extension hook
90      }
91  
92      protected void setUpContext(VelocityContext context)
93      {
94          // extension hook
95      }
96  
97      protected StringResourceRepository getStringRepository()
98      {
99          StringResourceRepository repo =
100             (StringResourceRepository)engine.getApplicationAttribute(stringRepoName);
101         if (repo == null)
102         {
103             engine.init();
104             repo =
105                 (StringResourceRepository)engine.getApplicationAttribute(stringRepoName);
106         }
107         return repo;
108     }
109 
110     protected void addTemplate(String name, String template)
111     {
112         info("Template '"+name+"':  "+template);
113         getStringRepository().putStringResource(name, template);
114     }
115 
116     protected void removeTemplate(String name)
117     {
118         info("Removed: '"+name+"'");
119         getStringRepository().removeStringResource(name);
120     }
121 
122     public void tearDown()
123     {
124         engine = null;
125         context = null;
126     }
127 
128     protected void info(String msg)
129     {
130         info(msg, null);
131     }
132 
133     protected void info(String msg, Throwable t)
134     {
135         if (DEBUG)
136         {
137             try
138             {
139                 if (engine == null)
140                 {
141                     Velocity.getLog().info(msg, t);
142                 }
143                 else
144                 {
145                     engine.getLog().info(msg, t);
146                 }
147             }
148             catch (Throwable t2)
149             {
150                 System.out.println("Failed to log: "+msg+(t!=null?" - "+t: ""));
151                 System.out.println("Cause: "+t2);
152                 t2.printStackTrace();
153             }
154         }
155     }
156 
157     public void testBase()
158     {
159         if (DEBUG && engine != null)
160         {
161             assertSchmoo("");
162             assertSchmoo("abc\n123");
163         }
164     }
165 
166     /**
167      * Compare an expected string with the given loaded template
168      */
169     protected void assertTmplEquals(String expected, String template)
170     {
171         info("Expected:  " + expected + " from '" + template + "'");
172 
173         StringWriter writer = new StringWriter();
174         try
175         {
176             engine.mergeTemplate(template, "utf-8", context, writer);
177         }
178         catch (RuntimeException re)
179         {
180             info("RuntimeException!", re);
181             throw re;
182         }
183         catch (Exception e)
184         {
185             info("Exception!", e);
186             throw new RuntimeException(e);
187         }
188 
189         info("Result:  " + writer.toString());
190         assertEquals(expected, writer.toString());
191     }
192 
193     /**
194      * Ensure that a context value is as expected.
195      */
196     protected void assertContextValue(String key, Object expected)
197     {
198         info("Expected value of '"+key+"': "+expected);
199         Object value = context.get(key);
200         info("Result: "+value);
201         assertEquals(expected, value);
202     }
203 
204     /**
205      * Ensure that a template renders as expected.
206      */
207     protected void assertEvalEquals(String expected, String template)
208     {
209         info("Expectation: "+expected);
210         assertEquals(expected, evaluate(template));
211     }
212 
213     /**
214      * Ensure that the given string renders as itself when evaluated.
215      */
216     protected void assertSchmoo(String templateIsExpected)
217     {
218         assertEvalEquals(templateIsExpected, templateIsExpected);
219     }
220 
221     /**
222      * Ensure that an exception occurs when the string is evaluated.
223      */
224     protected Exception assertEvalException(String evil)
225     {
226         return assertEvalException(evil, null);
227     }
228 
229     /**
230      * Ensure that a specified type of exception occurs when evaluating the string.
231      */
232     protected Exception assertEvalException(String evil, Class exceptionType)
233     {
234         try
235         {
236             if (!DEBUG)
237             {
238                 log.off();
239             }
240             if (exceptionType != null)
241             {
242                 info("Expectation: "+exceptionType.getName());
243             }
244             else
245             {
246                 info("Expectation: "+Exception.class.getName());
247             }
248             evaluate(evil);
249             String msg = "Template '"+evil+"' should have thrown an exception.";
250             info("Fail: "+msg);
251             fail(msg);
252         }
253         catch (Exception e)
254         {
255             if (exceptionType != null && !exceptionType.isAssignableFrom(e.getClass()))
256             {
257                 String msg = "Was expecting template '"+evil+"' to throw "+exceptionType+" not "+e;
258                 info("Fail: "+msg);
259                 fail(msg);
260             }
261             return e;
262         }
263         finally
264         {
265             if (!DEBUG)
266             {
267                 log.on();
268             }
269         }
270         return null;
271     }
272 
273     /**
274      * Ensure that the error message of the expected exception has the proper location info.
275      */
276     protected Exception assertEvalExceptionAt(String evil, String template,
277                                               int line, int col)
278     {
279         String loc = template+"[line "+line+", column "+col+"]";
280         info("Expectation: Exception at "+loc);
281         Exception e = assertEvalException(evil);
282 
283         info("Result: "+e.getClass().getName()+" - "+e.getMessage());
284         if (e.getMessage().indexOf(loc) < 1)
285         {
286             fail("Was expecting exception at "+loc+" instead of "+e.getMessage());
287         }
288         return e;
289     }
290 
291     /**
292      * Only ensure that the error message of the expected exception
293      * has the proper line and column info.
294      */
295     protected Exception assertEvalExceptionAt(String evil, int line, int col)
296     {
297          return assertEvalExceptionAt(evil, "", line, col);
298     }
299 
300     /**
301      * Evaluate the specified String as a template and return the result as a String.
302      */
303     protected String evaluate(String template)
304     {
305         StringWriter writer = new StringWriter();
306         try
307         {
308             info("Template: "+template);
309 
310             // use template as its own name, since our templates are short
311             // unless it's not that short, then shorten it...
312             String name = (template.length() <= 15) ? template : template.substring(0,15);
313             engine.evaluate(context, writer, name, template);
314 
315             String result = writer.toString();
316             info("Result: "+result);
317             return result;
318         }
319         catch (RuntimeException re)
320         {
321             info("RuntimeException!", re);
322             throw re;
323         }
324         catch (Exception e)
325         {
326             info("Exception!", e);
327             throw new RuntimeException(e);
328         }
329     }
330 
331     /**
332      * Concatenates the file name parts together appropriately.
333      *
334      * @return The full path to the file.
335      */
336     protected String getFileName(final String dir, final String base, final String ext)
337     {
338         return getFileName(dir, base, ext, false);
339     }
340 
341     protected String getFileName(final String dir, final String base, final String ext, final boolean mustExist)
342     {
343         StringBuffer buf = new StringBuffer();
344         try
345         {
346             File baseFile = new File(base);
347             if (dir != null)
348             {
349                 if (!baseFile.isAbsolute())
350                 {
351                     baseFile = new File(dir, base);
352                 }
353 
354                 buf.append(baseFile.getCanonicalPath());
355             }
356             else
357             {
358                 buf.append(baseFile.getPath());
359             }
360 
361             if (org.apache.commons.lang.StringUtils.isNotEmpty(ext))
362             {
363                 buf.append('.').append(ext);
364             }
365 
366             if (mustExist)
367             {
368                 File testFile = new File(buf.toString());
369 
370                 if (!testFile.exists())
371                 {
372                     String msg = "getFileName() result " + testFile.getPath() + " does not exist!";
373                     info(msg);
374                     fail(msg);
375                 }
376 
377                 if (!testFile.isFile())
378                 {
379                     String msg = "getFileName() result " + testFile.getPath() + " is not a file!";
380                     info(msg);
381                     fail(msg);
382                 }
383             }
384         }
385         catch (IOException e)
386         {
387             fail("IO Exception while running getFileName(" + dir + ", " + base + ", "+ ext + ", " + mustExist + "): " + e.getMessage());
388         }
389 
390         return buf.toString();
391     }
392 
393     /**
394      * Assures that the results directory exists.  If the results directory
395      * cannot be created, fails the test.
396      */
397     protected void assureResultsDirectoryExists(String resultsDirectory)
398     {
399         File dir = new File(resultsDirectory);
400         if (!dir.exists())
401         {
402             info("Template results directory ("+resultsDirectory+") does not exist");
403             if (dir.mkdirs())
404             {
405                 info("Created template results directory");
406                 if (DEBUG)
407                 {
408                     info("Created template results directory: "+resultsDirectory);
409                 }
410             }
411             else
412             {
413                 String errMsg = "Unable to create '"+resultsDirectory+"'";
414                 info(errMsg);
415                 fail(errMsg);
416             }
417         }
418     }
419 
420 
421     /**
422      * Normalizes lines to account for platform differences.  Macs use
423      * a single \r, DOS derived operating systems use \r\n, and Unix
424      * uses \n.  Replace each with a single \n.
425      *
426      * @return source with all line terminations changed to Unix style
427      */
428     protected String normalizeNewlines (String source)
429     {
430         return source.replaceAll("\r\n?", "\n");
431     }
432 
433     /**
434      * Returns whether the processed template matches the
435      * content of the provided comparison file.
436      *
437      * @return Whether the output matches the contents
438      *         of the comparison file.
439      *
440      * @exception Exception Test failure condition.
441      */
442     protected boolean isMatch (String resultsDir,
443                                String compareDir,
444                                String baseFileName,
445                                String resultExt,
446                                String compareExt) throws Exception
447     {
448         if (DEBUG)
449         {
450             info("Result: "+resultsDir+'/'+baseFileName+'.'+resultExt);
451         }
452         String result = getFileContents(resultsDir, baseFileName, resultExt);
453         return isMatch(result,compareDir,baseFileName,compareExt);
454     }
455 
456 
457     protected String getFileContents(String dir, String baseFileName, String ext)
458     {
459         String fileName = getFileName(dir, baseFileName, ext, true);
460         return StringUtils.fileContentsToString(fileName);
461     }
462 
463     /**
464      * Returns whether the processed template matches the
465      * content of the provided comparison file.
466      *
467      * @return Whether the output matches the contents
468      *         of the comparison file.
469      *
470      * @exception Exception Test failure condition.
471      */
472     protected boolean isMatch (String result,
473                                String compareDir,
474                                String baseFileName,
475                                String compareExt) throws Exception
476     {
477         String compare = getFileContents(compareDir, baseFileName, compareExt);
478 
479         // normalize each wrt newline
480         result = normalizeNewlines(result);
481         compare = normalizeNewlines(compare);
482         if (DEBUG)
483         {
484             info("Expection: "+compareDir+'/'+baseFileName+'.'+compareExt);
485         }
486         return result.equals(compare);
487     }
488 
489     /**
490      * Turns a base file name into a test case name.
491      *
492      * @param s The base file name.
493      * @return  The test case name.
494      */
495     protected static final String getTestCaseName(String s)
496     {
497         StringBuffer name = new StringBuffer();
498         name.append(Character.toTitleCase(s.charAt(0)));
499         name.append(s.substring(1, s.length()).toLowerCase());
500         return name.toString();
501     }
502 }