diff --git a/test/org/apache/catalina/ssi/TestByteArrayServletOutputStream.java b/test/org/apache/catalina/ssi/TestByteArrayServletOutputStream.java new file mode 100644 index 000000000000..e0cd6c0dab60 --- /dev/null +++ b/test/org/apache/catalina/ssi/TestByteArrayServletOutputStream.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.ssi; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests for {@link ByteArrayServletOutputStream}. + */ +public class TestByteArrayServletOutputStream { + + @Test + public void testWriteAndToByteArray() { + ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream(); + + basos.write('H'); + basos.write('i'); + + byte[] result = basos.toByteArray(); + Assert.assertEquals(2, result.length); + Assert.assertEquals('H', result[0]); + Assert.assertEquals('i', result[1]); + } + + + @Test + public void testEmptyStream() { + ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream(); + + byte[] result = basos.toByteArray(); + Assert.assertNotNull(result); + Assert.assertEquals(0, result.length); + } + + + @Test + public void testMultipleWrites() { + ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream(); + + String text = "Hello, World!"; + for (char c : text.toCharArray()) { + basos.write(c); + } + + byte[] result = basos.toByteArray(); + Assert.assertEquals(text.length(), result.length); + Assert.assertEquals(text, new String(result)); + } + + + @Test + public void testIsReady() { + ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream(); + // Default returns false (as per TODO in source) + Assert.assertFalse(basos.isReady()); + } + + + @Test + public void testSetWriteListener() { + ByteArrayServletOutputStream basos = new ByteArrayServletOutputStream(); + // Should not throw — listener is a no-op + basos.setWriteListener(null); + } +} diff --git a/test/org/apache/catalina/ssi/TestExpressionTokenizer.java b/test/org/apache/catalina/ssi/TestExpressionTokenizer.java new file mode 100644 index 000000000000..4a4e527de4da --- /dev/null +++ b/test/org/apache/catalina/ssi/TestExpressionTokenizer.java @@ -0,0 +1,230 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.ssi; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests for {@link ExpressionTokenizer}. + */ +public class TestExpressionTokenizer { + + @Test + public void testEmptyExpression() { + ExpressionTokenizer et = new ExpressionTokenizer(""); + Assert.assertFalse(et.hasMoreTokens()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_END, et.nextToken()); + } + + + @Test + public void testWhitespaceOnlyExpression() { + ExpressionTokenizer et = new ExpressionTokenizer(" "); + Assert.assertFalse(et.hasMoreTokens()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_END, et.nextToken()); + } + + + @Test + public void testSimpleString() { + ExpressionTokenizer et = new ExpressionTokenizer("hello"); + Assert.assertTrue(et.hasMoreTokens()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("hello", et.getTokenValue()); + Assert.assertFalse(et.hasMoreTokens()); + } + + + @Test + public void testBraces() { + ExpressionTokenizer et = new ExpressionTokenizer("(abc)"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_LBRACE, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("abc", et.getTokenValue()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_RBRACE, et.nextToken()); + } + + + @Test + public void testEquality() { + ExpressionTokenizer et = new ExpressionTokenizer("a = b"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("a", et.getTokenValue()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_EQ, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("b", et.getTokenValue()); + } + + + @Test + public void testNotEqual() { + ExpressionTokenizer et = new ExpressionTokenizer("a != b"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_NOT_EQ, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + } + + + @Test + public void testNotOperator() { + ExpressionTokenizer et = new ExpressionTokenizer("!abc"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_NOT, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("abc", et.getTokenValue()); + } + + + @Test + public void testAndOperator() { + ExpressionTokenizer et = new ExpressionTokenizer("a && b"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_AND, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + } + + + @Test + public void testOrOperator() { + ExpressionTokenizer et = new ExpressionTokenizer("a || b"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_OR, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + } + + + @Test + public void testComparisonOperators() { + ExpressionTokenizer et; + + et = new ExpressionTokenizer("a > b"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_GT, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + + et = new ExpressionTokenizer("a < b"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_LT, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + + et = new ExpressionTokenizer("a >= b"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_GE, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + + et = new ExpressionTokenizer("a <= b"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_LE, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + } + + + @Test + public void testQuotedString() { + ExpressionTokenizer et = new ExpressionTokenizer("\"hello world\""); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("hello world", et.getTokenValue()); + } + + + @Test + public void testSingleQuotedString() { + ExpressionTokenizer et = new ExpressionTokenizer("'hello world'"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("hello world", et.getTokenValue()); + } + + + @Test + public void testQuotedStringWithEscape() { + ExpressionTokenizer et = new ExpressionTokenizer("\"hello\\\"world\""); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("hello\\\"world", et.getTokenValue()); + } + + + @Test + public void testRegexPattern() { + ExpressionTokenizer et = new ExpressionTokenizer("/pattern/"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("/pattern/", et.getTokenValue()); + } + + + @Test + public void testRegexPatternWithEscape() { + ExpressionTokenizer et = new ExpressionTokenizer("/patt\\/ern/"); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("/patt\\/ern/", et.getTokenValue()); + } + + + @Test + public void testComplexExpression() { + ExpressionTokenizer et = new ExpressionTokenizer( + "\"abc\" = \"def\" && !\"ghi\""); + + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("abc", et.getTokenValue()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_EQ, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("def", et.getTokenValue()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_AND, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_NOT, et.nextToken()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_STRING, et.nextToken()); + Assert.assertEquals("ghi", et.getTokenValue()); + Assert.assertEquals(ExpressionTokenizer.TOKEN_END, et.nextToken()); + } + + + @Test + public void testGetIndex() { + ExpressionTokenizer et = new ExpressionTokenizer("abc"); + Assert.assertEquals(0, et.getIndex()); + et.nextToken(); + Assert.assertEquals(3, et.getIndex()); + } + + + @Test + public void testIsMetaChar() { + ExpressionTokenizer et = new ExpressionTokenizer(""); + + Assert.assertTrue(et.isMetaChar(' ')); + Assert.assertTrue(et.isMetaChar('\t')); + Assert.assertTrue(et.isMetaChar('(')); + Assert.assertTrue(et.isMetaChar(')')); + Assert.assertTrue(et.isMetaChar('!')); + Assert.assertTrue(et.isMetaChar('<')); + Assert.assertTrue(et.isMetaChar('>')); + Assert.assertTrue(et.isMetaChar('|')); + Assert.assertTrue(et.isMetaChar('&')); + Assert.assertTrue(et.isMetaChar('=')); + + Assert.assertFalse(et.isMetaChar('a')); + Assert.assertFalse(et.isMetaChar('0')); + Assert.assertFalse(et.isMetaChar('"')); + } + + + @Test + public void testTokenValueNullAfterNonString() { + ExpressionTokenizer et = new ExpressionTokenizer("("); + et.nextToken(); + Assert.assertNull(et.getTokenValue()); + } +} diff --git a/test/org/apache/catalina/ssi/TestSSIConditionalState.java b/test/org/apache/catalina/ssi/TestSSIConditionalState.java new file mode 100644 index 000000000000..832c89fc8299 --- /dev/null +++ b/test/org/apache/catalina/ssi/TestSSIConditionalState.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.ssi; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests for {@link SSIConditionalState}. + */ +public class TestSSIConditionalState { + + @Test + public void testDefaultValues() { + SSIConditionalState state = new SSIConditionalState(); + + Assert.assertFalse(state.branchTaken); + Assert.assertEquals(0, state.nestingCount); + Assert.assertFalse(state.processConditionalCommandsOnly); + } + + + @Test + public void testSetBranchTaken() { + SSIConditionalState state = new SSIConditionalState(); + + state.branchTaken = true; + Assert.assertTrue(state.branchTaken); + } + + + @Test + public void testSetNestingCount() { + SSIConditionalState state = new SSIConditionalState(); + + state.nestingCount = 3; + Assert.assertEquals(3, state.nestingCount); + } + + + @Test + public void testSetProcessConditionalCommandsOnly() { + SSIConditionalState state = new SSIConditionalState(); + + state.processConditionalCommandsOnly = true; + Assert.assertTrue(state.processConditionalCommandsOnly); + } +} diff --git a/test/org/apache/catalina/ssi/TestSSIServlet.java b/test/org/apache/catalina/ssi/TestSSIServlet.java new file mode 100644 index 000000000000..5bab93db9e7d --- /dev/null +++ b/test/org/apache/catalina/ssi/TestSSIServlet.java @@ -0,0 +1,304 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.ssi; + +import java.io.File; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; + +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.Wrapper; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; + +/** + * Integration tests for {@link SSIServlet}. + */ +public class TestSSIServlet extends TomcatBaseTest { + + @Test + public void testSSIEchoDateLocal() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = getTemporaryDirectory(); + Context ctx = tomcat.addContext("", docBase.getAbsolutePath()); + + Wrapper wrapper = Tomcat.addServlet(ctx, "ssi", new SSIServlet()); + wrapper.addInitParameter("buffered", "true"); + wrapper.addInitParameter("debug", "0"); + ctx.addServletMappingDecoded("*.shtml", "ssi"); + + // Create test SSI file + File ssiFile = new File(docBase, "test.shtml"); + try (PrintWriter pw = new PrintWriter(new FileWriter(ssiFile))) { + pw.println("
"); + pw.println(""); + pw.println(""); + } + + tomcat.start(); + + ByteChunk res = new ByteChunk(); + res.setCharset(StandardCharsets.UTF_8); + int rc = getUrl("http://localhost:" + getPort() + "/test.shtml", res, null); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + + String body = res.toString(); + Assert.assertNotNull(body); + // DATE_LOCAL should be replaced with actual date + Assert.assertFalse("DATE_LOCAL should be resolved", + body.contains("DATE_LOCAL")); + } + + + @Test + public void testSSISetAndEcho() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = getTemporaryDirectory(); + Context ctx = tomcat.addContext("", docBase.getAbsolutePath()); + + Wrapper wrapper = Tomcat.addServlet(ctx, "ssi", new SSIServlet()); + wrapper.addInitParameter("buffered", "true"); + ctx.addServletMappingDecoded("*.shtml", "ssi"); + + File ssiFile = new File(docBase, "set.shtml"); + try (PrintWriter pw = new PrintWriter(new FileWriter(ssiFile))) { + pw.println(""); + pw.println(""); + pw.println(""); + pw.println(""); + } + + tomcat.start(); + + ByteChunk res = new ByteChunk(); + res.setCharset(StandardCharsets.UTF_8); + int rc = getUrl("http://localhost:" + getPort() + "/set.shtml", res, null); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + + String body = res.toString(); + Assert.assertTrue("Should contain our variable value", + body.contains("Hello SSI")); + } + + + @Test + public void testSSIIfConditional() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = getTemporaryDirectory(); + Context ctx = tomcat.addContext("", docBase.getAbsolutePath()); + + Wrapper wrapper = Tomcat.addServlet(ctx, "ssi", new SSIServlet()); + wrapper.addInitParameter("buffered", "true"); + ctx.addServletMappingDecoded("*.shtml", "ssi"); + + File ssiFile = new File(docBase, "cond.shtml"); + try (PrintWriter pw = new PrintWriter(new FileWriter(ssiFile))) { + pw.println(""); + pw.println(""); + pw.println(""); + pw.println("CONDITION_TRUE"); + pw.println(""); + pw.println("CONDITION_FALSE"); + pw.println(""); + pw.println(""); + } + + tomcat.start(); + + ByteChunk res = new ByteChunk(); + res.setCharset(StandardCharsets.UTF_8); + int rc = getUrl("http://localhost:" + getPort() + "/cond.shtml", res, null); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + + String body = res.toString(); + Assert.assertTrue("True branch should be shown", + body.contains("CONDITION_TRUE")); + Assert.assertFalse("False branch should not be shown", + body.contains("CONDITION_FALSE")); + } + + + @Test + public void testSSIWebInfBlocked() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = getTemporaryDirectory(); + Context ctx = tomcat.addContext("", docBase.getAbsolutePath()); + + Wrapper wrapper = Tomcat.addServlet(ctx, "ssi", new SSIServlet()); + wrapper.addInitParameter("buffered", "true"); + ctx.addServletMappingDecoded("*.shtml", "ssi"); + + tomcat.start(); + + ByteChunk res = new ByteChunk(); + int rc = getUrl("http://localhost:" + getPort() + + "/WEB-INF/secret.shtml", res, null); + + Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); + } + + + @Test + public void testSSIMetaInfBlocked() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = getTemporaryDirectory(); + Context ctx = tomcat.addContext("", docBase.getAbsolutePath()); + + Wrapper wrapper = Tomcat.addServlet(ctx, "ssi", new SSIServlet()); + wrapper.addInitParameter("buffered", "true"); + ctx.addServletMappingDecoded("*.shtml", "ssi"); + + tomcat.start(); + + ByteChunk res = new ByteChunk(); + int rc = getUrl("http://localhost:" + getPort() + + "/META-INF/data.shtml", res, null); + + Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); + } + + + @Test + public void testSSIResourceNotFound() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = getTemporaryDirectory(); + Context ctx = tomcat.addContext("", docBase.getAbsolutePath()); + + Wrapper wrapper = Tomcat.addServlet(ctx, "ssi", new SSIServlet()); + wrapper.addInitParameter("buffered", "true"); + ctx.addServletMappingDecoded("*.shtml", "ssi"); + + tomcat.start(); + + ByteChunk res = new ByteChunk(); + int rc = getUrl("http://localhost:" + getPort() + + "/nonexistent.shtml", res, null); + + Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); + } + + + @Test + public void testSSIPrintenv() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = getTemporaryDirectory(); + Context ctx = tomcat.addContext("", docBase.getAbsolutePath()); + + Wrapper wrapper = Tomcat.addServlet(ctx, "ssi", new SSIServlet()); + wrapper.addInitParameter("buffered", "true"); + ctx.addServletMappingDecoded("*.shtml", "ssi"); + + File ssiFile = new File(docBase, "printenv.shtml"); + try (PrintWriter pw = new PrintWriter(new FileWriter(ssiFile))) { + pw.println("");
+ pw.println("");
+ pw.println("");
+ }
+
+ tomcat.start();
+
+ ByteChunk res = new ByteChunk();
+ res.setCharset(StandardCharsets.UTF_8);
+ int rc = getUrl("http://localhost:" + getPort() +
+ "/printenv.shtml", res, null);
+
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+ String body = res.toString();
+ // printenv should output server variables
+ Assert.assertTrue("Should contain SERVER_NAME",
+ body.contains("SERVER_NAME"));
+ }
+
+
+ @Test
+ public void testSSIConfig() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ File docBase = getTemporaryDirectory();
+ Context ctx = tomcat.addContext("", docBase.getAbsolutePath());
+
+ Wrapper wrapper = Tomcat.addServlet(ctx, "ssi", new SSIServlet());
+ wrapper.addInitParameter("buffered", "true");
+ ctx.addServletMappingDecoded("*.shtml", "ssi");
+
+ File ssiFile = new File(docBase, "config.shtml");
+ try (PrintWriter pw = new PrintWriter(new FileWriter(ssiFile))) {
+ pw.println("");
+ pw.println("");
+ pw.println("");
+ pw.println("");
+ }
+
+ tomcat.start();
+
+ ByteChunk res = new ByteChunk();
+ res.setCharset(StandardCharsets.UTF_8);
+ int rc = getUrl("http://localhost:" + getPort() +
+ "/config.shtml", res, null);
+
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+ String body = res.toString();
+ // With timefmt=%Y, the date should be a 4-digit year
+ Assert.assertTrue("Year should appear in output",
+ body.matches("(?s).*20\\d{2}.*"));
+ }
+
+
+ @Test
+ public void testSSIFsize() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ File docBase = getTemporaryDirectory();
+ Context ctx = tomcat.addContext("", docBase.getAbsolutePath());
+
+ Wrapper wrapper = Tomcat.addServlet(ctx, "ssi", new SSIServlet());
+ wrapper.addInitParameter("buffered", "true");
+ wrapper.addInitParameter("isVirtualWebappRelative", "true");
+ ctx.addServletMappingDecoded("*.shtml", "ssi");
+
+ // Create a target file to check its size
+ File targetFile = new File(docBase, "data.txt");
+ try (PrintWriter pw = new PrintWriter(new FileWriter(targetFile))) {
+ pw.print("Hello World");
+ }
+
+ File ssiFile = new File(docBase, "fsize.shtml");
+ try (PrintWriter pw = new PrintWriter(new FileWriter(ssiFile))) {
+ pw.println("");
+ pw.println("");
+ pw.println("");
+ }
+
+ tomcat.start();
+
+ ByteChunk res = new ByteChunk();
+ res.setCharset(StandardCharsets.UTF_8);
+ int rc = getUrl("http://localhost:" + getPort() +
+ "/fsize.shtml", res, null);
+
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+ }
+}
diff --git a/test/org/apache/catalina/ssi/TestSSIStopProcessingException.java b/test/org/apache/catalina/ssi/TestSSIStopProcessingException.java
new file mode 100644
index 000000000000..ddac227e25fd
--- /dev/null
+++ b/test/org/apache/catalina/ssi/TestSSIStopProcessingException.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.ssi;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for {@link SSIStopProcessingException}.
+ */
+public class TestSSIStopProcessingException {
+
+ @Test
+ public void testDefaultConstructor() {
+ SSIStopProcessingException ex = new SSIStopProcessingException();
+ Assert.assertNull(ex.getMessage());
+ Assert.assertNull(ex.getCause());
+ }
+
+
+ @Test
+ public void testCauseConstructor() {
+ RuntimeException cause = new RuntimeException("root cause");
+ SSIStopProcessingException ex = new SSIStopProcessingException(cause);
+ Assert.assertSame(cause, ex.getCause());
+ }
+
+
+ @Test
+ public void testIsException() {
+ SSIStopProcessingException ex = new SSIStopProcessingException();
+ Assert.assertTrue(ex instanceof Exception);
+ }
+}