skipTest = true; parent::setUp(); $this->origBackend = Image::get_backend(); if($this->skipTest) return; if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH); // Create a test folders for each of the fixture references $folderIDs = $this->allFixtureIDs('Folder'); foreach($folderIDs as $folderID) { $folder = DataObject::get_by_id('Folder', $folderID); if(!file_exists(BASE_PATH."/$folder->Filename")) mkdir(BASE_PATH."/$folder->Filename"); } // Copy test images for each of the fixture references $imageIDs = $this->allFixtureIDs('Image'); foreach($imageIDs as $imageID) { $image = DataObject::get_by_id('Image', $imageID); $filePath = BASE_PATH."/$image->Filename"; $sourcePath = str_replace('assets/ImageTest/', 'framework/tests/model/testimages/', $filePath); if(!file_exists($filePath)) { if (!copy($sourcePath, $filePath)) user_error('Failed to copy test images', E_USER_ERROR); } } } public function tearDown() { if($this->origBackend) Image::set_backend($this->origBackend); // Remove the test files that we've created $fileIDs = $this->allFixtureIDs('Image'); foreach($fileIDs as $fileID) { $file = DataObject::get_by_id('Image', $fileID); if($file && file_exists(BASE_PATH."/$file->Filename")) unlink(BASE_PATH."/$file->Filename"); } // Remove the test folders that we've created $folderIDs = $this->allFixtureIDs('Folder'); foreach($folderIDs as $folderID) { $folder = DataObject::get_by_id('Folder', $folderID); if($folder && file_exists(BASE_PATH."/$folder->Filename")) { Filesystem::removeFolder(BASE_PATH."/$folder->Filename"); } if($folder && file_exists(BASE_PATH."/".$folder->Filename."_resampled")) { Filesystem::removeFolder(BASE_PATH."/".$folder->Filename."_resampled"); } } parent::tearDown(); } public function testGetTagWithTitle() { Config::inst()->update('Image', 'force_resample', false); $image = $this->objFromFixture('Image', 'imageWithTitle'); $expected = 'This is a image Title'; $actual = $image->getTag(); $this->assertEquals($expected, $actual); } public function testGetTagWithoutTitle() { Config::inst()->update('Image', 'force_resample', false); $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $expected = 'test_image'; $actual = $image->getTag(); $this->assertEquals($expected, $actual); } public function testGetTagWithoutTitleContainingDots() { Config::inst()->update('Image', 'force_resample', false); $image = $this->objFromFixture('Image', 'imageWithoutTitleContainingDots'); $expected = 'test.image.with.dots'; $actual = $image->getTag(); $this->assertEquals($expected, $actual); } /** * Tests that multiple image manipulations may be performed on a single Image */ public function testMultipleGenerateManipulationCalls() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $imageFirst = $image->ScaleWidth(200); $this->assertNotNull($imageFirst); $expected = 200; $actual = $imageFirst->getWidth(); $this->assertEquals($expected, $actual); $imageSecond = $imageFirst->setHeight(100); $this->assertNotNull($imageSecond); $expected = 100; $actual = $imageSecond->getHeight(); $this->assertEquals($expected, $actual); } /** * Tests that image manipulations that do not affect the resulting dimensions * of the output image do not resample the file. */ public function testReluctanceToResampling() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $this->assertTrue($image->isSize(300, 300)); // Set width to 300 pixels $imageScaleWidth = $image->ScaleWidth(300); $this->assertEquals($imageScaleWidth->getWidth(), 300); $this->assertEquals($image->Filename, $imageScaleWidth->Filename); // Set height to 300 pixels $imageScaleHeight = $image->ScaleHeight(300); $this->assertEquals($imageScaleHeight->getHeight(), 300); $this->assertEquals($image->Filename, $imageScaleHeight->Filename); // Crop image to 300 x 300 $imageCropped = $image->Fill(300, 300); $this->assertTrue($imageCropped->isSize(300, 300)); $this->assertEquals($image->Filename, $imageCropped->Filename); // Resize (padded) to 300 x 300 $imageSized = $image->Pad(300, 300); $this->assertTrue($imageSized->isSize(300, 300)); $this->assertEquals($image->Filename, $imageSized->Filename); // Padded image 300 x 300 (same as above) $imagePadded = $image->Pad(300, 300); $this->assertTrue($imagePadded->isSize(300, 300)); $this->assertEquals($image->Filename, $imagePadded->Filename); // Resized (stretched) to 300 x 300 $imageStretched = $image->ResizedImage(300, 300); $this->assertTrue($imageStretched->isSize(300, 300)); $this->assertEquals($image->Filename, $imageStretched->Filename); // Fit (various options) $imageFit = $image->Fit(300, 600); $this->assertTrue($imageFit->isSize(300, 300)); $this->assertEquals($image->Filename, $imageFit->Filename); $imageFit = $image->Fit(600, 300); $this->assertTrue($imageFit->isSize(300, 300)); $this->assertEquals($image->Filename, $imageFit->Filename); $imageFit = $image->Fit(300, 300); $this->assertTrue($imageFit->isSize(300, 300)); $this->assertEquals($image->Filename, $imageFit->Filename); } /** * Tests that a URL to a resampled image is provided when force_resample is * set to true, if the resampled file is smaller than the original. */ public function testForceResample() { $imageHQ = $this->objFromFixture('Image', 'highQualityJPEG'); $imageHQR = $imageHQ->Resampled(); $imageLQ = $this->objFromFixture('Image', 'lowQualityJPEG'); $imageLQR = $imageLQ->Resampled(); // Test resampled file is served when force_resample = true Config::inst()->update('Image', 'force_resample', true); $this->assertLessThan($imageHQ->getAbsoluteSize(), $imageHQR->getAbsoluteSize(), 'Resampled image is smaller than original'); $this->assertEquals($imageHQ->getURL(), $imageHQR->getSourceURL(), 'Path to a resampled image was returned by getURL()'); // Test original file is served when force_resample = true but original file is low quality $this->assertGreaterThanOrEqual($imageLQ->getAbsoluteSize(), $imageLQR->getAbsoluteSize(), 'Resampled image is larger or same size as original'); $this->assertNotEquals($imageLQ->getURL(), $imageLQR->getSourceURL(), 'Path to the original image file was returned by getURL()'); // Test original file is served when force_resample = false Config::inst()->update('Image', 'force_resample', false); $this->assertNotEquals($imageHQ->getURL(), $imageHQR->getSourceURL(), 'Path to the original image file was returned by getURL()'); } public function testImageResize() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $this->assertTrue($image->isSize(300, 300)); // Test normal resize $resized = $image->Pad(150, 100); $this->assertTrue($resized->isSize(150, 100)); // Test cropped resize $cropped = $image->Fill(100, 200); $this->assertTrue($cropped->isSize(100, 200)); // Test padded resize $padded = $image->Pad(200, 100); $this->assertTrue($padded->isSize(200, 100)); // Test Fit $ratio = $image->Fit(80, 160); $this->assertTrue($ratio->isSize(80, 80)); // Test FitMax $fitMaxDn = $image->FitMax(200, 100); $this->assertTrue($fitMaxDn->isSize(100, 100)); $fitMaxUp = $image->FitMax(500, 400); $this->assertTrue($fitMaxUp->isSize(300, 300)); //Test ScaleMax $scaleMaxWDn = $image->ScaleMaxWidth(200); $this->assertTrue($scaleMaxWDn->isSize(200, 200)); $scaleMaxWUp = $image->ScaleMaxWidth(400); $this->assertTrue($scaleMaxWUp->isSize(300, 300)); $scaleMaxHDn = $image->ScaleMaxHeight(200); $this->assertTrue($scaleMaxHDn->isSize(200, 200)); $scaleMaxHUp = $image->ScaleMaxHeight(400); $this->assertTrue($scaleMaxHUp->isSize(300, 300)); // Test FillMax $cropMaxDn = $image->FillMax(200, 100); $this->assertTrue($cropMaxDn->isSize(200, 100)); $cropMaxUp = $image->FillMax(400, 200); $this->assertTrue($cropMaxUp->isSize(300, 150)); // Test Clip $clipWDn = $image->CropWidth(200); $this->assertTrue($clipWDn->isSize(200, 300)); $clipWUp = $image->CropWidth(400); $this->assertTrue($clipWUp->isSize(300, 300)); $clipHDn = $image->CropHeight(200); $this->assertTrue($clipHDn->isSize(300, 200)); $clipHUp = $image->CropHeight(400); $this->assertTrue($clipHUp->isSize(300, 300)); } /** * @expectedException InvalidArgumentException */ public function testGenerateImageWithInvalidParameters() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $image->setHeight('String'); $image->Pad(600,600,'XXXXXX'); } public function testCacheFilename() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $imageFirst = $image->Pad(200,200,'CCCCCC'); $imageFilename = $imageFirst->getFullPath(); // Encoding of the arguments is duplicated from cacheFilename $neededPart = 'Pad' . Convert::base64url_encode(array(200,200,'CCCCCC')); $this->assertContains($neededPart, $imageFilename, 'Filename for cached image is correctly generated'); } public function testMultipleGenerateManipulationCalls_Regeneration() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $folder = new SS_FileFinder(); $imageFirst = $image->Pad(200,200); $this->assertNotNull($imageFirst); $expected = 200; $actual = $imageFirst->getWidth(); $this->assertEquals($expected, $actual); $imageSecond = $imageFirst->setHeight(100); $this->assertNotNull($imageSecond); $expected = 100; $actual = $imageSecond->getHeight(); $this->assertEquals($expected, $actual); $imageThird = $imageSecond->Pad(600,600,'0F0F0F'); // Encoding of the arguments is duplicated from cacheFilename $argumentString = Convert::base64url_encode(array(600,600,'0F0F0F')); $this->assertNotNull($imageThird); $this->assertContains($argumentString, $imageThird->getFullPath(), 'Image contains background color for padded resizement'); $resampledFolder = dirname($image->getFullPath()) . "/_resampled"; $filesInFolder = $folder->find($resampledFolder); $this->assertEquals(3, count($filesInFolder), 'Image folder contains only the expected number of images before regeneration'); $imageThirdPath = $imageThird->getFullPath(); $hash = md5_file($imageThirdPath); $this->assertEquals(3, $image->regenerateFormattedImages(), 'Cached images were regenerated in the right number'); $this->assertEquals($hash, md5_file($imageThirdPath), 'Regeneration of third image is correct'); /* Check that no other images exist, to ensure that the regeneration did not create other images */ $this->assertEquals($filesInFolder, $folder->find($resampledFolder), 'Image folder contains only the expected image files after regeneration'); } public function testRegenerateImages() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $image_generated = $image->ScaleWidth(200); $p = $image_generated->getFullPath(); $this->assertTrue(file_exists($p), 'Resized image exists after creation call'); $this->assertEquals(1, $image->regenerateFormattedImages(), 'Cached images were regenerated correct'); $this->assertEquals($image_generated->getWidth(), 200, 'Resized image has correct width after regeneration call'); $this->assertTrue(file_exists($p), 'Resized image exists after regeneration call'); } /** * Test that propertes from the source Image are inherited by resampled images */ public function testPropertyInheritance() { $testString = 'This is a test'; $origImage = $this->objFromFixture('Image', 'imageWithTitle'); $origImage->TestProperty = $testString; $resampled = $origImage->ScaleWidth(10); $this->assertEquals($resampled->TestProperty, $testString); $resampled2 = $resampled->ScaleWidth(5); $this->assertEquals($resampled2->TestProperty, $testString); } /** * Tests that cached images are regenerated properly after a cached file is renamed with new arguments * ToDo: This doesn't seem like something that is worth testing - what is the point of this? */ public function testRegenerateImagesWithRenaming() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $image_generated = $image->ScaleWidth(200); $p = $image_generated->getFullPath(); $this->assertTrue(file_exists($p), 'Resized image exists after creation call'); // Encoding of the arguments is duplicated from cacheFilename $oldArgumentString = Convert::base64url_encode(array(200)); $newArgumentString = Convert::base64url_encode(array(300)); $newPath = str_replace($oldArgumentString, $newArgumentString, $p); if(!file_exists(dirname($newPath))) mkdir(dirname($newPath)); $newRelative = str_replace($oldArgumentString, $newArgumentString, $image_generated->getFileName()); rename($p, $newPath); $this->assertFalse(file_exists($p), 'Resized image does not exist at old path after renaming'); $this->assertTrue(file_exists($newPath), 'Resized image exists at new path after renaming'); $this->assertEquals(1, $image->regenerateFormattedImages(), 'Cached images were regenerated in the right number'); $image_generated_2 = new Image_Cached($newRelative); $this->assertEquals(300, $image_generated_2->getWidth(), 'Cached image was regenerated with correct width'); } public function testGeneratedImageDeletion() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $image_generated = $image->ScaleWidth(200); $p = $image_generated->getFullPath(); $this->assertTrue(file_exists($p), 'Resized image exists after creation call'); $numDeleted = $image->deleteFormattedImages(); $this->assertEquals(1, $numDeleted, 'Expected one image to be deleted, but deleted ' . $numDeleted . ' images'); $this->assertFalse(file_exists($p), 'Resized image not existing after deletion call'); } /** * Tests that generated images with multiple image manipulations are all deleted */ public function testMultipleGenerateManipulationCallsImageDeletion() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $firstImage = $image->ScaleWidth(200); $firstImagePath = $firstImage->getFullPath(); $this->assertTrue(file_exists($firstImagePath)); $secondImage = $firstImage->ScaleHeight(100); $secondImagePath = $secondImage->getFullPath(); $this->assertTrue(file_exists($secondImagePath)); $image->deleteFormattedImages(); $this->assertFalse(file_exists($firstImagePath)); $this->assertFalse(file_exists($secondImagePath)); } /** * Tests path properties of cached images with multiple image manipulations */ public function testPathPropertiesCachedImage() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $firstImage = $image->ScaleWidth(200); $firstImagePath = $firstImage->getRelativePath(); $this->assertEquals($firstImagePath, $firstImage->Filename); $secondImage = $firstImage->ScaleHeight(100); $secondImagePath = $secondImage->getRelativePath(); $this->assertEquals($secondImagePath, $secondImage->Filename); } /** * Tests the static function Image::strip_resampled_prefix, to ensure that * the original filename can be extracted from the path of transformed images, * both in current and previous formats */ public function testStripResampledPrefix() { $orig_image = $this->objFromFixture('Image', 'imageWithoutTitleContainingDots'); // current format (3.3+). Example: // assets/ImageTest/_resampled/ScaleHeightWzIwMF0=/ScaleWidthWzQwMF0=/test.image.with.dots.png; $firstImage = $orig_image->ScaleWidth(200); $secondImage = $firstImage->ScaleHeight(200); $paths_1 = $firstImage->Filename; $paths_2 = $secondImage->Filename; // 3.2 format (did not work for multiple transformations) $paths_3 = 'assets/ImageTest/_resampled/ScaleHeightWzIwMF0=-test.image.with.dots.png'; // 3.1 (and earlier) format (did not work for multiple transformations) $paths_4 = 'assets/ImageTest/_resampled/ScaleHeight200-test.image.with.dots.png'; $this->assertEquals($orig_image->Filename, Image::strip_resampled_prefix($paths_1)); $this->assertEquals($orig_image->Filename, Image::strip_resampled_prefix($paths_2)); $this->assertEquals($orig_image->Filename, Image::strip_resampled_prefix($paths_3)); $this->assertEquals($orig_image->Filename, Image::strip_resampled_prefix($paths_4)); } /** * Test all generate methods */ public function testGenerateMethods() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $generateMethods = $this->getGenerateMethods(); // test each generate method foreach ($generateMethods as $method) { $generatedImage = $image->$method(333, 333, 'FFFFFF'); $this->assertFileExists( $generatedImage->getFullPath(), 'Formatted ' . $method . ' image exists' ); } } /** * Test deleteFormattedImages() against all generate methods */ public function testDeleteFormattedImages() { $image = $this->objFromFixture('Image', 'imageWithoutTitle'); $generateMethods = $this->getGenerateMethods(); // get paths for each generate method $paths = array(); foreach ($generateMethods as $method) { $generatedImage = $image->$method(333, 333, 'FFFFFF'); $paths[$method] = $generatedImage->getFullPath(); } // delete formatted images $image->deleteFormattedImages(); // test that all formatted images are deleted foreach ($paths as $method => $path) { $this->assertFalse( file_exists($path), 'Formatted ' . $method . ' image does not exist' ); } } /** * @param bool $custom include methods added dynamically at runtime * @return array */ protected function getGenerateMethods($custom = true) { $generateMethods = array(); $methodNames = Image::create()->allMethodNames($custom); foreach ($methodNames as $methodName) { if (substr($methodName, 0, 8) == 'generate' && $methodName != 'generateformattedimage') { $format = substr($methodName, 8); $generateMethods[] = $format; } } return $generateMethods; } }