From 492d0b9f9bd72591183ff8bf4e8d5f9b4aecba35 Mon Sep 17 00:00:00 2001 From: Julius Haertl Date: Sun, 18 Sep 2016 20:15:06 +0200 Subject: [PATCH] Caching for icon files using AppData Signed-off-by: Julius Haertl --- .../theming/lib/Controller/IconController.php | 53 +++-- apps/theming/lib/IconBuilder.php | 18 +- apps/theming/lib/ImageManager.php | 118 +++++++++++ .../tests/Controller/IconControllerTest.php | 133 ++++++------- apps/theming/tests/IconBuilderTest.php | 19 +- apps/theming/tests/ImageManagerTest.php | 185 ++++++++++++++++++ 6 files changed, 433 insertions(+), 93 deletions(-) create mode 100644 apps/theming/lib/ImageManager.php create mode 100644 apps/theming/tests/ImageManagerTest.php diff --git a/apps/theming/lib/Controller/IconController.php b/apps/theming/lib/Controller/IconController.php index f2355fe3f82..eb65ae54f1a 100644 --- a/apps/theming/lib/Controller/IconController.php +++ b/apps/theming/lib/Controller/IconController.php @@ -23,13 +23,16 @@ namespace OCA\Theming\Controller; use OCA\Theming\IconBuilder; +use OCA\Theming\ImageManager; use OCA\Theming\ThemingDefaults; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataDisplayResponse; +use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IRequest; use OCA\Theming\Util; +use OCP\IConfig; class IconController extends Controller { /** @var ThemingDefaults */ @@ -38,8 +41,12 @@ class IconController extends Controller { private $util; /** @var ITimeFactory */ private $timeFactory; + /** @var IConfig */ + private $config; /** @var IconBuilder */ private $iconBuilder; + /** @var ImageManager */ + private $imageManager; /** * IconController constructor. @@ -49,7 +56,9 @@ class IconController extends Controller { * @param ThemingDefaults $themingDefaults * @param Util $util * @param ITimeFactory $timeFactory + * @param IConfig $config * @param IconBuilder $iconBuilder + * @param ImageManager $imageManager */ public function __construct( $appName, @@ -57,14 +66,18 @@ class IconController extends Controller { ThemingDefaults $themingDefaults, Util $util, ITimeFactory $timeFactory, - IconBuilder $iconBuilder + IConfig $config, + IconBuilder $iconBuilder, + ImageManager $imageManager ) { parent::__construct($appName, $request); $this->themingDefaults = $themingDefaults; $this->util = $util; $this->timeFactory = $timeFactory; + $this->config = $config; $this->iconBuilder = $iconBuilder; + $this->imageManager = $imageManager; } /** @@ -73,14 +86,15 @@ class IconController extends Controller { * * @param $app string app name * @param $image string image file name (svg required) - * @return DataDisplayResponse + * @return FileDisplayResponse */ public function getThemedIcon($app, $image) { - $image = $this->util->getAppImage($app, $image); - $svg = file_get_contents($image); - $color = $this->util->elementColor($this->themingDefaults->getMailHeaderColor()); - $svg = $this->util->colorizeSvg($svg, $color); - $response = new DataDisplayResponse($svg, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']); + $iconFile = $this->imageManager->getCachedImage("icon-" . $app . '-' . str_replace("/","_",$image)); + if ($iconFile === null) { + $icon = $this->iconBuilder->colorSvg($app, $image); + $iconFile = $this->imageManager->setCachedImage("icon-" . $app . '-' . str_replace("/","_",$image), $icon); + } + $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']); $response->cacheFor(86400); $response->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $response->addHeader('Pragma', 'cache'); @@ -94,12 +108,16 @@ class IconController extends Controller { * @NoCSRFRequired * * @param $app string app name - * @return DataDisplayResponse + * @return FileDisplayResponse|DataDisplayResponse */ - public function getFavicon($app="core") { - if($this->themingDefaults->shouldReplaceIcons()) { + public function getFavicon($app = "core") { + $iconFile = $this->imageManager->getCachedImage('favIcon-' . $app); + if($iconFile === null && $this->themingDefaults->shouldReplaceIcons()) { $icon = $this->iconBuilder->getFavicon($app); - $response = new DataDisplayResponse($icon, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']); + $iconFile = $this->imageManager->setCachedImage('favIcon-' . $app, $icon); + } + if ($this->themingDefaults->shouldReplaceIcons()) { + $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']); } else { $response = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND); } @@ -116,12 +134,16 @@ class IconController extends Controller { * @NoCSRFRequired * * @param $app string app name - * @return DataDisplayResponse + * @return FileDisplayResponse|DataDisplayResponse */ - public function getTouchIcon($app="core") { - if($this->themingDefaults->shouldReplaceIcons()) { + public function getTouchIcon($app = "core") { + $iconFile = $this->imageManager->getCachedImage('touchIcon-' . $app); + if ($iconFile === null && $this->themingDefaults->shouldReplaceIcons()) { $icon = $this->iconBuilder->getTouchIcon($app); - $response = new DataDisplayResponse($icon, Http::STATUS_OK, ['Content-Type' => 'image/png']); + $iconFile = $this->imageManager->setCachedImage('touchIcon-' . $app, $icon); + } + if ($this->themingDefaults->shouldReplaceIcons()) { + $response = new FileDisplayResponse($iconFile, Http::STATUS_OK, ['Content-Type' => 'image/png']); } else { $response = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND); } @@ -130,5 +152,4 @@ class IconController extends Controller { $response->addHeader('Pragma', 'cache'); return $response; } - } \ No newline at end of file diff --git a/apps/theming/lib/IconBuilder.php b/apps/theming/lib/IconBuilder.php index edd7602a2e4..10ba3cacc43 100644 --- a/apps/theming/lib/IconBuilder.php +++ b/apps/theming/lib/IconBuilder.php @@ -80,19 +80,21 @@ class IconBuilder { */ public function renderAppIcon($app) { $appIcon = $this->util->getAppIcon($app); + $appIconContent = file_get_contents($appIcon); $color = $this->themingDefaults->getMailHeaderColor(); $mime = mime_content_type($appIcon); + // generate background image with rounded corners $background = '' . '' . '' . ''; - // resize svg magic as this seems broken in Imagemagick - if($mime === "image/svg+xml") { - $svg = file_get_contents($appIcon); - + if($mime === "image/svg+xml" || substr($appIconContent, 0, 4) === "".$appIconContent; + } $tmp = new Imagick(); $tmp->readImageBlob($svg); $x = $tmp->getImageWidth(); @@ -137,4 +139,12 @@ class IconBuilder { return $finalIconFile; } + public function colorSvg($app, $image) { + $imageFile = $this->util->getAppImage($app, $image); + $svg = file_get_contents($imageFile); + $color = $this->util->elementColor($this->themingDefaults->getMailHeaderColor()); + $svg = $this->util->colorizeSvg($svg, $color); + return $svg; + } + } \ No newline at end of file diff --git a/apps/theming/lib/ImageManager.php b/apps/theming/lib/ImageManager.php new file mode 100644 index 00000000000..5e1b61e3a92 --- /dev/null +++ b/apps/theming/lib/ImageManager.php @@ -0,0 +1,118 @@ + + * + * @author Julius Härtl + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +namespace OCA\Theming; + +use OCP\IConfig; +use OCP\Files\IAppData; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; + +class ImageManager { + + /** @var IConfig */ + private $config; + /** @var IAppData */ + private $appData; + + /** + * ImageManager constructor. + * + * @param IConfig $config + * @param IAppData $appData + */ + public function __construct(IConfig $config, + IAppData $appData + ) { + $this->config = $config; + $this->appData = $appData; + } + + /** + * Get folder for current theming files + * + * @return \OCP\Files\SimpleFS\ISimpleFolder + * @throws NotPermittedException + * @throws \RuntimeException + */ + public function getCacheFolder() { + $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); + try { + $folder = $this->appData->getFolder($cacheBusterValue); + } catch (NotFoundException $e) { + $folder = $this->appData->newFolder($cacheBusterValue); + $this->cleanup(); + } + return $folder; + } + + /** + * Get a file from AppData + * + * @param string $filename + * @return null|\OCP\Files\SimpleFS\ISimpleFile + */ + public function getCachedImage($filename) { + $currentFolder = $this->getCacheFolder(); + if($currentFolder->fileExists($filename)) { + return $currentFolder->getFile($filename); + } else { + return null; + } + } + + /** + * Store a file for theming in AppData + * + * @param string $filename + * @param string $data + * @return \OCP\Files\SimpleFS\ISimpleFile + */ + public function setCachedImage($filename, $data) { + $currentFolder = $this->getCacheFolder(); + if ($currentFolder->fileExists($filename)) { + $file = $currentFolder->getFile($filename); + } else { + $file = $currentFolder->newFile($filename); + } + $file->putContent($data); + return $file; + } + + /** + * remove cached files that are not required any longer + */ + public function cleanup() { + $currentFolder = $this->getCacheFolder(); + $folders = $this->appData->getDirectoryListing(); + foreach ($folders as $folder) { + if ($folder->getName() !== $currentFolder->getName()) { + $folder->delete(); + } + } + } + + + +} diff --git a/apps/theming/tests/Controller/IconControllerTest.php b/apps/theming/tests/Controller/IconControllerTest.php index 09cb41088de..82a937c3b91 100644 --- a/apps/theming/tests/Controller/IconControllerTest.php +++ b/apps/theming/tests/Controller/IconControllerTest.php @@ -23,12 +23,18 @@ namespace OCA\Theming\Tests\Controller; +use OC\Files\SimpleFS\SimpleFile; +use OCA\Theming\ImageManager; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataDisplayResponse; +use OCP\Files\IRootFolder; +use OCP\IConfig; +use OCP\IL10N; use OCP\IRequest; use Test\TestCase; use OCA\Theming\Util; use OCA\Theming\Controller\IconController; +use OCP\AppFramework\Http\FileDisplayResponse; class IconControllerTest extends TestCase { @@ -42,8 +48,12 @@ class IconControllerTest extends TestCase { private $timeFactory; /** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ private $iconController; + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + private $config; /** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */ private $iconBuilder; + /** @var ImageManager */ + private $imageManager; public function setUp() { $this->request = $this->getMockBuilder('OCP\IRequest')->getMock(); @@ -54,9 +64,10 @@ class IconControllerTest extends TestCase { $this->timeFactory = $this->getMockBuilder('OCP\AppFramework\Utility\ITimeFactory') ->disableOriginalConstructor() ->getMock(); + $this->config = $this->getMockBuilder('OCP\IConfig')->getMock(); $this->iconBuilder = $this->getMockBuilder('OCA\Theming\IconBuilder') ->disableOriginalConstructor()->getMock(); - + $this->imageManager = $this->getMockBuilder('OCA\Theming\ImageManager')->disableOriginalConstructor()->getMock(); $this->timeFactory->expects($this->any()) ->method('getTime') ->willReturn(123); @@ -67,124 +78,114 @@ class IconControllerTest extends TestCase { $this->themingDefaults, $this->util, $this->timeFactory, - $this->iconBuilder + $this->config, + $this->iconBuilder, + $this->imageManager ); parent::setUp(); } + private function iconFileMock($filename, $data) { + $icon = $this->getMockBuilder('OCP\Files\File')->getMock(); + $icon->expects($this->any())->method('getContent')->willReturn($data); + $icon->expects($this->any())->method('getMimeType')->willReturn('image type'); + $icon->expects($this->any())->method('getEtag')->willReturn('my etag'); + $icon->method('getName')->willReturn($filename); + return new SimpleFile($icon); + } + public function testGetThemedIcon() { - $this->util->expects($this->once()) - ->method('getAppImage') - ->with('core','filetypes/folder.svg') - ->willReturn(\OC::$SERVERROOT . "/core/img/filetypes/folder.svg"); - $this->themingDefaults - ->expects($this->once()) - ->method('getMailHeaderColor') - ->willReturn('#000000'); - $this->util - ->expects($this->once()) - ->method('elementColor') - ->willReturn('#000000'); - - $svg = " - - - - - -"; - $expected = new DataDisplayResponse($svg, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']); + $file = $this->iconFileMock('icon-core-filetypes_folder.svg', 'filecontent'); + $this->imageManager->expects($this->once()) + ->method('getCachedImage') + ->with('icon-core-filetypes_folder.svg') + ->willReturn($file); + $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/svg+xml']); $expected->cacheFor(86400); $expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $expected->addHeader('Pragma', 'cache'); - @$this->assertEquals($expected, $this->iconController->getThemedIcon('core','filetypes/folder.svg')); + @$this->assertEquals($expected, $this->iconController->getThemedIcon('core', 'filetypes/folder.svg')); + + } public function testGetFaviconDefault() { - if(!extension_loaded('imagick')) { + if (!extension_loaded('imagick')) { $this->markTestSkipped('Imagemagick is required for dynamic icon generation.'); } $checkImagick = new \Imagick(); if (count($checkImagick->queryFormats('SVG')) < 1) { $this->markTestSkipped('No SVG provider present.'); } - $this->themingDefaults->expects($this->once()) + $this->themingDefaults->expects($this->any()) ->method('shouldReplaceIcons') ->willReturn(true); - $expectedIcon = new \Imagick(realpath(dirname(__FILE__)) . '/../data/favicon-original.ico'); + $this->iconBuilder->expects($this->once()) ->method('getFavicon') ->with('core') - ->willReturn($expectedIcon); - $favicon = $this->iconController->getFavicon(); + ->willReturn('filecontent'); + $file = $this->iconFileMock('filename', 'filecontent'); + $this->imageManager->expects($this->once()) + ->method('setCachedImage') + ->willReturn($file); - $expected = new DataDisplayResponse($expectedIcon, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']); + $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/x-icon']); $expected->cacheFor(86400); $expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $expected->addHeader('Pragma', 'cache'); - $this->assertEquals($expected, $favicon); + $this->assertEquals($expected, $this->iconController->getFavicon()); } + + public function testGetFaviconFail() { + $this->themingDefaults->expects($this->any()) + ->method('shouldReplaceIcons') + ->willReturn(false); + $expected = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND); + $expected->cacheFor(86400); + $expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); + $expected->addHeader('Pragma', 'cache'); + $this->assertEquals($expected, $this->iconController->getFavicon()); + } + public function testGetTouchIconDefault() { - if(!extension_loaded('imagick')) { + if (!extension_loaded('imagick')) { $this->markTestSkipped('Imagemagick is required for dynamic icon generation.'); } $checkImagick = new \Imagick(); if (count($checkImagick->queryFormats('SVG')) < 1) { $this->markTestSkipped('No SVG provider present.'); } - $this->themingDefaults->expects($this->once()) + $this->themingDefaults->expects($this->any()) ->method('shouldReplaceIcons') ->willReturn(true); - $expectedIcon = new \Imagick(realpath(dirname(__FILE__)) . '/../data/touch-original.png'); + $this->iconBuilder->expects($this->once()) ->method('getTouchIcon') ->with('core') - ->willReturn($expectedIcon); - $favicon = $this->iconController->getTouchIcon(); + ->willReturn('filecontent'); + $file = $this->iconFileMock('filename', 'filecontent'); + $this->imageManager->expects($this->once()) + ->method('setCachedImage') + ->willReturn($file); - $expected = new DataDisplayResponse($expectedIcon, Http::STATUS_OK, ['Content-Type' => 'image/png']); + $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'image/png']); $expected->cacheFor(86400); $expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $expected->addHeader('Pragma', 'cache'); - $this->assertEquals($expected, $favicon); + $this->assertEquals($expected, $this->iconController->getTouchIcon()); } - public function testGetFaviconFail() { - if(!extension_loaded('imagick')) { - $this->markTestSkipped('Imagemagick is required for dynamic icon generation.'); - } - $checkImagick = new \Imagick(); - if (count($checkImagick->queryFormats('SVG')) < 1) { - $this->markTestSkipped('No SVG provider present.'); - } - $this->themingDefaults->expects($this->once()) - ->method('shouldReplaceIcons') - ->willReturn(false); - $favicon = $this->iconController->getFavicon(); - $expected = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND); - $expected->cacheFor(86400); - $expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); - $expected->addHeader('Pragma', 'cache'); - $this->assertEquals($expected, $favicon); - } public function testGetTouchIconFail() { - if(!extension_loaded('imagick')) { - $this->markTestSkipped('Imagemagick is required for dynamic icon generation.'); - } - $checkImagick = new \Imagick(); - if (count($checkImagick->queryFormats('SVG')) < 1) { - $this->markTestSkipped('No SVG provider present.'); - } - $this->themingDefaults->expects($this->once()) + $this->themingDefaults->expects($this->any()) ->method('shouldReplaceIcons') ->willReturn(false); - $favicon = $this->iconController->getTouchIcon(); $expected = new DataDisplayResponse(null, Http::STATUS_NOT_FOUND); $expected->cacheFor(86400); $expected->addHeader('Expires', date(\DateTime::RFC2822, $this->timeFactory->getTime())); $expected->addHeader('Pragma', 'cache'); - $this->assertEquals($expected, $favicon); + $this->assertEquals($expected, $this->iconController->getTouchIcon()); } } diff --git a/apps/theming/tests/IconBuilderTest.php b/apps/theming/tests/IconBuilderTest.php index 03054367210..529518b30de 100644 --- a/apps/theming/tests/IconBuilderTest.php +++ b/apps/theming/tests/IconBuilderTest.php @@ -45,6 +45,15 @@ class IconBuilderTest extends TestCase { protected function setUp() { parent::setUp(); + $this->config = $this->getMockBuilder('\OCP\IConfig')->getMock(); + $this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder')->getMock(); + $this->themingDefaults = $this->getMockBuilder('OCA\Theming\ThemingDefaults') + ->disableOriginalConstructor()->getMock(); + $this->util = new Util($this->config, $this->rootFolder); + $this->iconBuilder = new IconBuilder($this->themingDefaults, $this->util); + } + + private function checkImagick() { if(!extension_loaded('imagick')) { $this->markTestSkipped('Imagemagick is required for dynamic icon generation.'); } @@ -52,13 +61,6 @@ class IconBuilderTest extends TestCase { if (count($checkImagick->queryFormats('SVG')) < 1) { $this->markTestSkipped('No SVG provider present.'); } - - $this->config = $this->getMockBuilder('\OCP\IConfig')->getMock(); - $this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder')->getMock(); - $this->themingDefaults = $this->getMockBuilder('OCA\Theming\ThemingDefaults') - ->disableOriginalConstructor()->getMock(); - $this->util = new Util($this->config, $this->rootFolder); - $this->iconBuilder = new IconBuilder($this->themingDefaults, $this->util); } public function dataRenderAppIcon() { @@ -78,6 +80,7 @@ class IconBuilderTest extends TestCase { * @param $file */ public function testRenderAppIcon($app, $color, $file) { + $this->checkImagick(); $this->themingDefaults->expects($this->once()) ->method('getMailHeaderColor') ->willReturn($color); @@ -102,6 +105,7 @@ class IconBuilderTest extends TestCase { * @param $file */ public function testGetTouchIcon($app, $color, $file) { + $this->checkImagick(); $this->themingDefaults->expects($this->once()) ->method('getMailHeaderColor') ->willReturn($color); @@ -127,6 +131,7 @@ class IconBuilderTest extends TestCase { * @param $file */ public function testGetFavicon($app, $color, $file) { + $this->checkImagick(); $this->themingDefaults->expects($this->once()) ->method('getMailHeaderColor') ->willReturn($color); diff --git a/apps/theming/tests/ImageManagerTest.php b/apps/theming/tests/ImageManagerTest.php new file mode 100644 index 00000000000..b52d557ab67 --- /dev/null +++ b/apps/theming/tests/ImageManagerTest.php @@ -0,0 +1,185 @@ + + * + * @author Julius Härtl + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Theming\Tests; + +use OCP\Files\SimpleFS\ISimpleFile; +use OCP\IConfig; +use Test\TestCase; +use OCP\Files\SimpleFS\ISimpleFolder; +use OCP\Files\IAppData; +use OCP\Files\NotFoundException; + +class ImageManager extends TestCase { + + /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ + protected $config; + /** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */ + protected $appData; + /** @var ImageManager */ + protected $imageManager; + + protected function setUp() { + parent::setUp(); + $this->config = $this->getMockBuilder('\OCP\IConfig')->getMock(); + $this->appData = $this->getMockBuilder('OCP\Files\IAppData')->getMock(); + $this->imageManager = new \OCA\Theming\ImageManager( + $this->config, + $this->appData + ); + } + + public function testGetCacheFolder() { + $folder = $this->createMock(ISimpleFolder::class); + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('theming', 'cachebuster', '0') + ->willReturn('0'); + $this->appData->expects($this->at(0)) + ->method('getFolder') + ->with('0') + ->willReturn($folder); + $this->assertEquals($folder, $this->imageManager->getCacheFolder()); + } + public function testGetCacheFolderCreate() { + $folder = $this->createMock(ISimpleFolder::class); + $this->config->expects($this->exactly(2)) + ->method('getAppValue') + ->with('theming', 'cachebuster', '0') + ->willReturn('0'); + $this->appData->expects($this->at(0)) + ->method('getFolder') + ->willThrowException(new NotFoundException()); + $this->appData->expects($this->at(1)) + ->method('newFolder') + ->with('0') + ->willReturn($folder); + $this->appData->expects($this->at(2)) + ->method('getFolder') + ->with('0') + ->willReturn($folder); + $this->appData->expects($this->once()) + ->method('getDirectoryListing') + ->willReturn([]); + $this->assertEquals($folder, $this->imageManager->getCacheFolder()); + } + + public function testGetCachedImage() { + $folder = $this->setupCacheFolder(); + $folder->expects($this->once()) + ->method('fileExists') + ->with('filename') + ->willReturn(true); + $folder->expects($this->once()) + ->method('getFile') + ->with('filename') + ->willReturn('filecontent'); + $expected = 'filecontent'; + $this->assertEquals($expected, $this->imageManager->getCachedImage('filename')); + } + + public function testGetCachedImageNotFound() { + $folder = $this->setupCacheFolder(); + $folder->expects($this->once()) + ->method('fileExists') + ->with('filename') + ->willReturn(false); + $expected = null; + $this->assertEquals($expected, $this->imageManager->getCachedImage('filename')); + } + + public function testSetCachedImage() { + $folder = $this->setupCacheFolder(); + $file = $this->createMock(ISimpleFile::class); + $folder->expects($this->once()) + ->method('fileExists') + ->with('filename') + ->willReturn(true); + $folder->expects($this->once()) + ->method('getFile') + ->with('filename') + ->willReturn($file); + $file->expects($this->once()) + ->method('putContent') + ->with('filecontent'); + $this->assertEquals($file, $this->imageManager->setCachedImage('filename', 'filecontent')); + } + + public function testSetCachedImageCreate() { + $folder = $this->setupCacheFolder(); + $file = $this->createMock(ISimpleFile::class); + $folder->expects($this->once()) + ->method('fileExists') + ->with('filename') + ->willReturn(false); + $folder->expects($this->once()) + ->method('newFile') + ->with('filename') + ->willReturn($file); + $file->expects($this->once()) + ->method('putContent') + ->with('filecontent'); + $this->assertEquals($file, $this->imageManager->setCachedImage('filename', 'filecontent')); + } + + private function setupCacheFolder() { + $folder = $this->createMock(ISimpleFolder::class); + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('theming', 'cachebuster', '0') + ->willReturn('0'); + $this->appData->expects($this->at(0)) + ->method('getFolder') + ->with('0') + ->willReturn($folder); + return $folder; + } + + public function testCleanup() { + $folders = [ + $this->createMock(ISimpleFolder::class), + $this->createMock(ISimpleFolder::class), + $this->createMock(ISimpleFolder::class) + ]; + foreach ($folders as $index=>$folder) { + $folder->expects($this->any()) + ->method('getName') + ->willReturn($index); + } + $folders[0]->expects($this->once())->method('delete'); + $folders[1]->expects($this->once())->method('delete'); + $folders[2]->expects($this->never())->method('delete'); + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('theming','cachebuster','0') + ->willReturn('2'); + $this->appData->expects($this->once()) + ->method('getDirectoryListing') + ->willReturn($folders); + $this->appData->expects($this->once()) + ->method('getFolder') + ->with('2') + ->willReturn($folders[2]); + $this->imageManager->cleanup(); + } + +} -- 2.39.5