wiki.techinc.nl/tests/phpunit/includes/libs/MemoizedCallableTest.php
Kunal Mehta b6b10bb58f Require ClassMatchesFilename sniff to pass for most of tests/
phpunit-patch-coverage assumes that the filename matches the classname
as a performance optimization. And for most test cases, this is true. We
should enforce this with PHPCS, mostly to help developers not make
mistakes.

Test cases that have mock classes will need to ensure that the test case
class that matches the filename comes first, since that's the only class
the sniff will look at.

Tests in GlobalFunctions/ and maintenance/ are still exempted for now,
since they don't match yet.

Change-Id: Iede341504290f5ba2da1c81908069ba9d465600f
2019-01-29 23:57:12 -08:00

142 lines
3.8 KiB
PHP

<?php
/**
* PHPUnit tests for MemoizedCallable class.
* @covers MemoizedCallable
*/
class MemoizedCallableTest extends PHPUnit\Framework\TestCase {
use MediaWikiCoversValidator;
/**
* The memoized callable should relate inputs to outputs in the same
* way as the original underlying callable.
*/
public function testReturnValuePassedThrough() {
$mock = $this->getMockBuilder( stdClass::class )
->setMethods( [ 'reverse' ] )->getMock();
$mock->expects( $this->any() )
->method( 'reverse' )
->will( $this->returnCallback( 'strrev' ) );
$memoized = new MemoizedCallable( [ $mock, 'reverse' ] );
$this->assertEquals( 'flow', $memoized->invoke( 'wolf' ) );
}
/**
* Consecutive calls to the memoized callable with the same arguments
* should result in just one invocation of the underlying callable.
*
* @requires extension apcu
*/
public function testCallableMemoized() {
$observer = $this->getMockBuilder( stdClass::class )
->setMethods( [ 'computeSomething' ] )->getMock();
$observer->expects( $this->once() )
->method( 'computeSomething' )
->will( $this->returnValue( 'ok' ) );
$memoized = new ArrayBackedMemoizedCallable( [ $observer, 'computeSomething' ] );
// First invocation -- delegates to $observer->computeSomething()
$this->assertEquals( 'ok', $memoized->invoke() );
// Second invocation -- returns memoized result
$this->assertEquals( 'ok', $memoized->invoke() );
}
/**
* @covers MemoizedCallable::invoke
*/
public function testInvokeVariadic() {
$memoized = new MemoizedCallable( 'sprintf' );
$this->assertEquals(
$memoized->invokeArgs( [ 'this is %s', 'correct' ] ),
$memoized->invoke( 'this is %s', 'correct' )
);
}
/**
* @covers MemoizedCallable::call
*/
public function testShortcutMethod() {
$this->assertEquals(
'this is correct',
MemoizedCallable::call( 'sprintf', [ 'this is %s', 'correct' ] )
);
}
/**
* Outlier TTL values should be coerced to range 1 - 86400.
*/
public function testTTLMaxMin() {
$memoized = new MemoizedCallable( 'abs', 100000 );
$this->assertEquals( 86400, $this->readAttribute( $memoized, 'ttl' ) );
$memoized = new MemoizedCallable( 'abs', -10 );
$this->assertEquals( 1, $this->readAttribute( $memoized, 'ttl' ) );
}
/**
* Closure names should be distinct.
*/
public function testMemoizedClosure() {
$a = new MemoizedCallable( function () {
return 'a';
} );
$b = new MemoizedCallable( function () {
return 'b';
} );
$this->assertEquals( $a->invokeArgs(), 'a' );
$this->assertEquals( $b->invokeArgs(), 'b' );
$this->assertNotEquals(
$this->readAttribute( $a, 'callableName' ),
$this->readAttribute( $b, 'callableName' )
);
$c = new ArrayBackedMemoizedCallable( function () {
return rand();
} );
$this->assertEquals( $c->invokeArgs(), $c->invokeArgs(), 'memoized random' );
}
/**
* @expectedExceptionMessage non-scalar argument
* @expectedException InvalidArgumentException
*/
public function testNonScalarArguments() {
$memoized = new MemoizedCallable( 'gettype' );
$memoized->invoke( new stdClass() );
}
/**
* @expectedExceptionMessage must be an instance of callable
* @expectedException InvalidArgumentException
*/
public function testNotCallable() {
$memoized = new MemoizedCallable( 14 );
}
}
/**
* A MemoizedCallable subclass that stores function return values
* in an instance property rather than APC or APCu.
*/
class ArrayBackedMemoizedCallable extends MemoizedCallable {
private $cache = [];
protected function fetchResult( $key, &$success ) {
if ( array_key_exists( $key, $this->cache ) ) {
$success = true;
return $this->cache[$key];
}
$success = false;
return false;
}
protected function storeResult( $key, $result ) {
$this->cache[$key] = $result;
}
}