Skip to content

Commit aeb5516

Browse files
Fix space-padding bug in CobolGroupField.moveFrom(byte[]) (#813)
* fix: CobolGroupField.moveFrom(byte[]) * test: add TestMoveFromBytes.java
1 parent 8e0d440 commit aeb5516

3 files changed

Lines changed: 213 additions & 1 deletion

File tree

libcobj/app/src/main/java/jp/osscons/opensourcecobol/libcobj/data/CobolGroupField.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public void moveFrom(byte[] bytes) {
7777
this.dataStorage.setBytes(bytes, this.size);
7878
} else {
7979
this.dataStorage.setBytes(bytes, bytes.length);
80-
this.dataStorage.fillBytes(bytes.length, (byte) 0x20, this.size);
80+
this.dataStorage.fillBytes(bytes.length, (byte) 0x20, this.size - bytes.length);
8181
}
8282
}
8383

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import jp.osscons.opensourcecobol.libcobj.data.CobolDataStorage;
2+
import jp.osscons.opensourcecobol.libcobj.data.CobolFieldAttribute;
3+
import jp.osscons.opensourcecobol.libcobj.data.CobolGroupField;
4+
5+
public class TestMoveFromBytes {
6+
7+
static int testCount = 0;
8+
static int passCount = 0;
9+
10+
static CobolGroupField makeGroupField(int size) {
11+
CobolFieldAttribute attr = new CobolFieldAttribute(
12+
CobolFieldAttribute.COB_TYPE_GROUP, 0, 0, 0, null);
13+
CobolDataStorage storage = new CobolDataStorage(size);
14+
return new CobolGroupField(size, storage, attr);
15+
}
16+
17+
static void check(String label, CobolGroupField field, byte[] expected) {
18+
testCount++;
19+
byte[] allData = field.getDataStorage().getData(0);
20+
byte[] actual = java.util.Arrays.copyOf(allData, field.getSize());
21+
boolean ok = java.util.Arrays.equals(actual, expected);
22+
if (ok) {
23+
passCount++;
24+
}
25+
System.out.printf("TEST %02d %-40s %s EXPECT=%s ACTUAL=%s%n",
26+
testCount, label,
27+
ok ? "OK" : "NG",
28+
toDispString(expected),
29+
toDispString(actual));
30+
}
31+
32+
static String toDispString(byte[] bytes) {
33+
StringBuilder sb = new StringBuilder("(");
34+
for (byte b : bytes) {
35+
if (b >= 0x20 && b < 0x7F) {
36+
sb.append((char) b);
37+
} else {
38+
sb.append(String.format("\\x%02X", b & 0xFF));
39+
}
40+
}
41+
sb.append(")");
42+
return sb.toString();
43+
}
44+
45+
public static void main(String[] args) {
46+
47+
// ========================================
48+
// Group 1: moveFrom(byte array) directly
49+
// ========================================
50+
51+
// Test: bytes.length == field.size (exact fit)
52+
{
53+
CobolGroupField f = makeGroupField(5);
54+
f.moveFrom(new byte[] {'A', 'B', 'C', 'D', 'E'});
55+
check("byte array exact fit (5==5)",
56+
f, new byte[] {'A', 'B', 'C', 'D', 'E'});
57+
}
58+
59+
// Test: bytes.length > field.size (truncation)
60+
{
61+
CobolGroupField f = makeGroupField(3);
62+
f.moveFrom(new byte[] {'A', 'B', 'C', 'D', 'E'});
63+
check("byte array longer than field (5>3)",
64+
f, new byte[] {'A', 'B', 'C'});
65+
}
66+
67+
// Test: bytes.length < field.size (padding with 0x20)
68+
{
69+
CobolGroupField f = makeGroupField(5);
70+
f.moveFrom(new byte[] {'A', 'B'});
71+
check("byte array shorter than field (2<5)",
72+
f, new byte[] {'A', 'B', ' ', ' ', ' '});
73+
}
74+
75+
// Test: bytes.length == 0 (empty -> all spaces)
76+
{
77+
CobolGroupField f = makeGroupField(3);
78+
f.moveFrom(new byte[] {});
79+
check("byte array empty (0<3)",
80+
f, new byte[] {' ', ' ', ' '});
81+
}
82+
83+
// Test: bytes.length == 1, field.size == 1
84+
{
85+
CobolGroupField f = makeGroupField(1);
86+
f.moveFrom(new byte[] {'X'});
87+
check("byte array single byte (1==1)",
88+
f, new byte[] {'X'});
89+
}
90+
91+
// Test: bytes.length == 1, field.size > 1
92+
{
93+
CobolGroupField f = makeGroupField(5);
94+
f.moveFrom(new byte[] {'X'});
95+
check("byte array 1 byte into 5-byte field",
96+
f, new byte[] {'X', ' ', ' ', ' ', ' '});
97+
}
98+
99+
// ========================================
100+
// Group 2: moveFrom(String) -> moveFrom(byte array)
101+
// This is the actual path from generated Java
102+
// (execute method of CALL target program)
103+
// ========================================
104+
105+
// Test: String exact fit
106+
{
107+
CobolGroupField f = makeGroupField(5);
108+
f.moveFrom("ABCDE");
109+
check("String exact fit (5==5)",
110+
f, new byte[] {'A', 'B', 'C', 'D', 'E'});
111+
}
112+
113+
// Test: String shorter than field (padding)
114+
{
115+
CobolGroupField f = makeGroupField(5);
116+
f.moveFrom("AB");
117+
check("String shorter than field (2<5)",
118+
f, new byte[] {'A', 'B', ' ', ' ', ' '});
119+
}
120+
121+
// Test: String longer than field (truncation)
122+
{
123+
CobolGroupField f = makeGroupField(3);
124+
f.moveFrom("ABCDE");
125+
check("String longer than field (5>3)",
126+
f, new byte[] {'A', 'B', 'C'});
127+
}
128+
129+
// Test: Empty string
130+
{
131+
CobolGroupField f = makeGroupField(3);
132+
f.moveFrom("");
133+
check("String empty (0<3)",
134+
f, new byte[] {' ', ' ', ' '});
135+
}
136+
137+
// Test: String with spaces (preserves spaces)
138+
{
139+
CobolGroupField f = makeGroupField(5);
140+
f.moveFrom("A B");
141+
check("String with spaces 'A B' into 5",
142+
f, new byte[] {'A', ' ', 'B', ' ', ' '});
143+
}
144+
145+
// ========================================
146+
// Group 3: Overwrite behavior
147+
// Verify padding overwrites previous data
148+
// (This is what commit 9ac28a3 fixed)
149+
// ========================================
150+
151+
// Test: Short data overwrites previous longer data
152+
{
153+
CobolGroupField f = makeGroupField(5);
154+
f.moveFrom("XXXXX");
155+
f.moveFrom("AB");
156+
check("Overwrite: 'XXXXX' then 'AB'",
157+
f, new byte[] {'A', 'B', ' ', ' ', ' '});
158+
}
159+
160+
// Test: Short bytes overwrite previous data
161+
{
162+
CobolGroupField f = makeGroupField(10);
163+
f.moveFrom("ZZZZZZZZZZ");
164+
f.moveFrom(new byte[] {'H', 'I'});
165+
check("Overwrite: 10-byte then 2-byte",
166+
f, new byte[] {'H', 'I', ' ', ' ', ' ',
167+
' ', ' ', ' ', ' ', ' '});
168+
}
169+
170+
// Test: Exact fit after short (no stale data)
171+
{
172+
CobolGroupField f = makeGroupField(5);
173+
f.moveFrom("AB");
174+
f.moveFrom("12345");
175+
check("Overwrite: 'AB' then '12345'",
176+
f, new byte[] {'1', '2', '3', '4', '5'});
177+
}
178+
179+
// ========================================
180+
// Summary
181+
// ========================================
182+
System.out.println();
183+
System.out.printf("Result: %d/%d passed%n", passCount, testCount);
184+
if (passCount != testCount) {
185+
System.exit(1);
186+
}
187+
}
188+
}

tests/run.src/miscellaneous.at

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2502,3 +2502,27 @@ AT_CHECK([java prog], [0],
25022502
[30
25032503
])
25042504
AT_CLEANUP
2505+
2506+
AT_SETUP([moveFrom(byte@<:@@:>@)])
2507+
AT_CHECK([cp ../../run.src/TestMoveFromBytes.java .])
2508+
AT_CHECK([javac TestMoveFromBytes.java])
2509+
AT_CHECK([java TestMoveFromBytes], [0],
2510+
[TEST 01 byte array exact fit (5==5) OK EXPECT=(ABCDE) ACTUAL=(ABCDE)
2511+
TEST 02 byte array longer than field (5>3) OK EXPECT=(ABC) ACTUAL=(ABC)
2512+
TEST 03 byte array shorter than field (2<5) OK EXPECT=(AB ) ACTUAL=(AB )
2513+
TEST 04 byte array empty (0<3) OK EXPECT=( ) ACTUAL=( )
2514+
TEST 05 byte array single byte (1==1) OK EXPECT=(X) ACTUAL=(X)
2515+
TEST 06 byte array 1 byte into 5-byte field OK EXPECT=(X ) ACTUAL=(X )
2516+
TEST 07 String exact fit (5==5) OK EXPECT=(ABCDE) ACTUAL=(ABCDE)
2517+
TEST 08 String shorter than field (2<5) OK EXPECT=(AB ) ACTUAL=(AB )
2518+
TEST 09 String longer than field (5>3) OK EXPECT=(ABC) ACTUAL=(ABC)
2519+
TEST 10 String empty (0<3) OK EXPECT=( ) ACTUAL=( )
2520+
TEST 11 String with spaces 'A B' into 5 OK EXPECT=(A B ) ACTUAL=(A B )
2521+
TEST 12 Overwrite: 'XXXXX' then 'AB' OK EXPECT=(AB ) ACTUAL=(AB )
2522+
TEST 13 Overwrite: 10-byte then 2-byte OK EXPECT=(HI ) ACTUAL=(HI )
2523+
TEST 14 Overwrite: 'AB' then '12345' OK EXPECT=(12345) ACTUAL=(12345)
2524+
2525+
Result: 14/14 passed
2526+
])
2527+
2528+
AT_CLEANUP

0 commit comments

Comments
 (0)