Revert "composer package update"

This reverts commit e979959219e95fc6435ad40f7e60b602f8bbd208.
This commit is contained in:
2020-01-28 00:23:38 +09:00
parent ed69eb5d7b
commit 9cd40b213d
421 changed files with 16273 additions and 14007 deletions
+36
View File
@@ -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);
}
}
+268
View File
@@ -0,0 +1,268 @@
<?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 testRefreshCustom()
{
$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, 20);
$lock->refresh(20);
}
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());
}
}
+115
View File
@@ -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,92 @@
<?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 extension posix
* @requires function pcntl_sigwaitinfo
*/
public function testBlockingLocks()
{
// Amount a microsecond used to order async actions
$clockDelay = 50000;
/** @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);
}
}
}
}
+356
View File
@@ -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());
}
}
+78
View File
@@ -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);
}
}
+60
View File
@@ -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');
}
}
+36
View File
@@ -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;
}
}
+38
View File
@@ -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;
}
}
+36
View File
@@ -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);
}
}
+60
View File
@@ -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\Key;
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()
{
return new SemaphoreStore();
}
public function testResourceRemoval()
{
$initialCount = $this->getOpenedSemaphores();
$store = new SemaphoreStore();
$key = new Key(uniqid(__METHOD__, true));
$store->waitAndSave($key);
$this->assertGreaterThan($initialCount, $this->getOpenedSemaphores(), 'Semaphores should have been created');
$store->delete($key);
$this->assertEquals($initialCount, $this->getOpenedSemaphores(), 'All semaphores should be removed');
}
private function getOpenedSemaphores()
{
$lines = explode(PHP_EOL, trim(`ipcs -su`));
if ('------ Semaphore Status --------' !== $lines[0]) {
throw new \Exception('Failed to extract list of opend semaphores. Expect a Semaphore status, got '.implode(PHP_EOL, $lines));
}
list($key, $value) = explode(' = ', $lines[1]);
if ('used arrays' !== $key) {
throw new \Exception('Failed to extract list of opend semaphores. Expect a used arrays key, got '.implode(PHP_EOL, $lines));
}
return (int) $value;
}
}
@@ -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));
}
}