зеркало из https://github.com/nextcloud/cookbook.git
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:
Родитель
247b166e33
Коммит
83af7e89f9
|
@ -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);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче