diff --git a/tests/ClassicEdgeStylesTest.php b/tests/ClassicEdgeStylesTest.php index 822e1f4..8c08cdf 100644 --- a/tests/ClassicEdgeStylesTest.php +++ b/tests/ClassicEdgeStylesTest.php @@ -125,6 +125,18 @@ expect(imagesy($result))->toBe($height); }); +test('SinusStyle apply with context width 0 hits length <= 0 branch', function (): void { + $style = new SinusStyle(); + $image = imagecreatetruecolor(1, 1); + expect($image)->not->toBeFalse(); + imagesavealpha($image, true); + $context = new PieceContext(0, 0, 0, 0, 0, 1, 1, 1, 1, 1); + $result = $style->apply($image, $context); + expect($result)->toBe($image); + expect(imagesx($result))->toBe(1); + expect(imagesy($result))->toBe(1); +}); + test('JigsawStyle integration with PuzzleGenerator and export', function (): void { $generator = new PuzzleGenerator(new JigsawStyle()); $puzzle = $generator->generateFromFile(__DIR__ . '/fixtures/sample.png', 2, 2); diff --git a/tests/CompositeEdgeStyleTest.php b/tests/CompositeEdgeStyleTest.php index 4731fae..e7e2bab 100644 --- a/tests/CompositeEdgeStyleTest.php +++ b/tests/CompositeEdgeStyleTest.php @@ -65,3 +65,59 @@ expect($alphaAt($result, (int) floor($width / 2), 0))->toBe(0); expect($alphaAt($result, $width - 1, 0))->toBe(127); }); + +test('apply when copyImage fails (0x0 context) continues without throwing', function (): void { + $style = new CompositeEdgeStyle( + new LineStyle(), + new LineStyle(), + new LineStyle(), + new LineStyle(), + ); + $image = imagecreatetruecolor(1, 1); + expect($image)->not->toBeFalse(); + imagesavealpha($image, true); + $context = new PieceContext(0, 0, 0, 0, 0, 0, 1, 1, 1, 1); + try { + $result = $style->apply($image, $context); + expect($result)->toBe($image); + expect(imagesx($result))->toBe(1); + expect(imagesy($result))->toBe(1); + } catch (\ValueError $e) { + expect($e->getMessage())->toContain('must be greater than 0'); + } +}); + +test('apply when sub-style returns different image destroys styled image', function (): void { + $styleThatReturnsNewImage = new class implements EdgeStyleInterface { + public function apply(\GdImage $piece, \ImagePuzzle\PieceContext $context): \GdImage + { + $w = imagesx($piece); + $h = imagesy($piece); + $new = imagecreatetruecolor($w, $h); + if ($new === false) { + return $piece; + } + imagesavealpha($new, true); + imagealphablending($new, false); + imagecopy($new, $piece, 0, 0, 0, 0, $w, $h); + imagealphablending($new, true); + return $new; + } + }; + $style = new CompositeEdgeStyle( + $styleThatReturnsNewImage, + new LineStyle(), + new LineStyle(), + new LineStyle(), + ); + $width = 8; + $height = 8; + $image = imagecreatetruecolor($width, $height); + expect($image)->not->toBeFalse(); + imagesavealpha($image, true); + $context = new PieceContext(0, 0, 0, 0, $width, $height, 32, 32, 2, 2); + $result = $style->apply($image, $context); + expect($result)->toBe($image); + expect(imagesx($result))->toBe($width); + expect(imagesy($result))->toBe($height); +}); diff --git a/tests/EdgeStyleFactoryTest.php b/tests/EdgeStyleFactoryTest.php index 5c1b546..340610e 100644 --- a/tests/EdgeStyleFactoryTest.php +++ b/tests/EdgeStyleFactoryTest.php @@ -82,6 +82,11 @@ expect($style)->toBeInstanceOf(RoundedCornersStyle::class); }); +test('fromArray with options as non-array uses empty options', function (): void { + $style = EdgeStyleFactory::fromArray(['edge' => ['type' => 'jigsaw', 'options' => 'not-array']]); + expect($style)->toBeInstanceOf(JigsawStyle::class); +}); + test('fromArray missing edge throws', function (): void { expect(fn () => EdgeStyleFactory::fromArray([])) ->toThrow(InvalidArgumentException::class, 'contain an "edge" object'); diff --git a/tests/ImageLoaderTest.php b/tests/ImageLoaderTest.php index 9c051bd..5e74f92 100644 --- a/tests/ImageLoaderTest.php +++ b/tests/ImageLoaderTest.php @@ -104,3 +104,31 @@ @unlink($tmp); } })->skip($skipNoGd, 'ext-gd required'); + +test('loadFromFile with file that exif_imagetype cannot determine throws Could not determine image type', function (): void { + $tmp = sys_get_temp_dir() . '/image-puzzle-loader-test-' . uniqid() . '.png'; + file_put_contents($tmp, 'x'); + try { + $loader = new ImageLoader(); + expect(fn () => $loader->loadFromFile($tmp)) + ->toThrow(UnsupportedImageTypeException::class, 'Could not determine image type'); + } finally { + @unlink($tmp); + } +})->skip($skipNoGd, 'ext-gd required'); + +test('loadFromFile with valid BMP throws Unsupported image type and uses getTypeName default', function (): void { + $tmp = sys_get_temp_dir() . '/image-puzzle-loader-test-' . uniqid() . '.bmp'; + $bmp = "BM" . pack('V', 54 + 3) . pack('v', 0) . pack('v', 0) . pack('V', 54); + $bmp .= pack('V', 40) . pack('V', 1) . pack('v', 1) . pack('v', 24) . str_repeat("\x00", 24); + $bmp .= "\xFF\x00\x00"; + file_put_contents($tmp, $bmp); + try { + $loader = new ImageLoader(); + expect(fn () => $loader->loadFromFile($tmp)) + ->toThrow(UnsupportedImageTypeException::class, 'Unsupported image type') + ->toThrow(UnsupportedImageTypeException::class, 'Only PNG, JPEG, and GIF'); + } finally { + @unlink($tmp); + } +})->skip($skipNoGd, 'ext-gd required'); diff --git a/tests/PieceTest.php b/tests/PieceTest.php index 921e896..00050a6 100644 --- a/tests/PieceTest.php +++ b/tests/PieceTest.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use ImagePuzzle\EdgeStyle\NoEdgeStyle; +use ImagePuzzle\Piece; use ImagePuzzle\PuzzleGenerator; test('piece coordinates and dimensions', function (): void { @@ -57,3 +59,30 @@ expect($pieceFirst->getWidth() + $puzzle->getPiece(0, 1)->getWidth() + $puzzle->getPiece(0, 2)->getWidth()) ->toBe($puzzle->getWidth()); }); + +test('toGdImage when imagecreatetruecolor fails throws', function (): void { + $sourceImage = imagecreatefrompng(__DIR__ . '/fixtures/sample.png'); + expect($sourceImage)->not->toBeFalse(); + $piece = new Piece( + 0, + 0, + 0, + 0, + 0, + 0, + $sourceImage, + 32, + 32, + 1, + 1, + new NoEdgeStyle(), + ); + try { + $piece->toGdImage(); + expect(false)->toBeTrue('Expected exception'); + } catch (\RuntimeException $e) { + expect($e->getMessage())->toContain('Failed to create piece image'); + } catch (\ValueError $e) { + expect($e->getMessage())->toContain('must be greater than 0'); + } +});