Initial release: Go Jdenticon library v0.1.0
- Core library with SVG and PNG generation - CLI tool with generate and batch commands - Cross-platform path handling for Windows compatibility - Comprehensive test suite with integration tests
This commit is contained in:
@@ -1,719 +1,161 @@
|
||||
package jdenticon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
size int
|
||||
name string
|
||||
input string
|
||||
size int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "email address",
|
||||
value: "test@example.com",
|
||||
size: 64,
|
||||
name: "valid_email",
|
||||
input: "user@example.com",
|
||||
size: 64,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "username",
|
||||
value: "johndoe",
|
||||
size: 32,
|
||||
name: "valid_username",
|
||||
input: "johndoe",
|
||||
size: 128,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "large icon",
|
||||
value: "large-icon-test",
|
||||
size: 256,
|
||||
name: "empty_input",
|
||||
input: "",
|
||||
size: 64,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "zero_size",
|
||||
input: "test",
|
||||
size: 0,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "negative_size",
|
||||
input: "test",
|
||||
size: -1,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
icon, err := Generate(tt.value, tt.size)
|
||||
icon, err := Generate(context.Background(), tt.input, tt.size)
|
||||
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("Generate(context.Background(), ) expected error for %s, but got none", tt.name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Generate failed: %v", err)
|
||||
t.Errorf("Generate(context.Background(), ) unexpected error for %s: %v", tt.name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if icon == nil {
|
||||
t.Fatal("Generate returned nil icon")
|
||||
}
|
||||
|
||||
// Test SVG generation
|
||||
svg, err := icon.ToSVG()
|
||||
if err != nil {
|
||||
t.Fatalf("ToSVG failed: %v", err)
|
||||
}
|
||||
|
||||
if svg == "" {
|
||||
t.Error("ToSVG returned empty string")
|
||||
}
|
||||
|
||||
// Basic SVG validation
|
||||
if !strings.Contains(svg, "<svg") {
|
||||
t.Error("SVG output does not contain svg tag")
|
||||
}
|
||||
|
||||
if !strings.Contains(svg, "</svg>") {
|
||||
t.Error("SVG output does not contain closing svg tag")
|
||||
}
|
||||
|
||||
// Test PNG generation
|
||||
png, err := icon.ToPNG()
|
||||
if err != nil {
|
||||
t.Fatalf("ToPNG failed: %v", err)
|
||||
}
|
||||
|
||||
if len(png) == 0 {
|
||||
t.Error("ToPNG returned empty data")
|
||||
}
|
||||
|
||||
// Basic PNG validation (check PNG signature)
|
||||
if len(png) < 8 || string(png[1:4]) != "PNG" {
|
||||
t.Error("PNG output does not have valid PNG signature")
|
||||
t.Errorf("Generate(context.Background(), ) returned nil icon for %s", tt.name)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateConsistency(t *testing.T) {
|
||||
value := "consistency-test"
|
||||
size := 64
|
||||
|
||||
// Generate the same icon multiple times
|
||||
icon1, err := Generate(value, size)
|
||||
if err != nil {
|
||||
t.Fatalf("First generate failed: %v", err)
|
||||
}
|
||||
|
||||
icon2, err := Generate(value, size)
|
||||
if err != nil {
|
||||
t.Fatalf("Second generate failed: %v", err)
|
||||
}
|
||||
|
||||
// SVG should be identical
|
||||
svg1, err := icon1.ToSVG()
|
||||
if err != nil {
|
||||
t.Fatalf("First ToSVG failed: %v", err)
|
||||
}
|
||||
|
||||
svg2, err := icon2.ToSVG()
|
||||
if err != nil {
|
||||
t.Fatalf("Second ToSVG failed: %v", err)
|
||||
}
|
||||
|
||||
if svg1 != svg2 {
|
||||
t.Error("SVG outputs are not consistent for same input")
|
||||
}
|
||||
|
||||
// PNG should be identical
|
||||
png1, err := icon1.ToPNG()
|
||||
if err != nil {
|
||||
t.Fatalf("First ToPNG failed: %v", err)
|
||||
}
|
||||
|
||||
png2, err := icon2.ToPNG()
|
||||
if err != nil {
|
||||
t.Fatalf("Second ToPNG failed: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(png1, png2) {
|
||||
t.Error("PNG outputs are not consistent for same input")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateInvalidInputs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
size int
|
||||
}{
|
||||
{
|
||||
name: "zero size",
|
||||
value: "test",
|
||||
size: 0,
|
||||
},
|
||||
{
|
||||
name: "negative size",
|
||||
value: "test",
|
||||
size: -10,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := Generate(tt.value, tt.size)
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid input")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateVariety(t *testing.T) {
|
||||
// Test that different inputs produce different outputs
|
||||
values := []string{"value1", "value2", "value3", "test@example.com", "another-test"}
|
||||
size := 64
|
||||
|
||||
svgs := make([]string, len(values))
|
||||
|
||||
for i, value := range values {
|
||||
icon, err := Generate(value, size)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate failed for %s: %v", value, err)
|
||||
}
|
||||
|
||||
svg, err := icon.ToSVG()
|
||||
if err != nil {
|
||||
t.Fatalf("ToSVG failed for %s: %v", value, err)
|
||||
}
|
||||
|
||||
svgs[i] = svg
|
||||
}
|
||||
|
||||
// Check that all SVGs are different
|
||||
for i := 0; i < len(svgs); i++ {
|
||||
for j := i + 1; j < len(svgs); j++ {
|
||||
if svgs[i] == svgs[j] {
|
||||
t.Errorf("SVG outputs are identical for different inputs: %s and %s", values[i], values[j])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGenerate(b *testing.B) {
|
||||
value := "benchmark-test"
|
||||
size := 64
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := Generate(value, size)
|
||||
if err != nil {
|
||||
b.Fatalf("Generate failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkGenerateVariousSizes tests generation performance across different icon sizes
|
||||
func BenchmarkGenerateVariousSizes(b *testing.B) {
|
||||
sizes := []int{64, 128, 256, 512, 1024}
|
||||
|
||||
for _, size := range sizes {
|
||||
b.Run(fmt.Sprintf("size_%d", size), func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := Generate("benchmark@test.com", size)
|
||||
if err != nil {
|
||||
b.Fatalf("Generate failed for size %d: %v", size, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkGenerateVariousInputs tests generation performance with different input types
|
||||
func BenchmarkGenerateVariousInputs(b *testing.B) {
|
||||
inputs := []struct {
|
||||
name string
|
||||
value string
|
||||
}{
|
||||
{"email", "user@example.com"},
|
||||
{"username", "john_doe_123"},
|
||||
{"uuid", "550e8400-e29b-41d4-a716-446655440000"},
|
||||
{"short", "abc"},
|
||||
{"long", "this_is_a_very_long_identifier_that_might_be_used_for_generating_identicons_in_some_applications"},
|
||||
{"special_chars", "user+test@domain.co.uk"},
|
||||
{"numbers", "12345678901234567890"},
|
||||
}
|
||||
|
||||
for _, input := range inputs {
|
||||
b.Run(input.name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := Generate(input.value, 128)
|
||||
if err != nil {
|
||||
b.Fatalf("Generate failed for input %s: %v", input.name, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkToSVG(b *testing.B) {
|
||||
icon, err := Generate("benchmark-test", 64)
|
||||
if err != nil {
|
||||
b.Fatalf("Generate failed: %v", err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := icon.ToSVG()
|
||||
if err != nil {
|
||||
b.Fatalf("ToSVG failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkToPNG(b *testing.B) {
|
||||
icon, err := Generate("benchmark-test", 64)
|
||||
if err != nil {
|
||||
b.Fatalf("Generate failed: %v", err)
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := icon.ToPNG()
|
||||
if err != nil {
|
||||
b.Fatalf("ToPNG failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkHashGeneration benchmarks just hash computation performance
|
||||
func BenchmarkHashGeneration(b *testing.B) {
|
||||
inputs := []string{
|
||||
"user@example.com",
|
||||
"john_doe_123",
|
||||
"550e8400-e29b-41d4-a716-446655440000",
|
||||
"abc",
|
||||
"this_is_a_very_long_identifier_that_might_be_used_for_generating_identicons",
|
||||
}
|
||||
|
||||
for _, input := range inputs {
|
||||
b.Run(fmt.Sprintf("len_%d", len(input)), func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ComputeHash(input)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSVGRenderingVariousSizes benchmarks SVG rendering across different sizes
|
||||
func BenchmarkSVGRenderingVariousSizes(b *testing.B) {
|
||||
sizes := []int{64, 128, 256, 512}
|
||||
icons := make(map[int]*Icon)
|
||||
|
||||
// Pre-generate icons
|
||||
for _, size := range sizes {
|
||||
icon, err := Generate("benchmark@test.com", size)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to generate icon for size %d: %v", size, err)
|
||||
}
|
||||
icons[size] = icon
|
||||
}
|
||||
|
||||
for _, size := range sizes {
|
||||
b.Run(fmt.Sprintf("size_%d", size), func(b *testing.B) {
|
||||
icon := icons[size]
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := icon.ToSVG()
|
||||
if err != nil {
|
||||
b.Fatalf("ToSVG failed for size %d: %v", size, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkPNGRenderingVariousSizes benchmarks PNG rendering across different sizes
|
||||
func BenchmarkPNGRenderingVariousSizes(b *testing.B) {
|
||||
sizes := []int{64, 128, 256} // Smaller range for PNG due to higher memory usage
|
||||
icons := make(map[int]*Icon)
|
||||
|
||||
// Pre-generate icons
|
||||
for _, size := range sizes {
|
||||
icon, err := Generate("benchmark@test.com", size)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to generate icon for size %d: %v", size, err)
|
||||
}
|
||||
icons[size] = icon
|
||||
}
|
||||
|
||||
for _, size := range sizes {
|
||||
b.Run(fmt.Sprintf("size_%d", size), func(b *testing.B) {
|
||||
icon := icons[size]
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := icon.ToPNG()
|
||||
if err != nil {
|
||||
b.Fatalf("ToPNG failed for size %d: %v", size, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkWithCustomConfig benchmarks generation with custom configuration
|
||||
func BenchmarkWithCustomConfig(b *testing.B) {
|
||||
config, err := Configure(
|
||||
WithHueRestrictions([]float64{0.0, 0.33, 0.66}),
|
||||
WithColorSaturation(0.6),
|
||||
WithBackgroundColor("#ffffff"),
|
||||
WithPadding(0.1),
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatalf("Configure failed: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := ToSVG("benchmark@test.com", 128, config)
|
||||
if err != nil {
|
||||
b.Fatalf("ToSVG with config failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkWithCustomConfigPNG benchmarks PNG generation with custom configuration
|
||||
func BenchmarkWithCustomConfigPNG(b *testing.B) {
|
||||
config, err := Configure(
|
||||
WithColorSaturation(0.6),
|
||||
WithBackgroundColor("#123456"),
|
||||
WithPadding(0.1),
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatalf("Configure failed: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := ToPNG("benchmark-png-config@test.com", 128, config)
|
||||
if err != nil {
|
||||
b.Fatalf("ToPNG with config failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkConcurrentGeneration tests performance under concurrent load
|
||||
func BenchmarkConcurrentGeneration(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
value := fmt.Sprintf("concurrent_user_%d@example.com", i)
|
||||
_, err := Generate(value, 128)
|
||||
if err != nil {
|
||||
b.Fatalf("Generate failed: %v", err)
|
||||
}
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkBatchGeneration tests memory allocation patterns for batch generation
|
||||
func BenchmarkBatchGeneration(b *testing.B) {
|
||||
const batchSize = 100
|
||||
|
||||
b.SetBytes(batchSize) // Report throughput as items/sec
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := 0; j < batchSize; j++ {
|
||||
value := fmt.Sprintf("batch_user_%d@example.com", j)
|
||||
_, err := Generate(value, 64)
|
||||
if err != nil {
|
||||
b.Fatalf("Generate failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for new public API functions
|
||||
|
||||
func TestToSVG(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value interface{}
|
||||
size int
|
||||
valid bool
|
||||
}{
|
||||
{"string input", "test@example.com", 64, true},
|
||||
{"int input", 12345, 64, true},
|
||||
{"float input", 123.45, 64, true},
|
||||
{"bool input", true, 64, true},
|
||||
{"nil input", nil, 64, true},
|
||||
{"byte slice", []byte("hello"), 64, true},
|
||||
{"zero size", "test", 0, false},
|
||||
{"negative size", "test", -10, false},
|
||||
input := "test@example.com"
|
||||
size := 64
|
||||
|
||||
svg, err := ToSVG(context.Background(), input, size)
|
||||
if err != nil {
|
||||
t.Fatalf("ToSVG(context.Background(), ) failed: %v", err)
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
svg, err := ToSVG(tt.value, tt.size)
|
||||
|
||||
if tt.valid {
|
||||
if err != nil {
|
||||
t.Fatalf("ToSVG failed: %v", err)
|
||||
}
|
||||
|
||||
if svg == "" {
|
||||
t.Error("ToSVG returned empty string")
|
||||
}
|
||||
|
||||
// Basic SVG validation
|
||||
if !strings.Contains(svg, "<svg") {
|
||||
t.Error("SVG output does not contain svg tag")
|
||||
}
|
||||
|
||||
if !strings.Contains(svg, "</svg>") {
|
||||
t.Error("SVG output does not contain closing svg tag")
|
||||
}
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid input")
|
||||
}
|
||||
}
|
||||
})
|
||||
if svg == "" {
|
||||
t.Error("ToSVG() returned empty string")
|
||||
}
|
||||
|
||||
// Basic SVG validation
|
||||
if !strings.HasPrefix(svg, "<svg") {
|
||||
t.Error("ToSVG() output doesn't start with <svg")
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(svg, "</svg>") {
|
||||
t.Error("ToSVG() output doesn't end with </svg>")
|
||||
}
|
||||
|
||||
if !strings.Contains(svg, "xmlns") {
|
||||
t.Error("ToSVG() output missing xmlns attribute")
|
||||
}
|
||||
}
|
||||
|
||||
func TestToPNG(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value interface{}
|
||||
size int
|
||||
valid bool
|
||||
}{
|
||||
{"string input", "test@example.com", 64, true},
|
||||
{"int input", 12345, 64, true},
|
||||
{"float input", 123.45, 64, true},
|
||||
{"bool input", false, 64, true},
|
||||
{"nil input", nil, 64, true},
|
||||
{"byte slice", []byte("hello"), 64, true},
|
||||
{"zero size", "test", 0, false},
|
||||
{"negative size", "test", -10, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
png, err := ToPNG(tt.value, tt.size)
|
||||
|
||||
if tt.valid {
|
||||
if err != nil {
|
||||
t.Fatalf("ToPNG failed: %v", err)
|
||||
}
|
||||
|
||||
if len(png) == 0 {
|
||||
t.Error("ToPNG returned empty data")
|
||||
}
|
||||
|
||||
// Basic PNG validation (check PNG signature)
|
||||
if len(png) < 8 || string(png[1:4]) != "PNG" {
|
||||
t.Error("PNG output does not have valid PNG signature")
|
||||
}
|
||||
} else {
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid input")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToHash(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value interface{}
|
||||
expected string
|
||||
}{
|
||||
{"string", "test", ComputeHash("test")},
|
||||
{"int", 123, ComputeHash("123")},
|
||||
{"float", 123.45, ComputeHash("123.45")},
|
||||
{"bool true", true, ComputeHash("true")},
|
||||
{"bool false", false, ComputeHash("false")},
|
||||
{"nil", nil, ComputeHash("")},
|
||||
{"byte slice", []byte("hello"), ComputeHash("hello")},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
hash := ToHash(tt.value)
|
||||
|
||||
if hash != tt.expected {
|
||||
t.Errorf("ToHash(%v) = %s, expected %s", tt.value, hash, tt.expected)
|
||||
}
|
||||
|
||||
// Hash should be non-empty and valid hex
|
||||
if hash == "" {
|
||||
t.Error("ToHash returned empty string")
|
||||
}
|
||||
|
||||
// Should be valid hex characters
|
||||
for _, c := range hash {
|
||||
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
|
||||
t.Errorf("Hash contains invalid character: %c", c)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToSVGWithConfig(t *testing.T) {
|
||||
value := "config-test"
|
||||
input := "test@example.com"
|
||||
size := 64
|
||||
|
||||
// Test with custom configuration
|
||||
config, err := Configure(
|
||||
WithHueRestrictions([]float64{120, 240}), // Blue/green hues
|
||||
WithColorSaturation(0.8),
|
||||
WithBackgroundColor("#ffffff"),
|
||||
WithPadding(0.1),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Configure failed: %v", err)
|
||||
}
|
||||
|
||||
svg, err := ToSVG(value, size, config)
|
||||
if err != nil {
|
||||
t.Fatalf("ToSVG with config failed: %v", err)
|
||||
}
|
||||
|
||||
if svg == "" {
|
||||
t.Error("ToSVG with config returned empty string")
|
||||
}
|
||||
|
||||
// Test that it's different from default
|
||||
defaultSvg, err := ToSVG(value, size)
|
||||
if err != nil {
|
||||
t.Fatalf("ToSVG default failed: %v", err)
|
||||
}
|
||||
|
||||
if svg == defaultSvg {
|
||||
t.Error("SVG with config is identical to default SVG")
|
||||
}
|
||||
}
|
||||
|
||||
func TestToPNGWithConfig(t *testing.T) {
|
||||
value := "config-test"
|
||||
size := 64
|
||||
|
||||
// Test with custom configuration
|
||||
config, err := Configure(
|
||||
WithHueRestrictions([]float64{60, 180}), // Yellow/cyan hues
|
||||
WithColorSaturation(0.9),
|
||||
WithBackgroundColor("#000000"),
|
||||
WithPadding(0.05),
|
||||
)
|
||||
png, err := ToPNG(context.Background(), input, size)
|
||||
if err != nil {
|
||||
t.Fatalf("Configure failed: %v", err)
|
||||
t.Fatalf("ToPNG(context.Background(), ) failed: %v", err)
|
||||
}
|
||||
|
||||
png, err := ToPNG(value, size, config)
|
||||
if err != nil {
|
||||
t.Fatalf("ToPNG with config failed: %v", err)
|
||||
}
|
||||
|
||||
|
||||
if len(png) == 0 {
|
||||
t.Error("ToPNG with config returned empty data")
|
||||
t.Error("ToPNG() returned empty byte slice")
|
||||
}
|
||||
|
||||
// Test that it's different from default
|
||||
defaultPng, err := ToPNG(value, size)
|
||||
if err != nil {
|
||||
t.Fatalf("ToPNG default failed: %v", err)
|
||||
|
||||
// Basic PNG validation - check PNG signature
|
||||
if len(png) < 8 {
|
||||
t.Error("ToPNG() output too short to be valid PNG")
|
||||
return
|
||||
}
|
||||
|
||||
if bytes.Equal(png, defaultPng) {
|
||||
t.Error("PNG with config is identical to default PNG")
|
||||
|
||||
// PNG signature: 89 50 4E 47 0D 0A 1A 0A
|
||||
expectedSignature := []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}
|
||||
for i, expected := range expectedSignature {
|
||||
if png[i] != expected {
|
||||
t.Errorf("ToPNG(context.Background(), ) invalid PNG signature at byte %d: expected %02x, got %02x", i, expected, png[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublicAPIConsistency(t *testing.T) {
|
||||
value := "consistency-test"
|
||||
func TestDeterminism(t *testing.T) {
|
||||
input := "determinism-test"
|
||||
size := 64
|
||||
|
||||
// Generate with both old and new APIs
|
||||
icon, err := Generate(value, size)
|
||||
if err != nil {
|
||||
t.Fatalf("Generate failed: %v", err)
|
||||
|
||||
// Generate the same input multiple times
|
||||
svg1, err1 := ToSVG(context.Background(), input, size)
|
||||
svg2, err2 := ToSVG(context.Background(), input, size)
|
||||
|
||||
if err1 != nil || err2 != nil {
|
||||
t.Fatalf("ToSVG(context.Background(), ) failed: err1=%v, err2=%v", err1, err2)
|
||||
}
|
||||
|
||||
oldSvg, err := icon.ToSVG()
|
||||
if err != nil {
|
||||
t.Fatalf("Icon.ToSVG failed: %v", err)
|
||||
|
||||
if svg1 != svg2 {
|
||||
t.Error("ToSVG() not deterministic: same input produced different output")
|
||||
}
|
||||
|
||||
oldPng, err := icon.ToPNG()
|
||||
if err != nil {
|
||||
t.Fatalf("Icon.ToPNG failed: %v", err)
|
||||
|
||||
png1, err1 := ToPNG(context.Background(), input, size)
|
||||
png2, err2 := ToPNG(context.Background(), input, size)
|
||||
|
||||
if err1 != nil || err2 != nil {
|
||||
t.Fatalf("ToPNG(context.Background(), ) failed: err1=%v, err2=%v", err1, err2)
|
||||
}
|
||||
|
||||
newSvg, err := ToSVG(value, size)
|
||||
if err != nil {
|
||||
t.Fatalf("ToSVG failed: %v", err)
|
||||
|
||||
if len(png1) != len(png2) {
|
||||
t.Error("ToPNG() not deterministic: same input produced different length output")
|
||||
return
|
||||
}
|
||||
|
||||
newPng, err := ToPNG(value, size)
|
||||
if err != nil {
|
||||
t.Fatalf("ToPNG failed: %v", err)
|
||||
}
|
||||
|
||||
// Results should be identical
|
||||
if oldSvg != newSvg {
|
||||
t.Error("SVG output differs between old and new APIs")
|
||||
}
|
||||
|
||||
if !bytes.Equal(oldPng, newPng) {
|
||||
t.Error("PNG output differs between old and new APIs")
|
||||
|
||||
for i := range png1 {
|
||||
if png1[i] != png2[i] {
|
||||
t.Errorf("ToPNG(context.Background(), ) not deterministic: difference at byte %d", i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeConversion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input interface{}
|
||||
expected string
|
||||
}{
|
||||
{"string", "hello", "hello"},
|
||||
{"int", 42, "42"},
|
||||
{"int64", int64(42), "42"},
|
||||
{"float64", 3.14, "3.14"},
|
||||
{"bool true", true, "true"},
|
||||
{"bool false", false, "false"},
|
||||
{"nil", nil, ""},
|
||||
{"byte slice", []byte("test"), "test"},
|
||||
{"struct", struct{ Name string }{"test"}, "{test}"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := convertToString(tt.input)
|
||||
if result != tt.expected {
|
||||
t.Errorf("convertToString(%v) = %s, expected %s", tt.input, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function for min
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user