This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/11.0.x by this push:
     new 977dca97e9 Test cases co-authored with CoPilot/GPT-5.4
977dca97e9 is described below

commit 977dca97e9bb47554e346ac47ea675f3a740984e
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]

Reply via email to