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.BufferedWriter;
23  import java.io.FileOutputStream;
24  import java.io.OutputStreamWriter;
25  import java.io.StringWriter;
26  import java.io.Writer;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.Map;
30  import org.apache.velocity.Template;
31  import org.apache.velocity.VelocityContext;
32  import org.apache.velocity.app.VelocityEngine;
33  import org.apache.velocity.app.event.implement.EscapeHtmlReference;
34  import org.apache.velocity.context.Context;
35  import org.apache.velocity.exception.ParseErrorException;
36  import org.apache.velocity.runtime.RuntimeConstants;
37  
38  /**
39   * Test #evaluate directive.
40   * 
41   * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
42   * @version $Id: EvaluateTestCase.java 778045 2009-05-23 22:17:46Z nbubna $
43   */
44  public class EvaluateTestCase extends BaseTestCase
45  {
46      
47      /**
48      * VTL file extension.
49      */
50     private static final String TMPL_FILE_EXT = "vm";
51  
52     /**
53      * Comparison file extension.
54      */
55     private static final String CMP_FILE_EXT = "cmp";
56  
57     /**
58      * Comparison file extension.
59      */
60     private static final String RESULT_FILE_EXT = "res";
61  
62     /**
63      * Path for templates. This property will override the
64      * value in the default velocity properties file.
65      */
66     private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/evaluate";
67  
68     /**
69      * Results relative to the build directory.
70      */
71     private static final String RESULTS_DIR = TEST_RESULT_DIR + "/evaluate";
72  
73     /**
74      * Results relative to the build directory.
75      */
76     private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/evaluate/compare";
77  
78      /**
79       * Default constructor.
80       * @param name
81       */
82      public EvaluateTestCase(String name)
83      {
84          super(name);
85      }
86  
87      public void setUp() throws Exception
88      {
89          super.setUp();
90          assureResultsDirectoryExists(RESULTS_DIR);
91      }
92  
93      /**
94       * Test basic functionality.
95       * @throws Exception
96       */
97      public void testEvaluate()
98      throws Exception
99      {
100         Map props = new HashMap();
101         props.put(RuntimeConstants.EVALUATE_CONTEXT_CLASS, VelocityContext.class.getName());
102         testFile("eval1", props);
103     }
104 
105     /**
106      * Test evaluate directive preserves macros (VELOCITY-591)
107      * @throws Exception
108      */
109     public void testEvaluateMacroPreserve()
110     throws Exception
111     {
112         Map properties = new HashMap();
113         properties.clear();
114         properties.put(RuntimeConstants.VM_CONTEXT_LOCALSCOPE,"false");
115         testFile("eval2", properties);
116 
117         properties.clear();
118         properties.put(RuntimeConstants.VM_CONTEXT_LOCALSCOPE,"true");
119         testFile("eval2", properties);
120 
121         properties.clear();
122         properties.put(RuntimeConstants.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL,"false");
123         testFile("eval2", properties);
124 
125 
126     }
127 
128     /**
129      * Test in a macro context.
130      * @throws Exception
131      */
132     public void testEvaluateVMContext()
133     throws Exception
134     {
135         testFile("evalvmcontext", new HashMap());
136     }
137 
138     /**
139      * Test #stop and #break
140      * @throws Exception
141      */
142     public void testStopAndBreak()
143     {
144         engine.setProperty("evaluate.provide.scope.control", "true");
145         assertEvalEquals("t ", "t #stop t2 #evaluate('t3')");
146         assertEvalEquals("t ", "t #break t2 #evaluate('t3')");
147         //assertEvalEquals("t t2 t3 ", "t t2 #evaluate('t3 #stop t4') t5");
148         assertEvalEquals("t t2 t3  t5", "t t2 #evaluate('t3 #break t4') t5");
149         assertEvalEquals("t t2 t3 ", "t t2 #evaluate('t3 #break($evaluate.topmost) t4') t5");
150     }
151 
152     /**
153      * Test that the event handlers work in #evaluate (since they are
154      * attached to the context).  Only need to check one - they all 
155      * work the same.
156      * @throws Exception
157      */
158     public void testEventHandler()
159     throws Exception
160     {
161         VelocityEngine ve = new VelocityEngine();
162         ve.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, EscapeHtmlReference.class.getName());
163         ve.init();
164         
165         Context context = new VelocityContext();
166         context.put("lt","<");
167         context.put("gt",">");
168         StringWriter writer = new StringWriter();
169         ve.evaluate(context, writer, "test","${lt}test${gt} #evaluate('${lt}test2${gt}')");
170         assertEquals("&lt;test&gt; &lt;test2&gt;", writer.toString());
171         
172     }
173     
174     
175     /**
176      * Test errors are thrown
177      * @throws Exception
178      */
179     public void testErrors()
180     throws Exception
181     {
182         VelocityEngine ve = new VelocityEngine();
183         ve.init();
184         
185         Context context = new VelocityContext();
186         
187         // no arguments
188         StringWriter writer = new StringWriter();
189         try 
190         {
191             ve.evaluate(context, writer, "test",
192                               "#evaluate()");
193             fail("Expected exception");
194         }
195         catch (ParseErrorException e)
196         {
197             assertEquals("test",e.getTemplateName());
198             assertEquals(1,e.getLineNumber());
199             assertEquals(1,e.getColumnNumber());
200         }
201         
202         // too many arguments
203         writer = new StringWriter();
204         try 
205         {
206             ve.evaluate(context, writer, "test",
207                               "#evaluate('aaa' 'bbb')");
208             fail("Expected exception");
209         }
210         catch (ParseErrorException e)
211         {
212             assertEquals("test",e.getTemplateName());
213             assertEquals(1,e.getLineNumber());
214             assertEquals(17,e.getColumnNumber());
215         }
216         
217         // argument not a string or reference
218         writer = new StringWriter();
219         try 
220         {
221             ve.evaluate(context, writer, "test",
222                               "#evaluate(10)");
223             fail("Expected exception");
224         }
225         catch (ParseErrorException e)
226         {
227             assertEquals("test",e.getTemplateName());
228             assertEquals(1,e.getLineNumber());
229             assertEquals(11,e.getColumnNumber());
230         }
231         
232         // checking line/col for parse error
233         writer = new StringWriter();
234         try 
235         {
236             String eval = "this is a multiline\n\n\n\n\n test #foreach() with an error";
237             context.put("eval",eval);
238             ve.evaluate(context, writer, "test",
239                               "first line\n second line: #evaluate($eval)");
240             fail("Expected exception");
241         }
242         catch (ParseErrorException e)
243         {
244             // should be start of #evaluate
245             assertEquals("test",e.getTemplateName());
246             assertEquals(2,e.getLineNumber());
247             assertEquals(15,e.getColumnNumber());
248         }        
249     }
250     
251     /**
252      * Test a file parses with no errors and compare to existing file.
253      * @param basefilename
254      * @throws Exception
255      */
256     private void testFile(String basefilename, Map properties)
257     throws Exception
258     {
259         info("Test file: "+basefilename);
260         VelocityEngine ve = engine;
261         ve.addProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
262      
263         for (Iterator i = properties.keySet().iterator(); i.hasNext();)
264         {
265             String key = (String) i.next();
266             String value = (String) properties.get(key);
267             ve.addProperty(key, value);
268             info("Add property: "+key+" = "+value);
269         }
270         
271         ve.init();
272         
273         Template template;
274         FileOutputStream fos;
275         Writer fwriter;
276         
277         template = ve.getTemplate( getFileName(null, basefilename, TMPL_FILE_EXT) );
278         
279         fos = new FileOutputStream (
280                 getFileName(RESULTS_DIR, basefilename, RESULT_FILE_EXT));
281         
282         fwriter = new BufferedWriter( new OutputStreamWriter(fos) );
283         
284         template.merge(context, fwriter);
285         fwriter.flush();
286         fwriter.close();
287         
288         if (!isMatch(RESULTS_DIR, COMPARE_DIR, basefilename, RESULT_FILE_EXT, CMP_FILE_EXT))
289         {
290             String result = getFileContents(RESULTS_DIR, basefilename, RESULT_FILE_EXT);
291             String compare = getFileContents(COMPARE_DIR, basefilename, CMP_FILE_EXT);
292 
293             String msg = "Output was incorrect\n"+
294                 "-----Result-----\n"+ result +
295                 "----Expected----\n"+ compare +
296                 "----------------";
297             
298             fail(msg);
299         }
300     }
301 
302 }