Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding file matchers #73

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ Available Matchers
------------------
* [Array](../master/README.md#array)
* [Collection](../master/README.md#collection)
* [Core](../master/README.md#core)
* [File](../master/README.md#file)
* [Object](../master/README.md#object)
* [Numbers](../master/README.md#numbers)
* [Type checking](../master/README.md#type-checking)
Expand Down Expand Up @@ -227,6 +229,26 @@ assertThat([2, 4, 6], hasItem(equalTo(2)));
assertThat([1, 3, 5], hasItems(equalTo(1), equalTo(3)));
```

### File

All file matchers accepts `\SplFileInfo` objects or `string` paths only.

* `anExistingDirectory` - evaluates to true if the file exists and is a directory
```php
$directory = new \SplFileInfo('/var/log');
assertThat($directory, anExistingDirectory());

assertThat('/var/log', anExistingDirectory());
```

* `anExistingFile` - evaluates to true if the file exists and is a regular file
```php
$file = new \SplFileInfo('/var/log/php-fpm.log');
assertThat($file, anExistingFile());

assertThat('/var/log/php-fpm.log', anExistingFile());
```

### Object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The aFileWithAbsolutePath matcher wasn't implemented.


* `hasToString` - check `__toString` or `toString` method
Expand Down
26 changes: 26 additions & 0 deletions hamcrest/Hamcrest.php
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,32 @@ function notSet($property)
}
}

if (!function_exists('anExistingDirectory')) {
aik099 marked this conversation as resolved.
Show resolved Hide resolved
/**
* Evaluates to true if the file exists and is a directory.
* Accepts only <code>\SplFileInfo</code> objects or <code>string</code> paths.
*
* @return \Hamcrest\File\IsExistingDirectory
*/
function anExistingDirectory()
{
return \Hamcrest\File\IsExistingDirectory::anExistingDirectory();
}
}

if (!function_exists('anExistingFile')) {
/**
* Evaluates to true if the file exists and is a regular file.
* Accepts only <code>\SplFileInfo</code> objects or <code>string</code> paths.
*
* @return \Hamcrest\File\IsExistingFile
*/
function anExistingFile()
{
return \Hamcrest\File\IsExistingFile::anExistingFile();
}
}

if (!function_exists('closeTo')) {
/**
* Matches if value is a number equal to $value within some range of
Expand Down
76 changes: 76 additions & 0 deletions hamcrest/Hamcrest/File/FileMatcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

declare(strict_types=1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this and don't use non-PHP 5.3+ (from composer.json) incompatible code.


I haven't seen this in any of the other files.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I saw that the tests are running for PHP 7.0 and above, so I assumed that the first supported version is PHP 7.0. Will change the code to be compatible with PHP 5.3 (btw PHP 5.3 reached end of life on 14 Aug 2014)


/**
* @author Vasek Brychta <vaclav@brychtovi.cz>
*/

namespace Hamcrest\File;

use Hamcrest\BaseMatcher;
use Hamcrest\Description;

abstract class FileMatcher extends BaseMatcher
{
/** @var string */
private $ownDescription;
/** @var string */
private $failureDescription;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow the code format (also in comments) of the main codebase.

P.S.
The same applies to other places of the code.


DocBlock style doesn't match the rest of the matchers. They either don't have DocBlocks or format them in a multi-line way.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, I was too focused on the functionality itself that I forgot to maintain the style. Is there any cookbook/style definition or should I just try to find a similar case in the codebase?

Copy link
Member

@aik099 aik099 Sep 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea. The https://github.com/hamcrest/hamcrest-php/blob/master/CONTRIBUTING.md file mentions coding standard but doesn't tell what it is. I'm assuming it's PSR-2.

Anyway, it's usually best to copy/paste new files from parts of the existing file to ensure that the code style is the same.


/**
* @param string $ownDescription
* @param string $failureDescription
*/
public function __construct($ownDescription, $failureDescription)
{
$this->ownDescription = $ownDescription;
$this->failureDescription = $failureDescription;
}

public function describeTo(Description $description)
{
$description->appendText($this->ownDescription);
}

final public function matches($item)
{
return $this->isSafeType($item) && $this->matchesFile($this->createSplFileInfoObjectFromPath($item));
}

final public function describeMismatch($item, Description $mismatchDescription)
{
if ($this->isSafeType($item))
{
$this->describeFileMismatch($this->createSplFileInfoObjectFromPath($item), $mismatchDescription);
}
else
{
parent::describeMismatch($item, $mismatchDescription);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code formatting. Generally, it's the best idea to configure IDE as per the coding standards of the project and mass-reformat any new code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where can I get the coding standards for this project?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea. The https://github.com/hamcrest/hamcrest-php/blob/master/CONTRIBUTING.md file mentions coding standard but doesn't tell what it is. I'm assuming it's PSR-2.

Anyway, it's usually best to copy/paste new files from parts of the existing file to ensure that the code style is the same.

}

abstract protected function matchesFile(\SplFileInfo $file): bool;

protected function describeFileMismatch(\SplFileInfo $file, Description $mismatchDescription)
{
$mismatchDescription->appendValue($file)->appendText(' ')->appendText($this->failureDescription);
}

private function isSafeType($value): bool
{
return $value instanceof \SplFileInfo || is_string($value);
}

/**
* @param string|\SplFileInfo $item
* @return \SplFileInfo
*/
private function createSplFileInfoObjectFromPath($item): \SplFileInfo
{
if (is_string($item))
return new \SplFileInfo($item);

return $item;
}
}
34 changes: 34 additions & 0 deletions hamcrest/Hamcrest/File/IsExistingDirectory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

/**
* @author Vasek Brychta <vaclav@brychtovi.cz>
*/

namespace Hamcrest\File;

class IsExistingDirectory extends FileMatcher
{
public function __construct()
{
parent::__construct('an existing directory', 'is not a directory');
}

protected function matchesFile(\SplFileInfo $file): bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non a PHP 5.3+ compatible code. Likely you have more places, like this.

P.S.
I wonder any none of the builds have failed. Ha-ha, they don't include PHP 5.x in even though it's supported according to composer.json.

I guess that issue needs to be fixed in a separate PR if you're up for it.

{
return $file->isDir();
}

/**
* Evaluates to true if the file exists and is a directory.
* Accepts only <code>\SplFileInfo</code> objects or <code>string</code> paths.
*
* @return \Hamcrest\File\IsExistingDirectory
* @factory
*/
public static function anExistingDirectory()
{
return new self();
}
}
37 changes: 37 additions & 0 deletions hamcrest/Hamcrest/File/IsExistingFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

/**
* @author Vasek Brychta <vaclav@brychtovi.cz>
*/

namespace Hamcrest\File;

use Hamcrest\BaseMatcher;
use Hamcrest\Description;

class IsExistingFile extends FileMatcher
{
public function __construct()
{
parent::__construct('an existing file', 'is not a file');
}

protected function matchesFile(\SplFileInfo $file): bool
{
return $file->isFile();
}

/**
* Evaluates to true if the file exists and is a regular file.
* Accepts only <code>\SplFileInfo</code> objects or <code>string</code> paths.
*
* @return \Hamcrest\File\IsExistingFile
* @factory
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to fail when used by a generator mentioned above.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you think it might fail? I used the generator and it worked correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I saw @factory annotation elsewhere with a parameter (e.g. @factory la-la) and assumed (haven't checked with generator code), that generator might fail.

*/
public static function anExistingFile()
{
return new self();
}
}
22 changes: 22 additions & 0 deletions hamcrest/Hamcrest/Matchers.php
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,28 @@ public static function notSet($property)
return \Hamcrest\Core\Set::notSet($property);
}

/**
* Evaluates to true if the file exists and is a directory.
* Accepts only <code>\SplFileInfo</code> objects or <code>string</code> paths.
*
* @return \Hamcrest\File\IsExistingDirectory
*/
public static function anExistingDirectory()
{
return \Hamcrest\File\IsExistingDirectory::anExistingDirectory();
}

/**
* Evaluates to true if the file exists and is a regular file.
* Accepts only <code>\SplFileInfo</code> objects or <code>string</code> paths.
*
* @return \Hamcrest\File\IsExistingFile
*/
public static function anExistingFile()
{
return \Hamcrest\File\IsExistingFile::anExistingFile();
}

/**
* Matches if value is a number equal to $value within some range of
* acceptable error $delta.
Expand Down
136 changes: 136 additions & 0 deletions tests/Hamcrest/File/IsExistingDirectoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

declare(strict_types=1);

/**
* @author Vasek Brychta <vaclav@brychtovi.cz>
*/

namespace Hamcrest\File;

use Hamcrest\AbstractMatcherTest;

class IsExistingDirectoryTest extends AbstractMatcherTest
{
private static $EXISTING_DIRECTORY_PATH;
private static $NON_EXISTING_DIRECTORY_PATH;
private static $EXISTING_FILE_PATH;

private static $EXISTING_DIRECTORY;
private static $NON_EXISTING_DIRECTORY;
private static $EXISTING_FILE;

/**
* @beforeClass
*/
public static function initializeFiles()
{
self::$EXISTING_DIRECTORY_PATH = __DIR__;
self::$NON_EXISTING_DIRECTORY_PATH = __DIR__ . '/does-not-exist';
self::$EXISTING_FILE_PATH = __FILE__;

self::$EXISTING_DIRECTORY = new \SplFileInfo(self::$EXISTING_DIRECTORY_PATH);
self::$NON_EXISTING_DIRECTORY = new \SplFileInfo(self::$NON_EXISTING_DIRECTORY_PATH);
self::$EXISTING_FILE = new \SplFileInfo(self::$EXISTING_FILE_PATH);
}

protected function createMatcher()
{
return IsExistingDirectory::anExistingDirectory();
}

public function testMatchesAnExistingDirectory()
{
$this->assertMatches(
$this->createMatcher(),
self::$EXISTING_DIRECTORY,
"should match a directory that actualy exists"
);
}

public function testMatchesAnExistingDirectoryPath()
{
$this->assertMatches(
$this->createMatcher(),
self::$EXISTING_DIRECTORY_PATH,
"should match a path to directory that actualy exists"
);
}

public function testDoesNotMatchADirectoryThatDoesNotExist()
{
$this->assertDoesNotMatch(
$this->createMatcher(),
self::$NON_EXISTING_DIRECTORY,
"should not match a directory that does not exist"
);
}

public function testDoesNotMatchADirectoryPathThatDoesNotExist()
{
$this->assertDoesNotMatch(
$this->createMatcher(),
self::$NON_EXISTING_DIRECTORY_PATH,
"should not match a path to directory that does not exist"
);
}

public function testDoesNotMatchAnExistingFileThatIsNotADirectory()
{
$this->assertDoesNotMatch(
$this->createMatcher(),
self::$EXISTING_FILE,
"should not match an existing file that is not a directory"
);
}

public function testDoesNotMatchAnExistingFilePathThatIsNotADirectory()
{
$this->assertDoesNotMatch(
$this->createMatcher(),
self::$EXISTING_FILE_PATH,
"should not match a path to existing file that is not a directory"
);
}

public function testDoesNotMatchNull()
{
$this->assertDoesNotMatch(
$this->createMatcher(),
null,
'should not match null'
);
}

public function testHasAReadableDescription()
{
$this->assertDescription('an existing directory', $this->createMatcher());
}

public function testHasAReadableMissmatchDescription()
{
$this->assertMismatchDescription(
'<' . self::$NON_EXISTING_DIRECTORY . '> is not a directory',
$this->createMatcher(),
self::$NON_EXISTING_DIRECTORY
);
}

public function testHasAReadableMissmatchDescriptionForPath()
{
$this->assertMismatchDescription(
'<' . self::$NON_EXISTING_DIRECTORY_PATH . '> is not a directory',
$this->createMatcher(),
self::$NON_EXISTING_DIRECTORY_PATH
);
}

public function testHasAReadableTypeMissmatchDescription()
{
$this->assertMismatchDescription(
'was <stdClass>',
$this->createMatcher(),
new \stdClass()
);
}
}