aboutsummaryrefslogtreecommitdiff
path: root/velocity-engine-core/src/test/java/org/apache/velocity
diff options
context:
space:
mode:
Diffstat (limited to 'velocity-engine-core/src/test/java/org/apache/velocity')
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/io/UnicodeInputStreamTestCase.java239
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/runtime/RuntimeInstanceTest.java49
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/AbsoluteFileResourceLoaderTestCase.java150
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/AlternateValuesTestCase.java61
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ArithmeticTestCase.java244
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ArrayMethodsTestCase.java211
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/BaseTestCase.java515
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/BlockMacroTestCase.java141
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/BreakDirectiveTestCase.java117
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/BuiltInEventHandlerTestCase.java592
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ClassloaderChangeTestCase.java169
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ClasspathResourceTestCase.java166
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/CommentsTestCase.java108
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/CommonsExtPropTestCase.java178
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ContextAutoreferenceKeyTestCase.java58
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ContextSafetyTestCase.java145
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/DefineTestCase.java120
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/EncodingTestCase.java208
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/EvaluateTestCase.java299
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/EventHandlingTestCase.java271
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ExceptionTestCase.java171
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ExpressionAsMethodArgumentTestCase.java56
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/FilteredEventHandlingTestCase.java238
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ForeachTestCase.java147
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/GetAsTestCase.java145
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/HyphenInIdentifiersTestCase.java50
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyNoEmptyCheckTestCase.java120
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyTestCase.java144
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/IfNullTestCase.java101
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/IncludeErrorTestCase.java119
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/IncludeEventHandlingTestCase.java250
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/IndexTestCase.java171
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/InfoTestCase.java118
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/InlineScopeVMTestCase.java134
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/IntrospectionCacheDataTestCase.java81
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector2TestCase.java168
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector3TestCase.java151
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/IntrospectorTestCase.java226
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/InvalidEventHandlerTestCase.java646
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MacroAutoReloadTestCase.java103
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MacroCommentsTestCase.java56
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MacroDefaultArgTestCase.java73
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MacroForwardDefineTestCase.java119
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MethodCacheKeyTestCase.java88
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MethodInvocationExceptionTestCase.java278
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MethodOverloadingTestCase.java184
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MiscTestCase.java74
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MultiLoaderTestCase.java223
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/MultipleFileResourcePathTestCase.java144
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/NumberMethodCallsTestCase.java142
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/OldPropertiesTestCase.java170
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ParseExceptionTestCase.java179
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ParseWithMacroLibsTestCase.java309
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ParserTestCase.java201
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/PropertyMethodPrecedenceTestCase.java103
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/RenderVelocityTemplateTest.java155
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceCachingTestCase.java94
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/ResourceExistsTestCase.java105
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceLoaderInstanceTestCase.java154
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/ScopeTestCase.java369
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/SecureIntrospectionTestCase.java179
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/SetTestCase.java144
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/SpaceGobblingTestCase.java143
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/StaticUtilityMethodsTestCase.java57
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/StopDirectiveTestCase.java85
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/StrictAlternateValuesTestCase.java76
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/StrictCompareTestCase.java76
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/StrictEscapeTestCase.java139
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/StrictForeachTestCase.java110
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/StrictMathTestCase.java86
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/StrictReferenceTestCase.java257
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/StringConcatenationTestCase.java66
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/StringResourceLoaderRepositoryTestCase.java214
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/StringResourceLoaderTestCase.java209
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestBase.java84
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestCase.java237
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestSuite.java95
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/TestBaseTestCase.java64
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/TextblockTestCase.java163
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/URLResourceLoaderTimeoutTestCase.java82
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/UberspectorTestCase.java300
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/UnicodeEscapeTestCase.java61
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/VMLibraryTestCase.java325
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/VarargMethodsTestCase.java278
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/VelocimacroBCModeTestCase.java100
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/VelocimacroTestCase.java135
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/VelocityAppTestCase.java80
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/WrappedExceptionTestCase.java84
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/eventhandler/Handler1.java75
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/eventhandler/Handler2.java75
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/StackOverflow32805217TestCase.java39
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/VelTools66TestCase.java181
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity532TestCase.java52
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity537TestCase.java121
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity544TestCase.java75
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity579TestCase.java82
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity580TestCase.java116
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity587TestCase.java64
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity589TestCase.java40
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity614TestCase.java90
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity615TestCase.java139
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity616TestCase.java70
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity625TestCase.java40
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity627TestCase.java49
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity629TestCase.java55
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity62TestCase.java63
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity631TestCase.java49
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity644TestCase.java57
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity667TestCase.java39
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity682TestCase.java63
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity689TestCase.java78
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity701TestCase.java72
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity702TestCase.java99
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity709TestCase.java54
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity727TestCase.java40
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity728TestCase.java40
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity729TestCase.java46
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity736TestCase.java76
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity742TestCase.java57
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity747TestCase.java102
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity753TestCase.java62
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity755TestCase.java41
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity758TestCase.java66
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity762TestCase.java40
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity785TestCase.java42
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity830TestCase.java58
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity855TestCase.java47
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity889TestCase.java51
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity896TestCase.java40
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity904TestCase.java116
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity919TestCase.java23
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity924TestCase.java65
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity926TestCase.java38
-rwxr-xr-xvelocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity927TestCase.java39
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingDirective.java61
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingEventHandler.java55
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingResourceLoader.java62
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/misc/GetPutObject.java35
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/misc/TestContext.java85
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/misc/TestLogger.java360
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectTestException.java62
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectTestImpl.java66
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectorTestObject.java133
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/provider/BoolObj.java46
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/provider/Child.java37
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/provider/ForeachMethodCallHelper.java31
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/provider/NullToStringObject.java33
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/provider/NumberMethods.java70
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/provider/Person.java39
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/provider/TestNumber.java47
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/provider/TestProvider.java362
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/sql/BaseSQLTest.java96
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DBHelper.java123
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DataSourceResourceLoaderTestCase.java229
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/sql/TestDataSource.java105
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ChainedUberspectorsTestCase.java121
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ClassMapTestCase.java110
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java454
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/DeprecatedCheckUberspectorsTestCase.java108
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/EnumConstantConversionTestCase.java77
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/UberspectImplTestCase.java134
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/test/view/TemplateNodeView.java85
-rw-r--r--velocity-engine-core/src/test/java/org/apache/velocity/util/SimplePoolTestCase.java67
163 files changed, 20848 insertions, 0 deletions
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/io/UnicodeInputStreamTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/io/UnicodeInputStreamTestCase.java
new file mode 100644
index 00000000..68c10842
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/io/UnicodeInputStreamTestCase.java
@@ -0,0 +1,239 @@
+package org.apache.velocity.io;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+
+/**
+ * Test the UnicodeInputStream.
+ *
+ * @author $author$
+ * @version $Revision$, $Date$
+ */
+public class UnicodeInputStreamTestCase
+ extends TestCase
+{
+
+ public UnicodeInputStreamTestCase(final String name)
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(UnicodeInputStreamTestCase.class);
+ }
+
+ public void testSimpleStream()
+ throws Exception
+ {
+ testRun(null, "Ich bin zwei Oeltanks", "US-ASCII", true);
+ testRun(null, "Ich bin zwei Oeltanks", "US-ASCII", false);
+ }
+
+ public void testSimpleUTF8()
+ throws Exception
+ {
+ testRun(null, "Ich bin zwei Oeltanks", "UTF-8", true);
+ testRun(null, "Ich bin zwei Oeltanks", "UTF-8", false);
+ }
+
+ public void testRealUTF8()
+ throws Exception
+ {
+ testRun(null, "Ich bin zwei \u00d6ltanks", "UTF-8", true);
+ testRun(null, "Ich bin zwei \u00d6ltanks", "UTF-8", false);
+ }
+
+ public void testRealUTF8WithBOM()
+ throws Exception
+ {
+ testRun(UnicodeInputStream.UTF8_BOM, "Ich bin ein Test",
+ "UTF-8", true);
+ testRun(UnicodeInputStream.UTF8_BOM, "Ich bin ein Test",
+ "UTF-8", false);
+ }
+
+ public void testRealUTF16BEWithBOM()
+ throws Exception
+ {
+ testRun(UnicodeInputStream.UTF16BE_BOM, "Ich bin ein Test",
+ "UTF-16BE", true);
+ testRun(UnicodeInputStream.UTF16BE_BOM, "Ich bin ein Test",
+ "UTF-16BE", false);
+ }
+
+ public void testRealUTF16LEWithBOM()
+ throws Exception
+ {
+ testRun(UnicodeInputStream.UTF16LE_BOM, "Ich bin ein Test",
+ "UTF-16LE", true);
+ testRun(UnicodeInputStream.UTF16LE_BOM, "Ich bin ein Test",
+ "UTF-16LE", false);
+ }
+
+ public void testRealUTF32BEWithBOM()
+ throws Exception
+ {
+ testRun(UnicodeInputStream.UTF32BE_BOM, null,
+ "UTF-32BE", true);
+ testRun(UnicodeInputStream.UTF32BE_BOM, null,
+ "UTF-32BE", false);
+ }
+
+ public void testRealUTF32LEWithBOM()
+ throws Exception
+ {
+ testRun(UnicodeInputStream.UTF32LE_BOM, null,
+ "UTF-32LE", true);
+ testRun(UnicodeInputStream.UTF32LE_BOM, null,
+ "UTF-32LE", false);
+ }
+
+
+ protected void testRun(final UnicodeInputStream.UnicodeBOM bom, final String str, final String testEncoding, final boolean skipBOM)
+ throws Exception
+ {
+
+ byte [] testString = buildTestString(bom, str, testEncoding, skipBOM);
+
+ InputStream is = null;
+ UnicodeInputStream uis = null;
+
+ try
+ {
+ is = createInputStream(bom, str, testEncoding);
+ uis = new UnicodeInputStream(is, skipBOM);
+
+ assertEquals("BOM Skipping problem", skipBOM, uis.isSkipBOM());
+
+ if (bom != null)
+ {
+ assertEquals("Wrong Encoding detected", testEncoding, uis.getEncodingFromStream());
+ }
+
+ byte [] result = readAllBytes(uis, testEncoding);
+
+ assertNotNull(testString);
+ assertNotNull(result);
+ assertEquals("Wrong result length", testString.length, result.length);
+
+ for (int i = 0; i < result.length; i++)
+ {
+ assertEquals("Wrong Byte at " + i, testString[i], result[i]);
+ }
+ }
+ finally
+ {
+
+ if (uis != null)
+ {
+ uis.close();
+ }
+
+ if (is != null)
+ {
+ is.close();
+ }
+ }
+ }
+
+ protected InputStream createInputStream(final UnicodeInputStream.UnicodeBOM bom, final String str, final String enc)
+ throws Exception
+ {
+
+ if (bom == null)
+ {
+ if (str != null)
+ {
+ return new ByteArrayInputStream(str.getBytes(enc));
+ }
+ else
+ {
+ return new ByteArrayInputStream(new byte[0]);
+ }
+ }
+ else
+ {
+ if (str != null)
+ {
+ return new ByteArrayInputStream(ArrayUtils.addAll(bom.getBytes(), str.getBytes(enc)));
+ }
+ else
+ {
+ return new ByteArrayInputStream(ArrayUtils.addAll(bom.getBytes(), new byte[0]));
+ }
+ }
+ }
+
+ protected byte [] buildTestString(final UnicodeInputStream.UnicodeBOM bom, final String str, final String enc, final boolean skipBOM)
+ throws Exception
+ {
+
+ byte [] strBytes = (str != null) ? str.getBytes(enc) : new byte[0];
+
+ if ((bom == null) || skipBOM)
+ {
+ return strBytes;
+ }
+ else
+ {
+ return ArrayUtils.addAll(bom.getBytes(), strBytes);
+ }
+ }
+
+ protected byte [] readAllBytes(final InputStream inputStream, final String enc)
+ throws Exception
+ {
+ InputStreamReader isr = null;
+
+ byte [] res = new byte[0];
+
+ try
+ {
+ byte[] buf = new byte[1024];
+ int read = 0;
+
+ while ((read = inputStream.read(buf)) >= 0)
+ {
+ res = ArrayUtils.addAll(res, ArrayUtils.subarray(buf, 0, read));
+ }
+ }
+ finally
+ {
+
+ if (isr != null)
+ {
+ isr.close();
+ }
+ }
+
+ return res;
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/runtime/RuntimeInstanceTest.java b/velocity-engine-core/src/test/java/org/apache/velocity/runtime/RuntimeInstanceTest.java
new file mode 100644
index 00000000..9b88e209
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/runtime/RuntimeInstanceTest.java
@@ -0,0 +1,49 @@
+package org.apache.velocity.runtime;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.runtime.resource.ResourceManager;
+import org.junit.Test;
+
+public class RuntimeInstanceTest {
+
+ @Test
+ public void givenOverridenInputEncoding_whenInitializing_defaultEncodingIsOverridden() {
+ RuntimeInstance instance = new RuntimeInstance();
+ MockResourceManager manager = new MockResourceManager();
+ String value = "testDummyEncoding";
+ instance.addProperty(RuntimeConstants.INPUT_ENCODING, value);
+ instance.addProperty(RuntimeConstants.RESOURCE_MANAGER_INSTANCE, manager);
+ instance.init();
+
+ instance.getTemplate("some template");
+
+ assertEquals(value, manager.encoding);
+
+ }
+
+ class MockResourceManager implements ResourceManager {
+
+ String encoding = null;
+
+ @Override
+ public String getLoaderNameForResource(String resourceName) {
+ return null;
+ }
+
+ @Override
+ public Resource getResource(String resourceName, int resourceType, String encoding)
+ throws ResourceNotFoundException, ParseErrorException {
+ this.encoding = encoding;
+ return null;
+ }
+
+ @Override
+ public void initialize(RuntimeServices rs) {
+
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/AbsoluteFileResourceLoaderTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/AbsoluteFileResourceLoaderTestCase.java
new file mode 100644
index 00000000..abfe75cc
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/AbsoluteFileResourceLoaderTestCase.java
@@ -0,0 +1,150 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Test use of an absolute path with the FileResourceLoader
+ *
+ * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class AbsoluteFileResourceLoaderTestCase extends BaseTestCase
+{
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path to template file. This will get combined with the
+ * application directory to form an absolute path
+ */
+ private final static String TEMPLATE_PATH = TEST_COMPARE_DIR + "/absolute/absolute";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/absolute/results";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/absolute/compare";
+
+ /**
+ * Default constructor.
+ */
+ AbsoluteFileResourceLoaderTestCase()
+ {
+ super("AbsoluteFileResourceLoaderTest");
+ }
+
+ public static Test suite ()
+ {
+ return new AbsoluteFileResourceLoaderTestCase();
+ }
+
+ /**
+ * Runs the test.
+ */
+ @Override
+ public void runTest ()
+ {
+
+ try
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ Velocity.reset();
+
+ // signify we want to use an absolute path
+ Velocity.addProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, "");
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ }
+ catch (Exception e)
+ {
+ String msg = "Cannot setup AbsoluteFileResourceLoaderTest!";
+ info(msg, e);
+ fail(msg);
+ }
+ try
+ {
+
+ String curdir = System.getProperty("user.dir");
+ String f = getFileName(curdir, TEMPLATE_PATH, TMPL_FILE_EXT);
+
+ System.out.println("Retrieving template at absolute path: " + f);
+
+ Template template1 = RuntimeSingleton.getTemplate(f);
+
+ FileOutputStream fos1 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "absolute", RESULT_FILE_EXT));
+
+ Writer writer1 = new BufferedWriter(new OutputStreamWriter(fos1));
+
+ /*
+ * put the Vector into the context, and merge both
+ */
+ VelocityContext context = new VelocityContext();
+
+ template1.merge(context, writer1);
+ writer1.flush();
+ writer1.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "absolute",
+ RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+ }
+ catch (Exception e)
+ {
+ fail(e.getMessage());
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/AlternateValuesTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/AlternateValuesTestCase.java
new file mode 100644
index 00000000..a141c065
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/AlternateValuesTestCase.java
@@ -0,0 +1,61 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+/**
+ * Base test case that provides utility methods for
+ * the rest of the tests.
+ *
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author Nathan Bubna
+ * @version $Id$
+ */
+public class AlternateValuesTestCase extends BaseTestCase
+{
+ public AlternateValuesTestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testDefault()
+ {
+ assertEvalEquals("<foo>", "<${foo|'foo'}>");
+ assertEvalEquals("bar", "#set($bar='bar')${foo|$bar}");
+ assertEvalEquals("bar", "#set($bar='bar')${bar|'foo'}");
+ assertEvalEquals("bar", "#set($bar='bar')${foo|${bar}}");
+ assertEvalEquals("baz", "${foo|${baz|'baz'}}");
+ assertEvalEquals("hop", "${foo.bar.baz()[5]|'hop'}");
+ assertEvalEquals("{foo}", "{${foo|'foo'}}");
+ assertEvalEquals("<1>", "<${foo|1}>");
+ assertEvalEquals("<1.1>", "<${foo|1.1}>");
+ }
+
+ public void testComplexEval()
+ {
+ assertEvalEquals("<no date tool>", "<${date.format('medium', $date.date)|'no date tool'}>");
+ assertEvalEquals("true", "#set($val=false)${val.toString().replace(\"false\", \"true\")|'so what'}");
+ assertEvalEquals("so what", "#set($foo='foo')${foo.contains('bar')|'so what'}");
+ assertEvalEquals("so what", "#set($val=false)${val.toString().contains('bar')|'so what'}");
+ assertEvalEquals("true", "#set($val=false)${val.toString().contains('false')|'so what'}");
+ assertEvalEquals("", "$!{null|$null}");
+ assertEvalEquals("null", "$!{null|'null'}");
+ assertEvalEquals("so what", "#set($spaces=' ')${spaces.trim()|'so what'}");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ArithmeticTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ArithmeticTestCase.java
new file mode 100644
index 00000000..3bde4a88
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ArithmeticTestCase.java
@@ -0,0 +1,244 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.velocity.runtime.parser.node.MathUtils;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Test arithmetic operations. Introduced after extending from Integer-only
+ * to Number-handling.
+ *
+ * @author <a href="mailto:pero@antaramusic.de">Peter Romianowski</a>
+ */
+public class ArithmeticTestCase extends TestCase
+{
+
+ public ArithmeticTestCase(String testName)
+ {
+ super(testName);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(ArithmeticTestCase.class);
+ }
+
+ public void testAdd()
+ {
+ addHelper (10, (short) 20, 30, Integer.class);
+ addHelper ((byte) 10, (short) 20, 30, Short.class);
+ addHelper (10f, (short) 20, 30, Float.class);
+ addHelper ((byte) 10, 20d, 30, Double.class);
+ addHelper (BigInteger.valueOf(10), 20, 30, BigInteger.class);
+ addHelper (20, BigDecimal.valueOf(10), 30, BigDecimal.class);
+
+ // Test overflow
+ addHelper (Integer.MAX_VALUE, (short) 20, (double)Integer.MAX_VALUE+20, Long.class);
+ addHelper (20, Long.MAX_VALUE, (double)Long.MAX_VALUE+20, BigInteger.class);
+ addHelper (-20, Long.MIN_VALUE, (double)Long.MIN_VALUE-20, BigInteger.class);
+ }
+
+ private void addHelper (Number n1, Number n2, double expectedResult, Class expectedResultType)
+ {
+ Number result = MathUtils.add( n1, n2);
+ assertEquals("The arithmetic operation produced an unexpected result.", expectedResult, result.doubleValue(), 0.01);
+ assertEquals("ResultType does not match.", expectedResultType, result.getClass());
+ }
+
+ public void testSubtract()
+ {
+ subtractHelper (100, (short) 20, 80, Integer.class);
+ subtractHelper ((byte) 100, (short) 20, 80, Short.class);
+ subtractHelper (100f, (short) 20, 80, Float.class);
+ subtractHelper ((byte) 100, 20d, 80, Double.class);
+ subtractHelper (BigInteger.valueOf(100), 20, 80, BigInteger.class);
+ subtractHelper (100, BigDecimal.valueOf(20), 80, BigDecimal.class);
+
+ // Test overflow
+ subtractHelper (Integer.MIN_VALUE, (short) 20, (double)Integer.MIN_VALUE-20, Long.class);
+ subtractHelper(-20, Long.MAX_VALUE, -20d - (double) Long.MAX_VALUE, BigInteger.class);
+ subtractHelper(Integer.MAX_VALUE, Long.MIN_VALUE, (double) Long.MAX_VALUE + (double) Integer.MAX_VALUE, BigInteger.class);
+ }
+
+ private void subtractHelper (Number n1, Number n2, double expectedResult, Class expectedResultType)
+ {
+ Number result = MathUtils.subtract( n1, n2);
+ assertEquals("The arithmetic operation produced an unexpected result.", expectedResult, result.doubleValue(), 0.01);
+ assertEquals("ResultType does not match.", expectedResultType, result.getClass());
+ }
+
+ public void testMultiply()
+ {
+ multiplyHelper (10, (short) 20, 200, Integer.class);
+ multiplyHelper ((byte) 100, (short) 20, 2000, Short.class);
+ multiplyHelper ((byte) 100, (short) 2000, 200000, Integer.class);
+ multiplyHelper (100f, (short) 20, 2000, Float.class);
+ multiplyHelper ((byte) 100, 20d, 2000, Double.class);
+ multiplyHelper (BigInteger.valueOf(100), 20, 2000, BigInteger.class);
+ multiplyHelper (100, BigDecimal.valueOf(20), 2000, BigDecimal.class);
+
+ // Test overflow
+ multiplyHelper (Integer.MAX_VALUE, (short) 10, (double)Integer.MAX_VALUE*10d, Long.class);
+ multiplyHelper(Integer.MAX_VALUE, (short) -10, (double) Integer.MAX_VALUE * -10d, Long.class);
+ multiplyHelper(20, Long.MAX_VALUE, 20d * (double) Long.MAX_VALUE, BigInteger.class);
+ }
+
+ private void multiplyHelper (Number n1, Number n2, double expectedResult, Class expectedResultType)
+ {
+ Number result = MathUtils.multiply( n1, n2);
+ assertEquals("The arithmetic operation produced an unexpected result.", expectedResult, result.doubleValue(), 0.01);
+ assertEquals("ResultType does not match.", expectedResultType, result.getClass());
+ }
+
+ public void testDivide()
+ {
+ divideHelper (10, (short) 2, 5, Integer.class);
+ divideHelper ((byte) 10, (short) 2, 5, Short.class);
+ divideHelper (BigInteger.valueOf(10), (short) 2, 5, BigInteger.class);
+ divideHelper (10, (short) 4, 2, Integer.class);
+ divideHelper (10, 2.5f, 4, Float.class);
+ divideHelper(10, 2.5, 4, Double.class);
+ divideHelper(10, new BigDecimal(2.5), 4, BigDecimal.class);
+ }
+
+ private void divideHelper (Number n1, Number n2, double expectedResult, Class expectedResultType)
+ {
+ Number result = MathUtils.divide( n1, n2);
+ assertEquals("The arithmetic operation produced an unexpected result.", expectedResult, result.doubleValue(), 0.01);
+ assertEquals("ResultType does not match.", expectedResultType, result.getClass());
+ }
+
+ public void testModulo()
+ {
+ moduloHelper (10, (short) 2, 0, Integer.class);
+ moduloHelper ((byte) 10, (short) 3, 1, Short.class);
+ moduloHelper(BigInteger.valueOf(10), (short) 4, 2, BigInteger.class);
+ moduloHelper(10, 5.5f, 4.5, Float.class);
+
+ try
+ {
+ moduloHelper (10, new BigDecimal( 2.5), 4, BigDecimal.class);
+ fail ("Modulo with BigDecimal is not allowed! Should have thrown an ArithmeticException.");
+ }
+ catch( ArithmeticException e)
+ {
+ // do nothing
+ }
+ }
+
+ private void moduloHelper (Number n1, Number n2, double expectedResult, Class expectedResultType)
+ {
+ Number result = MathUtils.modulo( n1, n2);
+ assertEquals("The arithmetic operation produced an unexpected result.", expectedResult, result.doubleValue(), 0.01);
+ assertEquals("ResultType does not match.", expectedResultType, result.getClass());
+ }
+
+ public void testCompare()
+ {
+ compareHelper (10, (short) 10, 0);
+ compareHelper (10, (short) 11, -1);
+ compareHelper (BigInteger.valueOf(10), (short) 11, -1);
+ compareHelper ((byte) 10, (short) 3, 1);
+ compareHelper(10f, (short) 11, -1);
+ compareHelper(10d, (short) 11, -1);
+ }
+
+ private void compareHelper (Number n1, Number n2, int expectedResult)
+ {
+ int result = MathUtils.compare( n1, n2 );
+ assertEquals("The arithmetic operation produced an unexpected result.", expectedResult, result);
+ }
+
+ public void testNegate()
+ {
+ negateHelper((byte) 1, -1, Byte.class);
+ negateHelper((short) 1, -1, Short.class);
+ negateHelper(1, -1, Integer.class);
+ negateHelper(1L, -1, Long.class);
+ negateHelper(BigInteger.valueOf(1), -1, BigInteger.class);
+ negateHelper(BigDecimal.valueOf(1), -1, BigDecimal.class);
+ negateHelper(Long.MIN_VALUE, BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.valueOf(1)).doubleValue(), BigInteger.class);
+ }
+
+ private void negateHelper(Number n, double expectedResult, Class expectedResultType)
+ {
+ Number result = MathUtils.negate(n);
+ assertEquals ("The arithmetic operation produced an unexpected result.", expectedResult, result.doubleValue(), 0.01);
+ assertEquals ("ResultType does not match.", expectedResultType, result.getClass());
+ }
+
+/*
+ *
+ * COMMENT OUT FOR PERFORMANCE-MEASSUREMENTS
+ *
+ * public void testProfile()
+ * {
+ *
+ * long start = System.currentTimeMillis();
+ *
+ * Number v1 = new Long (1000);
+ * Number v2 = new Double (10.23);
+ * Number result = null;
+ * for (int a = 0; a < 10000; a++)
+ * {
+ *
+ * result = MathUtils.typeConvert (
+ * new BigDecimal (v1.doubleValue()).add (
+ * new BigDecimal (v2.doubleValue())), v1, v2, false);
+ *
+ * }
+ *
+ * System.out.println ("took: "+(System.currentTimeMillis()-start));
+ *
+ * start = System.currentTimeMillis();
+ * for (int a = 0; a < 10000; a++)
+ * {
+ *
+ * result = MathUtils.divide( v1, v2);
+ * }
+ *
+ * Number result2 = result;
+ * System.out.println ("took: "+(System.currentTimeMillis()-start));
+ * }
+ *
+ */
+
+ /**
+ * Test additional functions
+ */
+ public void testIsZero()
+ {
+ assertTrue (MathUtils.isZero (0));
+ assertTrue (!MathUtils.isZero (1));
+ assertTrue (!MathUtils.isZero (-1));
+
+ assertTrue (MathUtils.isZero (0f));
+ assertTrue (!MathUtils.isZero (0.00001f));
+ assertTrue (!MathUtils.isZero (-0.00001f));
+
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ArrayMethodsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ArrayMethodsTestCase.java
new file mode 100644
index 00000000..ceb82e80
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ArrayMethodsTestCase.java
@@ -0,0 +1,211 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Used to check that method calls on Array references work properly
+ * and that they produce the same results as the same methods would on
+ * a fixed-size {@link List}.
+ */
+public class ArrayMethodsTestCase extends BaseTestCase
+{
+ public ArrayMethodsTestCase(final String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testArrayMethods() throws Exception
+ {
+ // test an array of string objects
+ Object array = new String[] { "foo", "bar", "baz" };
+ checkResults(array, "woogie", true);
+
+ // test an array of primitive ints
+ array = new int[] { 1, 3, 7 };
+ checkResults(array, 11, false);
+
+ // test an array of mixed objects, including null
+ array = new Object[] { 2.2, null };
+ checkResults(array, "whatever", true);
+ // then set all the values to null
+ checkResults(array, null, true);
+
+ // then try an empty array
+ array = new Object[] {};
+ checkResults(array, null, true);
+
+ // while we have an empty array and list in the context,
+ // make sure $array.get(0) and $list.get(0) throw
+ // the same type of exception (MethodInvocationException)
+ Throwable lt = null;
+ Throwable at = null;
+ try
+ {
+ evaluate("$list.get(0)");
+ }
+ catch (Throwable t)
+ {
+ lt = t;
+ }
+ try
+ {
+ evaluate("$array.get(0)");
+ }
+ catch (Throwable t)
+ {
+ at = t;
+ }
+ assertEquals(lt.getClass(), at.getClass());
+ }
+
+ private void checkResults(Object array, Object setme,
+ boolean compareToList) throws Exception
+ {
+ context.put("array", array);
+ if (compareToList)
+ {
+ // create a list to match...
+ context.put("list", new ArrayList(Arrays.asList((Object[])array)));
+ }
+
+ // if the object to be set is null, then remove instead of put
+ if (setme != null)
+ {
+ context.put("setme", setme);
+ }
+ else
+ {
+ context.remove("setme");
+ }
+
+ info("Changing to an array of: " + array.getClass().getComponentType());
+ info("Changing setme to: " + setme);
+
+ int size = Array.getLength(array);
+ checkResult("size()", String.valueOf(size), compareToList);
+
+ boolean isEmpty = (size == 0);
+ checkResult("isEmpty()", String.valueOf(isEmpty), compareToList);
+
+ checkPropertyResult("empty", String.valueOf(isEmpty), compareToList);
+
+ // Since 2.1, arrays are rendered the same way as lists
+ String renderArray = evaluate("$array");
+ String renderList = evaluate("$list");
+ System.err.println("<<< " + renderArray);
+ System.err.println(">>> " + renderList);
+ if (compareToList) assertTrue(renderArray.equals(renderList));
+ else assertFalse(renderArray.equals(renderList));
+
+ for (int i=0; i < size; i++)
+ {
+ // put the index in the context, so we can try
+ // both an explicit index and a reference index
+ context.put("index", i);
+
+ Object value = Array.get(array, i);
+ String get = "get($index)";
+ String set = "set("+i+", $setme)";
+ if (value == null)
+ {
+ checkEmptyResult(get, compareToList);
+ // set should return null
+ checkEmptyResult(set, compareToList);
+ }
+ else
+ {
+ checkResult(get, value.toString(), compareToList);
+ // set should return the old get value
+ checkResult(set, value.toString(), compareToList);
+ }
+
+ // check that set() actually changed the value
+ assertEquals(setme, Array.get(array, i));
+
+ // and check that get() now returns setme
+ if (setme == null)
+ {
+ checkEmptyResult(get, compareToList);
+ }
+ else
+ {
+ checkResult(get, setme.toString(), compareToList);
+
+ // now check that contains() properly finds the new value
+ checkResult("contains($setme)", "true", compareToList);
+ }
+ }
+ }
+
+ private void checkEmptyResult(String method, boolean compareToList)
+ throws Exception
+ {
+ checkResult(method, "", compareToList);
+ }
+
+ private void checkResult(String method, String expected,
+ boolean compareToList) throws Exception
+ {
+ String result = evaluate("$!array."+method);
+ assertEquals(expected, result);
+
+ String listResult = null;
+ if (compareToList)
+ {
+ listResult = evaluate("$!list."+method);
+ assertEquals(result, listResult);
+ }
+
+ info(" <$!array." + method + "> resolved to <" + result + ">");
+ if (compareToList)
+ {
+ info(" <$!list."+method+"> resolved to "+listResult+">");
+ }
+ }
+
+ private void checkPropertyResult(String property, String expected,
+ boolean compareToList) throws Exception
+ {
+ String result = evaluate("$!array."+property);
+ assertEquals(expected, result);
+
+ String listResult = null;
+ if (compareToList)
+ {
+ listResult = evaluate("$!list."+property);
+ assertEquals(result, listResult);
+ }
+
+ info(" <$!array."+property+"> resolved to <"+result+">");
+ if (compareToList)
+ {
+ info(" <$!list."+property+"> resolved to "+listResult+">");
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/BaseTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/BaseTestCase.java
new file mode 100644
index 00000000..c6456dff
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/BaseTestCase.java
@@ -0,0 +1,515 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.runtime.resource.util.StringResourceRepository;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+/**
+ * Base test case that provides utility methods for
+ * the rest of the tests.
+ *
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author Nathan Bubna
+ * @version $Id$
+ */
+public abstract class BaseTestCase extends TestCase implements TemplateTestBase
+{
+ protected VelocityEngine engine;
+ protected VelocityContext context;
+ protected boolean DEBUG = Boolean.getBoolean("test.debug");
+ protected TestLogger log;
+ protected String stringRepoName = "string.repo";
+
+ public BaseTestCase(String name)
+ {
+ super(name);
+
+ // if we're just running one case, then have DEBUG
+ // automatically set to true
+ String test = System.getProperty("test");
+ if (test != null)
+ {
+ DEBUG = test.equals(getClass().getSimpleName());
+ }
+ }
+
+ protected VelocityEngine createEngine()
+ {
+ VelocityEngine ret = new VelocityEngine();
+ ret.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, log);
+
+ // use string resource loader by default, instead of file
+ ret.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file,string");
+ ret.addProperty("string.resource.loader.class", StringResourceLoader.class.getName());
+ ret.addProperty("string.resource.loader.repository.name", stringRepoName);
+ ret.addProperty("string.resource.loader.repository.static", "false");
+
+ setUpEngine(ret);
+ return ret;
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ //by default, make the engine's log output go to the test-report
+ log = new TestLogger(false, false);
+ engine = createEngine();
+ context = new VelocityContext();
+ setUpContext(context);
+ }
+
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ // extension hook
+ }
+
+ protected void setUpContext(VelocityContext context)
+ {
+ // extension hook
+ }
+
+ protected StringResourceRepository getStringRepository()
+ {
+ StringResourceRepository repo =
+ (StringResourceRepository)engine.getApplicationAttribute(stringRepoName);
+ if (repo == null)
+ {
+ engine.init();
+ repo =
+ (StringResourceRepository)engine.getApplicationAttribute(stringRepoName);
+ }
+ return repo;
+ }
+
+ protected void addTemplate(String name, String template)
+ {
+ info("Template '"+name+"': "+template);
+ getStringRepository().putStringResource(name, template);
+ }
+
+ protected void removeTemplate(String name)
+ {
+ info("Removed: '"+name+"'");
+ getStringRepository().removeStringResource(name);
+ }
+
+ @Override
+ public void tearDown()
+ {
+ engine = null;
+ context = null;
+ }
+
+ protected void info(String msg)
+ {
+ info(msg, null);
+ }
+
+ protected void info(String msg, Throwable t)
+ {
+ if (DEBUG)
+ {
+ try
+ {
+ if (engine == null)
+ {
+ Velocity.getLog().info(msg, t);
+ }
+ else
+ {
+ engine.getLog().info(msg, t);
+ }
+ }
+ catch (Throwable t2)
+ {
+ System.out.println("Failed to log: "+msg+(t!=null?" - "+t: ""));
+ System.out.println("Cause: "+t2);
+ t2.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Compare an expected string with the given loaded template
+ */
+ protected void assertTmplEquals(String expected, String template)
+ {
+ info("Expected: " + expected + " from '" + template + "'");
+
+ StringWriter writer = new StringWriter();
+ try
+ {
+ engine.mergeTemplate(template, "utf-8", context, writer);
+ }
+ catch (RuntimeException re)
+ {
+ info("RuntimeException!", re);
+ throw re;
+ }
+ catch (Exception e)
+ {
+ info("Exception!", e);
+ throw new RuntimeException(e);
+ }
+
+ info("Result: " + writer.toString());
+ assertEquals(expected, writer.toString());
+ }
+
+ /**
+ * Ensure that a context value is as expected.
+ */
+ protected void assertContextValue(String key, Object expected)
+ {
+ info("Expected value of '"+key+"': "+expected);
+ Object value = context.get(key);
+ info("Result: "+value);
+ assertEquals(expected, value);
+ }
+
+ /**
+ * Ensure that a template renders as expected.
+ */
+ protected void assertEvalEquals(String expected, String template)
+ {
+ info("Expectation: "+expected);
+ assertEquals(expected, evaluate(template));
+ }
+
+ /**
+ * Ensure that the given string renders as itself when evaluated.
+ */
+ protected void assertSchmoo(String templateIsExpected)
+ {
+ assertEvalEquals(templateIsExpected, templateIsExpected);
+ }
+
+ /**
+ * Ensure that an exception occurs when the string is evaluated.
+ */
+ protected Exception assertEvalException(String evil)
+ {
+ return assertEvalException(evil, null);
+ }
+
+ /**
+ * Ensure that a specified type of exception occurs when evaluating the string.
+ */
+ protected Exception assertEvalException(String evil, Class<?> exceptionType)
+ {
+ try
+ {
+ if (!DEBUG)
+ {
+ log.off();
+ }
+ if (exceptionType != null)
+ {
+ info("Expectation: "+exceptionType.getName());
+ }
+ else
+ {
+ info("Expectation: "+Exception.class.getName());
+ }
+ evaluate(evil);
+ String msg = "Template '"+evil+"' should have thrown an exception.";
+ info("Fail: "+msg);
+ fail(msg);
+ }
+ catch (Exception e)
+ {
+ if (exceptionType != null && !exceptionType.isAssignableFrom(e.getClass()))
+ {
+ String msg = "Was expecting template '"+evil+"' to throw "+exceptionType+" not "+e;
+ info("Fail: "+msg);
+ fail(msg);
+ }
+ return e;
+ }
+ finally
+ {
+ if (!DEBUG)
+ {
+ log.on();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Ensure that the error message of the expected exception has the proper location info.
+ */
+ protected Exception assertEvalExceptionAt(String evil, String template,
+ int line, int col)
+ {
+ String loc = template+"[line "+line+", column "+col+"]";
+ info("Expectation: Exception at "+loc);
+ Exception e = assertEvalException(evil);
+
+ info("Result: "+e.getClass().getName()+" - "+e.getMessage());
+ if (e.getMessage().indexOf(loc) < 1)
+ {
+ fail("Was expecting exception at "+loc+" instead of "+e.getMessage());
+ }
+ return e;
+ }
+
+ /**
+ * Only ensure that the error message of the expected exception
+ * has the proper line and column info.
+ */
+ protected Exception assertEvalExceptionAt(String evil, int line, int col)
+ {
+ return assertEvalExceptionAt(evil, "", line, col);
+ }
+
+ /**
+ * Evaluate the specified String as a template and return the result as a String.
+ */
+ protected String evaluate(String template)
+ {
+ StringWriter writer = new StringWriter();
+ try
+ {
+ info("Template: "+template);
+
+ // use template as its own name, since our templates are short
+ // unless it's not that short, then shorten it...
+ String name = (template.length() <= 15) ? template : template.substring(0,15);
+ engine.evaluate(context, writer, name, template);
+
+ String result = writer.toString();
+ info("Result: "+result);
+ return result;
+ }
+ catch (RuntimeException re)
+ {
+ info("RuntimeException!", re);
+ throw re;
+ }
+ catch (Exception e)
+ {
+ info("Exception!", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Concatenates the file name parts together appropriately.
+ *
+ * @return The full path to the file.
+ */
+ protected String getFileName(final String dir, final String base, final String ext)
+ {
+ return getFileName(dir, base, ext, false);
+ }
+
+ protected String getFileName(final String dir, final String base, final String ext, final boolean mustExist)
+ {
+ StringBuilder buf = new StringBuilder();
+ try
+ {
+ File baseFile = new File(base);
+ if (dir != null)
+ {
+ if (!baseFile.isAbsolute())
+ {
+ baseFile = new File(dir, base);
+ }
+
+ buf.append(baseFile.getCanonicalPath());
+ }
+ else
+ {
+ buf.append(baseFile.getPath());
+ }
+
+ if (org.apache.commons.lang3.StringUtils.isNotEmpty(ext))
+ {
+ buf.append('.').append(ext);
+ }
+
+ if (mustExist)
+ {
+ File testFile = new File(buf.toString());
+
+ if (!testFile.exists())
+ {
+ String msg = "getFileName() result " + testFile.getPath() + " does not exist!";
+ info(msg);
+ fail(msg);
+ }
+
+ if (!testFile.isFile())
+ {
+ String msg = "getFileName() result " + testFile.getPath() + " is not a file!";
+ info(msg);
+ fail(msg);
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ fail("IO Exception while running getFileName(" + dir + ", " + base + ", "+ ext + ", " + mustExist + "): " + e.getMessage());
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Assures that the results directory exists. If the results directory
+ * cannot be created, fails the test.
+ */
+ protected void assureResultsDirectoryExists(String resultsDirectory)
+ {
+ File dir = new File(resultsDirectory);
+ if (!dir.exists())
+ {
+ info("Template results directory ("+resultsDirectory+") does not exist");
+ if (dir.mkdirs())
+ {
+ info("Created template results directory");
+ if (DEBUG)
+ {
+ info("Created template results directory: "+resultsDirectory);
+ }
+ }
+ else
+ {
+ String errMsg = "Unable to create '"+resultsDirectory+"'";
+ info(errMsg);
+ fail(errMsg);
+ }
+ }
+ }
+
+
+ /**
+ * Normalizes lines to account for platform differences. Macs use
+ * a single \r, DOS derived operating systems use \r\n, and Unix
+ * uses \n. Replace each with a single \n.
+ *
+ * @return source with all line terminations changed to Unix style
+ */
+ protected String normalizeNewlines (String source)
+ {
+ return source.replaceAll("\r\n?", "\n");
+ }
+
+ /**
+ * Returns whether the processed template matches the
+ * content of the provided comparison file.
+ *
+ * @return Whether the output matches the contents
+ * of the comparison file.
+ *
+ * @exception Exception Test failure condition.
+ */
+ protected boolean isMatch (String resultsDir,
+ String compareDir,
+ String baseFileName,
+ String resultExt,
+ String compareExt) throws Exception
+ {
+ if (DEBUG)
+ {
+ info("Result: "+resultsDir+'/'+baseFileName+'.'+resultExt);
+ }
+ String result = getFileContents(resultsDir, baseFileName, resultExt);
+ return isMatch(result,compareDir,baseFileName,compareExt);
+ }
+
+
+ protected String getFileContents(String dir, String baseFileName, String ext)
+ {
+ String fileName = getFileName(dir, baseFileName, ext, true);
+ return getFileContents(fileName);
+ }
+
+ protected String getFileContents(String file)
+ {
+ String contents = null;
+
+ try
+ {
+ contents = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ return contents;
+ }
+
+ /**
+ * Returns whether the processed template matches the
+ * content of the provided comparison file.
+ *
+ * @return Whether the output matches the contents
+ * of the comparison file.
+ *
+ * @exception Exception Test failure condition.
+ */
+ protected boolean isMatch (String result,
+ String compareDir,
+ String baseFileName,
+ String compareExt) throws Exception
+ {
+ String compare = getFileContents(compareDir, baseFileName, compareExt);
+
+ // normalize each wrt newline
+ result = normalizeNewlines(result);
+ compare = normalizeNewlines(compare);
+ if (DEBUG)
+ {
+ info("Expection: "+compareDir+'/'+baseFileName+'.'+compareExt);
+ }
+ return result.equals(compare);
+ }
+
+ /**
+ * Turns a base file name into a test case name.
+ *
+ * @param s The base file name.
+ * @return The test case name.
+ */
+ protected static String getTestCaseName(String s)
+ {
+ StringBuilder name = new StringBuilder();
+ name.append(Character.toTitleCase(s.charAt(0)));
+ name.append(s.substring(1, s.length()).toLowerCase(Locale.ROOT));
+ return name.toString();
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/BlockMacroTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/BlockMacroTestCase.java
new file mode 100644
index 00000000..8eb3f7b5
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/BlockMacroTestCase.java
@@ -0,0 +1,141 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeConstants;
+
+/**
+ * This class tests the BlockMacro functionality.
+ */
+public class BlockMacroTestCase extends BaseTestCase
+{
+ public BlockMacroTestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testMultipleBodyContentIncludes() throws Exception
+ {
+ String template = "#macro(foo $txt) Yeah, $txt! $bodyContent $bodyContent#end #@foo(\"woohoo\")jee#end";
+ String result = " Yeah, woohoo! jee jee";
+
+ assertEvalEquals(result, template);
+ }
+
+ public void testNestedVelocityLogic() throws Exception
+ {
+ String template = "#macro(foo $txt) Yeah, $txt! $bodyContent#end #@foo(\"woohoo\")#foreach($i in [1..3])$i:#{end}#end";
+ String result = " Yeah, woohoo! 1:2:3:";
+
+ assertEvalEquals(result, template);
+ }
+
+ public void testEmptyBody() throws Exception
+ {
+ String template = "#macro(foo $txt) Yeah, $txt! $bodyContent#end #@foo(\"woohoo\")#end";
+ String result = " Yeah, woohoo! ";
+
+ assertEvalEquals(result, template);
+ }
+
+ public void testNoArgumentsEmptyBodyCall() throws Exception
+ {
+ String template = "#macro(foo) Yeah! $bodyContent#end #@foo()#end";
+ String result = " Yeah! ";
+
+ assertEvalEquals(result, template);
+ }
+
+ public void testCustomBodyReference() throws Exception
+ {
+ engine.setProperty(RuntimeConstants.VM_BODY_REFERENCE, "myBody");
+ String template = "#macro(foo) Yeah! $myBody#end #@foo()#end";
+ String result = " Yeah! ";
+
+ assertEvalEquals(result, template);
+ }
+
+ public void testVelocity671() throws Exception
+ {
+ engine.setProperty(RuntimeConstants.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ String template = "#macro(echo)$bodyContent#end #@echo()Yeah!#end";
+ String result = " Yeah!";
+ assertEvalEquals(result, template);
+ }
+
+ public void testStrict()
+ {
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, true);
+ assertEvalException("#@foo#end");
+ assertEvalException("#@foo()#end");
+ }
+
+ public void testVelocity690()
+ {
+ assertEvalEquals(" output ", "#macro(foo) output #end#@foo #end");
+ assertEvalEquals("#[ output )", "#macro(foo2)#[$bodyContent)#end#@foo2 output #end");
+ assertEvalEquals("#[output)", "#macro(foo2)#[$bodyContent)#end#{@foo2}output#end");
+ assertEvalException("#macro(foo) output #end#@foo");
+ }
+
+ public void testVelocity675() throws Exception
+ {
+ assertEvalEquals("#@foo#end", "#@foo#end");
+ }
+
+ public void testVelocity685() throws Exception
+ {
+ engine.setProperty(RuntimeConstants.VM_ARGUMENTS_STRICT, Boolean.TRUE);
+ assertEvalEquals(" ", "#macro(foo)#end #@foo() junk #end");
+ }
+
+ public void testVelocity686() throws Exception
+ {
+ String template = "#macro(foo)#set( $x = $bodyContent )#end"+
+ "#@foo()b#end a $x ";
+ assertEvalEquals(" a b ", template);
+ }
+
+ public void testNestedBlockMacro()
+ {
+ String template = "#macro(foo)foo:$bodyContent#end"+
+ "#macro(bar)bar:$bodyContent#end"+
+ "#@foo()foo,#@bar()bar#end#end";
+ assertEvalEquals("foo:foo,bar:bar", template);
+ }
+
+ public void testRecursiveBlockMacro()
+ {
+ engine.setProperty(RuntimeConstants.VM_MAX_DEPTH, 3);
+ String template = "#macro(foo)start:$bodyContent#end"+
+ "#@foo()call:$bodyContent#end";
+ assertEvalEquals("start:call:call:call:$bodyContent", template);
+ }
+
+ public void testBlueJoesProblem()
+ {
+ engine.setProperty("macro."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, Boolean.TRUE);
+ addTemplate("a", "#macro(wrap $layout)$!macro.put($layout,$bodyContent)#parse($layout)#end"+
+ "#@wrap('b')a#end");
+ addTemplate("b", "#@wrap('c')b$!macro.get('b')b#end");
+ addTemplate("c", "c$!macro.get('c')c");
+ assertTmplEquals("cbabc", "a");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/BreakDirectiveTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/BreakDirectiveTestCase.java
new file mode 100644
index 00000000..0f0cb616
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/BreakDirectiveTestCase.java
@@ -0,0 +1,117 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * This class tests the break directive.
+ */
+public class BreakDirectiveTestCase extends BaseTestCase
+{
+ public BreakDirectiveTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ engine.setProperty("a.provide.scope.control", "true");
+ engine.setProperty("define.provide.scope.control", "true");
+ engine.setProperty("evaluate.provide.scope.control", "true");
+ engine.setProperty("macro.provide.scope.control", "true");
+ engine.setProperty("template.provide.scope.control", "true");
+ }
+
+ public void testBadArgs()
+ {
+ context.put("foo","foo");
+ assertEvalException("#break($null)");
+ assertEvalException("#break($foo)");
+ assertEvalException("#break(true)");
+ assertEvalException("#break(1.2)");
+ assertEvalException("#break([0..1])");
+ assertEvalException("#break( $too $many )");
+ }
+
+ public void testStopForeach()
+ {
+ String template = "#foreach($i in [1..5])$i#if($i>2)#break($foreach)#end#end test";
+ assertEvalEquals("123 test", template);
+
+ // only inner should be stopped, not outer
+ String t2 = "#foreach($j in [1..2])"+template+"#end";
+ assertEvalEquals("123 test123 test", t2);
+
+ // stop outer using #break($foreach.parent)
+ String t3 = "#foreach($i in [1..2])#foreach($j in [2..3])$i$j#if($i+$j==5)#break($foreach.parent)#end#end test#end";
+ assertEvalEquals("1213 test2223", t3);
+
+ // without specifying scope...
+ assertEvalEquals("1, 2, 3, 4, 5",
+ "#foreach($i in [1..10])$i#if($i > 4)#break#end, #end");
+ assertEvalEquals("1", "#foreach($i in [1..5])$i#break #end");
+ assertEvalEquals("~~~, ~~, ~, ",
+ "#foreach($i in [1..3])#foreach($j in [2..4])#if($i*$j >= 8)#break#end~#end, #end");
+ }
+
+ public void testStopTemplate()
+ {
+ addTemplate("a", "a#break($template)b");
+ assertTmplEquals("a", "a");
+ assertEvalEquals("ac", "#parse('a')c");
+
+ addTemplate("b", "b#{break}a");
+ assertTmplEquals("b", "b");
+ }
+
+ public void testStopEvaluate()
+ {
+ assertEvalEquals("a", "a#break($evaluate)b");
+ assertEvalEquals("a", "#evaluate('a#break($evaluate)b')");
+ assertEvalEquals("a", "a#evaluate('#break($evaluate.topmost)')b");
+ assertEvalEquals("a", "a#{break}b");
+ }
+
+ public void testStopDefineBlock()
+ {
+ assertEvalEquals("a", "#define($a)a#break($define)b#end$a");
+ assertEvalEquals("aa", "#define($a)a#break($define.parent)b#end#define($b)a${a}b#end$b");
+ assertEvalEquals("a", "#define($a)a#{break}b#end$a");
+ }
+
+ public void testStopMacro()
+ {
+ assertEvalEquals("a ", "#macro(a)a #break($macro) b#end#a");
+ assertEvalEquals("b c ", "#macro(c)c #break($macro.parent) d#end"+
+ "#macro(b)b #c c#end"+
+ "#b");
+ assertEvalEquals("d", "#macro(d)d#{break}e#end#d");
+ }
+
+ public void testStopMacroBodyBlock()
+ {
+ assertEvalEquals(" a ", "#macro(a) $bodyContent #end"+
+ "#@a()a#break($a)b#end");
+ assertEvalEquals(" b ", "#macro(b) $bodyContent #end"+
+ "#@b()b#{break}c#end");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/BuiltInEventHandlerTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/BuiltInEventHandlerTestCase.java
new file mode 100644
index 00000000..c415e476
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/BuiltInEventHandlerTestCase.java
@@ -0,0 +1,592 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.app.event.EventCartridge;
+import org.apache.velocity.app.event.implement.EscapeHtmlReference;
+import org.apache.velocity.app.event.implement.EscapeJavaScriptReference;
+import org.apache.velocity.app.event.implement.EscapeReference;
+import org.apache.velocity.app.event.implement.EscapeSqlReference;
+import org.apache.velocity.app.event.implement.EscapeXmlReference;
+import org.apache.velocity.app.event.implement.InvalidReferenceInfo;
+import org.apache.velocity.app.event.implement.ReportInvalidReferences;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests the operation of the built in event handlers.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class BuiltInEventHandlerTestCase extends BaseTestCase {
+
+ protected boolean DEBUG = false;
+
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/includeevent";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/includeevent";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/includeevent/compare";
+
+ /**
+ * Default constructor.
+ */
+ public BuiltInEventHandlerTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+ super.setUp();
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(BuiltInEventHandlerTestCase.class);
+ }
+
+ protected void log(String out)
+ {
+ if (DEBUG)
+ {
+ System.out.println (out);
+ }
+ }
+
+ /**
+ * Test reporting of invalid syntax
+ * @throws Exception
+ */
+ public void testReportInvalidReferences1() throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ReportInvalidReferences reporter = new ReportInvalidReferences();
+ ve.init();
+
+ VelocityContext context = new VelocityContext();
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(reporter);
+ ec.attachToContext(context);
+
+ context.put("a1","test");
+ context.put("b1","test");
+ context.put("n1", null);
+ Writer writer = new StringWriter();
+
+ ve.evaluate(context,writer,"test","$a1 $c1 $a1.length() $a1.foobar() $!c1 $n1 $!n1 #if($c1) nop #end");
+
+ List errors = reporter.getInvalidReferences();
+ assertEquals(2,errors.size());
+ assertEquals("$c1",((InvalidReferenceInfo) errors.get(0)).getInvalidReference());
+ assertEquals("$a1.foobar()",((InvalidReferenceInfo) errors.get(1)).getInvalidReference());
+
+ log("Caught invalid references (local configuration).");
+ }
+
+ public void testReportInvalidReferences2() throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty("event_handler.invalid_references.exception","true");
+ ReportInvalidReferences reporter = new ReportInvalidReferences();
+ ve.init();
+
+ VelocityContext context = new VelocityContext();
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(reporter);
+ ec.attachToContext(context);
+
+ context.put("a1","test");
+ context.put("b1","test");
+ Writer writer = new StringWriter();
+
+ ve.evaluate(context,writer,"test","$a1 no problem");
+
+ try {
+ ve.evaluate(context,writer,"test","$a1 $c1 $a1.length() $a1.foobar()");
+ fail ("Expected exception.");
+ } catch (RuntimeException E) {}
+
+
+ log("Caught invalid references (global configuration).");
+
+ }
+
+ /**
+ * Test reporting of invalid syntax
+ * @throws Exception
+ */
+ public void testReportQuietInvalidReferences() throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty("event_handler.invalid_references.quiet","true");
+ ReportInvalidReferences reporter = new ReportInvalidReferences();
+ ve.init();
+
+ VelocityContext context = new VelocityContext();
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(reporter);
+ ec.attachToContext(context);
+
+ context.put("a1","test");
+ context.put("b1","test");
+ context.put("n1", null);
+ Writer writer = new StringWriter();
+
+ ve.evaluate(context,writer,"test","$a1 $c1 $a1.length() $a1.foobar() $!c1 $n1 $!n1 #if($c1) nop #end");
+
+ List errors = reporter.getInvalidReferences();
+ assertEquals(3,errors.size());
+ assertEquals("$c1",((InvalidReferenceInfo) errors.get(0)).getInvalidReference());
+ assertEquals("$a1.foobar()",((InvalidReferenceInfo) errors.get(1)).getInvalidReference());
+ assertEquals("$c1",((InvalidReferenceInfo) errors.get(2)).getInvalidReference());
+
+ log("Caught invalid references (local configuration).");
+ }
+
+ /**
+ * Test reporting of invalid syntax
+ * @throws Exception
+ */
+ public void testReportNullInvalidReferences() throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty("event_handler.invalid_references.null","true");
+ ReportInvalidReferences reporter = new ReportInvalidReferences();
+ ve.init();
+
+ VelocityContext context = new VelocityContext();
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(reporter);
+ ec.attachToContext(context);
+
+ context.put("a1","test");
+ context.put("b1","test");
+ context.put("n1", null);
+ Writer writer = new StringWriter();
+
+ ve.evaluate(context,writer,"test","$a1 $c1 $a1.length() $a1.foobar() $!c1 $n1 $!n1 #if($c1) nop #end");
+
+ List errors = reporter.getInvalidReferences();
+ assertEquals(3,errors.size());
+ assertEquals("$c1",((InvalidReferenceInfo) errors.get(0)).getInvalidReference());
+ assertEquals("$a1.foobar()",((InvalidReferenceInfo) errors.get(1)).getInvalidReference());
+ assertEquals("$n1",((InvalidReferenceInfo) errors.get(2)).getInvalidReference());
+
+ log("Caught invalid references (local configuration).");
+ }
+
+ /**
+ * Test reporting of invalid syntax
+ * @throws Exception
+ */
+ public void testReportNullQuietInvalidReferences() throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty("event_handler.invalid_references.quiet","true");
+ ve.setProperty("event_handler.invalid_references.null","true");
+ ReportInvalidReferences reporter = new ReportInvalidReferences();
+ ve.init();
+
+ VelocityContext context = new VelocityContext();
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(reporter);
+ ec.attachToContext(context);
+
+ context.put("a1","test");
+ context.put("b1","test");
+ context.put("n1", null);
+ Writer writer = new StringWriter();
+
+ ve.evaluate(context,writer,"test","$a1 $c1 $a1.length() $a1.foobar() $!c1 $n1 $!n1 #if($c1) nop #end");
+
+ List errors = reporter.getInvalidReferences();
+ assertEquals(5,errors.size());
+ assertEquals("$c1",((InvalidReferenceInfo) errors.get(0)).getInvalidReference());
+ assertEquals("$a1.foobar()",((InvalidReferenceInfo) errors.get(1)).getInvalidReference());
+ assertEquals("$c1",((InvalidReferenceInfo) errors.get(2)).getInvalidReference());
+ assertEquals("$n1",((InvalidReferenceInfo) errors.get(3)).getInvalidReference());
+ assertEquals("$n1",((InvalidReferenceInfo) errors.get(4)).getInvalidReference());
+
+ log("Caught invalid references (local configuration).");
+ }
+
+ /**
+ * Test reporting of invalid syntax
+ * @throws Exception
+ */
+ public void testReportTestedInvalidReferences() throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty("event_handler.invalid_references.tested","true");
+ ReportInvalidReferences reporter = new ReportInvalidReferences();
+ ve.init();
+
+ VelocityContext context = new VelocityContext();
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(reporter);
+ ec.attachToContext(context);
+
+ context.put("a1","test");
+ context.put("b1","test");
+ context.put("n1", null);
+ Writer writer = new StringWriter();
+
+ ve.evaluate(context,writer,"test","$a1 $c1 $a1.length() $a1.foobar() $!c1 $n1 $!n1 #if($c1) nop #end");
+
+ List errors = reporter.getInvalidReferences();
+ assertEquals(3,errors.size());
+ assertEquals("$c1",((InvalidReferenceInfo) errors.get(0)).getInvalidReference());
+ assertEquals("$a1.foobar()",((InvalidReferenceInfo) errors.get(1)).getInvalidReference());
+ assertEquals("$c1",((InvalidReferenceInfo) errors.get(2)).getInvalidReference());
+
+ log("Caught invalid references (local configuration).");
+ }
+
+ /**
+ * Test escaping
+ * @throws Exception
+ */
+ public void testEscapeHtml() throws Exception
+ {
+ EscapeReference esc = new EscapeHtmlReference();
+ assertEquals("test string&amp;another&lt;b&gt;bold&lt;/b&gt;test",esc.referenceInsert(null,"","test string&another<b>bold</b>test"));
+ assertEquals("&lt;&quot;&gt;",esc.referenceInsert(null,"","<\">"));
+ assertEquals("test string",esc.referenceInsert(null,"","test string"));
+
+ log("Correctly escaped HTML");
+
+ }
+
+ /**
+ * Test escaping
+ * @throws Exception
+ */
+ public void testEscapeXml() throws Exception
+ {
+ EscapeReference esc = new EscapeXmlReference();
+ assertEquals("test string&amp;another&lt;b&gt;bold&lt;/b&gt;test",esc.referenceInsert(null,"","test string&another<b>bold</b>test"));
+ assertEquals("&lt;&quot;&gt;",esc.referenceInsert(null,"","<\">"));
+ assertEquals("&apos;",esc.referenceInsert(null,"","'"));
+ assertEquals("test string",esc.referenceInsert(null,"","test string"));
+
+ log("Correctly escaped XML");
+
+ }
+
+ /**
+ * Test escaping
+ * @throws Exception
+ */
+ public void testEscapeSql() throws Exception
+ {
+ EscapeReference esc = new EscapeSqlReference();
+ assertEquals("Jimmy''s Pizza",esc.referenceInsert(null,"","Jimmy's Pizza"));
+ assertEquals("test string",esc.referenceInsert(null,"","test string"));
+
+ log("Correctly escaped SQL");
+
+ }
+
+ /**
+ * Test escaping
+ * @throws Exception
+ */
+ public void testEscapeJavaScript() throws Exception
+ {
+ EscapeReference esc = new EscapeJavaScriptReference();
+ assertEquals("Jimmy\\'s Pizza",esc.referenceInsert(null,"","Jimmy's Pizza"));
+ assertEquals("test string",esc.referenceInsert(null,"","test string"));
+
+
+ log("Correctly escaped Javascript");
+ }
+
+ /**
+ * test that escape reference handler works with no match restrictions
+ * @throws Exception
+ */
+ public void testEscapeReferenceMatchAll() throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, "org.apache.velocity.app.event.implement.EscapeHtmlReference");
+ ve.init();
+
+ Context context;
+ Writer writer;
+
+ // test normal reference
+ context = new VelocityContext();
+ writer = new StringWriter();
+ context.put("bold","<b>");
+ ve.evaluate(context,writer,"test","$bold test & test");
+ assertEquals("&lt;b&gt; test & test",writer.toString());
+
+ // test method reference
+ context = new VelocityContext();
+ writer = new StringWriter();
+ context.put("bold","<b>");
+ ve.evaluate(context,writer,"test","$bold.substring(0,1)");
+ assertEquals("&lt;",writer.toString());
+
+ log("Escape matched all references (global configuration)");
+
+ }
+
+ /**
+ * test that escape reference handler works with match restrictions
+ * @throws Exception
+ */
+ public void testEscapeReferenceMatch() throws Exception
+ {
+ // set up HTML match on everything, JavaScript match on _js*
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, "org.apache.velocity.app.event.implement.EscapeHtmlReference,org.apache.velocity.app.event.implement.EscapeJavaScriptReference");
+ ve.setProperty("eventhandler.escape.javascript.match", "/.*_js.*/");
+ ve.init();
+
+ Writer writer;
+
+ // Html no JavaScript
+ writer = new StringWriter();
+ ve.evaluate(newEscapeContext(),writer,"test","$test1");
+ assertEquals("Jimmy's &lt;b&gt;pizza&lt;/b&gt;",writer.toString());
+
+ // comment out bad test -- requires latest commons-lang
+ /*
+
+ // JavaScript and HTML
+ writer = new StringWriter();
+ ve.evaluate(newEscapeContext(),writer,"test","$test1_js");
+ assertEquals("Jimmy\\'s &lt;b&gt;pizza&lt;/b&gt;",writer.toString());
+
+ // JavaScript and HTML
+ writer = new StringWriter();
+ ve.evaluate(newEscapeContext(),writer,"test","$test1_js_test");
+ assertEquals("Jimmy\\'s &lt;b&gt;pizza&lt;/b&gt;",writer.toString());
+
+ // JavaScript and HTML (method call)
+ writer = new StringWriter();
+ ve.evaluate(newEscapeContext(),writer,"test","$test1_js.substring(0,7)");
+ assertEquals("Jimmy\\'s",writer.toString());
+
+ **/
+
+ log("Escape selected references (global configuration)");
+
+
+
+ }
+
+ private Context newEscapeContext()
+ {
+ Context context = new VelocityContext();
+ context.put("test1","Jimmy's <b>pizza</b>");
+ context.put("test1_js","Jimmy's <b>pizza</b>");
+ context.put("test1_js_test","Jimmy's <b>pizza</b>");
+ return context;
+ }
+
+ public void testPrintExceptionHandler() throws Exception
+ {
+ VelocityEngine ve1 = new VelocityEngine();
+ ve1.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, "org.apache.velocity.app.event.implement.PrintExceptions");
+ ve1.init();
+
+ VelocityEngine ve2 = new VelocityEngine();
+ ve2.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, "org.apache.velocity.app.event.implement.PrintExceptions");
+ ve2.setProperty("eventhandler.methodexception.templateinfo","true");
+ ve2.init();
+
+ VelocityEngine ve3 = new VelocityEngine();
+ ve3.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, "org.apache.velocity.app.event.implement.PrintExceptions");
+ ve3.setProperty("eventhandler.methodexception.stacktrace","true");
+ ve3.init();
+
+ Context context;
+ StringWriter writer;
+
+ context = new VelocityContext();
+ context.put("list",new ArrayList());
+
+ // exception and message only
+ writer = new StringWriter();
+ ve1.evaluate(context, writer, "test", "$list.get(0)");
+ String result = writer.toString();
+ assertTrue(result.contains("IndexOutOfBoundsException"));
+ assertTrue(
+ result.contains("Index: 0, Size: 0") // JDK8
+ || result.contains("Index 0 out of bounds for length 0") // JDK 11 / JDK 15
+ );
+ assertTrue(!result.contains("at test (line 1, column 7)"));
+ assertFalse(
+ result.contains("rangeCheck") // JDK 8
+ || result.contains("Preconditions.outOfBounds") // JDK 11 / JDK 15
+ );
+
+ // exception, message and template info
+ writer = new StringWriter();
+ ve2.evaluate(context,writer,"test","$list.get(0)");
+ result = writer.toString();
+ assertTrue(result.contains("IndexOutOfBoundsException"));
+ assertTrue(
+ result.contains("Index: 0, Size: 0") // JDK8
+ || result.contains("Index 0 out of bounds for length 0") // JDK 11 / JDK 15
+ );
+ assertTrue(result.contains("at test (line 1, column 7)"));
+ assertFalse(
+ result.contains("rangeCheck") // JDK 8
+ || result.contains("Preconditions.outOfBounds") // JDK 11 / JDK 15
+ );
+
+ // exception, message and stack trace
+ writer = new StringWriter();
+ ve3.evaluate(context,writer,"test","$list.get(0)");
+ result = writer.toString();
+ assertTrue(result.contains("IndexOutOfBoundsException"));
+ assertTrue(
+ result.contains("Index: 0, Size: 0") // JDK8
+ || result.contains("Index 0 out of bounds for length 0") // JDK 11 / JDK 15
+ );
+ assertTrue(!result.contains("at test (line 1, column 7)"));
+ assertTrue(
+ result.contains("rangeCheck") // JDK 8
+ || result.contains("Preconditions.outOfBounds") // JDK 11 / JDK 15
+ );
+
+ log("PrintException handler successful.");
+
+ }
+
+ public void testIncludeNotFound() throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_INCLUDE, "org.apache.velocity.app.event.implement.IncludeNotFound");
+ ve.addProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+ ve.init();
+
+ Template template;
+ FileOutputStream fos;
+ Writer fwriter;
+ Context context;
+
+ template = ve.getTemplate( getFileName(null, "test6", TMPL_FILE_EXT) );
+
+ fos = new FileOutputStream (
+ getFileName(RESULTS_DIR, "test6", RESULT_FILE_EXT));
+
+ fwriter = new BufferedWriter( new OutputStreamWriter(fos) );
+
+ context = new VelocityContext();
+ template.merge(context, fwriter);
+ fwriter.flush();
+ fwriter.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "test6", RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+
+ log("IncludeNotFound handler successful.");
+
+ }
+
+ public void testIncludeNotFoundMissingResourceName() throws Exception
+ {
+ // uses base test support
+ engine.setProperty(RuntimeConstants.EVENTHANDLER_INCLUDE, "org.apache.velocity.app.event.implement.IncludeNotFound");
+ addTemplate("notfound.vm", "$missingResource");
+ assertEvalEquals("foo", "#parse('foo')");
+ }
+
+ public void testIncludeRelativePath() throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_INCLUDE, "org.apache.velocity.app.event.implement.IncludeRelativePath");
+ ve.addProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+ ve.init();
+
+ Template template;
+ FileOutputStream fos;
+ Writer fwriter;
+ Context context;
+
+ template = ve.getTemplate( getFileName(null, "subdir/test2", TMPL_FILE_EXT) );
+
+ fos = new FileOutputStream (
+ getFileName(RESULTS_DIR, "test2", RESULT_FILE_EXT));
+
+ fwriter = new BufferedWriter( new OutputStreamWriter(fos) );
+
+ context = new VelocityContext();
+ template.merge(context, fwriter);
+ fwriter.flush();
+ fwriter.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "test2", RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+
+ log("IncludeRelativePath handler successful.");
+
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ClassloaderChangeTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ClassloaderChangeTestCase.java
new file mode 100644
index 00000000..096dbe7b
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ClassloaderChangeTestCase.java
@@ -0,0 +1,169 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.io.IOUtils;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.introspection.IntrospectorCache;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.StringWriter;
+
+/**
+ * Tests if we can hand Velocity an arbitrary class for logging.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class ClassloaderChangeTestCase extends TestCase
+{
+ private VelocityEngine ve = null;
+ private TestLogger logger = null;
+
+ private static String OUTPUT = "Hello From Foo";
+
+ /**
+ * Default constructor.
+ */
+ public ClassloaderChangeTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ ve = new VelocityEngine();
+ logger = new TestLogger(false, true);
+ logger.setEnabledLevel(TestLogger.LOG_LEVEL_DEBUG);
+ ve.setProperty(VelocityEngine.RUNTIME_LOG_INSTANCE, logger);
+ ve.init();
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(ClassloaderChangeTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testClassloaderChange()
+ throws Exception
+ {
+ logger.on();
+
+ VelocityContext vc = new VelocityContext();
+ Object foo = null;
+
+ /*
+ * first, we need a classloader to make our foo object
+ */
+
+ TestClassloader cl = new TestClassloader();
+ Class<?> fooclass = cl.loadClass("Foo");
+ foo = fooclass.newInstance();
+
+ /*
+ * put it into the context
+ */
+ vc.put("foo", foo);
+
+ /*
+ * and render something that would use it
+ * that will get it into the introspector cache
+ */
+ StringWriter writer = new StringWriter();
+ ve.evaluate( vc, writer, "test", "$foo.doIt()");
+
+ /*
+ * Check to make sure ok. note the obvious
+ * dependency on the Foo class...
+ */
+
+ if ( !writer.toString().equals( OUTPUT ))
+ {
+ fail("Output from doIt() incorrect");
+ }
+
+ /*
+ * and do it again :)
+ */
+ cl = new TestClassloader();
+ fooclass = cl.loadClass("Foo");
+ foo = fooclass.newInstance();
+
+ vc.put("foo", foo);
+
+ writer = new StringWriter();
+ ve.evaluate( vc, writer, "test", "$foo.doIt()");
+
+ if ( !writer.toString().equals( OUTPUT ))
+ {
+ fail("Output from doIt() incorrect");
+ }
+
+ if (!logger.getLog().contains(IntrospectorCache.CACHEDUMP_MSG))
+ {
+ fail("Didn't see introspector cache dump.");
+ }
+ }
+
+ /**
+ * Simple (real simple...) classloader that depends
+ * on a Foo.class being located in the classloader
+ * directory under test
+ */
+ public static class TestClassloader extends ClassLoader
+ {
+ private final static String testclass =
+ "classloader/Foo.class";
+
+ private Class<?> fooClass = null;
+
+ public TestClassloader()
+ throws Exception
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ InputStream fis = getClass().getResourceAsStream("/" + testclass);
+ IOUtils.copy(fis, os);
+ fis.close();
+ os.close();
+
+ byte[] barr = os.toByteArray();
+
+ fooClass = defineClass("classloader.Foo", barr, 0, barr.length);
+ }
+
+
+ @Override
+ public Class<?> findClass(String name)
+ {
+ return fooClass;
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ClasspathResourceTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ClasspathResourceTestCase.java
new file mode 100644
index 00000000..41285fab
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ClasspathResourceTestCase.java
@@ -0,0 +1,166 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Load templates from the Classpath.
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:daveb@miceda-data.com">Dave Bryson</a>
+ * @version $Id$
+ */
+public class ClasspathResourceTestCase extends BaseTestCase
+{
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/cpload";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/cpload/compare";
+
+ /**
+ * Default constructor.
+ */
+ public ClasspathResourceTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ Velocity.reset();
+ Velocity.setProperty(Velocity.RESOURCE_LOADERS, "classpath");
+
+ /*
+ * I don't think I should have to do this, these should
+ * be in the default config file.
+ */
+
+ Velocity.addProperty(
+ "classpath." + Velocity.RESOURCE_LOADER + ".class",
+ "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+
+ Velocity.setProperty(
+ "classpath." + Velocity.RESOURCE_LOADER + ".cache", "false");
+
+ Velocity.setProperty(
+ "classpath." + Velocity.RESOURCE_LOADER + ".modificationCheckInterval",
+ "2");
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(ClasspathResourceTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testClasspathResource ()
+ throws Exception
+ {
+ /*
+ * lets ensure the results directory exists
+ */
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ Template template1 = RuntimeSingleton.getTemplate("/includeevent/test1-cp." + TMPL_FILE_EXT);
+
+ // Uncomment when http://jira.codehaus.org/browse/MPTEST-57 has been resolved
+ // Template template2 = RuntimeSingleton.getTemplate(
+ // getFileName(null, "template/test2", TMPL_FILE_EXT));
+
+ FileOutputStream fos1 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "test1", RESULT_FILE_EXT));
+
+ // Uncomment when http://jira.codehaus.org/browse/MPTEST-57 has been resolved
+ // FileOutputStream fos2 =
+ // new FileOutputStream (
+ // getFileName(RESULTS_DIR, "test2", RESULT_FILE_EXT));
+
+ Writer writer1 = new BufferedWriter(new OutputStreamWriter(fos1));
+ // Uncomment when http://jira.codehaus.org/browse/MPTEST-57 has been resolved
+ // Writer writer2 = new BufferedWriter(new OutputStreamWriter(fos2));
+
+ /*
+ * put the Vector into the context, and merge both
+ */
+
+ VelocityContext context = new VelocityContext();
+
+ template1.merge(context, writer1);
+ writer1.flush();
+ writer1.close();
+
+ // Uncomment when http://jira.codehaus.org/browse/MPTEST-57 has been resolved
+ // template2.merge(context, writer2);
+ // writer2.flush();
+ // writer2.close();
+
+ if (!isMatch(RESULTS_DIR,COMPARE_DIR,"test1",RESULT_FILE_EXT,CMP_FILE_EXT)
+ // Uncomment when http://jira.codehaus.org/browse/MPTEST-57 has been resolved
+ // || !isMatch(RESULTS_DIR,COMPARE_DIR,"test2",RESULT_FILE_EXT,CMP_FILE_EXT)
+ )
+ {
+ fail("Output is incorrect!");
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/CommentsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/CommentsTestCase.java
new file mode 100644
index 00000000..39724a64
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/CommentsTestCase.java
@@ -0,0 +1,108 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+
+import java.io.StringWriter;
+
+/**
+ * Test comments
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class CommentsTestCase extends BaseTestCase
+{
+
+ public static Test suite()
+ {
+ return new TestSuite(CommentsTestCase.class);
+ }
+
+ /**
+ * Default constructor.
+ * @param name
+ */
+ public CommentsTestCase(String name)
+ {
+ super(name);
+ }
+
+
+ /**
+ * Test multiline comments
+ * @throws Exception
+ */
+ public void testMultiLine()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.init();
+
+ Context context = new VelocityContext();
+ StringWriter writer = new StringWriter();
+ ve.evaluate(context, writer, "test","abc #* test\r\ntest2*#\r\ndef");
+ assertEquals("abc \r\ndef", writer.toString());
+ }
+
+ /**
+ * Test single line comments
+ * @throws Exception
+ */
+ public void testSingleLine()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.init();
+
+ Context context = new VelocityContext();
+ StringWriter writer = new StringWriter();
+ ve.evaluate(context, writer, "test","123 ## test test\r\nabc");
+ assertEquals("123 abc", writer.toString());
+
+ context = new VelocityContext();
+ writer = new StringWriter();
+ ve.evaluate(context, writer, "test","123 \r\n## test test\r\nabc");
+ assertEquals("123 \r\nabc", writer.toString());
+
+ }
+
+ /**
+ * Test combined comments
+ * @throws Exception
+ */
+ public void testCombined()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.init();
+
+ Context context = new VelocityContext();
+ StringWriter writer = new StringWriter();
+ ve.evaluate(context, writer, "test","test\r\n## #* *# ${user \r\nabc");
+ assertEquals("test\r\nabc", writer.toString());
+
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/CommonsExtPropTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/CommonsExtPropTestCase.java
new file mode 100644
index 00000000..200150f6
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/CommonsExtPropTestCase.java
@@ -0,0 +1,178 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestSuite;
+import org.apache.velocity.util.ExtProperties;
+
+import java.io.FileWriter;
+import java.util.Iterator;
+import java.util.Vector;
+
+
+/**
+ * Tests for the ExtProperties class. This is an identical
+ * copy of the ConfigurationTestCase, which will disappear when
+ * the Configuration class does
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class CommonsExtPropTestCase extends BaseTestCase
+{
+ /**
+ * Comparison directory.
+ */
+ private static final String COMPARE_DIR =
+ TEST_COMPARE_DIR + "/configuration/compare";
+
+ /**
+ * Results directory.
+ */
+ private static final String RESULTS_DIR =
+ TEST_RESULT_DIR + "/configuration";
+
+ /**
+ * Test configuration
+ */
+ private static final String TEST_CONFIG =
+ TEST_COMPARE_DIR + "/configuration/test-config.properties";
+
+ /**
+ * Creates a new instance.
+ *
+ */
+ public CommonsExtPropTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new TestSuite(CommonsExtPropTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testExtendedProperties ()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ ExtProperties c = new ExtProperties(TEST_CONFIG);
+
+ FileWriter result = new FileWriter(
+ getFileName(RESULTS_DIR, "output", "res"));
+
+ message(result, "Testing order of keys ...");
+ showIterator(result, c.getKeys());
+
+ message(result, "Testing retrieval of CSV values ...");
+ showVector(result, c.getVector("resource.loaders"));
+
+ message(result, "Testing subset(prefix).getKeys() ...");
+ ExtProperties subset = c.subset("resource.loader.file");
+ showIterator(result, subset.getKeys());
+
+ message(result, "Testing getVector(prefix) ...");
+ showVector(result, subset.getVector("path"));
+
+ message(result, "Testing getString(key) ...");
+ result.write(c.getString("config.string.value"));
+ result.write("\n\n");
+
+ message(result, "Testing getBoolean(key) ...");
+ result.write(Boolean.valueOf(c.getBoolean("config.boolean.value")).toString());
+ result.write("\n\n");
+
+ message(result, "Testing getByte(key) ...");
+ result.write(new Byte(c.getByte("config.byte.value")).toString());
+ result.write("\n\n");
+
+ message(result, "Testing getShort(key) ...");
+ result.write(new Short(c.getShort("config.short.value")).toString());
+ result.write("\n\n");
+
+ message(result, "Testing getInt(key) ...");
+ result.write(new Integer(c.getInt("config.int.value")).toString());
+ result.write("\n\n");
+
+ message(result, "Testing getLong(key) ...");
+ result.write(new Long(c.getLong("config.long.value")).toString());
+ result.write("\n\n");
+
+ message(result, "Testing getFloat(key) ...");
+ result.write(new Float(c.getFloat("config.float.value")).toString());
+ result.write("\n\n");
+
+ message(result, "Testing getDouble(key) ...");
+ result.write(new Double(c.getDouble("config.double.value")).toString());
+ result.write("\n\n");
+
+ message(result, "Testing escaped-comma scalar...");
+ result.write( c.getString("escape.comma1"));
+ result.write("\n\n");
+
+ message(result, "Testing escaped-comma vector...");
+ showVector(result, c.getVector("escape.comma2"));
+ result.write("\n\n");
+
+ result.flush();
+ result.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "output","res","cmp"))
+ {
+ fail("Output incorrect.");
+ }
+ }
+
+ private void showIterator(FileWriter result, Iterator i)
+ throws Exception
+ {
+ while(i.hasNext())
+ {
+ result.write((String) i.next());
+ result.write("\n");
+ }
+ result.write("\n");
+ }
+
+ private void showVector(FileWriter result, Vector v)
+ throws Exception
+ {
+ for (Object aV : v)
+ {
+ result.write((String) aV);
+ result.write("\n");
+ }
+ result.write("\n");
+ }
+
+ private void message(FileWriter result, String message)
+ throws Exception
+ {
+ result.write("--------------------------------------------------\n");
+ result.write(message + "\n");
+ result.write("--------------------------------------------------\n");
+ result.write("\n");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ContextAutoreferenceKeyTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ContextAutoreferenceKeyTestCase.java
new file mode 100644
index 00000000..f427f4f7
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ContextAutoreferenceKeyTestCase.java
@@ -0,0 +1,58 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * This class tests passing expressions as method arguments
+ */
+
+public class ContextAutoreferenceKeyTestCase extends BaseTestCase
+{
+ public ContextAutoreferenceKeyTestCase(final String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ engine.setProperty(VelocityEngine.CONTEXT_AUTOREFERENCE_KEY, "self");
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("foo", "bar");
+ }
+
+ public void testAutoreference()
+ {
+ assertEvalEquals("bar", "$foo");
+ assertEvalEquals("bar", "$self.foo");
+ assertEvalEquals("bar", "$self.self.foo");
+ assertEvalEquals("true", "$self.containsKey('foo')");
+ assertEvalEquals("false", "$self.containsKey('bar')");
+ assertEvalEquals("bar", "$self.put('foo', 'baz')");
+ assertEvalEquals("baz", "$foo");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ContextSafetyTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ContextSafetyTestCase.java
new file mode 100644
index 00000000..a30c2940
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ContextSafetyTestCase.java
@@ -0,0 +1,145 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Vector;
+
+/**
+ * Tests if we are context safe : can we switch objects in the context
+ * and re-merge the template safely.
+ *
+ * NOTE:
+ * This class should not extend RuntimeTestCase because this test
+ * is run from the VelocityTestSuite which in effect a runtime
+ * test suite and the test suite initializes the Runtime. Extending
+ * RuntimeTestCase causes the Runtime to be initialized twice.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class ContextSafetyTestCase extends BaseTestCase implements TemplateTestBase
+{
+ public ContextSafetyTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ Velocity.reset();
+ Velocity.setProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(ContextSafetyTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testContextSafety ()
+ throws Exception
+ {
+ /*
+ * make a Vector and String array because
+ * they are treated differently in Foreach()
+ */
+ Vector<String> v = new Vector();
+
+ v.addElement("vector hello 1");
+ v.addElement( "vector hello 2");
+ v.addElement( "vector hello 3");
+
+ String strArray[] = new String[3];
+
+ strArray[0] = "array hello 1";
+ strArray[1] = "array hello 2";
+ strArray[2] = "array hello 3";
+
+ VelocityContext context = new VelocityContext();
+
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ /*
+ * get the template and the output
+ */
+
+ Template template = RuntimeSingleton.getTemplate(
+ getFileName(null, "context_safety", TMPL_FILE_EXT));
+
+ FileOutputStream fos1 =
+ new FileOutputStream (
+ getFileName(RESULT_DIR, "context_safety1", RESULT_FILE_EXT));
+
+ FileOutputStream fos2 =
+ new FileOutputStream (
+ getFileName(RESULT_DIR, "context_safety2", RESULT_FILE_EXT));
+
+ Writer writer1 = new BufferedWriter(new OutputStreamWriter(fos1));
+ Writer writer2 = new BufferedWriter(new OutputStreamWriter(fos2));
+
+ /*
+ * put the Vector into the context, and merge
+ */
+
+ context.put("vector", v);
+ template.merge(context, writer1);
+ writer1.flush();
+ writer1.close();
+
+ /*
+ * now put the string array into the context, and merge
+ */
+
+ context.put("vector", strArray);
+ template.merge(context, writer2);
+ writer2.flush();
+ writer2.close();
+
+ if (!isMatch(RESULT_DIR,COMPARE_DIR,"context_safety1",
+ RESULT_FILE_EXT,CMP_FILE_EXT) ||
+ !isMatch(RESULT_DIR,COMPARE_DIR,"context_safety2",
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/DefineTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/DefineTestCase.java
new file mode 100755
index 00000000..1c66ced8
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/DefineTestCase.java
@@ -0,0 +1,120 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+
+/**
+ * This class tests the #define directive
+ */
+public class DefineTestCase extends BaseTestCase
+{
+ public DefineTestCase(String name)
+ {
+ super(name);
+ }
+
+ protected String defAndEval(String block)
+ {
+ return defAndEval("def", block);
+ }
+
+ protected String defAndEval(String key, String block)
+ {
+ return evaluate("#define( $"+key+" )"+block+"#end$"+key);
+ }
+
+ public void testSimple()
+ {
+ assertEquals("abc", defAndEval("abc"));
+ assertEvalEquals("abc abc abc", "#define( $a )abc#end$a $a $a");
+ }
+
+ public void testNotSimple()
+ {
+ assertEquals("true", defAndEval("#if( $def )true#end"));
+ assertEquals("123", defAndEval("#foreach( $i in [1..3] )$i#end"));
+ assertEquals("hello world", defAndEval("#macro( test )hello world#end#test()"));
+ }
+
+ public void testOverridingDefinitionInternally()
+ {
+ assertEvalEquals("truefalse", "#define( $or )true#set( $or = false )#end$or$or");
+ }
+
+ public void testLateBinding()
+ {
+ context.put("baz", "foo");
+ assertEvalEquals("foobar", "#define( $lb )$baz#end${lb}#set( $baz = 'bar' )${lb}");
+ }
+
+ public void testRerendering()
+ {
+ context.put("inc", new Inc());
+ assertEvalEquals("1 2 3", "#define( $i )$inc#end$i $i $i");
+ }
+
+ public void testAssignation()
+ {
+ assertEvalEquals("[][hello]","#define( $orig )hello#end[#set( $assig = $orig )][$assig]");
+ }
+
+ public void testNonRenderingUsage()
+ {
+ String template = "#define($foo)\n" +
+ " foo_contents\n" +
+ "#end\n" +
+ "#if ($foo)\n" +
+ " found foo\n" +
+ "#end";
+ assertEvalEquals(" found foo\n", template);
+ }
+
+ public void testRecursionLimit()
+ {
+ try
+ {
+ assertEvalEquals("$r", "#define( $r )$r#end$r");
+ }
+ catch (Exception t)
+ {
+ fail("Recursion should not have thrown an exception");
+ }
+ catch (Error e)
+ {
+ fail("Infinite recursion should not be possible.");
+ }
+ }
+
+ public void testThingsOfQuestionableMorality()
+ {
+ // redefining $foo within $foo
+ assertEquals("foobar", defAndEval("foo", "foo#define( $foo )bar#end$foo"));
+ }
+
+
+ public static class Inc
+ {
+ int foo = 1;
+ public String toString()
+ {
+ return String.valueOf(foo++);
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/EncodingTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/EncodingTestCase.java
new file mode 100644
index 00000000..bc056222
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/EncodingTestCase.java
@@ -0,0 +1,208 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests input encoding handling. The input target is UTF-8, having
+ * chinese and and a spanish enyay (n-twiddle)
+ *
+ * Thanks to Kent Johnson for the example input file.
+ *
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class EncodingTestCase extends BaseTestCase implements TemplateTestBase
+{
+ public EncodingTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ Velocity.reset();
+
+ Velocity.setProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+
+ Velocity.setProperty( Velocity.INPUT_ENCODING, "UTF-8" );
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(EncodingTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testChineseEncoding ()
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext();
+
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ /*
+ * get the template and the output
+ */
+
+ /*
+ * Chinese and spanish
+ */
+
+ Template template = Velocity.getTemplate(
+ getFileName(null, "encodingtest", TMPL_FILE_EXT), "UTF-8");
+
+ FileOutputStream fos =
+ new FileOutputStream (
+ getFileName(RESULT_DIR, "encodingtest", RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8));
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR,COMPARE_DIR,"encodingtest",
+ RESULT_FILE_EXT,CMP_FILE_EXT) )
+ {
+ fail("Output 1 incorrect.");
+ }
+ }
+
+ public void testHighByteChinese()
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext();
+
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ /*
+ * a 'high-byte' chinese example from Michael Zhou
+ */
+
+ Template template = Velocity.getTemplate(
+ getFileName( null, "encodingtest2", TMPL_FILE_EXT), "UTF-8");
+
+ FileOutputStream fos =
+ new FileOutputStream (
+ getFileName(RESULT_DIR, "encodingtest2", RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8));
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR,COMPARE_DIR,"encodingtest2",
+ RESULT_FILE_EXT,CMP_FILE_EXT) )
+ {
+ fail("Output 2 incorrect.");
+ }
+
+ }
+
+ public void testHighByteChinese2()
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext();
+
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ /*
+ * a 'high-byte' chinese from Ilkka
+ */
+
+ Template template = Velocity.getTemplate(
+ getFileName( null, "encodingtest3", TMPL_FILE_EXT), "GBK");
+
+ FileOutputStream fos =
+ new FileOutputStream (
+ getFileName(RESULT_DIR, "encodingtest3", RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos, "GBK"));
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR,COMPARE_DIR,"encodingtest3",
+ RESULT_FILE_EXT,CMP_FILE_EXT) )
+ {
+ fail("Output 3 incorrect.");
+ }
+ }
+
+ public void testRussian()
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext();
+
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ /*
+ * Russian example from Vitaly Repetenko
+ */
+
+ Template template = Velocity.getTemplate(
+ getFileName( null, "encodingtest_KOI8-R", TMPL_FILE_EXT), "KOI8-R");
+
+ FileOutputStream fos =
+ new FileOutputStream (
+ getFileName(RESULT_DIR, "encodingtest_KOI8-R", RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos, "KOI8-R"));
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR,COMPARE_DIR,"encodingtest_KOI8-R",
+ RESULT_FILE_EXT,CMP_FILE_EXT) )
+ {
+ fail("Output 4 incorrect.");
+ }
+ }
+}
+
+
+
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/EvaluateTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/EvaluateTestCase.java
new file mode 100644
index 00000000..fc5c1d1b
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/EvaluateTestCase.java
@@ -0,0 +1,299 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.app.event.implement.EscapeHtmlReference;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Test #evaluate directive.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class EvaluateTestCase extends BaseTestCase
+{
+
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/evaluate";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/evaluate";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/evaluate/compare";
+
+ /**
+ * Default constructor.
+ * @param name
+ */
+ public EvaluateTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ assureResultsDirectoryExists(RESULTS_DIR);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ // extension hook
+ }
+
+
+ /**
+ * Test basic functionality.
+ * @throws Exception
+ */
+ public void testEvaluate()
+ throws Exception
+ {
+ Map props = new HashMap();
+ testFile("eval1", props);
+ }
+
+ /**
+ * Test evaluate directive preserves macros (VELOCITY-591)
+ * @throws Exception
+ */
+ public void testEvaluateMacroPreserve()
+ throws Exception
+ {
+ Map properties = new HashMap();
+ testFile("eval2", properties);
+
+ properties.put(RuntimeConstants.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL,"false");
+ testFile("eval2", properties);
+ }
+
+ /**
+ * Test in a macro context.
+ * @throws Exception
+ */
+ public void testEvaluateVMContext()
+ throws Exception
+ {
+ testFile("evalvmcontext", new HashMap());
+ }
+
+ /**
+ * Test #stop and #break
+ * @throws Exception
+ */
+ public void testStopAndBreak()
+ {
+ engine.setProperty("evaluate.provide.scope.control", "true");
+ assertEvalEquals("t ", "t #stop t2 #evaluate('t3')");
+ assertEvalEquals("t ", "t #break t2 #evaluate('t3')");
+ assertEvalEquals("t t2 t3 ", "t t2 #evaluate('t3 #stop t4') t5");
+ assertEvalEquals("t t2 t3 t5", "t t2 #evaluate('t3 #break t4') t5");
+ assertEvalEquals("t t2 t3 ", "t t2 #evaluate('t3 #break($evaluate.topmost) t4') t5");
+ }
+
+ /**
+ * Test that the event handlers work in #evaluate (since they are
+ * attached to the context). Only need to check one - they all
+ * work the same.
+ * @throws Exception
+ */
+ public void testEventHandler()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, EscapeHtmlReference.class.getName());
+ ve.init();
+
+ Context context = new VelocityContext();
+ context.put("lt","<");
+ context.put("gt",">");
+ StringWriter writer = new StringWriter();
+ ve.evaluate(context, writer, "test","${lt}test${gt} #evaluate('${lt}test2${gt}')");
+ assertEquals("&lt;test&gt; &lt;test2&gt;", writer.toString());
+
+ }
+
+
+ /**
+ * Test errors are thrown
+ * @throws Exception
+ */
+ public void testErrors()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.init();
+
+ Context context = new VelocityContext();
+
+ // no arguments
+ StringWriter writer = new StringWriter();
+ try
+ {
+ ve.evaluate(context, writer, "test",
+ "#evaluate()");
+ fail("Expected exception");
+ }
+ catch (ParseErrorException e)
+ {
+ assertEquals("test",e.getTemplateName());
+ assertEquals(1,e.getLineNumber());
+ assertEquals(1,e.getColumnNumber());
+ }
+
+ // too many arguments
+ writer = new StringWriter();
+ try
+ {
+ ve.evaluate(context, writer, "test",
+ "#evaluate('aaa' 'bbb')");
+ fail("Expected exception");
+ }
+ catch (ParseErrorException e)
+ {
+ assertEquals("test",e.getTemplateName());
+ assertEquals(1,e.getLineNumber());
+ assertEquals(17,e.getColumnNumber());
+ }
+
+ // argument not a string or reference
+ writer = new StringWriter();
+ try
+ {
+ ve.evaluate(context, writer, "test",
+ "#evaluate(10)");
+ fail("Expected exception");
+ }
+ catch (ParseErrorException e)
+ {
+ assertEquals("test",e.getTemplateName());
+ assertEquals(1,e.getLineNumber());
+ assertEquals(11,e.getColumnNumber());
+ }
+
+ // checking line/col for parse error
+ writer = new StringWriter();
+ try
+ {
+ String eval = "this is a multiline\n\n\n\n\n test #foreach() with an error";
+ context.put("eval",eval);
+ ve.evaluate(context, writer, "test",
+ "first line\n second line: #evaluate($eval)");
+ fail("Expected exception");
+ }
+ catch (ParseErrorException e)
+ {
+ // should be start of #evaluate
+ assertEquals("test",e.getTemplateName());
+ assertEquals(2,e.getLineNumber());
+ assertEquals(15,e.getColumnNumber());
+ }
+ }
+
+ /**
+ * Test a file parses with no errors and compare to existing file.
+ * @param basefilename
+ * @throws Exception
+ */
+ private void testFile(String basefilename, Map<String, Object> properties)
+ throws Exception
+ {
+ info("Test file: "+basefilename);
+ VelocityEngine ve = engine;
+ ve.addProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+
+ for (String key : properties.keySet())
+ {
+ String value = (String) properties.get(key);
+ ve.addProperty(key, value);
+ info("Add property: " + key + " = " + value);
+ }
+
+ ve.init();
+
+ Template template;
+ FileOutputStream fos;
+ Writer fwriter;
+
+ template = ve.getTemplate( getFileName(null, basefilename, TMPL_FILE_EXT) );
+
+ fos = new FileOutputStream (
+ getFileName(RESULTS_DIR, basefilename, RESULT_FILE_EXT));
+
+ fwriter = new BufferedWriter( new OutputStreamWriter(fos) );
+
+ template.merge(context, fwriter);
+ fwriter.flush();
+ fwriter.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, basefilename, RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ String result = getFileContents(RESULTS_DIR, basefilename, RESULT_FILE_EXT);
+ String compare = getFileContents(COMPARE_DIR, basefilename, CMP_FILE_EXT);
+
+ String msg = "Output was incorrect\n"+
+ "-----Result-----\n"+ result +
+ "----Expected----\n"+ compare +
+ "----------------";
+
+ fail(msg);
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/EventHandlingTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/EventHandlingTestCase.java
new file mode 100644
index 00000000..ef3bfcdf
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/EventHandlingTestCase.java
@@ -0,0 +1,271 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.event.EventCartridge;
+import org.apache.velocity.app.event.MethodExceptionEventHandler;
+import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.util.ContextAware;
+import org.apache.velocity.util.RuntimeServicesAware;
+import org.apache.velocity.util.introspection.Info;
+
+/**
+ * Tests event handling for all event handlers except IncludeEventHandler. This is tested
+ * separately due to its complexity.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class EventHandlingTestCase extends BaseTestCase
+{
+ private static String NO_REFERENCE_VALUE = "<no reference value>";
+ private static String REFERENCE_VALUE = "<reference value>";
+
+ public EventHandlingTestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testManualEventHandlers()
+ throws Exception
+ {
+ TestEventCartridge te = new TestEventCartridge();
+ /*
+ * Test attaching the event cartridge to the context.
+ * Make an event cartridge, register all the
+ * event handlers (at once) and attach it to the
+ * Context
+ */
+
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(te);
+ ec.attachToContext(context);
+
+ /*
+ * now wrap the event cartridge - we want to make sure that
+ * we can do this w/o harm
+ */
+ doTestReferenceInsertionEventHandler1();
+ doTestReferenceInsertionEventHandler2();
+ doTestMethodExceptionEventHandler1();
+ doTestMethodExceptionEventHandler2();
+ }
+
+ /**
+ * Test assigning the event handlers via properties
+ */
+ public void testConfigurationEventHandlers()
+ throws Exception
+ {
+ engine.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, TestEventCartridge.class.getName());
+ engine.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, TestEventCartridge.class.getName());
+
+ doTestReferenceInsertionEventHandler1();
+ doTestReferenceInsertionEventHandler2();
+ doTestMethodExceptionEventHandler1();
+ doTestMethodExceptionEventHandler2();
+ }
+
+ /**
+ * Test all the event handlers using the given engine.
+ */
+ private void doTestReferenceInsertionEventHandler1()
+ throws Exception
+ {
+ VelocityContext outer = context;
+ context = new VelocityContext(context);
+ context.put("name", "Velocity");
+
+ /*
+ * First, the reference insertion handler
+ */
+ String expected = REFERENCE_VALUE + REFERENCE_VALUE + REFERENCE_VALUE;
+ assertEvalEquals(expected, "$name$name$name");
+
+ context = outer;
+ }
+
+ private void doTestReferenceInsertionEventHandler2()
+ throws Exception
+ {
+ VelocityContext outer = context;
+ context = new VelocityContext(context);
+ context.put("name", "Velocity");
+
+ /*
+ * using the same handler, we can deal with
+ * null references as well
+ */
+ assertEvalEquals(NO_REFERENCE_VALUE, "$floobie");
+
+ context = outer;
+ }
+
+ private void doTestMethodExceptionEventHandler1()
+ throws Exception
+ {
+ VelocityContext outer = context;
+ context = new VelocityContext(context);
+
+ /*
+ * finally, we test a method exception event - we do this
+ * by putting this class in the context, and calling
+ * a method that does nothing but throw an exception.
+ * we use flag in the context to turn the event handling
+ * on and off
+ *
+ * Note also how the reference insertion process
+ * happens as well
+ */
+ context.put("allow_exception",Boolean.TRUE);
+ context.put("this", this );
+
+ evaluate(" $this.throwException()");
+
+ context = outer;
+ }
+
+ private void doTestMethodExceptionEventHandler2()
+ throws Exception
+ {
+ VelocityContext outer = context;
+ context = new VelocityContext(context);
+ context.put("this", this );
+
+ /*
+ * now, we remove the exception flag, and we can see that the
+ * exception will propgate all the way up here, and
+ * wil be caught by the catch() block below
+ */
+ assertEvalException("$this.throwException()", MethodInvocationException.class);
+
+ context = outer;
+ }
+
+ /**
+ * silly method to throw an exception to test
+ * the method invocation exception event handling
+ */
+ public void throwException()
+ throws Exception
+ {
+ throw new Exception("Hello from throwException()");
+ }
+
+
+
+ public static class TestEventCartridge
+ implements ReferenceInsertionEventHandler,
+ MethodExceptionEventHandler,
+ RuntimeServicesAware,ContextAware
+ {
+ private RuntimeServices rs;
+
+ /**
+ * Required by EventHandler
+ */
+ @Override
+ public void setRuntimeServices(RuntimeServices rs )
+ {
+ // make sure this is only called once
+ if (this.rs == null)
+ this.rs = rs;
+
+ else
+ fail("initialize called more than once.");
+ }
+
+ /**
+ * Event handler for when a reference is inserted into the output stream.
+ */
+ @Override
+ public Object referenceInsert(Context context, String reference, Object value )
+ {
+ // as a test, make sure this EventHandler is initialized
+ if (rs == null)
+ fail ("Event handler not initialized!");
+
+
+ /*
+ * if we have a value
+ * return a known value
+ */
+ String s = null;
+
+ if( value != null )
+ {
+ s = REFERENCE_VALUE;
+ }
+ else
+ {
+ /*
+ * we only want to deal with $floobie - anything
+ * else we let go
+ */
+ if ( reference.equals("$floobie") )
+ {
+ s = NO_REFERENCE_VALUE;
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Handles exceptions thrown during in-template method access
+ */
+ @Override
+ public Object methodException(Context context, Class claz, String method, Exception e, Info info )
+ {
+ // as a test, make sure this EventHandler is initialized
+ if (rs == null)
+ fail ("Event handler not initialized!");
+
+ // only do processing if the switch is on
+ if (context != null)
+ {
+ boolean exceptionSwitch = context.containsKey("allow_exception");
+
+ if( exceptionSwitch && method.equals("throwException"))
+ {
+ return "handler";
+ }
+ else
+ throw new RuntimeException(e);
+
+ } else
+
+ throw new RuntimeException(e);
+ }
+
+ Context context;
+
+
+ @Override
+ public void setContext(Context context)
+ {
+ this.context = context;
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ExceptionTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ExceptionTestCase.java
new file mode 100644
index 00000000..b555e1bc
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ExceptionTestCase.java
@@ -0,0 +1,171 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.misc.ExceptionGeneratingDirective;
+import org.apache.velocity.test.misc.ExceptionGeneratingEventHandler;
+import org.apache.velocity.test.misc.ExceptionGeneratingResourceLoader;
+import org.apache.velocity.test.provider.TestProvider;
+
+import java.io.StringWriter;
+
+/**
+ * Test case for miscellaneous Exception related issues.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class ExceptionTestCase extends BaseTestCase implements TemplateTestBase
+{
+ VelocityEngine ve;
+
+ /**
+ * Default constructor.
+ */
+ public ExceptionTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(ExceptionTestCase.class);
+ }
+
+
+ public void testReferenceInsertionEventHandlerException()
+ throws Exception
+ {
+ ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION,ExceptionGeneratingEventHandler.class.getName());
+ ve.init();
+ assertException(ve);
+ }
+
+ /**
+ * Note - this is the one case where RuntimeExceptions *are not* passed through
+ * verbatim.
+ * @throws Exception
+ */
+ public void testMethodExceptionEventHandlerException()
+ throws Exception
+ {
+ ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION,ExceptionGeneratingEventHandler.class.getName());
+ ve.init();
+ Context context = new VelocityContext();
+ context.put ("test",new TestProvider());
+ assertMethodInvocationException(ve,context,"$test.getThrow()");
+ assertMethodInvocationException(ve,context,"$test.throw");
+ }
+
+ public void testIncludeEventHandlerException()
+ throws Exception
+ {
+ ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_INCLUDE,ExceptionGeneratingEventHandler.class.getName());
+ ve.init();
+ assertException(ve,"#include('dummy')");
+ }
+
+ public void testResourceLoaderException()
+ throws Exception
+ {
+ ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.RESOURCE_LOADERS,"except");
+ ve.setProperty("except.resource.loader.class",ExceptionGeneratingResourceLoader.class.getName());
+ try
+ {
+ ve.init(); // tries to get the macro file
+ ve.getTemplate("test.txt");
+ fail("Should have thrown RuntimeException");
+ }
+ catch (RuntimeException E)
+ {
+ // do nothing
+ }
+ }
+
+
+ public void testDirectiveException()
+ throws Exception
+ {
+ ve = new VelocityEngine();
+ ve.setProperty("userdirective",ExceptionGeneratingDirective.class.getName());
+ ve.init();
+ assertException(ve,"#Exception() test #end");
+ }
+
+
+
+ public void assertException(VelocityEngine ve)
+ throws Exception
+ {
+ Context context = new VelocityContext();
+ context.put ("test","test");
+ assertException(ve,context,"this is a $test");
+ }
+
+ public void assertException(VelocityEngine ve, String input)
+ throws Exception
+ {
+ Context context = new VelocityContext();
+ context.put ("test","test");
+ assertException(ve,context,input);
+ }
+
+ public void assertException(VelocityEngine ve, Context context, String input)
+ throws Exception
+ {
+ try
+ {
+ StringWriter writer = new StringWriter();
+ ve.evaluate(context,writer,"test",input);
+ fail("Expected RuntimeException");
+ }
+ catch (RuntimeException E)
+ {
+ // do nothing
+ }
+ }
+ public void assertMethodInvocationException(VelocityEngine ve, Context context, String input)
+ throws Exception
+ {
+ try
+ {
+ StringWriter writer = new StringWriter();
+ ve.evaluate(context,writer,"test",input);
+ fail("Expected MethodInvocationException");
+ }
+ catch (MethodInvocationException E)
+ {
+ // do nothing
+ }
+ }
+
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ExpressionAsMethodArgumentTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ExpressionAsMethodArgumentTestCase.java
new file mode 100644
index 00000000..2816799a
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ExpressionAsMethodArgumentTestCase.java
@@ -0,0 +1,56 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+
+/**
+ * This class tests passing expressions as method arguments
+ */
+
+public class ExpressionAsMethodArgumentTestCase extends BaseTestCase
+{
+ public ExpressionAsMethodArgumentTestCase(final String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("tool",new MyAbsTool());
+ context.put("foo",2);
+ context.put("bar",-3);
+ }
+
+ public void testExpressionAsMethod()
+ {
+ assertEvalEquals("6","$tool.abs( $foo * $bar )");
+ assertEvalEquals("12","$tool.abs( $foo * $tool.abs( $foo * $bar ) )");
+ }
+
+ public static class MyAbsTool
+ {
+ public int abs(int num)
+ {
+ return Math.abs(num);
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/FilteredEventHandlingTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/FilteredEventHandlingTestCase.java
new file mode 100644
index 00000000..e56fa733
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/FilteredEventHandlingTestCase.java
@@ -0,0 +1,238 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+
+/**
+ * Tests event handling for all event handlers when multiple event handlers are
+ * assigned for each type.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class FilteredEventHandlingTestCase extends BaseTestCase
+{
+
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/includeevent";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/includeevent";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/includeevent/compare";
+
+
+ private TestLogger logger = new TestLogger(false, false);
+
+ /**
+ * Default constructor.
+ */
+ public FilteredEventHandlingTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(FilteredEventHandlingTestCase.class);
+ }
+
+ public void testFilteredEventHandling() throws Exception
+ {
+ String handler1 = "org.apache.velocity.test.eventhandler.Handler1";
+ String handler2 = "org.apache.velocity.test.eventhandler.Handler2";
+ String sequence1 = handler1 + "," + handler2;
+ String sequence2 = handler2 + "," + handler1;
+
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ /*
+ * Set up two VelocityEngines that will apply the handlers in both orders
+ */
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, sequence1);
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, sequence1);
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_INCLUDE, sequence1);
+ ve.addProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+ ve.init();
+
+ VelocityEngine ve2 = new VelocityEngine();
+ ve2.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ ve2.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, sequence2);
+ ve2.setProperty(RuntimeConstants.EVENTHANDLER_REFERENCEINSERTION, sequence2);
+ ve2.setProperty(RuntimeConstants.EVENTHANDLER_INCLUDE, sequence2);
+ ve2.addProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+ ve2.init();
+
+ VelocityContext context;
+ StringWriter w;
+
+
+ // check reference insertion with both sequences
+ context = new VelocityContext();
+ w = new StringWriter();
+ context.put("test","abc");
+ ve.evaluate( context, w, "test", "$test" );
+ if ( !w.toString().equals( "ABCABC" ))
+ {
+ fail( "Reference insertion test 1");
+ }
+
+ context = new VelocityContext();
+ w = new StringWriter();
+ context.put("test","abc");
+ ve2.evaluate( context, w, "test", "$test" );
+ if ( !w.toString().equals( "ABCabc" ))
+ {
+ fail( "Reference insertion test 2");
+ }
+
+ // check method exception with both sequences
+
+ // sequence 1
+ context = new VelocityContext();
+ w = new StringWriter();
+ context.put("test",new ArrayList());
+
+ try
+ {
+ ve.evaluate( context, w, "test", "$test.get(0)");
+ fail ( "Method exception event test 1" );
+ }
+ catch( MethodInvocationException mee )
+ {
+ // do nothing
+ }
+
+ // sequence2
+ context = new VelocityContext();
+ w = new StringWriter();
+ context.put("test",new ArrayList());
+
+ ve2.evaluate( context, w, "test", "$test.get(0)");
+
+ // check log on null set with both sequences
+ // sequence 1
+ context = new VelocityContext();
+ w = new StringWriter();
+ logger.startCapture();
+ ve.evaluate( context, w, "test", "#set($test1 = $test2)" );
+ String log = logger.getLog();
+ if ( log != null && log.length() > 0)
+ {
+ fail( "log null set test 1");
+ }
+
+ // sequence 2
+ context = new VelocityContext();
+ w = new StringWriter();
+ logger.startCapture();
+ ve2.evaluate( context, w, "test", "#set($test1 = $test2)" );
+ log = logger.getLog();
+ if ( log != null && log.length() > 0)
+ {
+ fail( "log null set test 2");
+ }
+
+ logger.stopCapture();
+
+ // check include event handler with both sequences
+
+ // sequence 1
+ Template template;
+ FileOutputStream fos;
+ Writer fwriter;
+
+ template = ve.getTemplate( getFileName(null, "test4", TMPL_FILE_EXT) );
+
+ fos = new FileOutputStream (
+ getFileName(RESULTS_DIR, "test4", RESULT_FILE_EXT));
+
+ fwriter = new BufferedWriter( new OutputStreamWriter(fos) );
+
+ context = new VelocityContext();
+ template.merge(context, fwriter);
+ fwriter.flush();
+ fwriter.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "test4", RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+
+ // sequence 2
+ template = ve2.getTemplate( getFileName(null, "test5", TMPL_FILE_EXT) );
+
+ fos = new FileOutputStream (
+ getFileName(RESULTS_DIR, "test5", RESULT_FILE_EXT));
+
+ fwriter = new BufferedWriter( new OutputStreamWriter(fos) );
+
+ context = new VelocityContext();
+ template.merge(context, fwriter);
+ fwriter.flush();
+ fwriter.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "test5", RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ForeachTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ForeachTestCase.java
new file mode 100644
index 00000000..2e495843
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ForeachTestCase.java
@@ -0,0 +1,147 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.provider.ForeachMethodCallHelper;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class tests the Foreach loop.
+ *
+ * @author Daniel Rall
+ * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
+ */
+public class ForeachTestCase extends BaseTestCase
+{
+ public ForeachTestCase(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Tests limiting of the number of loop iterations.
+ */
+ public void testMaxNbrLoopsConstraint()
+ throws Exception
+ {
+ // Limit the loop to three iterations.
+ engine.setProperty(RuntimeConstants.MAX_NUMBER_LOOPS,
+ 3);
+
+ assertEvalEquals("1 2 3 ", "#foreach ($item in [1..10])$item #end");
+ }
+
+ /**
+ * Tests proper method execution during a Foreach loop over a Collection
+ * with items of varying classes.
+ */
+ public void testCollectionAndMethodCall()
+ throws Exception
+ {
+ List col = new ArrayList();
+ col.add(100);
+ col.add("STRVALUE");
+ context.put("helper", new ForeachMethodCallHelper());
+ context.put("col", col);
+
+ assertEvalEquals("int 100 str STRVALUE ", "#foreach ( $item in $col )$helper.getFoo($item) #end");
+ }
+
+ /**
+ * Tests that #foreach will be able to retrieve an iterator from
+ * an arbitrary object that happens to have an iterator() method.
+ * (With the side effect of supporting the new Java 5 Iterable interface)
+ */
+ public void testObjectWithIteratorMethod()
+ throws Exception
+ {
+ context.put("iterable", new MyIterable());
+
+ assertEvalEquals("1 2 3 ", "#foreach ($i in $iterable)$i #end");
+ }
+
+ public void testNotReallyIterableIteratorMethod()
+ throws Exception
+ {
+ context.put("nri", new NotReallyIterable());
+
+ assertEvalEquals("", "#foreach ($i in $nri)$i #end");
+ }
+
+ public void testVelocityHasNextProperty()
+ throws Exception
+ {
+ List list = new ArrayList();
+ list.add("test1");
+ list.add("test2");
+ list.add("test3");
+ context.put("list", list);
+ assertEvalEquals("test1 SEPARATOR test2 SEPARATOR test3 ", "#foreach ($value in $list)$value #if( $foreach.hasNext )SEPARATOR #end#end");
+ }
+
+ public void testNestedVelocityHasNextProperty()
+ throws Exception
+ {
+ List list = new ArrayList();
+ list.add("test1");
+ list.add("test2");
+ list.add("test3");
+ list.add("test4");
+ context.put("list", list);
+ List list2 = new ArrayList();
+ list2.add("a1");
+ list2.add("a2");
+ list2.add("a3");
+ context.put("list2", list2);
+
+ assertEvalEquals("test1 (a1;a2;a3)-test2 (a1;a2;a3)-test3 (a1;a2;a3)-test4 (a1;a2;a3)", "#foreach ($value in $list)$value (#foreach ($val in $list2)$val#if( $foreach.hasNext );#end#end)#if( $foreach.hasNext )-#end#end");
+ }
+
+ public static class MyIterable
+ {
+ private List foo;
+
+ public MyIterable()
+ {
+ foo = new ArrayList();
+ foo.add(1);
+ foo.add(2L);
+ foo.add("3");
+ }
+
+ public Iterator iterator()
+ {
+ return foo.iterator();
+ }
+ }
+
+ public static class NotReallyIterable
+ {
+ public Object iterator()
+ {
+ return new Object();
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/GetAsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/GetAsTestCase.java
new file mode 100644
index 00000000..776f0b43
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/GetAsTestCase.java
@@ -0,0 +1,145 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.util.TemplateBoolean;
+import org.apache.velocity.util.TemplateNumber;
+import org.apache.velocity.util.TemplateString;
+
+/**
+ * Test objects with getAs<Type>() methods.
+ */
+public class GetAsTestCase extends BaseTestCase
+{
+ public GetAsTestCase(final String name)
+ {
+ super(name);
+ }
+
+ public void testCustomString()
+ {
+ // render
+ context.put("foo", new CustomString("getAsString"));
+ assertEvalEquals("getAsString", "$foo");
+ // aborted value
+ context.put("bar", new CustomString(null));
+ assertEvalEquals("", "$!bar");
+ // concatenation
+ context.put("half", new CustomString("half"));
+ assertEvalEquals("1half", "#set( $out = 1 + $half )$out");
+ }
+
+ public void testCustomBoolean()
+ {
+ context.put("foo", new CustomBoolean(false));
+ assertEvalEquals("right", "#if( !$foo )right#end");
+ }
+
+ public void testCustomNumber()
+ {
+ context.put("foo", new CustomNumber(7));
+ assertEvalEquals("14", "#set( $bar = $foo * 2 )$bar");
+ }
+
+
+ public void testTemplateString()
+ {
+ context.put("foo", new CustomTemplateString("getAsString"));
+ assertEvalEquals("getAsString", "$foo");
+ }
+
+ public void testTemplateBoolean()
+ {
+ context.put("foo", new CustomTemplateBoolean(false));
+ assertEvalEquals("right", "#if( !$foo )right#end");
+ }
+
+ public void testTemplateNumber()
+ {
+ context.put("foo", new CustomTemplateNumber(5));
+ assertEvalEquals("25", "#set( $foo = $foo * $foo )$foo");
+ }
+
+
+
+ public static class CustomString
+ {
+ String string;
+ public CustomString(String string)
+ {
+ this.string = string;
+ }
+ public String getAsString()
+ {
+ return string;
+ }
+ }
+
+ public static class CustomBoolean
+ {
+ boolean bool;
+ public CustomBoolean(boolean bool)
+ {
+ this.bool = bool;
+ }
+ public boolean getAsBoolean()
+ {
+ return bool;
+ }
+ }
+
+ public static class CustomNumber
+ {
+ Number num;
+ public CustomNumber(Number num)
+ {
+ this.num = num;
+ }
+ public Number getAsNumber()
+ {
+ return num;
+ }
+ }
+
+ public static class CustomTemplateString extends CustomString implements TemplateString
+ {
+ public CustomTemplateString(String string)
+ {
+ super(string);
+ }
+ }
+
+ public static class CustomTemplateBoolean extends CustomBoolean implements TemplateBoolean
+ {
+ public CustomTemplateBoolean(Boolean bool)
+ {
+ super(bool);
+ }
+ }
+
+ public static class CustomTemplateNumber extends CustomNumber implements TemplateNumber
+ {
+ public CustomTemplateNumber(Number num)
+ {
+ super(num);
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/HyphenInIdentifiersTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/HyphenInIdentifiersTestCase.java
new file mode 100644
index 00000000..e4ccc7f9
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/HyphenInIdentifiersTestCase.java
@@ -0,0 +1,50 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * This class tests passing expressions as method arguments
+ */
+
+public class HyphenInIdentifiersTestCase extends BaseTestCase
+{
+ public HyphenInIdentifiersTestCase(final String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ engine.addProperty("parser.allow_hyphen_in_identifiers", true);
+ }
+
+ public void testHyphen()
+ {
+ assertEvalEquals("6","#set($var-1 = 7)#set($var-2 = $var-1 - 1)$var-2");
+ }
+
+ public void testHyphenInCollection()
+ {
+ assertEvalEquals("foofoofoo","#set($map = {'name-with-hyphen':'foo'})$map.name-with-hyphen${map.name-with-hyphen}${map.get('name-with-hyphen')}");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyNoEmptyCheckTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyNoEmptyCheckTestCase.java
new file mode 100644
index 00000000..2935100c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyNoEmptyCheckTestCase.java
@@ -0,0 +1,120 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+import java.util.Collections;
+
+/**
+ * Used to check that empty values are properly handled in #if statements
+ */
+public class IfEmptyNoEmptyCheckTestCase extends BaseTestCase
+{
+ public IfEmptyNoEmptyCheckTestCase(final String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ engine.setProperty(RuntimeConstants.CHECK_EMPTY_OBJECTS, "false");
+ }
+
+ protected void assertEmpty(Object obj)
+ {
+ context.put("obj", obj);
+ assertEvalEquals("", "#if( $obj )fail#end");
+ }
+
+ protected void assertNotEmpty(Object obj)
+ {
+ context.put("obj", obj);
+ assertEvalEquals("", "#if( !$obj )fail#end");
+ }
+
+ public void testNull()
+ {
+ assertEmpty(null);
+ assertNotEmpty(new NullAsString());
+ assertNotEmpty(new NullAsNumber());
+ }
+
+ public void testDataStructures()
+ {
+ assertNotEmpty(Collections.emptyMap());
+ assertNotEmpty(Collections.emptyList());
+ assertNotEmpty(new Object[]{});
+ }
+
+ public void testString()
+ {
+ assertNotEmpty("");
+ assertNotEmpty(new EmptyAsString());
+ }
+
+ public void testNumber()
+ {
+ assertNotEmpty(0);
+ }
+
+ public void testStringBuilder()
+ {
+ StringBuilder builder = new StringBuilder();
+ assertNotEmpty(builder);
+ }
+
+ public void testLiterals()
+ {
+ assertEvalEquals("", "#if( !0 )fail#end");
+ assertEvalEquals("", "#if( !0.0 )fail#end");
+ assertEvalEquals("", "#if( !'' )fail#end");
+ assertEvalEquals("", "#if( !\"\" )fail#end");
+ assertEvalEquals("", "#if( ![] )fail#end");
+ assertEvalEquals("", "#if( !{} )fail#end");
+ }
+
+ public static class NullAsString
+ {
+ public String getAsString()
+ {
+ return null;
+ }
+ }
+
+ public static class EmptyAsString
+ {
+ public String getAsString()
+ {
+ return "";
+ }
+ }
+
+ public static class NullAsNumber
+ {
+ public String getAsNumber()
+ {
+ return null;
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyTestCase.java
new file mode 100644
index 00000000..52631a19
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/IfEmptyTestCase.java
@@ -0,0 +1,144 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collections;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Used to check that empty values are properly handled in #if statements
+ */
+public class IfEmptyTestCase extends BaseTestCase
+{
+ public IfEmptyTestCase(final String name)
+ {
+ super(name);
+ }
+
+ protected void assertEmpty(Object obj)
+ {
+ context.put("obj", obj);
+ assertEvalEquals("", "#if( $obj )fail#end");
+ }
+
+ protected void assertNotEmpty(Object obj)
+ {
+ context.put("obj", obj);
+ assertEvalEquals("", "#if( !$obj )fail#end");
+ }
+
+ public void testNull()
+ {
+ assertEmpty(null);
+ assertEmpty(new NullAsString());
+ assertEmpty(new NullAsNumber());
+ }
+
+ public void testDataStructures()
+ {
+ assertEmpty(Collections.emptyMap());
+ assertEmpty(Collections.emptyList());
+ assertEmpty(new Object[]{});
+ List list = new ArrayList();
+ list.add(1);
+ assertNotEmpty(list);
+ Map map = new TreeMap();
+ map.put("foo", 1);
+ assertNotEmpty(map);
+ }
+
+ public void testString()
+ {
+ assertEmpty("");
+ assertEmpty(new EmptyAsString());
+ assertNotEmpty("hello");
+ }
+
+ public void testNumber()
+ {
+ assertEmpty(0);
+ assertEmpty(0L);
+ assertEmpty(0.0f);
+ assertEmpty(0.0);
+ assertEmpty(BigInteger.ZERO);
+ assertEmpty(BigDecimal.ZERO);
+ assertNotEmpty(1);
+ assertNotEmpty(1L);
+ assertNotEmpty(1.0f);
+ assertNotEmpty(1.0);
+ assertNotEmpty(BigInteger.ONE);
+ assertNotEmpty(BigDecimal.ONE);
+ }
+
+ public void testStringBuilder()
+ {
+ StringBuilder builder = new StringBuilder();
+ assertEmpty(builder);
+ builder.append("yo");
+ assertNotEmpty(builder);
+ }
+
+ public void testLiterals()
+ {
+ assertEvalEquals("", "#if( 0 )fail#end");
+ assertEvalEquals("", "#if( 0.0 )fail#end");
+ assertEvalEquals("", "#if( '' )fail#end");
+ assertEvalEquals("", "#if( \"\" )fail#end");
+ assertEvalEquals("", "#if( [] )fail#end");
+ assertEvalEquals("", "#if( {} )fail#end");
+
+ assertEvalEquals("", "#if( !1 )fail#end");
+ assertEvalEquals("", "#if( !1.0 )fail#end");
+ assertEvalEquals("", "#if( !'foo' )fail#end");
+ assertEvalEquals("", "#if( !\"foo\" )fail#end");
+ assertEvalEquals("", "#if( ![ 'foo' ] )fail#end");
+ assertEvalEquals("", "#if( !{ 'foo':'bar' } )fail#end");
+ }
+
+ public static class NullAsString
+ {
+ public String getAsString()
+ {
+ return null;
+ }
+ }
+
+ public static class EmptyAsString
+ {
+ public String getAsString()
+ {
+ return "";
+ }
+ }
+
+ public static class NullAsNumber
+ {
+ public String getAsNumber()
+ {
+ return null;
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/IfNullTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/IfNullTestCase.java
new file mode 100755
index 00000000..f1ce938d
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/IfNullTestCase.java
@@ -0,0 +1,101 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+
+/**
+ * Used to check that nulls are properly handled in #if statements
+ */
+public class IfNullTestCase extends BaseTestCase
+{
+ public IfNullTestCase(final String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("nullToString", new NullToString());
+ context.put("notnull", new Object());
+ }
+
+ public void testIfEquals()
+ {
+ // both null
+ assertEvalEquals("foo", "#if( $null == $otherNull )foo#{else}bar#end");
+ assertEvalEquals("foo", "#if( $null == $nullToString )foo#{else}bar#end");
+ assertEvalEquals("foo", "#if( $nullToString == $null )foo#{else}bar#end");
+ // left null, right not
+ assertEvalEquals("bar", "#if( $nullToString == $notnull )foo#{else}bar#end");
+ assertEvalEquals("bar", "#if( $null == $notnull )foo#{else}bar#end");
+ // right null, left not
+ assertEvalEquals("bar", "#if( $notnull == $nullToString )foo#{else}bar#end");
+ assertEvalEquals("bar", "#if( $notnull == $null )foo#{else}bar#end");
+ }
+
+ public void testIfNotEquals()
+ {
+ // both null
+ assertEvalEquals("bar", "#if( $null != $otherNull )foo#{else}bar#end");
+ assertEvalEquals("bar", "#if( $null != $nullToString )foo#{else}bar#end");
+ assertEvalEquals("bar", "#if( $nullToString != $null )foo#{else}bar#end");
+ // left null, right not
+ assertEvalEquals("foo", "#if( $nullToString != $notnull )foo#{else}bar#end");
+ assertEvalEquals("foo", "#if( $null != $notnull )foo#{else}bar#end");
+ // right null, left not
+ assertEvalEquals("foo", "#if( $notnull != $nullToString )foo#{else}bar#end");
+ assertEvalEquals("foo", "#if( $notnull != $null )foo#{else}bar#end");
+ }
+
+ public void testIfValue()
+ {
+ assertEvalEquals("bar", "#if( $null )foo#{else}bar#end");
+ assertEvalEquals("bar", "#if( $nullToString )foo#{else}bar#end");
+ assertEvalEquals("foo", "#if( !$null )foo#{else}bar#end");
+ assertEvalEquals("foo", "#if( !$nullToString )foo#{else}bar#end");
+ }
+
+ public void testIfAnd()
+ {
+ assertEvalEquals("bar", "#if( $null && $nullToString )foo#{else}bar#end");
+ assertEvalEquals("bar", "#if( $nullToString && $null )foo#{else}bar#end");
+ assertEvalEquals("bar", "#if( $null && $notnull )foo#{else}bar#end");
+ assertEvalEquals("bar", "#if( $notnull && $nullToString )foo#{else}bar#end");
+ }
+
+ public void testIfOr()
+ {
+ assertEvalEquals("bar", "#if( $null || $nullToString )foo#{else}bar#end");
+ assertEvalEquals("bar", "#if( $nullToString || $null )foo#{else}bar#end");
+ assertEvalEquals("foo", "#if( $null || $notnull )foo#{else}bar#end");
+ assertEvalEquals("foo", "#if( $notnull || $nullToString )foo#{else}bar#end");
+ }
+
+ public static class NullToString
+ {
+ public String getAsString()
+ {
+ return null;
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/IncludeErrorTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/IncludeErrorTestCase.java
new file mode 100644
index 00000000..a242d05f
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/IncludeErrorTestCase.java
@@ -0,0 +1,119 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+
+import java.io.StringWriter;
+
+
+
+
+/**
+ * Test that #parse and #include pass errors to calling code.
+ * Specifically checking against VELOCITY-95 and VELOCITY-96.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class IncludeErrorTestCase extends BaseTestCase implements TemplateTestBase
+{
+ VelocityEngine ve;
+
+ /**
+ * Default constructor.
+ */
+ public IncludeErrorTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(IncludeErrorTestCase.class);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ ve = new VelocityEngine();
+ ve.setProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, TemplateTestBase.TEST_COMPARE_DIR + "/includeerror");
+
+ ve.init();
+ }
+
+
+
+ public void testMissingParseError() throws Exception
+ {
+ checkException("missingparse.vm",ResourceNotFoundException.class);
+ }
+
+ public void testMissingIncludeError() throws Exception
+ {
+ checkException("missinginclude.vm",ResourceNotFoundException.class);
+ }
+
+ public void testParseError() throws Exception
+ {
+ checkException("parsemain.vm",ParseErrorException.class);
+ }
+
+ public void testParseError2() throws Exception
+ {
+ checkException("parsemain2.vm",ParseErrorException.class);
+ }
+
+
+ /**
+ * Check that an exception is thrown for the given template
+ * @param templateName
+ * @param exceptionClass
+ * @throws Exception
+ */
+ private void checkException(String templateName,Class exceptionClass)
+ throws Exception
+ {
+ Context context = new VelocityContext();
+ Template template = ve.getTemplate(templateName, "UTF-8");
+
+ try (StringWriter writer = new StringWriter())
+ {
+ template.merge(context, writer);
+ writer.flush();
+ fail("File should have thrown an exception");
+ }
+ catch (Exception E)
+ {
+ assertTrue(exceptionClass.isAssignableFrom(E.getClass()));
+ }
+
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/IncludeEventHandlingTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/IncludeEventHandlingTestCase.java
new file mode 100644
index 00000000..720ffc5c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/IncludeEventHandlingTestCase.java
@@ -0,0 +1,250 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.event.EventCartridge;
+import org.apache.velocity.app.event.IncludeEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.RuntimeServicesAware;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Tests event handling
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class IncludeEventHandlingTestCase extends BaseTestCase implements IncludeEventHandler,RuntimeServicesAware
+{
+
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/includeevent";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/includeevent";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/includeevent/compare";
+
+
+ private static final int PASS_THROUGH=0;
+ private static final int RELATIVE_PATH=1;
+ private static final int BLOCK=2;
+
+ private int EventHandlerBehavior = PASS_THROUGH;
+
+ /**
+ * Default constructor.
+ */
+ public IncludeEventHandlingTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ Velocity.reset();
+ Velocity.addProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+
+
+ }
+
+
+ public static Test suite ()
+ {
+ return new TestSuite(IncludeEventHandlingTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testIncludeEventHandling ()
+ throws Exception
+ {
+ Template template1 = RuntimeSingleton.getTemplate(
+ getFileName(null, "test1", TMPL_FILE_EXT));
+
+ Template template2 = RuntimeSingleton.getTemplate(
+ getFileName(null, "subdir/test2", TMPL_FILE_EXT));
+
+ Template template3 = RuntimeSingleton.getTemplate(
+ getFileName(null, "test3", TMPL_FILE_EXT));
+
+ FileOutputStream fos1 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "test1", RESULT_FILE_EXT));
+
+ FileOutputStream fos2 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "test2", RESULT_FILE_EXT));
+
+ FileOutputStream fos3 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "test3", RESULT_FILE_EXT));
+
+ Writer writer1 = new BufferedWriter(new OutputStreamWriter(fos1));
+ Writer writer2 = new BufferedWriter(new OutputStreamWriter(fos2));
+ Writer writer3 = new BufferedWriter(new OutputStreamWriter(fos3));
+
+ /*
+ * lets make a Context and add the event cartridge
+ */
+
+ Context context = new VelocityContext();
+
+ /*
+ * Now make an event cartridge, register the
+ * input event handler and attach it to the
+ * Context
+ */
+
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(this);
+ ec.attachToContext( context );
+
+
+ // BEHAVIOR A: pass through #input and #parse with no change
+ EventHandlerBehavior = PASS_THROUGH;
+
+ template1.merge(context, writer1);
+ writer1.flush();
+ writer1.close();
+
+ // BEHAVIOR B: pass through #input and #parse with using a relative path
+ EventHandlerBehavior = RELATIVE_PATH;
+
+ template2.merge(context, writer2);
+ writer2.flush();
+ writer2.close();
+
+ // BEHAVIOR C: refuse to pass through #input and #parse
+ EventHandlerBehavior = BLOCK;
+
+ template3.merge(context, writer3);
+ writer3.flush();
+ writer3.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "test1",
+ RESULT_FILE_EXT, CMP_FILE_EXT) ||
+ !isMatch(RESULTS_DIR, COMPARE_DIR, "test2",
+ RESULT_FILE_EXT, CMP_FILE_EXT) ||
+ !isMatch(RESULTS_DIR, COMPARE_DIR, "test3",
+ RESULT_FILE_EXT, CMP_FILE_EXT)
+ )
+ {
+ fail("Output incorrect.");
+ }
+ }
+
+
+ @Override
+ public void setRuntimeServices(RuntimeServices rs )
+ {
+ }
+
+ /**
+ * Sample handler with different behaviors for the different tests.
+ */
+ @Override
+ public String includeEvent(Context context, String includeResourcePath, String currentResourcePath, String directiveName)
+ {
+ if (EventHandlerBehavior == PASS_THROUGH)
+ return includeResourcePath;
+
+
+ // treat as relative path
+ else if (EventHandlerBehavior == RELATIVE_PATH)
+ {
+ // if the resource name starts with a slash, it's not a relative path
+ if (includeResourcePath.startsWith("/") || includeResourcePath.startsWith("\\") ) {
+ return includeResourcePath;
+ }
+
+ int lastslashpos = Math.max(
+ currentResourcePath.lastIndexOf("/"),
+ currentResourcePath.lastIndexOf("\\")
+ );
+
+ // root of resource tree
+ if ( (lastslashpos == -1))
+ return includeResourcePath;
+
+ // prepend path to the input path
+ else
+ return currentResourcePath.substring(0,lastslashpos) + "/" + includeResourcePath;
+
+
+
+ } else if (EventHandlerBehavior == BLOCK)
+ return null;
+
+ // should never happen
+ else
+ return null;
+
+
+ }
+
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/IndexTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/IndexTestCase.java
new file mode 100644
index 00000000..a585de7e
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/IndexTestCase.java
@@ -0,0 +1,171 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeConstants;
+
+import java.util.ArrayList;
+/**
+ * Test index syntax e.g, $foo[1]
+ */
+public class IndexTestCase extends BaseTestCase
+{
+ public IndexTestCase(String name)
+ {
+ super(name);
+ //DEBUG = true;
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE);
+
+ context.put("NULL", null);
+ context.put("red", "blue");
+
+ int[] a = {1, 2, 3};
+ context.put("a", a);
+ String[] str = {"a", "ab", "abc"};
+ context.put("str", str);
+
+ ArrayList alist = new ArrayList();
+ alist.add(1);
+ alist.add(2);
+ alist.add(3);
+ alist.add(a);
+ alist.add(null);
+ context.put("alist", alist);
+
+ Foo foo = new Foo();
+ foo.bar = alist;
+ context.put("foo", foo);
+
+ Boo boo = new Boo();
+ context.put("boo", boo);
+ }
+
+ public void testCallingIndex()
+ {
+ assertEvalEquals("1 -3-", "$a[0] -$a[ 2 ]-");
+ assertEvalEquals("x21", "#set($i = 1)x$a[$i]1");
+ assertEvalEquals("3", "$a[ $a[ $a[0] ] ]");
+ assertEvalEquals("ab", "$str[1]");
+ assertEvalEquals("123", "$alist[0]$alist[1]$alist[2]");
+ assertEvalEquals("1][2-[3]", "$alist[0]][$alist[$a[0]]-[$alist[2]]");
+
+ assertEvalEquals("2", "$alist[3][1]");
+ assertEvalEquals("3 [1]", "$alist[2] [1]");
+
+ assertEvalEquals("4", "#set($bar = [3, 4, 5])$bar[1]");
+ assertEvalEquals("21", "#set($i = 1)#set($bar = [3, $a[$i], $a[0]])$bar[1]$bar[2]");
+
+ assertEvalEquals("2", "$foo.bar[1]");
+ assertEvalEquals("2", "$foo.getBar()[1]");
+ assertEvalEquals("2", "$foo.getBar()[3][1]");
+
+ assertEvalEquals(" a ab abc ", "#foreach($i in $foo.bar[3]) $str[$foreach.index] #end");
+
+ assertEvalEquals("apple", "#set($hash = {'a':'apple', 'b':'orange'})$hash['a']");
+
+ assertEvalEquals("xx ", "#if($alist[4] == $NULL)xx#end #if($alist[4])yy#end");
+
+ assertEvalEquals("BIG TRUEaBIG FALSE", "$foo[true]a$foo[false]");
+ assertEvalEquals("junk foobar ", "$foo[\"junk\"]");
+ assertEvalEquals("GOT NULL", "#set($i=$NULL)$boo[$i]");
+
+ assertEvalEquals("321", "$a[-1]$a[ -2]$a[-3 ]");
+ assertEvalEquals("67xx", "#set($hash={1:11, 5:67, 23:2})$hash[5]$!hash[6]#if(!$hash[1000])xx#end");
+
+ // Some cases that should be evaluated as text
+ assertEvalEquals("[]", "[]");
+ assertEvalEquals("$[]", "$[]");
+ assertEvalEquals("$.[]", "$.[]");
+ assertEvalEquals("$[1]", "$[1]");
+ assertEvalEquals("$[1]1", "$[1]1");
+ assertEvalEquals("$1[1]1", "$1[1]1");
+ assertEvalEquals("blue.[]", "$red.[]");
+ assertEvalEquals("blue[]", "${red}[]");
+ assertEvalEquals("blue][", "$red][");
+ assertEvalEquals("1[]", "${a[0]}[]");
+ assertEvalEquals("1a$[]", "$a[0]a$[]");
+ assertEvalEquals("$![]", "$![]");
+ assertEvalEquals("$\\![]", "$\\![]");
+ }
+
+ public void testIndexSetting()
+ {
+ assertEvalEquals("foo", "#set($str[1] = \"foo\")$str[1]");
+ assertEvalEquals("5150", "#set($alist[1] = 5150)$alist[1]");
+ assertEvalEquals("15", "$alist[3][0]#set($alist[3][0] = 5)$alist[3][0]");
+ assertEvalEquals("orange","#set($blaa = {\"apple\":\"grape\"})#set($blaa[\"apple\"] = \"orange\")$blaa[\"apple\"]");
+ assertEvalEquals("null","#set($str[0] = $NULL)#if($str[0] == $NULL)null#end");
+ assertEvalEquals("null","#set($blaa = {\"apple\":\"grape\"})#set($blaa[\"apple\"] = $NULL)#if($blaa[\"apple\"] == $NULL)null#end");
+ assertEvalEquals("2112", "#set($a[-1] = 2112)$a[2]");
+ assertEvalEquals("3344","#set($hash = {1:11, 2:22, 5:66})#set($hash[2]=33)#set($hash[3]=44)$hash[2]$hash[3]");
+ }
+
+
+ public void testErrorHandling()
+ {
+ assertEvalExceptionAt("$boo['throwex']", 1, 5);
+ assertEvalExceptionAt("$boo[]", 1, 6);
+ assertEvalExceptionAt("$boo[blaa]", 1, 6);
+ assertEvalExceptionAt("#set($foo[1] = 3)", 1, 10);
+ assertEvalExceptionAt("$a[500]", 1, 3);
+ }
+
+
+ public static class Foo
+ {
+ public Object bar = null;
+ public Object getBar()
+ {
+ return bar;
+ }
+
+ public String get(Boolean bool)
+ {
+ if (bool)
+ return "BIG TRUE";
+ else
+ return "BIG FALSE";
+ }
+
+ public String get(String str)
+ {
+ return str + " foobar ";
+ }
+ }
+
+ public static class Boo
+ {
+ public Object get(Object obj)
+ {
+ if (obj == null)
+ return "GOT NULL";
+ else if (obj.equals("throwex"))
+ throw new RuntimeException("Generated Exception");
+
+ return obj;
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/InfoTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/InfoTestCase.java
new file mode 100644
index 00000000..0ca5a150
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/InfoTestCase.java
@@ -0,0 +1,118 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.test.misc.UberspectTestException;
+import org.apache.velocity.util.introspection.Info;
+
+import java.io.StringWriter;
+
+
+
+
+/**
+ * Test that the Info class in the Introspector holds the correct information.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @author <a href="mailto:isidore@setgame.com">Llewellyn Falco</a>
+ * @version $Id$
+ */
+public class InfoTestCase extends BaseTestCase implements TemplateTestBase
+{
+ VelocityEngine ve;
+
+ /**
+ * Default constructor.
+ */
+ public InfoTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(InfoTestCase.class);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ ve = new VelocityEngine();
+ ve.setProperty(
+ "runtime.introspector.uberspect", "org.apache.velocity.test.misc.UberspectTestImpl");
+
+ ve.setProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, TemplateTestBase.TEST_COMPARE_DIR + "/info");
+
+ ve.init();
+ }
+
+
+
+ public void testInfoProperty() throws Exception
+ {
+ // check property
+ checkInfo("info1.vm", 1, 7);
+ }
+
+ public void testInfoMethod() throws Exception
+ {
+ // check method
+ checkInfo("info2.vm", 1, 7);
+ }
+
+ public void checkInfo(String templateName,
+ int expectedLine, int expectedCol) throws Exception
+ {
+ Context context = new VelocityContext();
+ Template template = ve.getTemplate(templateName, "UTF-8");
+ Info info = null;
+
+ context.put("main", this);
+
+ try (StringWriter writer = new StringWriter())
+ {
+ template.merge(context, writer);
+ writer.flush();
+ fail("Uberspect should have thrown an exception");
+ }
+ catch (UberspectTestException E)
+ {
+ info = E.getInfo();
+ }
+ assertInfoEqual(info, templateName, expectedLine, expectedCol);
+
+ }
+
+ private void assertInfoEqual(Info i, String name, int line, int column)
+ {
+ assertEquals("Template Name", name, i.getTemplateName());
+ assertEquals("Template Line", line, i.getLine());
+ assertEquals("Template Column", column, i.getColumn());
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/InlineScopeVMTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/InlineScopeVMTestCase.java
new file mode 100644
index 00000000..acaafba9
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/InlineScopeVMTestCase.java
@@ -0,0 +1,134 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Tests if the VM template-locality is working.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:dlr@collab.net">Daniel Rall</a>
+ * @version $Id$
+ */
+public class InlineScopeVMTestCase extends BaseTestCase implements TemplateTestBase
+{
+ public InlineScopeVMTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ /*
+ * do our properties locally, and just override the ones we want
+ * changed
+ */
+
+ Velocity.reset();
+
+ Velocity.setProperty(
+ Velocity.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL, "true");
+
+ Velocity.setProperty(
+ Velocity.VM_PERM_INLINE_LOCAL, "true");
+
+ Velocity.setProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.setProperty("space.gobbling", "bc");
+
+ Velocity.init();
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(InlineScopeVMTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testInlineScopeVM ()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ /*
+ * Get the template and the output. Do them backwards.
+ * vm_test2 uses a local VM and vm_test1 doesn't
+ */
+
+ Template template2 = RuntimeSingleton.getTemplate(
+ getFileName(null, "vm_test2", TMPL_FILE_EXT));
+
+ Template template1 = RuntimeSingleton.getTemplate(
+ getFileName(null, "vm_test1", TMPL_FILE_EXT));
+
+ FileOutputStream fos1 =
+ new FileOutputStream (
+ getFileName(RESULT_DIR, "vm_test1", RESULT_FILE_EXT));
+
+ FileOutputStream fos2 =
+ new FileOutputStream (
+ getFileName(RESULT_DIR, "vm_test2", RESULT_FILE_EXT));
+
+ Writer writer1 = new BufferedWriter(new OutputStreamWriter(fos1));
+ Writer writer2 = new BufferedWriter(new OutputStreamWriter(fos2));
+
+ /*
+ * put the Vector into the context, and merge both
+ */
+
+ VelocityContext context = new VelocityContext();
+
+ template1.merge(context, writer1);
+ writer1.flush();
+ writer1.close();
+
+ template2.merge(context, writer2);
+ writer2.flush();
+ writer2.close();
+
+ if (!isMatch(RESULT_DIR,COMPARE_DIR,"vm_test1",
+ RESULT_FILE_EXT,CMP_FILE_EXT) ||
+ !isMatch(RESULT_DIR,COMPARE_DIR,"vm_test2",
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/IntrospectionCacheDataTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/IntrospectionCacheDataTestCase.java
new file mode 100644
index 00000000..bbd4bfab
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/IntrospectionCacheDataTestCase.java
@@ -0,0 +1,81 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.util.introspection.IntrospectionCacheData;
+
+import java.io.IOException;
+import java.io.StringWriter;
+
+/**
+ * Checks that arrays are cached correctly in the Introspector.
+ *
+ * @author <a href="Alexey Pachenko">alex+news@olmisoft.com</a>
+ * @version $Id$
+ */
+public class IntrospectionCacheDataTestCase extends TestCase
+{
+
+ private static class CacheHitCountingVelocityContext extends VelocityContext
+ {
+ public int cacheHit = 0;
+
+ @Override
+ public IntrospectionCacheData icacheGet(Object key)
+ {
+ final IntrospectionCacheData result = super.icacheGet(key);
+ if (result != null) {
+ ++cacheHit;
+ }
+ return result;
+ }
+
+ }
+
+ public void testCache() throws ParseErrorException, MethodInvocationException,
+ ResourceNotFoundException, IOException
+ {
+ CacheHitCountingVelocityContext context = new CacheHitCountingVelocityContext();
+ context.put("this", this);
+ StringWriter w = new StringWriter();
+ Velocity.evaluate(context, w, "test", "$this.exec('a')$this.exec('b')");
+ assertEquals("[a][b]", w.toString());
+ assertTrue(context.cacheHit > 0);
+ }
+
+
+ /**
+ * For use when acting as a context reference.
+ *
+ * @param value
+ * @return
+ */
+ public String exec(String value)
+ {
+ return "[" + value + "]";
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector2TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector2TestCase.java
new file mode 100644
index 00000000..f97f1c49
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector2TestCase.java
@@ -0,0 +1,168 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.introspection.Introspector;
+
+import java.lang.reflect.Method;
+
+/**
+ * Test case for the Velocity Introspector which
+ * tests the ability to find a 'best match'
+ *
+ *
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class Introspector2TestCase extends BaseTestCase
+{
+
+ /**
+ * Creates a new instance.
+ */
+ public Introspector2TestCase(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Get the containing <code>TestSuite</code>.
+ *
+ * @return The <code>TestSuite</code> to run.
+ */
+ public static Test suite ()
+ {
+ return new TestSuite(Introspector2TestCase.class);
+ }
+
+ public void testIntrospector()
+ throws Exception
+ {
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+
+ Method method;
+ String result;
+ Tester t = new Tester();
+
+ Object[] params = { new Foo(), new Foo() };
+
+ Introspector introspector = new Introspector(log);
+
+ method = introspector
+ .getMethod( Tester.class, "find", params );
+
+ if ( method == null)
+ fail("Returned method was null");
+
+ result = (String) method.invoke( t, params);
+
+ if ( !result.equals( "Bar-Bar" ) )
+ {
+ fail("Should have gotten 'Bar-Bar' : received '" + result + "'");
+ }
+
+ /*
+ * now test for failure due to ambiguity
+ */
+
+ method = introspector
+ .getMethod( Tester2.class, "find", params );
+
+ if ( method != null)
+ fail("Introspector shouldn't have found a method as it's ambiguous.");
+ }
+
+ public interface Woogie
+ {
+ }
+
+ public static class Bar implements Woogie
+ {
+ int i;
+ }
+
+ public static class Foo extends Bar
+ {
+ int j;
+ }
+
+ public static class Tester
+ {
+ public static String find(Woogie w, Object o )
+ {
+ return "Woogie-Object";
+ }
+
+ public static String find(Object w, Bar o )
+ {
+ return "Object-Bar";
+ }
+
+ public static String find(Bar w, Bar o )
+ {
+ return "Bar-Bar";
+ }
+
+ public static String find( Object o )
+ {
+ return "Object";
+ }
+
+ public static String find( Woogie o )
+ {
+ return "Woogie";
+ }
+ }
+
+ public static class Tester2
+ {
+ public static String find(Woogie w, Object o )
+ {
+ return "Woogie-Object";
+ }
+
+ public static String find(Object w, Bar o )
+ {
+ return "Object-Bar";
+ }
+
+ public static String find(Bar w, Object o )
+ {
+ return "Bar-Object";
+ }
+
+ public static String find( Object o )
+ {
+ return "Object";
+ }
+
+ public static String find( Woogie o )
+ {
+ return "Woogie";
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector3TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector3TestCase.java
new file mode 100644
index 00000000..aef11f2c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector3TestCase.java
@@ -0,0 +1,151 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.util.introspection.Introspector;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Simple introspector test case for primitive problem found in 1.3
+ *
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class Introspector3TestCase extends BaseTestCase
+{
+ /**
+ * Creates a new instance.
+ */
+ public Introspector3TestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(Introspector3TestCase.class);
+ }
+
+ public void testSimple()
+ throws Exception
+ {
+ Method method;
+ String result;
+
+ MethodProvider mp = new MethodProvider();
+
+ /*
+ * string integer
+ */
+
+ Object[] listIntInt = { new ArrayList(), 1, 2};
+ Object[] listLongList = { new ArrayList(), 1L, new ArrayList() };
+ Object[] intInt = {1, 2};
+ Object[] longInt = {1L, 2};
+ Object[] longLong = {1L, 2L};
+
+ Introspector introspector = new Introspector(log);
+ method = introspector.getMethod(
+ MethodProvider.class, "lii", listIntInt);
+ result = (String) method.invoke(mp, listIntInt);
+
+ assertTrue(result.equals("lii"));
+
+ method = introspector.getMethod(
+ MethodProvider.class, "ii", intInt);
+ result = (String) method.invoke(mp, intInt);
+
+ assertTrue(result.equals("ii"));
+
+ method = introspector.getMethod(
+ MethodProvider.class, "ll", longInt);
+ result = (String) method.invoke(mp, longInt);
+
+ assertTrue(result.equals("ll"));
+
+ /*
+ * test overloading with primitives
+ */
+
+ method = introspector.getMethod(
+ MethodProvider.class, "ll", longLong);
+ result = (String) method.invoke(mp, longLong);
+
+ assertTrue(result.equals("ll"));
+
+ method = introspector.getMethod(
+ MethodProvider.class, "lll", listLongList);
+ result = (String) method.invoke(mp, listLongList);
+
+ assertTrue(result.equals("lll"));
+
+ /*
+ * test invocation with nulls
+ */
+
+ Object [] oa = {null, 0};
+ method = introspector.getMethod(
+ MethodProvider.class, "lll", oa );
+ result = (String) method.invoke(mp, oa);
+
+ assertTrue(result.equals("Listl"));
+
+ }
+
+ public static class MethodProvider
+ {
+ public String ii(int p, int d)
+ {
+ return "ii";
+ }
+
+ public String lii(List s, int p, int d)
+ {
+ return "lii";
+ }
+
+ public String lll(List s, long p, List d)
+ {
+ return "lll";
+ }
+
+
+ public String lll(List s, long p, int d)
+ {
+ return "lli";
+ }
+
+ public String lll(List s, long p)
+ {
+ return "Listl";
+ }
+
+ public String ll(long p, long d)
+ {
+ return "ll";
+ }
+
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/IntrospectorTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/IntrospectorTestCase.java
new file mode 100644
index 00000000..ec2fec2b
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/IntrospectorTestCase.java
@@ -0,0 +1,226 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.introspection.Introspector;
+
+import java.lang.reflect.Method;
+
+/**
+ * Test case for the Velocity Introspector which uses
+ * the Java Reflection API to determine the correct
+ * signature of the methods used in VTL templates.
+ *
+ * This should be split into separate tests for each
+ * of the methods searched for but this is a start
+ * for now.
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @version $Id$
+ */
+public class IntrospectorTestCase extends BaseTestCase
+{
+ private static MethodProvider mp;
+
+ private Introspector introspector;
+
+ @Override
+ public void setUp()
+ {
+ mp = new MethodProvider();
+ log = new TestLogger();
+ introspector = new Introspector(log);
+ }
+
+ /**
+ * Creates a new instance.
+ */
+ public IntrospectorTestCase (String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Get the containing <code>TestSuite</code>. This is always
+ * <code>VelocityTestSuite</code>.
+ *
+ * @return The <code>TestSuite</code> to run.
+ */
+ public static Test suite ()
+ {
+ return new TestSuite(IntrospectorTestCase.class);
+ }
+
+ public void testIntrospectorBoolean()
+ throws Exception
+ {
+ // Test boolean primitive.
+ Object[] booleanParams = {Boolean.TRUE};
+ String type = "boolean";
+ Method method = introspector.getMethod(
+ MethodProvider.class, type + "Method", booleanParams);
+ String result = (String) method.invoke(mp, booleanParams);
+
+ assertEquals("Method could not be found", type, result);
+ }
+
+ public void testIntrospectorByte()
+ throws Exception
+ {
+ // Test byte primitive.
+ Object[] byteParams = { new Byte("1") };
+ String type = "byte";
+ Method method = introspector.getMethod(
+ MethodProvider.class, type + "Method", byteParams);
+ String result = (String) method.invoke(mp, byteParams);
+
+ assertEquals("Method could not be found", type, result);
+ }
+
+ public void testIntrospectorChar()
+ throws Exception
+ {
+ // Test char primitive.
+ Object[] characterParams = {'a'};
+ String type = "character";
+ Method method = introspector.getMethod(
+ MethodProvider.class, type + "Method", characterParams);
+ String result = (String) method.invoke(mp, characterParams);
+
+ assertEquals("Method could not be found", type, result);
+ }
+
+ public void testIntrospectorDouble()
+ throws Exception
+ {
+
+ // Test double primitive.
+ Object[] doubleParams = { 1.0 };
+ String type = "double";
+ Method method = introspector.getMethod(
+ MethodProvider.class, type + "Method", doubleParams);
+ String result = (String) method.invoke(mp, doubleParams);
+
+ assertEquals("Method could not be found", type, result);
+ }
+
+ public void testIntrospectorFloat()
+ throws Exception
+ {
+
+ // Test float primitive.
+ Object[] floatParams = { 1.0f };
+ String type = "float";
+ Method method = introspector.getMethod(
+ MethodProvider.class, type + "Method", floatParams);
+ String result = (String) method.invoke(mp, floatParams);
+
+ assertEquals("Method could not be found", type, result);
+ }
+
+ public void testIntrospectorInteger()
+ throws Exception
+ {
+
+ // Test integer primitive.
+ Object[] integerParams = { 1 };
+ String type = "integer";
+ Method method = introspector.getMethod(
+ MethodProvider.class, type + "Method", integerParams);
+ String result = (String) method.invoke(mp, integerParams);
+
+ assertEquals("Method could not be found", type, result);
+ }
+
+ public void testIntrospectorPrimitiveLong()
+ throws Exception
+ {
+
+ // Test long primitive.
+ Object[] longParams = { 1L };
+ String type = "long";
+ Method method = introspector.getMethod(
+ MethodProvider.class, type + "Method", longParams);
+ String result = (String) method.invoke(mp, longParams);
+
+ assertEquals("Method could not be found", type, result);
+ }
+
+ public void testIntrospectorPrimitiveShort()
+ throws Exception
+ {
+ // Test short primitive.
+ Object[] shortParams = {(short) 1};
+ String type = "short";
+ Method method = introspector.getMethod(
+ MethodProvider.class, type + "Method", shortParams);
+ String result = (String) method.invoke(mp, shortParams);
+
+ assertEquals("Method could not be found", type, result);
+ }
+
+ public void testIntrospectorUntouchable()
+ throws Exception
+ {
+ // Test untouchable
+
+ Object[] params = {};
+
+ Method method = introspector.getMethod(
+ MethodProvider.class, "untouchable", params);
+
+ assertNull("able to access a private-access method.", method);
+ }
+
+ public void testIntrospectorReallyUntouchable()
+ throws Exception
+ {
+ // Test really untouchable
+ Object[] params = {};
+
+ Method method = introspector.getMethod(
+ MethodProvider.class, "reallyuntouchable", params);
+
+ assertNull("able to access a private-access method.", method);
+ }
+
+ public static class MethodProvider
+ {
+ /*
+ * Methods with native parameter types.
+ */
+ public String booleanMethod (boolean p) { return "boolean"; }
+ public String byteMethod (byte p) { return "byte"; }
+ public String characterMethod (char p) { return "character"; }
+ public String doubleMethod (double p) { return "double"; }
+ public String floatMethod (float p) { return "float"; }
+ public String integerMethod (int p) { return "integer"; }
+ public String longMethod (long p) { return "long"; }
+ public String shortMethod (short p) { return "short"; }
+
+ String untouchable() { return "yech";}
+ // don't remove! Used through introspection for testing!
+ private String reallyuntouchable() { return "yech!"; }
+
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/InvalidEventHandlerTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/InvalidEventHandlerTestCase.java
new file mode 100644
index 00000000..48ba8e61
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/InvalidEventHandlerTestCase.java
@@ -0,0 +1,646 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.app.event.EventCartridge;
+import org.apache.velocity.app.event.InvalidReferenceEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.util.RuntimeServicesAware;
+import org.apache.velocity.util.introspection.Info;
+
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Tests event handling for all event handlers except IncludeEventHandler. This is tested
+ * separately due to its complexity.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class InvalidEventHandlerTestCase
+extends TestCase
+{
+ // @@ VELOCITY-553
+ public class TestObject {
+ private String nullValueAttribute = null;
+
+ public String getNullValueAttribute() {
+ return nullValueAttribute;
+ }
+
+ public String getRealString() {
+ return new String("helloFooRealStr");
+ }
+
+ public String getString() {
+ return new String("helloFoo");
+ }
+
+ public String getNullString() {
+ return null;
+ }
+
+ public java.util.Date getNullDate() {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("TestObject [nullValueAttribute=");
+ builder.append(nullValueAttribute);
+ builder.append("]");
+ return builder.toString();
+ }
+ }
+ // @@ VELOCITY-553
+
+
+ /**
+ * Default constructor.
+ */
+ public InvalidEventHandlerTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(InvalidEventHandlerTestCase.class);
+ }
+
+ public void testManualEventHandlers()
+ throws Exception
+ {
+ TestEventCartridge te = new TestEventCartridge();
+
+ /*
+ * Test attaching the event cartridge to the context
+ */
+ VelocityEngine ve = new VelocityEngine();
+ ve.init();
+
+ /*
+ * lets make a Context and add the event cartridge
+ */
+
+ VelocityContext inner = new VelocityContext();
+
+ /*
+ * Now make an event cartridge, register all the
+ * event handlers (at once) and attach it to the
+ * Context
+ */
+
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(te);
+ ec.attachToContext( inner );
+
+ doTestInvalidReferenceEventHandler0(ve, inner);
+ doTestInvalidReferenceEventHandler1(ve, inner);
+ doTestInvalidReferenceEventHandler2(ve, inner);
+ doTestInvalidReferenceEventHandler3(ve, inner);
+ doTestInvalidReferenceEventHandler4(ve, inner);
+ }
+
+ /**
+ * Test assigning the event handlers via properties
+ */
+ public void testConfigurationEventHandlers()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_INVALIDREFERENCES, TestEventCartridge.class.getName());
+
+ ve.init();
+ doTestInvalidReferenceEventHandler0(ve, null);
+ doTestInvalidReferenceEventHandler1(ve, null);
+ doTestInvalidReferenceEventHandler2(ve, null);
+ doTestInvalidReferenceEventHandler3(ve, null);
+ doTestInvalidReferenceEventHandler4(ve, null);
+ }
+
+ /**
+ * Test deeper structures
+ * @param ve
+ * @param vc
+ * @throws Exception
+ */
+ private void doTestInvalidReferenceEventHandler4(VelocityEngine ve, VelocityContext vc)
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext(vc);
+
+ Tree test = new Tree();
+ test.setField("10");
+ Tree test2 = new Tree();
+ test2.setField("12");
+ test.setChild(test2);
+
+ context.put("tree",test);
+ String s;
+ Writer w;
+
+ // show work fine
+ s = "$tree.Field $tree.field $tree.child.Field";
+ w = new StringWriter();
+ ve.evaluate(context, w, "mystring", s);
+
+ s = "$tree.x $tree.field.x $tree.child.y $tree.child.Field.y";
+ w = new StringWriter();
+ ve.evaluate(context, w, "mystring", s);
+
+ }
+
+ /**
+ * Test invalid #set
+ * @param ve
+ * @param vc
+ * @throws Exception
+ */
+ private void doTestInvalidReferenceEventHandler3(VelocityEngine ve, VelocityContext vc)
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext(vc);
+ context.put("a1", 5);
+ context.put("a4", 5);
+ context.put("b1","abc");
+
+ String s;
+ Writer w;
+
+ // good object, bad right hand side
+ s = "#set($xx = $a1.afternoon())";
+ w = new StringWriter();
+ try {
+ ve.evaluate( context, w, "mystring", s );
+ fail("Expected exception.");
+ } catch (RuntimeException e) {}
+
+ // good object, bad right hand reference
+ s = "#set($yy = $q1)";
+ w = new StringWriter();
+ try {
+ ve.evaluate( context, w, "mystring", s );
+ fail("Expected exception.");
+ } catch (RuntimeException e) {}
+
+ }
+
+ /**
+ * Test invalid method calls
+ * @param ve
+ * @param vc
+ * @throws Exception
+ */
+ private void doTestInvalidReferenceEventHandler2(VelocityEngine ve, VelocityContext vc)
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext(vc);
+ context.put("a1", 5);
+ context.put("a4", 5);
+ context.put("b1","abc");
+
+ String s;
+ Writer w;
+
+ // good object, bad method
+ s = "$a1.afternoon()";
+ w = new StringWriter();
+ try {
+ ve.evaluate( context, w, "mystring", s );
+ fail("Expected exception.");
+ } catch (RuntimeException e) {}
+
+ // good object, bad method, quiet reference
+ s = "$!a1.afternoon()";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+ assertEquals("", w.toString());
+
+ // bad object, bad method -- fails on get
+ s = "$zz.daylight()";
+ w = new StringWriter();
+ try {
+ ve.evaluate( context, w, "mystring", s );
+ fail("Expected exception.");
+ } catch (RuntimeException e) {}
+
+ // bad object, bad method, quiet reference
+ s = "$!zz.daylight()";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+ assertEquals("", w.toString());
+
+ // change result
+ s = "$b1.baby()";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+ assertEquals("www",w.toString());
+ }
+
+ /**
+ * Test invalid gets/references
+ * @param ve
+ * @param vc
+ * @throws Exception
+ */
+ private void doTestInvalidReferenceEventHandler1(VelocityEngine ve, VelocityContext vc)
+ throws Exception
+ {
+ String result;
+
+ VelocityContext context = new VelocityContext(vc);
+ context.put("a1", 5);
+ context.put("a4", 5);
+ context.put("b1","abc");
+
+ // normal - should be no calls to handler
+ String s = "$a1 $a1.intValue() $b1 $b1.length() #set($c1 = '5')";
+ Writer w = new StringWriter();
+ ve.evaluate(context, w, "mystring", s);
+
+ // good object, bad property
+ s = "$a1.foobar";
+ w = new StringWriter();
+ try {
+ ve.evaluate( context, w, "mystring", s );
+ fail("Expected exception.");
+ } catch (RuntimeException e) {}
+
+ // same one as a quiet reference should not fail
+ s = "$!a1.foobar";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+ assertEquals("",w.toString());
+
+ // same one inside an #if statement should not fail
+ s = "#if($a1.foobar)yes#{else}no#end";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+ assertEquals("no",w.toString());
+
+
+ // bad object, bad property
+ s = "$a2.foobar";
+ w = new StringWriter();
+ try {
+ ve.evaluate( context, w, "mystring", s );
+ fail("Expected exception.");
+ } catch (RuntimeException e) {}
+
+ // same one as a quiet reference should not fail
+ s = "$!a2.foobar";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+ assertEquals("",w.toString());
+
+ // same one inside an #if statement should still fail
+ s = "#if($a2.foobar)yes#{else}no#end";
+ w = new StringWriter();
+ try {
+ ve.evaluate( context, w, "mystring", s );
+ fail("Expected exception.");
+ } catch (RuntimeException e) {}
+
+ // except if object is tested first
+ s = "#if($a2 and $a2.foobar)yes#{else}no#end";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+ assertEquals("no", w.toString());
+
+ // bad object, no property
+ s = "$a3";
+ w = new StringWriter();
+ try {
+ ve.evaluate( context, w, "mystring", s );
+ fail("Expected exception.");
+ } catch (RuntimeException e) {}
+
+ // bad object, no property as quiet reference should not fail
+ s = "$!a3";
+ w = new StringWriter();
+ ve.evaluate(context, w, "mystring", s);
+ result = w.toString();
+ assertEquals("", result);
+
+ // bad object, no property as #if condition should not fail
+ s = "#if($a3)yes#{else}no#end";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+ result = w.toString();
+ assertEquals("no", result);
+
+ // good object, bad property; change the value
+ s = "$a4.foobar";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+ result = w.toString();
+ assertEquals("zzz", result);
+
+ }
+
+ /**
+ * Test invalidGetMethod
+ *
+ * Test behaviour (which should be the same) of
+ * $objRef.myAttribute and $objRef.getMyAttribute()
+ *
+ * @param ve
+ * @param vc
+ * @throws Exception
+ */
+ private void doTestInvalidReferenceEventHandler0(VelocityEngine ve, VelocityContext vc)
+ throws Exception
+ {
+ String result;
+ Writer w;
+ String s;
+ boolean rc;
+
+ VelocityContext context = new VelocityContext(vc);
+ context.put("propertyAccess", new String("lorem ipsum"));
+ context.put("objRef", new TestObject());
+ java.util.ArrayList arrayList = new java.util.ArrayList();
+ arrayList.add("firstOne");
+ arrayList.add(null);
+ java.util.HashMap hashMap = new java.util.HashMap();
+ hashMap.put(41, "41 is not 42");
+
+ context.put("objRefArrayList", arrayList);
+ context.put("objRefHashMap", hashMap);
+
+ // good object, good property (returns non null value)
+ s = "#set($resultVar = $propertyAccess.bytes)"; // -> getBytes()
+ w = new StringWriter();
+ rc = ve.evaluate( context, w, "mystring", s );
+
+ // good object, good property accessor method (returns non null value)
+ s = "#set($resultVar = $propertyAccess.getBytes())"; // -> getBytes()
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+
+ // good object, good property (returns non null value)
+ s = "$objRef.getRealString()";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+
+ // good object, good property accessor method (returns null value)
+ // No exception shall be thrown, as returning null should be valid
+ s = "$objRef.getNullValueAttribute()";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+
+ // good object, good property (returns null value)
+ // No exception shall be thrown, as returning null should be valid
+ s = "$objRef.nullValueAttribute"; // -> getNullValueAttribute()
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+
+ // good object, good accessor method which returns a non-null object reference
+ // Test removing a hashmap element which exists
+ s = "$objRefHashMap.remove(41)";
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+
+
+ // good object, good accessor method which returns null
+ // Test removing a hashmap element which DOES NOT exist
+ // Expected behaviour: Returning null as a value should be
+ // OK and not result in an exception
+ s = "$objRefHashMap.remove(42)"; // will return null, as the key does not exist
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+
+ // good object, good method invocation (returns non-null object reference)
+ s = "$objRefArrayList.get(0)"; // element 0 is NOT NULL
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+
+
+ // good object, good method invocation (returns null value)
+ // Expected behaviour: Returning null as a value should be
+ // OK and not result in an exception
+ s = "$objRefArrayList.get(1)"; // element 1 is null
+ w = new StringWriter();
+ ve.evaluate( context, w, "mystring", s );
+
+ }
+
+
+
+ /**
+ * Test assigning the event handlers via properties
+ */
+
+ public static class TestEventCartridge
+ implements InvalidReferenceEventHandler,
+ RuntimeServicesAware
+ {
+ private RuntimeServices rs;
+
+ public TestEventCartridge()
+ {
+ }
+
+ /**
+ * Required by EventHandler
+ */
+ @Override
+ public void setRuntimeServices(RuntimeServices rs )
+ {
+ // make sure this is only called once
+ if (this.rs == null)
+ this.rs = rs;
+
+ else
+ fail("initialize called more than once.");
+ }
+
+
+ @Override
+ public Object invalidGetMethod(Context context, String reference, Object object, String property, Info info)
+ {
+ // as a test, make sure this EventHandler is initialized
+ if (rs == null)
+ fail ("Event handler not initialized!");
+
+ switch (reference)
+ {
+ // good object, bad property
+ case "$a1.foobar":
+ assertEquals(Integer.valueOf(5), object);
+ assertEquals("foobar", property);
+ throw new RuntimeException("expected exception");
+
+ // bad object, bad property
+ case "$a2":
+ assertNull(object);
+ assertNull(property);
+ throw new RuntimeException("expected exception");
+
+
+ // bad object, no property
+ case "$a3":
+ assertNull(object);
+ assertNull(property);
+ throw new RuntimeException("expected exception");
+
+
+ // good object, bad property; change the value
+ case "$a4.foobar":
+ assertEquals(Integer.valueOf(5), object);
+ assertEquals("foobar", property);
+ return "zzz";
+
+
+ // bad object, bad method -- fail on the object
+ case "$zz":
+ assertNull(object);
+ assertNull(property);
+ throw new RuntimeException("expected exception");
+
+
+ // pass q1 through
+ case "$q1":
+
+ break;
+ case "$tree.x":
+ assertEquals("x", property);
+ break;
+ case "$tree.field.x":
+ assertEquals("x", property);
+ break;
+ case "$tree.child.y":
+ assertEquals("y", property);
+ break;
+ case "$tree.child.Field.y":
+ assertEquals("y", property);
+ break;
+ default:
+ fail("invalidGetMethod: unexpected reference: " + reference);
+ break;
+ }
+ return null;
+ }
+
+ @Override
+ public Object invalidMethod(Context context, String reference, Object object, String method, Info info)
+ {
+ // as a test, make sure this EventHandler is initialized
+ if (rs == null)
+ fail ("Event handler not initialized!");
+
+ // good reference, bad method
+ if (object.getClass().equals(Integer.class))
+ {
+ assertEquals("$a1.afternoon()",reference);
+ assertEquals("afternoon",method);
+ throw new RuntimeException("expected exception");
+ }
+
+ else if (object.getClass().equals(String.class) && "baby".equals(method))
+ {
+ return "www";
+ }
+
+ else
+ {
+ fail("Unexpected invalid method. " + method);
+ }
+
+ return null;
+ }
+
+
+ @Override
+ public boolean invalidSetMethod(Context context, String leftreference, String rightreference, Info info)
+ {
+
+ // as a test, make sure this EventHandler is initialized
+ if (rs == null)
+ fail ("Event handler not initialized!");
+
+ // good object, bad method
+ if (leftreference.equals("xx"))
+ {
+ assertEquals("q1.afternoon()",rightreference);
+ throw new RuntimeException("expected exception");
+ }
+ if (leftreference.equals("yy"))
+ {
+ assertEquals("$q1",rightreference);
+ throw new RuntimeException("expected exception");
+ }
+ else
+ {
+ fail("Unexpected left hand side. " + leftreference);
+ }
+
+ return false;
+ }
+
+ }
+
+ public static class Tree
+ {
+ String field;
+ Tree child;
+
+ public Tree()
+ {
+
+ }
+
+ public String getField()
+ {
+ return field;
+ }
+
+ public void setField(String field)
+ {
+ this.field = field;
+ }
+
+ public Tree getChild()
+ {
+ return child;
+ }
+
+ public void setChild(Tree child)
+ {
+ this.child = child;
+ }
+
+ public String testMethod()
+ {
+ return "123";
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroAutoReloadTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroAutoReloadTestCase.java
new file mode 100644
index 00000000..d8f12dde
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroAutoReloadTestCase.java
@@ -0,0 +1,103 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+
+/**
+ * This class tests the velocimacro.library.autoreload functionality, and issue VELOCITY-
+ */
+public class MacroAutoReloadTestCase extends BaseTestCase
+{
+ private final String RELOAD_TEMPLATE_PATH = TEST_COMPARE_DIR + "/reload";
+
+ public MacroAutoReloadTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ // always copy macros library before modifying it, to ensure successive tests will pass
+ Files.copy(FileSystems.getDefault().getPath(RELOAD_TEMPLATE_PATH + "/macros.vtl"), FileSystems.getDefault().getPath(RELOAD_TEMPLATE_PATH + "/macros2.vtl"), StandardCopyOption.REPLACE_EXISTING);
+
+ engine = new VelocityEngine();
+
+ //by default, make the engine's log output go to the test-report
+ log = new TestLogger(false, false);
+ engine.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, log);
+
+ // use file resource loader
+ engine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file,string");
+ engine.addProperty("file.resource.loader.path", RELOAD_TEMPLATE_PATH);
+ engine.addProperty("velocimacro.library", "macros2.vtl");
+ engine.addProperty("velocimacro.library.autoreload", "true");
+ engine.addProperty("file.resource.loader.cache", "false");
+ engine.addProperty("string.resource.loader.class", StringResourceLoader.class.getName());
+ engine.addProperty("string.resource.loader.repository.name", "stringRepo");
+ engine.addProperty("string.resource.loader.repository.static", "false");
+ context = new VelocityContext();
+ }
+
+
+ public void testChangedMacro() throws Exception
+ {
+ String template = "#foo('hip')";
+ assertEvalEquals("hop_hip", template);
+
+ FileWriter writer = new FileWriter(RELOAD_TEMPLATE_PATH + "/macros2.vtl");
+ writer.write("#macro(foo $txt)hip_$txt#{end}");
+ writer.close();
+ // last modified timestamps resolution is one second before Java 8,
+ // so force reloading by setting a file date in the future
+ File macros2 = new File(TEST_COMPARE_DIR + "/reload/macros2.vtl");
+ macros2.setLastModified(macros2.lastModified() + 1000);
+
+ assertEvalEquals("hip_hip", template);
+ }
+
+ public void testNewMacro() throws Exception
+ {
+ String template = "#foo('hip')";
+ assertEvalEquals("hop_hip", template);
+
+ FileWriter writer = new FileWriter(TEST_COMPARE_DIR + "/reload/macros2.vtl", true);
+ writer.write("\n#macro(bar $txt)hep_$txt#{end}");
+ writer.close();
+ // last modified timestamps resolution is one second before Java 8,
+ // so force reloading by setting a file date in the future
+ File macros2 = new File(TEST_COMPARE_DIR + "/reload/macros2.vtl");
+ macros2.setLastModified(macros2.lastModified() + 1000);
+
+ template = "#foo('hip') #bar('hip')";
+ assertEvalEquals("hop_hip hep_hip", template);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroCommentsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroCommentsTestCase.java
new file mode 100644
index 00000000..3a38f9e4
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroCommentsTestCase.java
@@ -0,0 +1,56 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.exception.ParseErrorException;
+
+/**
+ * Test Macro comment functionality
+ */
+public class MacroCommentsTestCase extends BaseTestCase
+{
+ public MacroCommentsTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ public void testComments()
+ {
+ assertEvalEquals("ab","#macro(foo ##\n $bar \n ## blaa\n $bar2##\n)$bar$bar2#end#foo(\"a\" \"b\")");
+ assertEvalEquals("","#macro(foo1##\n)#end#foo1()");
+ assertEvalEquals("ab","#macro(foo2##\n\t ####\r $bar \n ##\n## Testing blaa\n $bar2##\n)$bar$bar2#end#foo2(\"a\" \"b\")");
+ assertEvalEquals("","#macro(foo4 ## test\n ## test2 ## test3 \n)#end#foo4()");
+ assertEvalEquals("","#macro(foo4 ## test\n $x = 5 ## test2 ## test3 \n)#end#foo4()");
+ }
+
+ public void testErrors()
+ {
+ // We only allow comment lines in macro definitions
+ assertEvalException("#foo1(## test)", ParseErrorException.class);
+ assertEvalException("#foo1($test ## test)", ParseErrorException.class);
+ assertEvalException("#break(## test)", ParseErrorException.class);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroDefaultArgTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroDefaultArgTestCase.java
new file mode 100644
index 00000000..368991f4
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroDefaultArgTestCase.java
@@ -0,0 +1,73 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeConstants;
+
+/**
+ * Test macro default parameters.
+ */
+public class MacroDefaultArgTestCase extends BaseTestCase
+{
+ public MacroDefaultArgTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE);
+ engine.setProperty(RuntimeConstants.VM_ARGUMENTS_STRICT, Boolean.TRUE);
+ engine.setProperty(RuntimeConstants.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE, Boolean.TRUE);
+ }
+
+ public void testCompare()
+ {
+ assertEvalEquals("121", "#macro(foo $a=1)$a#end#foo()#foo(2)#foo");
+ assertEvalEquals("12", "#macro(foo $a = 1)$a#end#foo()#foo(2)");
+ assertEvalEquals("12", "#macro(foo $a= 1 )$a#end#foo()#foo(2)");
+ assertEvalEquals("1x2x", "#macro(foo $a= 1 $b = \"x\")$a$b#end#foo()#foo(2)");
+ assertEvalEquals("1 2 5 2 5 [1, 2] ", "#macro(foo $a=1 $b=2)$a $b #end#foo()#foo(5)#foo(5 [1,2])");
+ assertEvalEquals("1 2 5 2 5 [1, 2] ", "#macro(foo $a=1 , $b=2)$a $b #end#foo()#foo(5)#foo(5 [1,2])");
+ assertEvalEquals("1 2 5 2 5 [1, 2] ", "#macro(foo, $a=1\n $b =2 )$a $b #end#foo()#foo(5)#foo(5 [1,2])");
+
+ assertEvalEquals("3 2 5 2 5 [1, 2] ", "#macro(foo, $a=$x $b =2 )$a $b #end#set($x=3)#foo()#foo(5)#foo(5 [1,2])");
+ assertEvalEquals("{a=3} 2 5 2 5 [1, 2] ", "#macro(foo, $a = {\"a\":$x} $b =2 )$a $b #end#set($x=3)#foo()#foo(5)#foo(5 [1,2])");
+ assertEvalEquals("3 2 5 2 5 [1, 2] ", "#macro(foo, $a = \"$x\" $b =2 )$a $b #end#set($x=3)#foo()#foo(5)#foo(5 [1,2])");
+ assertEvalEquals("3$y 2 5 2 5 [1, 2] ", "#macro(foo, $a = \"$x\\$y\" $b =2 )$a $b #end#set($x=3)#foo()#foo(5)#foo(5 [1,2])");
+ assertEvalEquals("5 3 2 5 [1, 2] 2 ", "#macro(foo, $c $a = \"$x\" $b =2 )$c $a $b #end#set($x=3)#foo(5)#foo(5 [1,2])");
+
+ assertEvalEquals("1xno2xyes", "#macro(foo $a= 1 $b = \"x\")$a$b$bodyContent#end#@foo()no#end#@foo(2)yes#end");
+
+ assertEvalEquals("xy", "#macro(foo $a=\"$b$c\"##\n)$a#end#set($b=\"x\")#set($c=\"y\")#foo()");
+ }
+
+ public void testErrors()
+ {
+ assertEvalException("#macro(foo $a = 1 $b)#end");
+ assertEvalException("#macro(foo $c $a = 3 $b)#end");
+ assertEvalException("#macro(foo $a $b = 1)#end#foo()"); // Too few arguments
+ assertEvalException("#macro(foo $a $b $c = 4)#end#foo(1)"); // Too few arguments
+ assertEvalException("#macro(foo $a = 3)#end#foo(2 3)"); // Too many arguments
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroForwardDefineTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroForwardDefineTestCase.java
new file mode 100644
index 00000000..5a9fb8da
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MacroForwardDefineTestCase.java
@@ -0,0 +1,119 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.StringWriter;
+
+/**
+ * Make sure that a forward referenced macro inside another macro definition does
+ * not report an error in the log.
+ * (VELOCITY-71).
+ *
+ * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+ * @version $Id$
+ */
+public class MacroForwardDefineTestCase
+ extends BaseTestCase
+{
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/macroforwarddefine";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/macroforwarddefine";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/macroforwarddefine/compare";
+
+ /**
+ * Collects the log messages.
+ */
+ private TestLogger logger = new TestLogger(false, true);
+
+ /**
+ * Default constructor.
+ */
+ public MacroForwardDefineTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ // use Velocity.setProperty (instead of properties file) so that we can use actual instance of log
+ Velocity.reset();
+ Velocity.setProperty(RuntimeConstants.RESOURCE_LOADERS,"file");
+ Velocity.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH );
+ Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_REFERENCE_LOG_INVALID,"true");
+
+ // actual instance of logger
+ logger.setEnabledLevel(TestLogger.LOG_LEVEL_DEBUG);
+ Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ Velocity.init();
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(MacroForwardDefineTestCase.class);
+ }
+
+ public void testLogResult()
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext();
+ Template template = Velocity.getTemplate("macros.vm");
+
+ // try to get only messages during merge
+ logger.startCapture();
+ template.merge(context, new StringWriter());
+ logger.stopCapture();
+
+ String resultLog = logger.getLog();
+ if ( !isMatch(resultLog, COMPARE_DIR, "velocity.log", "cmp"))
+ {
+ String compare = getFileContents(COMPARE_DIR, "velocity.log", CMP_FILE_EXT);
+
+ String msg = "Log output was incorrect\n"+
+ "-----Result-----\n"+ resultLog +
+ "----Expected----\n"+ compare +
+ "----------------";
+
+ fail(msg);
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MethodCacheKeyTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MethodCacheKeyTestCase.java
new file mode 100644
index 00000000..43436f0b
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MethodCacheKeyTestCase.java
@@ -0,0 +1,88 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.velocity.runtime.parser.node.ASTMethod;
+
+/**
+ * Checks that the equals method works correctly when caching method keys.
+ *
+ * @author <a href="Will Glass-Husain">wglass@forio.com</a>
+ * @version $Id$
+ */
+public class MethodCacheKeyTestCase extends TestCase
+{
+
+ public void testMethodKeyCacheEquals()
+ {
+ Class [] elements1 = new Class [] { Object.class };
+ ASTMethod.MethodCacheKey mck1 = new ASTMethod.MethodCacheKey("test",elements1, false);
+
+ selfEqualsAssertions(mck1);
+
+ Class [] elements2 = new Class [] { Object.class };
+ ASTMethod.MethodCacheKey mck2 = new ASTMethod.MethodCacheKey("test",elements2, false);
+
+ assertTrue(mck1.equals(mck2));
+
+ Class [] elements3 = new Class [] { String.class };
+ ASTMethod.MethodCacheKey mck3 = new ASTMethod.MethodCacheKey("test",elements3, false);
+
+ assertFalse(mck1.equals(mck3));
+
+ Class [] elements4 = new Class [] { Object.class };
+ ASTMethod.MethodCacheKey mck4 = new ASTMethod.MethodCacheKey("boo",elements4, false);
+
+ assertFalse(mck1.equals(mck4));
+
+ /* check for potential NPE's */
+ Class [] elements5 = ArrayUtils.EMPTY_CLASS_ARRAY;
+ ASTMethod.MethodCacheKey mck5 = new ASTMethod.MethodCacheKey("boo",elements5, false);
+ selfEqualsAssertions(mck5);
+
+ Class [] elements6 = null;
+ ASTMethod.MethodCacheKey mck6 = new ASTMethod.MethodCacheKey("boo",elements6, false);
+ selfEqualsAssertions(mck6);
+
+ Class [] elements7 = new Class [] {};
+ ASTMethod.MethodCacheKey mck7 = new ASTMethod.MethodCacheKey("boo",elements7, false);
+ selfEqualsAssertions(mck7);
+
+ Class [] elements8 = new Class [] {null};
+ ASTMethod.MethodCacheKey mck8 = new ASTMethod.MethodCacheKey("boo",elements8, false);
+ selfEqualsAssertions(mck8);
+
+ Class [] elements9 = new Class [] { Object.class };
+ ASTMethod.MethodCacheKey mck9 = new ASTMethod.MethodCacheKey("boo",elements9, false);
+ selfEqualsAssertions(mck9);
+
+ }
+
+ private void selfEqualsAssertions(ASTMethod.MethodCacheKey mck)
+ {
+ assertTrue(mck.equals(mck));
+ assertTrue(!mck.equals(null));
+ assertTrue(!mck.equals(null));
+ }
+
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MethodInvocationExceptionTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MethodInvocationExceptionTestCase.java
new file mode 100644
index 00000000..34f4d813
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MethodInvocationExceptionTestCase.java
@@ -0,0 +1,278 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.StringWriter;
+
+/**
+ * Tests if we can hand Velocity an arbitrary class for logging.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class MethodInvocationExceptionTestCase extends TestCase
+{
+ protected boolean DEBUG = false;
+
+ /**
+ * Default constructor.
+ * @param name
+ */
+ public MethodInvocationExceptionTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ /*
+ * init() Runtime with defaults
+ */
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(MethodInvocationExceptionTestCase.class);
+ }
+
+ protected void log(String out)
+ {
+ Velocity.getLog().debug(out);
+ if (DEBUG)
+ {
+ System.out.println(out);
+ }
+ }
+
+ /**
+ * Runs the test :
+ *
+ * uses the Velocity class to eval a string
+ * which accesses a method that throws an
+ * exception.
+ * @throws Exception
+ */
+ public void testNormalMethodInvocationException ()
+ throws Exception
+ {
+ String template = "$woogie.doException() boing!";
+
+ VelocityContext vc = new VelocityContext();
+
+ vc.put("woogie", this );
+
+ StringWriter w = new StringWriter();
+
+ try
+ {
+ Velocity.evaluate( vc, w, "test", template );
+ fail("No exception thrown");
+ }
+ catch( MethodInvocationException mie )
+ {
+ log("Caught MIE (good!) :" );
+ log(" reference = " + mie.getReferenceName() );
+ log(" method = " + mie.getMethodName() );
+
+ Throwable t = mie.getCause();
+ log(" throwable = " + t );
+
+ if( t instanceof Exception)
+ {
+ log(" exception = " + t.getMessage() );
+ }
+ }
+ }
+
+
+ public void testGetterMethodInvocationException ()
+ throws Exception
+ {
+ VelocityContext vc = new VelocityContext();
+ vc.put("woogie", this );
+
+ StringWriter w = new StringWriter();
+
+ /*
+ * second test - to ensure that methods accessed via get+ construction
+ * also work
+ */
+
+ String template = "$woogie.foo boing!";
+
+ try
+ {
+ Velocity. evaluate( vc, w, "test", template );
+ fail("No exception thrown, second test.");
+ }
+ catch( MethodInvocationException mie )
+ {
+ log("Caught MIE (good!) :" );
+ log(" reference = " + mie.getReferenceName() );
+ log(" method = " + mie.getMethodName() );
+
+ Throwable t = mie.getCause();
+ log(" throwable = " + t );
+
+ if( t instanceof Exception)
+ {
+ log(" exception = " + t.getMessage() );
+ }
+ }
+ }
+
+
+ public void testCapitalizedGetterMethodInvocationException ()
+ throws Exception
+ {
+ VelocityContext vc = new VelocityContext();
+ vc.put("woogie", this );
+
+ StringWriter w = new StringWriter();
+
+ String template = "$woogie.Foo boing!";
+
+ try
+ {
+ Velocity. evaluate( vc, w, "test", template );
+ fail("No exception thrown, third test.");
+ }
+ catch( MethodInvocationException mie )
+ {
+ log("Caught MIE (good!) :" );
+ log(" reference = " + mie.getReferenceName() );
+ log(" method = " + mie.getMethodName() );
+
+ Throwable t = mie.getCause();
+ log(" throwable = " + t );
+
+ if( t instanceof Exception)
+ {
+ log(" exception = " + t.getMessage() );
+ }
+ }
+ }
+
+ public void testSetterMethodInvocationException ()
+ throws Exception
+ {
+ VelocityContext vc = new VelocityContext();
+ vc.put("woogie", this );
+
+ StringWriter w = new StringWriter();
+
+ String template = "#set($woogie.foo = 'lala') boing!";
+
+ try
+ {
+ Velocity. evaluate( vc, w, "test", template );
+ fail("No exception thrown, set test.");
+ }
+ catch( MethodInvocationException mie )
+ {
+ log("Caught MIE (good!) :" );
+ log(" reference = " + mie.getReferenceName() );
+ log(" method = " + mie.getMethodName() );
+
+ Throwable t = mie.getCause();
+ log(" throwable = " + t );
+
+ if( t instanceof Exception)
+ {
+ log(" exception = " + t.getMessage() );
+ }
+ }
+ }
+
+
+ /**
+ * test that no exception is thrown when in parameter to macro.
+ * This is the way we expect the system to work, but it would be better
+ * to throw an exception.
+ * @throws Exception
+ */
+ public void testMacroInvocationException ()
+ throws Exception
+ {
+ VelocityContext vc = new VelocityContext();
+ vc.put("woogie", this );
+
+ StringWriter w = new StringWriter();
+
+ String template = "#macro (macro1 $param) $param #end #macro1($woogie.getFoo())";
+
+ try
+ {
+ Velocity. evaluate( vc, w, "test", template );
+ fail("No exception thrown, macro invocation test.");
+ }
+ catch( MethodInvocationException mie )
+ {
+ log("Caught MIE (good!) :" );
+ log(" reference = " + mie.getReferenceName() );
+ log(" method = " + mie.getMethodName() );
+
+ Throwable t = mie.getCause();
+ log(" throwable = " + t );
+
+ if( t instanceof Exception)
+ {
+ log(" exception = " + t.getMessage() );
+ }
+ }
+ catch( Exception e)
+ {
+ fail("Wrong exception thrown, test of exception within macro parameter");
+ }
+ }
+
+ public void doException()
+ throws Exception
+ {
+ throw new NullPointerException();
+ }
+
+ public void getFoo()
+ throws Exception
+ {
+ throw new Exception("Hello from getFoo()" );
+ }
+
+ public void setFoo( String foo )
+ throws Exception
+ {
+ throw new Exception("Hello from setFoo()");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MethodOverloadingTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MethodOverloadingTestCase.java
new file mode 100644
index 00000000..711481c8
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MethodOverloadingTestCase.java
@@ -0,0 +1,184 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Test a reported bug in which method overloading throws IllegalArgumentException
+ * after a null return value.
+ * (VELOCITY-132).
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class MethodOverloadingTestCase extends BaseTestCase
+{
+ String logData;
+
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/methodoverloading";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/methodoverloading";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/methodoverloading/compare";
+
+ /**
+ * Default constructor.
+ */
+ public MethodOverloadingTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(MethodOverloadingTestCase.class);
+ }
+
+ public void testMethodOverloading()
+ throws Exception
+ {
+ /*
+ * test overloading in a single template
+ */
+ testFile("single");
+
+ assertTrue(!logData.contains("IllegalArgumentException"));
+ }
+
+ public void testParsedMethodOverloading()
+ throws Exception
+ {
+ /*
+ * test overloading in a file included with #parse
+ */
+ testFile("main");
+
+ assertTrue(!logData.contains("IllegalArgumentException"));
+
+ }
+
+ public void testFile(String basefilename)
+ throws Exception
+ {
+ TestLogger logger = new TestLogger();
+ VelocityEngine ve = new VelocityEngine();
+ ve.addProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+ ve.setProperty(VelocityEngine.RUNTIME_LOG_INSTANCE, logger );
+ ve.init();
+
+ Template template;
+ FileOutputStream fos;
+ Writer fwriter;
+ Context context;
+
+ template = ve.getTemplate( getFileName(null, basefilename, TMPL_FILE_EXT) );
+
+ fos = new FileOutputStream (
+ getFileName(RESULTS_DIR, basefilename, RESULT_FILE_EXT));
+
+ fwriter = new BufferedWriter( new OutputStreamWriter(fos) );
+
+ context = new VelocityContext();
+ setupContext(context);
+ logger.on();
+ template.merge(context, fwriter);
+ logger.off();
+ fwriter.flush();
+ fwriter.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, basefilename, RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+ logData = logger.getLog();
+ }
+
+ public void setupContext(Context context)
+ {
+ context.put("test", this);
+ context.put("nullValue", null);
+ }
+
+
+ public String overloadedMethod ( Integer s )
+ {
+ return "Integer";
+ }
+
+ public String overloadedMethod ( String s )
+ {
+ return "String";
+ }
+
+
+ public String overloadedMethod2 ( Integer s )
+ {
+ return "Integer";
+ }
+
+ public String overloadedMethod2 ( String i )
+ {
+ return "String";
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MiscTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MiscTestCase.java
new file mode 100644
index 00000000..a1a284a7
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MiscTestCase.java
@@ -0,0 +1,74 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.runtime.RuntimeInstance;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Test case for any miscellaneous stuff. If it isn't big, and doesn't fit
+ * anywhere else, it goes here
+ *
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class MiscTestCase extends BaseTestCase
+{
+ public MiscTestCase (String name)
+ {
+ super(name);
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(MiscTestCase.class);
+ }
+
+ public void testRuntimeInstanceProperties()
+ {
+ // check that runtime instance properties can be set and retrieved
+ RuntimeInstance ri = new RuntimeInstance();
+ ri.setProperty("baabaa.test","the answer");
+ assertEquals("the answer",ri.getProperty("baabaa.test"));
+ }
+
+ public void testStringUtils()
+ {
+ /*
+ * some StringUtils tests
+ */
+
+ String arg = null;
+ String res = StringUtils.trim(arg);
+ assertNull(arg);
+
+ arg = " test ";
+ res = StringUtils.trim(arg);
+ assertEquals("test",res);
+
+ arg = "test";
+ res = StringUtils.trim(arg);
+ assertEquals("test",res);
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MultiLoaderTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MultiLoaderTestCase.java
new file mode 100644
index 00000000..dc7d67aa
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MultiLoaderTestCase.java
@@ -0,0 +1,223 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Load templates from the Classpath.
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:daveb@miceda-data.com">Dave Bryson</a>
+ * @version $Id$
+ */
+public class MultiLoaderTestCase extends BaseTestCase
+{
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/multiloader";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/multiloader";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/multiloader/compare";
+
+ /**
+ * Default constructor.
+ */
+ public MultiLoaderTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ /*
+ * Set up the file loader.
+ */
+
+ Velocity.reset();
+
+ Velocity.setProperty(Velocity.RESOURCE_LOADERS, "file");
+
+ Velocity.setProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+
+ Velocity.addProperty(Velocity.RESOURCE_LOADERS, "classpath");
+
+ Velocity.addProperty(Velocity.RESOURCE_LOADERS, "jar");
+
+ /*
+ * Set up the classpath loader.
+ */
+
+ Velocity.setProperty(
+ Velocity.RESOURCE_LOADER + ".classpath.class",
+ "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+
+ Velocity.setProperty(
+ Velocity.RESOURCE_LOADER + ".classpath.cache",
+ "false");
+
+ Velocity.setProperty(
+ Velocity.RESOURCE_LOADER + ".classpath.modification_check_interval",
+ "2");
+
+ /*
+ * setup the Jar loader
+ */
+
+ Velocity.setProperty(
+ Velocity.RESOURCE_LOADER + ".jar.class",
+ "org.apache.velocity.runtime.resource.loader.JarResourceLoader");
+
+ Velocity.setProperty(
+ Velocity.RESOURCE_LOADER + ".jar.path",
+ "jar:file:" + FILE_RESOURCE_LOADER_PATH + "/test2.jar" );
+
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(MultiLoaderTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testMultiLoader ()
+ throws Exception
+ {
+ /*
+ * lets ensure the results directory exists
+ */
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ /*
+ * Template to find with the file loader.
+ */
+ Template template1 = Velocity.getTemplate(
+ getFileName(null, "path1", TMPL_FILE_EXT));
+
+ /*
+ * Template to find with the classpath loader.
+ */
+ Template template2 = Velocity.getTemplate("includeevent/test1-cp." + TMPL_FILE_EXT);
+
+ /*
+ * Template to find with the jar loader
+ */
+ Template template3 = Velocity.getTemplate("template/test2." + TMPL_FILE_EXT);
+
+ /*
+ * and the results files
+ */
+
+ FileOutputStream fos1 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "path1", RESULT_FILE_EXT));
+
+ FileOutputStream fos2 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "test2", RESULT_FILE_EXT));
+
+ FileOutputStream fos3 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "test3", RESULT_FILE_EXT));
+
+ Writer writer1 = new BufferedWriter(new OutputStreamWriter(fos1));
+ Writer writer2 = new BufferedWriter(new OutputStreamWriter(fos2));
+ Writer writer3 = new BufferedWriter(new OutputStreamWriter(fos3));
+
+ /*
+ * put the Vector into the context, and merge both
+ */
+
+ VelocityContext context = new VelocityContext();
+
+ template1.merge(context, writer1);
+ writer1.flush();
+ writer1.close();
+
+ template2.merge(context, writer2);
+ writer2.flush();
+ writer2.close();
+
+ template3.merge(context, writer3);
+ writer3.flush();
+ writer3.close();
+
+ if (!isMatch(RESULTS_DIR,COMPARE_DIR,"path1",RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Output incorrect for FileResourceLoader test.");
+ }
+
+ if (!isMatch(RESULTS_DIR,COMPARE_DIR,"test2",RESULT_FILE_EXT,CMP_FILE_EXT) )
+ {
+ fail("Output incorrect for ClasspathResourceLoader test.");
+ }
+
+ if( !isMatch(RESULTS_DIR,COMPARE_DIR,"test3",RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Output incorrect for JarResourceLoader test.");
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/MultipleFileResourcePathTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/MultipleFileResourcePathTestCase.java
new file mode 100644
index 00000000..c3cd32f3
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/MultipleFileResourcePathTestCase.java
@@ -0,0 +1,144 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Multiple paths in the file resource loader.
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @version $Id$
+ */
+public class MultipleFileResourcePathTestCase extends BaseTestCase
+{
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH1 = TEST_COMPARE_DIR + "/multi/path1";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH2 = TEST_COMPARE_DIR + "/multi/path2";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/multi";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/multi/compare";
+
+ /**
+ * Default constructor.
+ */
+ public MultipleFileResourcePathTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(MultipleFileResourcePathTestCase.class);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ Velocity.reset();
+
+ Velocity.addProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH1);
+
+ Velocity.addProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH2);
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testMultipleFileResources ()
+ throws Exception
+ {
+ Template template1 = RuntimeSingleton.getTemplate(
+ getFileName(null, "path1", TMPL_FILE_EXT));
+
+ Template template2 = RuntimeSingleton.getTemplate(
+ getFileName(null, "path2", TMPL_FILE_EXT));
+
+ FileOutputStream fos1 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "path1", RESULT_FILE_EXT));
+
+ FileOutputStream fos2 =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "path2", RESULT_FILE_EXT));
+
+ Writer writer1 = new BufferedWriter(new OutputStreamWriter(fos1));
+ Writer writer2 = new BufferedWriter(new OutputStreamWriter(fos2));
+
+ /*
+ * put the Vector into the context, and merge both
+ */
+
+ VelocityContext context = new VelocityContext();
+
+ template1.merge(context, writer1);
+ writer1.flush();
+ writer1.close();
+
+ template2.merge(context, writer2);
+ writer2.flush();
+ writer2.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "path1",
+ RESULT_FILE_EXT, CMP_FILE_EXT) ||
+ !isMatch(RESULTS_DIR, COMPARE_DIR, "path2",
+ RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/NumberMethodCallsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/NumberMethodCallsTestCase.java
new file mode 100644
index 00000000..2a24d39a
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/NumberMethodCallsTestCase.java
@@ -0,0 +1,142 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeServices;
+import org.apache.velocity.test.provider.NumberMethods;
+
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+
+/**
+ * Used to check that method calls with number parameters are executed correctly.
+ *
+ * @author <a href="mailto:wglass@forio.com">Peter Romianowski</a>
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ */
+public class NumberMethodCallsTestCase extends TestCase
+{
+ private VelocityEngine ve = null;
+
+ private final static boolean PRINT_RESULTS = false;
+
+ /**
+ * Default constructor.
+ */
+ public NumberMethodCallsTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ ve = new VelocityEngine();
+ ve.init();
+ }
+
+ public void init( RuntimeServices rs )
+ {
+ // do nothing with it
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(NumberMethodCallsTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testNumberMethodCalls ()
+ throws Exception
+ {
+ VelocityContext vc = new VelocityContext();
+
+ // context object with overloaded methods with number arguments
+ vc.put("Test",new NumberMethods());
+
+ // numbers for context
+ vc.put("AByte",new Byte("10"));
+ vc.put("AShort",new Short("10"));
+ vc.put("AInteger", 10);
+ vc.put("ALong", 10L);
+ vc.put("ADouble", 10.0);
+ vc.put("AFloat", 10f);
+ vc.put("ABigDecimal",new BigDecimal(10));
+ vc.put("ABigInteger",new BigInteger("10"));
+
+ // check context objects
+ System.out.println("Testing: method calls with arguments as context objects");
+ checkResults(vc,"$Test.numMethod($AByte)","byte (10)");
+ checkResults(vc,"$Test.numMethod($AShort)","short (10)");
+ checkResults(vc,"$Test.numMethod($AInteger)","int (10)");
+ checkResults(vc,"$Test.numMethod($ADouble)","double (10.0)");
+ checkResults(vc,"$Test.numMethod($AFloat)","double (10.0)");
+ checkResults(vc,"$Test.numMethod($ALong)","long (10)");
+ checkResults(vc,"$Test.numMethod($ABigDecimal)","BigDecimal (10)");
+ checkResults(vc,"$Test.numMethod($ABigInteger)","BigInteger (10)");
+
+ // check literals
+ // -- will cast floating point literal to smallest possible of Double, BigDecimal
+ // -- will cast integer literal to smallest possible of Integer, Long, BigInteger
+ System.out.println("Testing: method calls with arguments as literals");
+ checkResults(vc,"$Test.numMethod(10.0)","double (10.0)");
+ checkResults(vc,"$Test.numMethod(10)","int (10)");
+ checkResults(vc,"$Test.numMethod(10000000000)","long (10000000000)");
+ checkResults(vc,"$Test.numMethod(10000000000000000000000)","BigInteger (10000000000000000000000)");
+
+ // check calculated results
+ // -- note calculated value is cast to smallest possible type
+ // -- method invoked is smallest relevant method
+ // -- it's an unusual case here of both byte and int methods, but this works as expected
+ System.out.println("Testing: method calls with arguments as calculated values");
+ checkResults(vc,"#set($val = 10.0 + 1.5)$Test.numMethod($val)","double (11.5)");
+ checkResults(vc,"#set($val = 100 + 1)$Test.numMethod($val)","int (101)");
+ checkResults(vc,"#set($val = 100 * 1000)$Test.numMethod($val)","int (100000)");
+ checkResults(vc,"#set($val = 100 + 1.5)$Test.numMethod($val)","double (101.5)");
+ checkResults(vc,"#set($val = $ALong + $AInteger)$Test.numMethod($val)","long (20)");
+ checkResults(vc,"#set($val = $ABigInteger + $AInteger)$Test.numMethod($val)","BigInteger (20)");
+ }
+
+
+ private void checkResults(Context vc, String template, String compare) throws Exception
+ {
+
+ StringWriter writer = new StringWriter();
+ ve.evaluate( vc, writer, "test", template);
+ assertEquals("Incorrect results for template '" + template + "'.",compare,writer.toString());
+
+ if (PRINT_RESULTS)
+ System.out.println ("Method call successful: " + template);
+
+ }
+
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/OldPropertiesTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/OldPropertiesTestCase.java
new file mode 100644
index 00000000..46c3b06d
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/OldPropertiesTestCase.java
@@ -0,0 +1,170 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.DeprecationAwareExtProperties;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Tests if we can hand Velocity an arbitrary class for logging.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class OldPropertiesTestCase extends TestCase implements TemplateTestBase
+{
+ private VelocityEngine ve = null;
+ private TestLogger logger = null;
+
+ /**
+ * Default constructor.
+ */
+ public OldPropertiesTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(OldPropertiesTestCase.class);
+ }
+
+ static Pattern propPattern = Pattern.compile("^([a-z._]+)\\s*=\\s*[^#]+.*$", Pattern.CASE_INSENSITIVE);
+ static Pattern warnPattern = Pattern.compile("^\\s*\\[warn\\]\\s*configuration key '([a-z._]+)' has been deprecated in favor of '([a-z._]+)'$", Pattern.CASE_INSENSITIVE);
+
+ static class Translator extends DeprecationAwareExtProperties
+ {
+ @Override
+ public String translateKey(String oldName) { return super.translateKey(oldName); }
+ }
+
+ /**
+ * Check old properties setting and retrieval
+ */
+ public void testOldProperties()
+ throws Exception
+ {
+ String oldProperties = TEST_COMPARE_DIR + "/oldproperties/velocity.properties";
+ ve = new VelocityEngine();
+ logger = new TestLogger(false, true);
+ logger.setEnabledLevel(TestLogger.LOG_LEVEL_WARN);
+
+ // put our test logger where it belongs for this test
+ Field loggerField = DeprecationAwareExtProperties.class.getDeclaredField("logger");
+ loggerField.setAccessible(true);
+ loggerField.set(null, logger);
+
+ logger.on();
+ ve.setProperties(oldProperties);
+ logger.off();
+
+ Translator translator = new Translator();
+
+ // check getting old/new values
+ List<String> oldPropSettings = FileUtils.readLines(new File(oldProperties));
+ Set<String> oldKeys = new HashSet<>();
+ for (String oldProp : oldPropSettings)
+ {
+ Matcher matcher = propPattern.matcher(oldProp);
+ if (matcher.matches())
+ {
+ String propName = matcher.group(1);
+ String translated = translator.translateKey(propName);
+ if (!translated.equals(propName))
+ {
+ Object oldKeyValue = ve.getProperty(propName);
+ Object newKeyValue = ve.getProperty(translated);
+ assertEquals(oldKeyValue, newKeyValue);
+ oldKeys.add(propName);
+ }
+ }
+ }
+
+ // check warnings in the logs
+ String log = logger.getLog();
+ String logLines[] = log.split("\\r?\\n");
+ for (String logLine : logLines)
+ {
+ Matcher matcher = warnPattern.matcher(logLine);
+ if (matcher.matches() && matcher.groupCount() == 2)
+ {
+ String oldName = matcher.group(1);
+ assertTrue(oldKeys.remove(oldName));
+ }
+ }
+ if (oldKeys.size() > 0)
+ {
+ fail("No warning detected for the following properties: " + StringUtils.join(oldKeys, ", "));
+ }
+ }
+
+ /**
+ * Check default properties
+ */
+ public void testNewProperties()
+ throws Exception
+ {
+ ve = new VelocityEngine();
+ logger = new TestLogger(false, true);
+ logger.setEnabledLevel(TestLogger.LOG_LEVEL_WARN);
+
+ // put our test logger where it belongs for this test
+ Field loggerField = DeprecationAwareExtProperties.class.getDeclaredField("logger");
+ loggerField.setAccessible(true);
+ loggerField.set(null, logger);
+
+ logger.on();
+ ve.init();
+ logger.off();
+
+ // check warnings in the logs
+ String log = logger.getLog();
+ String logLines[] = log.split("\\r?\\n");
+ for (String logLine : logLines)
+ {
+ Matcher matcher = warnPattern.matcher(logLine);
+ if (matcher.matches() && matcher.groupCount() == 2)
+ {
+ fail("Default properties contain deprecated property '" + matcher.group(1) + "', deprecated in favor of '" + matcher.group(2) + "'");
+ }
+ }
+
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ParseExceptionTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ParseExceptionTestCase.java
new file mode 100644
index 00000000..5dd2bd24
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ParseExceptionTestCase.java
@@ -0,0 +1,179 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Test parser exception is generated with appropriate info.
+ *
+ * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class ParseExceptionTestCase extends BaseTestCase
+{
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = "parseexception";
+
+
+ /**
+ * Default constructor.
+ * @param name name of test
+ */
+ public ParseExceptionTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ Velocity.reset();
+ super.setUp();
+ }
+
+ /**
+ * Tests that parseException has useful info when called by template.marge()
+ * @throws Exception
+ */
+ public void testParseExceptionFromTemplate ()
+ throws Exception
+ {
+
+ VelocityEngine ve = new VelocityEngine();
+
+ ve.setProperty("file.resource.loader.cache", "true");
+ ve.setProperty("file.resource.loader.path", TemplateTestBase.TEST_COMPARE_DIR + "/" + FILE_RESOURCE_LOADER_PATH);
+ ve.init();
+
+
+ Writer writer = new StringWriter();
+
+ VelocityContext context = new VelocityContext();
+
+ try
+ {
+ Template template = ve.getTemplate("badtemplate.vm");
+ template.merge(context, writer);
+ fail("Should have thown a ParseErrorException");
+ }
+ catch (ParseErrorException e)
+ {
+ assertEquals("badtemplate.vm",e.getTemplateName());
+ assertEquals(5,e.getLineNumber());
+ assertEquals(9,e.getColumnNumber());
+ }
+ finally
+ {
+ if (writer != null)
+ {
+ writer.close();
+ }
+ }
+ }
+
+ /**
+ * Tests that parseException has useful info when thrown in VelocityEngine.evaluate()
+ * @throws Exception
+ */
+ public void testParseExceptionFromEval ()
+ throws Exception
+ {
+ assertEvalExceptionAt(" #set($abc) ", 1, 13);
+ }
+
+ /**
+ * Tests that parseException has useful info when thrown in VelocityEngine.evaluate()
+ * and the problem comes from a macro definition
+ * @throws Exception
+ */
+ public void testParseExceptionFromMacroDef ()
+ throws Exception
+ {
+ assertEvalExceptionAt("#macro($blarg) foo #end", 1, 7);
+ }
+
+ /**
+ * Tests that parseException has useful info when thrown in VelocityEngine.evaluate()
+ * and the problem comes from a macro definition
+ * @throws Exception
+ */
+ public void testParseExceptionFromMacroDefBody ()
+ throws Exception
+ {
+ assertEvalExceptionAt("#macro(aa $blarg) #set(!! = bb) #end #aa('aa')", 1, 24);
+ }
+
+ /**
+ * Tests that parseException has useful info when thrown in VelocityEngine.evaluate()
+ * and the problem comes from a macro invocation
+ * @throws Exception
+ */
+ public void testParseExceptionFromMacroInvoke ()
+ {
+ assertEvalExceptionAt("#macro( foo $a) $a #end #foo(woogie)", 1, 32);
+ }
+
+
+ /**
+ * Tests that parseException has useful info with macro calls with
+ * invalid number of arguments
+ * @throws Exception
+ */
+ public void testParseExceptionMacroInvalidArgumentCount ()
+ throws Exception
+ {
+ engine.setProperty(RuntimeConstants.VM_ARGUMENTS_STRICT,"true");
+ assertEvalExceptionAt("#macro(foo $a) $a #end #foo('test1' 'test2')", 1, 24);
+ }
+
+
+ /**
+ * Tests that parseException has useful info with macro calls with
+ * invalid number of arguments
+ * @throws Exception
+ */
+ public void testParseExceptionMacroInvalidArgumentCountNoException ()
+ throws Exception
+ {
+ assertEvalEquals("test1", "#macro(foo $a)$a#end#foo('test1' 'test2')");
+ }
+
+ /**
+ * Minus is not any more allowed inside a symbol (reference, property or method).
+ * @throws Exception
+ */
+ public void testParseExceptionMinusSignDissalowed()
+ throws Exception
+ {
+ assertEvalExceptionAt("${foo-bar}", 1, 6);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ParseWithMacroLibsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ParseWithMacroLibsTestCase.java
new file mode 100644
index 00000000..1b83d5f4
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ParseWithMacroLibsTestCase.java
@@ -0,0 +1,309 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Test case for including macro libraries via the #parse method.
+ */
+public class ParseWithMacroLibsTestCase extends BaseTestCase
+{
+ private static final String RESULT_DIR = TEST_RESULT_DIR + "/parsemacros";
+
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/parsemacros/compare";
+
+ public ParseWithMacroLibsTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ super.setUp();
+ }
+
+ /**
+ * Test suite
+ * @return test suite
+ */
+ public static junit.framework.Test suite()
+ {
+ return new TestSuite(ParseWithMacroLibsTestCase.class);
+ }
+
+ public void testParseMacroLocalCacheOn()
+ throws Exception
+ {
+ /*
+ * local scope, cache on
+ */
+ VelocityEngine ve = createEngine(true, true);
+
+ // render twice to make sure there is no difference with cached templates
+ testParseMacro(ve, "vm_library1.vm", "parseMacro1_1", false);
+ testParseMacro(ve, "vm_library1.vm", "parseMacro1_1", false);
+
+ // run again with different macro library
+ testParseMacro(ve, "vm_library2.vm", "parseMacro1_1b", false);
+ testParseMacro(ve, "vm_library2.vm", "parseMacro1_1b", false);
+ }
+
+ /**
+ * Runs the tests with global namespace.
+ */
+ public void testParseMacroLocalCacheOff()
+ throws Exception
+ {
+ /*
+ * local scope, cache off
+ */
+ VelocityEngine ve = createEngine(false, true);
+
+ testParseMacro(ve, "vm_library1.vm", "parseMacro1_2", true);
+
+ // run again with different macro library
+ testParseMacro(ve, "vm_library2.vm", "parseMacro1_2b", true);
+ }
+
+ public void testParseMacroGlobalCacheOn()
+ throws Exception
+ {
+ /*
+ * global scope, cache on
+ */
+ VelocityEngine ve = createEngine(true, false);
+
+ // render twice to make sure there is no difference with cached templates
+ testParseMacro(ve, "vm_library1.vm", "parseMacro1_3", false);
+ testParseMacro(ve, "vm_library1.vm", "parseMacro1_3", false);
+
+ // run again with different macro library
+ testParseMacro(ve, "vm_library2.vm", "parseMacro1_3b", false);
+ testParseMacro(ve, "vm_library2.vm", "parseMacro1_3b", false);
+ }
+
+ public void testParseMacroGlobalCacheOff()
+ throws Exception
+ {
+ /*
+ * global scope, cache off
+ */
+ VelocityEngine ve = createEngine(false, false);
+
+ testParseMacro(ve, "vm_library1.vm", "parseMacro1_4", true);
+
+ // run again with different macro library
+ testParseMacro(ve, "vm_library2.vm", "parseMacro1_4b", true);
+
+ }
+
+ /**
+ * Test #parse with macros. Can be used to test different engine configurations
+ * @param ve
+ * @param outputBaseFileName
+ * @param testCachingOff
+ * @throws Exception
+ */
+ private void testParseMacro(VelocityEngine ve, String includeFile, String outputBaseFileName, boolean testCachingOff)
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ FileOutputStream fos = new FileOutputStream (getFileName(
+ RESULT_DIR, outputBaseFileName, RESULT_FILE_EXT));
+
+ VelocityContext context = new VelocityContext();
+ context.put("includefile", includeFile);
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ Template template = ve.getTemplate("parseMacro1.vm");
+ template.merge(context, writer);
+
+ /*
+ * Write to the file
+ */
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR, COMPARE_DIR, outputBaseFileName,
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ String result = getFileContents(RESULT_DIR, outputBaseFileName, RESULT_FILE_EXT);
+ String compare = getFileContents(COMPARE_DIR, outputBaseFileName, CMP_FILE_EXT);
+
+ String msg = "Processed template did not match expected output\n"+
+ "-----Result-----\n"+ result +
+ "----Expected----\n"+ compare +
+ "----------------";
+
+ fail(msg);
+ }
+
+ /*
+ * Show that caching is turned off
+ */
+ if (testCachingOff)
+ {
+ Template t1 = ve.getTemplate("parseMacro1.vm");
+ Template t2 = ve.getTemplate("parseMacro1.vm");
+
+ assertNotSame("Different objects", t1, t2);
+ }
+ }
+
+ /**
+ * Return and initialize engine
+ * @return
+ */
+ private VelocityEngine createEngine(boolean cache, boolean local)
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ ve.setProperty("velocimacro.permissions.allow.inline.to.replace.global",
+ local);
+ ve.setProperty("file.resource.loader.cache", cache);
+ ve.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+ ve.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file");
+ ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
+ TEST_COMPARE_DIR + "/parsemacros");
+ ve.init();
+
+ return ve;
+ }
+
+
+ /**
+ * Test whether the literal text is given if a definition cannot be
+ * found for a macro.
+ *
+ * @throws Exception
+ */
+ public void testParseMacrosWithNoDefinition()
+ throws Exception
+ {
+ /*
+ * ve1: local scope, cache on
+ */
+ VelocityEngine ve1 = new VelocityEngine();
+
+ ve1.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ ve1.setProperty("velocimacro.permissions.allow.inline.to.replace.global",
+ Boolean.FALSE);
+ ve1.setProperty("file.resource.loader.cache", Boolean.TRUE);
+ ve1.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+ ve1.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file");
+ ve1.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
+ TEST_COMPARE_DIR + "/parsemacros");
+ ve1.init();
+
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ FileOutputStream fos = new FileOutputStream (getFileName(
+ RESULT_DIR, "parseMacro2", RESULT_FILE_EXT));
+
+ VelocityContext context = new VelocityContext();
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ Template template = ve1.getTemplate("parseMacro2.vm");
+ template.merge(context, writer);
+
+ /*
+ * Write to the file
+ */
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR, COMPARE_DIR, "parseMacro2",
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Processed template did not match expected output");
+ }
+ }
+
+
+ /**
+ * Test that if a macro is duplicated, the second one takes precendence
+ *
+ * @throws Exception
+ */
+ public void testDuplicateDefinitions()
+ throws Exception
+ {
+ /*
+ * ve1: local scope, cache on
+ */
+ VelocityEngine ve1 = new VelocityEngine();
+
+ ve1.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ ve1.setProperty("velocimacro.permissions.allow.inline.to.replace.global",
+ Boolean.FALSE);
+ ve1.setProperty("file.resource.loader.cache", Boolean.TRUE);
+ ve1.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+ ve1.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file");
+ ve1.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
+ TEST_COMPARE_DIR + "/parsemacros");
+ ve1.init();
+
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ FileOutputStream fos = new FileOutputStream (getFileName(
+ RESULT_DIR, "parseMacro3", RESULT_FILE_EXT));
+
+ VelocityContext context = new VelocityContext();
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ Template template = ve1.getTemplate("parseMacro3.vm");
+ template.merge(context, writer);
+
+ /*
+ * Write to the file
+ */
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR, COMPARE_DIR, "parseMacro3",
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Processed template did not match expected output");
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ParserTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ParserTestCase.java
new file mode 100644
index 00000000..f3e4af7b
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ParserTestCase.java
@@ -0,0 +1,201 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * More specific parser tests where just templating
+ * isn't enough.
+ *
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class ParserTestCase extends TestCase
+{
+ public ParserTestCase(String testName)
+ {
+ super(testName);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(ParserTestCase.class);
+ }
+
+ /**
+ * Test to make sure that using '=' in #if() throws a PEE
+ */
+ public void testEquals()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ TestLogger logger = new TestLogger();
+ ve.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ ve.init();
+
+ /*
+ * this should parse fine -> uses ==
+ */
+
+ String template = "#if($a == $b) foo #end";
+
+ ve.evaluate(new VelocityContext(), new StringWriter(), "foo", template);
+
+ /*
+ * this should throw an exception
+ */
+
+ template = "#if($a = $b) foo #end";
+
+ try
+ {
+ ve.evaluate(new VelocityContext(), new StringWriter(), "foo", template);
+ fail("Could evaluate template with errors!");
+ }
+ catch(ParseErrorException pe)
+ {
+ // Do nothing
+ }
+ }
+
+ /**
+ * Test to see if we force the first arg to #macro() to be a word
+ */
+ public void testMacro()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ TestLogger logger = new TestLogger();
+ ve.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ ve.init();
+
+ /*
+ * this should work
+ */
+
+ String template = "#macro(foo) foo #end";
+
+ ve.evaluate(new VelocityContext(), new StringWriter(), "foo", template);
+
+ /*
+ * this should throw an exception
+ */
+
+ template = "#macro($x) foo #end";
+
+ try
+ {
+ ve.evaluate(new VelocityContext(), new StringWriter(), "foo", template);
+ fail("Could evaluate macro with errors!");
+ }
+ catch(ParseErrorException pe)
+ {
+ // Do nothing
+ }
+ }
+
+ /**
+ * Test to see if don't tolerate passing word tokens in anything but the
+ * 0th arg to #macro() and the 1th arg to foreach()
+ */
+ public void testArgs()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ TestLogger logger = new TestLogger();
+ ve.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ ve.init();
+
+ /*
+ * this should work
+ */
+
+ String template = "#macro(foo) foo #end";
+
+ ve.evaluate(new VelocityContext(), new StringWriter(), "foo", template);
+
+ /*
+ * this should work - spaces intentional
+ */
+
+ template = "#foreach( $i in $woogie ) end #end";
+
+ ve.evaluate(new VelocityContext(), new StringWriter(), "foo", template);
+
+ /*
+ * this should bomb
+ */
+
+ template = "#macro( foo $a) $a #end #foo(woogie)";
+
+ try
+ {
+ ve.evaluate(new VelocityContext(), new StringWriter(), "foo", template);
+ fail("Evaluation of macro with errors succeeded!");
+ }
+ catch(ParseErrorException pe)
+ {
+ // Do nothing
+ }
+ }
+
+ /**
+ * Test to see if we toString is called multiple times on references.
+ */
+ public void testASTReferenceToStringOnlyCalledOnce()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ TestLogger logger = new TestLogger();
+ ve.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ ve.init();
+
+ String template = "$counter";
+
+ ToStringCounter counter = new ToStringCounter();
+ Map m = new HashMap();
+ m.put("counter", counter);
+
+ ve.evaluate(new VelocityContext(m), new StringWriter(), "foo", template);
+
+ assertEquals(1, counter.timesCalled);
+ }
+
+ public static class ToStringCounter {
+ public int timesCalled = 0;
+ public String toString() {
+ this.timesCalled++;
+ return "foo";
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/PropertyMethodPrecedenceTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/PropertyMethodPrecedenceTestCase.java
new file mode 100755
index 00000000..13f2266a
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/PropertyMethodPrecedenceTestCase.java
@@ -0,0 +1,103 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+
+/**
+ * Used to check that vararg method calls on references work properly
+ */
+public class PropertyMethodPrecedenceTestCase extends BaseTestCase
+{
+ public PropertyMethodPrecedenceTestCase(final String name)
+ {
+ super(name);
+ // DEBUG = true;
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("geta", new getGetgetisTool());
+ context.put("getA", new GetgetisTool());
+ context.put("geta2", new get2getisTool());
+ context.put("get_a", new getisTool());
+ context.put("isA", new isTool());
+ }
+
+ public void testLowercasePropertyMethods()
+ {
+ assertEvalEquals("getfoo", "$geta.foo");
+ assertEvalEquals("getFoo", "$getA.foo");
+ assertEvalEquals("get(foo)", "$get_a.foo");
+ assertEvalEquals("true", "$isA.foo");
+ }
+
+ public void testUppercasePropertyMethods()
+ {
+ assertEvalEquals("getFoo", "$geta.Foo");
+ assertEvalEquals("getfoo", "$geta2.Foo");
+ assertEvalEquals("getFoo", "$getA.Foo");
+ assertEvalEquals("get(Foo)", "$get_a.Foo");
+ assertEvalEquals("true", "$isA.Foo");
+ }
+
+
+ public static class isTool
+ {
+ public boolean isFoo()
+ {
+ return true;
+ }
+ }
+
+ public static class getisTool extends isTool
+ {
+ public String get(String s)
+ {
+ return "get("+s+")";
+ }
+ }
+
+ public static class GetgetisTool extends getisTool
+ {
+ public String getFoo()
+ {
+ return "getFoo";
+ }
+ }
+
+ public static class getGetgetisTool extends GetgetisTool
+ {
+ public String getfoo()
+ {
+ return "getfoo";
+ }
+ }
+
+ public static class get2getisTool extends getisTool
+ {
+ public String getfoo()
+ {
+ return "getfoo";
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/RenderVelocityTemplateTest.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/RenderVelocityTemplateTest.java
new file mode 100644
index 00000000..74288393
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/RenderVelocityTemplateTest.java
@@ -0,0 +1,155 @@
+package org.apache.velocity.test;
+
+import junit.framework.Assert;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+public class RenderVelocityTemplateTest
+{
+
+ static class RenderVelocityTemplate
+ {
+ static
+ {
+ try
+ {
+ Properties p = new Properties();
+ p.put("velocimacro.permissions.allow.inline.local.scope", "true");
+ Velocity.init(p);
+ }
+ catch (Exception e)
+ {
+ throw new AssertionError("Failed to init Velocity");
+ }
+ }
+
+ private final VelocityContext velocityContext = new VelocityContext();
+
+ private final String template;
+
+ public RenderVelocityTemplate(String template)
+ {
+ this.template = template;
+ }
+
+ public String getContent()
+ throws Exception
+ {
+ StringWriter stringWriter = new StringWriter();
+ Velocity.evaluate(velocityContext, stringWriter, "", template);
+ return stringWriter.toString();
+ }
+ }
+
+
+ private static final String templateString = "" + //
+ "#macro (showhelloworld $foo)\n" + //
+ "Hello, World\n" + //
+ "#end\n" + //
+ "\n" + //
+ "<html>\n" + //
+ "<head>\n" + //
+ "<title>page title</title>\n" + //
+ "</head>\n" + //
+ "<body>\n" + //
+ "<p>This is a test</p>\n" + //
+ "<p>#showhelloworld ($foo)</p>\n" + //
+ "</body>\n" + //
+ "</html>";
+
+ public void testMultipleEvals()
+ throws Exception
+ {
+ RenderVelocityTemplate template = new RenderVelocityTemplate(templateString);
+
+ String result = null;
+ for (int i = 0; i < 1000; ++i)
+ {
+ result = template.getContent();
+
+ // Verify that the original macro invocation has been replaced with its result.
+ int index = result.indexOf("#showhelloworld");
+ if (index != -1)
+ {
+ throw new AssertionError("Failed to substitute macro:\n" + result);
+ }
+
+ // Verify that the macro did indeed expand.
+ int indexHW = result.indexOf("<p>Hello, World");
+ Assert.assertTrue(indexHW >= 0);
+
+ // Assert.assertEquals("", result); // enable to show what we really get
+ }
+ }
+
+ /** Helper class for testMultiThreadMultipleEvals(). */
+ static class ExceptionHandler
+ implements Thread.UncaughtExceptionHandler
+ {
+ List<Throwable> errors = new ArrayList<>();
+
+ @Override
+ public void uncaughtException(Thread t, Throwable e)
+ {
+ errors.add(e);
+ }
+ }
+
+ /** Helper class for testMultiThreadMultipleEvals(). */
+ class RunMultipleEvals
+ extends Thread
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ testMultipleEvals();
+ }
+ catch (Exception e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * Spawn multiple threads that concurrently call testMultipleEvals.
+ */
+ public void testMultiThreadMultipleEvals()
+ throws Throwable
+ {
+ int nthreads = 4;
+ ExceptionHandler eh = new ExceptionHandler();
+
+ List<Thread> threads = new ArrayList<>(nthreads);
+ for (int i = 0; i < nthreads; ++i)
+ {
+ Thread t = new RunMultipleEvals();
+ t.setUncaughtExceptionHandler(eh);
+ threads.add(t);
+ }
+
+ for (Thread t : threads)
+ {
+ t.start();
+ }
+
+ for (Thread t : threads)
+ {
+ t.join();
+ }
+
+ if (eh.errors.size() > 0)
+ {
+ // Rethrow the first failing exception.
+ System.out.println("Failed " + eh.errors.size() + " out of " + nthreads + " template evaluations");
+ throw eh.errors.get(0);
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceCachingTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceCachingTestCase.java
new file mode 100644
index 00000000..4ca9a5a6
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceCachingTestCase.java
@@ -0,0 +1,94 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Test resource caching related issues.
+ *
+ * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class ResourceCachingTestCase extends BaseTestCase
+{
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = "/resourcecaching";
+
+
+ /**
+ * Default constructor.
+ */
+ public ResourceCachingTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(ResourceCachingTestCase.class);
+ }
+
+ /**
+ * Tests for fix of bug VELOCITY-98 where a #include followed by #parse
+ * of the same file throws ClassCastException when caching is on.
+ * @throws Exception
+ */
+ public void testIncludeParseCaching ()
+ throws Exception
+ {
+
+ VelocityEngine ve = new VelocityEngine();
+
+ ve.setProperty("file.resource.loader.cache", "true");
+ ve.setProperty("file.resource.loader.path", TemplateTestBase.TEST_COMPARE_DIR + FILE_RESOURCE_LOADER_PATH);
+ ve.init();
+
+ Template template = ve.getTemplate("testincludeparse.vm");
+
+ Writer writer = new StringWriter();
+
+ VelocityContext context = new VelocityContext();
+
+ // will produce a ClassCastException if Velocity-98 is not solved
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+ }
+
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceExistsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceExistsTestCase.java
new file mode 100755
index 00000000..0c50219d
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceExistsTestCase.java
@@ -0,0 +1,105 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.test.misc.TestLogger;
+
+/**
+ * Test the resource exists method
+ *
+ * @version $Id: ResourceExistsTestCase.java 687191 2008-08-19 23:02:41Z nbubna $
+ */
+public class ResourceExistsTestCase extends BaseTestCase
+{
+ private VelocityEngine velocity;
+ private String path = TEST_COMPARE_DIR + "/resourceexists";
+ private TestLogger logger = new TestLogger();
+
+ public ResourceExistsTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+try {
+ velocity = new VelocityEngine();
+ velocity.setProperty("resource.loader", "file,string");
+ velocity.setProperty("file.resource.loader.path", path);
+ velocity.setProperty("string.resource.loader.class", StringResourceLoader.class.getName());
+
+ // actual instance of logger
+ logger.on();
+ velocity.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ velocity.setProperty("runtime.log.logsystem.test.level", "debug");
+} catch (Exception e) {
+ System.out.println("exception via gump: "+e);
+ e.printStackTrace();
+ System.out.println("log: "+logger.getLog());
+}
+ }
+
+ public void testFileResourceExists() throws Exception
+ {
+try {
+ if (!velocity.resourceExists("testfile.vm"))
+ {
+ String msg = "testfile.vm was not found in path "+path;
+ System.out.println(msg);
+ System.out.println("Log was: "+logger.getLog());
+ path = path+"/testfile.vm";
+ java.io.File file = new java.io.File(path);
+ if (file.exists()) {
+ System.out.println("file system found "+path);
+ } else {
+ System.out.println(file+" could not be found as a file");
+ }
+ fail(msg);
+ }
+ if (velocity.resourceExists("nosuchfile.vm"))
+ {
+ String msg = "nosuchfile.vm should not have been found in path "+path;
+ System.out.println(msg);
+ fail(msg);
+ }
+} catch (Exception e) {
+ System.out.println("exception via gump: "+e);
+ e.printStackTrace();
+ System.out.println("log: "+logger.getLog());
+}
+ }
+
+ public void testStringResourceExists() throws Exception
+ {
+try {
+ assertFalse(velocity.resourceExists("foo.vm"));
+ StringResourceLoader.getRepository().putStringResource("foo.vm", "Make it so!");
+ assertTrue(velocity.resourceExists("foo.vm"));
+} catch (Exception e) {
+ System.out.println("exception via gump: "+e);
+ e.printStackTrace();
+ System.out.println("log: "+logger.getLog());
+}
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceLoaderInstanceTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceLoaderInstanceTestCase.java
new file mode 100644
index 00000000..e6217b0c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ResourceLoaderInstanceTestCase.java
@@ -0,0 +1,154 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.runtime.resource.loader.FileResourceLoader;
+import org.apache.velocity.runtime.resource.loader.ResourceLoader;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Test that an instance of a ResourceLoader can be successfully passed in.
+ *
+ * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class ResourceLoaderInstanceTestCase extends BaseTestCase
+{
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/resourceinstance";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/resourceinstance";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/resourceinstance/compare";
+
+ private TestLogger logger = new TestLogger();
+
+ /**
+ * Default constructor.
+ */
+ public ResourceLoaderInstanceTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+
+ ResourceLoader rl = new FileResourceLoader();
+
+ // pass in an instance to Velocity
+ Velocity.reset();
+ Velocity.setProperty( "resource.loader", "testrl" );
+ Velocity.setProperty( "testrl.resource.loader.instance", rl );
+ Velocity.setProperty( "testrl.resource.loader.path", FILE_RESOURCE_LOADER_PATH );
+
+ // actual instance of logger
+ logger.on();
+ Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ Velocity.setProperty("runtime.log.logsystem.test.level", "debug");
+
+ Velocity.init();
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(ResourceLoaderInstanceTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testResourceLoaderInstance ()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ Template template = RuntimeSingleton.getTemplate(
+ getFileName(null, "testfile", TMPL_FILE_EXT));
+
+ FileOutputStream fos =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "testfile", RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ /*
+ * put the Vector into the context, and merge both
+ */
+
+ VelocityContext context = new VelocityContext();
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ if ( !isMatch(RESULTS_DIR, COMPARE_DIR, "testfile",
+ RESULT_FILE_EXT, CMP_FILE_EXT) )
+ {
+ String result = getFileContents(RESULT_DIR, "testfile", RESULT_FILE_EXT);
+ String compare = getFileContents(COMPARE_DIR, "testfile", CMP_FILE_EXT);
+
+ String msg = "Processed template did not match expected output\n"+
+ "-----Result-----\n"+ result +
+ "----Expected----\n"+ compare +
+ "----------------";
+
+ fail(msg);
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/ScopeTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/ScopeTestCase.java
new file mode 100755
index 00000000..21a6490d
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/ScopeTestCase.java
@@ -0,0 +1,369 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+import java.util.HashMap;
+
+/**
+ * This class tests the directive scope controls
+ */
+public class ScopeTestCase extends BaseTestCase
+{
+ public ScopeTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ engine.setProperty("a.provide.scope.control", "true");
+ engine.setProperty("define.provide.scope.control", "true");
+ engine.setProperty("evaluate.provide.scope.control", "true");
+ engine.setProperty("foo.provide.scope.control", "true");
+ engine.setProperty("macro.provide.scope.control", "true");
+ engine.setProperty("template.provide.scope.control", "true");
+ engine.setProperty("vm.provide.scope.control", "true");
+ engine.setProperty("space.gobbling", "bc");
+ }
+
+ public void testScopeGetLeakIntoInner()
+ {
+ addTemplate("foo", "#foreach($i in [1..1])#set($foreach.a=$i)"+
+ "#foreach($j in [2..2])$foreach.a#set($foreach.a=$j)"+
+ "#foreach($k in [3..3])$foreach.a#end#end$foreach.a#end");
+ assertTmplEquals("121", "foo");
+ }
+
+ public void testScopeGetLeakDoesntHideNullset()
+ {
+ addTemplate("a", "#macro(a)#set($macro.a='a')#b()$macro.a#end"+
+ "#macro(b)$macro.a#set($macro.a=$null)$!macro.a#end"+
+ "#a()");
+ assertTmplEquals("aa", "a");
+ }
+
+ public void testRootTemplateMergeScope()
+ {
+ addTemplate("foo", "foo#break($template)bar");
+ assertTmplEquals("foo", "foo");
+ assertNull(context.get("template"));
+ }
+
+ public void testParseScope()
+ {
+ addTemplate("test", "$template.info.depth"+
+ "$!parse.parent.info.depth"+
+ "#set( $template.foo = 'bar' )"+
+ "$template.foo"+
+ "#break($template)"+
+ "woogie");
+ assertEvalEquals("1bar", "#parse( 'test' )");
+ assertNull(context.get("template"));
+ }
+
+ public void testNestedParseScope()
+ {
+ HashMap grab = new HashMap();
+ context.put("grab", grab);
+
+ addTemplate("inner", "Inner depth: $template.info.depth"+
+ "#set( $template.foo = '?' )"+
+ "$!grab.put('inner',$template)"+
+ "#break($template)$template.foo");
+ addTemplate("outer", "#set( $template.foo = '!' )"+
+ "Outer depth: $template.info.depth "+
+ "#parse('inner')"+
+ "$!grab.put('outer', $template)"+
+ "$template.foo");
+ assertEvalEquals("Outer depth: 1 Inner depth: 2!", "#parse('outer')");
+ // make extra sure that the outer control was restored after the stop
+ assertFalse(grab.get("inner") == grab.get("outer"));
+ // make sure the outer control was cleaned up
+ assertNull(context.get("template"));
+
+ addTemplate("3", "$template.topmost.foo#set( $template.topmost.foo = 'bar' )");
+ addTemplate("2", "#parse( '3' )$!parse.foo");
+ addTemplate("1", "#set( $template.foo = 'foo' )#parse('2')$template.foo");
+ assertEvalEquals("foobar", "#parse('1')$!parse");
+ // make sure the top control was cleaned up
+ assertNull(context.get("template"));
+ }
+
+ public void testForeachScope()
+ {
+ String template = "#foreach( $i in [0..2] )"+
+ "#if( $i > 1 )#break($foreach)#end"+
+ "$foreach.index:$foreach.count:$foreach.hasNext,"+
+ "#end";
+ assertEvalEquals("0:1:true,1:2:true,", template);
+ assertNull(context.get("foreach"));
+ }
+
+ public void testNestedForeachScope()
+ {
+ String template = "#foreach( $i in [1..5] )"+
+ "#foreach( $j in [1..2] )"+
+ "#if ( $i > $foreach.count + $foreach.index + $foreach.info.depth )#break($foreach.topmost)#end"+
+ "#end"+
+ "$i"+
+ "#end";
+ assertEvalEquals("123", template);
+ assertNull(context.get("foreach"));
+ }
+
+ public void testMacroScope()
+ {
+ String template = "#macro( foo $i )"+
+ "#if($i > 2 )#break($macro)#end"+
+ "$i#end"+
+ "#foo( 0 )#foo( 1 )#foo( 2 )";
+ assertEvalEquals("012", template);
+ assertNull(context.get("macro"));
+ }
+
+ public void testRecursiveMacroScope()
+ {
+ String template = "#macro( foo )$macro.info.depth"+
+ "#if($macro.info.depth > 2 )#break($macro.topmost)#end"+
+ "#foo()#end#foo()";
+ assertEvalEquals("123", template);
+ assertNull(context.get("macro"));
+ }
+
+ public void testNestedMacroScope()
+ {
+ String template = "#macro( a )$macro.info.depth#set($macro.c = 'a')$macro.c#end"+
+ "#macro( b )#set($macro.c = 'b' )#a()$macro.c#end"+
+ "#b()";
+ assertEvalEquals("2ab", template);
+ assertNull(context.get("macro"));
+ }
+
+ public void testBodyMacroScope()
+ {
+ String template = "#macro( foo $bar )$bodyContent$macro.bar#end"+
+ "#@foo( 'bar' )#set( $macro.bar = 'foo'+$bar )"+
+ "#set( $foo.d = $foo.info.depth )$foo.d #end";
+ assertEvalEquals("1 foobar", template);
+ assertNull(context.get("foo"));
+ assertNull(context.get("macro"));
+ }
+
+ public void testRecursiveBodyMacroScope()
+ {
+ engine.setProperty(RuntimeConstants.VM_MAX_DEPTH, "5");
+ String template = "#macro( foo )$bodyContent$macro.i#end"+
+ "#@foo()#set( $macro.i = \"$!macro.i$foo.info.depth,\" )"+
+ "$!bodyContent#end";
+ assertEvalEquals("1,2,3,4,5,", template);
+ assertNull(context.get("foo"));
+ assertNull(context.get("macro"));
+ }
+
+ public void testDefineScope()
+ {
+ String template = "#define( $foo )#set( $define.bar = 'bar'+$define.info.depth )$define.bar#end$foo";
+ assertEvalEquals("bar1", template);
+ assertNull(context.get("define"));
+ }
+
+ public void testNestedDefineScope()
+ {
+ String template = "#define($a)$b c#end"+
+ "#define($b)$define.info.depth#break($define.topmost)#end"+
+ "$a";
+ assertEvalEquals("2", template);
+ assertNull(context.get("define"));
+ }
+
+ public void testRecursiveDefineScope()
+ {
+ engine.setProperty(RuntimeConstants.DEFINE_DIRECTIVE_MAXDEPTH, "10");
+ String template = "#define($a)$define.info.depth"+
+ "#if($define.info.depth == 5)#break($define)#end,$a#end$a";
+ assertEvalEquals("1,2,3,4,5", template);
+ assertNull(context.get("define"));
+ }
+
+ public void testRootEvaluateScope()
+ {
+ assertEvalEquals("1", "$evaluate.info.depth");
+ assertEvalEquals("foo", "foo#break($evaluate)bar");
+ assertNull(context.get("evaluate"));
+ }
+
+ public void testEvaluateScope()
+ {
+ context.put("h", "#");
+ context.put("d", "$");
+ String template = "${h}set( ${d}evaluate.foo = 'bar' )"+
+ "${d}evaluate.foo ${d}evaluate.info.depth";
+ addTemplate("eval", "#evaluate(\""+template+"\")");
+ assertTmplEquals("bar 1", "eval");
+ assertNull(context.get("evaluate"));
+ assertNull(context.get("template"));
+ }
+
+ public void testNestedEvaluateScope()
+ {
+ context.put("h", "#");
+ context.put("d", "$");
+ addTemplate("e", "#evaluate(\"${h}evaluate( '${d}evaluate.info.depth${h}stop(${d}evaluate) blah' )\")");
+ assertTmplEquals("2", "e");
+ assertNull(context.get("evaluate"));
+ assertNull(context.get("template"));
+ }
+
+ public void testTurningOffTemplateScope()
+ {
+ engine.setProperty("template."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, "false");
+ // root
+ addTemplate("test", "$template.info.depth");
+ assertTmplEquals("$template.info.depth", "test");
+ // #parse
+ assertEvalEquals("$template.info.depth", "#parse('test')");
+ }
+
+ public void testTurningOffEvaluateScope()
+ {
+ engine.setProperty("evaluate."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, "false");
+ // root
+ assertSchmoo("$evaluate.info.depth");
+ // #evaluate
+ assertEvalEquals("$evaluate.info.depth", "#evaluate( '$evaluate.info.depth' )");
+ }
+
+ public void testTurningOffMacroScope()
+ {
+ engine.setProperty("macro."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, "false");
+ engine.setProperty("foo."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, "false");
+ // macro definition
+ assertEvalEquals("$macro", "#macro(a)$macro#end#a()");
+ // macro body
+ assertEvalEquals("$macro $foo", "#macro(foo)$bodyContent#end#@foo()$macro $foo#end");
+ }
+
+ public void testTurningOffDefineScope()
+ {
+ engine.setProperty("define."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, "false");
+ assertEvalEquals("$define", "#define($a)$define#end$a");
+ }
+
+ public void testTurningOffForeachScope()
+ {
+ engine.setProperty("foreach."+RuntimeConstants.PROVIDE_SCOPE_CONTROL, "false");
+ assertEvalEquals("$foreach$foreach", "#foreach($i in [0..1])$foreach#end");
+ }
+
+ public void testTemplateReplaced()
+ {
+ context.put("template", "foo");
+ addTemplate("test", "$template.replaced");
+ assertTmplEquals("foo", "test");
+ assertEvalEquals("foo", "#parse('test')");
+ assertContextValue("template", "foo");
+ }
+
+ public void testEvaluateReplaced()
+ {
+ context.put("evaluate","foo");
+ assertEvalEquals("foo", "$evaluate.replaced");
+ assertEvalEquals("foo", "#evaluate('$evaluate.replaced')");
+ assertContextValue("evaluate", "foo");
+ }
+
+ public void testMacroReplaced()
+ {
+ context.put("macro", "foo");
+ assertEvalEquals("foo foo foo", "$macro #macro(a)$macro.replaced#end#a() $macro");
+ assertContextValue("macro", "foo");
+ }
+
+ public void testForeachReplaced()
+ {
+ context.put("foreach", "foo");
+ assertEvalEquals("foofoofoo", "$foreach#foreach($i in [1..1])$foreach.replaced#end$foreach");
+ assertEquals("foo", context.get("foreach"));
+ context.put("foreach", "a");
+ assertEvalEquals("a", "#foreach($i in [1..1])#foreach($j in [1..1])$foreach.replaced#end#end");
+ assertContextValue("foreach", "a");
+ }
+
+ public void testDefineReplaced()
+ {
+ context.put("define", "a");
+ assertEvalEquals("a", "#define($a)$define.replaced#end$a");
+ assertContextValue("define", "a");
+ }
+
+ public void testBodyContentReplaced()
+ {
+ context.put("vm", "a");
+ assertEvalEquals("a", "#macro(vm)$bodyContent#end#@vm()$vm.replaced#end");
+ assertContextValue("vm", "a");
+ }
+
+ public void testInfoDepth()
+ {
+ String template = "#foreach($i in [1..1])"+
+ "#foreach($j in [0..0])"+
+ "$foreach.info.depth"+
+ "#end"+
+ "#end";
+ assertEvalEquals("2", template);
+ }
+
+ public void testInfoName()
+ {
+ String template = "#foreach($i in [1..1])"+
+ "$foreach.info.name #evaluate('$evaluate.info.name')"+
+ "#end";
+ assertEvalEquals("foreach evaluate", template);
+ }
+
+ public void testInfoType()
+ {
+ addTemplate("info", "#foreach($i in [1..1])"+
+ "$foreach.info.type"+
+ "#end "+
+ "#evaluate('$evaluate.info.type') "+
+ "$template.info.type");
+ assertTmplEquals("block line utf-8", "info");
+ }
+
+ public void testInfoLineAndColumn()
+ {
+ String template = " #evaluate('$evaluate.info.line, $evaluate.info.column')";
+ assertEvalEquals(" 1, 2", template);
+ assertEvalEquals("\n\n 3, 4", "\n\n "+template);
+ }
+
+ public void testInfoTemplate()
+ {
+ addTemplate("test", "#evaluate('$evaluate.info.template')");
+ assertTmplEquals("test", "test");
+ assertEvalEquals("test", "#parse('test')");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/SecureIntrospectionTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/SecureIntrospectionTestCase.java
new file mode 100644
index 00000000..f504029f
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/SecureIntrospectionTestCase.java
@@ -0,0 +1,179 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.util.introspection.SecureUberspector;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collection;
+import java.util.HashSet;
+
+/**
+ * Checks that the secure introspector is working properly.
+ *
+ * @author <a href="Will Glass-Husain">wglass@forio.com</a>
+ * @version $Id$
+ */
+public class SecureIntrospectionTestCase extends BaseTestCase
+{
+
+ /**
+ * Default constructor.
+ * @param name
+ */
+ public SecureIntrospectionTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(SecureIntrospectionTestCase.class);
+ }
+
+
+ private String [] badTemplateStrings =
+ {
+ "$test.Class.Methods",
+ "$test.Class.ClassLoader",
+ "$test.Class.ClassLoader.loadClass('java.util.HashMap').newInstance().size()"
+ };
+
+ private String [] goodTemplateStrings =
+ {
+ "#foreach($item in $test.collection)$item#end",
+ "$test.Class.Name",
+ "#set($test.Property = 'abc')$test.Property",
+ "$test.aTestMethod()"
+ };
+
+ /**
+ * Test to see that "dangerous" methods are forbidden
+ * @exception Exception
+ */
+ public void testBadMethodCalls()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.UBERSPECT_CLASSNAME, SecureUberspector.class.getName());
+ ve.init();
+
+ /*
+ * all of the following method calls should not work
+ */
+ doTestMethods(ve, badTemplateStrings, false);
+ }
+
+ /**
+ * Test to see that "dangerous" methods are forbidden
+ * @exception Exception
+ */
+ public void testGoodMethodCalls()
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(RuntimeConstants.UBERSPECT_CLASSNAME, SecureUberspector.class.getName());
+ ve.init();
+
+ /*
+ * all of the following method calls should not work
+ */
+ doTestMethods(ve, goodTemplateStrings, true);
+ }
+
+ private void doTestMethods(VelocityEngine ve, String[] templateStrings, boolean shouldeval)
+ {
+ Context c = new VelocityContext();
+ c.put("test", this);
+
+ try
+ {
+ for (String templateString : templateStrings)
+ {
+ if (shouldeval && !doesStringEvaluate(ve, c, templateString))
+ {
+ fail("Should have evaluated: " + templateString);
+ }
+
+ if (!shouldeval && doesStringEvaluate(ve, c, templateString))
+ {
+ fail("Should not have evaluated: " + templateString);
+ }
+ }
+
+ }
+ catch (Exception e)
+ {
+ fail(e.toString());
+ }
+ }
+
+ private boolean doesStringEvaluate(VelocityEngine ve, Context c, String inputString) throws ParseErrorException, MethodInvocationException, ResourceNotFoundException, IOException
+ {
+ // assume that an evaluation is bad if the input and result are the same (e.g. a bad reference)
+ // or the result is an empty string (e.g. bad #foreach)
+ Writer w = new StringWriter();
+ ve.evaluate(c, w, "foo", inputString);
+ String result = w.toString();
+ return (result.length() > 0 ) && !result.equals(inputString);
+ }
+
+ private String testProperty;
+ public String getProperty()
+ {
+ return testProperty;
+ }
+
+ public int aTestMethod()
+ {
+ return 1;
+ }
+
+ public void setProperty(String val)
+ {
+ testProperty = val;
+ }
+
+
+ public Collection getCollection()
+ {
+ Collection c = new HashSet();
+ c.add("aaa");
+ c.add("bbb");
+ c.add("ccc");
+ return c;
+ }
+}
+
+
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/SetTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/SetTestCase.java
new file mode 100644
index 00000000..e6794800
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/SetTestCase.java
@@ -0,0 +1,144 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.Template;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Test that an instance of a ResourceLoader can be successfully passed in.
+ *
+ * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class SetTestCase extends BaseTestCase
+{
+ /**
+ * VTL file extension.
+ */
+ private static final String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ private final static String FILE_RESOURCE_LOADER_PATH = TEST_COMPARE_DIR + "/set";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/set";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/set/compare";
+
+ /**
+ * Default constructor.
+ */
+ public SetTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.addProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+ assureResultsDirectoryExists(RESULTS_DIR);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testSetNull()
+ throws Exception
+ {
+ /*
+ * Check that #set does accept nulls
+ */
+ checkTemplate("set1");
+
+ /*
+ * Check that #set can accept nulls, and has the correct behaviour for complex LHS
+ */
+ checkTemplate("set2");
+ }
+
+ public void checkTemplate(String templateName) throws Exception
+ {
+ Template template;
+ FileOutputStream fos;
+ Writer fwriter;
+
+ template = engine.getTemplate(getFileName(null, templateName, TMPL_FILE_EXT));
+
+ fos = new FileOutputStream (
+ getFileName(RESULTS_DIR, templateName, RESULT_FILE_EXT));
+
+ fwriter = new BufferedWriter( new OutputStreamWriter(fos) );
+
+ template.merge(context, fwriter);
+ fwriter.flush();
+ fwriter.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, templateName, RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+ }
+
+ public void testInvalidSet() throws Exception
+ {
+ /* the purpose of this test is to check that in case of error, the calculation of the
+ literal representation of the expression, which is displayed in the logs, does not raise a null exception
+ */
+ assertEvalEquals("", "#set($c = $a - $b - $c)");
+ assertEvalEquals("", "#set($c = $a + $b + $c)");
+ assertEvalEquals("", "#set($c = $a * $b * $c)");
+ assertEvalEquals("", "#set($c = $a / $b / $c)");
+ assertEvalEquals("", "#set($c = $a % $b % $c)");
+ assertEvalEquals("", "#set($c = $a && $b && $c)");
+ assertEvalEquals("", "#set($c = $a || $b || $c)");
+ assertEvalEquals("", "#set($c = $a + $b + !$c)");
+ assertEvalEquals("", "#set($c = $a + $b + (-$c))");
+ assertEvalEquals("", "#set($c = $a && ($b < $c))");
+ assertEvalEquals("", "#set($c = !$a)");
+ assertEvalEquals("", "#set($c = ($a < $b) - ($c < $d))");
+ assertEvalEquals("","#set($b = !$a)");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/SpaceGobblingTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/SpaceGobblingTestCase.java
new file mode 100644
index 00000000..e8d22bdf
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/SpaceGobblingTestCase.java
@@ -0,0 +1,143 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.RuntimeConstants.SpaceGobbling;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Test case for conversion handler
+ */
+public class SpaceGobblingTestCase extends BaseTestCase
+{
+ private static final String RESULT_DIR = TEST_RESULT_DIR + "/gobbling";
+
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/gobbling/compare";
+
+ public SpaceGobblingTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ super.setUp();
+ }
+
+ /**
+ * Test suite
+ * @return test suite
+ */
+ public static junit.framework.Test suite()
+ {
+ return new TestSuite(SpaceGobblingTestCase.class);
+ }
+
+ /**
+ * Return and initialize engine
+ * @return
+ */
+ private VelocityEngine createEngine(SpaceGobbling mode)
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty(Velocity.RUNTIME_LOG_INSTANCE, log);
+ ve.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file");
+ ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, TEST_COMPARE_DIR + "/gobbling");
+ ve.setProperty(RuntimeConstants.SPACE_GOBBLING, mode.toString());
+ ve.init();
+
+ return ve;
+ }
+
+ public void testSpaceGobbling() throws Exception
+ {
+ for (SpaceGobbling mode : SpaceGobbling.values())
+ {
+ testMode(mode);
+ }
+ }
+
+ private void testMode(SpaceGobbling mode) throws Exception
+ {
+ File dir = new File(TEST_COMPARE_DIR + "/gobbling");
+ File[] directoryListing = dir.listFiles();
+ if (directoryListing != null)
+ {
+ for (File child : directoryListing)
+ {
+ if (child.isFile())
+ {
+ testTemplate(child.getName(), mode);
+ }
+ }
+ }
+ else
+ {
+ throw new Exception("cannot read input templates");
+ }
+ }
+
+ private void testTemplate(String templateFile, SpaceGobbling mode) throws Exception
+ {
+ assureResultsDirectoryExists(RESULT_DIR);
+ FileOutputStream fos = new FileOutputStream (getFileName(RESULT_DIR, templateFile, mode.toString()));
+ VelocityContext context = new VelocityContext();
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ VelocityEngine ve = createEngine(mode);
+ Template template = ve.getTemplate(templateFile);
+ template.merge(context, writer);
+
+ /*
+ * Write to the file
+ */
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR, COMPARE_DIR, templateFile, mode.toString(), mode.toString()))
+ {
+ String result = getFileContents(RESULT_DIR, templateFile, mode.toString());
+ String compare = getFileContents(COMPARE_DIR, templateFile, mode.toString());
+
+ String msg = "Processed template did not match expected output for template " + templateFile + " and mode " + mode + "\n"+
+ "-----Result-----\n"+ result +
+ "----Expected----\n"+ compare +
+ "----------------";
+
+ fail(msg);
+ }
+
+ }
+}
+
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StaticUtilityMethodsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StaticUtilityMethodsTestCase.java
new file mode 100755
index 00000000..60cabbf6
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StaticUtilityMethodsTestCase.java
@@ -0,0 +1,57 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+
+/**
+ * This class tests support for putting static utility classes
+ * like java.lang.Math directly into the context in order to
+ * use their methods.
+ */
+public class StaticUtilityMethodsTestCase extends BaseTestCase
+{
+ public StaticUtilityMethodsTestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testMath()
+ {
+ context.put("Math", Math.class);
+ assertEvalEquals("java.lang.Math", "$Math.name");
+ assertEvalEquals("3.0", "$Math.ceil(2.5)");
+ }
+
+ public void testFoo()
+ {
+ context.put("Foo", Foo.class);
+ assertEvalEquals("test", "$Foo.foo('test')");
+ }
+
+
+ public static class Foo
+ {
+ private Foo() {}
+ public static String foo(String s)
+ {
+ return s == null ? "foo" : s;
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StopDirectiveTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StopDirectiveTestCase.java
new file mode 100644
index 00000000..f09a53d0
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StopDirectiveTestCase.java
@@ -0,0 +1,85 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.misc.TestLogger;
+
+/**
+ * Test the #stop directive
+ */
+public class StopDirectiveTestCase extends BaseTestCase
+{
+ public StopDirectiveTestCase(String name)
+ {
+ super(name);
+ //DEBUG=true;
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, TemplateTestBase.TEST_COMPARE_DIR + "/stop");
+ engine.setProperty(RuntimeConstants.VM_LIBRARY, "vmlib1.vm");
+ }
+
+ public void testStop()
+ {
+ // Make it work through the evaluate method call
+ assertEvalEquals("Text1", "Text1#{stop}Text2");
+ // Make sure stop works in a template
+ assertTmplEquals("Text 1", "stop1.vm");
+ // Make sure stop works when called from a velocity macro
+ assertTmplEquals("Text123stuff1", "stop2.vm");
+ // Make sure stop works when called located in another parsed file
+ assertTmplEquals("text1blaa1", "stop3.vm");
+ }
+
+ public void testNestedStopAll()
+ {
+ addTemplate("ns", ",template"+
+ "#macro(vm),macro${bodyContent}macro#end"+
+ "#define($define),define"+
+ "#foreach($i in [1..2]),foreach"+
+ "#{stop}foreach"+
+ "#{end}define"+
+ "#{end}"+
+ "#@vm(),bodyContent"+
+ "${define}bodyContent"+
+ "#{end}template");
+ String expected = "evaluate,template,macro,bodyContent,define,foreach";
+ assertEvalEquals(expected, "#evaluate('evaluate#parse(\"ns\")evaluate')");
+ }
+
+ public void testStopMessage()
+ {
+ log.setEnabledLevel(TestLogger.LOG_LEVEL_DEBUG);
+ context.put("log", log);
+
+ assertEvalEquals("a", "a$!log.startCapture()#stop('woogie!')b");
+
+ log.stopCapture();
+ log.on();
+ info("Log: "+log.getLog());
+ assertTrue(log.getLog().contains("StopCommand: woogie!"));
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictAlternateValuesTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictAlternateValuesTestCase.java
new file mode 100644
index 00000000..36fe903b
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictAlternateValuesTestCase.java
@@ -0,0 +1,76 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+/**
+ * Base test case that provides utility methods for
+ * the rest of the tests.
+ *
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author Nathan Bubna
+ * @version $Id: AlternateValuesTestCase.java 1843764 2018-10-13 14:52:28Z cbrisson $
+ */
+public class StrictAlternateValuesTestCase extends BaseTestCase
+{
+ public StrictAlternateValuesTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE);
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("foo", null);
+ }
+
+ public void testDefault()
+ {
+ assertEvalEquals("<foo>", "<${foo|'foo'}>");
+ assertEvalEquals("bar", "#set($bar='bar')${foo|$bar}");
+ assertEvalEquals("bar", "#set($bar='bar')${foo|${bar}}");
+ assertEvalException("${foo.bar.baz()[5]|'hop'}", VelocityException.class);
+ assertEvalEquals("{foo}", "{${foo|'foo'}}");
+ assertEvalException("$foo", VelocityException.class);
+ }
+
+ public void testComplexEval()
+ {
+ assertEvalException("<${date.format('medium', $date.date)|'no date tool'}>", VelocityException.class);
+ assertEvalEquals("true", "#set($val=false)${val.toString().replace(\"false\", \"true\")|'so what'}");
+ assertEvalEquals("so what", "#set($foo='foo')${foo.contains('bar')|'so what'}");
+ assertEvalEquals("so what", "#set($val=false)${val.toString().contains('bar')|'so what'}");
+ assertEvalEquals("true", "#set($val=false)${val.toString().contains('false')|'so what'}");
+ assertEvalException("$!{null|$null}", VelocityException.class);
+ assertEvalEquals("null", "$!{null|'null'}");
+ assertEvalEquals("so what", "#set($spaces=' ')${spaces.trim()|'so what'}");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictCompareTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictCompareTestCase.java
new file mode 100644
index 00000000..5174f248
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictCompareTestCase.java
@@ -0,0 +1,76 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+/**
+ * Make sure exceptions are thrown for strict comparisons that
+ * cannot be compared.
+ */
+public class StrictCompareTestCase extends BaseTestCase
+{
+ public StrictCompareTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE);
+ context.put("NULL", null);
+ context.put("a", "abc");
+ context.put("b", 3);
+ context.put("TRUE", Boolean.TRUE);
+ }
+
+ public void testCompare()
+ {
+ assertVelocityEx("#if($a > $NULL)#end");
+ assertVelocityEx("#if($a < $NULL)#end");
+ assertVelocityEx("#if($a >= $NULL)#end");
+ assertVelocityEx("#if($a <= $NULL)#end");
+
+ assertVelocityEx("#if($NULL > $a)#end");
+ assertVelocityEx("#if($NULL < $a)#end");
+ assertVelocityEx("#if($NULL >= $a)#end");
+ assertVelocityEx("#if($NULL <= $a)#end");
+
+ assertVelocityEx("#if($NULL >= $NULL)#end");
+ assertVelocityEx("#if($a >= $b)#end");
+ assertVelocityEx("#if($a <= $b)#end");
+ assertVelocityEx("#if($a > $b)#end");
+ assertVelocityEx("#if($a < $b)#end");
+
+ assertVelocityEx("#if($a < 5)#end");
+ assertVelocityEx("#if($a > 5)#end");
+ }
+
+ /**
+ * Assert that we get a VelocityException when calling evaluate
+ */
+ public void assertVelocityEx(String template)
+ {
+ assertEvalException(template, VelocityException.class);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictEscapeTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictEscapeTestCase.java
new file mode 100644
index 00000000..b3ab04a6
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictEscapeTestCase.java
@@ -0,0 +1,139 @@
+package org.apache.velocity.test;
+/*
+ * 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.
+ */
+import org.apache.velocity.runtime.RuntimeConstants;
+
+
+/**
+ * Test Strict escape mode
+ * property: RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE set to true
+ */
+public class StrictEscapeTestCase extends BaseTestCase
+{
+ public StrictEscapeTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE, Boolean.TRUE);
+ context.put("pow", "bang");
+ context.put("NULL", null);
+ context.put("animal", new Animal());
+ // DEBUG = true;
+ }
+
+ public void testReferenceEscape()
+ {
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE);
+
+ assertEvalException("\\\\$bogus");
+ assertEvalException("\\\\\\\\$bogus");
+
+ assertEvalEquals("$bogus", "\\$bogus");
+ assertEvalEquals("$bogus.xyz", "\\$bogus.xyz");
+ assertEvalEquals("${bogus}", "\\${bogus}");
+ assertEvalEquals("${bogus.xyz}", "\\${bogus.xyz}");
+ assertEvalEquals("\\$bogus", "\\\\\\$bogus");
+ assertEvalEquals("\\xyz","#set($foo = \"xyz\")\\\\$foo");
+ assertEvalEquals("\\$foo","#set($foo = \"xyz\")\\\\\\$foo");
+ assertEvalEquals("$foo\\","#set($foo = \"xyz\")\\$foo\\");
+ assertEvalEquals("$pow", "#set($foo = \"\\$pow\")$foo");
+ assertEvalEquals("\\bang", "#set($foo = \"\\\\$pow\")$foo");
+ assertEvalEquals("\\$pow", "#set($foo = \"\\\\\\$pow\")$foo");
+
+ assertEvalEquals("\\$%", "\\$%");
+
+ // This should work but does not... may be related to VELOCITY-679
+ // This is broken from existing escape behavior
+ // assertEvalEquals("\\$bang", "\\$$pow");
+
+ assertEvalEquals("$!foo", "#set($foo = $NULL)\\$!foo");
+ assertEvalEquals("$!animal.null", "\\$!animal.null");
+ assertEvalEquals("$!animal", "\\$!animal");
+ }
+
+ public void testMacroEscape()
+ {
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE);
+ assertEvalException("\\\\#bogus()");
+
+ // define the foo macro
+ assertEvalEquals("", "#macro(foo)bar#end");
+
+ assertEvalEquals("#foo()", "\\#foo()");
+ assertEvalEquals("\\bar", "\\\\#foo()");
+ assertEvalEquals("\\#foo()", "\\\\\\#foo()");
+
+ assertEvalEquals("bar", "#set($abc = \"#foo()\")$abc");
+ assertEvalEquals("#foo()", "#set($abc = \"\\#foo()\")$abc");
+ assertEvalEquals("\\bar", "#set($abc = \"\\\\#foo()\")$abc");
+
+ assertEvalEquals("#@foo()", "\\#@foo()");
+ assertEvalEquals("#@foo", "\\#@foo");
+ assertEvalEquals("#@bar", "\\#@bar");
+ assertEvalEquals("\\bar", "\\\\#@foo()#end");
+ assertEvalEquals("#@foo()#end", "\\#@foo()\\#end");
+ assertEvalEquals("#@foo#end", "\\#@foo\\#end");
+ assertEvalEquals("#@bar #end", "\\#@bar \\#end");
+
+ assertEvalEquals("#end #foreach #define() #elseif", "\\#end \\#foreach \\#define() \\#elseif");
+ assertEvalEquals("#{end} #{foreach} #{define}() #{elseif}", "\\#{end} \\#{foreach} \\#{define}() \\#{elseif}");
+ assertEvalEquals("#macro(foo) #end", "\\#macro(foo) \\#end");
+
+ assertEvalException("\\\\#end");
+ assertEvalException("\\\\#if()");
+
+ // This should work but does not, probably related to VELOCITY-678
+ // this is broken from existing behavior
+ //assertEvalEquals("\\$bar", "\\$#foo()");
+ }
+
+ /**
+ * Tests for non strict-mode
+ */
+ public void testStrictMode()
+ {
+ assertEvalEquals("#bogus()", "\\#bogus()");
+ assertEvalEquals("\\#bogus", "\\\\#bogus");
+
+ assertEvalEquals("\\$bogus", "\\\\$bogus");
+ assertEvalEquals("\\\\$bogus", "\\\\\\\\$bogus");
+ assertEvalEquals("\\$bogus", "#set($foo = \"\\\\$bogus\")$foo");
+ }
+
+ /**
+ * Test object for escaping
+ */
+ public static class Animal
+ {
+ public Object getNull()
+ {
+ return null;
+ }
+
+ public String toString()
+ {
+ return null;
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictForeachTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictForeachTestCase.java
new file mode 100755
index 00000000..3632016b
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictForeachTestCase.java
@@ -0,0 +1,110 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * This class tests support for strict foreach mode.
+ */
+public class StrictForeachTestCase extends BaseTestCase
+{
+ public StrictForeachTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty(RuntimeConstants.SKIP_INVALID_ITERATOR, Boolean.FALSE);
+ context.put("good", new GoodIterable());
+ context.put("bad", new BadIterable());
+ context.put("ugly", new UglyIterable());
+ }
+
+ public void testGood()
+ {
+ try
+ {
+ evaluate("#foreach( $i in $good )$i#end");
+ }
+ catch (VelocityException ve)
+ {
+ fail("Doing #foreach on $good should not have exploded!");
+ }
+ }
+
+ public void testBad()
+ {
+ try
+ {
+ evaluate("#foreach( $i in $bad )$i#end");
+ fail("Doing #foreach on $bad should have exploded!");
+ }
+ catch (VelocityException ve)
+ {
+ // success!
+ }
+ }
+
+ public void testUgly()
+ {
+ try
+ {
+ evaluate("#foreach( $i in $ugly )$i#end");
+ fail("Doing #foreach on $ugly should have exploded!");
+ }
+ catch (VelocityException ve)
+ {
+ // success!
+ }
+ }
+
+
+ public static class GoodIterable
+ {
+ public Iterator iterator()
+ {
+ return new ArrayList().iterator();
+ }
+ }
+
+ public static class BadIterable
+ {
+ public Object iterator()
+ {
+ return new Object();
+ }
+ }
+
+ public static class UglyIterable
+ {
+ public Iterator iterator()
+ {
+ return null;
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictMathTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictMathTestCase.java
new file mode 100755
index 00000000..4515a899
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictMathTestCase.java
@@ -0,0 +1,86 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.exception.MathException;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+/**
+ * This class tests support for strict math mode.
+ */
+public class StrictMathTestCase extends BaseTestCase
+{
+ public StrictMathTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty(RuntimeConstants.STRICT_MATH, Boolean.TRUE);
+ context.put("num", 5);
+ context.put("zero", 0);
+ }
+
+ protected void assertNullMathEx(String operation)
+ {
+ String leftnull = "#set( $foo = $null "+operation+" $num )";
+ assertEvalException(leftnull, MathException.class);
+ String rightnull = "#set( $foo = $num "+operation+" $null )";
+ assertEvalException(rightnull, MathException.class);
+ }
+
+ protected void assertImaginaryMathEx(String operation)
+ {
+ String infinity = "#set( $foo = $num "+operation+" $zero )";
+ assertEvalException(infinity, MathException.class);
+ }
+
+
+ public void testAdd()
+ {
+ assertNullMathEx("+");
+ }
+
+ public void testSub()
+ {
+ assertNullMathEx("-");
+ }
+
+ public void testMul()
+ {
+ assertNullMathEx("*");
+ }
+
+ public void testMod()
+ {
+ assertNullMathEx("%");
+ assertImaginaryMathEx("%");
+ }
+
+ public void testDiv()
+ {
+ assertNullMathEx("/");
+ assertImaginaryMathEx("/");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictReferenceTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictReferenceTestCase.java
new file mode 100644
index 00000000..5a6b6d17
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictReferenceTestCase.java
@@ -0,0 +1,257 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+/**
+ * Test strict reference mode turned on by the velocity property
+ * runtime.references.strict
+ */
+public class StrictReferenceTestCase extends BaseTestCase
+{
+ public StrictReferenceTestCase(String name)
+ {
+ super(name);
+ }
+
+ // second engine to test WITH conversions
+ VelocityEngine engine2;
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+
+ /* first engine without conversions */
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE);
+ engine.setProperty(RuntimeConstants.CONVERSION_HANDLER_CLASS, "none");
+
+ /* second engine with conversions */
+ engine2 = createEngine();
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE);
+
+ context.put("NULL", null);
+ context.put("bar", null);
+ context.put("TRUE", Boolean.TRUE);
+ }
+
+ /**
+ * Test the modified behavior of #if in strict mode. Mainly, that
+ * single variables references in #if statements use non strict rules
+ */
+ public void testIfStatement()
+ {
+ Fargo fargo = new Fargo();
+ fargo.next = new Fargo();
+ context.put("fargo", fargo);
+ assertEvalEquals("", "#if($bogus)xxx#end");
+ assertEvalEquals("xxx", "#if($fargo)xxx#end");
+ assertEvalEquals("", "#if( ! $fargo)xxx#end");
+ assertEvalEquals("xxx", "#if($bogus || $fargo)xxx#end");
+ assertEvalEquals("", "#if($bogus && $fargo)xxx#end");
+ assertEvalEquals("", "#if($fargo != $NULL && $bogus)xxx#end");
+ assertEvalEquals("xxx", "#if($fargo == $NULL || ! $bogus)xxx#end");
+ assertEvalEquals("xxx", "#if(! $bogus1 && ! $bogus2)xxx#end");
+ assertEvalEquals("xxx", "#if($fargo.prop == \"propiness\" && ! $bogus && $bar == $NULL)xxx#end");
+ assertEvalEquals("", "#if($bogus && $bogus.foo)xxx#end");
+
+ assertMethodEx("#if($bogus.foo)#end");
+ assertMethodEx("#if(!$bogus.foo)#end");
+ }
+
+
+ /**
+ * We make sure that variables can actuall hold null
+ * values.
+ */
+ public void testAllowNullValues()
+ throws Exception
+ {
+ evaluate("$!bar");
+ assertEvalEquals("true", "#if($bar == $NULL)true#end");
+ assertEvalEquals("true", "#set($foobar = $NULL)#if($foobar == $NULL)true#end");
+ assertEvalEquals("13", "#set($list = [1, $NULL, 3])#foreach($item in $list)#if($item != $NULL)$item#end#end");
+ }
+
+ /**
+ * Test that variables references that have not been defined throw exceptions
+ */
+ public void testStrictVariableRef()
+ throws Exception
+ {
+ // We expect a Method exception on the following
+ assertMethodEx("$bogus");
+ assertMethodEx("#macro(test)$bogus#end #test()");
+
+ assertMethodEx("#set($bar = $bogus)");
+
+ assertMethodEx("#if($bogus == \"bar\") #end");
+ assertMethodEx("#if($bogus != \"bar\") #end");
+ assertMethodEx("#if(\"bar\" == $bogus) #end");
+ assertMethodEx("#if($bogus > 1) #end");
+ assertMethodEx("#foreach($item in $bogus)#end");
+
+ // make sure no exceptions are thrown here
+ evaluate("#set($foo = \"bar\") $foo");
+ evaluate("#macro(test1 $foo1) $foo1 #end #test1(\"junk\")");
+ evaluate("#macro(test2) #set($foo2 = \"bar\") $foo2 #end #test2()");
+ }
+
+ /**
+ * Test that exceptions are thrown when methods are called on
+ * references that contains objects that do not contains those
+ * methods.
+ */
+ public void testStrictMethodRef()
+ {
+ Fargo fargo = new Fargo();
+ fargo.next = new Fargo();
+ context.put("fargo", fargo);
+
+ // Mainly want to make sure no exceptions are thrown here
+ assertEvalEquals("propiness", "$fargo.prop");
+ assertEvalEquals("", "$!fargo.nullVal");
+ assertEvalEquals("propiness", "$fargo.next.prop");
+
+ assertMethodEx("$fargo.foobar");
+ assertMethodEx("$fargo.next.foobar");
+ assertMethodEx("$fargo.foobar()");
+ assertMethodEx("#set($fargo.next.prop = $TRUE)");
+ assertMethodEx("$fargo.next.setProp($TRUE)");
+ }
+
+ /**
+ * Make sure exceptions are thrown when when we attempt to call
+ * methods on null values.
+ */
+ public void testStrictMethodOnNull()
+ {
+ Fargo fargo = new Fargo();
+ fargo.next = new Fargo();
+ context.put("fargo", fargo);
+
+ assertVelocityEx("$NULL.bogus");
+ assertVelocityEx("$fargo.nullVal.bogus");
+ assertVelocityEx("$fargo.next.nullVal.bogus");
+ assertVelocityEx("#if (\"junk\" == $fargo.nullVal.bogus)#end");
+ assertVelocityEx("#if ($fargo.nullVal.bogus > 2)#end");
+ assertVelocityEx("#set($fargo.next.nullVal.bogus = \"junk\")");
+ assertVelocityEx("#set($foo = $NULL.bogus)");
+ assertVelocityEx("#foreach($item in $fargo.next.nullVal.bogus)#end");
+
+ evaluate("$fargo.prop.toString()");
+ assertVelocityEx("#set($fargo.prop = $NULL)$fargo.prop.next");
+
+ // make sure no exceptions are thrown here
+ evaluate("$!fargo.next.next");
+ evaluate("$!fargo.next.nullVal");
+ evaluate("#foreach($item in $fargo.nullVal)#end");
+ }
+
+ /**
+ * Make sure undefined macros throw exceptions
+ */
+ public void testMacros()
+ {
+ assertVelocityEx("#bogus()");
+ assertVelocityEx("#bogus ( )");
+ assertVelocityEx("#bogus( $a )");
+ assertVelocityEx("abc#bogus ( $a )a ");
+
+ assertEvalEquals(" true ", "#macro(test1) true #end#test1()");
+ assertEvalEquals(" true ", "#macro(test2 $a) $a #end#test2 ( \"true\")");
+ assertEvalEquals("#CCFFEE", "#CCFFEE");
+ assertEvalEquals("#F - ()", "#F - ()");
+ assertEvalEquals("#F{}", "#F{}");
+ }
+
+
+ public void testRenderingNull()
+ {
+ Fargo fargo = new Fargo();
+ fargo.next = new Fargo();
+ context.put("fargo", fargo);
+
+ assertVelocityEx("#set($foo = $NULL)$foo");
+ assertEvalEquals("", "#set($foo = $NULL)$!foo");
+ assertVelocityEx("$fargo.nullVal");
+ assertEvalEquals("", "$!fargo.nullVal");
+ assertVelocityEx("$fargo.next.next");
+ assertEvalEquals("", "$!fargo.next.next");
+ assertVelocityEx("$fargo.next.nullVal");
+ assertEvalEquals("", "$!fargo.next.nullVal");
+ }
+
+ /**
+ * Assert that we get a MethodInvocationException when calling evaluate
+ */
+ public void assertMethodEx(String template)
+ {
+ assertEvalException(template, MethodInvocationException.class);
+ }
+
+ /**
+ * Assert that we get a VelocityException when calling evaluate
+ */
+ public void assertVelocityEx(String template)
+ {
+ assertEvalException(template, VelocityException.class);
+ }
+
+ /**
+ * Assert that we get a MethodInvocationException when calling evaluate
+ */
+ public void assertParseEx(String template)
+ {
+ assertEvalException(template, ParseErrorException.class);
+ }
+
+
+ public static class Fargo
+ {
+ String prop = "propiness";
+ Fargo next = null;
+
+ public String getProp()
+ {
+ return prop;
+ }
+
+ public void setProp(String val)
+ {
+ this.prop = val;
+ }
+
+ public String getNullVal()
+ {
+ return null;
+ }
+
+ public Fargo getNext()
+ {
+ return next;
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StringConcatenationTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StringConcatenationTestCase.java
new file mode 100755
index 00000000..f86e06c4
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StringConcatenationTestCase.java
@@ -0,0 +1,66 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+
+/**
+ * This class tests support for string concatenation.
+ */
+public class StringConcatenationTestCase extends BaseTestCase
+{
+ public StringConcatenationTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ context.put("foo", "foo");
+ context.put("baz", "baz");
+ }
+
+ public void testStringRefLeft()
+ {
+ assertEvalEquals("foobar", "#set( $o = $foo + 'bar' )$o");
+ assertEvalEquals("foo$bar", "#set( $o = $foo + $bar )$o");
+ assertEvalEquals("foo1", "#set( $o = $foo + 1 )$o");
+ assertEvalEquals("foobaz", "#set( $o = $foo + $baz )$o");
+ }
+
+ public void testStringRefRight()
+ {
+ assertEvalEquals("barfoo", "#set( $o = 'bar' + $foo )$o");
+ assertEvalEquals("$barfoo", "#set( $o = $bar + $foo )$o");
+ assertEvalEquals("1foo", "#set( $o = 1 + $foo )$o");
+ }
+
+ public void testNoRef()
+ {
+ assertEvalEquals("bar1", "#set( $o = 'bar' + 1 )$o");
+ }
+
+ public void testAll()
+ {
+ assertEvalEquals("foobar$bar1baz", "#set( $o = $foo + 'bar' + $bar + 1 + $baz )$o");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StringResourceLoaderRepositoryTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StringResourceLoaderRepositoryTestCase.java
new file mode 100644
index 00000000..23aee4ce
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StringResourceLoaderRepositoryTestCase.java
@@ -0,0 +1,214 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestCase;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.runtime.resource.util.StringResourceRepository;
+import org.apache.velocity.runtime.resource.util.StringResourceRepositoryImpl;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.StringWriter;
+
+/**
+ * Tests ability to have multiple repositories in the same app.
+ *
+ * @author Nathan Bubna
+ * @version $Id: StringResourceLoaderRepositoryTestCase.java 479058 2006-11-25 00:26:32Z henning $
+ */
+public class StringResourceLoaderRepositoryTestCase extends TestCase
+{
+ private VelocityContext context;
+
+ public StringResourceLoaderRepositoryTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ Velocity.reset();
+ Velocity.setProperty(Velocity.RESOURCE_LOADERS, "string");
+ Velocity.addProperty("string.resource.loader.class", StringResourceLoader.class.getName());
+ Velocity.addProperty("string.resource.loader.modificationCheckInterval", "1");
+ Velocity.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+ Velocity.init();
+
+ StringResourceRepository repo = getRepo(null, null);
+ repo.putStringResource("foo", "This is $foo");
+ repo.putStringResource("bar", "This is $bar");
+
+ context = new VelocityContext();
+ context.put("foo", "wonderful!");
+ context.put("bar", "horrible!");
+ context.put("woogie", "a woogie");
+ }
+
+
+ protected VelocityEngine newStringEngine(String repoName, boolean isStatic)
+ {
+ VelocityEngine engine = new VelocityEngine();
+ TestLogger logger = new TestLogger();
+ engine.setProperty(Velocity.RESOURCE_LOADERS, "string");
+ engine.addProperty("string.resource.loader.class", StringResourceLoader.class.getName());
+ if (repoName != null)
+ {
+ engine.addProperty("string.resource.loader.repository.name", repoName);
+ }
+ if (!isStatic)
+ {
+ engine.addProperty("string.resource.loader.repository.static", "false");
+ }
+ engine.addProperty("string.resource.loader.modificationCheckInterval", "1");
+ engine.setProperty(Velocity.RUNTIME_LOG_INSTANCE, logger);
+ return engine;
+ }
+
+ protected StringResourceRepository getRepo(String name, VelocityEngine engine)
+ {
+ if (engine == null)
+ {
+ if (name == null)
+ {
+ return StringResourceLoader.getRepository();
+ }
+ else
+ {
+ return StringResourceLoader.getRepository(name);
+ }
+ }
+ else
+ {
+ if (name == null)
+ {
+ return (StringResourceRepository)engine.getApplicationAttribute(StringResourceLoader.REPOSITORY_NAME_DEFAULT);
+ }
+ else
+ {
+ return (StringResourceRepository)engine.getApplicationAttribute(name);
+ }
+ }
+ }
+
+ protected String render(Template template) throws Exception
+ {
+ StringWriter out = new StringWriter();
+ template.merge(this.context, out);
+ return out.toString();
+ }
+
+
+ public void testSharedRepo() throws Exception
+ {
+ // this engine's string resource loader should share a repository
+ // with the singleton's string resource loader
+ VelocityEngine engine = newStringEngine(null, true);
+
+ // get and merge the same template from both runtimes with the same context
+ String engineOut = render(engine.getTemplate("foo"));
+ String singletonOut = render(RuntimeSingleton.getTemplate("foo"));
+
+ // make sure they're equal
+ assertEquals(engineOut, singletonOut);
+ }
+
+ public void testAlternateStaticRepo() throws Exception
+ {
+ VelocityEngine engine = newStringEngine("alternate.repo", true);
+ // should be null be for init
+ StringResourceRepository repo = getRepo("alternate.repo", null);
+ assertNull(repo);
+ engine.init();
+ // and not null after init
+ repo = getRepo("alternate.repo", null);
+ assertNotNull(repo);
+ repo.putStringResource("foo", "This is NOT $foo");
+
+ // get and merge template with the same name from both runtimes with the same context
+ String engineOut = render(engine.getTemplate("foo"));
+ String singletonOut = render(RuntimeSingleton.getTemplate("foo"));
+
+ // make sure they're NOT equal
+ assertFalse(engineOut.equals(singletonOut));
+ }
+
+ public void testPreCreatedStaticRepo() throws Exception
+ {
+ VelocityEngine engine = newStringEngine("my.repo", true);
+ MyRepo repo = new MyRepo();
+ repo.put("bar", "This is NOT $bar");
+ StringResourceLoader.setRepository("my.repo", repo);
+
+ String out = render(engine.getTemplate("bar"));
+ assertEquals(out, "This is NOT horrible!");
+ }
+
+ public void testAppRepo() throws Exception
+ {
+ VelocityEngine engine = newStringEngine(null, false);
+ engine.init();
+
+ StringResourceRepository repo = getRepo(null, engine);
+ assertNotNull(repo);
+ repo.putStringResource("woogie", "What is $woogie?");
+
+ String out = render(engine.getTemplate("woogie"));
+ assertEquals(out, "What is a woogie?");
+ }
+
+ public void testAlternateAppRepo() throws Exception
+ {
+ VelocityEngine engine = newStringEngine("alternate.app.repo", false);
+ engine.init();
+
+ StringResourceRepository repo = getRepo("alternate.app.repo", engine);
+ assertNotNull(repo);
+ repo.putStringResource("you/foo.vm", "You look $foo");
+
+ String out = render(engine.getTemplate("you/foo.vm"));
+ assertEquals(out, "You look wonderful!");
+ }
+
+ public void testPreCreatedAppRepo() throws Exception
+ {
+ VelocityEngine engine = newStringEngine("my.app.repo", false);
+ MyRepo repo = new MyRepo();
+ repo.put("you/bar.vm", "You look $bar");
+ engine.setApplicationAttribute("my.app.repo", repo);
+
+ String out = render(engine.getTemplate("you/bar.vm"));
+ assertEquals(out, "You look horrible!");
+ }
+
+ public static class MyRepo extends StringResourceRepositoryImpl
+ {
+ public void put(String name, String template)
+ {
+ putStringResource(name, template);
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/StringResourceLoaderTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/StringResourceLoaderTestCase.java
new file mode 100644
index 00000000..69c09bb5
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/StringResourceLoaderTestCase.java
@@ -0,0 +1,209 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * Multiple paths in the file resource loader.
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @version $Id$
+ */
+public class StringResourceLoaderTestCase extends BaseTestCase
+{
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/stringloader";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/stringloader/compare";
+
+ /**
+ * Default constructor.
+ */
+ public StringResourceLoaderTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(StringResourceLoaderTestCase.class);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ Velocity.reset();
+
+ Velocity.setProperty(Velocity.RESOURCE_LOADERS, "string");
+ Velocity.addProperty("string.resource.loader.class", StringResourceLoader.class.getName());
+ Velocity.addProperty("string.resource.loader.modificationCheckInterval", "1");
+
+ // Silence the logger.
+ Velocity.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ }
+
+ public void testSimpleTemplate()
+ throws Exception
+ {
+ StringResourceLoader.getRepository().putStringResource("simpletemplate.vm", "This is a test for ${foo}");
+
+ Template template = RuntimeSingleton.getTemplate(getFileName(null, "simpletemplate", TMPL_FILE_EXT));
+
+ FileOutputStream fos =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "simpletemplate", RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ VelocityContext context = new VelocityContext();
+ context.put("foo", "a foo object");
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "simpletemplate",
+ RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect.");
+ }
+ }
+
+ public void testMultipleTemplates()
+ throws Exception
+ {
+ StringResourceLoader.getRepository().putStringResource("multi1.vm", "I am the $first template.");
+ StringResourceLoader.getRepository().putStringResource("multi2.vm", "I am the $second template.");
+
+ Template template1 = RuntimeSingleton.getTemplate(getFileName(null, "multi1", TMPL_FILE_EXT));
+
+ FileOutputStream fos =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "multi1", RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ VelocityContext context = new VelocityContext();
+ context.put("first", 1);
+ context.put("second", "two");
+
+ template1.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ Template template2 = RuntimeSingleton.getTemplate(getFileName(null, "multi2", TMPL_FILE_EXT));
+
+ fos = new FileOutputStream (
+ getFileName(RESULTS_DIR, "multi2", RESULT_FILE_EXT));
+
+ writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ template2.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "multi1",
+ RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Template 1 incorrect.");
+ }
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "multi2",
+ RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Template 2 incorrect.");
+ }
+ }
+
+ public void testContentChange()
+ throws Exception
+ {
+ StringResourceLoader.getRepository().putStringResource("change.vm", "I am the $first template.");
+
+ Template template = RuntimeSingleton.getTemplate(getFileName(null, "change", TMPL_FILE_EXT));
+
+ FileOutputStream fos =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, "change1", RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ VelocityContext context = new VelocityContext();
+ context.put("first", 1);
+ context.put("second", "two");
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ StringResourceLoader.getRepository().putStringResource("change.vm", "I am the $second template.");
+ Thread.sleep(2000L);
+ template = RuntimeSingleton.getTemplate(getFileName(null, "change", TMPL_FILE_EXT));
+
+ fos = new FileOutputStream (
+ getFileName(RESULTS_DIR, "change2", RESULT_FILE_EXT));
+
+ writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "change1",
+ RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Template 1 incorrect.");
+ }
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, "change2",
+ RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Template 2 incorrect.");
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestBase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestBase.java
new file mode 100644
index 00000000..e35e00d7
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestBase.java
@@ -0,0 +1,84 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+/**
+ * This is a base interface that contains a bunch of static final
+ * strings that are of use when testing templates.
+ *
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @version $Id$
+ */
+public interface TemplateTestBase
+{
+ /**
+ * Directory relative to the distribution root, where the
+ * values to compare test results to are stored.
+ */
+ String TEST_COMPARE_DIR = System.getProperty("test.compare.dir");
+
+ /**
+ * Directory relative to the distribution root, where the
+ * test cases should put their output
+ */
+ String TEST_RESULT_DIR = System.getProperty("test.result.dir");
+
+
+ /**
+ * VTL file extension.
+ */
+ String TMPL_FILE_EXT = "vm";
+
+ /**
+ * Comparison file extension.
+ */
+ String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path for templates. This property will override the
+ * value in the default velocity properties file.
+ */
+ String FILE_RESOURCE_LOADER_PATH =
+ TEST_COMPARE_DIR + "/templates";
+
+ /**
+ * Properties file that lists which template tests to run.
+ */
+ String TEST_CASE_PROPERTIES =
+ FILE_RESOURCE_LOADER_PATH + "/templates.properties";
+
+ /**
+ * Results relative to the build directory.
+ */
+ String RESULT_DIR =
+ TEST_RESULT_DIR + "/templates";
+
+ /**
+ * Results relative to the build directory.
+ */
+ String COMPARE_DIR =
+ FILE_RESOURCE_LOADER_PATH + "/compare";
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestCase.java
new file mode 100644
index 00000000..99a4eacf
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestCase.java
@@ -0,0 +1,237 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.FieldMethodizer;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.test.provider.BoolObj;
+import org.apache.velocity.test.provider.NullToStringObject;
+import org.apache.velocity.test.provider.TestNumber;
+import org.apache.velocity.test.provider.TestProvider;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * Easily add test cases which evaluate templates and check their output.
+ *
+ * NOTE:
+ * This class DOES NOT extend RuntimeTestCase because the TemplateTestSuite
+ * already initializes the Velocity runtime and adds the template
+ * test cases. Having this class extend RuntimeTestCase causes the
+ * Runtime to be initialized twice which is not good. I only discovered
+ * this after a couple hours of wondering why all the properties
+ * being setup were ending up as Vectors. At first I thought it
+ * was a problem with the Configuration class, but the Runtime
+ * was being initialized twice: so the first time the property
+ * is seen it's stored as a String, the second time it's seen
+ * the Configuration class makes a Vector with both Strings.
+ * As a result all the getBoolean(property) calls were failing because
+ * the Configurations class was trying to create a Boolean from
+ * a Vector which doesn't really work that well. I have learned
+ * my lesson and now have to add some code to make sure the
+ * Runtime isn't initialized more then once :-)
+ *
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @version $Id$
+ */
+public class TemplateTestCase extends BaseTestCase implements TemplateTestBase
+{
+ /**
+ * The base file name of the template and comparison file (i.e. array for
+ * array.vm and array.cmp).
+ */
+ protected String baseFileName;
+
+ private TestProvider provider;
+ private ArrayList al;
+ private Hashtable h;
+ private VelocityContext context;
+ private VelocityContext context1;
+ private VelocityContext context2;
+ private Vector vec;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param baseFileName The base name of the template and comparison file to
+ * use (i.e. array for array.vm and array.cmp).
+ */
+ public TemplateTestCase (String baseFileName)
+ {
+ super(getTestCaseName(baseFileName));
+ this.baseFileName = baseFileName;
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new TemplateTestSuite();
+ }
+
+ /**
+ * Sets up the test.
+ */
+ @Override
+ protected void setUp ()
+ throws Exception
+ {
+ super.setUp();
+ Velocity.reset();
+ Velocity.setProperty(
+ Velocity.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.setProperty("space.gobbling", "bc");
+
+ Velocity.init();
+
+ provider = new TestProvider();
+ al = provider.getCustomers();
+ h = new Hashtable();
+
+ h.put("Bar", "this is from a hashtable!");
+ h.put("Foo", "this is from a hashtable too!");
+
+ /*
+ * lets set up a vector of objects to test late introspection. See ASTMethod.java
+ */
+
+ vec = new Vector();
+
+ vec.addElement(new String("string1"));
+ vec.addElement(new String("string2"));
+
+ /*
+ * set up 3 chained contexts, and add our data
+ * throught the 3 of them.
+ */
+
+ context2 = new VelocityContext();
+ context1 = new VelocityContext( context2 );
+ context = new VelocityContext( context1 );
+
+ context.put("provider", provider);
+ context1.put("name", "jason");
+ context1.put("name2", new StringBuffer("jason"));
+ context1.put("name3", new StringBuffer("geoge"));
+ context2.put("providers", provider.getCustomers2());
+ context.put("list", al);
+ context1.put("hashtable", h);
+ context2.put("hashmap", new HashMap());
+ context2.put("search", provider.getSearch());
+ context.put("relatedSearches", provider.getRelSearches());
+ context1.put("searchResults", provider.getRelSearches());
+ context2.put("stringarray", provider.getArray());
+ context.put("vector", vec );
+ context.put("mystring", new String());
+ context.put("runtime", new FieldMethodizer( "org.apache.velocity.runtime.RuntimeSingleton" ));
+ context.put("fmprov", new FieldMethodizer( provider ));
+ context.put("Floog", "floogie woogie");
+ context.put("boolobj", new BoolObj() );
+
+ /*
+ * we want to make sure we test all types of iterative objects
+ * in #foreach()
+ */
+
+ Object[] oarr = { "a","b","c","d" } ;
+ int intarr[] = { 10, 20, 30, 40, 50 };
+ int emptyarr[] = {};
+
+ context.put( "collection", vec );
+ context2.put("iterator", vec.iterator());
+ context1.put("map", h );
+ context.put("obarr", oarr );
+ context.put("enumerator", vec.elements());
+ context.put("intarr", intarr );
+ context.put("emptyarr", emptyarr );
+
+ // Add some Numbers
+ context.put ("int1", 1000);
+ context.put ("long1", 10000000000l);
+ context.put ("float1", 1000.1234f);
+ context.put ("double1", 10000000000d);
+
+ // Add a TemplateNumber
+ context.put ("templatenumber1", new TestNumber (999.125));
+
+ /*
+ * Test #foreach() with a list containing nulls
+ */
+ ArrayList nullList = new ArrayList();
+ nullList.add("a");
+ nullList.add("b");
+ nullList.add(null);
+ nullList.add("d");
+ context.put("nullList", nullList);
+
+ // test silent references with a null tostring
+ context.put("nullToString",new NullToStringObject());
+ }
+
+ /**
+ * Runs the test.
+ */
+ @Override
+ public void runTest ()
+ throws Exception
+ {
+ Template template = RuntimeSingleton.getTemplate
+ (getFileName(null, baseFileName, TMPL_FILE_EXT));
+
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ /* get the file to write to */
+ FileOutputStream fos =
+ new FileOutputStream (getFileName(
+ RESULT_DIR, baseFileName, RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ /* process the template */
+ template.merge( context, writer);
+
+ /* close the file */
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR,COMPARE_DIR,baseFileName,
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Processed template "+getFileName(
+ RESULT_DIR, baseFileName, RESULT_FILE_EXT)+" did not match expected output");
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestSuite.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestSuite.java
new file mode 100644
index 00000000..36c46951
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/TemplateTestSuite.java
@@ -0,0 +1,95 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestSuite;
+
+import java.io.FileInputStream;
+import java.util.Properties;
+
+/**
+ * Test suite for Templates.
+ *
+ * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @version $Id$
+ */
+public class TemplateTestSuite extends TestSuite implements TemplateTestBase
+{
+ private Properties testProperties;
+
+ /**
+ * Creates an instace of the Apache Velocity test suite.
+ */
+ public TemplateTestSuite()
+ {
+ try
+ {
+ testProperties = new Properties();
+ testProperties.load(new FileInputStream(TEST_CASE_PROPERTIES));
+ }
+ catch (Exception e)
+ {
+ System.err.println("Cannot setup TemplateTestSuite!");
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ addTemplateTestCases();
+ }
+
+ /**
+ * Adds the template test cases to run to this test suite. Template test
+ * cases are listed in the <code>TEST_CASE_PROPERTIES</code> file.
+ */
+ private void addTemplateTestCases()
+ {
+ String template;
+ for (int i = 1 ;; i++)
+ {
+ template = testProperties.getProperty(getTemplateTestKey(i));
+
+ if (template != null)
+ {
+ System.out.println("Adding TemplateTestCase : " + template);
+ addTest(new TemplateTestCase(template));
+ }
+ else
+ {
+ // Assume we're done adding template test cases.
+ break;
+ }
+ }
+ }
+
+ /**
+ * Macro which returns the properties file key for the specified template
+ * test number.
+ *
+ * @param nbr The template test number to return a property key for.
+ * @return The property key.
+ */
+ private static String getTemplateTestKey(int nbr)
+ {
+ return ("test.template." + nbr);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/TestBaseTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/TestBaseTestCase.java
new file mode 100644
index 00000000..ee4afe7c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/TestBaseTestCase.java
@@ -0,0 +1,64 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import java.io.File;
+
+/**
+ * I keep breaking the getFileName method all the time...
+ */
+public class TestBaseTestCase
+ extends BaseTestCase
+{
+ public TestBaseTestCase(final String name)
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(TestBaseTestCase.class);
+ }
+
+ public void testGetFileName()
+ throws Exception
+ {
+ String fs = System.getProperty("file.separator");
+ String pwd = System.getProperty("user.dir");
+
+ String root = new File("/").getCanonicalPath();
+
+ assertEquals(pwd + fs + "baz" + fs + "foo.bar", getFileName("baz", "foo", "bar"));
+ assertEquals(root + "baz" + fs + "foo.bar", getFileName(root + "baz", "foo", "bar"));
+ assertEquals(root + "foo.bar", getFileName("baz", root + "foo", "bar"));
+ assertEquals(root + "foo.bar", getFileName(root + "baz", root + "foo", "bar"));
+ assertEquals("", getFileName(null, "", ""));
+ assertEquals(root + "", getFileName("", "", ""));
+ assertEquals(".x", getFileName(null, "", "x"));
+ assertEquals(root + ".x", getFileName("", "", "x"));
+ assertEquals("foo.bar", getFileName(null, "foo", "bar"));
+ assertEquals(root + "foo.bar", getFileName(null, root + "foo", "bar"));
+ assertEquals(root + "foo.bar", getFileName("", "foo", "bar"));
+ assertEquals(root + "foo.bar", getFileName("", root + "foo", "bar"));
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/TextblockTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/TextblockTestCase.java
new file mode 100644
index 00000000..fc042544
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/TextblockTestCase.java
@@ -0,0 +1,163 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeInstance;
+import org.apache.velocity.runtime.parser.Parser;
+import org.apache.velocity.runtime.parser.node.ASTTextblock;
+
+import java.lang.reflect.Field;
+
+/**
+ * This class tests the Textblock directive.
+ */
+public class TextblockTestCase extends BaseTestCase
+{
+ // these are all here so that the test case adapts instantly
+ // to changes in the textblock start/end sequences
+ private String START = null;
+ private String END = null;
+ private String PARTIAL_START = null;
+ private String PARTIAL_END = null;
+ private String END_OF_START = null;
+ private String START_OF_END = null;
+
+ public TextblockTestCase(String name)
+ {
+ super(name);
+ //DEBUG = true;
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ // get a valid parser instance to initialize string constants
+ Field riField = engine.getClass().getDeclaredField("ri");
+ riField.setAccessible(true);
+ RuntimeInstance ri = (RuntimeInstance)riField.get(engine);
+ Parser parser = ri.createNewParser();
+ ASTTextblock astTextblock = new ASTTextblock(parser, 0);
+ START = astTextblock.START;
+ END = astTextblock.END;
+ PARTIAL_START = START.substring(0, START.length() - 1);
+ PARTIAL_END = END.substring(1);
+ END_OF_START = START.substring(START.length() - 1);
+ START_OF_END = END.substring(0, 1);
+ }
+
+ public String textblock(String s)
+ {
+ return START + s + END;
+ }
+
+ public void assertTextblockEvalEquals(String s) throws Exception
+ {
+ assertEvalEquals(s, textblock(s));
+ }
+
+ /**
+ * https://issues.apache.org/jira/browse/VELOCITY-661
+ */
+ public void testTextblockAjaxcode() throws Exception
+ {
+ String s = "var myId = 'someID';$('#test).append($.template('<div id=\"${myId}\"></div>').apply({myId: myId}));";
+ assertEvalEquals(s + " 123", textblock(s)+" #foreach($i in [1..3])$i#end");
+ }
+
+ public void testLooseTextblockEnd() throws Exception
+ {
+ // just like a multi-line comment end (*#), this must be
+ // followed by a character. by itself, it bombs for some reason.
+ assertEvalEquals(END+" ", END+" ");
+ }
+
+ public void testTextblockStartInTextblock() throws Exception
+ {
+ assertTextblockEvalEquals(START);
+ }
+
+ public void testTextblockEndBetweenTwoTextblockHalves() throws Exception
+ {
+ // just like a multi-line comment end (*#), the end token
+ // in the middle must be followed by some character.
+ // by itself, it bombs. not sure why that is, but the
+ // same has been true of multi-line comments without complaints,
+ // so i'm not going to worry about it just yet.
+ assertEvalEquals(" "+END+" ", textblock(" ")+END+" "+textblock(" "));
+ }
+
+ public void testZerolengthTextblock() throws Exception
+ {
+ assertTextblockEvalEquals("");
+ }
+
+ public void testTextblockInsideForeachLoop() throws Exception
+ {
+ String s = "var myId = 'someID';$('#test).append($.template('<div id=\"${myId}\"></div>').apply({myId: myId}));";
+ assertEvalEquals("1 "+s+"2 "+s+"3 "+s, "#foreach($i in [1..3])$i "+ textblock(s) + "#end");
+ }
+
+ public void testSingleHashInsideTextblock() throws Exception
+ {
+ assertTextblockEvalEquals(" # ");
+ }
+
+ public void testDollarInsideTextblock() throws Exception
+ {
+ assertTextblockEvalEquals("$");
+ }
+
+ public void testTextblockInsideComment() throws Exception
+ {
+ String s = "FOOBAR";
+ assertEvalEquals("", "#* comment "+textblock(s) + " *#");
+ }
+
+ public void testPartialStartEndTokensInsideTextblock() throws Exception
+ {
+ assertTextblockEvalEquals(PARTIAL_START+"foo"+PARTIAL_END);
+ }
+
+ public void testDupeTokenChars() throws Exception
+ {
+ assertTextblockEvalEquals(END_OF_START+START_OF_END);
+ assertTextblockEvalEquals(END_OF_START+END_OF_START+START_OF_END+START_OF_END);
+ assertTextblockEvalEquals(END_OF_START+END_OF_START+"#"+START_OF_END+START_OF_END);
+ }
+
+ /**
+ * https://issues.apache.org/jira/browse/VELOCITY-584
+ */
+ public void testServerSideIncludeEscaping() throws Exception
+ {
+ assertTextblockEvalEquals("<!--#include file=\"wisdom.inc\"--> ");
+ }
+
+ /**
+ * https://issues.apache.org/jira/browse/VELOCITY-676
+ */
+ public void testLineCommentInsideTextblock() throws Exception
+ {
+ assertTextblockEvalEquals("##x");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/URLResourceLoaderTimeoutTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/URLResourceLoaderTimeoutTestCase.java
new file mode 100755
index 00000000..775a75b3
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/URLResourceLoaderTimeoutTestCase.java
@@ -0,0 +1,82 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.URLResourceLoader;
+import org.apache.velocity.test.misc.TestLogger;
+
+/**
+ * This class tests support for custom timeouts in URLResourceLoader.
+ */
+public class URLResourceLoaderTimeoutTestCase extends BaseTestCase
+{
+ private static boolean isJava5plus;
+ static
+ {
+ try
+ {
+ Class.forName("java.lang.annotation.Annotation");
+ isJava5plus = true;
+ }
+ catch (ClassNotFoundException cnfe)
+ {
+ isJava5plus = false;
+ }
+ }
+ private TestLogger logger = new TestLogger();
+ private URLResourceLoader loader = new URLResourceLoader();
+ private int timeout = 2000;
+
+ public URLResourceLoaderTimeoutTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty("resource.loader", "url");
+ engine.setProperty("url.resource.loader.instance", loader);
+ engine.setProperty("url.resource.loader.timeout", timeout);
+
+ // actual instance of logger
+ logger.on();
+ engine.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, logger);
+ engine.setProperty("runtime.log.logsystem.test.level", "debug");
+ engine.init();
+ }
+
+ public void testTimeout()
+ {
+ if (isJava5plus)
+ {
+ System.out.println("Testing a 1.5+ JDK");
+ assertEquals(timeout, loader.getTimeout());
+ }
+ else
+ {
+ System.out.println("Testing a pre-1.5 JDK");
+ assertEquals(-1, loader.getTimeout());
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/UberspectorTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/UberspectorTestCase.java
new file mode 100644
index 00000000..4e243c3b
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/UberspectorTestCase.java
@@ -0,0 +1,300 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.runtime.RuntimeInstance;
+import org.apache.velocity.test.misc.GetPutObject;
+import org.apache.velocity.test.misc.UberspectorTestObject;
+import org.apache.velocity.util.introspection.Uberspect;
+import org.apache.velocity.util.introspection.VelPropertyGet;
+import org.apache.velocity.util.introspection.VelPropertySet;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class UberspectorTestCase
+ extends BaseTestCase
+{
+ private RuntimeInstance ri;
+
+ public UberspectorTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(UberspectorTestCase.class);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ ri = new RuntimeInstance();
+ ri.init();
+ }
+
+ public void testNullObjects()
+ throws Exception
+ {
+ // How about some null objects... Gee, I'm mean. ;-)
+ Uberspect u = ri.getUberspect();
+
+ VelPropertyGet getter = u.getPropertyGet(null, "foo", null);
+ assertNull(getter);
+
+ VelPropertySet setter = u.getPropertySet(null, "foo", Object.class, null);
+ assertNull(setter);
+ }
+
+ public void testEmptyPropertyGetter()
+ throws Exception
+ {
+ Uberspect u = ri.getUberspect();
+ Map map = new HashMap();
+
+ VelPropertyGet getter = u.getPropertyGet(map, "", null);
+
+ // Don't screw up on empty properties. That should map to get("")
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "get", getter.getMethodName());
+ }
+
+ public void testEmptyPropertySetter()
+ throws Exception
+ {
+ Uberspect u = ri.getUberspect();
+ Map map = new HashMap();
+
+ VelPropertySet setter = u.getPropertySet(map, "", Object.class, null);
+
+ // Don't screw up on empty properties. That should map to put("", Object)
+ assertNotNull(setter);
+ assertEquals("Found wrong method", "put", setter.getMethodName());
+ }
+
+ public void testNullPropertyGetter()
+ throws Exception
+ {
+ Uberspect u = ri.getUberspect();
+ GetPutObject gpo = new GetPutObject();
+ Map map = new HashMap();
+
+ VelPropertyGet getter = u.getPropertyGet(gpo, null, null);
+
+ // Don't screw up on null properties. That should map to get() on the GPO.
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "get", getter.getMethodName());
+
+ // And should be null on a Map which does not have a get()
+ getter = u.getPropertyGet(map, null, null);
+ assertNull(getter);
+
+ }
+
+ public void testNullPropertySetter()
+ throws Exception
+ {
+ Uberspect u = ri.getUberspect();
+ GetPutObject gpo = new GetPutObject();
+ Map map = new HashMap();
+
+ // Don't screw up on null properties. That should map to put() on the GPO.
+ VelPropertySet setter = u.getPropertySet(gpo, null, "", null);
+ assertNotNull(setter);
+ assertEquals("Found wrong method", "put", setter.getMethodName());
+
+ // And should be null on a Map which does not have a put()
+ setter = u.getPropertySet(map, null, "", null);
+ assertNull(setter);
+ }
+
+ public void testNullParameterType()
+ throws Exception
+ {
+ VelPropertySet setter;
+
+ Uberspect u = ri.getUberspect();
+ UberspectorTestObject uto = new UberspectorTestObject();
+
+ // setRegular()
+ setter = u.getPropertySet(uto, "Regular", null, null);
+ assertNotNull(setter);
+ assertEquals("Found wrong method", "setRegular", setter.getMethodName());
+
+ // setAmbigous() - String and StringBuffer available
+ setter = u.getPropertySet(uto, "Ambigous", null, null);
+ assertNull(setter);
+
+ // setAmbigous() - same with Object?
+ setter = u.getPropertySet(uto, "Ambigous", new Object(), null);
+ assertNull(setter);
+ }
+
+ public void testMultipleParameterTypes()
+ throws Exception
+ {
+ VelPropertySet setter;
+
+ Uberspect u = ri.getUberspect();
+ UberspectorTestObject uto = new UberspectorTestObject();
+
+ // setAmbigous() - String
+ setter = u.getPropertySet(uto, "Ambigous", "", null);
+ assertNotNull(setter);
+ assertEquals("Found wrong method", "setAmbigous", setter.getMethodName());
+
+ // setAmbigous() - StringBuffer
+ setter = u.getPropertySet(uto, "Ambigous", new StringBuffer(), null);
+ assertNotNull(setter);
+ assertEquals("Found wrong method", "setAmbigous", setter.getMethodName());
+ }
+
+
+ public void testRegularGetters()
+ throws Exception
+ {
+ VelPropertyGet getter;
+
+ Uberspect u = ri.getUberspect();
+ UberspectorTestObject uto = new UberspectorTestObject();
+
+ // getRegular()
+ getter = u.getPropertyGet(uto, "Regular", null);
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "getRegular", getter.getMethodName());
+
+ // Lowercase regular
+ getter = u.getPropertyGet(uto, "regular", null);
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "getRegular", getter.getMethodName());
+
+ // lowercase: getpremium()
+ getter = u.getPropertyGet(uto, "premium", null);
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "getpremium", getter.getMethodName());
+
+ // test uppercase: getpremium()
+ getter = u.getPropertyGet(uto, "Premium", null);
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "getpremium", getter.getMethodName());
+ }
+
+ public void testBooleanGetters()
+ throws Exception
+ {
+ VelPropertyGet getter;
+
+ Uberspect u = ri.getUberspect();
+ UberspectorTestObject uto = new UberspectorTestObject();
+
+ // getRegular()
+ getter = u.getPropertyGet(uto, "RegularBool", null);
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "isRegularBool", getter.getMethodName());
+
+ // Lowercase regular
+ getter = u.getPropertyGet(uto, "regularBool", null);
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "isRegularBool", getter.getMethodName());
+
+ // lowercase: getpremiumBool()
+ getter = u.getPropertyGet(uto, "premiumBool", null);
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "ispremiumBool", getter.getMethodName());
+
+ // test uppercase: ()
+ getter = u.getPropertyGet(uto, "PremiumBool", null);
+ assertNotNull(getter);
+ assertEquals("Found wrong method", "ispremiumBool", getter.getMethodName());
+ }
+
+
+ public void testRegularSetters()
+ throws Exception
+ {
+ VelPropertySet setter;
+
+ Uberspect u = ri.getUberspect();
+ UberspectorTestObject uto = new UberspectorTestObject();
+
+ // setRegular()
+ setter = u.getPropertySet(uto, "Regular", "", null);
+ assertNotNull(setter);
+ assertEquals("Found wrong method", "setRegular", setter.getMethodName());
+
+ // Lowercase regular
+ setter = u.getPropertySet(uto, "regular", "", null);
+ assertNotNull(setter);
+ assertEquals("Found wrong method", "setRegular", setter.getMethodName());
+
+ // lowercase: setpremium()
+ setter = u.getPropertySet(uto, "premium", "", null);
+ assertNotNull(setter);
+ assertEquals("Found wrong method", "setpremium", setter.getMethodName());
+
+ // test uppercase: getpremium()
+ setter = u.getPropertySet(uto, "Premium", "", null);
+ assertNotNull(setter);
+ assertEquals("Found wrong method", "setpremium", setter.getMethodName());
+ }
+
+ public void testDisambiguation()
+ throws Exception
+ {
+ VelPropertySet setter;
+
+ Uberspect u = ri.getUberspect();
+ UberspectorTestObject uto = new UberspectorTestObject();
+
+ // setUnambigous() - String
+ setter = u.getPropertySet(uto, "unambiguous", "string", null);
+ assertNotNull(setter);
+ setter.invoke(uto, "string");
+
+ // setUnambigous() - HashMap
+ setter = u.getPropertySet(uto, "unambiguous", new HashMap(), null);
+ assertNotNull(setter);
+ setter.invoke(uto, new HashMap());
+ }
+
+ /*
+ *
+ * public void testMapGetSet()
+ * throws Exception
+ * {
+ * Uberspect u = ri.getUberspect();
+ * Map map = new HashMap();
+ *
+ * VelPropertyGet getter = u.getPropertyGet(map, "", null);
+ * VelPropertySet setter = u.getPropertySet(map, "", Object.class, null);
+ *
+ * assertNotNull("Got a null getter", getter);
+ * assertNotNull("Got a null setter", setter);
+ *
+ * assertEquals("Got wrong getter", "foo", getter.getMethodName());
+ * assertEquals("Got wrong setter", "bar", setter.getMethodName());
+ * }
+ */
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/UnicodeEscapeTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/UnicodeEscapeTestCase.java
new file mode 100644
index 00000000..1cb45475
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/UnicodeEscapeTestCase.java
@@ -0,0 +1,61 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.parser.node.ASTStringLiteral;
+
+/**
+ * Test Case for <a href="https://issues.apache.org/jira/browse/VELTOOLS-520">Velocity Tools Issue 520</a>.
+ */
+public class UnicodeEscapeTestCase extends BaseTestCase
+{
+ public UnicodeEscapeTestCase(final String name) throws Exception
+ {
+ super(name);
+ }
+
+ public void testUnicodeEscape() throws Exception
+ {
+ assertEvalEquals("a", "#set($v = \"\\u0061\")$v");
+ }
+
+ private void assertUnescape(String expected, String escaped)
+ {
+ String unescaped = ASTStringLiteral.unescape(escaped);
+ assertEquals(expected, unescaped);
+ if (escaped.equals(expected))
+ {
+ // checking that no new string allocated, for perfomance
+ assertSame(unescaped, escaped);
+ }
+ }
+
+ public void testASTStringLiteralUnescape()
+ {
+ assertUnescape("", "");
+ assertUnescape("bebe", "bebe");
+ assertUnescape("as\\nsd", "as\\nsd");
+ assertUnescape("a", "\\u0061");
+ assertUnescape("abc", "\\u0061bc");
+ assertUnescape("\u0061bc\u0064", "\\u0061bc\\u0064");
+ assertUnescape("z\u0061bc\u0064f", "z\\u0061bc\\u0064f");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/VMLibraryTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/VMLibraryTestCase.java
new file mode 100644
index 00000000..dde8cbd7
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/VMLibraryTestCase.java
@@ -0,0 +1,325 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Macro library inclution via the Template.merge method is tested using this
+ * class.
+ */
+
+public class VMLibraryTestCase extends BaseTestCase
+{
+ /**
+ * This engine is used with local namespaces
+ */
+ private VelocityEngine ve1 = new VelocityEngine();
+
+ /**
+ * This engine is used with global namespaces
+ */
+ private VelocityEngine ve2 = new VelocityEngine();
+
+ private static final String RESULT_DIR = TEST_RESULT_DIR + "/macrolibs";
+
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/macrolibs/compare";
+
+ public VMLibraryTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ /*
+ * setup local scope for templates
+ */
+ ve1.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ ve1.setProperty("velocimacro.permissions.allow.inline.to.replace.global",
+ Boolean.FALSE);
+ /*
+ * Turn on the cache
+ */
+ ve1.setProperty("file.resource.loader.cache", Boolean.TRUE);
+
+ ve1.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ ve1.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file");
+ ve1.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
+ TEST_COMPARE_DIR + "/macrolibs");
+ ve1.init();
+
+ /*
+ * Set to global namespaces
+ */
+ ve2.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.FALSE);
+ ve2.setProperty("velocimacro.permissions.allow.inline.to.replace.global",
+ Boolean.TRUE);
+ /*
+ * Turn on the cache
+ */
+ ve2.setProperty("file.resource.loader.cache", Boolean.FALSE);
+
+ ve2.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ ve2.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file");
+ ve2.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
+ TEST_COMPARE_DIR + "/macrolibs");
+ ve2.init();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new TestSuite(VMLibraryTestCase.class);
+ }
+
+ /*
+ * Runs the tests with local namespace.
+ */
+ public void testVelociMacroLibWithLocalNamespace()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULT_DIR);
+ /*
+ * Clear the file before proceeding
+ */
+ File file = new File(getFileName(
+ RESULT_DIR, "vm_library_local", RESULT_FILE_EXT));
+ if (file.exists())
+ {
+ file.delete();
+ }
+
+ /*
+ * Create a file output stream for appending
+ */
+ FileOutputStream fos = new FileOutputStream (getFileName(
+ RESULT_DIR, "vm_library_local", RESULT_FILE_EXT), true);
+
+ List templateList = new ArrayList();
+ VelocityContext context = new VelocityContext();
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ templateList.add("vm_library1.vm");
+
+ Template template = ve1.getTemplate("vm_library_local.vm");
+ template.merge(context, writer, templateList);
+
+ /*
+ * remove the first template library and includes a new library
+ * with a new definition for macros
+ */
+ templateList.remove(0);
+ templateList.add("vm_library2.vm");
+ template = ve1.getTemplate("vm_library_local.vm");
+ template.merge(context, writer, templateList);
+
+ /*
+ *Show that caching is working
+ */
+ Template t1 = ve1.getTemplate("vm_library_local.vm");
+ Template t2 = ve1.getTemplate("vm_library_local.vm");
+
+ assertEquals("Both templates refer to the same object", t1, t2);
+
+ /*
+ * Remove the libraries
+ */
+ template = ve1.getTemplate("vm_library_local.vm");
+ template.merge(context, writer);
+
+ /*
+ * Write to the file
+ */
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR, COMPARE_DIR, "vm_library_local",
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Processed template did not match expected output");
+ }
+ }
+
+ /*
+ * Runs the tests with global namespace.
+ */
+ public void testVelociMacroLibWithGlobalNamespace()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULT_DIR);
+ /*
+ * Clear the file before proceeding
+ */
+ File file = new File(getFileName(
+ RESULT_DIR, "vm_library_global", RESULT_FILE_EXT));
+ if (file.exists())
+ {
+ file.delete();
+ }
+
+ /*
+ * Create a file output stream for appending
+ */
+ FileOutputStream fos = new FileOutputStream (getFileName(
+ RESULT_DIR, "vm_library_global", RESULT_FILE_EXT), true);
+
+ List templateList = new ArrayList();
+ VelocityContext context = new VelocityContext();
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ templateList.add("vm_library1.vm");
+
+ Template template = ve1.getTemplate("vm_library_global.vm");
+ template.merge(context, writer, templateList);
+
+ /*
+ * remove the first template library and includes a new library
+ * with a new definition for macros
+ */
+ templateList.remove(0);
+ templateList.add("vm_library2.vm");
+ template = ve1.getTemplate("vm_library_global.vm");
+ template.merge(context, writer, templateList);
+
+ /*
+ *Show that caching is not working (We have turned off cache)
+ */
+ Template t1 = ve2.getTemplate("vm_library_global.vm");
+ Template t2 = ve2.getTemplate("vm_library_global.vm");
+
+ assertNotSame("Defferent objects", t1, t2);
+
+ /*
+ * Write to the file
+ */
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR, COMPARE_DIR, "vm_library_global",
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Processed template did not match expected output");
+ }
+ }
+
+ /*
+ * Runs the tests with global namespace.
+ */
+ public void testVelociMacroLibWithDuplicateDefinitions()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULT_DIR);
+ /*
+ * Clear the file before proceeding
+ */
+ File file = new File(getFileName(
+ RESULT_DIR, "vm_library_duplicate", RESULT_FILE_EXT));
+ if (file.exists())
+ {
+ file.delete();
+ }
+
+ /*
+ * Create a file output stream for appending
+ */
+ FileOutputStream fos = new FileOutputStream (getFileName(
+ RESULT_DIR, "vm_library_duplicate", RESULT_FILE_EXT), true);
+
+ List templateList = new ArrayList();
+ VelocityContext context = new VelocityContext();
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ templateList.add("vm_library1.vm");
+ templateList.add("vm_library2.vm");
+
+ Template template = ve1.getTemplate("vm_library.vm");
+ template.merge(context, writer, templateList);
+
+ /*
+ * Write to the file
+ */
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR, COMPARE_DIR, "vm_library_duplicate",
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Processed template did not match expected output");
+ }
+ }
+
+ /*
+ * Test whether the literal text is given if a definition cannot be
+ * found for a macro.
+ *
+ * @throws Exception
+ */
+ public void testMacrosWithNoDefinition()
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ FileOutputStream fos = new FileOutputStream (getFileName(
+ RESULT_DIR, "vm_library", RESULT_FILE_EXT));
+
+ VelocityContext context = new VelocityContext();
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ Template template = ve1.getTemplate("vm_library.vm");
+ template.merge(context, writer, null);
+
+ /*
+ * Write to the file
+ */
+ writer.flush();
+ writer.close();
+
+ /*
+ * outputs the macro calls
+ */
+ if (!isMatch(RESULT_DIR, COMPARE_DIR, "vm_library",
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ fail("Processed template did not match expected output");
+ }
+ }
+
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/VarargMethodsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/VarargMethodsTestCase.java
new file mode 100644
index 00000000..a87bba8e
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/VarargMethodsTestCase.java
@@ -0,0 +1,278 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+
+/**
+ * Used to check that vararg method calls on references work properly
+ */
+public class VarargMethodsTestCase extends BaseTestCase
+{
+ public VarargMethodsTestCase(final String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("nice", new NiceTool());
+ context.put("nasty", new NastyTool());
+ context.put("objects", new Object[] { this, VelocityContext.class });
+ context.put("strings", new String[] { "one", "two" });
+ context.put("doubles", new double[] { 1.5, 2.5 });
+ context.put("float", 1f);
+ context.put("ints", new int[] { 1, 2 });
+ }
+
+ public void testStrings()
+ {
+ assertEvalEquals("onetwo", "$nice.var($strings)");
+ assertEvalEquals("onetwo", "$nice.var('one','two')");
+ assertEvalEquals("one", "$nice.var('one')");
+ assertEvalEquals("", "$nice.var()");
+ }
+
+ public void testDoubles()
+ {
+ assertEvalEquals("4.0", "$nice.add($doubles)");
+ assertEvalEquals("3.0", "$nice.add(1,2)");
+ assertEvalEquals("1.0", "$nice.add(1)");
+ assertEvalEquals("0.0", "$nice.add()");
+ }
+
+ public void testFloatToDoubleVarArg()
+ {
+ assertEvalEquals("1.0", "$nice.add($float)");
+ }
+
+ public void testStringVsStrings()
+ {
+ assertEvalEquals("onlyone", "$nasty.var('one')");
+ assertEvalEquals("onlynull", "$nasty.var($null)");
+ assertEvalEquals("", "$nasty.var()");
+ }
+
+ public void testIntVsDoubles()
+ {
+ assertEvalEquals("1", "$nasty.add(1)");
+ assertEvalEquals("1.0", "$nasty.add(1.0)");
+ assertEvalEquals("3.0", "$nasty.add(1.0,2)");
+ }
+
+ public void testInts()
+ {
+ assertEvalEquals("3", "$nasty.add($ints)");
+ assertEvalEquals("3", "$nasty.add(1,2)");
+ assertEvalEquals("1", "$nasty.add(1)");
+ // add(int[]) wins because it is "more specific"
+ assertEvalEquals("0", "$nasty.add()");
+ }
+
+ public void testStringsVsObjectsAKASubclassVararg()
+ {
+ assertEvalEquals("objects", "$nice.test($objects)");
+ assertEvalEquals("objects", "$nice.test($nice,$nasty,$ints)");
+ assertEvalEquals("strings", "$nice.test('foo')");
+ }
+
+ public void testObjectVarArgVsObjectEtc()
+ {
+ assertEvalEquals("object,string", "$nasty.test($nice,'foo')");
+ }
+
+ public void testObjectVarArgVsObjectVelocity605()
+ {
+ assertEvalEquals("string", "$nasty.test('joe')");
+ assertEvalEquals("object", "$nasty.test($nice)");
+ }
+
+ public void testNoArgs()
+ {
+ assertEvalEquals("noargs", "$nasty.test()");
+ }
+
+ public void testPassingArrayToVarArgVelocity642()
+ {
+ assertEvalEquals("[one, two]", "$nasty.test642($strings)");
+ assertEvalEquals("[1, 2]", "#set( $list = [1..2] )$nasty.test642($list.toArray())");
+ }
+
+ public void testNullToPrimitiveVarArg()
+ {
+ assertEvalEquals("int[]", "$nasty.test649($null)");
+ }
+
+ public void testArgsBeforeVarargWithNoArgs()
+ {
+ assertEvalEquals("String,String,Object[]", "$nasty.test651('a','b')");
+ }
+
+ public void testVelocity651()
+ {
+ assertEvalEquals("String,List", "$nasty.test651('test',['TEST'])");
+ }
+
+ public void testMax()
+ {
+ assertEvalEquals("4", "$nasty.max(4, 3.5)");
+ }
+
+ public static class NiceTool
+ {
+ public String var(String[] ss)
+ {
+ StringBuilder out = new StringBuilder();
+ for (String s : ss)
+ {
+ out.append(s);
+ }
+ return out.toString();
+ }
+
+ public double add(double[] dd)
+ {
+ double total = 0;
+ for (double aDd : dd)
+ {
+ total += aDd;
+ }
+ return total;
+ }
+
+ public String test(Object[] oo)
+ {
+ return "objects";
+ }
+
+ public String test(String[] oo)
+ {
+ return "strings";
+ }
+
+ }
+
+ public static class NastyTool extends NiceTool
+ {
+ public String var(String s)
+ {
+ return "only"+s;
+ }
+
+ public int add(int[] ii)
+ {
+ int total = 0;
+ for (int anIi : ii)
+ {
+ total += anIi;
+ }
+ return total;
+ }
+
+ public int add(int i)
+ {
+ return i;
+ }
+
+ public String test()
+ {
+ return "noargs";
+ }
+
+ public Object test(Object arg)
+ {
+ return "object";
+ }
+
+ public Object test(String arg)
+ {
+ return "string";
+ }
+
+ @Override
+ public String test(Object[] array)
+ {
+ return "object[]";
+ }
+
+ public String test(Object object, String property)
+ {
+ return "object,string";
+ }
+
+ public String test642(Object[] array)
+ {
+ //JDK5: return Arrays.deepToString(array);
+ if (array == null)
+ {
+ return null;
+ }
+ StringBuilder o = new StringBuilder("[");
+ for (int i=0; i < array.length; i++)
+ {
+ if (i > 0)
+ {
+ o.append(", ");
+ }
+ o.append((array[i]));
+ }
+ o.append("]");
+ return o.toString();
+ }
+
+ public String test649(int[] array)
+ {
+ return "int[]";
+ }
+
+ public String test651(String s, String s2, Object[] args)
+ {
+ return "String,String,Object[]";
+ }
+
+ public String test651(String s, java.util.List l)
+ {
+ return "String,List";
+ }
+
+ public Number max(Number n1, Number n2) { return max(new Number[] { n1, n2 }); }
+
+ public Number max(Number[] numbers)
+ {
+ if (numbers.length == 0) return null;
+ int minindex = -1, i = 0;
+ double val = Double.MIN_VALUE;
+ for (Number n : numbers)
+ {
+ if (n.floatValue() > val)
+ {
+ minindex = i;
+ val = n.floatValue();
+ }
+ ++i;
+ }
+ if (minindex < 0) minindex = 0;
+ return numbers[minindex];
+ }
+
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/VelocimacroBCModeTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/VelocimacroBCModeTestCase.java
new file mode 100644
index 00000000..e41fe434
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/VelocimacroBCModeTestCase.java
@@ -0,0 +1,100 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * This class tests the mode where velocimacros do preserve arguments literals
+ */
+
+public class VelocimacroBCModeTestCase extends BaseTestCase
+{
+ private static final String BASE_DIR = TEST_COMPARE_DIR + "/bc_mode";
+ private static final String CMP_DIR = BASE_DIR + "/compare";
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/bc_mode";
+
+ public VelocimacroBCModeTestCase(final String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ boolean bcMode = !getName().contains("NoPreserve");
+ engine.setProperty(RuntimeConstants.VM_ENABLE_BC_MODE, bcMode);
+ engine.setProperty("file.resource.loader.path", TEST_COMPARE_DIR + "/bc_mode");
+ }
+
+ public void testPreserveLiterals()
+ {
+ assertEvalEquals("$bar","#macro(m $foo)$foo#end#m($bar)");
+ }
+
+ public void testGlobalDefaults()
+ {
+ assertEvalEquals("foo","#macro(m $foo)$foo#end#set($foo='foo')#m()");
+ }
+
+ public void testVariousCasesPreserve() throws Exception
+ {
+ doTestVariousCases("bc_mode_enabled");
+ }
+
+ public void testVariousCasesNoPreserve() throws Exception
+ {
+ doTestVariousCases("bc_mode_disabled");
+ }
+
+ private void doTestVariousCases(String compare_ext) throws Exception
+ {
+ assureResultsDirectoryExists(RESULTS_DIR);
+ String basefilename = "test_bc_mode";
+ Template template = engine.getTemplate( getFileName(null, basefilename, "vtl") );
+ context = new VelocityContext();
+ FileOutputStream fos;
+ Writer fwriter;
+
+ fos = new FileOutputStream (getFileName(RESULTS_DIR, basefilename, RESULT_FILE_EXT));
+
+ fwriter = new BufferedWriter( new OutputStreamWriter(fos) );
+
+ template.merge(context, fwriter);
+ fwriter.flush();
+ fwriter.close();
+
+ if (!isMatch(RESULTS_DIR, CMP_DIR, basefilename, RESULT_FILE_EXT, compare_ext))
+ {
+ String result = getFileContents(RESULTS_DIR, basefilename, RESULT_FILE_EXT);
+ String compare = getFileContents(CMP_DIR, basefilename, compare_ext);
+
+ assertEquals(compare, result);
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/VelocimacroTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/VelocimacroTestCase.java
new file mode 100644
index 00000000..445cf44a
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/VelocimacroTestCase.java
@@ -0,0 +1,135 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.exception.MacroOverflowException;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.StringWriter;
+
+/**
+ * This class tests strange Velocimacro issues.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class VelocimacroTestCase extends TestCase
+{
+ private String template1 = "#macro(foo $a)$a#end #macro(bar $b)#foo($b)#end #foreach($i in [1..3])#if($i == 3)#foo($i)#else#bar($i)#end#end";
+ private String result1 = " 123";
+ private String template2 = "#macro(bar $a)#set($a = $a + 1)$a#bar($a)#end#bar(0)";
+ private String template3 = "#macro(baz $a)#set($a = $a + 1)$a#inner($a)#end#macro(inner $b)#baz($b)#end#baz(0)";
+ private String template4 = "#macro(bad $a)#set($a = $a + 1)$a#inside($a)#end#macro(inside $b)#loop($b)#end#macro(loop $c)#bad($c)#end#bad(0)";
+
+ public VelocimacroTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ /*
+ * setup local scope for templates
+ */
+ Velocity.reset();
+ Velocity.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ Velocity.setProperty( Velocity.VM_MAX_DEPTH, 5);
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+ Velocity.init();
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(VelocimacroTestCase.class);
+ }
+
+ /**
+ * Runs the test.
+ */
+ public void testVelociMacro ()
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext();
+
+ StringWriter writer = new StringWriter();
+ Velocity.evaluate(context, writer, "vm_chain1", template1);
+
+ String out = writer.toString();
+
+ if( !result1.equals( out ) )
+ {
+ fail("output incorrect.");
+ }
+ }
+
+ /**
+ * Test case for evaluating max calling depths of macros
+ */
+ public void testVelociMacroCallMax()
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext();
+ StringWriter writer = new StringWriter();
+
+ try
+ {
+ Velocity.evaluate(context, writer, "vm_chain2", template2);
+ fail("Did not exceed max macro call depth as expected");
+ }
+ catch (MacroOverflowException e)
+ {
+ assertEquals("Max calling depth of 5 was exceeded in macro 'bar'"+
+ " with Call Stack:bar->bar->bar->bar->bar at vm_chain2[line 1, column 15]",
+ e.getMessage());
+ }
+
+ try
+ {
+ Velocity.evaluate(context, writer, "vm_chain3", template3);
+ fail("Did not exceed max macro call depth as expected");
+ }
+ catch (MacroOverflowException e)
+ {
+ assertEquals("Max calling depth of 5 was exceeded in macro 'inner'"+
+ " with Call Stack:baz->inner->baz->inner->baz at vm_chain3[line 1, column 64]",
+ e.getMessage());
+ }
+
+ try
+ {
+ Velocity.evaluate(context, writer, "vm_chain4", template4);
+ fail("Did not exceed max macro call depth as expected");
+ }
+ catch (MacroOverflowException e)
+ {
+ assertEquals("Max calling depth of 5 was exceeded in macro 'loop'"+
+ " with Call Stack:bad->inside->loop->bad->inside at vm_chain4[line 1, column 94]",
+ e.getMessage());
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/VelocityAppTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/VelocityAppTestCase.java
new file mode 100644
index 00000000..ecc9f8fc
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/VelocityAppTestCase.java
@@ -0,0 +1,80 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+
+import java.io.StringWriter;
+
+/**
+ * This class is intended to test the app.Velocity.java class.
+ *
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
+ * @version $Id$
+ */
+public class VelocityAppTestCase extends BaseTestCase implements TemplateTestBase
+{
+ private StringWriter compare1 = new StringWriter();
+ private String input1 = "My name is $name -> $Floog";
+ private String result1 = "My name is jason -> floogie woogie";
+
+ public VelocityAppTestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testVelocityApp()
+ throws Exception
+ {
+ engine.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+ engine.init();
+
+ // the usage of engine here is equivalent to using static calls to Velocity. class
+
+ VelocityContext context = new VelocityContext();
+ context.put("name", "jason");
+ context.put("Floog", "floogie woogie");
+
+ String cmp = "Hello jason! Nice floogie woogie!";
+
+ engine.evaluate(context, compare1, "evaltest", input1);
+ if (!result1.equals(compare1.toString()))
+ {
+ fail("Output 1 incorrect.");
+ }
+
+ StringWriter result2 = new StringWriter();
+ engine.mergeTemplate("mergethis.vm", "UTF-8", context, result2);
+ if (!result2.toString().equals(cmp))
+ {
+ fail("Output 2 incorrect.");
+ }
+
+ StringWriter result3 = new StringWriter();
+ engine.invokeVelocimacro("floog", "test", new String[]{"name", "Floog"}, context, result3);
+
+ if (!result3.toString().equals(cmp))
+ {
+ fail("Output 3 incorrect.");
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/WrappedExceptionTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/WrappedExceptionTestCase.java
new file mode 100644
index 00000000..1c8bd11f
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/WrappedExceptionTestCase.java
@@ -0,0 +1,84 @@
+package org.apache.velocity.test;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.test.provider.TestProvider;
+
+import java.io.StringWriter;
+
+/**
+ * Test thrown exceptions include a proper cause (under JDK 1.4+).
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class WrappedExceptionTestCase extends BaseTestCase implements TemplateTestBase
+{
+ VelocityEngine ve;
+
+ /**
+ * Default constructor.
+ */
+ public WrappedExceptionTestCase(String name)
+ {
+ super(name);
+ }
+
+ public static Test suite ()
+ {
+ return new TestSuite(WrappedExceptionTestCase.class);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ ve = new VelocityEngine();
+ ve.init();
+ }
+
+
+ public void testMethodException() throws Exception
+ {
+
+ // accumulate a list of invalid references
+ Context context = new VelocityContext();
+ StringWriter writer = new StringWriter();
+ context.put("test",new TestProvider());
+
+ try
+ {
+ ve.evaluate(context,writer,"test","$test.getThrow()");
+ fail ("expected an exception");
+ }
+ catch (MethodInvocationException E)
+ {
+ assertEquals(Exception.class,E.getCause().getClass());
+ assertEquals("From getThrow()",E.getCause().getMessage());
+ }
+
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/eventhandler/Handler1.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/eventhandler/Handler1.java
new file mode 100644
index 00000000..52220557
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/eventhandler/Handler1.java
@@ -0,0 +1,75 @@
+package org.apache.velocity.test.eventhandler;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.event.IncludeEventHandler;
+import org.apache.velocity.app.event.MethodExceptionEventHandler;
+import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.util.introspection.Info;
+
+import java.util.Locale;
+
+/**
+ * This is a test set of event handlers, used to test event handler sequences.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class Handler1
+ implements ReferenceInsertionEventHandler, MethodExceptionEventHandler, IncludeEventHandler {
+
+ /**
+ * display output twice, once uppercase and once lowercase
+ */
+ @Override
+ public Object referenceInsert(Context context, String reference, Object value)
+ {
+ if (value == null)
+ return null;
+ else
+ return value.toString().toUpperCase(Locale.ROOT) + value.toString().toLowerCase(Locale.ROOT);
+ }
+
+ /**
+ * throw the exception
+ */
+ @Override
+ public Object methodException(Context context, Class<?> claz, String method, Exception e, Info info)
+ {
+ throw new RuntimeException(e);
+ }
+
+ /*
+ * redirect all requests to a page "login.vm" (simulates access control).
+ */
+ @Override
+ public String includeEvent(
+ Context context,
+ String includeResourcePath,
+ String currentResourcePath,
+ String directiveName)
+ {
+
+ return "notfound.vm";
+
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/eventhandler/Handler2.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/eventhandler/Handler2.java
new file mode 100644
index 00000000..033c9655
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/eventhandler/Handler2.java
@@ -0,0 +1,75 @@
+package org.apache.velocity.test.eventhandler;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.event.IncludeEventHandler;
+import org.apache.velocity.app.event.MethodExceptionEventHandler;
+import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.util.introspection.Info;
+
+import java.util.Locale;
+
+/**
+ * This is a test set of event handlers, used to test event handler sequences.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class Handler2
+ implements ReferenceInsertionEventHandler, MethodExceptionEventHandler, IncludeEventHandler {
+
+ /**
+ * convert output to upper case
+ */
+ @Override
+ public Object referenceInsert(Context context, String reference, Object value)
+ {
+ if (value == null)
+ return null;
+ else
+ return value.toString().toUpperCase(Locale.ROOT);
+ }
+
+ /**
+ * print the exception
+ */
+ @Override
+ public Object methodException(Context context, Class<?> claz, String method, Exception e, Info info)
+ {
+ return "Exception: " + e;
+ }
+
+ /*
+ * redirect all requests to a new directory "subdir" (simulates localization).
+ */
+ @Override
+ public String includeEvent(
+ Context context,
+ String includeResourcePath,
+ String currentResourcePath,
+ String directiveName)
+ {
+
+ return "subdir/" + includeResourcePath;
+
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/StackOverflow32805217TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/StackOverflow32805217TestCase.java
new file mode 100755
index 00000000..d70e0bcd
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/StackOverflow32805217TestCase.java
@@ -0,0 +1,39 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests bugfix for http://stackoverflow.com/questions/32805217/bug-or-hidden-feature-in-apache-velocity
+ */
+public class StackOverflow32805217TestCase extends BaseTestCase
+{
+ public StackOverflow32805217TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testIt()
+ {
+ assertEvalEquals("$map{value}", "$map{value}");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/VelTools66TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/VelTools66TestCase.java
new file mode 100644
index 00000000..529683ff
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/VelTools66TestCase.java
@@ -0,0 +1,181 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeInstance;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.introspection.Introspector;
+
+import java.lang.reflect.Method;
+import java.security.AccessControlException;
+import java.security.Permission;
+
+/**
+ * Test Case for <a href="https://issues.apache.org/jira/browse/VELTOOLS-66">Velocity Tools Issue 66</a>.
+ */
+public class VelTools66TestCase
+ extends BaseTestCase
+{
+ protected static boolean DEBUG = false;
+
+ public VelTools66TestCase(final String name)
+ throws Exception
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(VelTools66TestCase.class);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ Velocity.setProperty(
+ Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ System.setSecurityManager(new TestSecurityManager());
+
+ }
+
+ protected static void log(String out)
+ {
+ Velocity.getLog().debug(out);
+ if (DEBUG)
+ {
+ System.out.println(out);
+ }
+ }
+
+ @Override
+ public void tearDown()
+ {
+ System.setSecurityManager(null);
+ }
+
+ public void testVelTools66()
+ throws Exception
+ {
+ /* the testcase is obsolete in JDK 8, as SystemManager.checkMemberAccess is not anymore called
+ * by Class.getMethods() */
+
+ String [] javaVersionFields = System.getProperty("java.version").split("\\.");
+ int javaVersion = Integer.parseInt(javaVersionFields[0]);
+ if (javaVersion == 1)
+ {
+ javaVersion = Integer.parseInt(javaVersionFields[1]);
+ }
+
+ if (javaVersion >= 8)
+ {
+ return;
+ }
+
+ Method verifyMethod = TestInterface.class.getMethod("getTestValue");
+
+ RuntimeInstance ri = new RuntimeInstance();
+ log = new TestLogger(false, false);
+ Introspector introspector = new Introspector(log);
+
+ Method testMethod = introspector.getMethod(TestObject.class, "getTestValue", new Object[0]);
+ assertNotNull(testMethod);
+ assertEquals("Method object does not match!", verifyMethod, testMethod);
+ }
+
+ public interface TestInterface
+ {
+ String getTestValue();
+
+ void setTestValue(String testValue);
+ }
+
+ public static final class TestObject
+ implements TestInterface
+ {
+ String testValue = null;
+
+ public TestObject()
+ {
+ }
+
+ @Override
+ public String getTestValue()
+ {
+ return testValue;
+ }
+
+ @Override
+ public void setTestValue(final String testValue)
+ {
+ this.testValue = testValue;
+ }
+ }
+
+ public static final class TestSecurityManager extends SecurityManager
+ {
+ private final Class<?> clazz = TestObject.class;
+
+ public TestSecurityManager()
+ {
+ super();
+ }
+
+ public void checkMemberAccess(final Class<?> c, final int i)
+ {
+ log("checkMemberAccess(" + c.getName() + ", " + i + ")");
+
+ if (c.equals(clazz))
+ {
+ throw new AccessControlException("You are not allowed to access TestObject directly!");
+ }
+ }
+
+ @Override
+ public void checkRead(final String file)
+ {
+ log("checkRead(" + file + ")");
+ }
+
+ @Override
+ public void checkPackageAccess(final String s)
+ {
+ log("checkPackageAccess(" + s + ")");
+ }
+
+ @Override
+ public void checkPropertyAccess(final String s)
+ {
+ log("checkPropertyAccess(" + s + ")");
+ }
+
+ @Override
+ public void checkPermission(final Permission p)
+ {
+ log("checkPermission(" + p + ")");
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity532TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity532TestCase.java
new file mode 100755
index 00000000..16eaef1c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity532TestCase.java
@@ -0,0 +1,52 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-532.
+ */
+public class Velocity532TestCase extends BaseTestCase
+{
+ public Velocity532TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void test532()
+ {
+ String template = "#macro( test )$foreach.count#end"+
+ "#foreach( $i in [1..5] )#test()#end";
+ assertEvalEquals("12345", template);
+ }
+
+ public void test532b()
+ {
+ // try something a little more like Matt's example
+ String template = "#macro( test $baz )"+
+ "#if( $foo == $null )"+
+ "#if( $foreach.count == 3 )bar#end"+
+ "#end#end"+
+ "#foreach( $i in [1..5] )#test($i)#end";
+ assertEvalEquals("bar", template);
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity537TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity537TestCase.java
new file mode 100755
index 00000000..02ee5efb
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity537TestCase.java
@@ -0,0 +1,121 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Test Case for <a href="https://issues.apache.org/jira/browse/VELOCITY-537">Velocity Issue 537</a>.
+ */
+public class Velocity537TestCase extends BaseTestCase
+{
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/issues/velocity-537";
+
+ /**
+ * Template Directory
+ */
+ private static final String TEMPLATE_DIR = TEST_COMPARE_DIR + "/issues/velocity-537/templates";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/issues/velocity-537/compare";
+
+ public Velocity537TestCase(final String name) throws Exception
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(Velocity537TestCase.class);
+ }
+
+ private VelocityEngine velocityEngine;
+ @Override
+ public void setUp() throws Exception
+ {
+
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ velocityEngine = new VelocityEngine();
+ velocityEngine.addProperty(Velocity.FILE_RESOURCE_LOADER_PATH, TEMPLATE_DIR);
+
+ velocityEngine.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ velocityEngine.init();
+ }
+
+ public void testVelocity537() throws Exception
+ {
+ executeTest("velocity537.vm");
+ }
+
+ public void testVelocity537Again() throws Exception
+ {
+ executeTest("velocity537b.vm");
+ }
+
+ protected Template executeTest(final String templateName) throws Exception
+ {
+ Template template = velocityEngine.getTemplate(templateName);
+
+ FileOutputStream fos = new FileOutputStream(getFileName(RESULTS_DIR, templateName, RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ VelocityContext context = new VelocityContext();
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, templateName, RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ // just to be useful, output the output in the fail message
+ StringWriter out = new StringWriter();
+ template.merge(context, out);
+
+ String compare = getFileContents(COMPARE_DIR, templateName, CMP_FILE_EXT);
+
+ fail("Output incorrect for Template: " + templateName + ": \""+out+"\""+
+ "; it did not match: \""+compare+"\"");
+ }
+
+ return template;
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity544TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity544TestCase.java
new file mode 100644
index 00000000..1018208c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity544TestCase.java
@@ -0,0 +1,75 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * @see <a href="https://issues.apache.org/jira/browse/VELOCITY-544">VELOCITY-544</a>
+ */
+public class Velocity544TestCase
+ extends BaseTestCase
+{
+ public Velocity544TestCase(final String name)
+ throws Exception
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(Velocity544TestCase.class);
+ }
+
+ public void testBooleanPropertyExecutor()
+ throws Exception
+ {
+ context.put("foobarTrue", new Foobar(true));
+ context.put("foobarFalse", new Foobar(false));
+
+ String template = "$foobarTrue.True $foobarFalse.True $foobarTrue.TrueObject $foobarFalse.TrueObject";
+
+ String result = evaluate(template);
+
+ assertEquals("true false true false", result);
+ }
+
+ public static class Foobar
+ {
+ private boolean value;
+
+ public Foobar(boolean value)
+ {
+ this.value = value;
+ }
+
+ public boolean isTrue()
+ {
+ return(value);
+ }
+
+ public Boolean isTrueObject()
+ {
+ return(value);
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity579TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity579TestCase.java
new file mode 100755
index 00000000..3918178a
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity579TestCase.java
@@ -0,0 +1,82 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-579 and with some related stuff
+ * from VELOCITY-70 thrown in.
+ */
+public class Velocity579TestCase extends BaseTestCase
+{
+ public Velocity579TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testPublicMethodInPrivateImplOfPublicInterface()
+ {
+ context.put("foo", new Foobar());
+ assertEvalEquals("bar", "$foo.foo('bar')");
+ assertEvalEquals("$foo.bar()", "$foo.bar()");
+ }
+
+ public void testPublicMethodInheritedFromPrivateClass() throws Exception
+ {
+ context.put("bar", new MyBar());
+ // ugly hack to avoid failed test when running JDK 1.5 or earlier
+ String javaVersion = System.getProperty("java.version");
+ if (javaVersion.startsWith("1.6"))
+ assertEvalEquals("bar", "$bar.bar()");
+ }
+
+ public interface Foo
+ {
+ String foo(String s);
+ }
+
+ private static abstract class FooImpl implements Foo
+ {
+ @Override
+ public String foo(String s)
+ {
+ return s == null ? "foo" : s;
+ }
+ }
+
+ private static class Foobar extends FooImpl
+ {
+ public String bar()
+ {
+ return "bar";
+ }
+ }
+
+ public static class MyBar extends Foobar
+ {
+ @Override
+ public String bar()
+ {
+ return super.bar();
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity580TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity580TestCase.java
new file mode 100755
index 00000000..14904163
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity580TestCase.java
@@ -0,0 +1,116 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Test Case for <a href="https://issues.apache.org/jira/browse/VELOCITY-580">Velocity Issue 580</a>.
+ */
+public class Velocity580TestCase extends BaseTestCase
+{
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/issues/velocity-580";
+
+ /**
+ * Template Directory
+ */
+ private static final String TEMPLATE_DIR = TEST_COMPARE_DIR + "/issues/velocity-580/templates";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/issues/velocity-580/compare";
+
+ public Velocity580TestCase(final String name) throws Exception
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(Velocity580TestCase.class);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ Velocity.reset();
+
+ Velocity.addProperty(Velocity.FILE_RESOURCE_LOADER_PATH, TEMPLATE_DIR);
+
+ Velocity.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+
+ Velocity.init();
+ }
+
+ public void testVelocity580() throws Exception
+ {
+ executeTest("velocity580.vm");
+ }
+
+ protected Template executeTest(final String templateName) throws Exception
+ {
+ Template template = RuntimeSingleton.getTemplate(templateName);
+
+ FileOutputStream fos = new FileOutputStream(getFileName(RESULTS_DIR, templateName, RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ VelocityContext context = new VelocityContext();
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, templateName, RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ // just to be useful, output the output in the fail message
+ StringWriter out = new StringWriter();
+ template.merge(context, out);
+
+ String compare = getFileContents(COMPARE_DIR, templateName, CMP_FILE_EXT);
+
+ fail("Output incorrect for Template: " + templateName + ": \""+out+"\""+
+ "; it did not match: \""+compare+"\"");
+ }
+
+ return template;
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity587TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity587TestCase.java
new file mode 100755
index 00000000..7d5da4c2
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity587TestCase.java
@@ -0,0 +1,64 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-587.
+ */
+public class Velocity587TestCase extends BaseTestCase
+{
+ public Velocity587TestCase(String name)
+ {
+ super(name);
+ }
+
+ // remember, they're all doubled, since java will use them as escapes first.
+ public void testLiteralTwoBackslashes()
+ {
+ String template = "#set( $bs2 = \'\\\\\' )$bs2";
+ String expected = "\\\\";
+ assertEvalEquals(expected, template);
+ }
+
+ public void testLiteralOneBackslash()
+ {
+ String template = "#set( $bs = \'\\\' )$bs";
+ String expected = "\\";
+ assertEvalEquals(expected, template);
+ }
+
+ // remember, they're all doubled, since java will use them as escapes first.
+ public void testInterpolatedTwoBackslashes()
+ {
+ String template = "#set( $bs2 = \"\\\\\" )$bs2";
+ String expected = "\\\\";
+ assertEvalEquals(expected, template);
+ }
+
+ public void testInterpolatedOneBackslash()
+ {
+ String template = "#set( $bs = \"\\\" )$bs";
+ String expected = "\\";
+ assertEvalEquals(expected, template);
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity589TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity589TestCase.java
new file mode 100755
index 00000000..6502dc26
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity589TestCase.java
@@ -0,0 +1,40 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-589.
+ */
+public class Velocity589TestCase extends BaseTestCase
+{
+ public Velocity589TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testIt()
+ {
+ context.put("myId", "test");
+ assertEvalEquals("#test_bg", "#${myId}_bg");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity614TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity614TestCase.java
new file mode 100755
index 00000000..746b40a6
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity614TestCase.java
@@ -0,0 +1,90 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.TemplateInitException;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-614.
+ */
+public class Velocity614TestCase extends BaseTestCase
+{
+ public Velocity614TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testSchmoo()
+ {
+ String template = "#something(Stuff)";
+ assertEvalEquals(template, template);
+ }
+
+ public void testEscapeSchmooButNotReallySinceSchmooHasNoEscaping()
+ {
+ String template = "\\#something(Stuff)";
+ assertEvalEquals(template, template);
+ }
+
+ public void testEscapeMacroWithBadArg()
+ {
+ String template = "#macro( evil $arg )$arg#end \\#evil(bar)";
+ assertEvalEquals(" #evil(bar)", template);
+ }
+
+ public void testEarlyDefinedMacroWithBadArg()
+ {
+ // make sure this still bombs, but don't spam sysout
+ log.off();
+ assertEvalException("#macro( evil $arg )$arg#end #evil(bar)");
+ log.on();
+ }
+
+ // just make sure this doesn't get broken
+ public void testLateDefinedMacroWithGoodArg()
+ {
+ String good = "#good('bar') #macro( good $arg )$arg#end";
+ assertEvalEquals("bar ", good);
+ }
+
+ public void testDirectivesWithBadArg()
+ {
+ // make sure these all still bomb, but don't spam sysout
+ log.off();
+ assertEvalException("#foreach(Stuff in That)foo#end");
+ assertEvalException("#include(Stuff)");
+ assertEvalException("#parse(Stuff)");
+ assertEvalException("#define(Stuff)foo#end");
+ assertEvalException("#macro( name Stuff)foo#end");
+ assertEvalException("#foreach($i in [1..3])#break(Stuff)#end");
+ assertEvalException("#literal(Stuff)foo#end");
+ assertEvalException("#evaluate(Stuff)", ParseErrorException.class);
+ log.on();
+ }
+
+ public void testLateDefinedMacroWithBadArg()
+ {
+ String evil = "#evil(bar) #macro( evil $arg )$arg#end";
+ assertEvalException(evil, TemplateInitException.class);
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity615TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity615TestCase.java
new file mode 100755
index 00000000..fec7796d
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity615TestCase.java
@@ -0,0 +1,139 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-615.
+ */
+public class Velocity615TestCase extends BaseTestCase
+{
+ public Velocity615TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty("velocimacro.permissions.allow.inline", "true");
+ engine.setProperty("velocimacro.permissions.allow.inline.to.replace.global", "false");
+ engine.setProperty("velocimacro.permissions.allow.inline.local.scope", "true");
+ engine.setProperty("velocimacro.arguments.strict", "true");
+ engine.setProperty("space.gobbling", "bc");
+ }
+
+ public void testIt()
+ {
+ String template = "#set( $foo = 'old' )"+
+ "#macro( test $foo )"+
+ "#set( $foo = \"new $foo \" )"+
+ "$foo"+
+ "#end"+
+ "#test( 'foo' )"+
+ "$foo";
+ assertEvalEquals("new foo new foo ", template);
+ }
+
+ public void testForIrrationallyFearedRelatedPossibleProblem()
+ {
+ context.put("i", new Inc());
+ String template = "#macro( test $a )"+
+ "$a"+
+ "$a"+
+ "#end"+
+ "#test( \"$i\" )$i";
+ assertEvalEquals("001", template);
+ }
+
+ public void testForIrrationallyFearedRelatedPossibleProblem2()
+ {
+ context.put("i", new Inc());
+ String template = "#macro( test $a )"+
+ "#set( $a = 'a' )"+
+ "$a"+
+ "$a"+
+ "#end"+
+ "#test( \"$i\" )$i";
+ assertEvalEquals("aa1", template);
+ }
+
+ public void testForIrrationallyFearedRelatedPossibleProblem3()
+ {
+ context.put("i", new Inc());
+ String template = "#macro( test $a )"+
+ "$a"+
+ "$a"+
+ "#end"+
+ "#test( $i )$i";
+ assertEvalEquals("012", template);
+ }
+
+ public void testForIrrationallyFearedRelatedPossibleProblem4()
+ {
+ context.put("i", new Inc());
+ String template = "#macro( test $a )"+
+ "$a"+
+ "$a"+
+ "#end"+
+ "#test( $i.plus() )$i";
+ assertEvalEquals("001", template);
+ }
+
+ public void testForIrrationallyFearedRelatedPossibleProblem5()
+ {
+ context.put("i", new Inc());
+ String template = "#macro( test $a )"+
+ "#set( $a = $i )"+
+ "$a"+
+ "$a"+
+ "#end"+
+ "#test( 'a' )$i";
+ assertEvalEquals("012", template);
+ }
+
+ public void testVelocity681()
+ {
+ String template = "#macro(myMacro $result)"+
+ " #set($result = 'some value')"+
+ "#end"+
+ "#myMacro($x)"+
+ "$x";
+ assertEvalEquals("$x", template);
+ }
+
+ public static class Inc
+ {
+ private int i=0;
+
+ public int plus()
+ {
+ return i++;
+ }
+
+ public String toString()
+ {
+ return String.valueOf(i++);
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity616TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity616TestCase.java
new file mode 100755
index 00000000..e2974e26
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity616TestCase.java
@@ -0,0 +1,70 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-616.
+ */
+public class Velocity616TestCase extends BaseTestCase
+{
+ public Velocity616TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ context.put("bar", "bar");
+ context.put("foo", Boolean.FALSE);
+ }
+
+ public void testIfNoBrackets()
+ {
+ String template = "\\#if ($foo) \\$bar \\#end";
+ String expected = "#if (false) $bar #end";
+ assertEvalEquals(expected, template);
+ }
+
+ public void testForeachBrackets()
+ {
+ String template = "\\#{foreach}( $i in [1..3] )$i\\#{end}";
+ String expected = "#{foreach}( $i in [1..3] )$i#{end}";
+ assertEvalEquals(expected, template);
+ }
+
+ public void testIfBrackets()
+ {
+ String template = "\\#{if} ($foo) \\$bar \\#{end}";
+ String expected = "#{if} (false) $bar #{end}";
+ assertEvalEquals(expected, template);
+ }
+
+ public void testIfBracketsOnEndOnly()
+ {
+ String template = "\\#if( $foo ) \\$bar \\#{end}";
+ String expected = "#if( false ) $bar #{end}";
+ assertEvalEquals(expected, template);
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity625TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity625TestCase.java
new file mode 100755
index 00000000..bdbf43fe
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity625TestCase.java
@@ -0,0 +1,40 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-625.
+ */
+public class Velocity625TestCase extends BaseTestCase
+{
+ public Velocity625TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void test1()
+ {
+ String template = "#macro(test $a $b)test#end#test('x')";
+ assertEvalEquals("test", template);
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity627TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity627TestCase.java
new file mode 100644
index 00000000..faff4cad
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity627TestCase.java
@@ -0,0 +1,49 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-627. Make sure Foreach
+ * Error message reports correct line numbers.
+ */
+
+public class Velocity627TestCase extends BaseTestCase
+{
+ public Velocity627TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty(RuntimeConstants.SKIP_INVALID_ITERATOR, Boolean.FALSE);
+ }
+
+ public void test627()
+ {
+ // Make sure the error ouput contains "line 3, column 16"
+ assertEvalExceptionAt("##\n##\n#foreach($i in \"junk\")blaa#end", 3, 16);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity629TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity629TestCase.java
new file mode 100644
index 00000000..aa9132cf
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity629TestCase.java
@@ -0,0 +1,55 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-629. Make sure string literals
+ * Error message reports correct line and column numbers.
+ */
+public class Velocity629TestCase extends BaseTestCase
+{
+ public Velocity629TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void test629()
+ {
+ String template = "##\n"+
+ "##\n"+
+ "#set($list=[1])#set($x=\"\n"+
+ "$list.get(1)\n"+
+ "\")";
+ // Make sure the error ouput contains "line 4, column 7" if not throw
+ assertEvalExceptionAt(template, 4, 7);
+
+ template = "##\n"+
+ "##\n"+
+ "#set($x=\"#if\")";
+ assertEvalExceptionAt(template, 3, 9);
+
+ template = "##\n"+
+ "##\n"+
+ "#macro(test $i)$i#end#set($list=[1])#test(\"$list.get(1)\")";
+ assertEvalExceptionAt(template, 3, 50);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity62TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity62TestCase.java
new file mode 100755
index 00000000..e76a18bd
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity62TestCase.java
@@ -0,0 +1,63 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-62.
+ */
+public class Velocity62TestCase extends BaseTestCase
+{
+ public Velocity62TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ context.put("foo", "foo");
+ }
+
+ public void testNested()
+ {
+ String template = "#macro( outer )#set( $foo = 'bar' )#inner()#end"+
+ "#macro( inner )$foo#end"+
+ "#inner()#outer()#inner()";
+ assertEvalEquals("foobarbar", template);
+ }
+
+ public void testRecursive()
+ {
+ context.put("i", 1);
+ String template = "#macro(recurse $i)"+
+ "$i"+
+ "#if( $i < 5 )"+
+ "#set( $i = $i + 1 )"+
+ "#recurse($i)"+
+ "#end"+
+ "#end"+
+ "#recurse(1)";
+ assertEvalEquals("12345", template);
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity631TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity631TestCase.java
new file mode 100755
index 00000000..99289433
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity631TestCase.java
@@ -0,0 +1,49 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-631.
+ */
+public class Velocity631TestCase extends BaseTestCase
+{
+ public Velocity631TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty("space.gobbling", "bc");
+ }
+
+
+ public void test631()
+ {
+ assertEvalEquals("$a", "$a #set($b = 1)");
+ assertEvalEquals("$a", "$a#set($b = 1)");
+ assertEvalEquals("$a.b", "$a.b#set($b = 1)");
+ assertEvalEquals("$a.b(", "$a.b(#set($b = 1)");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity644TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity644TestCase.java
new file mode 100644
index 00000000..9e74fe24
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity644TestCase.java
@@ -0,0 +1,57 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-644. Make sure the reported filename
+ * is correct in exceptions when an error occurs in another template file.
+ */
+public class Velocity644TestCase extends BaseTestCase
+{
+ public Velocity644TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp() throws Exception
+ {
+ super.setUp();
+ engine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, FILE_RESOURCE_LOADER_PATH);
+ engine.setProperty(RuntimeConstants.VM_LIBRARY, "testCase644.vm");
+ engine.setProperty(RuntimeConstants.RUNTIME_REFERENCES_STRICT, Boolean.TRUE);
+ context.put("NULL", null);
+ }
+
+ public void test629()
+ {
+ // Calling a null method
+ assertEvalExceptionAt("#nullMethod()", "testCase644.vm", 9, 8);
+ // An invalid array
+ assertEvalExceptionAt("#arrayError()", "testCase644.vm", 4, 8);
+ // An invalid reference
+ assertEvalExceptionAt("#badRef()", "testCase644.vm", 13, 3);
+ // Non iterable object
+ assertEvalExceptionAt("#forloop()", "testCase644.vm", 18, 18);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity667TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity667TestCase.java
new file mode 100644
index 00000000..87f4e5a9
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity667TestCase.java
@@ -0,0 +1,39 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-667. Make "#macro" throws a parse exception
+ */
+public class Velocity667TestCase extends BaseTestCase
+{
+ public Velocity667TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void test667()
+ {
+ assertEvalExceptionAt("#macro", 1, 7);
+ assertEvalExceptionAt("#macro #macro", 1, 7);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity682TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity682TestCase.java
new file mode 100644
index 00000000..8808ac3d
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity682TestCase.java
@@ -0,0 +1,63 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-682.
+ */
+public class Velocity682TestCase extends BaseTestCase
+{
+ public Velocity682TestCase(String name)
+ {
+ super(name);
+ //DEBUG = true;
+ }
+
+ public void test682()
+ {
+ engine.setProperty(RuntimeConstants.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ assertEvalEquals("foo1foo2", "#macro(eval $e)#evaluate($e)#end#eval('foo1')#eval('foo2')");
+ }
+
+ public void test682b()
+ {
+ String template = "#macro( eval $e )#evaluate($e)#end" +
+ "#eval('foo')" +
+ "#eval('bar')";
+ String expected = "foo"+
+ "bar";
+ assertEvalEquals(expected, template);
+ }
+
+ public void test682c()
+ {
+ //NOTE: #eval call is apparently swallowing preceding newlines. :(
+ // appears to be a parser issue unrelated to VELOCITY-682
+ String template = "#macro( eval $e )#evaluate($e)#end" +
+ "\n#eval('foo')" +
+ "\n\n#eval('bar')";
+ String expected = "foo"+
+ "\nbar";
+ assertEvalEquals(expected, template);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity689TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity689TestCase.java
new file mode 100755
index 00000000..7ff2ddf8
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity689TestCase.java
@@ -0,0 +1,78 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-689.
+ */
+public class Velocity689TestCase extends BaseTestCase
+{
+ public Velocity689TestCase(String name)
+ {
+ super(name);
+ //DEBUG = true;
+ }
+
+ @Override
+ public void setUpContext(VelocityContext ctx)
+ {
+ ctx.put("foo", new Foo());
+ }
+
+ public void testIt()
+ {
+ String template = "$foo.baz, $foo.bar";
+ assertEvalEquals("baz, bar", template);
+ }
+
+ public interface HasMethod
+ {
+ String getBar();
+ }
+
+ public interface HasOtherMethod extends HasMethod
+ {
+ String getBaz();
+ }
+
+ public interface NoMethod extends HasOtherMethod
+ {
+ // nada!
+ }
+
+ private static class Foo implements NoMethod
+ {
+ @Override
+ public String getBar()
+ {
+ return "bar";
+ }
+
+ @Override
+ public String getBaz()
+ {
+ return "baz";
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity701TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity701TestCase.java
new file mode 100755
index 00000000..69bbf21e
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity701TestCase.java
@@ -0,0 +1,72 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-701.
+ */
+public class Velocity701TestCase extends BaseTestCase
+{
+ public Velocity701TestCase(String name)
+ {
+ super(name);
+ //DEBUG = true;
+ }
+
+ public void testAbstractClass()
+ {
+ context.put("foo", new Foo() {
+ @Override
+ public String getBar() {
+ return "bar";
+ }
+ });
+ assertEvalEquals("bar", "$foo.bar");
+ }
+
+ public static abstract class Foo {
+
+ public abstract String getBar();
+
+ }
+
+ public void testEnum()
+ {
+ context.put("bar", Bar.ONE);
+ assertEvalEquals("foo", "$bar.foo");
+ }
+
+ public enum Bar {
+
+ ONE(){
+ @Override
+ public String getFoo() {
+ return "foo";
+ }
+ };
+
+ //This was an abstract method, but Velocity 1.6 quit working with it.
+ public abstract String getFoo();
+
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity702TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity702TestCase.java
new file mode 100755
index 00000000..ed74c6e3
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity702TestCase.java
@@ -0,0 +1,99 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.runtime.resource.util.StringResourceRepository;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-702.
+ */
+public class Velocity702TestCase extends BaseTestCase
+{
+ public Velocity702TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUpEngine(VelocityEngine engine)
+ {
+ engine.setProperty(RuntimeConstants.RESOURCE_LOADERS, "high,low");
+ engine.addProperty("high.resource.loader.class", StringResourceLoader.class.getName());
+ engine.addProperty("high.resource.loader.cache", "false");
+ engine.addProperty("high.resource.loader.repository.name", "high");
+ engine.addProperty("high.resource.loader.repository.static", "false");
+ engine.addProperty("high.resource.loader.modificationCheckInterval", "1");
+ engine.addProperty("low.resource.loader.class", StringResourceLoader.class.getName());
+ engine.addProperty("low.resource.loader.cache", "true");
+ engine.addProperty("low.resource.loader.repository.name", "low");
+ engine.addProperty("low.resource.loader.repository.static", "false");
+ engine.addProperty("low.resource.loader.modificationCheckInterval", "1");
+ engine.init();
+ }
+
+ public void testIt() throws Exception
+ {
+ addToHigh("foo", "foo");
+ addToLow("foo", "bar");
+ assertTmplEquals("foo", "foo");
+
+ removeFromHigh("foo");
+ assertTmplEquals("bar", "foo");
+
+ Thread.sleep(1500);
+ addToHigh("foo", "woogie");
+ assertTmplEquals("woogie", "foo");
+ }
+
+ private void addToHigh(String name, String content)
+ {
+ getHighRepo().putStringResource(name, content);
+ }
+
+ private void removeFromHigh(String name)
+ {
+ getHighRepo().removeStringResource(name);
+ }
+
+ private StringResourceRepository getHighRepo()
+ {
+ return (StringResourceRepository)engine.getApplicationAttribute("high");
+ }
+
+ private void addToLow(String name, String content)
+ {
+ getLowRepo().putStringResource(name, content);
+ }
+
+ private void removeFromLow(String name)
+ {
+ getLowRepo().removeStringResource(name);
+ }
+
+ private StringResourceRepository getLowRepo()
+ {
+ return (StringResourceRepository)engine.getApplicationAttribute("low");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity709TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity709TestCase.java
new file mode 100644
index 00000000..50219726
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity709TestCase.java
@@ -0,0 +1,54 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-709.
+ */
+public class Velocity709TestCase extends BaseTestCase
+{
+ public Velocity709TestCase(String name)
+ {
+ super(name);
+ // DEBUG = true;
+ }
+
+ public void testEscapedBackslashInSetDirective()
+ {
+ String backslash = "\\";
+ String template = "#set($var = \"" + backslash + "\" )#set($var2 = \"${var}\")$var2";
+ System.out.println(template);
+ assertEvalEquals("\\", template);
+ }
+
+ public void testEscapedDoubleQuote()
+ {
+ String template = "#set($foo = \"jeah \"\"baby\"\" jeah! \"\"\"\"\")$foo";
+ assertEvalEquals("jeah \"baby\" jeah! \"\"", template);
+ }
+
+ public void testEscapedSingleQuote()
+ {
+ String template = "#set($foo = 'jeah ''baby'' jeah!')$foo";
+ assertEvalEquals("jeah 'baby' jeah!", template);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity727TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity727TestCase.java
new file mode 100644
index 00000000..46038152
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity727TestCase.java
@@ -0,0 +1,40 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-727.
+ */
+public class Velocity727TestCase extends BaseTestCase
+{
+ public Velocity727TestCase(String name)
+ {
+ super(name);
+ DEBUG = false;
+ }
+
+ public void testDefineWithNoArgument()
+ {
+ assertEvalException("#define() foo bar #end", VelocityException.class);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity728TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity728TestCase.java
new file mode 100644
index 00000000..824576d8
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity728TestCase.java
@@ -0,0 +1,40 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-728.
+ */
+public class Velocity728TestCase extends BaseTestCase
+{
+ public Velocity728TestCase(String name)
+ {
+ super(name);
+ DEBUG = false;
+ }
+
+ public void testParseWithNoArgument()
+ {
+ assertEvalException("#parse()", VelocityException.class);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity729TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity729TestCase.java
new file mode 100644
index 00000000..869c609f
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity729TestCase.java
@@ -0,0 +1,46 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-729.
+ */
+public class Velocity729TestCase extends BaseTestCase
+{
+ public Velocity729TestCase(String name)
+ {
+ super(name);
+ // DEBUG = true;
+ }
+
+ public void testDotRightAfterDollarReference()
+ {
+ String s = "$.x schmoo $jee";
+ context.put("jee", "foo");
+ assertEvalEquals("$.x schmoo foo", s);
+ }
+
+ public void testVelocity754jQueryPost()
+ {
+ assertSchmoo("$.post(\"someUrl\", \"\")");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity736TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity736TestCase.java
new file mode 100644
index 00000000..0a62dce3
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity736TestCase.java
@@ -0,0 +1,76 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-736.
+ */
+public class Velocity736TestCase extends BaseTestCase
+{
+ public Velocity736TestCase(String name)
+ {
+ super(name);
+ DEBUG = true;
+ }
+
+ public void testPublicMethodInheritedFromAbstractProtectedClass() throws Exception
+ {
+ try
+ {
+ toobig(100);
+ }
+ catch (Exception e)
+ {
+ context.put("e", e);
+ assertEvalEquals("100", "$e.permittedSize");
+ }
+ }
+
+ public void toobig(long permitted) throws Exception
+ {
+ throw new FileSizeLimitExceededException(permitted);
+ }
+
+ public static class FileUploadException extends Exception {}
+
+ protected abstract static class SizeException extends FileUploadException
+ {
+ private final long permitted;
+ protected SizeException(long permitted)
+ {
+ this.permitted = permitted;
+ }
+ public long getPermittedSize()
+ {
+ return this.permitted;
+ }
+ }
+
+ public static class FileSizeLimitExceededException extends SizeException
+ {
+ public FileSizeLimitExceededException(long permitted)
+ {
+ super(permitted);
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity742TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity742TestCase.java
new file mode 100644
index 00000000..45b7dfde
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity742TestCase.java
@@ -0,0 +1,57 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-742.
+ */
+public class Velocity742TestCase extends BaseTestCase
+{
+ public Velocity742TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ // we need to call init here because otherwise it is not called until assertEvalEquals
+ // and therefore the removeDirective call is ignored.
+ engine.init();
+ }
+
+ public void testDisableAndRestoreDirective()
+ {
+ String s = "#include('doesnotexist.vm') directive is disabled";
+
+ // first remove the #include directive and see that is treated as normal text
+ engine.removeDirective("include");
+ assertEvalEquals(s, s);
+
+ // now reload the directive and see that the include directive works again and
+ // Velocity throws ResourceNotFoundException because it can't find the template
+ engine.loadDirective("org.apache.velocity.runtime.directive.Include");
+ assertEvalException(s, ResourceNotFoundException.class);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity747TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity747TestCase.java
new file mode 100644
index 00000000..5dba3633
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity747TestCase.java
@@ -0,0 +1,102 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.io.FileReader;
+import java.io.StringWriter;
+import java.util.Properties;
+
+/**
+ * This class tests VELOCITY-785.
+ */
+public class Velocity747TestCase extends BaseTestCase
+{
+ public Velocity747TestCase(String name)
+ {
+ super(name);
+ }
+
+ VelocityEngine engine1;
+ VelocityEngine engine2;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ Properties props = new Properties();
+ /* The props file contains *spaces* at the end of the line:
+ * velocimacro.permissions.allow.inline.local.scope = true
+ * which caused the initial problem
+ */
+ props.load(new FileReader(TEST_COMPARE_DIR + "/issues/velocity-747/vel.props"));
+ props.setProperty("file.resource.loader.path", TEST_COMPARE_DIR + "/issues/velocity-747/");
+ engine1 = new VelocityEngine(props);
+
+ //by default, make the engine's log output go to the test-report
+ log = new TestLogger(false, false);
+ engine1.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, log);
+
+ engine2 = new VelocityEngine();
+ engine2.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file,string");
+ engine2.addProperty("file.resource.loader.path", TEST_COMPARE_DIR + "/issues/velocity-747/");
+ engine2.addProperty("file.resource.loader.cache", "true");
+ engine2.addProperty("file.resource.loader.modificationCheckInterval", "-1");
+ engine2.addProperty("velocimacro.permissions.allow.inline.local.scope", "true");
+ engine2.addProperty("velocimacro.max.depth", "-1");
+ engine2.addProperty("string.resource.loader.class", StringResourceLoader.class.getName());
+ engine2.addProperty("string.resource.loader.repository.name", "stringRepo");
+ engine2.addProperty("string.resource.loader.repository.static", "false");
+ log = new TestLogger(false, false);
+ engine2.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, log);
+ }
+
+ public void testMacroIsolation1()
+ {
+ StringWriter writer = new StringWriter();
+ VelocityContext ctx = new VelocityContext();
+ engine1.mergeTemplate("one.vm", "UTF-8", new VelocityContext(), writer);
+ String result = writer.toString();
+ assertEquals(result, "This is from Test1 macro of one.vm");
+ writer = new StringWriter();
+ engine1.mergeTemplate("two.vm", "UTF-8", ctx, writer);
+ result = writer.toString();
+ assertEquals(result, "This is from Test1 macro of two.vm");
+ }
+
+ public void testMacroIsolation2()
+ {
+ StringWriter writer = new StringWriter();
+ VelocityContext ctx = new VelocityContext();
+ engine2.mergeTemplate("one.vm", "UTF-8", new VelocityContext(), writer);
+ String result = writer.toString();
+ assertEquals(result, "This is from Test1 macro of one.vm");
+
+ writer = new StringWriter();
+ engine2.mergeTemplate("two.vm", "UTF-8", ctx, writer);
+ result = writer.toString();
+ assertEquals(result, "This is from Test1 macro of two.vm");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity753TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity753TestCase.java
new file mode 100755
index 00000000..21fff75c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity753TestCase.java
@@ -0,0 +1,62 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-753.
+ */
+public class Velocity753TestCase extends BaseTestCase
+{
+ public Velocity753TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testFloatArg() throws Exception
+ {
+ // verify precedence outside of Velocity
+ Tool tool = new Tool();
+ Float f = 5.23f;
+ assertEquals("object", tool.test(f));
+
+ context.put("tool", tool);
+ context.put("float", f);
+
+ String template = "$tool.test($float)";
+ // in reflection-land, Float and float are equivalent, so double is selected
+ assertEvalEquals("double", template);
+ }
+
+ public static class Tool
+ {
+ public String test(double d)
+ {
+ return "double";
+ }
+
+ public String test(Object o)
+ {
+ return "object";
+ }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity755TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity755TestCase.java
new file mode 100755
index 00000000..c3261da4
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity755TestCase.java
@@ -0,0 +1,41 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-755.
+ */
+public class Velocity755TestCase extends BaseTestCase
+{
+ public Velocity755TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testMapOrder()
+ {
+ String template = "#set( $map = {'a': 1, 'b': true, 'c': 3, 'd': false, 'e': 5} )"+
+ "#foreach( $i in $map )$i#end";
+ assertEvalEquals("1true3false5", template);
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity758TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity758TestCase.java
new file mode 100644
index 00000000..4c8d84c2
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity758TestCase.java
@@ -0,0 +1,66 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.event.EventCartridge;
+import org.apache.velocity.app.event.IncludeEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-758.
+ */
+public class Velocity758TestCase extends BaseTestCase
+{
+ public Velocity758TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testNullArgumentForParse()
+ {
+ assertEvalEquals("", "#parse($foo)");
+ }
+
+ public void testOverrideNullArgumentForParse()
+ {
+ String nullContent = "Parse arg was null";
+ addTemplate("null.vm", nullContent);
+
+ EventCartridge ec = new EventCartridge();
+ ec.addEventHandler(new Handler());
+ ec.attachToContext(context);
+
+ assertEvalEquals(nullContent, "#parse($foo)");
+ }
+
+ public static class Handler implements IncludeEventHandler
+ {
+ @Override
+ public String includeEvent(Context context, String parsePath, String parentPath, String directive)
+ {
+ if (parsePath == null)
+ {
+ parsePath = "null.vm";
+ }
+ return parsePath;
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity762TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity762TestCase.java
new file mode 100755
index 00000000..ea025ef3
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity762TestCase.java
@@ -0,0 +1,40 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-762.
+ */
+public class Velocity762TestCase extends BaseTestCase
+{
+ public Velocity762TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testForeachIsLast()
+ {
+ String template = "#foreach( $i in [1..3] )$foreach.last #end";
+ assertEvalEquals("false false true ", template);
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity785TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity785TestCase.java
new file mode 100644
index 00000000..83a60a09
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity785TestCase.java
@@ -0,0 +1,42 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-785.
+ */
+public class Velocity785TestCase extends BaseTestCase
+{
+ public Velocity785TestCase(String name)
+ {
+ super(name);
+ // DEBUG = true;
+ }
+
+ public void testQuoteEscapes()
+ {
+ assertEvalEquals("\"", "#set($double_double = \"\"\"\")$double_double");
+ assertEvalEquals("'", "#set($single_single = '''')$single_single");
+ assertEvalEquals("''", "#set($double_single = \"''\")$double_single");
+ assertEvalEquals("\"\"", "#set($single_double = '\"\"')$single_double");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity830TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity830TestCase.java
new file mode 100644
index 00000000..4f014f4c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity830TestCase.java
@@ -0,0 +1,58 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests the VELOCITY-830 issue.
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ */
+public class Velocity830TestCase extends BaseTestCase
+{
+ public Velocity830TestCase(String name)
+ {
+ super(name);
+ }
+
+ public static class UnderscoreMethodObject
+ {
+ public String check() { return "ok"; }
+ public String _1() { return "gotit"; }
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("obj", new UnderscoreMethodObject());
+ }
+
+ /**
+ * Tests methods name beginning with _
+ */
+ public void testUnderscoreMethod()
+ throws Exception
+ {
+ assertEvalEquals("ok", "$obj.check()");
+ assertEvalEquals("gotit", "$obj._1()");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity855TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity855TestCase.java
new file mode 100755
index 00000000..5d2c94da
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity855TestCase.java
@@ -0,0 +1,47 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-855.
+ */
+public class Velocity855TestCase extends BaseTestCase
+{
+
+ public Velocity855TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("elementKind", javax.lang.model.element.ElementKind.class);
+ context.put("typeKind", javax.lang.model.type.TypeKind.class);
+ }
+
+ public void testVelocity855()
+ {
+ assertEvalEquals("ENUM DECLARED", "$elementKind.valueOf('ENUM') $typeKind.valueOf('DECLARED')");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity889TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity889TestCase.java
new file mode 100755
index 00000000..d4532f84
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity889TestCase.java
@@ -0,0 +1,51 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-589.
+ */
+public class Velocity889TestCase extends BaseTestCase
+{
+ public Velocity889TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testSpaceBeforeRParen()
+ {
+ assertEvalEquals("#foo(\n)", "#foo(\n)");
+ assertEvalEquals("#foo(\n )", "#foo(\n )");
+ }
+
+ public void testSpaceBeforeRParenWithArg()
+ {
+ assertEvalEquals("#foo(\n$bar\n)", "#foo(\n$bar\n)");
+ assertEvalEquals("#foo(\n $bar\n )", "#foo(\n $bar\n )");
+ }
+
+ public void testSpaceBeforeRParenWithDefaultArg()
+ {
+ assertEvalEquals("", "#macro(\nfoo\n,\n$bar\n=\n'bar')\n#end");
+ assertEvalEquals("", "#macro(\n foo\n ,\n $bar\n =\n 'bar'\n )\n #end");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity896TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity896TestCase.java
new file mode 100755
index 00000000..d7ea45da
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity896TestCase.java
@@ -0,0 +1,40 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-589.
+ */
+public class Velocity896TestCase extends BaseTestCase
+{
+ public Velocity896TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testTailingHash()
+ {
+ assertEvalEquals("#", "#");
+ assertEvalEquals("$", "$");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity904TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity904TestCase.java
new file mode 100755
index 00000000..86caa7cf
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity904TestCase.java
@@ -0,0 +1,116 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-904.
+ */
+public class Velocity904TestCase extends BaseTestCase
+{
+ public Velocity904TestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ // that will also test the deprecation of velocimacro.arguments.preserve_literals towards velocimacro.enable_bc_mode
+ engine.setProperty("velocimacro.arguments.preserve_literals", getName().contains("NoPreserve") ? "false" : "true");
+ }
+
+ public void testNullArgNoPreserve()
+ {
+ assertEvalEquals("$parameter", "#macro(testmacro $parameter)$parameter#end#testmacro($return)");
+ }
+
+ public void testNullArgPreserve()
+ {
+ assertEvalEquals("$return", "#macro(testmacro $parameter)$parameter#end#testmacro($return)");
+ }
+
+ public void testArgSetToNullNoPreserve()
+ {
+ assertEvalEquals("$input", "#macro(mymacro $input)#set($input = $null)$input#end#set($variable = 'value')#mymacro($variable)");
+ }
+
+ public void testArgSetToNullPreserve()
+ {
+ assertEvalEquals("$variable", "#macro(mymacro $input)#set($input = $null)$input#end#set($variable = 'value')#mymacro($variable)");
+ }
+
+ public void testSubMacroNoPreserve()
+ {
+ assertEvalEquals("$return$return$return", "#macro(macro1 $return)$return#macro2($param2)$return#end#macro(macro2 $return)$return#end#macro1($param)");
+ }
+
+ public void testSubMacroPreserve()
+ {
+ assertEvalEquals("$param$param2$param", "#macro(macro1 $return)$return#macro2($param2)$return#end#macro(macro2 $return)$return#end#macro1($param)");
+ }
+
+ public void testNoArgNoPreserve()
+ {
+ assertEvalEquals("","#macro(testMacro $param)#end#testMacro()");
+ }
+
+ public void testNoArgPreserve()
+ {
+ assertEvalEquals("","#macro(testMacro $param)#end#testMacro()");
+ }
+
+ public void testConstantSetToNullNoPreserve()
+ {
+ assertEvalEquals("$input", "#macro(mymacro $input)#set($input = $null)$input#end#mymacro('string-value')");
+ assertEvalEquals("$input", "#macro(mymacro $input)#set($input = $null)$input#end#mymacro(\"interpolated-$bar-value\")");
+ assertEvalEquals("$input", "#macro(mymacro $input)#set($input = $null)$input#end#mymacro(true)");
+ assertEvalEquals("$input", "#macro(mymacro $input)#set($input = $null)$input#end#mymacro(4.5)");
+ }
+
+ public void testConstantSetToNullPreserve()
+ {
+ assertEvalEquals("'string-value'", "#macro(mymacro $input)#set($input = $null)$input#end#mymacro('string-value')");
+ assertEvalEquals("\"interpolated-$bar-value\"", "#macro(mymacro $input)#set($input = $null)$input#end#mymacro(\"interpolated-$bar-value\")");
+ assertEvalEquals("$input", "#macro(mymacro $input)#set($input = $null)$input#end#mymacro(true)");
+ assertEvalEquals("$input", "#macro(mymacro $input)#set($input = $null)$input#end#mymacro(4.5)");
+ }
+
+ public void testConstantNoPreserve()
+ {
+ assertEvalEquals("true", "#macro(mymacro $input)$input#end#mymacro(true)");
+ assertEvalEquals("1.5", "#macro(mymacro $input)$input#end#mymacro(1.5)");
+ assertEvalEquals("foo", "#macro(mymacro $input)$input#end#mymacro('foo')");
+ assertEvalEquals("{}", "#macro(mymacro $input)$input#end#mymacro({})");
+ assertEvalEquals("[]", "#macro(mymacro $input)$input#end#mymacro([])");
+ }
+
+ public void testConstantPreserve()
+ {
+ assertEvalEquals("true", "#macro(mymacro $input)$input#end#mymacro(true)");
+ assertEvalEquals("1.5", "#macro(mymacro $input)$input#end#mymacro(1.5)");
+ assertEvalEquals("foo", "#macro(mymacro $input)$input#end#mymacro('foo')");
+ assertEvalEquals("{}", "#macro(mymacro $input)$input#end#mymacro({})");
+ assertEvalEquals("[]", "#macro(mymacro $input)$input#end#mymacro([])");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity919TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity919TestCase.java
new file mode 100644
index 00000000..33397cfc
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity919TestCase.java
@@ -0,0 +1,23 @@
+package org.apache.velocity.test.issues;
+
+import org.apache.velocity.test.BaseTestCase;
+
+public class Velocity919TestCase extends BaseTestCase
+{
+ public Velocity919TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testUnbreakableSpace() throws Exception
+ {
+ assertEvalEquals("before\u200Bafter", "before\u200Bafter");
+ }
+
+ public void testUserFileSeparator() throws Exception
+ {
+ assertEvalEquals("before\u001Cafter", "before\u001Cafter");
+ }
+
+}
+
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity924TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity924TestCase.java
new file mode 100755
index 00000000..5c1ee041
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity924TestCase.java
@@ -0,0 +1,65 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-855.
+ */
+public class Velocity924TestCase extends BaseTestCase
+{
+ public Velocity924TestCase(String name)
+ {
+ super(name);
+ }
+
+ public static class Foo
+ {
+ public String getName() { return "foo"; }
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("var", new Foo());
+ }
+
+ public void testVelocity924Getter()
+ {
+ assertEvalEquals("org.apache.velocity.test.issues.Velocity924TestCase$Foo foo", "$var.class.name $var.name");
+ }
+
+ public void testVelocity924Method()
+ {
+ assertEvalEquals("org.apache.velocity.test.issues.Velocity924TestCase$Foo foo", "$var.class.getName() $var.getName()");assertEvalEquals("org.apache.velocity.test.issues.Velocity924TestCase$Foo foo", "$var.class.name $var.name");
+ }
+
+ public void testVelocity924Getter2()
+ {
+ assertEvalEquals("foo org.apache.velocity.test.issues.Velocity924TestCase$Foo", "$var.name $var.class.name");
+ }
+
+ public void testVelocity924Method2()
+ {
+ assertEvalEquals("foo org.apache.velocity.test.issues.Velocity924TestCase$Foo", "$var.getName() $var.class.getName()");assertEvalEquals("org.apache.velocity.test.issues.Velocity924TestCase$Foo foo", "$var.class.name $var.name");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity926TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity926TestCase.java
new file mode 100755
index 00000000..41a33213
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity926TestCase.java
@@ -0,0 +1,38 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests VELOCITY-926.
+ */
+public class Velocity926TestCase extends BaseTestCase
+{
+ public Velocity926TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testNamesCollision()
+ {
+ assertEvalEquals("bar foo", "#set($foo='foo')#set($bar='bar')#macro(test, $foo, $bar)$foo $bar#end#test($bar, $foo)");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity927TestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity927TestCase.java
new file mode 100755
index 00000000..218ed7f2
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/Velocity927TestCase.java
@@ -0,0 +1,39 @@
+package org.apache.velocity.test.issues;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * This class tests bugfix for http://stackoverflow.com/questions/32805217/bug-or-hidden-feature-in-apache-velocity
+ */
+public class Velocity927TestCase extends BaseTestCase
+{
+ public Velocity927TestCase(String name)
+ {
+ super(name);
+ }
+
+ public void testIt()
+ {
+ assertEvalEquals("", "#set($map = {\n })");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingDirective.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingDirective.java
new file mode 100644
index 00000000..0717310a
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingDirective.java
@@ -0,0 +1,61 @@
+package org.apache.velocity.test.misc;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.directive.Directive;
+import org.apache.velocity.runtime.parser.node.Node;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * that always throws an exception. Used to test
+ * that RuntimeExceptions are passed through.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class ExceptionGeneratingDirective extends Directive
+{
+ @Override
+ public String getName()
+ {
+ return "Exception";
+ }
+
+ @Override
+ public int getType()
+ {
+ return Directive.BLOCK;
+ }
+
+ @Override
+ public boolean render(InternalContextAdapter context, Writer writer, Node node)
+ throws IOException, ResourceNotFoundException, ParseErrorException,
+ MethodInvocationException
+ {
+ throw new RuntimeException("exception");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingEventHandler.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingEventHandler.java
new file mode 100644
index 00000000..037594ac
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingEventHandler.java
@@ -0,0 +1,55 @@
+package org.apache.velocity.test.misc;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.app.event.IncludeEventHandler;
+import org.apache.velocity.app.event.MethodExceptionEventHandler;
+import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.util.introspection.Info;
+
+/**
+ * Event handlers that always throws an exception. Used to test
+ * that RuntimeExceptions are passed through.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class ExceptionGeneratingEventHandler implements IncludeEventHandler,
+ MethodExceptionEventHandler, ReferenceInsertionEventHandler
+{
+
+ @Override
+ public String includeEvent(Context context, String includeResourcePath, String currentResourcePath,
+ String directiveName)
+ {
+ throw new RuntimeException("exception");
+ }
+
+ @Override
+ public Object methodException(Context context, Class<?> claz, String method, Exception e, Info info) { throw new RuntimeException("exception"); }
+
+ @Override
+ public Object referenceInsert(Context context, String reference, Object value)
+ {
+ throw new RuntimeException("exception");
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingResourceLoader.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingResourceLoader.java
new file mode 100644
index 00000000..fef7adb3
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/ExceptionGeneratingResourceLoader.java
@@ -0,0 +1,62 @@
+package org.apache.velocity.test.misc;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.resource.Resource;
+import org.apache.velocity.runtime.resource.loader.ResourceLoader;
+import org.apache.velocity.util.ExtProperties;
+
+import java.io.Reader;
+
+/**
+ * Resource Loader that always throws an exception. Used to test
+ * that RuntimeExceptions are passed through.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class ExceptionGeneratingResourceLoader extends ResourceLoader
+{
+
+ @Override
+ public void init(ExtProperties configuration)
+ {
+ }
+
+ @Override
+ public Reader getResourceReader(String source, String encoding) throws ResourceNotFoundException
+ {
+ throw new RuntimeException("exception");
+ }
+
+ @Override
+ public boolean isSourceModified(Resource resource)
+ {
+ return false;
+ }
+
+ @Override
+ public long getLastModified(Resource resource)
+ {
+ return 0;
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/GetPutObject.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/GetPutObject.java
new file mode 100644
index 00000000..1cc528c2
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/GetPutObject.java
@@ -0,0 +1,35 @@
+package org.apache.velocity.test.misc;
+
+/*
+ * 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.
+ */
+
+public class GetPutObject
+{
+ private Object value;
+
+ public Object get()
+ {
+ return value;
+ }
+
+ public void put(final Object value)
+ {
+ this.value = value;
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/TestContext.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/TestContext.java
new file mode 100644
index 00000000..4b8e3bda
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/TestContext.java
@@ -0,0 +1,85 @@
+package org.apache.velocity.test.misc;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.context.Context;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Used for testing EvaluateContext. For testing purposes, this is a case insensitive
+ * context.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class TestContext implements Context
+{
+ Context innerContext = new VelocityContext();
+ Map<String, String> originalKeys = new HashMap<>();
+
+ @Override
+ public boolean containsKey(String key)
+ {
+ return innerContext.containsKey(normalizeKey(key));
+ }
+
+ @Override
+ public Object get(String key)
+ {
+ return innerContext.get(normalizeKey(key));
+ }
+
+ @Override
+ public String[] getKeys()
+ {
+ return originalKeys.values().toArray(new String[originalKeys.size()]);
+ }
+
+ @Override
+ public Object put(String key, Object value)
+ {
+ String normalizedKey = normalizeKey(key);
+ originalKeys.put(key, normalizedKey);
+ return innerContext.put(normalizedKey, value);
+ }
+
+ @Override
+ public Object remove(String key)
+ {
+ originalKeys.remove(key);
+ return innerContext.remove(normalizeKey(key));
+ }
+
+ private String normalizeKey(String key)
+ {
+ if (key == null)
+ {
+ return null;
+ }
+ else
+ {
+ return key.toUpperCase(Locale.ROOT);
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/TestLogger.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/TestLogger.java
new file mode 100644
index 00000000..e7a4e990
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/TestLogger.java
@@ -0,0 +1,360 @@
+package org.apache.velocity.test.misc;
+
+/*
+ * 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.
+ */
+
+import org.slf4j.helpers.FormattingTuple;
+import org.slf4j.helpers.MarkerIgnoringBase;
+import org.slf4j.helpers.MessageFormatter;
+import org.slf4j.spi.LocationAwareLogger;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+/**
+ * Logger implementation that can easily capture output
+ * or suppress it entirely. By default, both capture and suppress
+ * are on. To have this behave like a normal Logger,
+ * you must turn it on() and stopCapture().
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @author Nathan Bubna
+ * @author <a href="mailto:cbrisson@apache.org">Claude Brisson</a>
+ * @version $Id$
+ */
+public class TestLogger extends MarkerIgnoringBase
+{
+ private ByteArrayOutputStream log;
+ private PrintStream systemDotIn;
+
+ private boolean suppress = true;
+ private boolean capture = true;
+ private int enabledLevel = LOG_LEVEL_INFO;
+
+ public TestLogger()
+ {
+ this(true, true);
+ }
+
+ public TestLogger(boolean suppress, boolean capture)
+ {
+ this.suppress = suppress;
+ this.capture = capture;
+ if (suppress)
+ {
+ off();
+ }
+ else if (capture)
+ {
+ startCapture();
+ }
+ }
+
+ public static final int LOG_LEVEL_TRACE = LocationAwareLogger.TRACE_INT;
+ public static final int LOG_LEVEL_DEBUG = LocationAwareLogger.DEBUG_INT;
+ public static final int LOG_LEVEL_INFO = LocationAwareLogger.INFO_INT;
+ public static final int LOG_LEVEL_WARN = LocationAwareLogger.WARN_INT;
+ public static final int LOG_LEVEL_ERROR = LocationAwareLogger.ERROR_INT;
+
+ private static int stringToLevel(String levelStr)
+ {
+ if ("trace".equalsIgnoreCase(levelStr)) return LOG_LEVEL_TRACE;
+ else if ("debug".equalsIgnoreCase(levelStr)) return LOG_LEVEL_DEBUG;
+ else if ("info".equalsIgnoreCase(levelStr)) return LOG_LEVEL_INFO;
+ else if ("warn".equalsIgnoreCase(levelStr)) return LOG_LEVEL_WARN;
+ else if ("error".equalsIgnoreCase(levelStr)) return LOG_LEVEL_ERROR;
+ // assume INFO by default
+ return LOG_LEVEL_INFO;
+ }
+
+ private static String getPrefix(int level)
+ {
+ if (level <= LOG_LEVEL_TRACE) return " [trace] ";
+ else if (level <= LOG_LEVEL_DEBUG) return " [debug] ";
+ else if (level <= LOG_LEVEL_INFO) return " [info] ";
+ else if (level <= LOG_LEVEL_WARN) return " [warn] ";
+ else return " [error]";
+ }
+
+ public synchronized void on()
+ {
+ if (suppress)
+ {
+ suppress = false;
+ if (capture)
+ {
+ startCapture();
+ }
+ }
+ }
+
+ public synchronized void off()
+ {
+ suppress = true;
+ }
+
+ public synchronized void startCapture()
+ {
+ capture = true;
+ if (!suppress)
+ {
+ log = new ByteArrayOutputStream();
+ systemDotIn = new PrintStream(log, true);
+ }
+ }
+
+ public synchronized void stopCapture()
+ {
+ capture = false;
+ }
+
+ public void setEnabledLevel(int level)
+ {
+ enabledLevel = level;
+ }
+
+ public boolean isLevelEnabled(int level)
+ {
+ return !suppress && level >= enabledLevel;
+ }
+
+ /**
+ * Return the captured log messages to date.
+ * @return log messages
+ */
+ public String getLog()
+ {
+ return log.toString();
+ }
+
+ private synchronized void log(int level, String msg, Throwable t)
+ {
+ if(!suppress && level >= enabledLevel)
+ {
+ PrintStream writer = capture ? systemDotIn : System.err;
+ writer.print(getPrefix(enabledLevel));
+ writer.println(msg);
+ if (t != null)
+ {
+ writer.println(t.getMessage());
+ t.printStackTrace(writer);
+ }
+ writer.flush();
+ }
+ }
+
+ /**
+ * Logging API
+ */
+
+ @Override
+ public boolean isTraceEnabled()
+ {
+ return isLevelEnabled(LOG_LEVEL_TRACE);
+ }
+
+ @Override
+ public void trace(String msg)
+ {
+ log(LOG_LEVEL_TRACE, msg, null);
+ }
+
+ @Override
+ public void trace(String format, Object arg)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg);
+ log(LOG_LEVEL_TRACE, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void trace(String format, Object arg1, Object arg2)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
+ log(LOG_LEVEL_TRACE, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void trace(String format, Object[] argArray)
+ {
+ FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
+ log(LOG_LEVEL_TRACE, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void trace(String msg, Throwable t)
+ {
+ log(LOG_LEVEL_TRACE, msg, t);
+ }
+
+ @Override
+ public boolean isDebugEnabled()
+ {
+ return isLevelEnabled(LOG_LEVEL_DEBUG);
+ }
+
+ @Override
+ public void debug(String msg)
+ {
+ log(LOG_LEVEL_DEBUG, msg, null);
+ }
+
+ @Override
+ public void debug(String format, Object arg)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg);
+ log(LOG_LEVEL_DEBUG, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void debug(String format, Object arg1, Object arg2)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
+ log(LOG_LEVEL_DEBUG, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void debug(String format, Object[] argArray)
+ {
+ FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
+ log(LOG_LEVEL_DEBUG, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void debug(String msg, Throwable t)
+ {
+ log(LOG_LEVEL_DEBUG, msg, t);
+ }
+
+ @Override
+ public boolean isInfoEnabled()
+ {
+ return isLevelEnabled(LOG_LEVEL_INFO);
+ }
+
+ @Override
+ public void info(String msg)
+ {
+ log(LOG_LEVEL_INFO, msg, null);
+ }
+
+ @Override
+ public void info(String format, Object arg)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg);
+ log(LOG_LEVEL_INFO, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void info(String format, Object arg1, Object arg2)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
+ log(LOG_LEVEL_INFO, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void info(String format, Object[] argArray)
+ {
+ FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
+ log(LOG_LEVEL_INFO, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void info(String msg, Throwable t)
+ {
+ log(LOG_LEVEL_INFO, msg, t);
+ }
+
+ @Override
+ public boolean isWarnEnabled()
+ {
+ return isLevelEnabled(LOG_LEVEL_WARN);
+ }
+
+ @Override
+ public void warn(String msg)
+ {
+ log(LOG_LEVEL_WARN, msg, null);
+ }
+
+ @Override
+ public void warn(String format, Object arg)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg);
+ log(LOG_LEVEL_WARN, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void warn(String format, Object arg1, Object arg2)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
+ log(LOG_LEVEL_WARN, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void warn(String format, Object[] argArray)
+ {
+ FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
+ log(LOG_LEVEL_WARN, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void warn(String msg, Throwable t)
+ {
+ log(LOG_LEVEL_WARN, msg, t);
+ }
+
+ @Override
+ public boolean isErrorEnabled()
+ {
+ return isLevelEnabled(LOG_LEVEL_ERROR);
+ }
+
+ @Override
+ public void error(String msg)
+ {
+ log(LOG_LEVEL_ERROR, msg, null);
+ }
+
+ @Override
+ public void error(String format, Object arg)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg);
+ log(LOG_LEVEL_ERROR, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void error(String format, Object arg1, Object arg2)
+ {
+ FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
+ log(LOG_LEVEL_ERROR, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void error(String format, Object[] argArray)
+ {
+ FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
+ log(LOG_LEVEL_ERROR, ft.getMessage(), ft.getThrowable());
+ }
+
+ @Override
+ public void error(String msg, Throwable t)
+ {
+ log(LOG_LEVEL_ERROR, msg, t);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectTestException.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectTestException.java
new file mode 100644
index 00000000..71a96a4a
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectTestException.java
@@ -0,0 +1,62 @@
+package org.apache.velocity.test.misc;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.util.introspection.Info;
+
+
+
+/**
+ * Exception that returns an Info object for testing after a introspection problem.
+ * This extends Error so that it will stop parsing and allow
+ * internal info to be examined.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ * @author <a href="mailto:isidore@setgame.com">Llewellyn Falco</a>
+ * @version $Id$
+ */
+public class UberspectTestException extends RuntimeException
+{
+
+ /**
+ * Version Id for serializable
+ */
+ private static final long serialVersionUID = 3956896150436225712L;
+
+ Info info;
+
+ public UberspectTestException(String message, Info i)
+ {
+ super(message);
+ info = i;
+ }
+
+ public Info getInfo()
+ {
+ return info;
+ }
+
+ @Override
+ public String getMessage()
+ {
+ return super.getMessage() + "\n failed at " + info;
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectTestImpl.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectTestImpl.java
new file mode 100644
index 00000000..b07f6725
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectTestImpl.java
@@ -0,0 +1,66 @@
+package org.apache.velocity.test.misc;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.util.introspection.Info;
+import org.apache.velocity.util.introspection.UberspectImpl;
+import org.apache.velocity.util.introspection.VelMethod;
+import org.apache.velocity.util.introspection.VelPropertyGet;
+
+
+/**
+ * A introspector that allows testing when methods are not found.
+ */
+public class UberspectTestImpl extends UberspectImpl
+{
+
+ @Override
+ public VelMethod getMethod(Object obj, String methodName, Object[] args, Info i)
+ {
+ VelMethod method = super.getMethod(obj, methodName, args, i);
+
+ if (method == null)
+ {
+ if (obj == null)
+ throw new UberspectTestException("Can't call method '" + methodName + "' on null object",i);
+ else
+ throw new UberspectTestException("Did not find method "+ obj.getClass().getName()+"."+methodName, i);
+ }
+
+ return method;
+ }
+
+ @Override
+ public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i)
+ {
+ VelPropertyGet propertyGet = super.getPropertyGet(obj, identifier, i);
+
+ if (propertyGet == null)
+ {
+ if (obj == null)
+ throw new UberspectTestException("Can't call getter '" + identifier + "' on null object",i);
+ else
+ throw new UberspectTestException("Did not find "+ obj.getClass().getName()+"."+identifier, i);
+ }
+
+ return propertyGet;
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectorTestObject.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectorTestObject.java
new file mode 100644
index 00000000..34b9cbe0
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/misc/UberspectorTestObject.java
@@ -0,0 +1,133 @@
+package org.apache.velocity.test.misc;
+
+/*
+ * 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.
+ */
+
+import java.util.Map;
+
+public class UberspectorTestObject
+{
+ private String regular;
+ private String premium;
+
+ private boolean regularBool;
+ private boolean premiumBool;
+
+ private String ambigous;
+
+ private String unambiguous;
+
+ /**
+ * @return the premium
+ */
+ public String getpremium()
+ {
+ return premium;
+ }
+
+ /**
+ * @param premium the premium to set
+ */
+ public void setpremium(String premium)
+ {
+ this.premium = premium;
+ }
+
+ /**
+ * @return the premiumBool
+ */
+ public boolean ispremiumBool()
+ {
+ return premiumBool;
+ }
+
+ /**
+ * @param premiumBool the premiumBool to set
+ */
+ public void setpremiumBool(boolean premiumBool)
+ {
+ this.premiumBool = premiumBool;
+ }
+
+ /**
+ * @return the regular
+ */
+ public String getRegular()
+ {
+ return regular;
+ }
+
+ /**
+ * @param regular the regular to set
+ */
+ public void setRegular(String regular)
+ {
+ this.regular = regular;
+ }
+
+ /**
+ * @return the regularBool
+ */
+ public boolean isRegularBool()
+ {
+ return regularBool;
+ }
+
+ /**
+ * @param regularBool the regularBool to set
+ */
+ public void setRegularBool(boolean regularBool)
+ {
+ this.regularBool = regularBool;
+ }
+
+ /**
+ * @return the ambigous
+ */
+ public String getAmbigous()
+ {
+ return ambigous;
+ }
+
+ /**
+ * @param ambigous the ambigous to set
+ */
+ public void setAmbigous(String ambigous)
+ {
+ this.ambigous = ambigous;
+ }
+
+ /**
+ * @param ambigous the ambigous to set
+ */
+ public void setAmbigous(StringBuffer ambigous)
+ {
+ this.ambigous = ambigous.toString();
+ }
+
+ public void setUnambiguous(String unambiguous)
+ {
+ this.unambiguous = unambiguous;
+ }
+
+ public void setUnambiguous(Map unambiguous)
+ {
+ this.unambiguous = unambiguous.toString();
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/BoolObj.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/BoolObj.java
new file mode 100644
index 00000000..0e90299b
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/BoolObj.java
@@ -0,0 +1,46 @@
+package org.apache.velocity.test.provider;
+
+/*
+ * 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.
+ */
+
+/**
+ * simple class to test boolean property
+ * introspection - can't use TestProvider
+ * as there is a get( String )
+ * and that comes before isProperty
+ * in the search pattern
+ *
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ */
+public class BoolObj
+{
+ public boolean isBoolean()
+ {
+ return true;
+ }
+
+ /*
+ * not isProperty as it's not
+ * boolean return valued...
+ */
+ public String isNotboolean()
+ {
+ return "hello";
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/Child.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/Child.java
new file mode 100644
index 00000000..6515c752
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/Child.java
@@ -0,0 +1,37 @@
+package org.apache.velocity.test.provider;
+
+/*
+ * 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.
+ */
+
+/**
+ * Rudimentary class used in the testbed to test
+ * introspection with subclasses of a particular
+ * class.
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @version $Id$
+ */
+public class Child extends Person
+{
+ @Override
+ public String getName()
+ {
+ return "Child";
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/ForeachMethodCallHelper.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/ForeachMethodCallHelper.java
new file mode 100644
index 00000000..042f8657
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/ForeachMethodCallHelper.java
@@ -0,0 +1,31 @@
+package org.apache.velocity.test.provider;
+
+/*
+ * 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.
+ */
+
+/**
+ * Provides overloaded methods for testing method execution within a foreach
+ * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class ForeachMethodCallHelper
+{
+ public String getFoo(Integer v) { return "int "+v; }
+ public String getFoo(String v) { return "str "+v; }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/NullToStringObject.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/NullToStringObject.java
new file mode 100644
index 00000000..f30e81cb
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/NullToStringObject.java
@@ -0,0 +1,33 @@
+package org.apache.velocity.test.provider;
+
+/*
+ * 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.
+ */
+
+/**
+ * Used to confirm that a null to string is processed properly
+ * @author <a href="mailto:wglass@apache.org">Will Glass-Husain</a>
+ * @version $Id$
+ */
+public class NullToStringObject
+{
+ public String toString()
+ {
+ return null;
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/NumberMethods.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/NumberMethods.java
new file mode 100644
index 00000000..eb70fd9c
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/NumberMethods.java
@@ -0,0 +1,70 @@
+package org.apache.velocity.test.provider;
+
+/*
+ * 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.
+ */
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+
+/**
+ * Used to check that method calls with number parameters are executed correctly.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ */
+public class NumberMethods
+{
+
+ public String numMethod(byte val)
+ {
+ return "byte (" + val + ")";
+ }
+
+ public String numMethod(short val)
+ {
+ return "short (" + val + ")";
+ }
+
+ public String numMethod(int val)
+ {
+ return "int (" + val + ")";
+ }
+
+ public String numMethod(double val)
+ {
+ return "double (" + val + ")";
+ }
+
+ public String numMethod(long val)
+ {
+ return "long (" + val + ")";
+ }
+
+ public String numMethod(BigInteger val)
+ {
+ return "BigInteger (" + val + ")";
+ }
+
+ public String numMethod(BigDecimal val)
+ {
+ return "BigDecimal (" + val + ")";
+ }
+
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/Person.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/Person.java
new file mode 100644
index 00000000..239739d0
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/Person.java
@@ -0,0 +1,39 @@
+package org.apache.velocity.test.provider;
+
+/*
+ * 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.
+ */
+
+/**
+ * Rudimentary class used in the testbed to test
+ * introspection with subclasses of a particular
+ * class.
+ *
+ * This class need to be greatly extended to
+ * be useful :-)
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @version $Id$
+ */
+public class Person
+{
+ public String getName()
+ {
+ return "Person";
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/TestNumber.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/TestNumber.java
new file mode 100644
index 00000000..5850eec5
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/TestNumber.java
@@ -0,0 +1,47 @@
+package org.apache.velocity.test.provider;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.util.TemplateNumber;
+
+/**
+ * Used for testing purposes to check that an object implementing TemplateNumber
+ * will be treated as a Number.
+ *
+ * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
+ */
+public class TestNumber implements TemplateNumber
+{
+
+ private Number n;
+
+ public TestNumber(double val)
+ {
+ n = val;
+ }
+
+ @Override
+ public Number getAsNumber()
+ {
+ return n;
+ }
+
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/TestProvider.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/TestProvider.java
new file mode 100644
index 00000000..60eea81f
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/provider/TestProvider.java
@@ -0,0 +1,362 @@
+package org.apache.velocity.test.provider;
+
+/*
+ * 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.
+ */
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
+
+/**
+ * This class is used by the testbed. Instances of the class
+ * are fed into the context that is set before the AST
+ * is traversed and dynamic content generated.
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @version $Id$
+ */
+public class TestProvider
+{
+ String title = "lunatic";
+ boolean state;
+ Object ob = null;
+
+ public static String PUB_STAT_STRING = "Public Static String";
+
+ int stateint = 0;
+
+
+ public String getName()
+ {
+ return "jason";
+ }
+
+ public Stack getStack()
+ {
+ Stack stack = new Stack();
+ stack.push("stack element 1");
+ stack.push("stack element 2");
+ stack.push("stack element 3");
+ return stack;
+ }
+
+ public List getEmptyList()
+ {
+ return new ArrayList();
+ }
+
+ public List getList()
+ {
+ List list = new ArrayList();
+ list.add("list element 1");
+ list.add("list element 2");
+ list.add("list element 3");
+
+ return list;
+ }
+
+ public Hashtable getSearch()
+ {
+ Hashtable h = new Hashtable();
+ h.put("Text", "this is some text");
+ h.put("EscText", "this is escaped text");
+ h.put("Title", "this is the title");
+ h.put("Index", "this is the index");
+ h.put("URL", "http://periapt.com");
+
+ ArrayList al = new ArrayList();
+ al.add(h);
+
+ h.put("RelatedLinks", al);
+
+ return h;
+ }
+
+ public Hashtable getHashtable()
+ {
+ Hashtable h = new Hashtable();
+ h.put("key0", "value0");
+ h.put("key1", "value1");
+ h.put("key2", "value2");
+
+ return h;
+ }
+
+ public ArrayList getRelSearches()
+ {
+ ArrayList al = new ArrayList();
+ al.add(getSearch());
+
+ return al;
+ }
+
+ public String getTitle()
+ {
+ return title;
+ }
+
+ public void setTitle(String title)
+ {
+ this.title = title;
+ }
+
+ public Object[] getMenu()
+ {
+ //ArrayList al = new ArrayList();
+ Object[] menu = new Object[3];
+ for (int i = 0; i < 3; i++)
+ {
+ Hashtable item = new Hashtable();
+ item.put("id", "item" + (i+1));
+ item.put("name", "name" + (i+1));
+ item.put("label", "label" + (i+1));
+ //al.add(item);
+ menu[i] = item;
+ }
+
+ //return al;
+ return menu;
+ }
+
+ public ArrayList getCustomers()
+ {
+ ArrayList list = new ArrayList();
+
+ list.add("ArrayList element 1");
+ list.add("ArrayList element 2");
+ list.add("ArrayList element 3");
+ list.add("ArrayList element 4");
+
+ return list;
+ }
+
+ public ArrayList getCustomers2()
+ {
+ ArrayList list = new ArrayList();
+
+ list.add(new TestProvider());
+ list.add(new TestProvider());
+ list.add(new TestProvider());
+ list.add(new TestProvider());
+
+ return list;
+ }
+
+ public Object me()
+ {
+ return this;
+ }
+
+ public String toString()
+ {
+ return ("test provider");
+ }
+
+ public Vector getVector()
+ {
+ Vector list = new Vector();
+
+ list.addElement("vector element 1");
+ list.addElement("vector element 2");
+
+ return list;
+ }
+
+ public String[] getArray()
+ {
+ String[] strings = new String[2];
+ strings[0] = "first element";
+ strings[1] = "second element";
+ return strings;
+ }
+
+ public boolean theAPLRules()
+ {
+ return true;
+ }
+
+ public boolean getStateTrue()
+ {
+ return true;
+ }
+
+ public boolean getStateFalse()
+ {
+ return false;
+ }
+
+ public String objectArrayMethod(Object[] o)
+ {
+ return "result of objectArrayMethod";
+ }
+
+ public String concat(Object[] strings)
+ {
+ StringBuilder result = new StringBuilder();
+
+ for (Object string : strings)
+ {
+ result.append((String) string).append(' ');
+ }
+
+ return result.toString();
+ }
+
+ public String concat(List strings)
+ {
+ StringBuilder result = new StringBuilder();
+
+ for (Object string : strings)
+ {
+ result.append((String) string).append(' ');
+ }
+
+ return result.toString();
+ }
+
+ public String objConcat(List objects)
+ {
+ StringBuilder result = new StringBuilder();
+
+ for (Object object : objects)
+ {
+ result.append(object).append(' ');
+ }
+
+ return result.toString();
+ }
+
+ public String parse(String a, Object o, String c, String d)
+ {
+ return a + o.toString() + c + d;
+ }
+
+ public String concat(String a, String b)
+ {
+ return a + b;
+ }
+
+ // These two are for testing subclasses.
+
+ public Person getPerson()
+ {
+ return new Person();
+ }
+
+ public Child getChild()
+ {
+ return new Child();
+ }
+
+ public String showPerson(Person person)
+ {
+ return person.getName();
+ }
+
+ /**
+ * Chop i characters off the end of a string.
+ *
+ * @param string String to chop.
+ * @param i Number of characters to chop.
+ * @return String with processed answer.
+ */
+ public String chop(String string, int i)
+ {
+ return(string.substring(0, string.length() - i));
+ }
+
+ public boolean allEmpty(Object[] list)
+ {
+ int size = list.length;
+
+ for (Object aList : list)
+ if (aList.toString().length() > 0)
+ return false;
+
+ return true;
+ }
+
+ /*
+ * This can't have the signature
+ *
+ * public void setState(boolean state)
+ *
+ * or dynamically invoking the method
+ * doesn't work ... you would have to
+ * put a wrapper around a method for a
+ * real boolean property that takes a
+ * Boolean object if you wanted this to
+ * work. Not really sure how useful it
+ * is anyway. Who cares about boolean
+ * values you can just set a variable.
+ *
+ */
+
+ public void setState(Boolean state)
+ {
+ }
+
+ public void setBangStart( Integer i )
+ {
+ System.out.println("SetBangStart() : called with val = " + i );
+ stateint = i;
+ }
+ public Integer bang()
+ {
+ System.out.println("Bang! : " + stateint );
+ Integer ret = stateint;
+ stateint++;
+ return ret;
+ }
+
+ /**
+ * Test the ability of vel to use a get(key)
+ * method for any object type, not just one
+ * that implements the Map interface.
+ */
+ public String get(String key)
+ {
+ return key;
+ }
+
+ /**
+ * Test the ability of vel to use a put(key)
+ * method for any object type, not just one
+ * that implements the Map interface.
+ */
+ public String put(String key, Object o)
+ {
+ ob = o;
+ return key;
+ }
+
+ public String getFoo()
+ throws Exception
+ {
+ throw new Exception("From getFoo()");
+ }
+
+ public String getThrow()
+ throws Exception
+ {
+ throw new Exception("From getThrow()");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/BaseSQLTest.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/BaseSQLTest.java
new file mode 100644
index 00000000..8bf31dca
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/BaseSQLTest.java
@@ -0,0 +1,96 @@
+package org.apache.velocity.test.sql;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.test.BaseTestCase;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * A base class to implement tests that need a running
+ * Velocity engine and an initialized HSQLDB Database.
+ * It can also be used to test against other database engines
+ * by means of the proper environment parameters, see velocity-engine-core pom.xml file.
+ *
+ * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+ * @version $Id$
+ */
+
+public abstract class BaseSQLTest
+ extends BaseTestCase
+{
+ private static DBHelper dbHelper = null;
+
+ protected String TEST_JDBC_DRIVER_CLASS = System.getProperty("test.jdbc.driver.className");
+ protected String TEST_JDBC_URI = System.getProperty("test.jdbc.uri");
+ protected String TEST_JDBC_LOGIN = System.getProperty("test.jdbc.login");
+ protected String TEST_JDBC_PASSWORD = System.getProperty("test.jdbc.password");
+
+ /**
+ * String (not containing any VTL) used to test unicode
+ */
+ protected String UNICODE_TEMPLATE = "\\u00a9 test \\u0410 \\u0411";
+
+ /**
+ * Name of template for testing unicode.
+ */
+ protected String UNICODE_TEMPLATE_NAME = "testUnicode";
+
+
+ public BaseSQLTest(String name, String path)
+ throws Exception
+ {
+ super(name);
+
+ if (dbHelper == null)
+ {
+ dbHelper = new DBHelper(TEST_JDBC_DRIVER_CLASS, TEST_JDBC_URI, TEST_JDBC_LOGIN, TEST_JDBC_PASSWORD,path + "/create-db.sql");
+ setUpUnicode();
+ }
+ }
+
+ private void setUpUnicode()
+ throws Exception
+ {
+ String insertString = "insert into velocity_template_varchar (vt_id, vt_timestamp, vt_def) VALUES " +
+ "( '" + UNICODE_TEMPLATE_NAME + "', current_timestamp, '" + UNICODE_TEMPLATE + "');";
+ executeSQL(insertString);
+ insertString = "insert into velocity_template_clob (vt_id, vt_timestamp, vt_def) VALUES " +
+ "( '" + UNICODE_TEMPLATE_NAME + "', current_timestamp, '" + UNICODE_TEMPLATE + "');";
+ executeSQL(insertString);
+ }
+
+
+ public void executeSQL(String sql)
+ throws SQLException
+ {
+ Connection connection = dbHelper.getConnection();
+ Statement statement = connection.createStatement();
+ // Oracle and Derby do not want any final ';'
+ if ((TEST_JDBC_DRIVER_CLASS.equals("oracle.jdbc.OracleDriver")
+ || TEST_JDBC_DRIVER_CLASS.equals("org.apache.derby.jdbc.EmbeddedDriver")) && sql.endsWith(";"))
+ {
+ sql = sql.substring(0, sql.length() - 1);
+ }
+ statement.executeUpdate(sql);
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DBHelper.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DBHelper.java
new file mode 100644
index 00000000..4acef0f8
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DBHelper.java
@@ -0,0 +1,123 @@
+package org.apache.velocity.test.sql;
+
+/*
+ * 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.
+ */
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.Statement;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+public class DBHelper
+{
+ private String driverClass = null;
+ private Connection connection = null;
+
+ public DBHelper(String driverClass, String uri, String login, String password, String loadFile) throws Exception
+ {
+ this.driverClass = driverClass;
+ Class.forName(driverClass);
+
+ this.connection = DriverManager.getConnection(uri, login, password);
+
+ if (StringUtils.isNotEmpty(loadFile))
+ {
+ loadSqlFile(loadFile);
+ }
+ }
+
+ public Connection getConnection()
+ {
+ return connection;
+ }
+
+ public void close()
+ {
+
+ try
+ {
+ connection.close();
+ }
+ catch (Exception e)
+ {
+ System.out.println("While closing Connection" + e.getMessage());
+ }
+ }
+
+ // avoid ';' inside BEGIN/END blocks
+ private static int nextSemiColon(final String cmd)
+ {
+ int start = 0;
+ int ret = -1;
+ while (true)
+ {
+ ret = cmd.indexOf(';', start);
+ if (ret == -1) break;
+ int begin = cmd.lastIndexOf("BEGIN", ret);
+ int end = cmd.lastIndexOf("END;", ret);
+ if (begin == -1) break;
+ if (end > begin) break;
+ start = ret + 1;
+ }
+ return ret;
+ }
+
+ private void loadSqlFile(String fileName) throws Exception
+ {
+ Statement statement = null;
+
+ try
+ {
+ String commands = new String(Files.readAllBytes(Paths.get(fileName)), StandardCharsets.UTF_8);
+ // manually eat comments, some engines don't like them
+ Pattern removeComments = Pattern.compile("^--.*$", Pattern.MULTILINE);
+ Matcher matcher = removeComments.matcher(commands);
+ commands = matcher.replaceAll("");
+ for (int targetPos = nextSemiColon(commands); targetPos > -1; targetPos = nextSemiColon(commands))
+ {
+ statement = connection.createStatement();
+ String cmd = commands.substring(0, targetPos + 1);
+ // Oracle doesn't like semi-colons at the end, except for BEGIN/END blocks...
+ // nor does Derby
+ if (driverClass.equals("oracle.jdbc.OracleDriver") && !cmd.endsWith("END;") ||
+ driverClass.equals("org.apache.derby.jdbc.EmbeddedDriver"))
+ {
+ cmd = cmd.substring(0, cmd.length() - 1);
+ }
+ statement.executeUpdate(cmd);
+ commands = commands.substring(targetPos + 2);
+ statement.close();
+ }
+ }
+ finally
+ {
+ if (statement != null)
+ {
+ statement.close();
+ }
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DataSourceResourceLoaderTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DataSourceResourceLoaderTestCase.java
new file mode 100644
index 00000000..757b6e84
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/DataSourceResourceLoaderTestCase.java
@@ -0,0 +1,229 @@
+package org.apache.velocity.test.sql;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.runtime.RuntimeInstance;
+import org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.ExtProperties;
+
+import javax.sql.DataSource;
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+
+public class DataSourceResourceLoaderTestCase
+ extends BaseSQLTest
+{
+ /**
+ * Comparison file extension.
+ */
+ private static final String CMP_FILE_EXT = "cmp";
+
+ /**
+ * Comparison file extension.
+ */
+ private static final String RESULT_FILE_EXT = "res";
+
+ /**
+ * Path to template file. This will get combined with the
+ * application directory to form an absolute path
+ */
+ private final static String DATA_PATH = TEST_COMPARE_DIR + "/ds";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String RESULTS_DIR = TEST_RESULT_DIR + "/ds";
+
+ /**
+ * Results relative to the build directory.
+ */
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/ds/templates";
+
+ /* engine with VARCHAR templates data source */
+ private RuntimeInstance varcharTemplatesEngine = null;
+
+ /* engine with CLOB templates data source */
+ private RuntimeInstance clobTemplatesEngine = null;
+
+ public DataSourceResourceLoaderTestCase(final String name)
+ throws Exception
+ {
+ super(name, DATA_PATH);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(DataSourceResourceLoaderTestCase.class);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+
+ assureResultsDirectoryExists(RESULTS_DIR);
+
+ DataSource ds1 = new TestDataSource(TEST_JDBC_DRIVER_CLASS, TEST_JDBC_URI, TEST_JDBC_LOGIN, TEST_JDBC_PASSWORD);
+ DataSourceResourceLoader rl1 = new DataSourceResourceLoader();
+ rl1.setDataSource(ds1);
+
+ DataSource ds2 = new TestDataSource(TEST_JDBC_DRIVER_CLASS, TEST_JDBC_URI, TEST_JDBC_LOGIN, TEST_JDBC_PASSWORD);
+ DataSourceResourceLoader rl2 = new DataSourceResourceLoader();
+ rl2.setDataSource(ds2);
+
+ ExtProperties props = new ExtProperties();
+ props.addProperty( "resource.loader", "ds" );
+ props.setProperty( "ds.resource.loader.instance", rl1);
+ props.setProperty( "ds.resource.loader.resource.table", "velocity_template_varchar");
+ props.setProperty( "ds.resource.loader.resource.keycolumn", "vt_id");
+ props.setProperty( "ds.resource.loader.resource.templatecolumn", "vt_def");
+ props.setProperty( "ds.resource.loader.resource.timestampcolumn", "vt_timestamp");
+ props.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger(false, false));
+
+ varcharTemplatesEngine = new RuntimeInstance();
+ varcharTemplatesEngine.setConfiguration(props);
+ varcharTemplatesEngine.init();
+
+ ExtProperties props2 = (ExtProperties)props.clone();
+ props2.setProperty( "ds.resource.loader.instance", rl2);
+ props2.setProperty( "ds.resource.loader.resource.table", "velocity_template_clob");
+ clobTemplatesEngine = new RuntimeInstance();
+ clobTemplatesEngine.setConfiguration(props2);
+ clobTemplatesEngine.init();
+ }
+
+ /**
+ * Tests loading and rendering of a simple template. If that works, we are able to get data
+ * from the database.
+ */
+ public void testSimpleTemplate()
+ throws Exception
+ {
+ Template t = executeTest("testTemplate1", varcharTemplatesEngine);
+ assertFalse("Timestamp is 0", 0 == t.getLastModified());
+ t = executeTest("testTemplate1", clobTemplatesEngine);
+ assertFalse("Timestamp is 0", 0 == t.getLastModified()); }
+
+ public void testUnicode(RuntimeInstance engine)
+ throws Exception
+ {
+ Template template = engine.getTemplate(UNICODE_TEMPLATE_NAME);
+
+ Writer writer = new StringWriter();
+ VelocityContext context = new VelocityContext();
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ String outputText = writer.toString();
+
+ if (!normalizeNewlines(UNICODE_TEMPLATE).equals(
+ normalizeNewlines( outputText ) ))
+ {
+ fail("Output incorrect for Template: " + UNICODE_TEMPLATE_NAME);
+ }
+ }
+
+ /**
+ * Now we have a more complex example. Run a very simple tool.
+ * from the database.
+ */
+ public void testRenderTool()
+ throws Exception
+ {
+ Template t = executeTest("testTemplate2", varcharTemplatesEngine);
+ assertFalse("Timestamp is 0", 0 == t.getLastModified());
+ t = executeTest("testTemplate2", clobTemplatesEngine);
+ assertFalse("Timestamp is 0", 0 == t.getLastModified());
+ }
+
+ /**
+ * Will a NULL timestamp choke the loader?
+ */
+ public void testNullTimestamp()
+ throws Exception
+ {
+ Template t = executeTest("testTemplate3", varcharTemplatesEngine);
+ assertEquals("Timestamp is not 0", 0, t.getLastModified());
+ t = executeTest("testTemplate3", clobTemplatesEngine);
+ assertEquals("Timestamp is not 0", 0, t.getLastModified()); }
+
+ /**
+ * Does it load the global Macros from the DB?
+ */
+ public void testMacroInvocation()
+ throws Exception
+ {
+ Template t = executeTest("testTemplate4", varcharTemplatesEngine);
+ assertFalse("Timestamp is 0", 0 == t.getLastModified());
+ t = executeTest("testTemplate4", clobTemplatesEngine);
+ assertFalse("Timestamp is 0", 0 == t.getLastModified());
+ }
+
+ protected Template executeTest(final String templateName, RuntimeInstance engine)
+ throws Exception
+ {
+ Template template = engine.getTemplate(templateName);
+
+ FileOutputStream fos =
+ new FileOutputStream (
+ getFileName(RESULTS_DIR, templateName, RESULT_FILE_EXT));
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ VelocityContext context = new VelocityContext();
+ context.put("tool", new DSRLTCTool());
+
+ template.merge(context, writer);
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULTS_DIR, COMPARE_DIR, templateName,
+ RESULT_FILE_EXT, CMP_FILE_EXT))
+ {
+ fail("Output incorrect for Template: " + templateName);
+ }
+
+ return template;
+ }
+
+ public static final class DSRLTCTool
+ {
+ public int add(final int a, final int b)
+ {
+ return a + b;
+ }
+
+ public String getMessage()
+ {
+ return "And the result is:";
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/TestDataSource.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/TestDataSource.java
new file mode 100644
index 00000000..286cfc2a
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/sql/TestDataSource.java
@@ -0,0 +1,105 @@
+package org.apache.velocity.test.sql;
+
+/*
+ * 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.
+ */
+
+import javax.sql.DataSource;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.logging.Logger;
+
+public class TestDataSource implements DataSource
+{
+
+ private final String url;
+ private final String user;
+ private final String password;
+
+ private PrintWriter logWriter = null;
+
+ private int loginTimeout = 0;
+
+ public TestDataSource(final String driverClass, final String url, final String user, final String password) throws Exception
+ {
+ this.url = url;
+ this.user = user;
+ this.password = password;
+ Class.forName(driverClass);
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException
+ {
+ return DriverManager.getConnection(url, user, password);
+ }
+
+ @Override
+ public Connection getConnection(final String username, final String password)
+ throws SQLException
+ {
+ return DriverManager.getConnection(url, username, password);
+ }
+
+ @Override
+ public PrintWriter getLogWriter() throws SQLException
+ {
+ return logWriter;
+ }
+
+ @Override
+ public int getLoginTimeout() throws SQLException
+ {
+ return loginTimeout;
+ }
+
+ @Override
+ public void setLogWriter(final PrintWriter logWriter) throws SQLException
+ {
+ this.logWriter = logWriter;
+ }
+
+ @Override
+ public void setLoginTimeout(final int loginTimeout) throws SQLException
+ {
+ this.loginTimeout = loginTimeout;
+ }
+
+ @Override
+ public boolean isWrapperFor(final Class iface) throws SQLException
+ {
+ return false;
+ }
+
+ @Override
+ public Object unwrap(final Class iface) throws SQLException
+ {
+ throw new SQLException("Not implemented");
+ }
+
+ /* added to be able to compile with jdk 1.7+ */
+ @Override
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException
+ {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ChainedUberspectorsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ChainedUberspectorsTestCase.java
new file mode 100644
index 00000000..2687d662
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ChainedUberspectorsTestCase.java
@@ -0,0 +1,121 @@
+package org.apache.velocity.test.util.introspection;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.introspection.AbstractChainableUberspector;
+import org.apache.velocity.util.introspection.Info;
+import org.apache.velocity.util.introspection.UberspectImpl;
+import org.apache.velocity.util.introspection.VelPropertyGet;
+import org.apache.velocity.util.introspection.VelPropertySet;
+
+import java.io.StringWriter;
+
+/**
+ * Tests uberspectors chaining
+ */
+public class ChainedUberspectorsTestCase extends BaseTestCase {
+
+ public ChainedUberspectorsTestCase(String name)
+ throws Exception
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(ChainedUberspectorsTestCase.class);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ Velocity.reset();
+ Velocity.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+ Velocity.addProperty(Velocity.UBERSPECT_CLASSNAME,"org.apache.velocity.util.introspection.UberspectImpl");
+ Velocity.addProperty(Velocity.UBERSPECT_CLASSNAME,"org.apache.velocity.test.util.introspection.ChainedUberspectorsTestCase$ChainedUberspector");
+ Velocity.addProperty(Velocity.UBERSPECT_CLASSNAME,"org.apache.velocity.test.util.introspection.ChainedUberspectorsTestCase$LinkedUberspector");
+ Velocity.init();
+ }
+
+ @Override
+ public void tearDown()
+ {
+ }
+
+ public void testChaining()
+ throws Exception
+ {
+ VelocityContext context = new VelocityContext();
+ context.put("foo",new Foo());
+ StringWriter writer = new StringWriter();
+
+ Velocity.evaluate(context,writer,"test","$foo.zeMethod()");
+ assertEquals(writer.toString(),"ok");
+
+ Velocity.evaluate(context,writer,"test","#set($foo.foo = 'someValue')");
+
+ writer = new StringWriter();
+ Velocity.evaluate(context,writer,"test","$foo.bar");
+ assertEquals(writer.toString(),"someValue");
+
+ writer = new StringWriter();
+ Velocity.evaluate(context,writer,"test","$foo.foo");
+ assertEquals(writer.toString(),"someValue");
+ }
+
+ // replaces getFoo by getBar
+ public static class ChainedUberspector extends AbstractChainableUberspector
+ {
+ @Override
+ public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info info)
+ {
+ identifier = identifier.replaceAll("foo","bar");
+ return inner.getPropertySet(obj,identifier,arg,info);
+ }
+ }
+
+ // replaces setFoo by setBar
+ public static class LinkedUberspector extends UberspectImpl
+ {
+ @Override
+ public VelPropertyGet getPropertyGet(Object obj, String identifier, Info info)
+ {
+ identifier = identifier.replaceAll("foo","bar");
+ return super.getPropertyGet(obj,identifier,info);
+ }
+ }
+
+ public static class Foo
+ {
+ private String bar;
+
+ public String zeMethod() { return "ok"; }
+ public String getBar() { return bar; }
+ public void setBar(String s) { bar = s; }
+ }
+
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ClassMapTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ClassMapTestCase.java
new file mode 100644
index 00000000..be0a1e31
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ClassMapTestCase.java
@@ -0,0 +1,110 @@
+package org.apache.velocity.test.util.introspection;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.introspection.ClassMap;
+import org.slf4j.Logger;
+
+/**
+ * Test the ClassMap Lookup
+ */
+public class ClassMapTestCase
+ extends BaseTestCase
+{
+ public ClassMapTestCase(final String name)
+ throws Exception
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(ClassMapTestCase.class);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ Velocity.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger());
+ Velocity.init();
+ }
+
+ @Override
+ public void tearDown()
+ {
+ }
+
+ public void testPrimitives()
+ throws Exception
+ {
+ Logger log = Velocity.getLog();
+
+ ClassMap c = new ClassMap(TestClassMap.class, log);
+ assertNotNull(c.findMethod("setBoolean", new Object[] { Boolean.TRUE }));
+ assertNotNull(c.findMethod("setByte", new Object[] { new Byte((byte) 4)}));
+ assertNotNull(c.findMethod("setCharacter", new Object[] { new Character('c')}));
+ assertNotNull(c.findMethod("setDouble", new Object[] { new Double(8.0) }));
+ assertNotNull(c.findMethod("setFloat", new Object[] { new Float(15.0) }));
+ assertNotNull(c.findMethod("setInteger", new Object[] { new Integer(16) }));
+ assertNotNull(c.findMethod("setLong", new Object[] { new Long(23) }));
+ assertNotNull(c.findMethod("setShort", new Object[] { new Short((short)42)}));
+ }
+
+ public static final class TestClassMap
+ {
+ public void setBoolean(boolean b)
+ {
+ }
+
+ public void setByte(byte b)
+ {
+ }
+
+ public void setCharacter(char c)
+ {
+ }
+
+ public void setDouble(double d)
+ {
+ }
+
+ public void setFloat(float f)
+ {
+ }
+
+ public void setInteger(int i)
+ {
+ }
+
+ public void setLong(long l)
+ {
+ }
+
+ public void setShort(short s)
+ {
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java
new file mode 100644
index 00000000..c15b18fc
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java
@@ -0,0 +1,454 @@
+package org.apache.velocity.test.util.introspection;
+
+/*
+ * 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.
+ */
+
+import junit.framework.TestSuite;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.app.event.MethodExceptionEventHandler;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.RuntimeInstance;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.test.misc.TestLogger;
+import org.apache.velocity.util.introspection.Converter;
+import org.apache.velocity.util.introspection.Info;
+import org.apache.velocity.util.introspection.IntrospectionUtils;
+import org.apache.velocity.util.introspection.TypeConversionHandler;
+import org.apache.velocity.util.introspection.TypeConversionHandlerImpl;
+import org.apache.velocity.util.introspection.Uberspect;
+import org.apache.velocity.util.introspection.UberspectImpl;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Test case for conversion handler
+ */
+public class ConversionHandlerTestCase extends BaseTestCase
+{
+ private static final String RESULT_DIR = TEST_RESULT_DIR + "/conversion";
+
+ private static final String COMPARE_DIR = TEST_COMPARE_DIR + "/conversion/compare";
+
+ public ConversionHandlerTestCase(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ public void setUp()
+ throws Exception
+ {
+ super.setUp();
+ }
+
+ /**
+ * Test suite
+ * @return test suite
+ */
+ public static junit.framework.Test suite()
+ {
+ return new TestSuite(ConversionHandlerTestCase.class);
+ }
+
+ public void testConversionsWithoutHandler()
+ throws Exception
+ {
+ /*
+ * local scope, cache on
+ */
+ VelocityEngine ve = createEngine(false);
+
+ testConversions(ve, "test_conv.vtl", "test_conv_without_handler");
+ }
+
+ public void testConversionsWithHandler()
+ throws Exception
+ {
+ /*
+ * local scope, cache on
+ */
+ VelocityEngine ve = createEngine(true);
+
+ testConversions(ve, "test_conv.vtl", "test_conv_with_handler");
+ }
+
+ public void testConversionMatrix()
+ throws Exception
+ {
+ VelocityEngine ve = createEngine(true);
+ testConversions(ve, "matrix.vhtml", "matrix");
+ }
+
+ public void testCustomConverter()
+ {
+ RuntimeInstance ve = new RuntimeInstance();
+ ve.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ ve.setProperty(Velocity.RUNTIME_LOG_INSTANCE, log);
+ ve.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file");
+ ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, TEST_COMPARE_DIR + "/conversion");
+ ve.init();
+ Uberspect uberspect = ve.getUberspect();
+ assertTrue(uberspect instanceof UberspectImpl);
+ UberspectImpl ui = (UberspectImpl)uberspect;
+ TypeConversionHandler ch = ui.getConversionHandler();
+ assertTrue(ch != null);
+ ch.addConverter(Float.class, Obj.class, new Converter<Float>()
+ {
+ @Override
+ public Float convert(Object o)
+ {
+ return 4.5f;
+ }
+ });
+ ch.addConverter(TypeUtils.parameterize(List.class, Integer.class), String.class, new Converter<List<Integer>>()
+ {
+ @Override
+ public List<Integer> convert(Object o)
+ {
+ return Arrays.<Integer>asList(1,2,3);
+ }
+ });
+ ch.addConverter(TypeUtils.parameterize(List.class, String.class), String.class, new Converter<List<String>>()
+ {
+ @Override
+ public List<String> convert(Object o)
+ {
+ return Arrays.<String>asList("a", "b", "c");
+ }
+ });
+ VelocityContext context = new VelocityContext();
+ context.put("obj", new Obj());
+ Writer writer = new StringWriter();
+ ve.evaluate(context, writer, "test", "$obj.integralFloat($obj) / $obj.objectFloat($obj)");
+ assertEquals("float ok: 4.5 / Float ok: 4.5", writer.toString());
+ writer = new StringWriter();
+ ve.evaluate(context, writer, "test", "$obj.iWantAStringList('anything')");
+ assertEquals("correct", writer.toString());
+ writer = new StringWriter();
+ ve.evaluate(context, writer, "test", "$obj.iWantAnIntegerList('anything')");
+ assertEquals("correct", writer.toString());
+ }
+
+ /* converts *everything* to string "foo" */
+ public static class MyCustomConverter implements TypeConversionHandler
+ {
+ Converter<String> myCustomConverter = new Converter<String>()
+ {
+
+ @Override
+ public String convert(Object o)
+ {
+ return "foo";
+ }
+ };
+
+ @Override
+ public boolean isExplicitlyConvertible(Type formal, Class<?> actual, boolean possibleVarArg)
+ {
+ return true;
+ }
+
+ @Override
+ public Converter<?> getNeededConverter(Type formal, Class<?> actual)
+ {
+ return myCustomConverter;
+ }
+
+ @Override
+ public void addConverter(Type formal, Class<?> actual, Converter<?> converter)
+ {
+ throw new RuntimeException("not implemented");
+ }
+ }
+
+ public void testCustomConversionHandlerInstance()
+ {
+ RuntimeInstance ve = new RuntimeInstance();
+ ve.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ ve.setProperty(Velocity.RUNTIME_LOG_INSTANCE, log);
+ ve.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file");
+ ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, TEST_COMPARE_DIR + "/conversion");
+ ve.setProperty(RuntimeConstants.CONVERSION_HANDLER_INSTANCE, new MyCustomConverter());
+ ve.init();
+ Uberspect uberspect = ve.getUberspect();
+ assertTrue(uberspect instanceof UberspectImpl);
+ UberspectImpl ui = (UberspectImpl)uberspect;
+ TypeConversionHandler ch = ui.getConversionHandler();
+ assertTrue(ch != null);
+ assertTrue(ch instanceof MyCustomConverter);
+ VelocityContext context = new VelocityContext();
+ context.put("obj", new Obj());
+ Writer writer = new StringWriter();
+ ve.evaluate(context, writer, "test", "$obj.objectString(1.0)");
+ assertEquals("String ok: foo", writer.toString());
+ }
+
+ /**
+ * Test conversions
+ * @param ve
+ * @param templateFile template
+ * @param outputBaseFileName
+ * @throws Exception
+ */
+ private void testConversions(VelocityEngine ve, String templateFile, String outputBaseFileName)
+ throws Exception
+ {
+ assureResultsDirectoryExists(RESULT_DIR);
+
+ FileOutputStream fos = new FileOutputStream (getFileName(
+ RESULT_DIR, outputBaseFileName, RESULT_FILE_EXT));
+
+ VelocityContext context = createContext();
+
+ Writer writer = new BufferedWriter(new OutputStreamWriter(fos));
+
+ log.setEnabledLevel(TestLogger.LOG_LEVEL_ERROR);
+
+ Template template = ve.getTemplate(templateFile);
+ template.merge(context, writer);
+
+ /*
+ * Write to the file
+ */
+ writer.flush();
+ writer.close();
+
+ if (!isMatch(RESULT_DIR, COMPARE_DIR, outputBaseFileName,
+ RESULT_FILE_EXT,CMP_FILE_EXT))
+ {
+ String result = getFileContents(RESULT_DIR, outputBaseFileName, RESULT_FILE_EXT);
+ String compare = getFileContents(COMPARE_DIR, outputBaseFileName, CMP_FILE_EXT);
+
+ String msg = "Processed template did not match expected output\n"+
+ "-----Result-----\n"+ result +
+ "----Expected----\n"+ compare +
+ "----------------";
+
+ fail(msg);
+ }
+ }
+
+ public void testOtherConversions() throws Exception
+ {
+ VelocityEngine ve = createEngine(false);
+ VelocityContext context = createContext();
+ StringWriter writer = new StringWriter();
+ ve.evaluate(context, writer,"test", "$strings.join(['foo', 'bar'], ',')");
+ assertEquals("foo,bar", writer.toString());
+ }
+
+ /**
+ * Return and initialize engine
+ * @return
+ */
+ private VelocityEngine createEngine(boolean withConversionsHandler)
+ throws Exception
+ {
+ VelocityEngine ve = new VelocityEngine();
+ ve.setProperty( Velocity.VM_PERM_INLINE_LOCAL, Boolean.TRUE);
+ ve.setProperty(Velocity.RUNTIME_LOG_INSTANCE, log);
+ ve.setProperty(RuntimeConstants.RESOURCE_LOADERS, "file");
+ ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, TEST_COMPARE_DIR + "/conversion");
+ if (withConversionsHandler)
+ {
+ ve.setProperty(RuntimeConstants.EVENTHANDLER_METHODEXCEPTION, PrintException.class.getName());
+ }
+ else
+ {
+ ve.setProperty(RuntimeConstants.CONVERSION_HANDLER_CLASS, "none");
+ }
+ ve.init();
+
+ return ve;
+ }
+
+ public static class PrintException implements MethodExceptionEventHandler
+ {
+ @Override
+ public Object methodException(Context context,
+ Class claz,
+ String method,
+ Exception e,
+ Info info)
+ {
+ // JDK 11+ changed the exception message for big decimal conversion exceptions,
+ // which breaks the (brittle) tests. Clearly, it would be preferred to fix this
+ // right by comparing the result according to the JDK version, this is just a
+ // quick fix to get the build to pass on JDK 11+
+ //
+ if (e.getClass() == NumberFormatException.class && e.getMessage() != null && e.getMessage().startsWith("Character"))
+ {
+ return method + " -> " + e.getClass().getSimpleName() + ": null"; // compatible with JDK8
+ }
+
+ return method + " -> " + e.getClass().getSimpleName() + ": " + e.getMessage();
+ }
+ }
+
+ private VelocityContext createContext()
+ {
+ VelocityContext context = new VelocityContext();
+ Map<String, Object> map = new TreeMap<>();
+ map.put("A. bool-true", true);
+ map.put("B. bool-false", false);
+ map.put("C. byte-0", (byte)0);
+ map.put("D. byte-1", (byte)1);
+ map.put("E. short", (short)125);
+ map.put("F. int", 24323);
+ map.put("G. long", 5235235L);
+ map.put("H. float", 34523.345f);
+ map.put("I. double", 54235.3253d);
+ map.put("J. char", '@');
+ map.put("K. object", new Obj());
+ map.put("L. enum", Obj.Color.GREEN);
+ map.put("M. string", new String("foo"));
+ map.put("M. string-green", new String("green"));
+ map.put("N. string-empty", new String());
+ map.put("O. string-false", new String("false"));
+ map.put("P. string-true", new String("true"));
+ map.put("Q. string-zero", new String("0"));
+ map.put("R. string-integral", new String("123"));
+ map.put("S. string-big-integral", new String("12345678"));
+ map.put("T. string-floating", new String("123.345"));
+ map.put("U. null", null);
+ map.put("V. locale", "fr_FR");
+ map.put("W. BigInteger zero", BigInteger.ZERO);
+ map.put("X. BigInteger one", BigInteger.ONE);
+ map.put("Y. BigInteger ten", BigInteger.TEN);
+ map.put("Y. BigInteger bigint", new BigInteger("12345678901234567890"));
+ map.put("Z. BigDecimal zero", BigDecimal.ZERO);
+ map.put("ZA. BigDecimal one", BigDecimal.ONE);
+ map.put("ZB. BigDecimal ten", BigDecimal.TEN);
+ map.put("ZC. BigDecimal bigdec", new BigDecimal("12345678901234567890.01234567890123456789"));
+ context.put("map", map);
+ context.put("target", new Obj());
+ Class[] types =
+ {
+ Boolean.TYPE,
+ Character.TYPE,
+ Byte.TYPE,
+ Short.TYPE,
+ Integer.TYPE,
+ Long.TYPE,
+ Float.TYPE,
+ Double.TYPE,
+ Boolean.class,
+ Character.class,
+ Byte.class,
+ Short.class,
+ Integer.class,
+ Long.class,
+ BigInteger.class,
+ Float.class,
+ Double.class,
+ BigDecimal.class,
+ Number.class,
+ String.class,
+ Object.class
+ };
+ context.put("types", types);
+ context.put("introspect", new Introspect());
+ context.put("strings", new StringUtils());
+ return context;
+ }
+
+ public static class Obj
+ {
+ public enum Color { RED, GREEN }
+
+ public String integralBoolean(boolean b) { return "boolean ok: " + b; }
+ public String integralByte(byte b) { return "byte ok: " + b; }
+ public String integralShort(short s) { return "short ok: " + s; }
+ public String integralInt(int i) { return "int ok: " + i; }
+ public String integralLong(long l) { return "long ok: " + l; }
+ public String integralFloat(float f) { return "float ok: " + f; }
+ public String integralDouble(double d) { return "double ok: " + d; }
+ public String integralChar(char c) { return "char ok: " + c; }
+ public String objectBoolean(Boolean b) { return "Boolean ok: " + b; }
+ public String objectByte(Byte b) { return "Byte ok: " + b; }
+ public String objectShort(Short s) { return "Short ok: " + s; }
+ public String objectInt(Integer i) { return "Integer ok: " + i; }
+ public String objectLong(Long l) { return "Long ok: " + l; }
+ public String objectBigInteger(BigInteger bi) { return "BigInteger ok: " + bi; }
+ public String objectFloat(Float f) { return "Float ok: " + f; }
+ public String objectDouble(Double d) { return "Double ok: " + d; }
+ public String objectBigDecimal(BigDecimal bd) { return "BigDecimal ok: " + bd; }
+ public String objectCharacter(Character c) { return "Character ok: " + c; }
+ public String objectNumber(Number b) { return "Number ok: " + b; }
+ public String objectObject(Object o) { return "Object ok: " + o; }
+ public String objectString(String s) { return "String ok: " + s; }
+ public String objectEnum(Color c) { return "Enum ok: " + c; }
+ public String locale(Locale loc) { return "Locale ok: " + loc; }
+
+ public String toString() { return "instance of Obj"; }
+
+ public String iWantAStringList(List<String> list)
+ {
+ if (list != null && list.size() == 3 && list.get(0).equals("a") && list.get(1).equals("b") && list.get(2).equals("c"))
+ return "correct";
+ else return "wrong";
+ }
+
+ public String iWantAnIntegerList(List<Integer> list)
+ {
+ if (list != null && list.size() == 3 && list.get(0).equals(1) && list.get(1).equals(2) && list.get(2).equals(3))
+ return "correct";
+ else return "wrong";
+ }
+ }
+
+ public static class Introspect
+ {
+ private TypeConversionHandler handler;
+ public Introspect()
+ {
+ handler = new TypeConversionHandlerImpl();
+ }
+ public boolean isStrictlyConvertible(Class expected, Class provided)
+ {
+ return IntrospectionUtils.isStrictMethodInvocationConvertible(expected, provided, false);
+ }
+ public boolean isImplicitlyConvertible(Class expected, Class provided)
+ {
+ return IntrospectionUtils.isMethodInvocationConvertible(expected, provided, false);
+ }
+ public boolean isExplicitlyConvertible(Class expected, Class provided)
+ {
+ return handler.isExplicitlyConvertible(expected, provided, false);
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/DeprecatedCheckUberspectorsTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/DeprecatedCheckUberspectorsTestCase.java
new file mode 100644
index 00000000..6b5b8857
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/DeprecatedCheckUberspectorsTestCase.java
@@ -0,0 +1,108 @@
+package org.apache.velocity.test.util.introspection;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.test.misc.TestLogger;
+
+
+import java.io.StringWriter;
+
+/**
+ * Tests DeprecatedCheckUberspector
+ */
+public class DeprecatedCheckUberspectorsTestCase extends BaseTestCase {
+
+ public DeprecatedCheckUberspectorsTestCase(String name)
+ throws Exception
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(DeprecatedCheckUberspectorsTestCase.class);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ engine.setProperty(Velocity.RUNTIME_LOG_INSTANCE, new TestLogger(false, true));
+ engine.addProperty(Velocity.UBERSPECT_CLASSNAME, "org.apache.velocity.util.introspection.UberspectImpl");
+ engine.addProperty(Velocity.UBERSPECT_CLASSNAME, "org.apache.velocity.util.introspection.DeprecatedCheckUberspector");
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("obj1", new StandardObject());
+ context.put("obj2", new DeprecatedObject());
+ }
+
+ public void testDeprecatedCheck()
+ throws Exception
+ {
+ engine.init(); // make sure the engine is initialized, so that we get the logger we configured
+ TestLogger logger =(TestLogger)engine.getLog();
+ logger.startCapture(); // reset log capture
+ StringWriter writer = new StringWriter();
+ engine.evaluate(context, writer, "test", "$obj1.foo() $obj1.bar $obj2.foo() $obj2.bar");
+ String log = logger.getLog();
+ String lines[] = log.split("\\r?\\n");
+ assertEquals(lines[0], " [info] Deprecated usage of method [org.apache.velocity.test.util.introspection.DeprecatedCheckUberspectorsTestCase.StandardObject.foo] in test@1,7");
+ assertEquals(lines[1], " [info] Deprecated usage of getter [org.apache.velocity.test.util.introspection.DeprecatedCheckUberspectorsTestCase.StandardObject.getBar] in test@1,19");
+ assertEquals(lines[2], " [info] Deprecated usage of method [org.apache.velocity.test.util.introspection.DeprecatedCheckUberspectorsTestCase.DeprecatedObject.foo] in test@1,29");
+ assertEquals(lines[3], " [info] Deprecated usage of getter [org.apache.velocity.test.util.introspection.DeprecatedCheckUberspectorsTestCase.DeprecatedObject.getBar] in test@1,41");
+ }
+
+ public static class StandardObject
+ {
+ @Deprecated
+ public String foo()
+ {
+ return "foo";
+ }
+
+ @Deprecated
+ public String getBar()
+ {
+ return "bar";
+ }
+ }
+
+ @Deprecated
+ public static class DeprecatedObject
+ {
+ public String foo()
+ {
+ return "foo";
+ }
+
+ public String getBar()
+ {
+ return "bar";
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/EnumConstantConversionTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/EnumConstantConversionTestCase.java
new file mode 100644
index 00000000..3fbddf22
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/EnumConstantConversionTestCase.java
@@ -0,0 +1,77 @@
+package org.apache.velocity.test.util.introspection;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.test.BaseTestCase;
+
+/**
+ * Tests DeprecatedCheckUberspector
+ */
+public class EnumConstantConversionTestCase extends BaseTestCase {
+
+ public EnumConstantConversionTestCase(String name)
+ throws Exception
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(EnumConstantConversionTestCase.class);
+ }
+
+ public static class Obj
+ {
+ public enum Color { RED, GREEN }
+
+ public String getAction(Color color)
+ {
+ switch (color)
+ {
+ case RED: return "Stop";
+ case GREEN: return "Go";
+ default: return "???";
+ }
+ }
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("obj", new Obj());
+ }
+
+ public void testStringToEnumConversion()
+ throws Exception
+ {
+ assertEvalEquals("Stop", "$obj.getAction('RED')");
+ assertEvalEquals("Go", "$obj.getAction('GREEN')");
+ try
+ {
+ String result = evaluate("$obj.getAction('BLUE')");
+ fail();
+ }
+ catch(MethodInvocationException mie) {}
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/UberspectImplTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/UberspectImplTestCase.java
new file mode 100644
index 00000000..c074be5e
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/UberspectImplTestCase.java
@@ -0,0 +1,134 @@
+package org.apache.velocity.test.util.introspection;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.test.BaseTestCase;
+import org.apache.velocity.test.misc.TestLogger;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+/*
+ * 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.
+ */
+
+/**
+ * Tests the default uberspector.
+ */
+public class UberspectImplTestCase extends BaseTestCase
+{
+
+ public UberspectImplTestCase(String name)
+ throws Exception
+ {
+ super(name);
+ }
+
+ public static Test suite()
+ {
+ return new TestSuite(UberspectImplTestCase.class);
+ }
+
+ @Override
+ protected void setUpEngine(VelocityEngine engine)
+ {
+ engine.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, new TestLogger());
+ engine.addProperty(RuntimeConstants.UBERSPECT_CLASSNAME, "org.apache.velocity.util.introspection.UberspectImpl");
+ }
+
+ @Override
+ protected void setUpContext(VelocityContext context)
+ {
+ context.put("privateClass", new PrivateClass());
+ context.put("privateMethod", new PrivateMethod());
+ context.put("publicMethod", new PublicMethod());
+ context.put("iterable", new SomeIterable());
+ context.put("over", new OverloadedMethods());
+ }
+
+ public void testPrivateIterator()
+ throws Exception
+ {
+ assertEvalEquals("", "#foreach($i in $privateClass)$i#end");
+ assertEvalEquals("", "#foreach($i in $privateMethod)$i#end");
+ assertEvalEquals("123", "#foreach($i in $publicMethod)$i#end");
+ }
+
+ public void testIterableForeach()
+ {
+ assertEvalEquals("123", "#foreach($i in $iterable)$i#end");
+ }
+
+ private class PrivateClass
+ {
+ public Iterator iterator()
+ {
+ return Arrays.asList("X", "Y", "Z").iterator();
+ }
+ }
+
+ public class PrivateMethod
+ {
+ private Iterator iterator()
+ {
+ return Arrays.asList("A", "B", "C").iterator();
+ }
+ }
+
+ public class PublicMethod
+ {
+ public Iterator iterator()
+ {
+ return Arrays.asList("1", "2", "3").iterator();
+ }
+ }
+
+ public class SomeIterable implements Iterable
+ {
+ @Override
+ public Iterator iterator()
+ {
+ return Arrays.asList("1", "2", "3").iterator();
+ }
+ }
+
+ public class OverloadedMethods
+ {
+ public String foo() { return "foo0"; }
+ public String foo(String arg1) { return "foo1"; }
+ public String foo(String arg1, String arg2) { return "foo2"; }
+
+ public String bar(Number n, int i) { return "bar1"; }
+ public String bar(Number n, String s) { return "bar2"; }
+ }
+
+ public void testOverloadedMethods()
+ {
+ assertEvalEquals("foo0", "$over.foo()");
+ assertEvalEquals("foo1", "$over.foo('a')");
+ assertEvalEquals("foo1", "$over.foo($null)");
+ assertEvalEquals("foo2", "$over.foo('a', 'b')");
+ assertEvalEquals("foo2", "$over.foo('a', $null)");
+ assertEvalEquals("bar1", "$over.bar(1,1)");
+ assertEvalEquals("$over.bar(1,1.1)", "$over.bar(1,1.1)"); // this one is definitely ambiguous
+ assertEvalEquals("bar2", "$over.bar(1,'1.1')");
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/test/view/TemplateNodeView.java b/velocity-engine-core/src/test/java/org/apache/velocity/test/view/TemplateNodeView.java
new file mode 100644
index 00000000..2fcf411a
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/test/view/TemplateNodeView.java
@@ -0,0 +1,85 @@
+package org.apache.velocity.test.view;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.Template;
+import org.apache.velocity.runtime.RuntimeSingleton;
+import org.apache.velocity.runtime.parser.node.SimpleNode;
+import org.apache.velocity.runtime.visitor.NodeViewMode;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+
+/**
+ * Simple class for dumping the AST for a template.
+ * Good for debugging and writing new directives.
+ */
+public class TemplateNodeView
+{
+ /**
+ * Root of the AST node structure that results from
+ * parsing a template.
+ */
+ private SimpleNode document;
+
+ /**
+ * Visitor used to traverse the AST node structure
+ * and produce a visual representation of the
+ * node structure. Very good for debugging and
+ * writing new directives.
+ */
+ private NodeViewMode visitor;
+
+ /**
+ * Default constructor: sets up the Velocity
+ * Runtime, creates the visitor for traversing
+ * the node structure and then produces the
+ * visual representation by the visitation.
+ */
+ public TemplateNodeView(String templateFile)
+ {
+ try
+ {
+ RuntimeSingleton.init("velocity.properties");
+
+ InputStreamReader isr = new InputStreamReader(
+ new FileInputStream(templateFile),
+ RuntimeSingleton.getString(RuntimeSingleton.INPUT_ENCODING));
+
+ BufferedReader br = new BufferedReader( isr );
+
+ Template tmpl = new Template();
+ tmpl.setName(templateFile);
+ document = RuntimeSingleton.parse( br, tmpl);
+
+ visitor = new NodeViewMode();
+ visitor.setContext(null);
+ visitor.setWriter(new PrintWriter(System.out));
+ document.jjtAccept(visitor, null);
+ }
+ catch (Exception e)
+ {
+ System.out.println(e);
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/velocity-engine-core/src/test/java/org/apache/velocity/util/SimplePoolTestCase.java b/velocity-engine-core/src/test/java/org/apache/velocity/util/SimplePoolTestCase.java
new file mode 100644
index 00000000..87a52b68
--- /dev/null
+++ b/velocity-engine-core/src/test/java/org/apache/velocity/util/SimplePoolTestCase.java
@@ -0,0 +1,67 @@
+package org.apache.velocity.util;
+
+/*
+ * 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.
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Simpletest for the SimplePool
+ *
+ * @version $Id$
+ */
+public class SimplePoolTestCase extends TestCase
+{
+ public static Test suite()
+ {
+ return new TestSuite(SimplePoolTestCase.class);
+ }
+
+ public SimplePoolTestCase(String testName)
+ {
+ super(testName);
+ }
+
+ public void testPool()
+ throws Exception
+ {
+ SimplePool sp = new SimplePool(10);
+
+ for (int i=0; i<10; i++)
+ {
+ sp.put(i);
+ }
+
+ for (int i=9; i>=0; i--)
+ {
+ Integer obj = (Integer) sp.get();
+
+ assertTrue(i == obj);
+ }
+
+ Object[] pool = sp.getPool();
+
+ for (int i=0; i<10; i++)
+ {
+ assertTrue("Pool not empty", pool[i] == null);
+ }
+ }
+}