| Current File : /home/jvzmxxx/wiki1/extensions/Flow/tests/phpunit/Data/BagOStuff/BufferedBagOStuffTest.php |
<?php
namespace Flow\Tests;
use BagOStuff;
use EmptyBagOStuff;
use Flow\Data\BagOStuff\BufferedBagOStuff;
use HashBagOStuff;
use MediaWikiTestCase;
use MultiWriteBagOStuff;
use ObjectCache;
/**
* @group Flow
*/
class BufferedBagOStuffTest extends MediaWikiTestCase {
/**
* @var BagOStuff
*/
protected $cache;
/**
* @var BufferedBagOStuff
*/
protected $bufferedCache;
/**
* Array of keys used in these tests, so we can clear them on tearDown.
*
* @var string[]
*/
protected $keys = array( 'key', 'key2' );
protected function setUp() {
parent::setUp();
// type defined through parameter
if ( $this->getCliArg( 'use-bagostuff' ) ) {
$name = $this->getCliArg( 'use-bagostuff' );
$this->cache = ObjectCache::newFromId( $name );
} else {
// no type defined - use simple hash
$this->cache = new HashBagOStuff;
}
$this->bufferedCache = new BufferedBagOStuff( $this->cache );
$this->bufferedCache->begin();
}
protected function tearDown() {
// make sure all keys written to in any of these tests are deleted from
// the real cache
foreach ( $this->keys as $key ) {
$this->cache->delete( $key );
}
parent::tearDown();
}
public function testGetAndSet() {
$this->bufferedCache->set( 'key', 'value' );
// check that the value is only set on bufferedCache, not yet on real cache
$this->assertEquals( 'value', $this->bufferedCache->get( 'key' ) );
$this->assertEquals( false, $this->cache->get( 'key' ) );
$this->bufferedCache->commit();
// check that the value is also set on real cache
$this->assertEquals( 'value', $this->bufferedCache->get( 'key' ) );
$this->assertEquals( 'value', $this->cache->get( 'key' ) );
}
public function testAdd() {
$this->bufferedCache->add( 'key', 'value' );
// check that the value is only set on bufferedCache, not yet on real cache
$this->assertEquals( 'value', $this->bufferedCache->get( 'key' ) );
$this->assertEquals( false, $this->cache->get( 'key' ) );
$this->bufferedCache->commit();
// check that the value is also set on real cache
$this->assertEquals( 'value', $this->bufferedCache->get( 'key' ) );
$this->assertEquals( 'value', $this->cache->get( 'key' ) );
}
public function testAddFailImmediately() {
$this->cache->set( 'key', 'value' );
$this->bufferedCache->add( 'key', 'value-2' );
$this->bufferedCache->commit();
// check that the value is not added on bufferedCache, nor on real cache
$this->assertEquals( 'value', $this->bufferedCache->get( 'key' ) );
$this->assertEquals( 'value', $this->cache->get( 'key' ) );
}
public function testAddFailDeferred() {
$this->bufferedCache->add( 'key', 'value' );
// something else directly sets the key in the meantime...
$this->cache->set( 'key', 'value-2' );
// check that the value has been added to buffered cache but not yet to real cache
$this->assertEquals( 'value', $this->bufferedCache->get( 'key' ) );
$this->assertEquals( 'value-2', $this->cache->get( 'key' ) );
$this->bufferedCache->commit();
// check that the value failed to add and the key was properly cleared
$this->assertEquals( false, $this->bufferedCache->get( 'key' ) );
$this->assertEquals( false, $this->cache->get( 'key' ) );
}
public function testDelete() {
$this->cache->set( 'key', 'value' );
$this->bufferedCache->delete( 'key' );
// check that the value has been deleted from bufferedcache (only)
$this->assertEquals( false, $this->bufferedCache->get( 'key' ) );
$this->assertEquals( 'value', $this->cache->get( 'key' ) );
$this->bufferedCache->commit();
// check that the value has also been deleted from real cache
$this->assertEquals( false, $this->bufferedCache->get( 'key' ) );
$this->assertEquals( false, $this->cache->get( 'key' ) );
}
public function testGetMulti() {
$localValues = array(
'key' => 'value',
);
$cacheValues = array(
'key2' => 'value2',
);
foreach ( $localValues as $key => $value ) {
$this->bufferedCache->set( $key, $value );
}
foreach ( $cacheValues as $key => $value ) {
$this->cache->set( $key, $value );
}
// check that we're able to read the values from both buffered & real cache
$this->assertEquals( $localValues + $cacheValues, $this->bufferedCache->getMulti( array_keys( $localValues + $cacheValues ) ) );
// tearDown will cleanup everything that's been stored via buffered cache,
// however, this one went directly to real cache - clean up!
$this->cache->delete( 'key2' );
}
public function testSetMulti() {
$this->bufferedCache->setMulti( array(
'key' => 'value',
'key2' => 'value2',
) );
// check that the values are only set on bufferedCache, not yet on real cache
$this->assertEquals( 'value', $this->bufferedCache->get( 'key' ) );
$this->assertEquals( 'value2', $this->bufferedCache->get( 'key2' ) );
$this->assertEquals( false, $this->cache->get( 'key' ) );
$this->assertEquals( false, $this->cache->get( 'key2' ) );
$this->bufferedCache->commit();
// check that the values are also set on real cache
$this->assertEquals( 'value', $this->bufferedCache->get( 'key' ) );
$this->assertEquals( 'value2', $this->bufferedCache->get( 'key2' ) );
$this->assertEquals( 'value', $this->cache->get( 'key' ) );
$this->assertEquals( 'value2', $this->cache->get( 'key2' ) );
}
public function testMerge() {
$this->cache->set( 'key', 'value' );
$callback = function( \BagOStuff $cache, $key, $value ) {
return 'merged-value';
};
$this->bufferedCache->merge( 'key', $callback );
$this->bufferedCache->commit();
// check that the values are merged both in buffered & real cache
$this->assertEquals( 'merged-value', $this->bufferedCache->get( 'key' ) );
$this->assertEquals( 'merged-value', $this->cache->get( 'key' ) );
}
// Can't make a test for merge to fail immediately: the buffered part is
// only in memory, for the current process. There is no way something else
// will be able to overwrite something in there.
public function testMergeFailDelayed() {
/*
* Test concurrent merges by forking this process, if:
* - not manually called with --use-bagostuff
* - pcntl_fork is supported by the system
* - cache type will correctly support calls over forks
*/
$fork = (bool) $this->getCliArg( 'use-bagostuff' );
$fork &= function_exists( 'pcntl_fork' );
$fork &= !$this->cache instanceof HashBagOStuff;
$fork &= !$this->cache instanceof EmptyBagOStuff;
$fork &= !$this->cache instanceof MultiWriteBagOStuff;
if ( !$fork ) {
$this->markTestSkipped( "Unable to fork, can't test merge" );
}
$this->cache->set( 'key', 'value' );
$callback = function ( \BagOStuff $cache, $key, $value ) {
// prepend merged to whatever is in cache
return 'merged-' . (string) $value;
};
$this->bufferedCache->merge( 'key', $callback, 0, 1 );
// callback should take awhile now so that we can test concurrent merge attempts
$pid = pcntl_fork();
if ( $pid == -1 ) {
// can't fork, ignore this test...
} elseif ( $pid ) {
// wait a little, making sure that the child process is calling merge
usleep( 3000 );
// attempt a merge - this should fail to persist to real cache
$this->bufferedCache->commit();
// make sure the child's merge is completed and verify
usleep( 3000 );
// check that the values failed to merge
$this->assertEquals( false, $this->bufferedCache->get( 'key' ) );
$this->assertEquals( false, $this->cache->get( 'key' ) );
} else {
$this->bufferedCache->commit();
// before exiting, tear down - MediaWikiTestCase will check for it
// on destruct & throw an exception if it wasn't called
$this->tearDown();
// Note: I'm not even going to check if the merge worked, I'll
// compare values in the parent process to test if this merge worked.
// I'm just going to exit this child process, since I don't want the
// child to output any test results (would be rather confusing to
// have test output twice)
exit;
}
}
public function testRollback() {
$this->cache->set( 'key', 'value' );
$this->bufferedCache->set( 'key', 'value-2' );
$this->bufferedCache->add( 'key2', 'value-2' );
// something else directly sets the key in the meantime...
$this->cache->set( 'key2', 'value' );
$this->bufferedCache->commit();
// both changes should have been "rolled back" and both keys should've
// been cleared, in both buffered & real cache
$this->assertEquals( false, $this->bufferedCache->get( 'key' ) );
$this->assertEquals( false, $this->bufferedCache->get( 'key2' ) );
$this->assertEquals( false, $this->cache->get( 'key' ) );
$this->assertEquals( false, $this->cache->get( 'key2' ) );
}
}