195 lines
5.0 KiB
Go
195 lines
5.0 KiB
Go
package pngmeta
|
|
|
|
import (
|
|
"bytes"
|
|
"gf-lt/models"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"image"
|
|
"image/color"
|
|
"image/png"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestReadMeta(t *testing.T) {
|
|
cases := []struct {
|
|
Filename string
|
|
}{
|
|
{
|
|
Filename: "../sysprompts/llama.png",
|
|
},
|
|
}
|
|
for i, tc := range cases {
|
|
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
|
|
// Call the readMeta function
|
|
pembed, err := extractChar(tc.Filename)
|
|
if err != nil {
|
|
t.Errorf("Expected no error, but got %v", err)
|
|
}
|
|
v, err := pembed.GetDecodedValue()
|
|
if err != nil {
|
|
t.Errorf("Expected no error, but got %v\n", err)
|
|
}
|
|
fmt.Printf("%+v\n", v.Simplify("Adam", tc.Filename))
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test helper: Create a simple PNG image with test shapes
|
|
func createTestImage(t *testing.T) string {
|
|
img := image.NewRGBA(image.Rect(0, 0, 200, 200))
|
|
// Fill background with white
|
|
for y := 0; y < 200; y++ {
|
|
for x := 0; x < 200; x++ {
|
|
img.Set(x, y, color.White)
|
|
}
|
|
}
|
|
// Draw a red square
|
|
for y := 50; y < 150; y++ {
|
|
for x := 50; x < 150; x++ {
|
|
img.Set(x, y, color.RGBA{R: 255, A: 255})
|
|
}
|
|
}
|
|
// Draw a blue circle
|
|
center := image.Point{100, 100}
|
|
radius := 40
|
|
for y := center.Y - radius; y <= center.Y+radius; y++ {
|
|
for x := center.X - radius; x <= center.X+radius; x++ {
|
|
dx := x - center.X
|
|
dy := y - center.Y
|
|
if dx*dx+dy*dy <= radius*radius {
|
|
img.Set(x, y, color.RGBA{B: 255, A: 255})
|
|
}
|
|
}
|
|
}
|
|
// Create temp file
|
|
tmpDir := t.TempDir()
|
|
fpath := filepath.Join(tmpDir, "test-image.png")
|
|
f, err := os.Create(fpath)
|
|
if err != nil {
|
|
t.Fatalf("Error creating temp file: %v", err)
|
|
}
|
|
defer f.Close()
|
|
if err := png.Encode(f, img); err != nil {
|
|
t.Fatalf("Error encoding PNG: %v", err)
|
|
}
|
|
return fpath
|
|
}
|
|
|
|
func TestWriteToPng(t *testing.T) {
|
|
// Create test image
|
|
srcPath := createTestImage(t)
|
|
dstPath := filepath.Join(filepath.Dir(srcPath), "output.png")
|
|
// dstPath := "test.png"
|
|
// Create test metadata
|
|
metadata := &models.CharCardSpec{
|
|
Description: "Test image containing a red square and blue circle on white background",
|
|
}
|
|
// Embed metadata
|
|
if err := WriteToPng(metadata, srcPath, dstPath); err != nil {
|
|
t.Fatalf("WriteToPng failed: %v", err)
|
|
}
|
|
// Verify output file exists
|
|
if _, err := os.Stat(dstPath); os.IsNotExist(err) {
|
|
t.Fatalf("Output file not created: %v", err)
|
|
}
|
|
// Read and verify metadata
|
|
t.Run("VerifyMetadata", func(t *testing.T) {
|
|
data, err := os.ReadFile(dstPath)
|
|
if err != nil {
|
|
t.Fatalf("Error reading output file: %v", err)
|
|
}
|
|
// Verify PNG header
|
|
if string(data[:8]) != pngHeader {
|
|
t.Errorf("Invalid PNG header")
|
|
}
|
|
// Extract metadata
|
|
embedded := extractMetadata(t, data)
|
|
if embedded.Description != metadata.Description {
|
|
t.Errorf("Metadata mismatch\nWant: %q\nGot: %q",
|
|
metadata.Description, embedded.Description)
|
|
}
|
|
})
|
|
// Optional: Add cleanup if needed
|
|
// t.Cleanup(func() {
|
|
// os.Remove(dstPath)
|
|
// })
|
|
}
|
|
|
|
// Helper to extract embedded metadata from PNG bytes
|
|
func extractMetadata(t *testing.T, data []byte) *models.CharCardSpec {
|
|
r := bytes.NewReader(data[8:]) // Skip PNG header
|
|
for {
|
|
var length uint32
|
|
if err := binary.Read(r, binary.BigEndian, &length); err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
break
|
|
}
|
|
t.Fatalf("Error reading chunk length: %v", err)
|
|
}
|
|
chunkType := make([]byte, 4)
|
|
if _, err := r.Read(chunkType); err != nil {
|
|
t.Fatalf("Error reading chunk type: %v", err)
|
|
}
|
|
// Read chunk data
|
|
chunkData := make([]byte, length)
|
|
if _, err := r.Read(chunkData); err != nil {
|
|
t.Fatalf("Error reading chunk data: %v", err)
|
|
}
|
|
// Read and discard CRC
|
|
if _, err := r.Read(make([]byte, 4)); err != nil {
|
|
t.Fatalf("Error reading CRC: %v", err)
|
|
}
|
|
if string(chunkType) == embType {
|
|
parts := bytes.SplitN(chunkData, []byte{0}, 2)
|
|
if len(parts) != 2 {
|
|
t.Fatalf("Invalid tEXt chunk format")
|
|
}
|
|
decoded, err := base64.StdEncoding.DecodeString(string(parts[1]))
|
|
if err != nil {
|
|
t.Fatalf("Base64 decode error: %v", err)
|
|
}
|
|
var result models.CharCardSpec
|
|
if err := json.Unmarshal(decoded, &result); err != nil {
|
|
t.Fatalf("JSON unmarshal error: %v", err)
|
|
}
|
|
return &result
|
|
}
|
|
}
|
|
t.Fatal("Metadata not found in PNG")
|
|
return nil
|
|
}
|
|
|
|
func readTextChunk(t *testing.T, r io.ReadSeeker) *models.CharCardSpec {
|
|
var length uint32
|
|
binary.Read(r, binary.BigEndian, &length)
|
|
chunkType := make([]byte, 4)
|
|
r.Read(chunkType)
|
|
data := make([]byte, length)
|
|
r.Read(data)
|
|
// Read CRC (but skip validation for test purposes)
|
|
crc := make([]byte, 4)
|
|
r.Read(crc)
|
|
parts := bytes.SplitN(data, []byte{0}, 2) // Split key-value pair
|
|
if len(parts) != 2 {
|
|
t.Fatalf("Invalid tEXt chunk format")
|
|
}
|
|
// key := string(parts[0])
|
|
value := parts[1]
|
|
decoded, err := base64.StdEncoding.DecodeString(string(value))
|
|
if err != nil {
|
|
t.Fatalf("Base64 decode error: %v; value: %s", err, string(value))
|
|
}
|
|
var result models.CharCardSpec
|
|
if err := json.Unmarshal(decoded, &result); err != nil {
|
|
t.Fatalf("JSON unmarshal error: %v", err)
|
|
}
|
|
return &result
|
|
}
|