Completed
Push — master ( a8b991...dda2b8 )
by Marco
06:05 queued 02:56
created

FileCacheTest::getKeyAndPathFittingLength()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 29
rs 8.8571
cc 1
eloc 21
nc 1
nop 1
1
<?php
2
3
namespace Doctrine\Tests\Common\Cache;
4
5
use Doctrine\Common\Cache\Cache;
6
7
/**
8
 * @group DCOM-101
9
 */
10
class FileCacheTest extends \Doctrine\Tests\DoctrineTestCase
11
{
12
    /**
13
     * @var \Doctrine\Common\Cache\FileCache
14
     */
15
    private $driver;
16
17
    protected function setUp()
18
    {
19
        $this->driver = $this->getMock(
20
            'Doctrine\Common\Cache\FileCache',
21
            array('doFetch', 'doContains', 'doSave'),
22
            array(), '', false
23
        );
24
    }
25
26
    public function testFilenameShouldCreateThePathWithOneSubDirectory()
27
    {
28
        $cache          = $this->driver;
29
        $method         = new \ReflectionMethod($cache, 'getFilename');
30
        $key            = 'item-key';
31
        $expectedDir    = array(
32
            '84',
33
        );
34
        $expectedDir    = implode(DIRECTORY_SEPARATOR, $expectedDir);
35
36
        $method->setAccessible(true);
37
38
        $path       = $method->invoke($cache, $key);
39
        $dirname    = pathinfo($path, PATHINFO_DIRNAME);
40
41
        $this->assertEquals(DIRECTORY_SEPARATOR . $expectedDir, $dirname);
42
    }
43
44
    public function testFileExtensionCorrectlyEscaped()
45
    {
46
        $driver1 = $this->getMock(
47
            'Doctrine\Common\Cache\FileCache',
48
            array('doFetch', 'doContains', 'doSave'),
49
            array(__DIR__, '.*')
50
        );
51
        $driver2 = $this->getMock(
52
            'Doctrine\Common\Cache\FileCache',
53
            array('doFetch', 'doContains', 'doSave'),
54
            array(__DIR__, '.php')
55
        );
56
57
        $doGetStats = new \ReflectionMethod($driver1, 'doGetStats');
58
59
        $doGetStats->setAccessible(true);
60
61
        $stats1 = $doGetStats->invoke($driver1);
62
        $stats2 = $doGetStats->invoke($driver2);
63
64
        $this->assertSame(0, $stats1[Cache::STATS_MEMORY_USAGE]);
65
        $this->assertGreaterThan(0, $stats2[Cache::STATS_MEMORY_USAGE]);
66
    }
67
68
    /**
69
     * @group DCOM-266
70
     */
71
    public function testFileExtensionSlashCorrectlyEscaped()
72
    {
73
        $driver = $this->getMock(
74
            'Doctrine\Common\Cache\FileCache',
75
            array('doFetch', 'doContains', 'doSave'),
76
            array(__DIR__ . '/../', DIRECTORY_SEPARATOR . basename(__FILE__))
77
        );
78
79
        $doGetStats = new \ReflectionMethod($driver, 'doGetStats');
80
81
        $doGetStats->setAccessible(true);
82
83
        $stats = $doGetStats->invoke($driver);
84
85
        $this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_USAGE]);
86
    }
87
88
    public function testNonIntUmaskThrowsInvalidArgumentException()
89
    {
90
        $this->setExpectedException('InvalidArgumentException');
91
92
        $this->getMock(
93
            'Doctrine\Common\Cache\FileCache',
94
            array('doFetch', 'doContains', 'doSave'),
95
            array('', '', 'invalid')
96
        );
97
    }
98
99
    public function testGetDirectoryReturnsRealpathDirectoryString()
100
    {
101
        $directory = __DIR__ . '/../';
102
        $driver = $this->getMock(
103
            'Doctrine\Common\Cache\FileCache',
104
            array('doFetch', 'doContains', 'doSave'),
105
            array($directory)
106
        );
107
108
        $doGetDirectory = new \ReflectionMethod($driver, 'getDirectory');
109
110
        $actualDirectory = $doGetDirectory->invoke($driver);
111
        $expectedDirectory = realpath($directory);
112
113
        $this->assertEquals($expectedDirectory, $actualDirectory);
114
    }
115
116
    public function testGetExtensionReturnsExtensionString()
117
    {
118
        $directory = __DIR__ . '/../';
119
        $extension = DIRECTORY_SEPARATOR . basename(__FILE__);
120
        $driver = $this->getMock(
121
            'Doctrine\Common\Cache\FileCache',
122
            array('doFetch', 'doContains', 'doSave'),
123
            array($directory, $extension)
124
        );
125
126
        $doGetExtension = new \ReflectionMethod($driver, 'getExtension');
127
128
        $actualExtension = $doGetExtension->invoke($driver);
129
130
        $this->assertEquals($extension, $actualExtension);
131
    }
132
133
    const WIN_MAX_PATH_LEN = 258;
134
135
    public static function getBasePathForWindowsPathLengthTests($pathLength)
136
    {
137
        // Not using __DIR__ because it can get screwed up when xdebug debugger is attached.
138
        $basePath = realpath(sys_get_temp_dir());
139
140
        // Test whether the desired path length is odd or even.
141
        $desiredPathLengthIsOdd = ($pathLength % 2) == 1;
142
143
        // If the cache key is not too long, the filecache codepath will add
144
        // a slash and bin2hex($key). The length of the added portion will be an odd number.
145
        // len(desired) = len(base path) + len(slash . bin2hex($key))
146
        //          odd = even           + odd
147
        //         even = odd            + odd
148
        $basePathLengthShouldBeOdd = !$desiredPathLengthIsOdd;
149
150
        $basePathLengthIsOdd = (strlen($basePath) % 2) == 1;
151
152
        // If the base path needs to be odd or even where it is not, we add an odd number of
153
        // characters as a pad. In this case, we're adding '\aa' (or '/aa' depending on platform)
154
        // This is all to make it so that the key we're testing would result in
155
        // a path that is exactly the length we want to test IF the path length limit
156
        // were not in place in FileCache.
157
        if ($basePathLengthIsOdd != $basePathLengthShouldBeOdd) {
158
            $basePath .= DIRECTORY_SEPARATOR . "aa";
159
        }
160
161
        return $basePath;
162
    }
163
164
    public static function getKeyAndPathFittingLength($length)
165
    {
166
        $basePath = self::getBasePathForWindowsPathLengthTests($length);
167
168
        $baseDirLength = strlen($basePath);
169
        $extensionLength = strlen('.doctrine.cache');
170
        $directoryLength = strlen(DIRECTORY_SEPARATOR . 'aa' . DIRECTORY_SEPARATOR);
171
        $keyLength = $length - ($baseDirLength + $extensionLength + $directoryLength); // - 1 because of slash
172
173
        $key = str_repeat('a', floor($keyLength / 2));
174
175
        $keyHash = hash('sha256', $key);
176
177
        $keyPath = $basePath
178
            . DIRECTORY_SEPARATOR
179
            . substr($keyHash, 0, 2)
180
            . DIRECTORY_SEPARATOR
181
            . bin2hex($key)
182
            . '.doctrine.cache';
183
184
        $hashedKeyPath = $basePath
185
            . DIRECTORY_SEPARATOR
186
            . substr($keyHash, 0, 2)
187
            . DIRECTORY_SEPARATOR
188
            . '_' . $keyHash
189
            . '.doctrine.cache';
190
191
        return array($key, $keyPath, $hashedKeyPath);
192
    }
193
194
    public function getPathLengthsToTest()
195
    {
196
        // Windows officially supports 260 bytes including null terminator
197
        // 259 characters is too large due to PHP bug (https://bugs.php.net/bug.php?id=70943)
198
        // 260 characters is too large - null terminator is included in allowable length
199
        return array(
200
            array(257, false),
201
            array(258, false),
202
            array(259, true),
203
            array(260, true)
204
        );
205
    }
206
207
    /**
208
     * @runInSeparateProcess
209
     * @dataProvider getPathLengthsToTest
210
     *
211
     * @covers \Doctrine\Common\Cache\FileCache::getFilename
212
     */
213
    public function testWindowsPathLengthLimitationsAreCorrectlyRespected($length, $pathShouldBeHashed)
214
    {
215
        if (! defined('PHP_WINDOWS_VERSION_BUILD')) {
216
            define('PHP_WINDOWS_VERSION_BUILD', 'Yes, this is the "usual suspect", with the usual limitations');
217
        }
218
219
        $basePath = $this->getBasePathForWindowsPathLengthTests($length);
220
221
        $fileCache = $this->getMockForAbstractClass(
222
            'Doctrine\Common\Cache\FileCache',
223
            array($basePath, '.doctrine.cache')
224
        );
225
226
        list($key, $keyPath, $hashedKeyPath) = $this->getKeyAndPathFittingLength($length);
227
228
        $getFileName = new \ReflectionMethod($fileCache, 'getFilename');
229
230
        $getFileName->setAccessible(true);
231
232
        $this->assertEquals(
233
            $length,
234
            strlen($keyPath),
235
            sprintf('Path expected to be %d characters long is %d characters long', $length, strlen($keyPath))
236
        );
237
238
        if ($pathShouldBeHashed) {
239
            $keyPath = $hashedKeyPath;
240
        }
241
242
        if ($pathShouldBeHashed) {
243
            $this->assertSame(
244
                $hashedKeyPath,
245
                $getFileName->invoke($fileCache, $key),
246
                'Keys should be hashed correctly if they are over the limit.'
247
            );
248
        } else {
249
            $this->assertSame(
250
                $keyPath,
251
                $getFileName->invoke($fileCache, $key),
252
                'Keys below limit of the allowed length are used directly, unhashed'
253
            );
254
        }
255
    }
256
}
257