From 57ef984e959ce22a82b51940d236762b13f93679 Mon Sep 17 00:00:00 2001 From: Drak Date: Sat, 3 Dec 2011 15:27:39 +0545 Subject: [PATCH] [HttpFoundation] Added unit and functional testing session storage objects. --- .../SessionStorage/ArraySessionStorage.php | 100 +++++++++--- .../SessionStorage/MockFileSessionStorage.php | 153 ++++++++++++++++++ .../NativeFileSessionStorage.php | 64 ++++++++ .../ArraySessionStorageTest.php | 80 +++++++++ .../MockFileSessionStorageTest.php | 103 ++++++++++++ 5 files changed, 474 insertions(+), 26 deletions(-) create mode 100644 src/Symfony/Component/HttpFoundation/SessionStorage/MockFileSessionStorage.php create mode 100644 src/Symfony/Component/HttpFoundation/SessionStorage/NativeFileSessionStorage.php create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/ArraySessionStorageTest.php create mode 100644 tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/MockFileSessionStorageTest.php diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php index 5a6558a2b3..3d2f9741b0 100644 --- a/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/SessionStorage/ArraySessionStorage.php @@ -11,50 +11,56 @@ namespace Symfony\Component\HttpFoundation\SessionStorage; +use Symfony\Component\HttpFoundation\AttributeBagInterface; +use Symfony\Component\HttpFoundation\FlashBagInterface; + /** * ArraySessionStorage mocks the session for unit tests. * - * When doing functional testing, you should use FilesystemSessionStorage instead. + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle. + * + * When doing functional testing, you should use FileMockSessionStorage instead. * * @author Fabien Potencier * @author Bulat Shakirzyanov + * @author Drak */ - -class ArraySessionStorage implements SessionStorageInterface +class ArraySessionStorage extends AbstractSessionStorage { /** - * Storage data. - * + * @var string + */ + protected $sessionId; + + /** * @var array */ - private $data = array(); + private $attributes = array(); /** - * {@inheritdoc} + * @var array */ - public function read($key, $default = null) + private $flashes = array(); + + /** + * Injects array of attributes to simulate retrieval of existing session. + * + * @param array $array + */ + public function setAttributes(array $array) { - return array_key_exists($key, $this->data) ? $this->data[$key] : $default; + $this->attributes = $array; } /** - * {@inheritdoc} + * Injects array of flashes to simulate retrieval of existing session. + * + * @param array $array */ - public function regenerate($destroy = false) + public function setFlashes(array $array) { - if ($destroy) { - $this->data = array(); - } - - return true; - } - - /** - * {@inheritdoc} - */ - public function remove($key) - { - unset($this->data[$key]); + $this->flashes = $array; } /** @@ -62,6 +68,32 @@ class ArraySessionStorage implements SessionStorageInterface */ public function start() { + if ($this->started && !$this->closed) { + return true; + } + + $this->started = true; + $this->attributeBag->initialize($this->attributes); + $this->flashBag->initialize($this->flashes); + $this->sessionId = $this->generateSessionId(); + session_id($this->sessionId); + + return true; + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false) + { + if (!$this->started) { + $this->start(); + } + + $this->sessionId = $this->generateSessionId(); + session_id($this->sessionId); + + return true; } /** @@ -69,13 +101,29 @@ class ArraySessionStorage implements SessionStorageInterface */ public function getId() { + if (!$this->started) { + return ''; + } + + return $this->sessionId; } /** * {@inheritdoc} */ - public function write($key, $data) + public function save() { - $this->data[$key] = $data; + // nothing to do since we don't persist the session data + $this->closed = false; + } + + /** + * Generates a session ID. + * + * @return string + */ + protected function generateSessionId() + { + return sha1(uniqid(mt_rand(), true)); } } diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/MockFileSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/MockFileSessionStorage.php new file mode 100644 index 0000000000..f5f0999e83 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/SessionStorage/MockFileSessionStorage.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\SessionStorage; + +use Symfony\Component\HttpFoundation\AttributeBagInterface; +use Symfony\Component\HttpFoundation\FlashBagInterface; + +/** + * MockFileSessionStorage is used to mock sessions for + * functional testing when done in a single PHP process. + * + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle. + * + * @author Drak + */ +class MockFileSessionStorage extends ArraySessionStorage +{ + /** + * @var array + */ + private $sessionData = array(); + + /** + * @var string + */ + private $savePath; + + /** + * Constructor. + * + * @param string $savePath Path of directory to save session files. + * @param array $options Session options. + * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag) + * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag) + * + * @see AbstractSessionStorage::__construct() + */ + public function __construct($savePath = null, array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null) + { + if (is_null($savePath)) { + $savePath = sys_get_temp_dir(); + } + + if (!is_dir($savePath)) { + mkdir($savePath, 0777, true); + } + + $this->savePath = $savePath; + + parent::__construct($attributes, $flashes, $options); + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + if (!session_id()) { + session_id($this->generateSessionId()); + } + + $this->sessionId = session_id(); + + $this->read(); + + $this->started = true; + + return true; + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false) + { + if ($destroy) { + $this->destroy(); + } + + session_id($this->generateSessionId()); + $this->sessionId = session_id(); + + $this->save(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + if (!$this->started) { + return ''; + } + + return $this->sessionId; + } + + /** + * {@inheritdoc} + */ + public function save() + { + file_put_contents($this->getFilePath(), serialize($this->sessionData)); + } + + private function destroy() + { + if (is_file($this->getFilePath())) { + unlink($this->getFilePath()); + } + } + + /** + * Calculate path to file. + * + * @return string File path + */ + public function getFilePath() + { + return $this->savePath . '/' . $this->sessionId . '.sess'; + } + + private function read() + { + $filePath = $this->getFilePath(); + $this->sessionData = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array(); + + $key = $this->attributeBag->getStorageKey(); + $this->sessionData[$key] = isset($this->sessionData[$key]) ? $this->sessionData[$key] : array(); + $this->attributeBag->initialize($this->sessionData[$key]); + + $key = $this->flashBag->getStorageKey(); + $this->sessionData[$key] = isset($this->sessionData[$key]) ? $this->sessionData[$key] : array(); + $this->flashBag->initialize($this->sessionData[$key]); + } + +} diff --git a/src/Symfony/Component/HttpFoundation/SessionStorage/NativeFileSessionStorage.php b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeFileSessionStorage.php new file mode 100644 index 0000000000..9297e4b718 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/SessionStorage/NativeFileSessionStorage.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\SessionStorage; + +use Symfony\Component\HttpFoundation\AttributeBagInterface; +use Symfony\Component\HttpFoundation\FlashBagInterface; + +/** + * NativeFileSessionStorage. + * + * Native session handler using PHP's built in file storage. + * + * @author Drak + */ +class NativeFileSessionStorage extends AbstractSessionStorage +{ + /** + * @var string + */ + private $savePath; + + /** + * Constructor. + * + * @param string $savePath Path of directory to save session files. + * @param array $options Session configuration options. + * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag) + * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag) + * + * @see AbstractSessionStorage::__construct() + */ + public function __construct($savePath = null, array $options = array(), AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null) + { + if (is_null($savePath)) { + $savePath = sys_get_temp_dir(); + } + + if (!is_dir($savePath)) { + mkdir($savePath, 0777, true); + } + + $this->savePath = $savePath; + + parent::__construct($attributes, $flashes, $options); + } + + /** + * {@inheritdoc} + */ + protected function registerSaveHandlers() + { + ini_set('session.save_handlers', 'files'); + ini_set('session.save_path', $this->savePath); + } +} diff --git a/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/ArraySessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/ArraySessionStorageTest.php new file mode 100644 index 0000000000..924fe72594 --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/ArraySessionStorageTest.php @@ -0,0 +1,80 @@ + + */ +class ArraySessionStorageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ArraySessionStorage + */ + private $storage; + + /** + * @var array + */ + private $attributes; + + /** + * @var array + */ + private $flashes; + + protected function setUp() + { + $this->attributes = array('foo' => 'bar'); + $this->flashes = array('notice' => 'hello'); + $this->storage = new ArraySessionStorage(new AttributeBag(), new FlashBag()); + $this->storage->setFlashes($this->flashes); + $this->storage->setAttributes($this->attributes); + } + + protected function tearDown() + { + $this->flashes = null; + $this->attributes = null; + $this->storage = null; + } + + public function testStart() + { + $this->assertEquals('', $this->storage->getId()); + $this->storage->start(); + $id = $this->storage->getId(); + $this->assertNotEquals('', $id); + $this->storage->start(); + $this->assertEquals($id, $this->storage->getId()); + } + + public function testRegenerate() + { + $this->storage->start(); + $id = $this->storage->getId(); + $this->storage->regenerate(); + $this->assertNotEquals($id, $this->storage->getId()); + $this->assertEquals($this->attributes, $this->storage->getAttributes()->all()); + $this->assertEquals($this->flashes, $this->storage->getFlashes()->all()); + + $id = $this->storage->getId(); + $this->storage->regenerate(true); + $this->assertNotEquals($id, $this->storage->getId()); + $this->assertEquals($this->attributes, $this->storage->getAttributes()->all()); + $this->assertEquals($this->flashes, $this->storage->getFlashes()->all()); + } + + public function testGetId() + { + $this->assertEquals('', $this->storage->getId()); + $this->storage->start(); + $this->assertNotEquals('', $this->storage->getId()); + } +} diff --git a/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/MockFileSessionStorageTest.php b/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/MockFileSessionStorageTest.php new file mode 100644 index 0000000000..58f44ccf2e --- /dev/null +++ b/tests/Symfony/Tests/Component/HttpFoundation/SessionStorage/MockFileSessionStorageTest.php @@ -0,0 +1,103 @@ + + */ +class MockFileSessionStorageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var string + */ + private $sessionDir; + + /** + * @var FileMockSessionStorage + */ + protected $storage; + + protected function setUp() + { + $this->sessionDir = sys_get_temp_dir().'/sf2test'; + $this->storage = $this->getStorage(); + } + + protected function tearDown() + { + $this->sessionDir = null; + $this->storage = null; + array_map('unlink', glob($this->sessionDir.'/*.session')); + if (is_dir($this->sessionDir)) { + rmdir($this->sessionDir); + } + } + + public function testStart() + { + $this->assertEquals('', $this->storage->getId()); + $this->assertTrue($this->storage->start()); + $id = $this->storage->getId(); + $this->assertNotEquals('', $this->storage->getId()); + $this->assertTrue($this->storage->start()); + $this->assertEquals($id, $this->storage->getId()); + } + + public function testRegenerate() + { + $this->storage->start(); + $this->storage->getAttributes()->set('regenerate', 1234); + $this->storage->regenerate(); + $this->assertEquals(1234, $this->storage->getAttributes()->get('regenerate')); + $this->storage->regenerate(true); + $this->assertEquals(1234, $this->storage->getAttributes()->get('regenerate')); + } + + public function testGetId() + { + $this->assertEquals('', $this->storage->getId()); + $this->storage->start(); + $this->assertNotEquals('', $this->storage->getId()); + } + + public function testSave() + { + $this->storage->start(); + $this->assertNotEquals('108', $this->storage->getAttributes()->get('new')); + $this->assertFalse($this->storage->getFlashes()->has('newkey')); + $this->storage->getAttributes()->set('new', '108'); + $this->storage->getFlashes()->add('test', 'newkey'); + $this->storage->save(); + + $storage = $this->getStorage(); + $storage->start(); + $this->assertEquals('108', $storage->getAttributes()->get('new')); + $this->assertTrue($storage->getFlashes()->has('newkey')); + $this->assertEquals(array('test'), $storage->getFlashes()->get('newkey')); + } + + public function testMultipleInstances() + { + $storage1 = $this->getStorage(); + $storage1->start(); + $storage1->getAttributes()->set('foo', 'bar'); + $storage1->save(); + + $storage2 = $this->getStorage(); + $storage2->start(); + $this->assertEquals('bar', $storage2->getAttributes()->get('foo'), 'values persist between instances'); + } + + private function getStorage(array $options = array()) + { + return new MockFileSessionStorage($this->sessionDir, $options, new AttributeBag(), new FlashBag()); + } +}