diff --git a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php index 3854f3ef8f1d49ec9a95a9a13480d56b2266996c..8db24885962a23e43d62d3dd497139b40d314d69 100644 --- a/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php +++ b/core/tests/Drupal/KernelTests/Core/Image/ToolkitGdTest.php @@ -4,54 +4,46 @@ use Drupal\Core\File\FileSystemInterface; use Drupal\Core\Image\ImageInterface; -use Drupal\Component\Render\FormattableMarkup; -use Drupal\Core\Site\Settings; use Drupal\KernelTests\KernelTestBase; /** - * Tests that core image manipulations work properly: scale, resize, rotate, - * crop, scale and crop, and desaturate. + * Tests for the GD image toolkit. * + * @coversDefaultClass \Drupal\system\Plugin\ImageToolkit\GDToolkit * @group Image * @requires extension gd */ class ToolkitGdTest extends KernelTestBase { /** - * The image factory service. - * - * @var \Drupal\Core\Image\ImageFactory + * Colors that are used in testing. */ - protected $imageFactory; + protected const BLACK = [0, 0, 0, 0]; + protected const RED = [255, 0, 0, 0]; + protected const GREEN = [0, 255, 0, 0]; + protected const BLUE = [0, 0, 255, 0]; + protected const YELLOW = [255, 255, 0, 0]; + protected const WHITE = [255, 255, 255, 0]; + protected const TRANSPARENT = [0, 0, 0, 127]; + protected const FUCHSIA = [255, 0, 255, 0]; + protected const ROTATE_TRANSPARENT = [255, 255, 255, 127]; /** - * Colors that are used in testing. + * The image factory service. * - * @var array + * @var \Drupal\Core\Image\ImageFactory */ - protected $black = [0, 0, 0, 0]; - protected $red = [255, 0, 0, 0]; - protected $green = [0, 255, 0, 0]; - protected $blue = [0, 0, 255, 0]; - protected $yellow = [255, 255, 0, 0]; - protected $white = [255, 255, 255, 0]; - protected $transparent = [0, 0, 0, 127]; + protected $imageFactory; /** - * Used as rotate background colors. + * A directory where test image files can be saved to. * - * @var array + * @var string */ - protected $fuchsia = [255, 0, 255, 0]; - protected $rotateTransparent = [255, 255, 255, 127]; - - protected $width = 40; - protected $height = 20; + protected $directory; /** - * Modules to enable. - * - * @var array + * {@inheritdoc} */ protected static $modules = ['system']; @@ -62,32 +54,41 @@ protected function setUp(): void { parent::setUp(); $this->installConfig(['system']); + // Set the image factory service. $this->imageFactory = $this->container->get('image.factory'); + $this->assertEquals('gd', $this->imageFactory->getToolkitId(), 'The image factory is set to use the \'gd\' image toolkit.'); + + // Prepare a directory for test file results. + $this->directory = 'public://imagetest'; + \Drupal::service('file_system')->prepareDirectory($this->directory, FileSystemInterface::CREATE_DIRECTORY); } /** - * Function to compare two colors by RGBa. + * Assert two colors are equal by RGBA, net of full transparency. + * + * @param int[] $expected + * The expected RGBA array. + * @param int[] $actual + * The actual RGBA array. + * @param int $tolerance + * The acceptable difference between the colors. + * @param string $message + * The assertion message. */ - public function colorsAreEqual($color_a, $color_b) { - // Fully transparent pixels are equal, regardless of RGB. - if ($color_a[3] == 127 && $color_b[3] == 127) { - return TRUE; + protected function assertColorsAreEqual(array $expected, array $actual, int $tolerance, string $message = ''): void { + // Fully transparent colors are equal, regardless of RGB. + if ($actual[3] == 127 && $expected[3] == 127) { + return; } - - foreach ($color_a as $key => $value) { - if ($color_b[$key] != $value) { - return FALSE; - } - } - - return TRUE; + $distance = pow(($actual[0] - $expected[0]), 2) + pow(($actual[1] - $expected[1]), 2) + pow(($actual[2] - $expected[2]), 2) + pow(($actual[3] - $expected[3]), 2); + $this->assertLessThanOrEqual($tolerance, $distance, $message . " - Actual: {" . implode(',', $actual) . "}, Expected: {" . implode(',', $expected) . "}, Distance: " . $distance . ", Tolerance: " . $tolerance); } /** * Function for finding a pixel's RGBa values. */ - public function getPixelColor(ImageInterface $image, $x, $y) { + public function getPixelColor(ImageInterface $image, int $x, int $y): array { $toolkit = $image->getToolkit(); $color_index = imagecolorat($toolkit->getResource(), $x, $y); @@ -100,123 +101,87 @@ public function getPixelColor(ImageInterface $image, $x, $y) { } /** - * Since PHP can't visually check that our images have been manipulated - * properly, build a list of expected color values for each of the corners and - * the expected height and widths for the final images. + * Data provider for ::testManipulations(). */ - public function testManipulations() { - - // Test that the image factory is set to use the GD toolkit. - $this->assertEquals('gd', $this->imageFactory->getToolkitId(), 'The image factory is set to use the \'gd\' image toolkit.'); - - // Test the list of supported extensions. - $expected_extensions = ['png', 'gif', 'jpeg', 'jpg', 'jpe', 'webp']; - $supported_extensions = $this->imageFactory->getSupportedExtensions(); - $this->assertEquals($expected_extensions, array_intersect($expected_extensions, $supported_extensions)); - - // Test that the supported extensions map to correct internal GD image - // types. - $expected_image_types = [ - 'png' => IMAGETYPE_PNG, - 'gif' => IMAGETYPE_GIF, - 'jpeg' => IMAGETYPE_JPEG, - 'jpg' => IMAGETYPE_JPEG, - 'jpe' => IMAGETYPE_JPEG, - 'webp' => IMAGETYPE_WEBP, - ]; - $image = $this->imageFactory->get(); - foreach ($expected_image_types as $extension => $expected_image_type) { - $image_type = $image->getToolkit()->extensionToImageType($extension); - $this->assertSame($expected_image_type, $image_type); - } - + public function providerTestImageFiles(): array { // Typically the corner colors will be unchanged. These colors are in the // order of top-left, top-right, bottom-right, bottom-left. - $default_corners = [$this->red, $this->green, $this->blue, $this->transparent]; - - // A list of files that will be tested. - $files = [ - 'image-test.png', - 'image-test.gif', - 'image-test-no-transparency.gif', - 'image-test.jpg', - 'img-test.webp', - ]; + $default_corners = [static::RED, static::GREEN, static::BLUE, static::TRANSPARENT]; // Setup a list of tests to perform on each type. - $operations = [ + $test_cases = [ 'resize' => [ - 'function' => 'resize', + 'operation' => 'resize', 'arguments' => ['width' => 20, 'height' => 10], 'width' => 20, 'height' => 10, 'corners' => $default_corners, ], 'scale_x' => [ - 'function' => 'scale', + 'operation' => 'scale', 'arguments' => ['width' => 20], 'width' => 20, 'height' => 10, 'corners' => $default_corners, ], 'scale_y' => [ - 'function' => 'scale', + 'operation' => 'scale', 'arguments' => ['height' => 10], 'width' => 20, 'height' => 10, 'corners' => $default_corners, ], 'upscale_x' => [ - 'function' => 'scale', + 'operation' => 'scale', 'arguments' => ['width' => 80, 'upscale' => TRUE], 'width' => 80, 'height' => 40, 'corners' => $default_corners, ], 'upscale_y' => [ - 'function' => 'scale', + 'operation' => 'scale', 'arguments' => ['height' => 40, 'upscale' => TRUE], 'width' => 80, 'height' => 40, 'corners' => $default_corners, ], 'crop' => [ - 'function' => 'crop', + 'operation' => 'crop', 'arguments' => ['x' => 12, 'y' => 4, 'width' => 16, 'height' => 12], 'width' => 16, 'height' => 12, - 'corners' => array_fill(0, 4, $this->white), + 'corners' => array_fill(0, 4, static::WHITE), ], 'scale_and_crop' => [ - 'function' => 'scale_and_crop', + 'operation' => 'scale_and_crop', 'arguments' => ['width' => 10, 'height' => 8], 'width' => 10, 'height' => 8, - 'corners' => array_fill(0, 4, $this->black), + 'corners' => array_fill(0, 4, static::BLACK), ], 'convert_jpg' => [ - 'function' => 'convert', + 'operation' => 'convert', 'width' => 40, 'height' => 20, 'arguments' => ['extension' => 'jpeg'], 'corners' => $default_corners, ], 'convert_gif' => [ - 'function' => 'convert', + 'operation' => 'convert', 'width' => 40, 'height' => 20, 'arguments' => ['extension' => 'gif'], 'corners' => $default_corners, ], 'convert_png' => [ - 'function' => 'convert', + 'operation' => 'convert', 'width' => 40, 'height' => 20, 'arguments' => ['extension' => 'png'], 'corners' => $default_corners, ], 'convert_webp' => [ - 'function' => 'convert', + 'operation' => 'convert', 'width' => 40, 'height' => 20, 'arguments' => ['extension' => 'webp'], @@ -224,49 +189,51 @@ public function testManipulations() { ], ]; - // Systems using non-bundled GD2 don't have imagerotate. Test if available. - // @todo Remove the version check once - // https://www.drupal.org/project/drupal/issues/2670966 is resolved. - if (function_exists('imagerotate') && (version_compare(phpversion(), '7.0.26') < 0)) { - $operations += [ + // Systems using non-bundled GD2 may miss imagerotate(). Test if available. + if (function_exists('imagerotate')) { + $test_cases += [ 'rotate_5' => [ - 'function' => 'rotate', + 'operation' => 'rotate', // Fuchsia background. 'arguments' => ['degrees' => 5, 'background' => '#FF00FF'], - 'width' => 41, - 'height' => 23, - 'corners' => array_fill(0, 4, $this->fuchsia), + // @todo Re-enable dimensions' check once + // https://www.drupal.org/project/drupal/issues/2921123 is resolved. + // 'width' => 41, + // 'height' => 23, + 'corners' => array_fill(0, 4, static::FUCHSIA), + ], + 'rotate_transparent_5' => [ + 'operation' => 'rotate', + 'arguments' => ['degrees' => 5], + // @todo Re-enable dimensions' check once + // https://www.drupal.org/project/drupal/issues/2921123 is resolved. + // 'width' => 41, + // 'height' => 23, + 'corners' => array_fill(0, 4, static::ROTATE_TRANSPARENT), ], 'rotate_90' => [ - 'function' => 'rotate', + 'operation' => 'rotate', // Fuchsia background. 'arguments' => ['degrees' => 90, 'background' => '#FF00FF'], 'width' => 20, 'height' => 40, - 'corners' => [$this->transparent, $this->red, $this->green, $this->blue], - ], - 'rotate_transparent_5' => [ - 'function' => 'rotate', - 'arguments' => ['degrees' => 5], - 'width' => 41, - 'height' => 23, - 'corners' => array_fill(0, 4, $this->rotateTransparent), + 'corners' => [static::TRANSPARENT, static::RED, static::GREEN, static::BLUE], ], 'rotate_transparent_90' => [ - 'function' => 'rotate', + 'operation' => 'rotate', 'arguments' => ['degrees' => 90], 'width' => 20, 'height' => 40, - 'corners' => [$this->transparent, $this->red, $this->green, $this->blue], + 'corners' => [static::TRANSPARENT, static::RED, static::GREEN, static::BLUE], ], ]; } - // Systems using non-bundled GD2 don't have imagefilter. Test if available. + // Systems using non-bundled GD2 may miss imagefilter(). Test if available. if (function_exists('imagefilter')) { - $operations += [ + $test_cases += [ 'desaturate' => [ - 'function' => 'desaturate', + 'operation' => 'desaturate', 'arguments' => [], 'height' => 20, 'width' => 40, @@ -283,148 +250,202 @@ public function testManipulations() { ]; } - // Prepare a directory for test file results. - $directory = Settings::get('file_public_path') . '/imagetest'; - \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY); - - foreach ($files as $file) { - foreach ($operations as $op => $values) { - // Load up a fresh image. - $image = $this->imageFactory->get('core/tests/fixtures/files/' . $file); - $toolkit = $image->getToolkit(); - if (!$image->isValid()) { - $this->fail(new FormattableMarkup('Could not load image %file.', ['%file' => $file])); - continue 2; - } - $image_original_type = $image->getToolkit()->getType(); + $ret = []; + foreach ([ + 'image-test.png', + 'image-test.gif', + 'image-test-no-transparency.gif', + 'image-test.jpg', + 'img-test.webp', + ] as $file_name) { + foreach ($test_cases as $test_case => $values) { + $operation = $values['operation']; + $arguments = $values['arguments']; + unset($values['operation'], $values['arguments']); + $ret[] = [$file_name, $test_case, $operation, $arguments, $values]; + } + } - // All images should be converted to truecolor when loaded. - $image_truecolor = imageistruecolor($toolkit->getResource()); - $this->assertTrue($image_truecolor, new FormattableMarkup('Image %file after load is a truecolor image.', ['%file' => $file])); + return $ret; + } - // Store the original GD resource. - $old_res = $toolkit->getResource(); + /** + * Since PHP can't visually check that our images have been manipulated + * properly, build a list of expected color values for each of the corners and + * the expected height and widths for the final images. + * + * @dataProvider providerTestImageFiles + */ + public function testManipulations(string $file_name, string $test_case, string $operation, array $arguments, array $expected): void { + // Load up a fresh image. + $image = $this->imageFactory->get('core/tests/fixtures/files/' . $file_name); + $toolkit = $image->getToolkit(); + $this->assertTrue($image->isValid()); + $image_original_type = $image->getToolkit()->getType(); - // Perform our operation. - $image->apply($values['function'], $values['arguments']); + $this->assertTrue(imageistruecolor($toolkit->getResource()), "Image '$file_name' after load should be a truecolor image, but it is not."); - // If the operation replaced the resource, check that the old one has - // been destroyed. - $new_res = $toolkit->getResource(); - if ($new_res !== $old_res) { - // @todo In https://www.drupal.org/node/3133236 convert this to - // $this->assertIsNotResource($old_res). - $this->assertFalse(is_resource($old_res), new FormattableMarkup("'%operation' destroyed the original resource.", ['%operation' => $values['function']])); - } + // Perform our operation. + $image->apply($operation, $arguments); - // To keep from flooding the test with assert values, make a general - // value for whether each group of values fail. - $correct_dimensions_real = TRUE; - $correct_dimensions_object = TRUE; + // Flush Image object to disk storage. + $file_path = $this->directory . '/' . $test_case . image_type_to_extension($image->getToolkit()->getType()); + $image->save($file_path); - if (imagesy($toolkit->getResource()) != $values['height'] || imagesx($toolkit->getResource()) != $values['width']) { - $correct_dimensions_real = FALSE; - } + // Check that the both the GD object and the Image object have an accurate + // record of the dimensions. + if (isset($expected['height']) && isset($expected['width'])) { + $this->assertSame($expected['height'], imagesy($toolkit->getResource()), "Image '$file_name' after '$test_case' should have a proper height."); + $this->assertSame($expected['width'], imagesx($toolkit->getResource()), "Image '$file_name' after '$test_case' should have a proper width."); + $this->assertSame($expected['height'], $image->getHeight(), "Image '$file_name' after '$test_case' should have a proper height."); + $this->assertSame($expected['width'], $image->getWidth(), "Image '$file_name' after '$test_case' should have a proper width."); + } - // Check that the image object has an accurate record of the dimensions. - if ($image->getWidth() != $values['width'] || $image->getHeight() != $values['height']) { - $correct_dimensions_object = FALSE; + // Now check each of the corners to ensure color correctness. + foreach ($expected['corners'] as $key => $expected_color) { + // The test gif that does not have transparency color set is a + // special case. + if ($file_name === 'image-test-no-transparency.gif') { + if ($test_case == 'desaturate') { + // For desaturating, keep the expected color from the test + // data, but set alpha channel to fully opaque. + $expected_color[3] = 0; } - - $file_path = $directory . '/' . $op . image_type_to_extension($image->getToolkit()->getType()); - $image->save($file_path); - - $this->assertTrue($correct_dimensions_real, new FormattableMarkup('Image %file after %action action has proper dimensions.', ['%file' => $file, '%action' => $op])); - $this->assertTrue($correct_dimensions_object, new FormattableMarkup('Image %file object after %action action is reporting the proper height and width values.', ['%file' => $file, '%action' => $op])); - - // JPEG colors will always be messed up due to compression. So we skip - // these tests if the original or the result is in jpeg format. - if ($image->getToolkit()->getType() != IMAGETYPE_JPEG && $image_original_type != IMAGETYPE_JPEG) { - // Now check each of the corners to ensure color correctness. - foreach ($values['corners'] as $key => $corner) { - // The test gif that does not have transparency color set is a - // special case. - if ($file === 'image-test-no-transparency.gif') { - if ($op == 'desaturate') { - // For desaturating, keep the expected color from the test - // data, but set alpha channel to fully opaque. - $corner[3] = 0; - } - elseif ($corner === $this->transparent) { - // Set expected pixel to yellow where the others have - // transparent. - $corner = $this->yellow; - } - } - - // Get the location of the corner. - switch ($key) { - case 0: - $x = 0; - $y = 0; - break; - - case 1: - $x = $image->getWidth() - 1; - $y = 0; - break; - - case 2: - $x = $image->getWidth() - 1; - $y = $image->getHeight() - 1; - break; - - case 3: - $x = 0; - $y = $image->getHeight() - 1; - break; - } - $color = $this->getPixelColor($image, $x, $y); - // We also skip the color test for transparency for gif <-> png - // conversion. The convert operation cannot handle that correctly. - if ($image->getToolkit()->getType() == $image_original_type || $corner != $this->transparent) { - $correct_colors = $this->colorsAreEqual($color, $corner); - $this->assertTrue($correct_colors, new FormattableMarkup('Image %file object after %action action has the correct color placement at corner %corner.', - ['%file' => $file, '%action' => $op, '%corner' => $key])); - } - } + elseif ($expected_color === static::TRANSPARENT) { + // Set expected pixel to yellow where the others have + // transparent. + $expected_color = static::YELLOW; } + } - // Check that saved image reloads without raising PHP errors. - $image_reloaded = $this->imageFactory->get($file_path); - $resource = $image_reloaded->getToolkit()->getResource(); + // Get the location of the corner. + switch ($key) { + case 0: + $x = 0; + $y = 0; + break; + + case 1: + $x = $image->getWidth() - 1; + $y = 0; + break; + + case 2: + $x = $image->getWidth() - 1; + $y = $image->getHeight() - 1; + break; + + case 3: + $x = 0; + $y = $image->getHeight() - 1; + break; } - } + $actual_color = $this->getPixelColor($image, $x, $y); - // Test creation of image from scratch, and saving to storage. - foreach ([IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_WEBP] as $type) { - $image = $this->imageFactory->get(); - $image->createNew(50, 20, image_type_to_extension($type, FALSE), '#ffff00'); - $file = 'from_null' . image_type_to_extension($type); - $file_path = $directory . '/' . $file; - $this->assertEquals(50, $image->getWidth(), new FormattableMarkup('Image file %file has the correct width.', ['%file' => $file])); - $this->assertEquals(20, $image->getHeight(), new FormattableMarkup('Image file %file has the correct height.', ['%file' => $file])); - $this->assertEquals(image_type_to_mime_type($type), $image->getMimeType(), new FormattableMarkup('Image file %file has the correct MIME type.', ['%file' => $file])); - $this->assertTrue($image->save($file_path), new FormattableMarkup('Image %file created anew from a null image was saved.', ['%file' => $file])); - - // Reload saved image. - $image_reloaded = $this->imageFactory->get($file_path); - if (!$image_reloaded->isValid()) { - $this->fail(new FormattableMarkup('Could not load image %file.', ['%file' => $file])); + // If image cannot handle transparent colors, skip the pixel color test. + if ($actual_color[3] === 0 && $expected_color[3] === 127) { continue; } - $this->assertEquals(50, $image_reloaded->getWidth(), new FormattableMarkup('Image file %file has the correct width.', ['%file' => $file])); - $this->assertEquals(20, $image_reloaded->getHeight(), new FormattableMarkup('Image file %file has the correct height.', ['%file' => $file])); - $this->assertEquals(image_type_to_mime_type($type), $image_reloaded->getMimeType(), new FormattableMarkup('Image file %file has the correct MIME type.', ['%file' => $file])); - if ($image_reloaded->getToolkit()->getType() == IMAGETYPE_GIF) { - $this->assertEquals('#ffff00', $image_reloaded->getToolkit()->getTransparentColor(), new FormattableMarkup('Image file %file has the correct transparent color channel set.', ['%file' => $file])); - } - else { - $this->assertNull($image_reloaded->getToolkit()->getTransparentColor(), new FormattableMarkup('Image file %file has no color channel set.', ['%file' => $file])); - } + + // JPEG has small differences in color after processing. + $tolerance = $image_original_type === IMAGETYPE_JPEG ? 3 : 0; + + $this->assertColorsAreEqual($expected_color, $actual_color, $tolerance, "Image '$file_name' object after '$test_case' action has the correct color placement at corner '$key'"); + } + + // Check that saved image reloads without raising PHP errors. + $image_reloaded = $this->imageFactory->get($file_path); + if (PHP_VERSION_ID >= 80000) { + $this->assertInstanceOf(\GDImage::class, $image_reloaded->getToolkit()->getResource()); + } + else { + $this->assertIsResource($image_reloaded->getToolkit()->getResource()); + $this->assertSame(get_resource_type($image_reloaded->getToolkit()->getResource()), 'gd'); + } + } + + /** + * @covers ::getSupportedExtensions + * @covers ::extensionToImageType + */ + public function testSupportedExtensions(): void { + // Test the list of supported extensions. + $expected_extensions = ['png', 'gif', 'jpeg', 'jpg', 'jpe', 'webp']; + $this->assertEqualsCanonicalizing($expected_extensions, $this->imageFactory->getSupportedExtensions()); + + // Test that the supported extensions map to correct internal GD image + // types. + $expected_image_types = [ + 'png' => IMAGETYPE_PNG, + 'gif' => IMAGETYPE_GIF, + 'jpeg' => IMAGETYPE_JPEG, + 'jpg' => IMAGETYPE_JPEG, + 'jpe' => IMAGETYPE_JPEG, + 'webp' => IMAGETYPE_WEBP, + ]; + $image = $this->imageFactory->get(); + foreach ($expected_image_types as $extension => $expected_image_type) { + $this->assertSame($expected_image_type, $image->getToolkit()->extensionToImageType($extension)); } + } + + /** + * Data provider for ::testCreateImageFromScratch(). + */ + public function providerSupportedImageTypes(): array { + return [ + [IMAGETYPE_PNG], + [IMAGETYPE_GIF], + [IMAGETYPE_JPEG], + [IMAGETYPE_WEBP], + ]; + } - // Test failures of the 'create_new' operation. + /** + * Tests that GD functions for the image type are available. + * + * @dataProvider providerSupportedImageTypes + */ + public function testGdFunctionsExist(int $type): void { + $extension = image_type_to_extension($type, FALSE); + $this->assertTrue(function_exists("imagecreatefrom$extension"), "imagecreatefrom$extension should exist."); + $this->assertTrue(function_exists("image$extension"), "image$extension should exist."); + } + + /** + * Tests creation of image from scratch, and saving to storage. + * + * @dataProvider providerSupportedImageTypes + */ + public function testCreateImageFromScratch(int $type): void { + // Build an image from scratch. + $image = $this->imageFactory->get(); + $image->createNew(50, 20, image_type_to_extension($type, FALSE), '#ffff00'); + $file = 'from_null' . image_type_to_extension($type); + $file_path = $this->directory . '/' . $file; + $this->assertSame(50, $image->getWidth()); + $this->assertSame(20, $image->getHeight()); + $this->assertSame(image_type_to_mime_type($type), $image->getMimeType()); + $this->assertTrue($image->save($file_path), "Image '$file' should have been saved successfully, but it has not."); + + // Reload and check saved image. + $image_reloaded = $this->imageFactory->get($file_path); + $this->assertTrue($image_reloaded->isValid()); + $this->assertSame(50, $image_reloaded->getWidth()); + $this->assertSame(20, $image_reloaded->getHeight()); + $this->assertSame(image_type_to_mime_type($type), $image_reloaded->getMimeType()); + if ($image_reloaded->getToolkit()->getType() == IMAGETYPE_GIF) { + $this->assertSame('#ffff00', $image_reloaded->getToolkit()->getTransparentColor(), "Image '$file' after reload should have color channel set to #ffff00, but it has not."); + } + else { + $this->assertNull($image_reloaded->getToolkit()->getTransparentColor(), "Image '$file' after reload should have no color channel set, but it has."); + } + } + + /** + * Tests failures of the 'create_new' operation. + */ + public function testCreateNewFailures(): void { $image = $this->imageFactory->get(); $image->createNew(-50, 20); $this->assertFalse($image->isValid(), 'CreateNew with negative width fails.'); @@ -472,11 +493,7 @@ public function testResourceDestruction() { /** * Tests for GIF images with transparency. */ - public function testGifTransparentImages() { - // Prepare a directory for test file results. - $directory = Settings::get('file_public_path') . '/imagetest'; - \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY); - + public function testGifTransparentImages(): void { // Test loading an indexed GIF image with transparent color set. // Color at top-right pixel should be fully transparent. $file = 'image-test-transparent-indexed.gif'; @@ -484,20 +501,20 @@ public function testGifTransparentImages() { $resource = $image->getToolkit()->getResource(); $color_index = imagecolorat($resource, $image->getWidth() - 1, 0); $color = array_values(imagecolorsforindex($resource, $color_index)); - $this->assertEquals($this->rotateTransparent, $color, "Image {$file} after load has full transparent color at corner 1."); + $this->assertEquals(static::ROTATE_TRANSPARENT, $color, "Image {$file} after load has full transparent color at corner 1."); // Test deliberately creating a GIF image with no transparent color set. // Color at top-right pixel should be fully transparent while in memory, // fully opaque after flushing image to file. $file = 'image-test-no-transparent-color-set.gif'; - $file_path = $directory . '/' . $file; + $file_path = $this->directory . '/' . $file; // Create image. $image = $this->imageFactory->get(); $image->createNew(50, 20, 'gif', NULL); $resource = $image->getToolkit()->getResource(); $color_index = imagecolorat($resource, $image->getWidth() - 1, 0); $color = array_values(imagecolorsforindex($resource, $color_index)); - $this->assertEquals($this->rotateTransparent, $color, "New GIF image with no transparent color set after creation has full transparent color at corner 1."); + $this->assertEquals(static::ROTATE_TRANSPARENT, $color, "New GIF image with no transparent color set after creation has full transparent color at corner 1."); // Save image. $this->assertTrue($image->save($file_path), "New GIF image {$file} was saved."); // Reload image. @@ -522,37 +539,20 @@ public function testGifTransparentImages() { // can be loaded correctly. $file = 'image-test-transparent-out-of-range.gif'; $image = $this->imageFactory->get('core/tests/fixtures/files/' . $file); - $toolkit = $image->getToolkit(); - - if (!$image->isValid()) { - $this->fail(new FormattableMarkup('Could not load image %file.', ['%file' => $file])); - } - else { - // All images should be converted to truecolor when loaded. - $image_truecolor = imageistruecolor($toolkit->getResource()); - $this->assertTrue($image_truecolor, new FormattableMarkup('Image %file after load is a truecolor image.', ['%file' => $file])); - } + $this->assertTrue($image->isValid(), "Image '$file' after load should be valid, but it is not."); + $this->assertTrue(imageistruecolor($image->getToolkit()->getResource()), "Image '$file' after load should be a truecolor image, but it is not."); } /** * Tests calling a missing image operation plugin. */ - public function testMissingOperation() { - - // Test that the image factory is set to use the GD toolkit. - $this->assertEquals('gd', $this->imageFactory->getToolkitId(), 'The image factory is set to use the \'gd\' image toolkit.'); - - // An image file that will be tested. - $file = 'image-test.png'; - + public function testMissingOperation(): void { // Load up a fresh image. - $image = $this->imageFactory->get('core/tests/fixtures/files/' . $file); - if (!$image->isValid()) { - $this->fail(new FormattableMarkup('Could not load image %file.', ['%file' => $file])); - } + $image = $this->imageFactory->get('core/tests/fixtures/files/image-test.png'); + $this->assertTrue($image->isValid(), "Image 'image-test.png' after load should be valid, but it is not."); // Try perform a missing toolkit operation. - $this->assertFalse($image->apply('missing_op', []), 'Calling a missing image toolkit operation plugin fails.'); + $this->assertFalse($image->apply('missing_op', []), 'Calling a missing image toolkit operation plugin should fail, but it did not.'); } }