Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F585006
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/maintenance/checkImages.php b/maintenance/checkImages.php
index 169b0f929aa..6d463d2abd9 100644
--- a/maintenance/checkImages.php
+++ b/maintenance/checkImages.php
@@ -23,6 +23,7 @@
use MediaWiki\FileRepo\File\FileSelectQueryBuilder;
use MediaWiki\Maintenance\Maintenance;
+use Wikimedia\Rdbms\SelectQueryBuilder;
// @codeCoverageIgnoreStart
require_once __DIR__ . '/Maintenance.php';
@@ -50,11 +51,12 @@ class CheckImages extends Maintenance {
$repo = $this->getServiceContainer()->getRepoGroup()->getLocalRepo();
do {
- $queryBuilder = FileSelectQueryBuilder::newForFile( $dbr );
-
- $res = $queryBuilder->where( $dbr->expr( 'img_name', '>', $start ) )
+ $res = FileSelectQueryBuilder::newForFile( $dbr )
+ ->where( $dbr->expr( 'img_name', '>', $start ) )
->limit( $this->getBatchSize() )
- ->caller( __METHOD__ )->fetchResultSet();
+ ->orderBy( 'img_name', SelectQueryBuilder::SORT_ASC )
+ ->caller( __METHOD__ )
+ ->fetchResultSet();
foreach ( $res as $row ) {
$numImages++;
$start = $row->img_name;
diff --git a/tests/phpunit/maintenance/CheckImagesTest.php b/tests/phpunit/maintenance/CheckImagesTest.php
new file mode 100644
index 00000000000..ebbb995a3af
--- /dev/null
+++ b/tests/phpunit/maintenance/CheckImagesTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace MediaWiki\Tests\Maintenance;
+
+use CheckImages;
+use File;
+use LocalRepo;
+use RepoGroup;
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @covers \CheckImages
+ * @group Database
+ * @author Dreamy Jazz
+ */
+class CheckImagesTest extends MaintenanceBaseTestCase {
+
+ protected function getMaintenanceClass() {
+ return CheckImages::class;
+ }
+
+ public function testWhenImageIsNotLocallyAccessible() {
+ // Mock that all File objects obtained from the local repo does not have a path.
+ $mockFile = $this->createMock( File::class );
+ $mockFile->method( 'getPath' )
+ ->willReturn( false );
+ $mockFileRepo = $this->createMock( LocalRepo::class );
+ $mockFileRepo->method( 'newFileFromRow' )
+ ->willReturn( $mockFile );
+ $mockRepoGroup = $this->createMock( RepoGroup::class );
+ $mockRepoGroup->method( 'getLocalRepo' )
+ ->willReturn( $mockFileRepo );
+ $this->setService( 'RepoGroup', $mockRepoGroup );
+
+ $this->maintenance->execute();
+ $output = $this->getActualOutputForAssertion();
+ $this->assertStringContainsString( 'Random-112m.png: not locally accessible', $output );
+ $this->assertStringContainsString( 'Random-13m.png: not locally accessible', $output );
+ $this->assertStringContainsString( 'Good images: 0/2', $output );
+ }
+
+ public function testWhenAllImagesAreMissing() {
+ $this->maintenance->execute();
+ $output = $this->getActualOutputForAssertion();
+ $this->assertStringContainsString( 'Random-112m.png: missing', $output );
+ $this->assertStringContainsString( 'Random-13m.png: missing', $output );
+ $this->assertStringContainsString( 'Good images: 0/2', $output );
+ }
+
+ /**
+ * Creates a mock {@link LocalRepo} instance to be returned by the {@link RepoGroup} service
+ * that returns the specified integer from {@link LocalRepo::getFileSize}.
+ *
+ * @param int $mockFileSizeForAllFiles
+ * @return void
+ */
+ private function mockLocalRepoWithDefinedFileSize( int $mockFileSizeForAllFiles ): void {
+ $realLocalRepo = $this->getServiceContainer()->getRepoGroup()->getLocalRepo();
+ $mockFileRepo = $this->createMock( LocalRepo::class );
+ $mockFileRepo->method( 'getFileSize' )
+ ->willReturn( $mockFileSizeForAllFiles );
+ $mockFileRepo->method( 'newFileFromRow' )
+ ->willReturnCallback( static function ( $row ) use ( $realLocalRepo ) {
+ return $realLocalRepo->newFileFromRow( $row );
+ } );
+ $mockRepoGroup = $this->createMock( RepoGroup::class );
+ $mockRepoGroup->method( 'getLocalRepo' )
+ ->willReturn( $mockFileRepo );
+ $this->setService( 'RepoGroup', $mockRepoGroup );
+ }
+
+ public function testWhenFileSizeIsZero() {
+ $this->mockLocalRepoWithDefinedFileSize( 0 );
+
+ $this->maintenance->execute();
+ $output = $this->getActualOutputForAssertion();
+ $this->assertStringContainsString( 'Random-112m.png: truncated, was 12345', $output );
+ $this->assertStringContainsString( 'Random-13m.png: truncated, was 54321', $output );
+ $this->assertStringContainsString( 'Good images: 0/2', $output );
+ }
+
+ public function testWhenFileSizeDoesNotMatchSizeInDB() {
+ // Mock that all files have a size which does not match any size in the DB
+ $this->mockLocalRepoWithDefinedFileSize( 123 );
+
+ $this->maintenance->execute();
+ $output = $this->getActualOutputForAssertion();
+ $this->assertStringContainsString( 'Random-112m.png: size mismatch DB=12345, actual=123', $output );
+ $this->assertStringContainsString( 'Random-13m.png: size mismatch DB=54321, actual=123', $output );
+ $this->assertStringContainsString( 'Good images: 0/2', $output );
+ }
+
+ public function testWhenOneFileSizeMatches() {
+ // Mock that all files have the size that matches the size in the DB for Random-112m.png
+ $this->mockLocalRepoWithDefinedFileSize( 12345 );
+
+ // To prevent IDE errors, call the ::setBatchSize method when it has been explicitly documented as having the
+ // TestingAccessWrapper type.
+ /** @var TestingAccessWrapper $maintenance */
+ $maintenance = $this->maintenance;
+ $maintenance->setBatchSize( 1 );
+ $maintenance->execute();
+
+ $output = $this->getActualOutputForAssertion();
+ $this->assertStringNotContainsString( 'Random-112m.png', $output );
+ $this->assertStringContainsString( 'Random-13m.png: size mismatch DB=54321, actual=123', $output );
+ $this->assertStringContainsString( 'Good images: 1/2', $output );
+ }
+
+ public function addDBDataOnce() {
+ // Add some testing rows to the image table to simulate two images existing.
+ $testUser = $this->getTestUser()->getUser();
+ $commentId = $this->getServiceContainer()->getCommentStore()
+ ->createComment( $this->getDb(), 'test' )->id;
+ $this->getDb()->newInsertQueryBuilder()
+ ->insertInto( 'image' )
+ ->rows( [
+ [
+ 'img_name' => 'Random-13m.png',
+ 'img_size' => 54321,
+ 'img_width' => 1000,
+ 'img_height' => 1800,
+ 'img_metadata' => '',
+ 'img_bits' => 16,
+ 'img_media_type' => MEDIATYPE_BITMAP,
+ 'img_major_mime' => 'image',
+ 'img_minor_mime' => 'png',
+ 'img_description_id' => $commentId,
+ 'img_actor' => $testUser->getActorId(),
+ 'img_timestamp' => $this->getDb()->timestamp( '20201105234242' ),
+ 'img_sha1' => 'sy02psim0bgdh0jt4vdltuzoh7j80yu',
+ ],
+ [
+ 'img_name' => 'Random-112m.png',
+ 'img_size' => 12345,
+ 'img_width' => 1000,
+ 'img_height' => 1800,
+ 'img_metadata' => '',
+ 'img_bits' => 16,
+ 'img_media_type' => MEDIATYPE_BITMAP,
+ 'img_major_mime' => 'image',
+ 'img_minor_mime' => 'png',
+ 'img_description_id' => $commentId,
+ 'img_actor' => $testUser->getActorId(),
+ 'img_timestamp' => $this->getDb()->timestamp( '20201105235242' ),
+ 'img_sha1' => 'sy02psim0bgdh0jt4vdltuzoh7j80ru',
+ ],
+ ] )
+ ->caller( __METHOD__ )
+ ->execute();
+ }
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Jul 5, 5:31 AM (15 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
227465
Default Alt Text
(6 KB)
Attached To
Mode
rMW mediawiki
Attached
Detach File
Event Timeline
Log In to Comment