Steuerkram/tests/19_ImageCropTest.php

154 lines
5.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
/**
* Test 19: ImageCropService – automatisches Wegschneiden weißer Ränder.
*
* Erstellt synthetische GD-Bilder im Arbeitsspeicher, speichert sie als
* temporäre Dateien und prüft, ob cropWhitespace() korrekt zuschneidet.
* Kein Datenbankzugriff nötig.
*/
class ImageCropTest extends TestCase
{
protected string $testName = 'Test 19: ImageCropService – Weißrand-Zuschnitt';
/** @var string[] */
private array $tmpFiles = [];
public function run(): void
{
$this->testCropJpeg();
$this->testCropPng();
$this->testNoCropWhenBorderTooSmall();
$this->testAllWhiteReturnsNoChange();
$this->testUnsupportedMimeReturnsFalse();
}
// -------------------------------------------------------------------------
private function testCropJpeg(): void
{
// 300×200 weißes Bild, rotes Rechteck 50×50 px bei (100, 75)
$img = imagecreatetruecolor(300, 200);
$white = imagecolorallocate($img, 255, 255, 255);
$red = imagecolorallocate($img, 200, 50, 50);
imagefill($img, 0, 0, $white);
imagefilledrectangle($img, 100, 75, 149, 124, $red);
$path = $this->saveTmp($img, 'jpeg');
imagedestroy($img);
$service = new \Steuerkram\Services\ImageCropService();
$result = $service->cropWhitespace($path);
$this->assert($result === true, 'JPEG: cropWhitespace() gibt true zurück');
[$newW, $newH] = getimagesize($path);
// Erwartete Maße: Rechteck (100,75)–(149,124) + PADDING 25 ringsum → 100×100.
// JPEG-Verlust-Komprimierung kann Farbe ±2px in Nachbar-DCT-Blöcke bluten lassen,
// daher Toleranz von 2px je Seite.
$this->assert($newW <= 104 && $newW >= 96, "JPEG: Breite nach Crop im Bereich 96–104 px (erhalten: {$newW})");
$this->assert($newH <= 104 && $newH >= 96, "JPEG: Höhe nach Crop im Bereich 96–104 px (erhalten: {$newH})");
}
private function testCropPng(): void
{
// 400×300 weißes PNG, blaues Rechteck 80×60 px bei (160, 120)
$img = imagecreatetruecolor(400, 300);
$white = imagecolorallocate($img, 255, 255, 255);
$blue = imagecolorallocate($img, 50, 100, 200);
imagefill($img, 0, 0, $white);
imagefilledrectangle($img, 160, 120, 239, 179, $blue);
$path = $this->saveTmp($img, 'png');
imagedestroy($img);
$service = new \Steuerkram\Services\ImageCropService();
$result = $service->cropWhitespace($path);
$this->assert($result === true, 'PNG: cropWhitespace() gibt true zurück');
[$newW, $newH] = getimagesize($path);
// cropX=135, cropY=95, cropW=130, cropH=110
$this->assertEquals(130, $newW, 'PNG: Breite nach Crop korrekt (80px + 2×25 Padding)');
$this->assertEquals(110, $newH, 'PNG: Höhe nach Crop korrekt (60px + 2×25 Padding)');
}
private function testNoCropWhenBorderTooSmall(): void
{
// Inhalt füllt fast das gesamte Bild – weniger als 3 % Rand
$img = imagecreatetruecolor(200, 200);
$white = imagecolorallocate($img, 255, 255, 255);
$red = imagecolorallocate($img, 200, 50, 50);
imagefill($img, 0, 0, $white);
// Inhalt lässt nur 1px Rand ringsum → Einsparung < 3 %
imagefilledrectangle($img, 1, 1, 198, 198, $red);
$path = $this->saveTmp($img, 'jpeg');
imagedestroy($img);
$originalSize = filesize($path);
$service = new \Steuerkram\Services\ImageCropService();
$result = $service->cropWhitespace($path);
$this->assert($result === false, 'Kein Crop wenn Randersparnis < 3 %');
[$newW, $newH] = getimagesize($path);
$this->assertEquals(200, $newW, 'Bildbreite bleibt unverändert bei minimalem Rand');
}
private function testAllWhiteReturnsNoChange(): void
{
$img = imagecreatetruecolor(100, 100);
$white = imagecolorallocate($img, 255, 255, 255);
imagefill($img, 0, 0, $white);
$path = $this->saveTmp($img, 'jpeg');
imagedestroy($img);
$service = new \Steuerkram\Services\ImageCropService();
$result = $service->cropWhitespace($path);
$this->assert($result === false, 'Komplett weißes Bild: kein Crop, gibt false zurück');
}
private function testUnsupportedMimeReturnsFalse(): void
{
// Erstellt eine Dummy-Textdatei – MIME-Typ wird nicht als JPEG/PNG erkannt
$path = sys_get_temp_dir() . '/crop_test_' . uniqid() . '.txt';
file_put_contents($path, 'kein Bild');
$this->tmpFiles[] = $path;
$service = new \Steuerkram\Services\ImageCropService();
$result = $service->cropWhitespace($path);
$this->assert($result === false, 'Nicht-Bild-Datei: cropWhitespace() gibt false zurück');
}
// -------------------------------------------------------------------------
private function saveTmp(\GdImage $img, string $type): string
{
$path = sys_get_temp_dir() . '/crop_test_' . uniqid() . '.' . $type;
$this->tmpFiles[] = $path;
if ($type === 'jpeg') {
imagejpeg($img, $path, 92);
} else {
imagepng($img, $path);
}
return $path;
}
public function tearDown(): void
{
foreach ($this->tmpFiles as $f) {
if (file_exists($f)) unlink($f);
}
parent::tearDown();
}
}