imaging_test.go
1 package tools 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "image" 7 "image/color" 8 "image/jpeg" 9 "image/png" 10 "os" 11 "path/filepath" 12 "testing" 13 ) 14 15 func createMinimalPNG() []byte { 16 img := image.NewRGBA(image.Rect(0, 0, 1, 1)) 17 img.Set(0, 0, color.RGBA{255, 0, 0, 255}) 18 var buf bytes.Buffer 19 png.Encode(&buf, img) 20 return buf.Bytes() 21 } 22 23 func createMinimalJPEG() []byte { 24 img := image.NewRGBA(image.Rect(0, 0, 1, 1)) 25 img.Set(0, 0, color.RGBA{0, 255, 0, 255}) 26 var buf bytes.Buffer 27 jpeg.Encode(&buf, img, nil) 28 return buf.Bytes() 29 } 30 31 func TestEncodeImage(t *testing.T) { 32 pngData := createMinimalPNG() 33 path := filepath.Join(t.TempDir(), "test.png") 34 if err := os.WriteFile(path, pngData, 0644); err != nil { 35 t.Fatalf("failed to write test file: %v", err) 36 } 37 38 block, err := EncodeImage(path) 39 if err != nil { 40 t.Fatalf("EncodeImage error: %v", err) 41 } 42 if block.MediaType != "image/png" { 43 t.Errorf("expected MediaType 'image/png', got %q", block.MediaType) 44 } 45 46 decoded, err := base64.StdEncoding.DecodeString(block.Data) 47 if err != nil { 48 t.Fatalf("base64 decode error: %v", err) 49 } 50 if !bytes.Equal(decoded, pngData) { 51 t.Error("decoded base64 data does not match original PNG bytes") 52 } 53 } 54 55 func TestEncodeImage_FileNotFound(t *testing.T) { 56 _, err := EncodeImage("/nonexistent/file.png") 57 if err == nil { 58 t.Error("expected error for nonexistent file") 59 } 60 } 61 62 func TestEncodeImage_JPEG(t *testing.T) { 63 jpegData := createMinimalJPEG() 64 path := filepath.Join(t.TempDir(), "test.jpg") 65 if err := os.WriteFile(path, jpegData, 0644); err != nil { 66 t.Fatalf("failed to write test file: %v", err) 67 } 68 69 block, err := EncodeImage(path) 70 if err != nil { 71 t.Fatalf("EncodeImage error: %v", err) 72 } 73 if block.MediaType != "image/jpeg" { 74 t.Errorf("expected MediaType 'image/jpeg', got %q", block.MediaType) 75 } 76 } 77 78 func TestEncodeImage_JPEG_Uppercase(t *testing.T) { 79 jpegData := createMinimalJPEG() 80 path := filepath.Join(t.TempDir(), "test.JPEG") 81 if err := os.WriteFile(path, jpegData, 0644); err != nil { 82 t.Fatalf("failed to write test file: %v", err) 83 } 84 85 block, err := EncodeImage(path) 86 if err != nil { 87 t.Fatalf("EncodeImage error: %v", err) 88 } 89 if block.MediaType != "image/jpeg" { 90 t.Errorf("expected MediaType 'image/jpeg', got %q", block.MediaType) 91 } 92 } 93 94 func TestGetScreenDimensions(t *testing.T) { 95 w, h, err := GetScreenDimensions() 96 if err != nil { 97 t.Skipf("skipping: no display available (%v)", err) 98 } 99 if w <= 0 { 100 t.Errorf("expected width > 0, got %d", w) 101 } 102 if h <= 0 { 103 t.Errorf("expected height > 0, got %d", h) 104 } 105 } 106 107 func TestParseScreenDimensions_Resolution(t *testing.T) { 108 output := `Graphics/Displays: 109 110 Apple M2 Pro: 111 112 Chipset Model: Apple M2 Pro 113 Type: GPU 114 Bus: Built-In 115 Total Number of Cores: 19 116 Vendor: Apple (0x106b) 117 Metal Support: Metal 3 118 Displays: 119 Color LCD: 120 Display Type: Built-In Retina LCD 121 Resolution: 1512 x 982 122 Main Display: Yes 123 Mirror: Off 124 Online: Yes 125 Automatically Adjust Brightness: Yes 126 Connection Type: Internal 127 ` 128 w, h, err := parseScreenDimensions(output) 129 if err != nil { 130 t.Fatalf("parseScreenDimensions error: %v", err) 131 } 132 if w != 1512 { 133 t.Errorf("expected width 1512, got %d", w) 134 } 135 if h != 982 { 136 t.Errorf("expected height 982, got %d", h) 137 } 138 } 139 140 func TestParseScreenDimensions_UILooksLike(t *testing.T) { 141 output := `Graphics/Displays: 142 143 Apple M1: 144 145 Chipset Model: Apple M1 146 Displays: 147 Color LCD: 148 Display Type: Built-In Retina LCD 149 Resolution: 2560 x 1600 (Retina) 150 UI Looks like: 1440 x 900 @ 120.00Hz 151 Main Display: Yes 152 ` 153 w, h, err := parseScreenDimensions(output) 154 if err != nil { 155 t.Fatalf("parseScreenDimensions error: %v", err) 156 } 157 if w != 1440 { 158 t.Errorf("expected width 1440, got %d", w) 159 } 160 if h != 900 { 161 t.Errorf("expected height 900, got %d", h) 162 } 163 } 164 165 func TestParseScreenDimensions_RetinaStripped(t *testing.T) { 166 output := `Graphics/Displays: 167 168 Apple M2: 169 170 Displays: 171 Color LCD: 172 Resolution: 2880 x 1800 (Retina) 173 Main Display: Yes 174 ` 175 w, h, err := parseScreenDimensions(output) 176 if err != nil { 177 t.Fatalf("parseScreenDimensions error: %v", err) 178 } 179 if w != 2880 { 180 t.Errorf("expected width 2880, got %d", w) 181 } 182 if h != 1800 { 183 t.Errorf("expected height 1800, got %d", h) 184 } 185 } 186 187 func TestParseScreenDimensions_NoDisplay(t *testing.T) { 188 output := `Graphics/Displays: 189 190 Apple M2 Pro: 191 192 Chipset Model: Apple M2 Pro 193 ` 194 _, _, err := parseScreenDimensions(output) 195 if err == nil { 196 t.Error("expected error for output with no display info") 197 } 198 } 199 200 func TestScaleCoordinates(t *testing.T) { 201 // API: 1280x800, Screen: 1440x900 202 x, y := ScaleCoordinates(640, 400, 1280, 800, 1440, 900) 203 if x != 720 { 204 t.Errorf("expected x=720, got %d", x) 205 } 206 if y != 450 { 207 t.Errorf("expected y=450, got %d", y) 208 } 209 } 210 211 func TestScaleCoordinates_Identity(t *testing.T) { 212 x, y := ScaleCoordinates(100, 200, 1280, 800, 1280, 800) 213 if x != 100 { 214 t.Errorf("expected x=100, got %d", x) 215 } 216 if y != 200 { 217 t.Errorf("expected y=200, got %d", y) 218 } 219 } 220 221 func TestScaleCoordinates_Origin(t *testing.T) { 222 x, y := ScaleCoordinates(0, 0, 1280, 800, 1920, 1080) 223 if x != 0 { 224 t.Errorf("expected x=0, got %d", x) 225 } 226 if y != 0 { 227 t.Errorf("expected y=0, got %d", y) 228 } 229 } 230 231 func TestScaleCoordinates_MaxCorner(t *testing.T) { 232 x, y := ScaleCoordinates(1280, 800, 1280, 800, 1920, 1080) 233 if x != 1920 { 234 t.Errorf("expected x=1920, got %d", x) 235 } 236 if y != 1080 { 237 t.Errorf("expected y=1080, got %d", y) 238 } 239 } 240 241 func TestClampCoordinates(t *testing.T) { 242 x, y := ClampCoordinates(-10, 1000, 1280, 800) 243 if x != 0 { 244 t.Errorf("expected x=0, got %d", x) 245 } 246 if y != 799 { 247 t.Errorf("expected y=799, got %d", y) 248 } 249 } 250 251 func TestClampCoordinates_InBounds(t *testing.T) { 252 x, y := ClampCoordinates(500, 400, 1280, 800) 253 if x != 500 { 254 t.Errorf("expected x=500, got %d", x) 255 } 256 if y != 400 { 257 t.Errorf("expected y=400, got %d", y) 258 } 259 } 260 261 func TestClampCoordinates_BothNegative(t *testing.T) { 262 x, y := ClampCoordinates(-5, -10, 1920, 1080) 263 if x != 0 { 264 t.Errorf("expected x=0, got %d", x) 265 } 266 if y != 0 { 267 t.Errorf("expected y=0, got %d", y) 268 } 269 } 270 271 func TestClampCoordinates_BothOverflow(t *testing.T) { 272 x, y := ClampCoordinates(2000, 1500, 1280, 800) 273 if x != 1279 { 274 t.Errorf("expected x=1279, got %d", x) 275 } 276 if y != 799 { 277 t.Errorf("expected y=799, got %d", y) 278 } 279 } 280 281 func TestClampCoordinates_ExactBoundary(t *testing.T) { 282 x, y := ClampCoordinates(1280, 800, 1280, 800) 283 if x != 1279 { 284 t.Errorf("expected x=1279, got %d", x) 285 } 286 if y != 799 { 287 t.Errorf("expected y=799, got %d", y) 288 } 289 }