Created helper classes for management of image files

Added tests for helper classes

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

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

@ -0,0 +1,9 @@
<?php
namespace OCA\Cookbook\Exception;
class NoRecipeImageFoundException extends \Exception {
public function __construct($message = null, $code = null, $previous = null) {
parent::__construct($message, $code, $previous);
}
}

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

@ -0,0 +1,9 @@
<?php
namespace OCA\Cookbook\Exception;
class RecipeImageExistsException extends \Exception {
public function __construct($message = null, $code = null, $previous = null) {
parent::__construct($message, $code, $previous);
}
}

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

@ -0,0 +1,75 @@
<?php
namespace OCA\Cookbook\Helper\ImageService;
use OCP\IL10N;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCA\Cookbook\Exception\RecipeImageExistsException;
class ImageFileHelper {
private const NAME_MAIN = ImageSize::NAME_MAP[ImageSize::PRIMARY_IMAGE];
/**
* @var IL10N
*/
private $l;
public function __construct(
IL10N $l
) {
$this->l = $l;
}
/**
* Get the main image from a recipe
*
* @param Folder $recipeFolder The folder of the recipe
* @return File The image file
* @throws NotFoundException if no image has been found
* @todo Do not use low-level functions of NC core here.
*/
public function getImage(Folder $recipeFolder): File {
return $recipeFolder->get(self::NAME_MAIN);
}
/**
* Check if a recipe has a folder attached
*
* @param Folder $recipeFolder The folder of the recipe to check
* @return boolean true, if there is an image present
*/
public function hasImage(Folder $recipeFolder): bool {
return $recipeFolder->nodeExists(self::NAME_MAIN);
}
/**
* Drop the image of a recipe
*
* @param Folder $recipeFolder The folder contraining the recipe
* @return void
*/
public function dropImage(Folder $recipeFolder): void {
if ($recipeFolder->nodeExists(self::NAME_MAIN)) {
$recipeFolder->get(self::NAME_MAIN)->delete();
}
}
/**
* Create an image for a recipe
*
* @param Folder $recipeFolder The folder containing the recipe files.
* @return File The image object for the recipe
* @throws RecipeImageExistsException if the folder has already a image file present.
*/
public function createImage(Folder $recipeFolder): File {
if ($this->hasImage($recipeFolder)) {
throw new RecipeImageExistsException(
$this->l->t('The recipe has already an image file. Cannot create a new one.')
);
} else {
return $recipeFolder->newFile(self::NAME_MAIN);
}
}
}

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

@ -0,0 +1,129 @@
<?php
namespace OCA\Cookbook\Helper\ImageService;
use OCP\IL10N;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCA\Cookbook\Exception\NoRecipeImageFoundException;
/**
* This class allows to handle the files of the thumbnails
*/
class ThumbnailFileHelper {
/**
* @var ImageGenerationHelper
*/
private $generationHelper;
/**
* @var ImageFileHelper
*/
private $fileHelper;
/**
* @var IL10N
*/
private $l;
public function __construct(
ImageGenerationHelper $generationHelper,
ImageFileHelper $fileHelper,
IL10N $l
) {
$this->generationHelper = $generationHelper;
$this->fileHelper = $fileHelper;
$this->l = $l;
}
/**
* Ensure that a thumbnail for a certain size exists
*
* @param Folder $recipeFolder The folder of the recipe to check for
* @param integer $type The type of the thumbnail to generate
* @return File The thumbnail file
* @throws NoRecipeImageFoundException if the recipe has no primary image to create a thumbnail from
*/
public function ensureThumbnailExists(Folder $recipeFolder, int $type): File {
$filename = ImageSize::NAME_MAP[$type];
if ($recipeFolder->nodeExists($filename)) {
return $recipeFolder->get($filename);
} else {
if ($this->fileHelper->hasImage($recipeFolder)) {
$full = $this->fileHelper->getImage($recipeFolder);
$file = $recipeFolder->newFile($filename);
$this->generationHelper->generateThumbnail($full, $type, $file);
return $file;
} else {
throw new NoRecipeImageFoundException(
$this->l->t('There is no primary image for the recipe present.')
);
}
}
}
/**
* Recreate a thumbnail file.
* Check if a thumbnail file exists and reuse it if possible.
* Otherwise a new file is generated.
*
* @param Folder $recipeFolder The folder containing the recipe
* @param integer $type The thumbnail type to generate
* @return void
*/
private function recreateSingleThumbnail(Folder $recipeFolder, int $type): void {
$filename = ImageSize::NAME_MAP[$type];
if ($recipeFolder->nodeExists($filename)) {
$full = $this->fileHelper->getImage($recipeFolder);
$file = $recipeFolder->get($filename);
$this->generationHelper->generateThumbnail($full, $type, $file);
} else {
$this->ensureThumbnailExists($recipeFolder, $type);
}
}
/**
* Recreate all thumbnails in the recipe.
*
* This will remove them and create new ones.
*
* @param Folder $recipeFolder The folder containing the files of a recipe.
* @return void
* @throws NotFoundException If no full-scale image was found.
*/
public function recreateThumbnails(Folder $recipeFolder): void {
$this->recreateSingleThumbnail($recipeFolder, ImageSize::THUMBNAIL);
$this->recreateSingleThumbnail($recipeFolder, ImageSize::MINI_THUMBNAIL);
}
/**
* Drop a thumbnail in a recipe
*
* @param Folder $recipeFolder The folder of the recipe
* @param integer $type The thumbnail type to remove
* @return void
*/
private function dropSingleThumbnail(Folder $recipeFolder, int $type): void {
$filename = ImageSize::NAME_MAP[$type];
if ($recipeFolder->nodeExists($filename)) {
$recipeFolder->get($filename)->delete();
}
}
/**
* Drop all thumbnails from a recipe folder
*
* @param Folder $recipeFolder The folder to drop the thumbnails from
* @return void
*/
public function dropThumbnails(Folder $recipeFolder): void {
$this->dropSingleThumbnail($recipeFolder, ImageSize::THUMBNAIL);
$this->dropSingleThumbnail($recipeFolder, ImageSize::MINI_THUMBNAIL);
}
}

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

@ -0,0 +1,98 @@
<?php
namespace OCA\Cookbook\tests\unit\Helper\ImageService;
use OCA\Cookbook\Exception\RecipeImageExistsException;
use OCP\IL10N;
use OCP\Files\File;
use OCP\Files\Folder;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\MockObject;
use OCA\Cookbook\Helper\ImageService\ImageFileHelper;
use OCP\Files\NotFoundException;
/**
* @covers OCA\Cookbook\Helper\ImageService\ImageFileHelper
* @covers OCA\Cookbook\Exception\RecipeImageExistsException
*/
class ImageFileHelperTest extends TestCase {
/**
* @var ImageFileHelper
*/
private $dut;
/**
* @var MockObject|Folder
*/
private $folder;
protected function setUp(): void {
/**
* @var Stub|IL10N $l
*/
$l = $this->createStub(IL10N::class);
$l->method('t')->willReturnArgument(0);
$this->folder = $this->createMock(Folder::class);
$this->dut = new ImageFileHelper($l);
}
public function dpExisting() {
yield [true];
yield [false];
}
/**
* @dataProvider dpExisting
*/
public function testHasImage($present) {
$this->folder->method('nodeExists')->with('full.jpg')->willReturn($present);
$this->assertEquals($present, $this->dut->hasImage($this->folder));
}
public function testGetImage() {
$file = $this->createStub(File::class);
$this->folder->method('get')->with('full.jpg')->willReturn($file);
$this->assertSame($file, $this->dut->getImage($this->folder));
}
/**
* @dataProvider dpExisting
*/
public function testDropImage($existing) {
/**
* @var MockObject|File $file
*/
$file = $this->createMock(File::class);
$this->folder->method('nodeExists')->with('full.jpg')->willReturn($existing);
if ($existing) {
$this->folder->method('get')->with('full.jpg')->willReturn($file);
$file->expects($this->once())->method('delete');
} else {
$this->folder->method('get')->with('full.jpg')->willThrowException(new NotFoundException());
$file->expects($this->never())->method('delete');
}
$this->dut->dropImage($this->folder);
}
/**
* @dataProvider dpExisting
*/
public function testCreateImage($existing) {
$this->folder->method('nodeExists')->with('full.jpg')->willReturn($existing);
$file = $this->createStub(File::class);
$this->folder->method('newFile')->with('full.jpg')->willReturn($file);
if ($existing) {
$this->expectException(RecipeImageExistsException::class);
}
$this->assertSame($file, $this->dut->createImage($this->folder));
}
}

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

@ -0,0 +1,219 @@
<?php
namespace OCA\Cookbook\tests\unit\Helper\ImageService;
use OCA\Cookbook\Exception\NoRecipeImageFoundException;
use OCP\IL10N;
use OCP\Files\File;
use OCP\Files\Folder;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\MockObject\MockObject;
use OCA\Cookbook\Helper\ImageService\ImageSize;
use OCA\Cookbook\Helper\ImageService\ImageFileHelper;
use OCA\Cookbook\Helper\ImageService\ThumbnailFileHelper;
use OCA\Cookbook\Helper\ImageService\ImageGenerationHelper;
use OCP\Files\NotFoundException;
/**
* @covers OCA\Cookbook\Helper\ImageService\ThumbnailFileHelper
* @covers OCA\Cookbook\Exception\NoRecipeImageFoundException
*/
class ThumbnailFileHelperTest extends TestCase {
/**
* @var ThumbnailFileHelper
*/
private $dut;
/**
* @var MockObject|ImageGenerationHelper
*/
private $generationHelper;
/**
* @var MockObject|ImageFileHelper
*/
private $fileHelper;
/**
* @var MockObject|Folder
*/
private $folder;
protected function setUp(): void {
/**
* @var Stub|IL10N $l
*/
$l = $this->createStub(IL10N::class);
$l->method('t')->willReturnArgument(0);
$this->generationHelper = $this->createMock(ImageGenerationHelper::class);
$this->fileHelper = $this->createMock(ImageFileHelper::class);
$this->dut = new ThumbnailFileHelper($this->generationHelper, $this->fileHelper, $l);
}
public function dpExisting() {
yield [true];
yield [false];
}
public function dpFilename() {
yield [ImageSize::THUMBNAIL, 'thumb.jpg'];
yield [ImageSize::MINI_THUMBNAIL, 'thumb16.jpg'];
}
/**
* @dataProvider dpFilename
*/
public function testEnsureThumbnailExistsWithExistingThumbnail($type, $filename) {
/**
* @var MockObject|Folder $f
*/
$f = $this->createMock(Folder::class);
$f->method('nodeExists')->with($filename)->willReturn(true);
$file = $this->createStub(File::class);
$f->method('get')->with($filename)->willReturn($file);
$this->assertSame($file, $this->dut->ensureThumbnailExists($f, $type));
}
/**
* @dataProvider dpFilename
*/
public function testEnsureThumbnailExistsWithNonExistingThumbnail($type, $filename) {
/**
* @var MockObject|Folder $f
*/
$f = $this->createMock(Folder::class);
$f->method('nodeExists')->with($filename)->willReturn(false);
$f->method('get')->with($filename)->willThrowException(new NotFoundException());
$file = $this->createStub(File::class);
$f->method('newFile')->with($filename)->willReturn($file);
$this->fileHelper->method('hasImage')->willReturn(true);
$full = $this->createStub(File::class);
$this->fileHelper->method('getImage')->willReturn($full);
$this->generationHelper->expects($this->once())->method('generateThumbnail')
->with($full, $type, $file);
$this->assertSame($file, $this->dut->ensureThumbnailExists($f, $type));
}
/**
* @dataProvider dpFilename
*/
public function testEnsureThumbnailExistsWithNonExistingMainImage($type, $filename) {
/**
* @var MockObject|Folder $f
*/
$f = $this->createMock(Folder::class);
$f->method('nodeExists')->with($filename)->willReturn(false);
$f->method('get')->with($filename)->willThrowException(new NotFoundException());
$this->fileHelper->method('hasImage')->willReturn(false);
$this->fileHelper->method('getImage')->willThrowException(new NotFoundException());
$this->generationHelper->expects($this->never())->method('generateThumbnail');
$this->expectException(NoRecipeImageFoundException::class);
$this->dut->ensureThumbnailExists($f, $type);
}
public function dpDrop() {
return [
[false, false],
[false, true],
[true, false],
[true, true],
];
}
/**
* @dataProvider dpDrop
*/
public function testDropThumbnails($thumbExists, $miniExists) {
/**
* @var MockObject|Folder $f
*/
$f = $this->createMock(Folder::class);
$existMap = [
['thumb.jpg', $thumbExists],
['thumb16.jpg', $miniExists],
];
$f->method('nodeExists')->willReturnMap($existMap);
/**
* @var MockObject|File $thumb
*/
$thumb = $this->createMock(File::class);
/**
* @var MockObject|File $mini
*/
$mini = $this->createMock(File::class);
$fileMap = [
['thumb.jpg', $thumb],
['thumb16.jpg', $mini],
];
$f->method('get')->willReturnMap($fileMap);
$thumb->expects($this->exactly($thumbExists ? 1 : 0))->method('delete');
$mini->expects($this->exactly($miniExists ? 1 : 0))->method('delete');
$this->dut->dropThumbnails($f);
}
/**
* @dataProvider dpDrop
*/
public function testRecreateThumbnails($thumbExists, $miniExists) {
/**
* @var MockObject|Folder $f
*/
$f = $this->createMock(Folder::class);
$existMap = [
['thumb.jpg', $thumbExists],
['thumb16.jpg', $miniExists],
];
$f->method('nodeExists')->willReturnMap($existMap);
/**
* @var MockObject|File $thumb
*/
$thumb = $this->createMock(File::class);
/**
* @var MockObject|File $mini
*/
$mini = $this->createMock(File::class);
$fileMap = [
['thumb.jpg', $thumb],
['thumb16.jpg', $mini],
];
$f->method('get')->willReturnMap($fileMap);
$newFileMap = [
['thumb.jpg', null, $thumb],
['thumb16.jpg', null, $mini],
];
$f->method('newFile')->willReturnMap($newFileMap);
$full = $this->createStub(File::class);
$this->fileHelper->method('hasImage')->willReturn(true);
$this->fileHelper->method('getImage')->willReturn($full);
$this->generationHelper->expects($this->exactly(2))->method('generateThumbnail')
->withConsecutive(
[$full, ImageSize::THUMBNAIL, $thumb],
[$full, ImageSize::MINI_THUMBNAIL, $mini],
);
$this->dut->recreateThumbnails($f);
}
}