[TASK] Improve Locker
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Locking / LockerTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Locking;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2010-2013 Christian Kuhn <lolli@schwarzbu.ch>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26 use TYPO3\CMS\Core\Locking\Locker;
27
28 /**
29 * Testcase for \TYPO3\CMS\Core\Locking\Locker
30 *
31 * @author Christian Kuhn <lolli@schwarzbu.ch>
32 */
33 class LockerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
34
35 ///////////////////////////////
36 // tests concerning __construct
37 ///////////////////////////////
38 /**
39 * @test
40 */
41 public function constructorUsesDefaultLockingMethodSimple() {
42 $instance = new Locker('999999999');
43 $this->assertSame(Locker::LOCKING_METHOD_SIMPLE, $instance->getMethod());
44 }
45
46 /**
47 * @test
48 */
49 public function constructorSetsMethodToGivenParameter() {
50 $instance = new Locker('999999999', Locker::LOCKING_METHOD_FLOCK);
51 $this->assertSame(Locker::LOCKING_METHOD_FLOCK, $instance->getMethod());
52 }
53
54 /**
55 * @test
56 * @expectedException \InvalidArgumentException
57 */
58 public function constructorThrowsExceptionForNotExistingLockingMethod() {
59 new Locker('999999999', 'foo');
60 }
61
62 /**
63 * @test
64 */
65 public function constructorFetchesInstallToolConfigurationIfEmptyMethod() {
66 $GLOBALS['TYPO3_CONF_VARS']['SYS']['lockingMode'] = Locker::LOCKING_METHOD_SIMPLE;
67 $instance = new Locker('999999999', '');
68 $this->assertSame(Locker::LOCKING_METHOD_SIMPLE, $instance->getMethod());
69 }
70
71 /**
72 * @test
73 */
74 public function constructorUsesDefaultValueForLoops() {
75 $instance = $this->getAccessibleMock('TYPO3\\CMS\\Core\\Locking\\Locker', array('dummy'), array('999999999', Locker::LOCKING_METHOD_DISABLED));
76 $this->assertSame(150, $instance->_get('loops'));
77 }
78
79 /**
80 * @test
81 */
82 public function constructorSetsLoopsToGivenNumberOfLoops() {
83 $instance = $this->getAccessibleMock('TYPO3\\CMS\\Core\\Locking\\Locker', array('dummy'), array('999999999', Locker::LOCKING_METHOD_DISABLED, 10));
84 $this->assertSame(10, $instance->_get('loops'));
85 }
86
87 /**
88 * @test
89 */
90 public function constructorUsesDefaultValueForSteps() {
91 $instance = $this->getAccessibleMock('TYPO3\\CMS\\Core\\Locking\\Locker', array('dummy'), array('999999999', Locker::LOCKING_METHOD_DISABLED));
92 $this->assertSame(200, $instance->_get('step'));
93 }
94
95 /**
96 * @test
97 */
98 public function constructorSetsStepToGivenNumberOfStep() {
99 $instance = $this->getAccessibleMock('TYPO3\\CMS\\Core\\Locking\\Locker', array('dummy'), array('999999999', Locker::LOCKING_METHOD_DISABLED, 0, 10));
100 $this->assertSame(10, $instance->_get('step'));
101 }
102
103 /**
104 * @test
105 */
106 public function constructorCreatesLockDirectoryIfNotExisting() {
107 \TYPO3\CMS\Core\Utility\GeneralUtility::rmdir(PATH_site . Locker::FILE_LOCK_FOLDER, TRUE);
108 new Locker('999999999', Locker::LOCKING_METHOD_SIMPLE);
109 $this->assertTrue(is_dir(PATH_site . Locker::FILE_LOCK_FOLDER));
110 }
111
112 /**
113 * @test
114 */
115 public function constructorSetsIdToMd5OfStringIfUsingSimpleLocking() {
116 $instance = new Locker('999999999', Locker::LOCKING_METHOD_SIMPLE);
117 $this->assertSame(md5('999999999'), $instance->getId());
118 }
119
120 /**
121 * @test
122 */
123 public function constructorSetsResourceToPathWithIdIfUsingSimpleLocking() {
124 $instance = new Locker('999999999', Locker::LOCKING_METHOD_SIMPLE);
125 $this->assertSame(PATH_site . Locker::FILE_LOCK_FOLDER . md5('999999999'), $instance->getResource());
126 }
127
128 /**
129 * @test
130 */
131 public function constructorSetsIdToAbsCrc32OfIdStringIfUsingSemaphoreLocking() {
132 if (!function_exists('sem_get')) {
133 $this->markTestSkipped('The system does not support semaphore base locking.');
134 }
135 $instance = new Locker('999999999', Locker::LOCKING_METHOD_SEMAPHORE);
136 $this->assertSame(abs(crc32('999999999')), $instance->getId());
137 }
138
139 ///////////////////////////////
140 // tests concerning acquire
141 ///////////////////////////////
142 /**
143 * @test
144 */
145 public function acquireFixesPermissionsOnLockFileIfUsingSimpleLogging() {
146 if (TYPO3_OS == 'WIN') {
147 $this->markTestSkipped('acquireFixesPermissionsOnLockFileIfUsingSimpleLogging() test not available on Windows.');
148 }
149 // Use a very high id to be unique
150 $instance = new Locker(999999999, Locker::LOCKING_METHOD_SIMPLE);
151 $instance->setEnableLogging(FALSE);
152 $pathOfLockFile = $instance->getResource();
153 $GLOBALS['TYPO3_CONF_VARS']['BE']['fileCreateMask'] = '0777';
154 // Acquire lock, get actual file permissions and clean up
155 $instance->acquireExclusiveLock();
156 clearstatcache();
157 $resultFilePermissions = substr(decoct(fileperms($pathOfLockFile)), 2);
158 $instance->release();
159 $this->assertEquals($resultFilePermissions, '0777');
160 }
161
162 ///////////////////////////////
163 // tests concerning release
164 ///////////////////////////////
165
166 /**
167 * @test
168 */
169 public function releaseRemovesLockfileInTypo3TempLocks() {
170 // Use a very high id to be unique
171 $instance = new Locker(999999999, Locker::LOCKING_METHOD_SIMPLE);
172 // Disable logging
173 $instance->setEnableLogging(FALSE);
174 // File pointer to current lock file
175 $lockFile = $instance->getResource();
176 $instance->acquireExclusiveLock();
177 $instance->release();
178 $this->assertFalse(is_file($lockFile));
179 }
180
181 /**
182 * Dataprovider for releaseDoesNotRemoveFilesNotWithinTypo3TempLocksDirectory
183 */
184 public function invalidFileReferences() {
185 return array(
186 'simple not within PATH_site' => array('simple', '/tmp/TYPO3-Lock-Test'),
187 'flock not withing PATH_site' => array('flock', '/tmp/TYPO3-Lock-Test'),
188 'simple directory traversal' => array('simple', PATH_site . 'typo3temp/../typo3temp/locks/foo'),
189 'flock directory traversal' => array('flock', PATH_site . 'typo3temp/../typo3temp/locks/foo'),
190 'simple directory traversal 2' => array('simple', PATH_site . 'typo3temp/locks/../locks/foo'),
191 'flock directory traversal 2' => array('flock', PATH_site . 'typo3temp/locks/../locks/foo'),
192 'simple within uploads' => array('simple', PATH_site . 'uploads/TYPO3-Lock-Test'),
193 'flock within uploads' => array('flock', PATH_site . 'uploads/TYPO3-Lock-Test')
194 );
195 }
196
197 /**
198 * @test
199 * @dataProvider invalidFileReferences
200 */
201 public function releaseDoesNotRemoveFilesNotWithinTypo3TempLocksDirectory($lockMethod, $file) {
202 if (TYPO3_OS === 'WIN') {
203 $this->markTestSkipped('releaseDoesNotRemoveFilesNotWithinTypo3TempLocksDirectory() test not available on Windows.');
204 }
205 // Create test file
206 touch($file);
207 if (!is_file($file)) {
208 $this->markTestSkipped('releaseDoesNotRemoveFilesNotWithinTypo3TempLocksDirectory() skipped: Test file could not be created');
209 }
210 // Create instance, set lockfile to invalid path
211 $instance = new Locker(999999999, $lockMethod);
212 $instance->setEnableLogging(FALSE);
213 $t3libLockReflection = new \ReflectionClass('TYPO3\\CMS\\Core\\Locking\\Locker');
214 $t3libLockReflectionResourceProperty = $t3libLockReflection->getProperty('resource');
215 $t3libLockReflectionResourceProperty->setAccessible(TRUE);
216 $t3libLockReflectionResourceProperty->setValue($instance, $file);
217 $t3libLockReflectionAcquiredProperty = $t3libLockReflection->getProperty('isAcquired');
218 $t3libLockReflectionAcquiredProperty->setAccessible(TRUE);
219 $t3libLockReflectionAcquiredProperty->setValue($instance, TRUE);
220 // Call release method
221 $instance->release();
222 // Check if file is still there and clean up
223 $fileExists = is_file($file);
224 if (is_file($file)) {
225 unlink($file);
226 }
227 $this->assertTrue($fileExists);
228 }
229
230 }