This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/10.1.x by this push:
new 35cc6bbe7f Test cases co-authored with CoPilot/GPT-5.4
35cc6bbe7f is described below
commit 35cc6bbe7f9190b02f45b0db037828f2adeb50d0
Author: Mark Thomas <[email protected]>
AuthorDate: Tue Jun 2 21:16:26 2026 +0100
Test cases co-authored with CoPilot/GPT-5.4
---
.../apache/tomcat/util/buf/TestB2CConverter.java | 269 +++++++++++++++++++++
1 file changed, 269 insertions(+)
diff --git a/test/org/apache/tomcat/util/buf/TestB2CConverter.java
b/test/org/apache/tomcat/util/buf/TestB2CConverter.java
index 8c30591f44..a5e83fcfa6 100644
--- a/test/org/apache/tomcat/util/buf/TestB2CConverter.java
+++ b/test/org/apache/tomcat/util/buf/TestB2CConverter.java
@@ -16,6 +16,9 @@
*/
package org.apache.tomcat.util.buf;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.MalformedInputException;
import java.nio.charset.StandardCharsets;
@@ -24,6 +27,8 @@ import java.util.Locale;
import org.junit.Assert;
import org.junit.Test;
+import org.apache.catalina.connector.InputBuffer;
+
public class TestB2CConverter {
private static final byte[] UTF16_MESSAGE =
@@ -140,4 +145,268 @@ public class TestB2CConverter {
}
Assert.assertNotNull(e);
}
+
+
+ @Test
+ public void testLeftoverChunk() throws Exception {
+ // E2 8C A8 is the "keyboard" character
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
+ ByteChunk bc = new ByteChunk();
+ CharChunk cc = new CharChunk();
+ cc.allocate(1, -1);
+
+ bc.append((byte) 0x41);
+
+ bc.append((byte) 0xE2);
+ conv.convert(bc, cc, false);
+ Assert.assertEquals(1, cc.getLength());
+
+ bc.append((byte) 0x8C);
+ conv.convert(bc, cc, false);
+ Assert.assertEquals(1, cc.getLength());
+
+ bc.append((byte) 0xA8);
+ conv.convert(bc, cc, true);
+ // Expect overflow so while all 3 bytes are present, they aren't
converted
+ Assert.assertEquals(1, cc.getLength());
+
+ cc.setLimit(2);
+ cc.makeSpace(2);
+ conv.convert(bc, cc, true);
+ Assert.assertEquals("A\u2328", cc.toString());
+ }
+
+
+ @Test
+ public void testLeftoverBuffer() throws Exception {
+ // E2 8C A8 is the "keyboard" character
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
+ ByteBuffer bb = ByteBuffer.allocate(8);
+ CharBuffer cb = newCharBuffer(1);
+ TesterInputBuffer ib = new TesterInputBuffer(bb);
+
+ bb.put((byte) 0x41);
+ bb.flip();
+
+ conv.convert(bb, cb, ib, false);
+ assertCharBufferEquals("A", cb);
+
+ // Will trigger overflow if there is a complete character
+ bb.clear();
+ bb.put((byte) 0xE2);
+ bb.flip();
+ conv.convert(bb, cb, ib, false);
+ assertCharBufferEquals("A", cb);
+
+ // NO-OP (underflow)
+ conv.convert(bb, cb, ib, false);
+ assertCharBufferEquals("A", cb);
+
+ // Adds second byte of an incomplete 3 byte character to leftovers
+ ib.setNextRealBytes(new byte[] { (byte) 0x8C });
+ conv.convert(bb, cb, ib, false);
+ assertCharBufferEquals("A", cb);
+
+ // Adds final byte of 3 byte character to leftovers. Not converted as
output will overflow
+ ib.setNextRealBytes(new byte[] { (byte) 0xA8 });
+ conv.convert(bb, cb, ib, false);
+ assertCharBufferEquals("A", cb);
+
+ cb = newCharBuffer(2);
+ cb.limit(cb.capacity());
+ cb.put('A');
+ cb.limit(1);
+ conv.convert(bb, cb, ib, false);
+ assertCharBufferEquals("A\u2328", cb);
+ }
+
+
+ @Test
+ public void testLeftoverChunkWithTrailingBytes() throws Exception {
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
+ ByteChunk bc = new ByteChunk();
+ CharChunk cc = new CharChunk();
+ cc.allocate(4, -1);
+
+ bc.append((byte) 0x41);
+ bc.append((byte) 0xE2);
+ conv.convert(bc, cc, false);
+
+ bc.append((byte) 0x8C);
+ conv.convert(bc, cc, false);
+
+ bc.append((byte) 0xA8);
+ bc.append((byte) 0x42);
+ conv.convert(bc, cc, true);
+
+ Assert.assertEquals("A\u2328B", cc.toString());
+ }
+
+
+ @Test
+ public void testLeftoverChunkMalformed() throws Exception {
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8, true);
+ ByteChunk bc = new ByteChunk();
+ CharChunk cc = new CharChunk();
+ cc.allocate(4, -1);
+
+ bc.append((byte) 0x41);
+ bc.append((byte) 0xE2);
+ conv.convert(bc, cc, false);
+
+ bc.append((byte) 0x42);
+ conv.convert(bc, cc, true);
+
+ Assert.assertEquals("A\ufffdB", cc.toString());
+ }
+
+
+ @Test
+ public void testLeftoverChunkMalformedAtEnd() throws Exception {
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8, true);
+ ByteChunk bc = new ByteChunk();
+ CharChunk cc = new CharChunk();
+ cc.allocate(4, -1);
+
+ bc.append((byte) 0x41);
+ bc.append((byte) 0xE2);
+ conv.convert(bc, cc, true);
+
+ Assert.assertEquals("A\ufffd", cc.toString());
+ }
+
+
+ @Test
+ public void testBug54602d() throws Exception {
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
+ ByteBuffer bb = ByteBuffer.allocate(4);
+ CharBuffer cb = newCharBuffer(4);
+ TesterInputBuffer ib = new TesterInputBuffer(bb);
+
+ bb.put(UTF8_PARTIAL);
+ bb.flip();
+ conv.convert(bb, cb, ib, false);
+ assertCharBufferEquals("", cb);
+
+ conv.convert(bb, cb, ib, false);
+ assertCharBufferEquals("", cb);
+
+ Exception e = null;
+ try {
+ conv.convert(bb, cb, ib, true);
+ } catch (MalformedInputException mie) {
+ e = mie;
+ }
+ Assert.assertNotNull(e);
+ }
+
+
+ @Test(timeout = 1000)
+ public void testLeftoverBufferWithBufferedContinuationBytes() throws
Exception {
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
+ ByteBuffer bb = ByteBuffer.wrap(new byte[] { (byte) 0xE2 });
+ CharBuffer cb = newCharBuffer(4);
+ TesterInputBuffer ib = new TesterInputBuffer(bb);
+
+ conv.convert(bb, cb, ib, false);
+
+ bb = ByteBuffer.wrap(new byte[] { (byte) 0x8C, (byte) 0xA8, (byte)
0x42 });
+ ib.setByteBuffer(bb);
+ conv.convert(bb, cb, ib, true);
+
+ assertCharBufferEquals("\u2328B", cb);
+ }
+
+
+ @Test
+ public void testLeftoverBufferRefillWithTrailingBytes() throws Exception {
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
+ ByteBuffer bb = ByteBuffer.allocate(8);
+ CharBuffer cb = newCharBuffer(4);
+ TesterInputBuffer ib = new TesterInputBuffer(bb);
+
+ bb.put((byte) 0xE2);
+ bb.flip();
+ conv.convert(bb, cb, ib, false);
+
+ ib.setNextRealBytes(new byte[] { (byte) 0x8C, (byte) 0xA8, (byte) 0x42
});
+ conv.convert(bb, cb, ib, true);
+
+ assertCharBufferEquals("\u2328B", cb);
+ }
+
+
+ @Test
+ public void testLeftoverBufferRefillWithReplacementBuffer() throws
Exception {
+ B2CConverter conv = new B2CConverter(StandardCharsets.UTF_8);
+ ByteBuffer bb = ByteBuffer.allocate(8);
+ CharBuffer cb = newCharBuffer(4);
+ TesterInputBuffer ib = new TesterInputBuffer(bb);
+
+ bb.put((byte) 0xE2);
+ bb.flip();
+ conv.convert(bb, cb, ib, false);
+
+ ib.replaceNextByteBuffer(new byte[] { (byte) 0x8C, (byte) 0xA8, (byte)
0x42 });
+ conv.convert(bb, cb, ib, true);
+
+ assertCharBufferEquals("\u2328B", cb);
+ }
+
+
+ private static class TesterInputBuffer extends InputBuffer {
+
+ private byte[] nextRealBytes = null;
+ private boolean replaceByteBuffer = false;
+
+ TesterInputBuffer(ByteBuffer byteBuffer) {
+ super(null);
+ setByteBuffer(byteBuffer);
+ }
+
+ @Override
+ public int realReadBytes() throws IOException {
+ if (nextRealBytes == null) {
+ return -1;
+ } else {
+ ByteBuffer byteBuffer;
+ if (replaceByteBuffer) {
+ byteBuffer =
ByteBuffer.allocate(Math.max(getByteBuffer().capacity(), nextRealBytes.length));
+ setByteBuffer(byteBuffer);
+ } else {
+ byteBuffer = getByteBuffer();
+ }
+ byteBuffer.clear();
+ byteBuffer.put(nextRealBytes);
+ byteBuffer.flip();
+ int result = nextRealBytes.length;
+ nextRealBytes = null;
+ replaceByteBuffer = false;
+ return result;
+ }
+ }
+
+ public void setNextRealBytes(byte[] nextRealBytes) {
+ this.nextRealBytes = nextRealBytes;
+ }
+
+ public void replaceNextByteBuffer(byte[] nextRealBytes) {
+ this.nextRealBytes = nextRealBytes;
+ replaceByteBuffer = true;
+ }
+ }
+
+
+ private static CharBuffer newCharBuffer(int capacity) {
+ CharBuffer charBuffer = CharBuffer.allocate(capacity);
+ charBuffer.limit(0);
+ return charBuffer;
+ }
+
+
+ private static void assertCharBufferEquals(String expected, CharBuffer
actual) {
+ CharBuffer actualCopy = actual.asReadOnlyBuffer();
+ actualCopy.position(0);
+ Assert.assertEquals(expected, actualCopy.toString());
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]