From 859d44d22ce808398ea9a6e173c41dc6c8d48b09 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 21 Jul 2022 16:06:08 +0530 Subject: [PATCH 1/3] Add the extension to the suffix to ensure uniqueness --- modules/images/webp-uploads/helper.php | 10 ++++------ modules/images/webp-uploads/load.php | 3 ++- tests/modules/images/webp-uploads/helper-tests.php | 4 ++-- tests/modules/images/webp-uploads/load-tests.php | 4 ++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/modules/images/webp-uploads/helper.php b/modules/images/webp-uploads/helper.php index 4d98c1a9c7..774eec0488 100644 --- a/modules/images/webp-uploads/helper.php +++ b/modules/images/webp-uploads/helper.php @@ -138,13 +138,11 @@ function webp_uploads_generate_additional_image_source( $attachment_id, $image_s $editor->resize( $width, $height, $crop ); if ( null === $destination_file_name ) { + $ext = pathinfo( $image_path, PATHINFO_EXTENSION ); + $suffix = $editor->get_suffix(); + $suffix .= "-{$ext}"; $extension = explode( '|', $allowed_mimes[ $mime ] ); - $destination_file_name = $editor->generate_filename( null, null, $extension[0] ); - } - - // Skip creation of duplicate WebP image if an image file already exists in the directory. - if ( file_exists( $destination_file_name ) ) { - return new WP_Error( 'webp_image_file_present', __( 'The WebP image already exists.', 'performance-lab' ) ); + $destination_file_name = $editor->generate_filename( $suffix, null, $extension[0] ); } $image = $editor->save( $destination_file_name, $mime ); diff --git a/modules/images/webp-uploads/load.php b/modules/images/webp-uploads/load.php index 7c4e57d523..008da132db 100644 --- a/modules/images/webp-uploads/load.php +++ b/modules/images/webp-uploads/load.php @@ -74,6 +74,7 @@ function webp_uploads_create_sources_property( array $metadata, $attachment_id ) $original_directory = pathinfo( $file, PATHINFO_DIRNAME ); $filename = pathinfo( $file, PATHINFO_FILENAME ); + $ext = pathinfo( $file, PATHINFO_EXTENSION ); $allowed_mimes = array_flip( wp_get_mime_types() ); // Create the sources for the full sized image. @@ -89,7 +90,7 @@ function webp_uploads_create_sources_property( array $metadata, $attachment_id ) } $extension = explode( '|', $allowed_mimes[ $targeted_mime ] ); - $destination = trailingslashit( $original_directory ) . "{$filename}.{$extension[0]}"; + $destination = trailingslashit( $original_directory ) . "{$filename}-{$ext}.{$extension[0]}"; $image = webp_uploads_generate_additional_image_source( $attachment_id, 'full', $original_size_data, $targeted_mime, $destination ); if ( is_wp_error( $image ) ) { diff --git a/tests/modules/images/webp-uploads/helper-tests.php b/tests/modules/images/webp-uploads/helper-tests.php index a9338782db..c1957099c6 100644 --- a/tests/modules/images/webp-uploads/helper-tests.php +++ b/tests/modules/images/webp-uploads/helper-tests.php @@ -92,8 +92,8 @@ public function it_should_create_an_image_with_the_default_suffix_in_the_same_lo $this->assertIsArray( $result ); $this->assertArrayHasKey( 'filesize', $result ); $this->assertArrayHasKey( 'file', $result ); - $this->assertStringEndsWith( '300x300.webp', $result['file'] ); - $this->assertFileExists( "{$directory}{$name}-300x300.webp" ); + $this->assertStringEndsWith( '300x300-jpeg.webp', $result['file'] ); + $this->assertFileExists( "{$directory}{$name}-300x300-jpeg.webp" ); } /** diff --git a/tests/modules/images/webp-uploads/load-tests.php b/tests/modules/images/webp-uploads/load-tests.php index 7fcbc35a2b..a6792f51e5 100644 --- a/tests/modules/images/webp-uploads/load-tests.php +++ b/tests/modules/images/webp-uploads/load-tests.php @@ -197,7 +197,7 @@ function () { $this->assertStringEndsWith( '-scaled.jpg', get_attached_file( $attachment_id ) ); $this->assertImageHasSizeSource( $attachment_id, 'medium', 'image/webp' ); $this->assertStringEndsNotWith( '-scaled.webp', $metadata['sizes']['medium']['sources']['image/webp']['file'] ); - $this->assertStringEndsWith( '-300x200.webp', $metadata['sizes']['medium']['sources']['image/webp']['file'] ); + $this->assertStringEndsWith( '-300x200-jpg.webp', $metadata['sizes']['medium']['sources']['image/webp']['file'] ); } /** @@ -539,7 +539,7 @@ function () { $this->assertStringContainsString( $size['height'], $size['sources']['image/webp']['file'] ); $this->assertStringContainsString( // Remove the extension from the file. - substr( $size['sources']['image/webp']['file'], 0, -4 ), + substr( $size['sources']['image/webp']['file'], 0, -10 ), $size['sources']['image/jpeg']['file'] ); } From 4ec874c2df2bae0a678547d8fc635cc1aa0a0a5b Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Fri, 22 Jul 2022 11:39:09 +0530 Subject: [PATCH 2/3] Add tests for generate_filename --- .../images/webp-uploads/helper-tests.php | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/modules/images/webp-uploads/helper-tests.php b/tests/modules/images/webp-uploads/helper-tests.php index c1957099c6..7645863146 100644 --- a/tests/modules/images/webp-uploads/helper-tests.php +++ b/tests/modules/images/webp-uploads/helper-tests.php @@ -507,4 +507,27 @@ private function mock_empty_action( $action ) { remove_all_actions( $action ); do_action( $action ); } + + /** + * Check different suffix values for generate_filename. + * + * @test + */ + public function it_should_check_different_suffix_values_for_generate_filename() { + $image_path = TESTS_PLUGIN_DIR . '/tests/testdata/modules/images/car.jpeg'; + $editor = wp_get_image_editor( $image_path ); + $editor->resize( 100, 50, true ); + $ext = pathinfo( $image_path, PATHINFO_EXTENSION ); + $suffix = $editor->get_suffix(); + $suffix .= "-{$ext}"; + + // Test with no parameters. + $this->assertSame( 'car-100x50.jpeg', wp_basename( $editor->generate_filename() ) ); + // Test with webp parameter. + $this->assertSame( 'car-100x50.webp', wp_basename( $editor->generate_filename( null, null, 'webp' ) ) ); + // Test with suffix and webp parameter. + $this->assertSame( 'car-100x50-jpeg.webp', wp_basename( $editor->generate_filename( $suffix, null, 'webp' ) ) ); + // Test with suffix and png parameter. + $this->assertSame( 'car-100x50-jpeg.png', wp_basename( $editor->generate_filename( $suffix, null, 'png' ) ) ); + } } From a9f051ed6aed564ad24b4540e00d9914c42e60c3 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 1 Aug 2022 10:44:41 +0530 Subject: [PATCH 3/3] Address Unit test feedback --- .../images/webp-uploads/helper-tests.php | 47 +++++++++++------- tests/testdata/modules/images/image.jpeg | Bin 0 -> 12979 bytes tests/testdata/modules/images/image.jpg | Bin 0 -> 12026 bytes 3 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 tests/testdata/modules/images/image.jpeg create mode 100644 tests/testdata/modules/images/image.jpg diff --git a/tests/modules/images/webp-uploads/helper-tests.php b/tests/modules/images/webp-uploads/helper-tests.php index 7645863146..1025ece473 100644 --- a/tests/modules/images/webp-uploads/helper-tests.php +++ b/tests/modules/images/webp-uploads/helper-tests.php @@ -509,25 +509,38 @@ private function mock_empty_action( $action ) { } /** - * Check different suffix values for generate_filename. + * Add the original image's extension to the WebP file name to ensure it is unique + * + * @dataProvider data_provider_same_image_name * * @test */ - public function it_should_check_different_suffix_values_for_generate_filename() { - $image_path = TESTS_PLUGIN_DIR . '/tests/testdata/modules/images/car.jpeg'; - $editor = wp_get_image_editor( $image_path ); - $editor->resize( 100, 50, true ); - $ext = pathinfo( $image_path, PATHINFO_EXTENSION ); - $suffix = $editor->get_suffix(); - $suffix .= "-{$ext}"; - - // Test with no parameters. - $this->assertSame( 'car-100x50.jpeg', wp_basename( $editor->generate_filename() ) ); - // Test with webp parameter. - $this->assertSame( 'car-100x50.webp', wp_basename( $editor->generate_filename( null, null, 'webp' ) ) ); - // Test with suffix and webp parameter. - $this->assertSame( 'car-100x50-jpeg.webp', wp_basename( $editor->generate_filename( $suffix, null, 'webp' ) ) ); - // Test with suffix and png parameter. - $this->assertSame( 'car-100x50-jpeg.png', wp_basename( $editor->generate_filename( $suffix, null, 'png' ) ) ); + public function it_should_add_original_image_extension_to_the_webp_file_name_to_ensure_it_is_unique( $jpeg_image, $jpg_image ) { + $jpeg_image_attachment_id = $this->factory->attachment->create_upload_object( $jpeg_image ); + $jpg_image_attachment_id = $this->factory->attachment->create_upload_object( $jpg_image ); + + $size_data = array( + 'width' => 300, + 'height' => 300, + 'crop' => true, + ); + + $jpeg_image_result = webp_uploads_generate_additional_image_source( $jpeg_image_attachment_id, 'medium', $size_data, 'image/webp' ); + $jpg_image_result = webp_uploads_generate_additional_image_source( $jpg_image_attachment_id, 'medium', $size_data, 'image/webp' ); + + $this->assertIsArray( $jpeg_image_result ); + $this->assertIsArray( $jpg_image_result ); + $this->assertStringEndsWith( '300x300-jpeg.webp', $jpeg_image_result['file'] ); + $this->assertStringEndsWith( '300x300-jpg.webp', $jpg_image_result['file'] ); + $this->assertNotSame( $jpeg_image_result['file'], $jpg_image_result['file'] ); + } + + public function data_provider_same_image_name() { + return array( + array( + TESTS_PLUGIN_DIR . '/tests/testdata/modules/images/image.jpeg', + TESTS_PLUGIN_DIR . '/tests/testdata/modules/images/image.jpg', + ), + ); } } diff --git a/tests/testdata/modules/images/image.jpeg b/tests/testdata/modules/images/image.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..89389d8383ee0a304a517b0283d5e5629f04b6be GIT binary patch literal 12979 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 literal 0 HcmV?d00001 diff --git a/tests/testdata/modules/images/image.jpg b/tests/testdata/modules/images/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dc8212dafbd8f2073a05a437f7483a7b3f4818e5 GIT binary patch literal 12026 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