From 83af7e89f9c05896c5f2756eb4d71b364b721ef6 Mon Sep 17 00:00:00 2001 From: Christian Wolf Date: Thu, 24 Mar 2022 19:50:17 +0100 Subject: [PATCH] Created helper classes for management of image files Added tests for helper classes Signed-off-by: Christian Wolf --- lib/Exception/NoRecipeImageFoundException.php | 9 + lib/Exception/RecipeImageExistsException.php | 9 + lib/Helper/ImageService/ImageFileHelper.php | 75 ++++++ .../ImageService/ThumbnailFileHelper.php | 129 +++++++++++ .../ImageService/ImageFileHelperTest.php | 98 ++++++++ .../ImageService/ThumbnailFileHelperTest.php | 219 ++++++++++++++++++ 6 files changed, 539 insertions(+) create mode 100644 lib/Exception/NoRecipeImageFoundException.php create mode 100644 lib/Exception/RecipeImageExistsException.php create mode 100644 lib/Helper/ImageService/ImageFileHelper.php create mode 100644 lib/Helper/ImageService/ThumbnailFileHelper.php create mode 100644 tests/Unit/Helper/ImageService/ImageFileHelperTest.php create mode 100644 tests/Unit/Helper/ImageService/ThumbnailFileHelperTest.php diff --git a/lib/Exception/NoRecipeImageFoundException.php b/lib/Exception/NoRecipeImageFoundException.php new file mode 100644 index 00000000..ee209c2a --- /dev/null +++ b/lib/Exception/NoRecipeImageFoundException.php @@ -0,0 +1,9 @@ +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); + } + } +} diff --git a/lib/Helper/ImageService/ThumbnailFileHelper.php b/lib/Helper/ImageService/ThumbnailFileHelper.php new file mode 100644 index 00000000..b49aebc5 --- /dev/null +++ b/lib/Helper/ImageService/ThumbnailFileHelper.php @@ -0,0 +1,129 @@ +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); + } +} diff --git a/tests/Unit/Helper/ImageService/ImageFileHelperTest.php b/tests/Unit/Helper/ImageService/ImageFileHelperTest.php new file mode 100644 index 00000000..a5d51712 --- /dev/null +++ b/tests/Unit/Helper/ImageService/ImageFileHelperTest.php @@ -0,0 +1,98 @@ +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)); + } +} diff --git a/tests/Unit/Helper/ImageService/ThumbnailFileHelperTest.php b/tests/Unit/Helper/ImageService/ThumbnailFileHelperTest.php new file mode 100644 index 00000000..ca561d99 --- /dev/null +++ b/tests/Unit/Helper/ImageService/ThumbnailFileHelperTest.php @@ -0,0 +1,219 @@ +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); + } +}