Add comprehensive tests for sha256 marshalling and unmarshalling

Introduce tests to verify `MarshalBinary` and `UnmarshalBinary` behavior, covering block boundary cases, data integrity after state restoration, and error handling for invalid inputs. These tests ensure correctness for diverse data sizes, edge cases, and error scenarios.
This commit is contained in:
2025-06-26 21:28:52 +01:00
parent a7944e054c
commit 5bf9d72518

197
sha256/additional_test.go Normal file
View File

@@ -0,0 +1,197 @@
package sha256
import (
"bytes"
"fmt"
"testing"
)
// Test for the UnmarshalBinary issue where d.nx calculation might be incorrect
func TestUnmarshalBinaryNxCalculation(t *testing.T) {
// Create a digest and write some data that doesn't align to block boundary
d1 := New().(*digest)
testData := []byte("hello world") // 11 bytes, not aligned to 64-byte boundary
d1.Write(testData)
// Marshal the state
state, err := d1.MarshalBinary()
if err != nil {
t.Fatalf("MarshalBinary failed: %v", err)
}
// Create a new digest and unmarshal
d2 := New().(*digest)
err = d2.UnmarshalBinary(state)
if err != nil {
t.Fatalf("UnmarshalBinary failed: %v", err)
}
// Check that nx values match
if d1.nx != d2.nx {
t.Errorf("nx mismatch after unmarshal: original=%d, unmarshaled=%d", d1.nx, d2.nx)
}
// Check that the buffer contents match
if !bytes.Equal(d1.x[:d1.nx], d2.x[:d2.nx]) {
t.Errorf("buffer contents mismatch after unmarshal")
}
// Continue writing and verify results match
moreData := []byte(" more data")
d1.Write(moreData)
d2.Write(moreData)
sum1 := d1.Sum(nil)
sum2 := d2.Sum(nil)
if !bytes.Equal(sum1, sum2) {
t.Errorf("final sums don't match: %x vs %x", sum1, sum2)
}
}
// Test edge case with exactly block-sized data
func TestUnmarshalBinaryBlockBoundary(t *testing.T) {
// Create data that's exactly one block (64 bytes)
testData := make([]byte, BlockSize)
for i := range testData {
testData[i] = byte(i)
}
d1 := New().(*digest)
d1.Write(testData)
// Marshal and unmarshal
state, err := d1.MarshalBinary()
if err != nil {
t.Fatalf("MarshalBinary failed: %v", err)
}
d2 := New().(*digest)
err = d2.UnmarshalBinary(state)
if err != nil {
t.Fatalf("UnmarshalBinary failed: %v", err)
}
// After writing exactly one block, nx should be 0
if d1.nx != 0 || d2.nx != 0 {
t.Errorf("nx should be 0 after block boundary: d1.nx=%d, d2.nx=%d", d1.nx, d2.nx)
}
// Verify final results match
sum1 := d1.Sum(nil)
sum2 := d2.Sum(nil)
if !bytes.Equal(sum1, sum2) {
t.Errorf("final sums don't match: %x vs %x", sum1, sum2)
}
}
// Test marshaling/unmarshaling with various data sizes
func TestMarshalUnmarshalVariousSizes(t *testing.T) {
sizes := []int{0, 1, 32, 63, 64, 65, 127, 128, 129}
for _, size := range sizes {
t.Run(fmt.Sprintf("size_%d", size), func(t *testing.T) {
testData := make([]byte, size)
for i := range testData {
testData[i] = byte(i % 256)
}
d1 := New().(*digest)
d1.Write(testData)
// Marshal
state, err := d1.MarshalBinary()
if err != nil {
t.Fatalf("MarshalBinary failed for size %d: %v", size, err)
}
// Unmarshal
d2 := New().(*digest)
err = d2.UnmarshalBinary(state)
if err != nil {
t.Fatalf("UnmarshalBinary failed for size %d: %v", size, err)
}
// Verify state matches
if d1.nx != d2.nx {
t.Errorf("nx mismatch for size %d: %d vs %d", size, d1.nx, d2.nx)
}
if d1.len != d2.len {
t.Errorf("len mismatch for size %d: %d vs %d", size, d1.len, d2.len)
}
// Verify final results match
sum1 := d1.Sum(nil)
sum2 := d2.Sum(nil)
if !bytes.Equal(sum1, sum2) {
t.Errorf("final sums don't match for size %d: %x vs %x", size, sum1, sum2)
}
})
}
}
// Test the getDigest function to ensure it correctly extracts digest values
func TestGetDigest(t *testing.T) {
// Create a test state array with known values
state := make([]byte, 512)
// Fill with a pattern that we can verify
for i := 0; i < 512; i++ {
state[i] = byte(i % 256)
}
// Test extracting digest for different indices
for index := 0; index < 16; index++ {
digest := getDigest(index, state)
// Verify the digest has the expected size
if len(digest) != Size {
t.Errorf("digest size mismatch for index %d: got %d, want %d", index, len(digest), Size)
}
// The digest should not be all zeros (unless the state was all zeros)
allZero := true
for _, b := range digest {
if b != 0 {
allZero = false
break
}
}
// Since we filled state with a pattern, digest shouldn't be all zeros
if allZero {
t.Errorf("digest for index %d is all zeros, which is unexpected", index)
}
}
}
// Test error handling in UnmarshalBinary
func TestUnmarshalBinaryErrorHandling(t *testing.T) {
d := New().(*digest)
// Test with invalid magic
invalidMagic := []byte("bad\x03" + string(make([]byte, marshaledSize-4)))
err := d.UnmarshalBinary(invalidMagic)
if err == nil {
t.Error("expected error for invalid magic, got nil")
}
// Test with invalid size
validMagic := []byte("sha\x03")
tooShort := append(validMagic, make([]byte, 10)...)
err = d.UnmarshalBinary(tooShort)
if err == nil {
t.Error("expected error for invalid size, got nil")
}
// Test with exactly the minimum size but wrong magic
exactSize := make([]byte, marshaledSize)
copy(exactSize, "bad\x03")
err = d.UnmarshalBinary(exactSize)
if err == nil {
t.Error("expected error for wrong magic with correct size, got nil")
}
}