Skip to content

Commit 248f4bd

Browse files
committed
[test] unit tests for MemBIO matching BIO_s_mem behavior
1 parent 6a1e045 commit 248f4bd

2 files changed

Lines changed: 194 additions & 4 deletions

File tree

src/main/java/org/jruby/ext/openssl/impl/MemBIO.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class MemBIO extends BIO {
3737
// Some VMs reserve header words in arrays; this is the largest safe allocation.
3838
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
3939

40-
private byte[] buffer = new byte[1024];
40+
byte[] buffer = new byte[1024];
4141
private int wpointer = 0;
4242
private int rpointer = 0;
4343
private int slen = 0;
@@ -72,9 +72,8 @@ public int gets(byte[] in, int len) {
7272

7373
@Override
7474
public int read(byte[] in, int index, int len) throws IOException {
75-
if(rpointer == slen) {
76-
return 0;
77-
}
75+
if (rpointer == slen) return 0;
76+
7877
int toRead = Math.min(len, slen-rpointer);
7978
System.arraycopy(buffer, rpointer, in, index, toRead);
8079
rpointer+=toRead;
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
package org.jruby.ext.openssl.impl;
2+
3+
import java.io.IOException;
4+
5+
import org.junit.jupiter.api.Test;
6+
import static org.junit.jupiter.api.Assertions.*;
7+
8+
/**
9+
* Tests for MemBIO buffer management, verifying behavior consistent
10+
* with C OpenSSL's BIO_s_mem (crypto/bio/bss_mem.c).
11+
*/
12+
public class MemBIOTest {
13+
14+
// C OpenSSL: BIO_write returns number of bytes written
15+
@Test
16+
public void writeReturnsLenOnSuccess() throws IOException {
17+
MemBIO bio = new MemBIO();
18+
byte[] data = new byte[] { 1, 2, 3, 4, 5 };
19+
assertEquals(5, bio.write(data, 0, data.length));
20+
}
21+
22+
// C OpenSSL: BIO_read returns number of bytes read, 0 at EOF
23+
@Test
24+
public void readAfterWrite() throws IOException {
25+
MemBIO bio = new MemBIO();
26+
byte[] written = "hello".getBytes("ISO8859-1");
27+
bio.write(written, 0, written.length);
28+
29+
byte[] buf = new byte[10];
30+
int read = bio.read(buf, 0, buf.length);
31+
assertEquals(5, read);
32+
assertEquals('h', buf[0]);
33+
assertEquals('o', buf[4]);
34+
}
35+
36+
// C OpenSSL: BIO_read returns 0 when no data available
37+
@Test
38+
public void readOnEmptyReturnsZero() throws IOException {
39+
MemBIO bio = new MemBIO();
40+
byte[] buf = new byte[10];
41+
assertEquals(0, bio.read(buf, 0, buf.length));
42+
}
43+
44+
// C OpenSSL: mem_write returns 0 for inl <= 0
45+
@Test
46+
public void writeZeroLenReturnsZero() throws IOException {
47+
MemBIO bio = new MemBIO();
48+
assertEquals(0, bio.write(new byte[0], 0, 0));
49+
assertEquals(0, bio.length());
50+
}
51+
52+
// C OpenSSL: mem_write returns 0 for inl <= 0
53+
@Test
54+
public void writeNegativeLenReturnsZero() throws IOException {
55+
MemBIO bio = new MemBIO();
56+
assertEquals(0, bio.write(new byte[1], 0, -1));
57+
assertEquals(0, bio.length());
58+
}
59+
60+
// Buffer grows automatically on write (C OpenSSL uses BUF_MEM_grow_clean)
61+
@Test
62+
public void writeGrowsBeyondInitialBuffer() throws IOException {
63+
MemBIO bio = new MemBIO();
64+
// Initial buffer is 1024; write more than that
65+
byte[] data = new byte[2048];
66+
for (int i = 0; i < data.length; i++) data[i] = (byte) (i & 0xFF);
67+
68+
assertEquals(2048, bio.write(data, 0, data.length));
69+
assertEquals(2048, bio.length());
70+
71+
byte[] result = bio.toBytes();
72+
assertEquals(2048, result.length);
73+
assertEquals((byte) 0, result[0]);
74+
assertEquals((byte) 0xFF, result[255]);
75+
}
76+
77+
// Multiple writes accumulate (C OpenSSL appends to BUF_MEM)
78+
@Test
79+
public void multipleWritesAccumulate() throws IOException {
80+
MemBIO bio = new MemBIO();
81+
byte[] a = "abc".getBytes("ISO8859-1");
82+
byte[] b = "def".getBytes("ISO8859-1");
83+
84+
bio.write(a, 0, a.length);
85+
bio.write(b, 0, b.length);
86+
87+
assertEquals(6, bio.length());
88+
assertEquals("abcdef", new String(bio.toBytes(), "ISO8859-1"));
89+
}
90+
91+
// Write with offset
92+
@Test
93+
public void writeWithOffset() throws IOException {
94+
MemBIO bio = new MemBIO();
95+
byte[] data = "XXhelloXX".getBytes("ISO8859-1");
96+
97+
bio.write(data, 2, 5); // write "hello"
98+
assertEquals(5, bio.length());
99+
assertEquals("hello", new String(bio.toBytes(), "ISO8859-1"));
100+
}
101+
102+
// Partial read leaves remaining data available
103+
@Test
104+
public void partialReadAdvancesPointer() throws IOException {
105+
MemBIO bio = new MemBIO();
106+
byte[] data = "abcdef".getBytes("ISO8859-1");
107+
bio.write(data, 0, data.length);
108+
109+
byte[] buf = new byte[3];
110+
assertEquals(3, bio.read(buf, 0, 3));
111+
assertEquals("abc", new String(buf, "ISO8859-1"));
112+
113+
assertEquals(3, bio.read(buf, 0, 3));
114+
assertEquals("def", new String(buf, "ISO8859-1"));
115+
116+
// Now exhausted
117+
assertEquals(0, bio.read(buf, 0, 3));
118+
}
119+
120+
// C OpenSSL: BIO_reset rewinds read pointer
121+
@Test
122+
public void resetRewindsReadPointer() throws IOException {
123+
MemBIO bio = new MemBIO();
124+
byte[] data = "hello".getBytes("ISO8859-1");
125+
bio.write(data, 0, data.length);
126+
127+
byte[] buf = new byte[5];
128+
bio.read(buf, 0, 5);
129+
assertEquals(0, bio.read(buf, 0, 5)); // exhausted
130+
131+
bio.reset();
132+
assertEquals(5, bio.read(buf, 0, 5)); // readable again
133+
assertEquals("hello", new String(buf, "ISO8859-1"));
134+
}
135+
136+
// gets reads up to newline (C OpenSSL BIO_gets behavior)
137+
@Test
138+
public void getsReadsUpToNewline() throws IOException {
139+
MemBIO bio = new MemBIO();
140+
byte[] data = "line1\nline2\n".getBytes("ISO8859-1");
141+
bio.write(data, 0, data.length);
142+
143+
byte[] buf = new byte[20];
144+
int n = bio.gets(buf, 20);
145+
assertEquals(6, n); // "line1\n"
146+
assertEquals("line1\n", new String(buf, 0, n, "ISO8859-1"));
147+
148+
n = bio.gets(buf, 20);
149+
assertEquals(6, n); // "line2\n"
150+
assertEquals("line2\n", new String(buf, 0, n, "ISO8859-1"));
151+
}
152+
153+
// gets returns 0 when no data (C OpenSSL BIO_gets returns 0)
154+
@Test
155+
public void getsOnEmptyReturnsZero() {
156+
MemBIO bio = new MemBIO();
157+
assertEquals(0, bio.gets(new byte[10], 10));
158+
}
159+
160+
@Test
161+
public void largeWriteGrowsInternalBuffer() throws IOException {
162+
MemBIO bio = new MemBIO();
163+
byte[] data = new byte[1024 * 16];
164+
for (int i = 0; i < data.length; i++) data[i] = (byte) (i & 0xFF);
165+
166+
assertEquals(data.length, bio.write(data, 0, data.length));
167+
assertEquals(data.length, bio.length());
168+
assertArrayEquals(data, bio.toBytes());
169+
assertEquals(bio.buffer.length, bio.length());
170+
171+
bio.write(new byte[] { 1, 2 }, 0, 2);
172+
assertTrue(bio.buffer.length > bio.length());
173+
}
174+
175+
// toBytes / getMemCopy return a copy (C OpenSSL BIO_get_mem_copy semantics)
176+
@Test
177+
public void toBytesReturnsCopy() throws IOException {
178+
MemBIO bio = new MemBIO();
179+
byte[] data = "test".getBytes("ISO8859-1");
180+
bio.write(data, 0, data.length);
181+
182+
byte[] copy1 = bio.toBytes();
183+
byte[] copy2 = bio.getMemCopy();
184+
assertNotSame(copy1, copy2);
185+
assertArrayEquals(copy1, copy2);
186+
187+
// Mutating the copy doesn't affect the BIO
188+
copy1[0] = 'X';
189+
assertArrayEquals("test".getBytes("ISO8859-1"), bio.toBytes());
190+
}
191+
}

0 commit comments

Comments
 (0)