Browse Source

add unit tests from setPermissions to rmdir, minor fixes

olinox14 1 year ago
parent
commit
4cbe6b5fa1
3 changed files with 353 additions and 26 deletions
  1. 9 0
      TODO.txt
  2. 53 26
      src/Path.php
  3. 291 0
      tests/unit/PathTest.php

+ 9 - 0
TODO.txt

@@ -0,0 +1,9 @@
+- Finir les tests unitaires
+- ajouter une couverture de test
+- Faire un test en live sur linux
+- Déployer sur github
+- Mettre en place la CI avec travis
+- Maj le readme avec les badges tests et couverture
+- Créer la doc en ligne
+- Faire le guide des contribs
+- Rendre compatible avec windows / mac

+ 53 - 26
src/Path.php

@@ -142,29 +142,25 @@ class Path
         }
     }
 
-    /**
-     * TODO: en faire une méthode non statique et tester
-     * @noinspection SpellCheckingInspection
-     */
-    private static function _rrmdir(string $dir): void
+    protected function rrmdir(): bool
     {
-        if (!is_dir($dir)) {
-            return;
+        if (!is_dir($this->path)) {
+            return false;
         }
 
-        foreach (scandir($dir) as $object) {
+        foreach (scandir($this->path) as $object) {
             if ($object == "." || $object == "..") {
                 continue;
             }
 
-            if (is_dir($dir. DIRECTORY_SEPARATOR .$object) && !is_link($dir."/".$object)) {
-                self::_rrmdir($dir . DIRECTORY_SEPARATOR . $object);
+            if (is_dir($this->path. DIRECTORY_SEPARATOR .$object) && !is_link($this->path ."/".$object)) {
+                $this->rrmdir();
             }
             else {
-                unlink($dir . DIRECTORY_SEPARATOR . $object);
+                unlink($this->path . DIRECTORY_SEPARATOR . $object);
             }
         }
-        rmdir($dir);
+        return rmdir($this->path);
     }
 
     public function __construct(string|self $path)
@@ -768,7 +764,7 @@ class Path
         return (int)substr(sprintf('%o', $perms), -4);
     }
 
-    // TODO; add some more user-friendly methods to get permissions (read, write, exec...)
+    // TODO: add some more user-friendly methods to get permissions (read, write, exec...)
 
     /**
      * Changes the permissions of a file or directory.
@@ -776,6 +772,7 @@ class Path
      * @param int $permissions The new permissions to set. The value should be an octal number.
      * @return bool Returns true on success, false on failure.
      * @throws FileNotFoundException
+     * @throws IOException
      */
     public function setPermissions(int $permissions): bool
     {
@@ -783,7 +780,14 @@ class Path
             throw new FileNotFoundException("File or dir does not exist : " . $this->path);
         }
         $this->builtin->clearstatcache(); // TODO: check for a better way of dealing with PHP cache
-        return $this->builtin->chmod($this->path, $permissions);
+
+        $success = $this->builtin->chmod($this->path, $permissions);
+
+        if ($success === false) {
+            throw new IOException("Error while setting permissions on " . $this->path);
+        }
+
+        return true;
     }
 
     /**
@@ -793,16 +797,23 @@ class Path
      * @param string $group The new owner group name.
      * @return bool
      * @throws FileNotFoundException
+     * @throws IOException
      */
-    public function setOwner(string $user, string $group): bool
+    public function setOwner(string $user, string $group): void
     {
         if (!$this->isFile()) {
             throw new FileNotFoundException("File or dir does not exist : " . $this->path);
         }
+
         $this->builtin->clearstatcache(); // TODO: check for a better way of dealing with PHP cache
-        return
+
+        $success =
             $this->builtin->chown($this->path, $user) &&
             $this->builtin->chgrp($this->path, $group);
+
+        if ($success === false) {
+            throw new IOException("Error while setting owner of " . $this->path);
+        }
     }
 
     public function setATime()
@@ -856,14 +867,28 @@ class Path
      * Retrieves a list of files and directories that match a specified pattern.
      *
      * @param string $pattern The pattern to search for.
-     * @return Generator An iterable list of objects representing files and directories that match the pattern.
+     * @return array A list of files and directories that match the pattern.
+     * @throws FileNotFoundException
+     * @throws IOException
      */
-    public function glob(string $pattern): Generator
+    public function glob(string $pattern): array
     {
-        // TODO: concat $this->path and $pattern?
-        foreach ($this->builtin->glob($pattern) as $filename) {
-            yield new static($filename);
+        if (!$this->isDir()) {
+            throw new FileNotFoundException("Dir does not exist : " . $this->path);
+        }
+
+        $pattern = self::join($this->path, $pattern);
+
+        $result = $this->builtin->glob($pattern);
+
+        if ($result === false) {
+            throw new IOException("Error while getting blob on " . $this->path);
         }
+
+        return array_map(
+            function (string $s) { return new static($s); },
+            $result
+        );
     }
 
     public function remove()
@@ -879,17 +904,19 @@ class Path
      * Removes a directory and its contents recursively.
      *
      * @throws FileNotFoundException
+     * @throws IOException
      */
     public function rmdir(bool $recursive = false): void
     {
-        if (!$this->builtin->is_dir($this->path)) {
+        if (!$this->isDir()) {
             throw new FileNotFoundException("{$this->path} is not a directory");
         }
 
-        if ($recursive) {
-            self::_rrmdir($this->path);
-        } else {
-            $this->builtin->rmdir($this->path);
+        // TODO: maybe we could only rely on the recursive method?
+        $result = $recursive ? $this->rrmdir() : $this->builtin->rmdir($this->path);
+
+        if ($result === false) {
+            throw new IOException("Error while removing directory : " . $this->path);
         }
     }
 

+ 291 - 0
tests/unit/PathTest.php

@@ -20,6 +20,10 @@ class TestablePath extends Path {
     {
         return parent::cast($path);
     }
+
+    public function rrmdir(): bool {
+        return $this->rrmdir();
+    }
 }
 
 class PathTest extends TestCase
@@ -1703,4 +1707,291 @@ class PathTest extends TestCase
 
         $path->getPermissions();
     }
+
+    /**
+     * @throws FileNotFoundException|IOException
+     */
+    public function testSetPermissions(): void
+    {
+        $path = $this->getMock('/foo/file.ext', 'setPermissions');
+
+        $path->method('isFile')->willReturn(True);
+
+        $this->builtin
+            ->method('chmod')
+            ->with('/foo/file.ext', 0777)
+            ->willReturn(True);
+
+        $this->builtin
+            ->expects(self::once())
+            ->method('clearstatcache');
+
+        $this->assertTrue(
+            $path->setPermissions(0777)
+        );
+    }
+
+    /**
+     * @throws FileNotFoundException|IOException
+     */
+    public function testSetPermissionsFileDoesNotExist(): void
+    {
+        $path = $this->getMock('/foo/file.ext', 'setPermissions');
+
+        $path->method('isFile')->willReturn(False);
+
+        $this->builtin
+            ->expects(self::never())
+            ->method('clearstatcache');
+
+        $this->builtin
+            ->expects(self::never())
+            ->method('chmod');
+
+        $this->expectException(FileNotFoundException::class);
+
+        $path->setPermissions(0777);
+    }
+
+    /**
+     * @throws FileNotFoundException
+     */
+    public function testSetPermissionsWithError(): void
+    {
+        $path = $this->getMock('/foo/file.ext', 'setPermissions');
+
+        $path->method('isFile')->willReturn(True);
+
+        $this->builtin
+            ->expects(self::once())
+            ->method('clearstatcache');
+
+        $this->builtin
+            ->method('chmod')
+            ->with('/foo/file.ext', 0777)
+            ->willReturn(False);
+
+        $this->expectException(IOException::class);
+
+        $path->setPermissions(0777);
+    }
+
+    /**
+     * @throws FileNotFoundException
+     */
+    public function testSetOwner(): void
+    {
+        $path = $this->getMock('/foo/file.ext', 'setOwner');
+
+        $path->method('isFile')->willReturn(True);
+
+        $this->builtin
+            ->expects(self::once())
+            ->method('clearstatcache');
+
+        $this->builtin
+            ->method('chown')
+            ->with('/foo/file.ext', 'user')
+            ->willReturn(True);
+
+        $this->builtin
+            ->method('chgrp')
+            ->with('/foo/file.ext', 'group')
+            ->willReturn(True);
+
+        $path->setOwner('user', 'group');
+    }
+
+    /**
+     * @throws FileNotFoundException|IOException
+     */
+    public function testSetOwnerFileDoesNotExist(): void
+    {
+        $path = $this->getMock('/foo/file.ext', 'setOwner');
+
+        $path->method('isFile')->willReturn(False);
+
+        $this->builtin
+            ->expects(self::never())
+            ->method('clearstatcache');
+
+        $this->builtin
+            ->expects(self::never())
+            ->method('chown');
+
+        $this->builtin
+            ->expects(self::never())
+            ->method('chgrp');
+
+        $this->expectException(FileNotFoundException::class);
+
+        $path->setOwner('user', 'group');
+    }
+
+    /**
+     * @throws FileNotFoundException
+     */
+    public function testSetPermissionsWithChownError(): void
+    {
+        $path = $this->getMock('/foo/file.ext', 'setOwner');
+
+        $path->method('isFile')->willReturn(True);
+
+        $this->builtin
+            ->expects(self::once())
+            ->method('clearstatcache');
+
+        $this->builtin
+            ->method('chown')
+            ->with('/foo/file.ext', 'user')
+            ->willReturn(False);
+
+        $this->builtin
+            ->method('chgrp')
+            ->with('/foo/file.ext', 'group')
+            ->willReturn(True);
+
+        $this->expectException(IOException::class);
+
+        $path->setOwner('user', 'group');
+    }
+
+    /**
+     * @throws FileNotFoundException
+     */
+    public function testSetPermissionsWithChGroupError(): void
+    {
+        $path = $this->getMock('/foo/file.ext', 'setOwner');
+
+        $path->method('isFile')->willReturn(True);
+
+        $this->builtin
+            ->method('chown')
+            ->with('/foo/file.ext', 'user')
+            ->willReturn(True);
+
+        $this->builtin
+            ->method('chgrp')
+            ->with('/foo/file.ext', 'group')
+            ->willReturn(False);
+
+        $this->expectException(IOException::class);
+
+        $path->setOwner('user', 'group');
+    }
+
+    public function testExists(): void
+    {
+        $path = $this->getMock('/foo/file.ext', 'exists');
+
+        $this->builtin
+            ->expects(self::once())
+            ->method('file_exists')
+            ->with('/foo/file.ext')
+            ->willReturn(True);
+
+        $this->assertTrue(
+            $path->exists()
+        );
+    }
+
+    /**
+     * @throws IOException
+     * @throws FileNotFoundException
+     */
+    public function testGlob(): void {
+        $path = $this->getMock('/foo', 'glob');
+        $path->method('isDir')->willReturn(True);
+
+        $this->builtin
+            ->expects(self::once())
+            ->method('glob')
+            ->with('/foo/*')
+            ->willReturn(['a', 'b', 'c']);
+
+        $this->assertEquals(
+            ['a', 'b', 'c'],
+            array_map(
+                function (Path $p) { return (string)$p; },
+                $path->glob('*')
+            )
+        );
+    }
+
+    /**
+     * @throws IOException
+     */
+    public function testGlobDirDoesNotExist(): void {
+        $path = $this->getMock('/foo', 'glob');
+        $path->method('isDir')->willReturn(False);
+
+        $this->builtin
+            ->expects(self::never())
+            ->method('glob');
+
+        $this->expectException(FileNotFoundException::class);
+
+        $path->glob('*');
+    }
+
+    /**
+     * @throws FileNotFoundException
+     */
+    public function testGlobWithError(): void {
+        $path = $this->getMock('/foo', 'glob');
+        $path->method('isDir')->willReturn(True);
+
+        $this->builtin
+            ->expects(self::once())
+            ->method('glob')
+            ->with('/foo/*')
+            ->willReturn(False);
+
+        $this->expectException(IOException::class);
+
+        $path->glob('*');
+    }
+
+    /**
+     * @throws FileNotFoundException
+     * @throws IOException
+     */
+    public function testRmDirNonRecursive(): void
+    {
+        $path = $this->getMock('/foo', 'rmdir');
+
+        $path->method('isDir')->willReturn(True);
+
+        $this->builtin
+            ->method('rmdir')
+            ->with('/foo')
+            ->willReturn(True);
+
+        $path
+            ->expects(self::never())
+            ->method('rrmdir');
+
+        $path->rmdir();
+    }
+
+    /**
+     * @throws FileNotFoundException
+     * @throws IOException
+     */
+    public function testRmDirRecursive(): void
+    {
+        $path = $this->getMock('/foo', 'rmdir');
+
+        $path->method('isDir')->willReturn(True);
+
+        $this->builtin
+            ->expects(self::never())
+            ->method('rmdir');
+
+        $path
+            ->method('rrmdir')
+            ->willReturn(True);
+
+        $path->rmdir(True);
+    }
 }