Completed
Push — master ( 0bd31b...24de6a )
by Joschi
08:36
created

ObjectTest::testObjectFacadeRelativeInvalid()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
3
/**
4
 * apparat-object
5
 *
6
 * @category    Apparat
7
 * @package     Apparat\Object
8
 * @subpackage  Apparat\Object\Infrastructure
9
 * @author      Joschi Kuphal <[email protected]> / @jkphl
10
 * @copyright   Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
11
 * @license     http://opensource.org/licenses/MIT The MIT License (MIT)
12
 */
13
14
/***********************************************************************************
15
 *  The MIT License (MIT)
16
 *
17
 *  Copyright © 2016 Joschi Kuphal <[email protected]> / @jkphl
18
 *
19
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of
20
 *  this software and associated documentation files (the "Software"), to deal in
21
 *  the Software without restriction, including without limitation the rights to
22
 *  use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
23
 *  the Software, and to permit persons to whom the Software is furnished to do so,
24
 *  subject to the following conditions:
25
 *
26
 *  The above copyright notice and this permission notice shall be included in all
27
 *  copies or substantial portions of the Software.
28
 *
29
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
31
 *  FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
32
 *  COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
33
 *  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34
 *  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
 ***********************************************************************************/
36
37
namespace Apparat\Object\Tests {
38
39
    use Apparat\Kernel\Ports\Kernel;
40
    use Apparat\Object\Application\Factory\ObjectFactory;
41
    use Apparat\Object\Application\Model\Object\Article;
42
    use Apparat\Object\Domain\Model\Object\AbstractObject;
43
    use Apparat\Object\Domain\Model\Object\Id;
44
    use Apparat\Object\Domain\Model\Object\ResourceInterface;
45
    use Apparat\Object\Domain\Model\Object\Revision;
46
    use Apparat\Object\Domain\Model\Object\Type;
47
    use Apparat\Object\Domain\Model\Path\RepositoryPath;
48
    use Apparat\Object\Domain\Model\Properties\SystemProperties;
49
    use Apparat\Object\Domain\Repository\Repository;
50
    use Apparat\Object\Infrastructure\Repository\FileAdapterStrategy;
51
    use Apparat\Object\Ports\Object;
52
    use Apparat\Object\Ports\Repository as RepositoryFactory;
53
    use Prophecy\Prophecy\Revealer;
54
55
    /**
56
     * Object tests
57
     *
58
     * @package Apparat\Object
59
     * @subpackage Apparat\Object\Test
60
     */
61
    class ObjectTest extends AbstractRepositoryEnabledTest
62
    {
63
        /**
64
         * Example object path
65
         *
66
         * @var string
67
         */
68
        const OBJECT_PATH = '/2015/12/21/1-article/1';
69
        /**
70
         * Example hidden object path
71
         *
72
         * @var string
73
         */
74
        const HIDDEN_OBJECT_PATH = '/2016/05/26/6-article/6';
75
76
        /**
77
         * Tears down the fixture
78
         */
79
        public function tearDown()
80
        {
81
            putenv('MOCK_FLOCK');
82
            putenv('MOCK_RENAME');
83
            TestType::removeInvalidType();
84
            parent::tearDown();
85
        }
86
87
        /**
88
         * Test undefined object type
89
         *
90
         * @expectedException \Apparat\Object\Application\Factory\InvalidArgumentException
91
         * @expectedExceptionCode 1450905868
92
         */
93
        public function testUndefinedObjectType()
94
        {
95
            $resource = $this->createMock(ResourceInterface::class);
96
            $resource->method('getPropertyData')->willReturn([]);
97
            $repositoryPath = $this->getMockBuilder(RepositoryPath::class)->disableOriginalConstructor()->getMock();
98
99
            /** @var ResourceInterface $resource */
100
            /** @var RepositoryPath $repositoryPath */
101
            ObjectFactory::createFromResource($repositoryPath, $resource);
102
        }
103
104
        /**
105
         * Test invalid object type
106
         *
107
         * @expectedException \Apparat\Object\Domain\Model\Object\InvalidArgumentException
108
         * @expectedExceptionCode 1449871242
109
         */
110
        public function testInvalidObjectType()
111
        {
112
            $resource = $this->createMock(ResourceInterface::class);
113
            $resource->method('getPropertyData')->willReturn([SystemProperties::COLLECTION => ['type' => 'invalid']]);
114
            $articleObjectPath = new RepositoryPath(self::$repository, self::OBJECT_PATH);
115
116
            /** @var ResourceInterface $resource */
117
            ObjectFactory::createFromResource($articleObjectPath, $resource);
118
        }
119
120
        /**
121
         * Load an article object with an invalid visibility requirement
122
         *
123
         * @expectedException \Apparat\Object\Domain\Repository\InvalidArgumentException
124
         * @expectedExceptionCode 1449999646
125
         */
126
        public function testLoadArticleObjectInvalidVisibility()
127
        {
128
            $articleObjectPath = new RepositoryPath(self::$repository, self::OBJECT_PATH);
129
            self::$repository->loadObject($articleObjectPath, 0);
130
        }
131
132
        /**
133
         * Load an article object
134
         *
135
         * @expectedException \Apparat\Object\Domain\Model\Object\OutOfBoundsException
136
         * @expectedExceptionCode 1461619783
137
         */
138
        public function testLoadArticleObject()
139
        {
140
            $articleObjectPath = new RepositoryPath(self::$repository, self::OBJECT_PATH);
141
            $articleObject = self::$repository->loadObject($articleObjectPath);
142
            $this->assertEquals(
143
                getenv('APPARAT_BASE_URL').getenv('REPOSITORY_URL').self::OBJECT_PATH,
144
                $articleObject->getAbsoluteUrl()
145
            );
146
            $this->assertFalse($articleObject->isDeleted());
147
            $this->assertFalse($articleObject->getRepositoryPath()->isHidden());
148
149
            /** @var Revision $invalidRevision */
150
            $invalidRevision = Kernel::create(Revision::class, [99]);
151
            $articleObject->useRevision($invalidRevision);
152
        }
153
154
        /**
155
         * Load a hidden article object
156
         */
157
        public function testLoadHiddenArticleObject()
158
        {
159
            $articleObjectPath = new RepositoryPath(self::$repository, self::HIDDEN_OBJECT_PATH);
160
            $articleObject = self::$repository->loadObject($articleObjectPath);
161
            $this->assertTrue($articleObject->isDeleted());
162
            $this->assertTrue($articleObject->getRepositoryPath()->isHidden());
163
        }
164
165
        /**
166
         * Load an article object and test its system properties
167
         */
168
        public function testLoadArticleObjectSystemProperties()
169
        {
170
            $articleObjectPath = new RepositoryPath(self::$repository, self::OBJECT_PATH);
171
            $articleObject = self::$repository->loadObject($articleObjectPath);
172
            $this->assertInstanceOf(Article::class, $articleObject);
173
            $this->assertEquals(new Id(1), $articleObject->getId());
174
            $this->assertEquals(new Type(Type::ARTICLE), $articleObject->getType());
175
            $this->assertEquals(new Revision(1), $articleObject->getRevision());
176
            $this->assertFalse($articleObject->isDraft());
177
            $this->assertTrue($articleObject->isPublished());
178
            $this->assertEquals(new \DateTimeImmutable('2015-12-21T22:30:00'), $articleObject->getCreated());
179
            $this->assertEquals(new \DateTimeImmutable('2015-12-21T22:45:00'), $articleObject->getPublished());
180
            $this->assertNull($articleObject->getDeleted());
181
            $this->assertEquals('en', $articleObject->getLanguage());
182
            $this->assertEquals(
183
                "# Example article object\n\nThis file is an example for an object of type `\"article\"`. ".
184
                "It has a link to [Joschi Kuphal's website](https://jkphl.is) and features his avatar:\n".
185
                "![Joschi Kuphal](https://jkphl.is/avatar.jpg)",
186
                $articleObject->getPayload()
187
            );
188
        }
189
190
        /**
191
         * Load an article object and test its meta properties
192
         */
193
        public function testLoadArticleObjectMetaProperties()
194
        {
195
            $articleObjectPath = new RepositoryPath(self::$repository, self::OBJECT_PATH);
196
            $articleObject = self::$repository->loadObject($articleObjectPath);
197
            $this->assertInstanceOf(Article::class, $articleObject);
198
            $this->assertEquals('Example article object', $articleObject->getDescription());
199
            $this->assertEquals(
200
                'Article objects feature a Markdown payload along with some custom properties',
201
                $articleObject->getAbstract()
202
            );
203
            $this->assertArrayEquals(['apparat', 'object', 'example', 'article'], $articleObject->getKeywords());
204
            $this->assertArrayEquals(['example', 'text'], $articleObject->getCategories());
205
206
            // TODO Replace with contributed-by relations
207
//            $authorCount = count($articleObject->getAuthors());
208
//            $articleObject->addAuthor(AuthorFactory::createFromString(AuthorTest::GENERIC_AUTHOR));
209
//            $this->assertEquals($authorCount + 1, count($articleObject->getAuthors()));
210
        }
211
212
        /**
213
         * Load an article object and test its domain properties
214
         *
215
         * @expectedException \Apparat\Object\Domain\Model\Properties\InvalidArgumentException
216
         * @expectedExceptionCode 1450818168
217
         */
218
        public function testLoadArticleObjectDomainProperties()
219
        {
220
            $articleObjectPath = new RepositoryPath(self::$repository, self::OBJECT_PATH);
221
            $articleObject = self::$repository->loadObject($articleObjectPath);
222
            $this->assertEquals('/system/url', $articleObject->getDomainProperty('uid'));
223
            $this->assertEquals('value', $articleObject->getDomainProperty('group:single'));
224
            $articleObject->getDomainProperty('group:invalid');
225
        }
226
227
        /**
228
         * Load an article object and test an empty domain property name
229
         *
230
         * @expectedException \Apparat\Object\Domain\Model\Properties\InvalidArgumentException
231
         * @expectedExceptionCode 1450817720
232
         */
233
        public function testLoadArticleObjectDomainEmptyProperty()
234
        {
235
            $articleObjectPath = new RepositoryPath(self::$repository, self::OBJECT_PATH);
236
            $articleObject = self::$repository->loadObject($articleObjectPath);
237
            $articleObject->getDomainProperty('');
238
        }
239
240
        /**
241
         * Test the object facade with an absolute object URL
242
         */
243
        public function testObjectFacadeAbsolute()
244
        {
245
            $object = Object::instance(getenv('APPARAT_BASE_URL').getenv('REPOSITORY_URL').self::OBJECT_PATH);
246
            $this->assertInstanceOf(Article::class, $object);
247
        }
248
249
        /**
250
         * Test the object facade with a relative object URL
251
         */
252
        public function testObjectFacadeRelative()
253
        {
254
            $object = Object::instance(getenv('REPOSITORY_URL').self::OBJECT_PATH);
255
            $this->assertInstanceOf(Article::class, $object);
256
        }
257
258
        /**
259
         * Test the object facade with an invalid relative object URL
260
         *
261
         * @expectedException \Apparat\Resource\Ports\InvalidReaderArgumentException
262
         * @expectedExceptionCode 1447616824
263
         */
264
        public function testObjectFacadeRelativeInvalid()
265
        {
266
            $object = Object::instance(getenv('REPOSITORY_URL').'/2015/12/21/2-article/2');
267
            $this->assertInstanceOf(Article::class, $object);
268
        }
269
270
        /**
271
         * Test with a missing object type class
272
         *
273
         * @expectedException \Apparat\Object\Application\Factory\InvalidArgumentException
274
         * @expectedExceptionCode 1450824842
275
         */
276
        public function testInvalidObjectTypeClass()
277
        {
278
            TestType::addInvalidType();
279
280
            $resource = $this->createMock(ResourceInterface::class);
281
            $resource->method('getPropertyData')->willReturn([SystemProperties::COLLECTION => ['type' => 'invalid']]);
282
            $articleObjectPath = new RepositoryPath(self::$repository, '/2016/02/16/5-invalid/5');
283
284
            /** @var ResourceInterface $resource */
285
            ObjectFactory::createFromResource($articleObjectPath, $resource);
286
        }
287
288
        /**
289
         * Test instantiation of object with invalid domain properties collection
290
         *
291
         * @expectedException \Apparat\Object\Domain\Model\Properties\InvalidArgumentException
292
         * @expectedExceptionCode 1452288429
293
         */
294
        public function testInvalidDomainPropertyCollectionClass()
295
        {
296
            $this->getMockBuilder(AbstractObject::class)
297
                ->setConstructorArgs([new RepositoryPath(self::$repository, self::OBJECT_PATH)])
298
                ->getMock();
299
        }
300
301
        /**
302
         * Test the property data
303
         */
304
        public function testObjectPropertyData()
305
        {
306
//  $frontMarkResource = Resource::frontMark('file://'.__DIR__.DIRECTORY_SEPARATOR.'Fixture'.self::OBJECT_PATH.'.md');
307
            $object = Object::instance(getenv('REPOSITORY_URL').self::OBJECT_PATH);
308
            $this->assertTrue(is_array($object->getPropertyData()));
309
//        print_r($frontMarkResource->getData());
310
//        print_r($object->getPropertyData());
311
        }
312
313
        /**
314
         * Test mutation by altering metadata
315
         *
316
         * @expectedException \Apparat\Object\Domain\Model\Properties\OutOfBoundsException
317
         * @expectedExceptionCode 1462632083
318
         */
319
        public function testMetaDataMutation()
320
        {
321
            $object = Object::instance(getenv('REPOSITORY_URL').self::OBJECT_PATH);
322
            $this->assertTrue(is_array($object->getPropertyData()));
323
            $objectUrl = $object->getAbsoluteUrl();
324
            $objectRevision = $object->getRevision();
325
            $object->setTitle($object->getTitle().' (mutated)');
326
            $object->setSlug($object->getSlug().'-mutated');
327
            $object->setDescription($object->getDescription().' (mutated)');
328
            $object->setAbstract($object->getAbstract());
329
            $object->setLicense(ltrim($object->getLicense().', ', ', ').'MIT');
330
            $object->setKeywords(array_merge($object->getKeywords(), ['mutated']));
331
            $object->setCategories($object->getCategories());
332
            $this->assertEquals(preg_replace('%\/(.?+)$%', '/.$1-2', $objectUrl), $object->getAbsoluteUrl());
333
            $this->assertEquals($objectRevision->getRevision() + 1, $object->getRevision()->getRevision());
334
            $this->assertTrue($object->hasBeenModified());
335
            $this->assertTrue($object->hasBeenMutated());
336
            $this->assertEquals('MIT', $object->getLicense());
337
            $this->assertEquals(Object::PRIVACY_PRIVATE, $object->getPrivacy());
338
            $this->assertEquals(Object::PRIVACY_PUBLIC, $object->setPrivacy(Object::PRIVACY_PUBLIC)->getPrivacy());
339
            $object->setPrivacy('invalid');
340
        }
341
342
        /**
343
         * Test mutation by altering domain properties
344
         */
345
        public function testDomainPropertyMutation()
346
        {
347
            $object = Object::instance(getenv('REPOSITORY_URL').self::OBJECT_PATH);
348
            $this->assertTrue(is_array($object->getPropertyData()));
349
            $objectUrl = $object->getAbsoluteUrl();
350
            $objectRevision = $object->getRevision();
351
            $object->setDomainProperty('a:b:c', 'mutated');
352
            $this->assertEquals(preg_replace('%\/(.?+)$%', '/.$1-2', $objectUrl), $object->getAbsoluteUrl());
353
            $this->assertEquals($objectRevision->getRevision() + 1, $object->getRevision()->getRevision());
354
            $this->assertTrue($object->hasBeenModified());
355
            $this->assertTrue($object->hasBeenMutated());
356
        }
357
358
        /**
359
         * Test change by altering processing instructions
360
         */
361
        public function testProcessingInstructionChange()
362
        {
363
            $object = Object::instance(getenv('REPOSITORY_URL').self::OBJECT_PATH);
364
            $this->assertTrue(is_array($object->getPropertyData()));
365
            $objectUrl = $object->getAbsoluteUrl();
366
            $objectRevision = $object->getRevision();
367
            $object->setProcessingInstruction('css', 'other-style.css');
368
            $this->assertEquals($objectUrl, $object->getAbsoluteUrl());
369
            $this->assertEquals($objectRevision->getRevision(), $object->getRevision()->getRevision());
370
            $this->assertTrue($object->hasBeenModified());
371
            $this->assertFalse($object->hasBeenMutated());
372
        }
373
374
        /**
375
         * Test change by altering relations
376
         */
377
        public function testRelationChange()
378
        {
379
            // TODO: Implement
380
        }
381
382
        /**
383
         * Test to persist an earlier revision
384
         */
385
        public function testPersistEarlierRevision()
386
        {
387
            // TODO
388
        }
389
390
        /**
391
         * Test the creation and persisting of an article object with failing file lock
392
         *
393
         * @expectedException \Apparat\Object\Domain\Repository\RuntimeException
394
         * @expectedExceptionCode 1461406873
395
         */
396
        public function testCreateArticleObjectLockingImpossible()
397
        {
398
            putenv('MOCK_FLOCK=1');
399
            $this->testCreateAndPublishArticleObject();
400
        }
401
402
        /**
403
         * Test the creation and persisting of an article object
404
         *
405
         * @expectedException \Apparat\Object\Domain\Model\Object\RuntimeException
406
         * @expectedExceptionCode 1462124874
407
         */
408
        public function testCreateAndPublishArticleObject()
409
        {
410
            // Create a temporary repository & article
411
            $tempRepoDirectory = sys_get_temp_dir().DIRECTORY_SEPARATOR.'temp-repo';
412
            $payload = 'Revision 1 draft';
413
            $creationDate = new \DateTimeImmutable('yesterday');
414
            $article = $this->createRepositoryAndArticleObject($tempRepoDirectory, $payload, $creationDate);
415
            $this->assertInstanceOf(Article::class, $article);
416
            $this->assertEquals($payload, $article->getPayload());
417
            $this->assertFileExists($tempRepoDirectory.
418
                str_replace('/', DIRECTORY_SEPARATOR, $article->getRepositoryPath()
419
                    ->withExtension(getenv('OBJECT_RESOURCE_EXTENSION'))));
420
            $this->assertEquals($creationDate, $article->getCreated());
421
422
            // Alter and persist the object
423
            $article->setPayload('Revision 1 draft (updated)');
424
            $article->persist();
425
426
            // Publish and persist the first object revision
427
            $article->setPayload('Revision 1');
428
            $article->publish();
429
            $article->persist();
430
431
            // Draft a second object revision
432
            $article->setPayload('Revision 2 draft');
433
            $article->persist();
434
435
            // Publish and persist the second object revision
436
            $article->publish();
437
            $article->setPayload('Revision 2');
438
            $article->persist();
439
440
            // Modify and persist a third object draft revision
441
            $article->setPayload('Revision 3 draft');
442
            $article->persist();
443
444
            // Wait for 2 seconds, modify and re-persist the object
445
            $now = time();
446
            sleep(2);
447
            $article->setPayload('Revision 3 draft (delayed modification)');
448
            $article->persist();
449
            $this->assertGreaterThanOrEqual($now + 2, $article->getModified()->format('U'));
450
451
            // Iterate through all object revisions
452
            foreach ($article as $articleRevisionIndex => $articleRevision) {
453
                $this->assertInstanceOf(Article::class, $articleRevision);
454
                $this->assertInstanceOf(Revision::class, $articleRevisionIndex);
455
            }
456
457
            // Publish and persist a third object draft revision
458
            $article->publish()->persist();
459
460
            // Delete the object (and all it's revisions)
461
            $article->delete()->persist();
462
463
            // Undelete the object (and all it's revisions)
464
            $article->undelete()->persist();
465
466
            // Use the first revision
467
            $article->rewind();
468
469
            // Delete temporary repository
470
            $this->deleteRecursive($tempRepoDirectory);
471
472
            $article->persist();
473
        }
474
475
        /**
476
         * Test the creation and persisting of an article object with failing file lock
477
         *
478
         * @expectedException \Apparat\Object\Infrastructure\Repository\RuntimeException
479
         * @expectedExceptionCode 1464269155
480
         */
481
        public function testDeleteArticleObjectImpossible()
482
        {
483
            putenv('MOCK_RENAME=1');
484
            $this->tmpFiles[] = $tempRepoDirectory = sys_get_temp_dir().DIRECTORY_SEPARATOR.'temp-repo';
485
            $article = $this->createRepositoryAndArticleObject($tempRepoDirectory, 'Revision 1 draft');
486
            $this->deleteRecursive($tempRepoDirectory);
487
            $article->delete()->persist();
488
        }
489
490
        /**
491
         * Test the creation and persisting of an article object with failing file lock
492
         *
493
         * @expectedException \Apparat\Object\Infrastructure\Repository\RuntimeException
494
         * @expectedExceptionCode 1464269179
495
         */
496
        public function testUndeleteArticleObjectImpossible()
497
        {
498
            $this->tmpFiles[] = $tempRepoDirectory = sys_get_temp_dir().DIRECTORY_SEPARATOR.'temp-repo';
499
            $article = $this->createRepositoryAndArticleObject($tempRepoDirectory, 'Revision 1 draft');
500
            $article->getRepositoryPath()->getRepository()->deleteObject($article);
501
            $this->deleteRecursive($tempRepoDirectory);
502
            putenv('MOCK_RENAME=1');
503
            $article->undelete()->persist();
504
        }
505
506
        /**
507
         * Create a temporary repository and article object
508
         *
509
         * @param string $tempRepoDirectory Repository directory
510
         * @param string $payload Article payload
511
         * @param \DateTimeInterface $creationDate Article creation date
512
         * @return Article Article object
513
         */
514
        protected function createRepositoryAndArticleObject(
515
            $tempRepoDirectory,
516
            $payload,
517
            \DateTimeInterface $creationDate = null
518
        ) {
519
            $fileRepository = RepositoryFactory::create(
520
                getenv('REPOSITORY_URL'),
521
                [
522
                    'type' => FileAdapterStrategy::TYPE,
523
                    'root' => $tempRepoDirectory,
524
                ]
525
            );
526
            $this->assertInstanceOf(Repository::class, $fileRepository);
527
            $this->assertEquals($fileRepository->getAdapterStrategy()->getRepositorySize(), 0);
528
529
            // Create a new article in the temporary repository
530
            return $fileRepository->createObject(Type::ARTICLE, $payload, [], $creationDate);
531
        }
532
533
        /**
534
         * Recursively register a directory and all nested files and directories for deletion on teardown
535
         *
536
         * @param string $directory Directory
537
         */
538
        protected function deleteRecursive($directory)
539
        {
540
            $this->tmpFiles[] = $directory;
541
            foreach (scandir($directory) as $item) {
542
                if (!preg_match('%^\.+$%', $item)) {
543
                    $path = $directory.DIRECTORY_SEPARATOR.$item;
544
                    if (is_dir($path)) {
545
                        $this->deleteRecursive($path);
546
                        continue;
547
                    }
548
549
                    $this->tmpFiles[] = $path;
550
                }
551
            }
552
        }
553
    }
554
}
555
556
namespace Apparat\Object\Infrastructure\Repository {
557
558
    /**
559
     * Mocked version of the native flock() function
560
     *
561
     * @param resource $handle An open file pointer.
562
     * @param int $operation Operation is one of the following: LOCK_SH to acquire a shared lock (reader).
563
     * @param int $wouldblock The optional third argument is set to true if the lock would block (EWOULDBLOCK errno
564
     *     condition).
565
     * @return bool True on success or False on failure.
566
     */
567
    function flock($handle, $operation, &$wouldblock = null)
568
    {
569
        return (getenv('MOCK_FLOCK') != 1) ? \flock($handle, $operation, $wouldblock) : false;
570
    }
571
572
    /**
573
     * Mocked version of the native rename() function
574
     *
575
     * @param string $oldname The old name. The wrapper used in oldname must match the wrapper used in newname.
576
     * @param @param string $newname The new name.
577
     * @return bool true on success or false on failure.
578
     */
579
    function rename($oldname, $newname)
580
    {
581
        return (getenv('MOCK_RENAME') != 1) ? \rename($oldname, $newname) : false;
582
    }
583
}
584