Create image generation class to save thumbnails

Added test for complete coverage

Added dropping of thumbnails

Prevent accidential removal of primary image

Signed-off-by: Christian Wolf <github@christianwolf.email>
This commit is contained in:
Christian Wolf 2022-03-23 19:29:41 +01:00
Родитель 10a1e78c15
Коммит 247b166e33
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 9FC3120E932F73F1
3 изменённых файлов: 241 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,74 @@
<?php
namespace OCA\Cookbook\Helper\ImageService;
use OCP\Files\File;
use OCP\Files\Folder;
use OCA\Cookbook\Service\ThumbnailService;
/**
* This class provides heler function to generate appropriate thumbnails according to the needs.
*/
class ImageGenerationHelper {
private const MAP = [
ImageSize::PRIMARY_IMAGE => 'full.jpg',
ImageSize::THUMBNAIL => 'thumb.jpg',
ImageSize::MINI_THUMBNAIL => 'thumb16.jpg',
];
/**
* @var ThumbnailService
*/
private $thumbnailService;
public function __construct(
ThumbnailService $thumbnailService
) {
$this->thumbnailService = $thumbnailService;
}
/**
* Calculate the image data of a thumbnail of defined size and store it in the nextcloud server storage.
*
* @param File $fullImage The full-sized image to use as a starting point
* @param int $type The requested size of the thunbmail
* @param File $dstFile The name of the file to store the thumbnail to
* @return void
*/
public function generateThumbnail(File $fullImage, int $type, File $dstFile): void {
if ($type === ImageSize::PRIMARY_IMAGE) {
return;
}
$fullContent = $fullImage->getContent();
$thumbContent = $this->thumbnailService->getThumbnail($fullContent, $type);
$dstFile->putContent($thumbContent);
$dstFile->touch();
}
/**
* Ensure that a thumbnail is not existing in the file system.
*
* This method checks if a certain thumbail size is present in the recipe folder and removes the file accordingly.
* Note: If the thumbnail is not present, this method does nothing.
*
* The main image will not be dropped.
*
* @param Folder $recipeFolder The folder containing the recipe
* @param integer $type The type of the thumbnail to remove
* @return void
*/
public function drop(Folder $recipeFolder, int $type): void {
if ($type === ImageSize::PRIMARY_IMAGE) {
return;
}
$filename = ImageSize::NAME_MAP[$type];
if ($recipeFolder->nodeExists($filename)) {
$recipeFolder->get($filename)->delete();
}
}
}

Просмотреть файл

@ -0,0 +1,15 @@
<?php
namespace OCA\Cookbook\Helper\ImageService;
class ImageSize {
public const PRIMARY_IMAGE = 1;
public const THUMBNAIL = 2;
public const MINI_THUMBNAIL = 3;
public const NAME_MAP = [
self::PRIMARY_IMAGE => 'full.jpg',
self::THUMBNAIL => 'thumb.jpg',
self::MINI_THUMBNAIL => 'thumb16.jpg',
];
}

Просмотреть файл

@ -0,0 +1,152 @@
<?php
namespace OCA\Cookbook\tests\Unit\Helper\ImageService;
use OCA\Cookbook\Helper\ImageService\ImageGenerationHelper;
use PHPUnit\Framework\TestCase;
use OCA\Cookbook\Helper\ImageService\ImageSize;
use OCA\Cookbook\Service\ThumbnailService;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Stub;
/**
* @covers OCA\Cookbook\Helper\ImageService\ImageGenerationHelper
*/
class ImageGenerationHelperTest extends TestCase {
/**
* @var ThumbnailService|MockObject
*/
private $thumbnailService;
/**
* @var ImageGenerationHelper
*/
private $dut;
protected function setUp(): void {
$this->thumbnailService = $this->createMock(ThumbnailService::class);
$this->dut = new ImageGenerationHelper($this->thumbnailService);
}
public function dpThumbnails() {
yield [ImageSize::THUMBNAIL];
yield [ImageSize::MINI_THUMBNAIL];
}
/**
* @dataProvider dpThumbnails
*/
public function testThumbnailGeneration($type) {
/**
* @var Stub|File $fullImage
*/
$fullImage = $this->createStub(File::class);
/**
* @var MockObject|File
*/
$dstFile = $this->createMock(File::class);
$fullContent = 'The content of the full image';
$thumbContent = 'The content of the thumb file';
$fullImage->method('getContent')->willReturn($fullContent);
$fileWasSaved = true;
$dstFile->expects($this->once())->method('putContent')->with($thumbContent)->will(
$this->returnCallback(function ($content) use (&$fileWasSaved) {
$fileWasSaved = false;
})
);
$dstFile->expects($this->once())->method('touch')->will($this->returnCallback(
function () use (&$fileWasSaved) {
$fileWasSaved = true;
}
));
$this->thumbnailService->method('getThumbnail')->with($fullContent, $type)
->willReturn($thumbContent);
$this->dut->generateThumbnail($fullImage, $type, $dstFile);
$this->assertTrue($fileWasSaved, 'File was not touched after modifications.');
}
public function testFullSizeImage() {
$fullImage = $this->createStub(File::class);
/**
* @var MockObject|File $dstFile
*/
$dstFile = $this->createMock(File::class);
$dstFile->expects($this->never())->method('putContent');
$this->dut->generateThumbnail($fullImage, ImageSize::PRIMARY_IMAGE, $dstFile);
}
public function dpDropExisting() {
yield [ImageSize::THUMBNAIL, 'thumb.jpg'];
yield [ImageSize::MINI_THUMBNAIL, 'thumb16.jpg'];
}
/**
* @dataProvider dpDropExisting
*/
public function testDropThumbnailExisting($type, $filename) {
/**
* @var MockObject|Folder $folder
*/
$folder = $this->createMock(Folder::class);
/**
* @var MockObject|File $thumbnail
*/
$thumbnail = $this->createMock(File::class);
$folder->method('get')->with($filename)->willReturn($thumbnail);
$folder->method('nodeExists')->willReturn(true);
$thumbnail->expects($this->once())->method('delete');
$this->dut->drop($folder, $type);
}
/**
* @dataProvider dpDropExisting
*/
public function testDropThumbnailNonExisting($type, $filename) {
/**
* @var MockObject|Folder $folder
*/
$folder = $this->createMock(Folder::class);
$folder->method('get')->with($filename)->willThrowException(new NotFoundException());
$folder->method('nodeExists')->willReturn(false);
$this->dut->drop($folder, $type);
$this->assertTrue(true, 'No Exception was thrown');
}
public function testDropThumbnailMainImage() {
/**
* @var MockObject|Folder $folder
*/
$folder = $this->createMock(Folder::class);
/**
* @var MockObject|Folder $image
*/
$image = $this->createMock(File::class);
$folder->method('get')->with('full.jpg')->willReturn($image);
$folder->method('nodeExists')->willReturn(true);
$image->expects($this->never())->method('delete');
$this->dut->drop($folder, ImageSize::PRIMARY_IMAGE);
$this->assertTrue(true, 'No Exception was thrown');
}
}