Skip to content

Commit 368fe93

Browse files
committed
Byte Tests and Bug Fixes
1 parent dbd1933 commit 368fe93

File tree

2 files changed

+116
-8
lines changed
  • Codex/src
    • main/java/org/operatorfoundation/codex/symbols
    • test/java/org/operatorfoundation/codex

2 files changed

+116
-8
lines changed

Codex/src/main/java/org/operatorfoundation/codex/symbols/Byte.kt

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,30 @@ import java.math.BigInteger
66
* Byte symbol - encodes/decodes byte values (0-255).
77
* Used for binary data encoding.
88
*/
9-
class Byte : Symbol {
9+
class Byte : Symbol
10+
{
1011
override fun size() = 256
1112
override fun toString() = "Byte"
1213

13-
override fun encode(numericValue: BigInteger): ByteArray {
14-
if (numericValue < 0.toBigInteger() || numericValue > 255.toBigInteger()) {
15-
throw Exception("Invalid value $numericValue for Byte (must be 0-255)")
14+
override fun encode(numericValue: BigInteger): ByteArray
15+
{
16+
if (numericValue < BigInteger.ZERO || numericValue > BigInteger.valueOf(255))
17+
{
18+
throw IllegalArgumentException("Invalid value $numericValue for Byte (must be 0-255)")
1619
}
17-
return numericValue.toInt().toChar().toString().toByteArray()
20+
21+
// A single raw byte
22+
return byteArrayOf(numericValue.toInt().toByte())
1823
}
1924

20-
override fun decode(encodedValue: ByteArray): BigInteger {
25+
override fun decode(encodedValue: ByteArray): BigInteger
26+
{
2127
if (encodedValue.size != 1)
2228
{
23-
throw Exception("Too many bytes")
29+
throw IllegalArgumentException("Invalid encoded value length: ${encodedValue.size} ")
2430
}
2531

26-
return encodedValue[0].toInt().toBigInteger()
32+
// unsigned 0–255
33+
return encodedValue[0].toUByte().toInt().toBigInteger()
2734
}
2835
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package org.operatorfoundation.codex
2+
3+
import org.junit.jupiter.api.Assertions.*
4+
import org.junit.jupiter.api.Test
5+
import org.junit.jupiter.params.ParameterizedTest
6+
import org.junit.jupiter.params.provider.ValueSource
7+
import org.operatorfoundation.codex.symbols.Byte
8+
import java.math.BigInteger
9+
10+
class ByteTest
11+
{
12+
13+
private val symbol = Byte()
14+
15+
@Test
16+
fun `size should be 256`() {
17+
assertEquals(256, symbol.size())
18+
}
19+
20+
@Test
21+
fun `toString should return Byte`() {
22+
assertEquals("Byte", symbol.toString())
23+
}
24+
25+
@ParameterizedTest(name = "roundtrip {0}")
26+
@ValueSource(ints = [0, 1, 2, 127, 128, 200, 254, 255])
27+
fun `encode and decode roundtrip for selected values`(input: Int) {
28+
val value = BigInteger.valueOf(input.toLong())
29+
val encoded = symbol.encode(value)
30+
val decoded = symbol.decode(encoded)
31+
assertEquals(value, decoded)
32+
assertEquals(1, encoded.size)
33+
}
34+
35+
@Test
36+
fun `encode and decode roundtrip for all values`() {
37+
for (i in 0..255) {
38+
val value = BigInteger.valueOf(i.toLong()) // 1) make a BigInteger 0..255
39+
val encoded = symbol.encode(value) // 2) encode it to bytes
40+
val decoded = symbol.decode(encoded) // 3) decode back to BigInteger
41+
assertEquals(value,
42+
decoded,
43+
"Failed for $i") // 4) verify roundtrip
44+
}
45+
}
46+
47+
@Test
48+
fun `encode should throw for negative values`() {
49+
assertThrows(IllegalArgumentException::class.java) {
50+
symbol.encode(BigInteger.valueOf(-1))
51+
}
52+
}
53+
54+
@Test
55+
fun `encode should throw for values above 255`() {
56+
assertThrows(IllegalArgumentException::class.java) {
57+
symbol.encode(BigInteger.valueOf(256))
58+
}
59+
}
60+
61+
@Test
62+
fun `decode should throw for arrays larger than 1 byte`() {
63+
assertThrows(IllegalArgumentException::class.java) {
64+
symbol.decode(byteArrayOf(1, 2))
65+
}
66+
}
67+
68+
@Test
69+
fun `decode single byte 0 through 127`() {
70+
for (i in 0..127) {
71+
val input = byteArrayOf(i.toByte())
72+
val decoded = symbol.decode(input)
73+
assertEquals(BigInteger.valueOf(i.toLong()), decoded, "Failed for $i")
74+
}
75+
}
76+
77+
@Test
78+
fun `decode single byte 128 through 255`() {
79+
for (i in 128..255) {
80+
val input = byteArrayOf(i.toByte()) // values above 127 wrap to negative Kotlin Bytes
81+
val decoded = symbol.decode(input)
82+
assertEquals(BigInteger.valueOf(i.toLong()), decoded, "Failed for $i")
83+
}
84+
}
85+
86+
@Test
87+
fun `decode should throw when empty`() {
88+
val ex = assertThrows(IllegalArgumentException::class.java) {
89+
symbol.decode(byteArrayOf())
90+
}
91+
assertTrue(ex.message!!.contains("length"), "Expected message about length but got: ${ex.message}")
92+
}
93+
94+
@Test
95+
fun `decode should throw when more than 1 byte`() {
96+
val ex = assertThrows(IllegalArgumentException::class.java) {
97+
symbol.decode(byteArrayOf(1, 2, 3))
98+
}
99+
assertTrue(ex.message!!.contains("length"), "Expected message about length but got: ${ex.message}")
100+
}
101+
}

0 commit comments

Comments
 (0)