symfony/lock 추가
This commit is contained in:
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Lock\Factory;
|
||||
use Symfony\Component\Lock\LockInterface;
|
||||
use Symfony\Component\Lock\StoreInterface;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class FactoryTest extends TestCase
|
||||
{
|
||||
public function testCreateLock()
|
||||
{
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
|
||||
$factory = new Factory($store);
|
||||
$factory->setLogger($logger);
|
||||
|
||||
$lock = $factory->createLock('foo');
|
||||
|
||||
$this->assertInstanceOf(LockInterface::class, $lock);
|
||||
}
|
||||
}
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Lock\Exception\LockConflictedException;
|
||||
use Symfony\Component\Lock\Key;
|
||||
use Symfony\Component\Lock\Lock;
|
||||
use Symfony\Component\Lock\StoreInterface;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class LockTest extends TestCase
|
||||
{
|
||||
public function testAcquireNoBlocking()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('save');
|
||||
|
||||
$this->assertTrue($lock->acquire(false));
|
||||
}
|
||||
|
||||
public function testAcquireReturnsFalse()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('save')
|
||||
->willThrowException(new LockConflictedException());
|
||||
|
||||
$this->assertFalse($lock->acquire(false));
|
||||
}
|
||||
|
||||
public function testAcquireBlocking()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store);
|
||||
|
||||
$store
|
||||
->expects($this->never())
|
||||
->method('save');
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('waitAndSave');
|
||||
|
||||
$this->assertTrue($lock->acquire(true));
|
||||
}
|
||||
|
||||
public function testAcquireSetsTtl()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store, 10);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('save');
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('putOffExpiration')
|
||||
->with($key, 10);
|
||||
|
||||
$lock->acquire();
|
||||
}
|
||||
|
||||
public function testRefresh()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store, 10);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('putOffExpiration')
|
||||
->with($key, 10);
|
||||
|
||||
$lock->refresh();
|
||||
}
|
||||
|
||||
public function testIsAquired()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store, 10);
|
||||
|
||||
$store
|
||||
->expects($this->any())
|
||||
->method('exists')
|
||||
->with($key)
|
||||
->will($this->onConsecutiveCalls(true, false));
|
||||
|
||||
$this->assertTrue($lock->isAcquired());
|
||||
}
|
||||
|
||||
public function testRelease()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store, 10);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
->with($key);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('exists')
|
||||
->with($key)
|
||||
->willReturn(false);
|
||||
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
public function testReleaseOnDestruction()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store, 10);
|
||||
|
||||
$store
|
||||
->method('exists')
|
||||
->willReturnOnConsecutiveCalls(array(true, false))
|
||||
;
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
;
|
||||
|
||||
$lock->acquire(false);
|
||||
unset($lock);
|
||||
}
|
||||
|
||||
public function testNoAutoReleaseWhenNotConfigured()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store, 10, false);
|
||||
|
||||
$store
|
||||
->method('exists')
|
||||
->willReturnOnConsecutiveCalls(array(true, false))
|
||||
;
|
||||
$store
|
||||
->expects($this->never())
|
||||
->method('delete')
|
||||
;
|
||||
|
||||
$lock->acquire(false);
|
||||
unset($lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Lock\Exception\LockReleasingException
|
||||
*/
|
||||
public function testReleaseThrowsExceptionIfNotWellDeleted()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store, 10);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
->with($key);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('exists')
|
||||
->with($key)
|
||||
->willReturn(true);
|
||||
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Lock\Exception\LockReleasingException
|
||||
*/
|
||||
public function testReleaseThrowsAndLog()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store, 10, true);
|
||||
$lock->setLogger($logger);
|
||||
|
||||
$logger->expects($this->atLeastOnce())
|
||||
->method('notice')
|
||||
->with('Failed to release the "{resource}" lock.', array('resource' => $key));
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
->with($key);
|
||||
|
||||
$store
|
||||
->expects($this->once())
|
||||
->method('exists')
|
||||
->with($key)
|
||||
->willReturn(true);
|
||||
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideExpiredDates
|
||||
*/
|
||||
public function testExpiration($ttls, $expected)
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$lock = new Lock($key, $store, 10);
|
||||
|
||||
foreach ($ttls as $ttl) {
|
||||
if (null === $ttl) {
|
||||
$key->resetLifetime();
|
||||
} else {
|
||||
$key->reduceLifetime($ttl);
|
||||
}
|
||||
}
|
||||
$this->assertSame($expected, $lock->isExpired());
|
||||
}
|
||||
|
||||
public function provideExpiredDates()
|
||||
{
|
||||
yield array(array(-0.1), true);
|
||||
yield array(array(0.1, -0.1), true);
|
||||
yield array(array(-0.1, 0.1), true);
|
||||
|
||||
yield array(array(), false);
|
||||
yield array(array(0.1), false);
|
||||
yield array(array(-0.1, null), false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
use Symfony\Component\Lock\Store\RedisStore;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
abstract class AbstractRedisStoreTest extends AbstractStoreTest
|
||||
{
|
||||
use ExpiringStoreTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getClockDelay()
|
||||
{
|
||||
return 250000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a RedisConnection.
|
||||
*
|
||||
* @return \Redis|\RedisArray|\RedisCluster|\Predis\Client
|
||||
*/
|
||||
abstract protected function getRedisConnection();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStore()
|
||||
{
|
||||
return new RedisStore($this->getRedisConnection());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Lock\Exception\LockConflictedException;
|
||||
use Symfony\Component\Lock\Key;
|
||||
use Symfony\Component\Lock\StoreInterface;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
abstract class AbstractStoreTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @return StoreInterface
|
||||
*/
|
||||
abstract protected function getStore();
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$store = $this->getStore();
|
||||
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
$this->assertFalse($store->exists($key));
|
||||
$store->save($key);
|
||||
$this->assertTrue($store->exists($key));
|
||||
$store->delete($key);
|
||||
$this->assertFalse($store->exists($key));
|
||||
}
|
||||
|
||||
public function testSaveWithDifferentResources()
|
||||
{
|
||||
$store = $this->getStore();
|
||||
|
||||
$key1 = new Key(uniqid(__METHOD__, true));
|
||||
$key2 = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
$store->save($key1);
|
||||
$this->assertTrue($store->exists($key1));
|
||||
$this->assertFalse($store->exists($key2));
|
||||
|
||||
$store->save($key2);
|
||||
$this->assertTrue($store->exists($key1));
|
||||
$this->assertTrue($store->exists($key2));
|
||||
|
||||
$store->delete($key1);
|
||||
$this->assertFalse($store->exists($key1));
|
||||
$this->assertTrue($store->exists($key2));
|
||||
|
||||
$store->delete($key2);
|
||||
$this->assertFalse($store->exists($key1));
|
||||
$this->assertFalse($store->exists($key2));
|
||||
}
|
||||
|
||||
public function testSaveWithDifferentKeysOnSameResources()
|
||||
{
|
||||
$store = $this->getStore();
|
||||
|
||||
$resource = uniqid(__METHOD__, true);
|
||||
$key1 = new Key($resource);
|
||||
$key2 = new Key($resource);
|
||||
|
||||
$store->save($key1);
|
||||
$this->assertTrue($store->exists($key1));
|
||||
$this->assertFalse($store->exists($key2));
|
||||
|
||||
try {
|
||||
$store->save($key2);
|
||||
$this->fail('The store shouldn\'t save the second key');
|
||||
} catch (LockConflictedException $e) {
|
||||
}
|
||||
|
||||
// The failure of previous attempt should not impact the state of current locks
|
||||
$this->assertTrue($store->exists($key1));
|
||||
$this->assertFalse($store->exists($key2));
|
||||
|
||||
$store->delete($key1);
|
||||
$this->assertFalse($store->exists($key1));
|
||||
$this->assertFalse($store->exists($key2));
|
||||
|
||||
$store->save($key2);
|
||||
$this->assertFalse($store->exists($key1));
|
||||
$this->assertTrue($store->exists($key2));
|
||||
|
||||
$store->delete($key2);
|
||||
$this->assertFalse($store->exists($key1));
|
||||
$this->assertFalse($store->exists($key2));
|
||||
}
|
||||
|
||||
public function testSaveTwice()
|
||||
{
|
||||
$store = $this->getStore();
|
||||
|
||||
$resource = uniqid(__METHOD__, true);
|
||||
$key = new Key($resource);
|
||||
|
||||
$store->save($key);
|
||||
$store->save($key);
|
||||
// just asserts it don't throw an exception
|
||||
$this->addToAssertionCount(1);
|
||||
|
||||
$store->delete($key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
use Symfony\Component\Lock\Exception\LockConflictedException;
|
||||
use Symfony\Component\Lock\Key;
|
||||
use Symfony\Component\Lock\StoreInterface;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
trait BlockingStoreTestTrait
|
||||
{
|
||||
/**
|
||||
* @see AbstractStoreTest::getStore()
|
||||
*/
|
||||
abstract protected function getStore();
|
||||
|
||||
/**
|
||||
* Tests blocking locks thanks to pcntl.
|
||||
*
|
||||
* This test is time sensible: the $clockDelay could be adjust.
|
||||
*
|
||||
* @requires extension pcntl
|
||||
* @requires function pcntl_sigwaitinfo
|
||||
*/
|
||||
public function testBlockingLocks()
|
||||
{
|
||||
// Amount a microsecond used to order async actions
|
||||
$clockDelay = 50000;
|
||||
|
||||
if (\PHP_VERSION_ID < 50600 || defined('HHVM_VERSION_ID')) {
|
||||
$this->markTestSkipped('The PHP engine does not keep resource in child forks');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var StoreInterface $store */
|
||||
$store = $this->getStore();
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$parentPID = posix_getpid();
|
||||
|
||||
// Block SIGHUP signal
|
||||
pcntl_sigprocmask(SIG_BLOCK, array(SIGHUP));
|
||||
|
||||
if ($childPID = pcntl_fork()) {
|
||||
// Wait the start of the child
|
||||
pcntl_sigwaitinfo(array(SIGHUP), $info);
|
||||
|
||||
try {
|
||||
// This call should failed given the lock should already by acquired by the child
|
||||
$store->save($key);
|
||||
$this->fail('The store saves a locked key.');
|
||||
} catch (LockConflictedException $e) {
|
||||
}
|
||||
|
||||
// send the ready signal to the child
|
||||
posix_kill($childPID, SIGHUP);
|
||||
|
||||
// This call should be blocked by the child #1
|
||||
$store->waitAndSave($key);
|
||||
$this->assertTrue($store->exists($key));
|
||||
$store->delete($key);
|
||||
|
||||
// Now, assert the child process worked well
|
||||
pcntl_waitpid($childPID, $status1);
|
||||
$this->assertSame(0, pcntl_wexitstatus($status1), 'The child process couldn\'t lock the resource');
|
||||
} else {
|
||||
// Block SIGHUP signal
|
||||
pcntl_sigprocmask(SIG_BLOCK, array(SIGHUP));
|
||||
try {
|
||||
$store->save($key);
|
||||
// send the ready signal to the parent
|
||||
posix_kill($parentPID, SIGHUP);
|
||||
|
||||
// Wait for the parent to be ready
|
||||
pcntl_sigwaitinfo(array(SIGHUP), $info);
|
||||
|
||||
// Wait ClockDelay to let parent assert to finish
|
||||
usleep($clockDelay);
|
||||
$store->delete($key);
|
||||
exit(0);
|
||||
} catch (\Exception $e) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
use Symfony\Component\Lock\Exception\LockConflictedException;
|
||||
use Symfony\Component\Lock\Key;
|
||||
use Symfony\Component\Lock\Strategy\UnanimousStrategy;
|
||||
use Symfony\Component\Lock\Strategy\StrategyInterface;
|
||||
use Symfony\Component\Lock\Store\CombinedStore;
|
||||
use Symfony\Component\Lock\Store\RedisStore;
|
||||
use Symfony\Component\Lock\StoreInterface;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class CombinedStoreTest extends AbstractStoreTest
|
||||
{
|
||||
use ExpiringStoreTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getClockDelay()
|
||||
{
|
||||
return 250000;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStore()
|
||||
{
|
||||
$redis = new \Predis\Client('tcp://'.getenv('REDIS_HOST').':6379');
|
||||
try {
|
||||
$redis->connect();
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
|
||||
return new CombinedStore(array(new RedisStore($redis)), new UnanimousStrategy());
|
||||
}
|
||||
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $strategy;
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $store1;
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $store2;
|
||||
/** @var CombinedStore */
|
||||
private $store;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->strategy = $this->getMockBuilder(StrategyInterface::class)->getMock();
|
||||
$this->store1 = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$this->store2 = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
|
||||
$this->store = new CombinedStore(array($this->store1, $this->store2), $this->strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Lock\Exception\LockConflictedException
|
||||
*/
|
||||
public function testSaveThrowsExceptionOnFailure()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
$this->store1
|
||||
->expects($this->once())
|
||||
->method('save')
|
||||
->with($key)
|
||||
->willThrowException(new LockConflictedException());
|
||||
$this->store2
|
||||
->expects($this->once())
|
||||
->method('save')
|
||||
->with($key)
|
||||
->willThrowException(new LockConflictedException());
|
||||
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('canBeMet')
|
||||
->willReturn(true);
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('isMet')
|
||||
->willReturn(false);
|
||||
|
||||
$this->store->save($key);
|
||||
}
|
||||
|
||||
public function testSaveCleanupOnFailure()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
$this->store1
|
||||
->expects($this->once())
|
||||
->method('save')
|
||||
->with($key)
|
||||
->willThrowException(new LockConflictedException());
|
||||
$this->store2
|
||||
->expects($this->once())
|
||||
->method('save')
|
||||
->with($key)
|
||||
->willThrowException(new LockConflictedException());
|
||||
|
||||
$this->store1
|
||||
->expects($this->once())
|
||||
->method('delete');
|
||||
$this->store2
|
||||
->expects($this->once())
|
||||
->method('delete');
|
||||
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('canBeMet')
|
||||
->willReturn(true);
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('isMet')
|
||||
->willReturn(false);
|
||||
|
||||
try {
|
||||
$this->store->save($key);
|
||||
} catch (LockConflictedException $e) {
|
||||
// Catch the exception given this is not what we want to assert in this tests
|
||||
}
|
||||
}
|
||||
|
||||
public function testSaveAbortWhenStrategyCantBeMet()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
$this->store1
|
||||
->expects($this->once())
|
||||
->method('save')
|
||||
->with($key)
|
||||
->willThrowException(new LockConflictedException());
|
||||
$this->store2
|
||||
->expects($this->never())
|
||||
->method('save');
|
||||
|
||||
$this->strategy
|
||||
->expects($this->once())
|
||||
->method('canBeMet')
|
||||
->willReturn(false);
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('isMet')
|
||||
->willReturn(false);
|
||||
|
||||
try {
|
||||
$this->store->save($key);
|
||||
} catch (LockConflictedException $e) {
|
||||
// Catch the exception given this is not what we want to assert in this tests
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Lock\Exception\LockConflictedException
|
||||
*/
|
||||
public function testputOffExpirationThrowsExceptionOnFailure()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$ttl = random_int(1, 10);
|
||||
|
||||
$this->store1
|
||||
->expects($this->once())
|
||||
->method('putOffExpiration')
|
||||
->with($key, $this->lessThanOrEqual($ttl))
|
||||
->willThrowException(new LockConflictedException());
|
||||
$this->store2
|
||||
->expects($this->once())
|
||||
->method('putOffExpiration')
|
||||
->with($key, $this->lessThanOrEqual($ttl))
|
||||
->willThrowException(new LockConflictedException());
|
||||
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('canBeMet')
|
||||
->willReturn(true);
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('isMet')
|
||||
->willReturn(false);
|
||||
|
||||
$this->store->putOffExpiration($key, $ttl);
|
||||
}
|
||||
|
||||
public function testputOffExpirationCleanupOnFailure()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$ttl = random_int(1, 10);
|
||||
|
||||
$this->store1
|
||||
->expects($this->once())
|
||||
->method('putOffExpiration')
|
||||
->with($key, $this->lessThanOrEqual($ttl))
|
||||
->willThrowException(new LockConflictedException());
|
||||
$this->store2
|
||||
->expects($this->once())
|
||||
->method('putOffExpiration')
|
||||
->with($key, $this->lessThanOrEqual($ttl))
|
||||
->willThrowException(new LockConflictedException());
|
||||
|
||||
$this->store1
|
||||
->expects($this->once())
|
||||
->method('delete');
|
||||
$this->store2
|
||||
->expects($this->once())
|
||||
->method('delete');
|
||||
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('canBeMet')
|
||||
->willReturn(true);
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('isMet')
|
||||
->willReturn(false);
|
||||
|
||||
try {
|
||||
$this->store->putOffExpiration($key, $ttl);
|
||||
} catch (LockConflictedException $e) {
|
||||
// Catch the exception given this is not what we want to assert in this tests
|
||||
}
|
||||
}
|
||||
|
||||
public function testputOffExpirationAbortWhenStrategyCantBeMet()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$ttl = random_int(1, 10);
|
||||
|
||||
$this->store1
|
||||
->expects($this->once())
|
||||
->method('putOffExpiration')
|
||||
->with($key, $this->lessThanOrEqual($ttl))
|
||||
->willThrowException(new LockConflictedException());
|
||||
$this->store2
|
||||
->expects($this->never())
|
||||
->method('putOffExpiration');
|
||||
|
||||
$this->strategy
|
||||
->expects($this->once())
|
||||
->method('canBeMet')
|
||||
->willReturn(false);
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('isMet')
|
||||
->willReturn(false);
|
||||
|
||||
try {
|
||||
$this->store->putOffExpiration($key, $ttl);
|
||||
} catch (LockConflictedException $e) {
|
||||
// Catch the exception given this is not what we want to assert in this tests
|
||||
}
|
||||
}
|
||||
|
||||
public function testPutOffExpirationIgnoreNonExpiringStorage()
|
||||
{
|
||||
$store1 = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
$store2 = $this->getMockBuilder(StoreInterface::class)->getMock();
|
||||
|
||||
$store = new CombinedStore(array($store1, $store2), $this->strategy);
|
||||
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$ttl = random_int(1, 10);
|
||||
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('canBeMet')
|
||||
->willReturn(true);
|
||||
$this->strategy
|
||||
->expects($this->once())
|
||||
->method('isMet')
|
||||
->with(2, 2)
|
||||
->willReturn(true);
|
||||
|
||||
$store->putOffExpiration($key, $ttl);
|
||||
}
|
||||
|
||||
public function testExistsDontAskToEveryBody()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
$this->store1
|
||||
->expects($this->any())
|
||||
->method('exists')
|
||||
->with($key)
|
||||
->willReturn(false);
|
||||
$this->store2
|
||||
->expects($this->never())
|
||||
->method('exists');
|
||||
|
||||
$this->strategy
|
||||
->expects($this->any())
|
||||
->method('canBeMet')
|
||||
->willReturn(true);
|
||||
$this->strategy
|
||||
->expects($this->once())
|
||||
->method('isMet')
|
||||
->willReturn(true);
|
||||
|
||||
$this->assertTrue($this->store->exists($key));
|
||||
}
|
||||
|
||||
public function testExistsAbortWhenStrategyCantBeMet()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
$this->store1
|
||||
->expects($this->any())
|
||||
->method('exists')
|
||||
->with($key)
|
||||
->willReturn(false);
|
||||
$this->store2
|
||||
->expects($this->never())
|
||||
->method('exists');
|
||||
|
||||
$this->strategy
|
||||
->expects($this->once())
|
||||
->method('canBeMet')
|
||||
->willReturn(false);
|
||||
$this->strategy
|
||||
->expects($this->once())
|
||||
->method('isMet')
|
||||
->willReturn(false);
|
||||
|
||||
$this->assertFalse($this->store->exists($key));
|
||||
}
|
||||
|
||||
public function testDeleteDontStopOnFailure()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
$this->store1
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
->with($key)
|
||||
->willThrowException(new \Exception());
|
||||
$this->store2
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
->with($key);
|
||||
|
||||
$this->store->delete($key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
use Symfony\Component\Lock\Key;
|
||||
use Symfony\Component\Lock\StoreInterface;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
trait ExpiringStoreTestTrait
|
||||
{
|
||||
/**
|
||||
* Amount a microsecond used to order async actions.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
abstract protected function getClockDelay();
|
||||
|
||||
/**
|
||||
* @see AbstractStoreTest::getStore()
|
||||
*/
|
||||
abstract protected function getStore();
|
||||
|
||||
/**
|
||||
* Tests the store automatically delete the key when it expire.
|
||||
*
|
||||
* This test is time sensible: the $clockDelay could be adjust.
|
||||
*/
|
||||
public function testExpiration()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
$clockDelay = $this->getClockDelay();
|
||||
|
||||
/** @var StoreInterface $store */
|
||||
$store = $this->getStore();
|
||||
|
||||
$store->save($key);
|
||||
$store->putOffExpiration($key, $clockDelay / 1000000);
|
||||
$this->assertTrue($store->exists($key));
|
||||
|
||||
usleep(2 * $clockDelay);
|
||||
$this->assertFalse($store->exists($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the store thrown exception when TTL expires.
|
||||
*
|
||||
* @expectedException \Symfony\Component\Lock\Exception\LockExpiredException
|
||||
*/
|
||||
public function testAbortAfterExpiration()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
/** @var StoreInterface $store */
|
||||
$store = $this->getStore();
|
||||
|
||||
$store->save($key);
|
||||
$store->putOffExpiration($key, 1 / 1000000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the refresh can push the limits to the expiration.
|
||||
*
|
||||
* This test is time sensible: the $clockDelay could be adjust.
|
||||
*/
|
||||
public function testRefreshLock()
|
||||
{
|
||||
// Amount a microsecond used to order async actions
|
||||
$clockDelay = $this->getClockDelay();
|
||||
|
||||
// Amount a microsecond used to order async actions
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
/** @var StoreInterface $store */
|
||||
$store = $this->getStore();
|
||||
|
||||
$store->save($key);
|
||||
$store->putOffExpiration($key, $clockDelay / 1000000);
|
||||
$this->assertTrue($store->exists($key));
|
||||
|
||||
usleep(2 * $clockDelay);
|
||||
$this->assertFalse($store->exists($key));
|
||||
}
|
||||
|
||||
public function testSetExpiration()
|
||||
{
|
||||
$key = new Key(uniqid(__METHOD__, true));
|
||||
|
||||
/** @var StoreInterface $store */
|
||||
$store = $this->getStore();
|
||||
|
||||
$store->save($key);
|
||||
$store->putOffExpiration($key, 1);
|
||||
$this->assertGreaterThanOrEqual(0, $key->getRemainingLifetime());
|
||||
$this->assertLessThanOrEqual(1, $key->getRemainingLifetime());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
use Symfony\Component\Lock\Key;
|
||||
use Symfony\Component\Lock\Store\FlockStore;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class FlockStoreTest extends AbstractStoreTest
|
||||
{
|
||||
use BlockingStoreTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getStore()
|
||||
{
|
||||
return new FlockStore();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Lock\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage The directory "/a/b/c/d/e" is not writable.
|
||||
*/
|
||||
public function testConstructWhenRepositoryDoesNotExist()
|
||||
{
|
||||
if (!getenv('USER') || 'root' === getenv('USER')) {
|
||||
$this->markTestSkipped('This test will fail if run under superuser');
|
||||
}
|
||||
|
||||
new FlockStore('/a/b/c/d/e');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Lock\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage The directory "/" is not writable.
|
||||
*/
|
||||
public function testConstructWhenRepositoryIsNotWriteable()
|
||||
{
|
||||
if (!getenv('USER') || 'root' === getenv('USER')) {
|
||||
$this->markTestSkipped('This test will fail if run under superuser');
|
||||
}
|
||||
|
||||
new FlockStore('/');
|
||||
}
|
||||
|
||||
public function testSaveSanitizeName()
|
||||
{
|
||||
$store = $this->getStore();
|
||||
|
||||
$key = new Key('<?php echo "% hello word ! %" ?>');
|
||||
|
||||
$file = sprintf(
|
||||
'%s/sf.-php-echo-hello-word-.%s.lock',
|
||||
sys_get_temp_dir(),
|
||||
strtr(substr(base64_encode(hash('sha256', $key, true)), 0, 7), '/', '_')
|
||||
);
|
||||
// ensure the file does not exist before the store
|
||||
@unlink($file);
|
||||
|
||||
$store->save($key);
|
||||
|
||||
$this->assertFileExists($file);
|
||||
|
||||
$store->delete($key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
use Symfony\Component\Lock\Store\MemcachedStore;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @requires extension memcached
|
||||
*/
|
||||
class MemcachedStoreTest extends AbstractStoreTest
|
||||
{
|
||||
use ExpiringStoreTestTrait;
|
||||
|
||||
public static function setupBeforeClass()
|
||||
{
|
||||
$memcached = new \Memcached();
|
||||
$memcached->addServer(getenv('MEMCACHED_HOST'), 11211);
|
||||
$memcached->get('foo');
|
||||
$code = $memcached->getResultCode();
|
||||
|
||||
if (\Memcached::RES_SUCCESS !== $code && \Memcached::RES_NOTFOUND !== $code) {
|
||||
self::markTestSkipped('Unable to connect to the memcache host');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getClockDelay()
|
||||
{
|
||||
return 1000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getStore()
|
||||
{
|
||||
$memcached = new \Memcached();
|
||||
$memcached->addServer(getenv('MEMCACHED_HOST'), 11211);
|
||||
|
||||
return new MemcachedStore($memcached);
|
||||
}
|
||||
|
||||
public function testAbortAfterExpiration()
|
||||
{
|
||||
$this->markTestSkipped('Memcached expects a TTL greater than 1 sec. Simulating a slow network is too hard');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class PredisStoreTest extends AbstractRedisStoreTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
{
|
||||
$redis = new \Predis\Client('tcp://'.getenv('REDIS_HOST').':6379');
|
||||
try {
|
||||
$redis->connect();
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected function getRedisConnection()
|
||||
{
|
||||
$redis = new \Predis\Client('tcp://'.getenv('REDIS_HOST').':6379');
|
||||
$redis->connect();
|
||||
|
||||
return $redis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @requires extension redis
|
||||
*/
|
||||
class RedisArrayStoreTest extends AbstractRedisStoreTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
{
|
||||
if (!class_exists('RedisArray')) {
|
||||
self::markTestSkipped('The RedisArray class is required.');
|
||||
}
|
||||
if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) {
|
||||
$e = error_get_last();
|
||||
self::markTestSkipped($e['message']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getRedisConnection()
|
||||
{
|
||||
$redis = new \RedisArray(array(getenv('REDIS_HOST')));
|
||||
|
||||
return $redis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @requires extension redis
|
||||
*/
|
||||
class RedisStoreTest extends AbstractRedisStoreTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
{
|
||||
if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) {
|
||||
$e = error_get_last();
|
||||
self::markTestSkipped($e['message']);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getRedisConnection()
|
||||
{
|
||||
$redis = new \Redis();
|
||||
$redis->connect(getenv('REDIS_HOST'));
|
||||
|
||||
return $redis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
use Symfony\Component\Lock\Store\RedisStore;
|
||||
use Symfony\Component\Lock\Store\RetryTillSaveStore;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class RetryTillSaveStoreTest extends AbstractStoreTest
|
||||
{
|
||||
use BlockingStoreTestTrait;
|
||||
|
||||
public function getStore()
|
||||
{
|
||||
$redis = new \Predis\Client('tcp://'.getenv('REDIS_HOST').':6379');
|
||||
try {
|
||||
$redis->connect();
|
||||
} catch (\Exception $e) {
|
||||
self::markTestSkipped($e->getMessage());
|
||||
}
|
||||
|
||||
return new RetryTillSaveStore(new RedisStore($redis), 100, 100);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
use Symfony\Component\Lock\Store\SemaphoreStore;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @requires extension sysvsem
|
||||
*/
|
||||
class SemaphoreStoreTest extends AbstractStoreTest
|
||||
{
|
||||
use BlockingStoreTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getStore()
|
||||
{
|
||||
if (\PHP_VERSION_ID < 50601) {
|
||||
$this->markTestSkipped('Non blocking semaphore are supported by PHP version greater or equals than 5.6.1');
|
||||
}
|
||||
|
||||
return new SemaphoreStore();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Strategy;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Lock\Strategy\ConsensusStrategy;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class ConsensusStrategyTest extends TestCase
|
||||
{
|
||||
/** @var ConsensusStrategy */
|
||||
private $strategy;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->strategy = new ConsensusStrategy();
|
||||
}
|
||||
|
||||
public function provideMetResults()
|
||||
{
|
||||
// success, failure, total, isMet
|
||||
yield array(3, 0, 3, true);
|
||||
yield array(2, 1, 3, true);
|
||||
yield array(2, 0, 3, true);
|
||||
yield array(1, 2, 3, false);
|
||||
yield array(1, 1, 3, false);
|
||||
yield array(1, 0, 3, false);
|
||||
yield array(0, 3, 3, false);
|
||||
yield array(0, 2, 3, false);
|
||||
yield array(0, 1, 3, false);
|
||||
yield array(0, 0, 3, false);
|
||||
|
||||
yield array(2, 0, 2, true);
|
||||
yield array(1, 1, 2, false);
|
||||
yield array(1, 0, 2, false);
|
||||
yield array(0, 2, 2, false);
|
||||
yield array(0, 1, 2, false);
|
||||
yield array(0, 0, 2, false);
|
||||
}
|
||||
|
||||
public function provideIndeterminate()
|
||||
{
|
||||
// success, failure, total, canBeMet
|
||||
yield array(3, 0, 3, true);
|
||||
yield array(2, 1, 3, true);
|
||||
yield array(2, 0, 3, true);
|
||||
yield array(1, 2, 3, false);
|
||||
yield array(1, 1, 3, true);
|
||||
yield array(1, 0, 3, true);
|
||||
yield array(0, 3, 3, false);
|
||||
yield array(0, 2, 3, false);
|
||||
yield array(0, 1, 3, true);
|
||||
yield array(0, 0, 3, true);
|
||||
|
||||
yield array(2, 0, 2, true);
|
||||
yield array(1, 1, 2, false);
|
||||
yield array(1, 0, 2, true);
|
||||
yield array(0, 2, 2, false);
|
||||
yield array(0, 1, 2, false);
|
||||
yield array(0, 0, 2, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideMetResults
|
||||
*/
|
||||
public function testMet($success, $failure, $total, $isMet)
|
||||
{
|
||||
$this->assertSame($isMet, $this->strategy->isMet($success, $total));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIndeterminate
|
||||
*/
|
||||
public function testCanBeMet($success, $failure, $total, $isMet)
|
||||
{
|
||||
$this->assertSame($isMet, $this->strategy->canBeMet($failure, $total));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Strategy;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Lock\Strategy\UnanimousStrategy;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*/
|
||||
class UnanimousStrategyTest extends TestCase
|
||||
{
|
||||
/** @var UnanimousStrategy */
|
||||
private $strategy;
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->strategy = new UnanimousStrategy();
|
||||
}
|
||||
|
||||
public function provideMetResults()
|
||||
{
|
||||
// success, failure, total, isMet
|
||||
yield array(3, 0, 3, true);
|
||||
yield array(2, 1, 3, false);
|
||||
yield array(2, 0, 3, false);
|
||||
yield array(1, 2, 3, false);
|
||||
yield array(1, 1, 3, false);
|
||||
yield array(1, 0, 3, false);
|
||||
yield array(0, 3, 3, false);
|
||||
yield array(0, 2, 3, false);
|
||||
yield array(0, 1, 3, false);
|
||||
yield array(0, 0, 3, false);
|
||||
|
||||
yield array(2, 0, 2, true);
|
||||
yield array(1, 1, 2, false);
|
||||
yield array(1, 0, 2, false);
|
||||
yield array(0, 2, 2, false);
|
||||
yield array(0, 1, 2, false);
|
||||
yield array(0, 0, 2, false);
|
||||
}
|
||||
|
||||
public function provideIndeterminate()
|
||||
{
|
||||
// success, failure, total, canBeMet
|
||||
yield array(3, 0, 3, true);
|
||||
yield array(2, 1, 3, false);
|
||||
yield array(2, 0, 3, true);
|
||||
yield array(1, 2, 3, false);
|
||||
yield array(1, 1, 3, false);
|
||||
yield array(1, 0, 3, true);
|
||||
yield array(0, 3, 3, false);
|
||||
yield array(0, 2, 3, false);
|
||||
yield array(0, 1, 3, false);
|
||||
yield array(0, 0, 3, true);
|
||||
|
||||
yield array(2, 0, 2, true);
|
||||
yield array(1, 1, 2, false);
|
||||
yield array(1, 0, 2, true);
|
||||
yield array(0, 2, 2, false);
|
||||
yield array(0, 1, 2, false);
|
||||
yield array(0, 0, 2, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideMetResults
|
||||
*/
|
||||
public function testMet($success, $failure, $total, $isMet)
|
||||
{
|
||||
$this->assertSame($isMet, $this->strategy->isMet($success, $total));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIndeterminate
|
||||
*/
|
||||
public function testCanBeMet($success, $failure, $total, $isMet)
|
||||
{
|
||||
$this->assertSame($isMet, $this->strategy->canBeMet($failure, $total));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user