wiki.techinc.nl/tests/phpunit/integration/includes/poolcounter/PoolCounterWorkTest.php
Timo Tijhof 4e40bcdd8f PoolCounter: Adopt PSR logger in PoolCounter objects
* Inject from ServiceWiring via PoolCounterFactory.

* In production we use PoolCounterClient (which is backed by
  our poolcounterd service), we also offer PoolCounterRedis for
  third parties. Replace the local logger in the Redis variant in
  favour of the built-in one.

* Update PoolCounterWork to adopt this as well. Expose it via getter
  to here, so that DI works all the way, including for the existing
  test.

Bug: T358901
Change-Id: I5413825a0172b186d58e85bbc3cc93697b174c27
2024-03-02 00:41:11 +00:00

254 lines
7 KiB
PHP

<?php
/**
* Provides of semaphore semantics for restricting the number
* of workers that may be concurrently performing the same task.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
use MediaWiki\PoolCounter\PoolCounter;
use MediaWiki\PoolCounter\PoolCounterWork;
use MediaWiki\Status\Status;
use Psr\Log\LoggerInterface;
use Wikimedia\TestingAccessWrapper;
/**
* @group Concurrency
* @coversDefaultClass \MediaWiki\PoolCounter\PoolCounterWork
*/
class PoolCounterWorkTest extends MediaWikiIntegrationTestCase {
private function configureMock( $obj, $configMethods ) {
$obj->expects( $this->never() )
->method( $this->anythingBut( ...array_keys( $configMethods ) ) );
foreach ( $configMethods as $method => $value ) {
$obj->expects( $this->once() )
->method( $method )
->will( $value );
}
return $obj;
}
private function configureFixture(
array $workerMethods,
array $poolCounterMethods,
array $loggerMethods
) {
$logger = $this->createMock( LoggerInterface::class );
$this->configureMock( $logger, $loggerMethods );
$poolCounter = $this->getMockBuilder( PoolCounter::class )
->disableOriginalConstructor()
->getMockForAbstractClass();
$poolCounter->setLogger( $logger );
$this->configureMock( $poolCounter, $poolCounterMethods );
$worker = $this->getMockBuilder( PoolCounterWork::class )
->setConstructorArgs( [ 'PoolType', 'PoolKey', $poolCounter ] )
->onlyMethods( array_keys( $workerMethods ) )
->getMockForAbstractClass();
$this->configureMock( $worker, $workerMethods );
return TestingAccessWrapper::newFromObject( $worker );
}
public function provideExecuteFlow() {
yield "Not Cacheable and not skipCache should call acquireForMe" => [
false, false,
[
'acquireForMe' => $this->returnValue( Status::newGood( 'StupidAction' ) )
], [], [
'info' => $this->returnValue( null )
],
false
];
yield "Not Cacheable and skipCache should call acquireForMe" => [
false, true,
[
'acquireForMe' => $this->returnValue( Status::newGood( 'StupidAction' ) )
], [], [
'info' => $this->returnValue( null )
],
false
];
yield "Cacheable and not skipCache should call acquireForAnyone" => [
true, false,
[
'acquireForAnyone' => $this->returnValue( Status::newGood( 'StupidAction' ) )
], [], [
'info' => $this->returnValue( null )
],
false
];
yield "Cacheable and skipCache should call acquireForMe" => [
true, true,
[
'acquireForMe' => $this->returnValue( Status::newGood( 'StupidAction' ) )
], [], [
'info' => $this->returnValue( null )
],
false
];
yield 'If Fatal error call doWork and log error' => [
false, false,
[
'acquireForMe' => $this->returnValue( Status::newFatal( 'apierror' ) )
], [
'doWork' => $this->returnValue( "SomeResults" )
], [
'info' => $this->returnValue( null )
],
"SomeResults"
];
foreach ( [
'LOCK_HELD' => PoolCounter::LOCK_HELD,
'LOCKED' => PoolCounter::LOCKED
] as $name => $acquireResult ) {
yield 'If ' . $name . ' call doWork and return' => [
false, false,
[
'acquireForMe' => $this->returnValue( Status::newGood( $acquireResult ) ),
'release' => $this->returnValue( null )
], [
'doWork' => $this->returnValue( "SomeResults" )
], [],
"SomeResults"
];
}
yield 'If DONE and not chacheable call getCachedWork and return if OK' => [
false, false,
[
'acquireForMe' => $this->returnValue( Status::newGood( PoolCounter::DONE ) ),
], [
'getCachedWork' => $this->returnValue( "SomeResults" )
], [],
"SomeResults"
];
yield 'If DONE and chacheable call getCachedWork and return if OK' => [
true, false,
[
'acquireForAnyone' => $this->returnValue( Status::newGood( PoolCounter::DONE ) ),
], [
'getCachedWork' => $this->returnValue( "SomeResults" )
], [],
"SomeResults"
];
yield 'If DONE and chacheable call getCachedWork and repeat if not OK' => [
true, false,
[
'acquireForAnyone' => $this->returnValue( Status::newGood( PoolCounter::DONE ) ),
'acquireForMe' => $this->returnValue( Status::newGood( PoolCounter::LOCKED ) ),
'release' => $this->returnValue( '' )
], [
'getCachedWork' => $this->returnValue( false ),
'doWork' => $this->returnValue( 'SomeResults' )
], [],
"SomeResults"
];
foreach ( [
'QUEUE_FULL' => PoolCounter::QUEUE_FULL,
'TIMEOUT' => PoolCounter::TIMEOUT
] as $name => $acquireResult ) {
yield 'If ' . $name . ' and not chacheable call fallback and return if OK' => [
false, false,
[
'acquireForMe' => $this->returnValue( Status::newGood( $acquireResult ) ),
], [
'fallback' => $this->returnValue( "SomeResults" )
], [],
"SomeResults"
];
yield 'If ' . $name . ' and not chacheable call fallback and log if not OK' => [
false, false,
[
'acquireForMe' => $this->returnValue( Status::newGood( $acquireResult ) ),
], [
], [
'info' => $this->returnValue( null )
],
false
];
}
}
/**
* @dataProvider provideExecuteFlow
* @covers ::execute
*/
public function testExecuteFlow(
bool $cacheable,
bool $skipCache,
array $poolCounterMethods,
array $workerMethods,
array $loggerMethods,
$expectedResult
) {
$worker = $this->configureFixture(
$workerMethods,
$poolCounterMethods,
$loggerMethods
);
$worker->cacheable = $cacheable;
$this->assertSame( $expectedResult, $worker->execute( $skipCache ) );
}
/**
* @covers ::execute
*/
public function testDoWorkRaiseException() {
$expectedException = new RuntimeException( __METHOD__ );
$worker = $this->configureFixture(
[ 'doWork' => $this->throwException( $expectedException ) ],
[
'acquireForMe' => $this->returnValue( Status::newGood( PoolCounter::LOCK_HELD ) ),
'release' => $this->returnValue( '' )
],
[]
);
$this->expectExceptionObject( $expectedException );
$worker->execute();
}
/**
* @covers ::getCachedWork
* @covers ::error
* @covers ::fallback
* @covers ::__construct
*/
public function testDefaults() {
$worker = $this->configureFixture( [], [], [] );
$this->assertFalse( $worker->cacheable );
$this->assertFalse( $worker->getCachedWork() );
$this->assertFalse( $worker->error( Status::newFatal( 'apierror' ) ) );
$this->assertFalse( $worker->fallback( false ) );
}
}