Files
Erupe/common/pascalstring/pascalstring_test.go

707 lines
16 KiB
Go

package pascalstring
import (
"bytes"
"erupe-ce/common/byteframe"
"testing"
)
func TestUint8_NoTransform(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "Hello"
Uint8(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint8()
expectedLength := uint8(len(testString) + 1) // +1 for null terminator
if length != expectedLength {
t.Errorf("length = %d, want %d", length, expectedLength)
}
data := bf.ReadBytes(uint(length))
// Should be "Hello\x00"
expected := []byte("Hello\x00")
if !bytes.Equal(data, expected) {
t.Errorf("data = %v, want %v", data, expected)
}
}
func TestUint8_WithTransform(t *testing.T) {
bf := byteframe.NewByteFrame()
// ASCII string (no special characters)
testString := "Test"
Uint8(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint8()
if length == 0 {
t.Error("length should not be 0 for ASCII string")
}
data := bf.ReadBytes(uint(length))
// Should end with null terminator
if data[len(data)-1] != 0 {
t.Error("data should end with null terminator")
}
}
func TestUint8_EmptyString(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := ""
Uint8(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint8()
if length != 1 { // Just null terminator
t.Errorf("length = %d, want 1", length)
}
data := bf.ReadBytes(uint(length))
if data[0] != 0 {
t.Error("empty string should produce just null terminator")
}
}
func TestUint16_NoTransform(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "World"
Uint16(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint16()
expectedLength := uint16(len(testString) + 1)
if length != expectedLength {
t.Errorf("length = %d, want %d", length, expectedLength)
}
data := bf.ReadBytes(uint(length))
expected := []byte("World\x00")
if !bytes.Equal(data, expected) {
t.Errorf("data = %v, want %v", data, expected)
}
}
func TestUint16_WithTransform(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "Test"
Uint16(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint16()
if length == 0 {
t.Error("length should not be 0 for ASCII string")
}
data := bf.ReadBytes(uint(length))
if data[len(data)-1] != 0 {
t.Error("data should end with null terminator")
}
}
func TestUint16_EmptyString(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := ""
Uint16(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint16()
if length != 1 {
t.Errorf("length = %d, want 1", length)
}
}
func TestUint32_NoTransform(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "Testing"
Uint32(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint32()
expectedLength := uint32(len(testString) + 1)
if length != expectedLength {
t.Errorf("length = %d, want %d", length, expectedLength)
}
data := bf.ReadBytes(uint(length))
expected := []byte("Testing\x00")
if !bytes.Equal(data, expected) {
t.Errorf("data = %v, want %v", data, expected)
}
}
func TestUint32_WithTransform(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "Test"
Uint32(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint32()
if length == 0 {
t.Error("length should not be 0 for ASCII string")
}
data := bf.ReadBytes(uint(length))
if data[len(data)-1] != 0 {
t.Error("data should end with null terminator")
}
}
func TestUint32_EmptyString(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := ""
Uint32(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint32()
if length != 1 {
t.Errorf("length = %d, want 1", length)
}
}
func TestUint8_LongString(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "This is a longer test string with more characters"
Uint8(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint8()
expectedLength := uint8(len(testString) + 1)
if length != expectedLength {
t.Errorf("length = %d, want %d", length, expectedLength)
}
data := bf.ReadBytes(uint(length))
if !bytes.HasSuffix(data, []byte{0}) {
t.Error("data should end with null terminator")
}
if !bytes.HasPrefix(data, []byte("This is")) {
t.Error("data should start with expected string")
}
}
func TestUint16_LongString(t *testing.T) {
bf := byteframe.NewByteFrame()
// Create a string longer than 255 to test uint16
testString := ""
for i := 0; i < 300; i++ {
testString += "A"
}
Uint16(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint16()
expectedLength := uint16(len(testString) + 1)
if length != expectedLength {
t.Errorf("length = %d, want %d", length, expectedLength)
}
data := bf.ReadBytes(uint(length))
if !bytes.HasSuffix(data, []byte{0}) {
t.Error("data should end with null terminator")
}
}
func TestAllFunctions_NullTermination(t *testing.T) {
tests := []struct {
name string
writeFn func(*byteframe.ByteFrame, string, bool)
readSize func(*byteframe.ByteFrame) uint
}{
{
name: "Uint8",
writeFn: func(bf *byteframe.ByteFrame, s string, t bool) {
Uint8(bf, s, t)
},
readSize: func(bf *byteframe.ByteFrame) uint {
return uint(bf.ReadUint8())
},
},
{
name: "Uint16",
writeFn: func(bf *byteframe.ByteFrame, s string, t bool) {
Uint16(bf, s, t)
},
readSize: func(bf *byteframe.ByteFrame) uint {
return uint(bf.ReadUint16())
},
},
{
name: "Uint32",
writeFn: func(bf *byteframe.ByteFrame, s string, t bool) {
Uint32(bf, s, t)
},
readSize: func(bf *byteframe.ByteFrame) uint {
return uint(bf.ReadUint32())
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "Test"
tt.writeFn(bf, testString, false)
bf.Seek(0, 0)
size := tt.readSize(bf)
data := bf.ReadBytes(size)
// Verify null termination
if data[len(data)-1] != 0 {
t.Errorf("%s: data should end with null terminator", tt.name)
}
// Verify length includes null terminator
if size != uint(len(testString)+1) {
t.Errorf("%s: size = %d, want %d", tt.name, size, len(testString)+1)
}
})
}
}
func TestTransform_JapaneseCharacters(t *testing.T) {
// Test with Japanese characters that should be transformed to Shift-JIS
bf := byteframe.NewByteFrame()
testString := "テスト" // "Test" in Japanese katakana
Uint16(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint16()
if length == 0 {
t.Error("Transformed Japanese string should have non-zero length")
}
// The transformed Shift-JIS should be different length than UTF-8
// UTF-8: 9 bytes (3 chars * 3 bytes each), Shift-JIS: 6 bytes (3 chars * 2 bytes each) + 1 null
data := bf.ReadBytes(uint(length))
if data[len(data)-1] != 0 {
t.Error("Transformed string should end with null terminator")
}
}
func TestTransform_InvalidUTF8(t *testing.T) {
// This test verifies graceful handling of encoding errors
// When transformation fails, the functions should write length 0
bf := byteframe.NewByteFrame()
// Create a string with invalid UTF-8 sequence
// Note: Go strings are generally valid UTF-8, but we can test the error path
testString := "Valid ASCII"
Uint8(bf, testString, true)
// Should succeed for ASCII characters
bf.Seek(0, 0)
length := bf.ReadUint8()
if length == 0 {
t.Error("ASCII string should transform successfully")
}
}
func BenchmarkUint8_NoTransform(b *testing.B) {
testString := "Hello, World!"
b.ResetTimer()
for i := 0; i < b.N; i++ {
bf := byteframe.NewByteFrame()
Uint8(bf, testString, false)
}
}
func BenchmarkUint8_WithTransform(b *testing.B) {
testString := "Hello, World!"
b.ResetTimer()
for i := 0; i < b.N; i++ {
bf := byteframe.NewByteFrame()
Uint8(bf, testString, true)
}
}
func BenchmarkUint16_NoTransform(b *testing.B) {
testString := "Hello, World!"
b.ResetTimer()
for i := 0; i < b.N; i++ {
bf := byteframe.NewByteFrame()
Uint16(bf, testString, false)
}
}
func BenchmarkUint32_NoTransform(b *testing.B) {
testString := "Hello, World!"
b.ResetTimer()
for i := 0; i < b.N; i++ {
bf := byteframe.NewByteFrame()
Uint32(bf, testString, false)
}
}
func BenchmarkUint16_Japanese(b *testing.B) {
testString := "テストメッセージ"
b.ResetTimer()
for i := 0; i < b.N; i++ {
bf := byteframe.NewByteFrame()
Uint16(bf, testString, true)
}
}
// Edge case tests for additional coverage
func TestUint8_MaxLength(t *testing.T) {
// Uint8 length can hold max 255, but string + null = 254 chars max
// Test with string at the uint8 boundary
bf := byteframe.NewByteFrame()
testString := string(make([]byte, 254)) // 254 chars + 1 null = 255
Uint8(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint8()
// Length should be 255 (254 chars + null)
if length != 255 {
t.Errorf("length = %d, want 255", length)
}
}
func TestUint8_OverflowString(t *testing.T) {
// Test string that would overflow uint8 length (>255)
bf := byteframe.NewByteFrame()
testString := string(make([]byte, 300)) // Would need 301 for length+null, exceeds uint8
Uint8(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint8()
// Due to uint8 overflow, length will wrap around
// 301 % 256 = 45, but actual behavior depends on implementation
t.Logf("Overflow string produced length: %d", length)
// This test documents the current behavior (truncation via uint8 overflow)
}
func TestUint16_ExactBoundary(t *testing.T) {
// Test string that fits exactly in uint16 boundary
bf := byteframe.NewByteFrame()
testString := string(make([]byte, 1000))
Uint16(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint16()
expectedLength := uint16(1000 + 1)
if length != expectedLength {
t.Errorf("length = %d, want %d", length, expectedLength)
}
}
func TestUint32_LargeString(t *testing.T) {
// Test moderately large string with uint32
bf := byteframe.NewByteFrame()
testString := string(make([]byte, 10000))
Uint32(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint32()
expectedLength := uint32(10000 + 1)
if length != expectedLength {
t.Errorf("length = %d, want %d", length, expectedLength)
}
}
func TestTransform_MixedContent(t *testing.T) {
// Test string with mixed ASCII and Japanese content
bf := byteframe.NewByteFrame()
testString := "Player1: テスト"
Uint16(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint16()
if length == 0 {
t.Error("Mixed content string should transform successfully")
}
// Should have null terminator
data := bf.ReadBytes(uint(length))
if data[len(data)-1] != 0 {
t.Error("Transformed string should end with null terminator")
}
}
func TestTransform_SpecialSymbols(t *testing.T) {
// Test Japanese symbols that exist in Shift-JIS
bf := byteframe.NewByteFrame()
testString := "★☆●○"
Uint16(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint16()
// Some symbols may not transform, check length is valid
t.Logf("Special symbols produced length: %d", length)
// If length is 0, the transform failed which is expected for some symbols
}
func TestTransform_NumbersAndSymbols(t *testing.T) {
// ASCII numbers and basic symbols should always work
bf := byteframe.NewByteFrame()
testString := "12345!@#$%"
Uint16(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint16()
if length == 0 {
t.Error("ASCII numbers and symbols should transform successfully")
}
}
func TestUint8_SingleCharacter(t *testing.T) {
// Edge case: single character
bf := byteframe.NewByteFrame()
testString := "A"
Uint8(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint8()
// Length should be 2 (1 char + null)
if length != 2 {
t.Errorf("length = %d, want 2", length)
}
data := bf.ReadBytes(uint(length))
if data[0] != 'A' || data[1] != 0 {
t.Errorf("data = %v, want [A, 0]", data)
}
}
func TestUint16_SingleCharacter(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "B"
Uint16(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint16()
if length != 2 {
t.Errorf("length = %d, want 2", length)
}
data := bf.ReadBytes(uint(length))
if data[0] != 'B' || data[1] != 0 {
t.Errorf("data = %v, want [B, 0]", data)
}
}
func TestUint32_SingleCharacter(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "C"
Uint32(bf, testString, false)
bf.Seek(0, 0)
length := bf.ReadUint32()
if length != 2 {
t.Errorf("length = %d, want 2", length)
}
data := bf.ReadBytes(uint(length))
if data[0] != 'C' || data[1] != 0 {
t.Errorf("data = %v, want [C, 0]", data)
}
}
func TestTransform_OnlySpaces(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := " " // Three spaces
Uint8(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint8()
// Spaces should transform fine
if length != 4 { // 3 spaces + null
t.Errorf("length = %d, want 4", length)
}
}
func TestTransform_Hiragana(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "あいうえお"
Uint16(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint16()
if length == 0 {
t.Error("Hiragana should transform to Shift-JIS successfully")
}
// Hiragana in Shift-JIS is 2 bytes per character
// 5 characters * 2 bytes + 1 null = 11 bytes
data := bf.ReadBytes(uint(length))
if data[len(data)-1] != 0 {
t.Error("Transformed string should end with null terminator")
}
}
func TestTransform_Katakana(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := "アイウエオ"
Uint16(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint16()
if length == 0 {
t.Error("Katakana should transform to Shift-JIS successfully")
}
data := bf.ReadBytes(uint(length))
if data[len(data)-1] != 0 {
t.Error("Transformed string should end with null terminator")
}
}
func TestMultipleWrites(t *testing.T) {
// Test writing multiple strings to same buffer
bf := byteframe.NewByteFrame()
Uint8(bf, "First", false)
Uint8(bf, "Second", false)
Uint8(bf, "Third", false)
bf.Seek(0, 0)
// Read first string
len1 := bf.ReadUint8()
data1 := bf.ReadBytes(uint(len1))
// Read second string
len2 := bf.ReadUint8()
data2 := bf.ReadBytes(uint(len2))
// Read third string
len3 := bf.ReadUint8()
data3 := bf.ReadBytes(uint(len3))
// Verify each string
if string(data1[:len(data1)-1]) != "First" {
t.Errorf("First string = %s, want First", string(data1[:len(data1)-1]))
}
if string(data2[:len(data2)-1]) != "Second" {
t.Errorf("Second string = %s, want Second", string(data2[:len(data2)-1]))
}
if string(data3[:len(data3)-1]) != "Third" {
t.Errorf("Third string = %s, want Third", string(data3[:len(data3)-1]))
}
}
func TestTransform_EmptyStringWithTransform(t *testing.T) {
bf := byteframe.NewByteFrame()
testString := ""
Uint8(bf, testString, true) // Transform enabled but string is empty
bf.Seek(0, 0)
length := bf.ReadUint8()
// Empty string with transform should still produce length 1 (just null)
if length != 1 {
t.Errorf("Empty string with transform: length = %d, want 1", length)
}
}
func TestTransform_UnsupportedCharacters(t *testing.T) {
// Test characters that cannot be encoded in Shift-JIS
// Emoji and some Unicode characters are not supported
testStrings := []string{
"\U0001F600", // Emoji (grinning face) - not in Shift-JIS
"🎮", // Game controller emoji
"\U0001F4A9", // Pile of poo emoji
"中文测试", // Simplified Chinese (some chars not in Shift-JIS)
"العربية", // Arabic
"עברית", // Hebrew
"ไทย", // Thai
"한글", // Korean
}
for _, testString := range testStrings {
t.Run(testString, func(t *testing.T) {
bf := byteframe.NewByteFrame()
Uint8(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint8()
// These strings may fail to transform or transform partially
// Document the current behavior
t.Logf("String %q with transform produced length: %d", testString, length)
// If length is 0, the transform failed completely (error path)
// If length > 0, some or all characters were transformed
})
}
}
func TestTransform_Uint16_UnsupportedCharacters(t *testing.T) {
bf := byteframe.NewByteFrame()
// Use a string with characters not in Shift-JIS
testString := "🎮"
Uint16(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint16()
t.Logf("Uint16 transform of emoji produced length: %d", length)
}
func TestTransform_Uint32_UnsupportedCharacters(t *testing.T) {
bf := byteframe.NewByteFrame()
// Use a string with characters not in Shift-JIS
testString := "🎮"
Uint32(bf, testString, true)
bf.Seek(0, 0)
length := bf.ReadUint32()
t.Logf("Uint32 transform of emoji produced length: %d", length)
}