test impl. and code coverage improvements

This commit is contained in:
Dennis Eichhorn 2020-10-02 20:02:04 +02:00
parent 670be4fcf0
commit 2c49d9aee9
36 changed files with 671 additions and 283 deletions

View File

@ -63,7 +63,7 @@ class MazeGenerator
}
}
while (0 < $n) {
while ($n > 0) {
$potential = [
[$pos[0] + 1, $pos[1]],
[$pos[0], $pos[1] + 1],
@ -97,7 +97,6 @@ class MazeGenerator
$pos = \array_pop($path);
if ($pos === null) {
$n = 0;
break;
}
}

View File

@ -256,7 +256,7 @@ class Localization implements \JsonSerializable
$files = \glob(__DIR__ . '/../Localization/Defaults/Definitions/' . $langCode . '_' . $countryCode . '*');
if ($files === false) {
$files = [];
$files = []; // @codeCoverageIgnore
}
foreach ($files as $file) {

View File

@ -203,7 +203,7 @@ final class Money implements \Serializable
$right = \substr($value, -self::MAX_DECIMALS);
if ($right === false) {
throw new \Exception();
throw new \Exception(); // @codeCoverageIgnore
}
return ($decimals > 0) ? \number_format((float) $left, 0, $this->decimal, $this->thousands) . $this->decimal . \substr($right, 0, $decimals) : \str_pad($left, 1, '0');

View File

@ -125,6 +125,26 @@ final class Error
return MeasureOfDispersion::meanAbsoluteDeviation($errors);
}
/**
* Get mean absolute deviation (MAD).
*
* @param array<int, int|float> $observed Observed values
* @param array<int, int|float> $forecasted Forecasted values
*
* @return float
*
* @since 1.0.0
*/
public static function getMeanAbsoulteDeviation(array $observed, array $forecasted) : float
{
$deviation = 0.0;
foreach ($observed as $key => $value) {
$deviation += \abs($value - $forecasted[$key]);
}
return $deviation / \count($observed);
}
/**
* Get mean squared error (MSE).
*
@ -209,62 +229,6 @@ final class Error
return 1 - (1 - $R) * ($observations - 1) / ($observations - $predictors - 1);
}
/**
* Get Aike's information criterion (AIC)
*
* @param float $sse SSE
* @param int $observations Amount of observations
* @param int $predictors Amount of predictors
*
* @return float
*
* @todo Orange-Management/phpOMS#167
* Create unit test.
*
* @since 1.0.0
*/
public static function getAkaikeInformationCriterion(float $sse, int $observations, int $predictors) : float
{
return $observations * \log($sse / $observations) + 2 * ($predictors + 2);
}
/**
* Get corrected Aike's information criterion (AIC)
*
* Correction for small amount of observations
*
* @param float $aic AIC
* @param int $observations Amount of observations
* @param int $predictors Amount of predictors
*
* @return float
*
* @since 1.0.0
*/
public static function getCorrectedAkaikeInformationCriterion(float $aic, int $observations, int $predictors) : float
{
return $aic + (2 * ($predictors + 2) * ($predictors + 3)) / ($observations - $predictors - 3);
}
/**
* Get Bayesian information criterion (BIC)
*
* @param float $sse SSE
* @param int $observations Amount of observations
* @param int $predictors Amount of predictors
*
* @return float
*
* @todo Orange-Management/phpOMS#168
* Create unit test.
*
* @since 1.0.0
*/
public static function getSchwarzBayesianInformationCriterion(float $sse, int $observations, int $predictors) : float
{
return $observations * \log($sse / $observations) + ($predictors + 2) * \log($observations);
}
/**
* Get mean absolute percentage error (MAPE).
*
@ -273,9 +237,6 @@ final class Error
*
* @return float
*
* @todo Orange-Management/phpOMS#169
* Create unit test.
*
* @since 1.0.0
*/
public static function getMeanAbsolutePercentageError(array $observed, array $forecasted) : float
@ -291,9 +252,6 @@ final class Error
*
* @return float
*
* @todo Orange-Management/phpOMS#170
* Create unit test.
*
* @since 1.0.0
*/
public static function getSymmetricMeanAbsolutePercentageError(array $observed, array $forecasted) : float
@ -301,62 +259,12 @@ final class Error
$error = [];
foreach ($observed as $key => $value) {
$error[] = 200 * \abs($value - $forecasted[$key]) / ($value + $forecasted[$key]);
$error[] = \abs($value - $forecasted[$key]) / ($value + $forecasted[$key]) / 2;
}
return Average::arithmeticMean($error);
}
/**
* Get cross sectional scaled errors (CSSE)
*
* @param array<int, int|float> $errors Errors
* @param float[] $observed Dataset
*
* @return float[]
*
* @todo Orange-Management/phpOMS#172
* Create unit test.
*
* @since 1.0.0
*/
public static function getCrossSectionalScaledErrorArray(array $errors, array $observed) : array
{
$scaled = [];
$deviation = MeasureOfDispersion::meanDeviation($observed);
foreach ($errors as $error) {
$scaled[] = $error / $deviation;
}
return $scaled;
}
/**
* Get cross sectional scaled errors (CSSE)
*
* @param float $error Errors
* @param float[] $observed Dataset
*
* @return float
*
* @todo Orange-Management/phpOMS#171
* Create unit test.
*
* @since 1.0.0
*/
public static function getCrossSectionalScaledError(float $error, array $observed) : float
{
$mean = Average::arithmeticMean($observed);
$sum = 0.0;
foreach ($observed as $value) {
$sum += \abs($value - $mean);
}
return $error / MeasureOfDispersion::meanDeviation($observed);
}
/**
* Get mean absolute scaled error (MASE)
*
@ -372,15 +280,12 @@ final class Error
}
/**
* Get mean absolute scaled error (MSSE)
* Get mean squared scaled error (MSSE)
*
* @param array<int, int|float> $scaledErrors Scaled errors
*
* @return float
*
* @todo Orange-Management/phpOMS#173
* Create unit test.
*
* @since 1.0.0
*/
public static function getMeanSquaredScaledError(array $scaledErrors) : float

View File

@ -352,7 +352,7 @@ final class MeasureOfDispersion
}
/**
* Get mean absolute deviation.
* Get mean absolute deviation (MAD).
*
* @param array<int, int|float> $x Values
* @param float $mean Mean

View File

@ -43,11 +43,20 @@ interface DirectoryInterface extends \ArrayAccess, \Iterator, ContainerInterface
/**
* Add file or directory.
*
* @param mixed $file File to add
* @param ContainerInterface $file File to add
*
* @return bool
* @return self
*
* @since 1.0.0
*/
public function addNode($file) : bool;
public function addNode(ContainerInterface $file) : self;
/**
* Get files in directory.
*
* @return array
*
* @since 1.0.0
*/
public function getList() : array;
}

View File

@ -128,7 +128,7 @@ final class FileUtils
} elseif (!empty($path)) {
\array_pop($path);
} else {
throw new PathException($origPath);
throw new PathException($origPath); // @codeCoverageIgnore
}
}
@ -138,23 +138,25 @@ final class FileUtils
/**
* Change encoding of file
*
* @param string $file Path to file which should be re-encoded
* @param string $encoding New file encoding
* @param string $input Path to file which should be re-encoded
* @param string $output Output file path
* @param string $outputEncoding New file encoding
* @param string $inputEncoding Old file encoding
*
* @return void
*
* @since 1.0.0
*/
public static function changeFileEncoding(string $file, string $encoding) : void
public static function changeFileEncoding(string $input, string $output, string $outputEncoding, string $inputEncoding = '') : void
{
$content = \file_get_contents($file);
$content = \file_get_contents($input);
if ($content === false) {
return;
return; // @codeCoverageIgnore
}
$detected = \mb_detect_encoding($content);
\file_put_contents($file, \mb_convert_encoding($content, $encoding, $detected === false ? \mb_list_encodings() : $detected));
$detected = empty($inputEncoding) ? \mb_detect_encoding($content) : $inputEncoding;
\file_put_contents($output, \mb_convert_encoding($content, $outputEncoding, $detected === false ? \mb_list_encodings() : $detected));
}
/**

View File

@ -546,13 +546,15 @@ class Directory extends FileAbstract implements DirectoryInterface, FtpContainer
/**
* {@inheritdoc}
*/
public function addNode($file) : bool
public function addNode(ContainerInterface $node) : self
{
$this->count += $file->getCount();
$this->size += $file->getSize();
$this->nodes[$file->getName()] = $file;
$this->count += $node->getCount();
$this->size += $node->getSize();
$this->nodes[$node->getName()] = $node;
return $file->createNode();
$node->createNode();
return $this;
}
/**
@ -691,4 +693,12 @@ class Directory extends FileAbstract implements DirectoryInterface, FtpContainer
return 0;
}
/**
* {@inheritdoc}
*/
public function getList() : array
{
return [];
}
}

View File

@ -47,6 +47,14 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
*/
private array $nodes = [];
/**
* Is directory initialized
*
* @var bool
* @since 1.0.0
*/
private bool $isInitialized = false;
/**
* Constructor.
*
@ -55,12 +63,12 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
*
* @since 1.0.0
*/
public function __construct(string $path, string $filter = '*')
public function __construct(string $path, string $filter = '*', bool $initialize = true)
{
$this->filter = \ltrim($filter, '\\/');
parent::__construct($path);
if (\file_exists($this->path)) {
if ($initialize && \file_exists($this->path)) {
$this->index();
}
}
@ -85,7 +93,8 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
$path = \rtrim($path, '\\/');
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST);
\RecursiveIteratorIterator::SELF_FIRST
);
if ($filter !== '*') {
$iterator = new \RegexIterator($iterator, '/' . $filter . '/i', \RecursiveRegexIterator::GET_MATCH);
@ -138,11 +147,16 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
*/
public function index() : void
{
if ($this->isInitialized) {
return;
}
$this->isInitialized = true;
parent::index();
foreach (\glob($this->path . \DIRECTORY_SEPARATOR . $this->filter) as $filename) {
if (!StringUtils::endsWith(\trim($filename), '.')) {
$file = \is_dir($filename) ? new self($filename) : new File($filename);
$file = \is_dir($filename) ? new self($filename, '*', false) : new File($filename);
$this->addNode($file);
}
@ -152,13 +166,27 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
/**
* {@inheritdoc}
*/
public function addNode($file) : bool
public function addNode(ContainerInterface $node) : self
{
$this->count += $file->getCount();
$this->size += $file->getSize();
$this->nodes[$file->getName()] = $file;
$this->count += $node->getCount();
$this->size += $node->getSize();
$this->nodes[$node->getName()] = $node;
return $file->createNode();
$node->createNode();
return $this;
}
/**
* Create node
*
* @return bool
*
* @since 1.0.0
*/
public function createNode() : bool
{
return self::create($this->path, $this->permission, true);
}
/**
@ -174,7 +202,7 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
$directories = \scandir($dir);
if ($directories === false) {
return $countSize;
return $countSize; // @codeCoverageIgnore
}
foreach ($directories as $key => $filename) {
@ -218,7 +246,7 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
$ignore[] = '..';
if ($files === false) {
return $size;
return $size; // @codeCoverageIgnore
}
foreach ($files as $t) {
@ -249,7 +277,7 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
$files = \scandir($path);
if ($files === false) {
return false;
return false; // @codeCoverageIgnore
}
/* Removing . and .. */
@ -354,6 +382,7 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
self::create($to, 0755, true);
} elseif ($overwrite && \file_exists($to)) {
self::delete($to);
self::create($to, 0755, true);
} else {
return false;
}
@ -417,19 +446,11 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
*/
public function getNode(string $name) : ?ContainerInterface
{
return $this->nodes[$name] ?? null;
}
if (isset($this->nodes[$name]) && $this->nodes[$name] instanceof self) {
$this->nodes[$name]->index();
}
/**
* {@inheritdoc}
*/
public function createNode() : bool
{
return self::create($this->path, $this->permission, true);
/**
* @todo Orange-Management/phpOMS#??? [p:low] [t:todo] [d:medium]
* Add node to current node list
*/
return $this->nodes[$name] ?? null;
}
/**
@ -453,7 +474,7 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
try {
\mkdir($path, $permission, $recursive);
} catch (\Throwable $t) {
return false;
return false; // @codeCoverageIgnore
}
return true;
@ -462,28 +483,6 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
return false;
}
/**
* {@inheritdoc}
*/
public function remove(string $name) : bool
{
if (isset($this->nodes[$name])) {
$this->count -= $this->nodes[$name]->getCount();
$this->size -= $this->nodes[$name]->getSize();
unset($this->nodes[$name]);
/**
* @todo Orange-Management/phpOMS#??? [p:low] [t:question] [d:medium]
* Should this also remove the resource? \unlink();
*/
return true;
}
return false;
}
/**
* {@inheritdoc}
*/
@ -497,7 +496,13 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
*/
public function current()
{
return \current($this->nodes);
$current = \current($this->nodes);
if (isset($current) && $current instanceof self) {
$current->index();
}
return $current;
}
/**
@ -513,7 +518,13 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
*/
public function next()
{
return \next($this->nodes);
$next = \next($this->nodes);
if (isset($next) && $next instanceof self) {
$next->index();
}
return $next;
}
/**
@ -617,21 +628,44 @@ final class Directory extends FileAbstract implements DirectoryInterface, LocalC
*/
public function deleteNode() : bool
{
return self::delete($this->path);
/**
* @todo Orange-Management/phpOMS#??? [p:low] [t:todo] [d:medium]
* Remove node from node list
*/
self::delete($this->path);
if (!isset($this->nodes[$this->path])) {
return false;
}
$this->count -= $this->nodes[$this->path]->getCount();
$this->size -= $this->nodes[$this->path]->getSize();
unset($this->nodes[$this->path]);
return true;
}
/**
* {@inheritdoc}
*/
public function offsetGet($offset) : void
public function offsetGet($offset)
{
/**
* @todo Orange-Management/phpOMS#??? [p:low] [t:todo] [d:medium]
* Implement offsetGet()
*/
if (isset($this->nodes[$offset]) && $this->nodes[$offset] instanceof self) {
$this->nodes[$offset]->index();
}
return $this->nodes[$offset] ?? null;
}
/**
* {@inheritdoc}
*/
public function getList() : array
{
$pathLength = \strlen($this->path);
$content = [];
foreach ($this->nodes as $node) {
$content[] = \substr($node->getPath(), $pathLength + 1);
}
return $content;
}
}

View File

@ -2009,14 +2009,4 @@ abstract class MimeType extends Enum
public const M_ZMM = 'application/vnd.handheld-entertainment+xml';
public const M_123 = 'application/vnd.lotus-1-2-3';
/** {@inheritdoc} */
public static function getByName(string $name)
{
if (!self::isValidName($name)) {
return 'application/octet-stream';
}
return \constant('self::' . $name);
}
}

View File

@ -69,7 +69,7 @@ class LZW implements CompressionInterface
$entry = '';
$dictSize = 256;
if (empty($compressed) || $compressed === false) {
if (empty($compressed) || $compressed === [''] || $compressed === false) {
return '';
}

View File

@ -97,26 +97,28 @@ final class Currency
*/
public static function getEcbEuroRates() : array
{
if (empty(self::$ecbCurrencies)) {
$request = new HttpRequest(new HttpUri('https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'));
$request->setMethod(RequestMethod::GET);
if (!empty(self::$ecbCurrencies)) {
return self::$ecbCurrencies;
}
try {
$xml = new \SimpleXMLElement(Rest::request($request)->getBody());
$request = new HttpRequest(new HttpUri('https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'));
$request->setMethod(RequestMethod::GET);
if (!isset($xml->Cube)) {
throw new \Exception('Invalid xml path');
}
try {
$xml = new \SimpleXMLElement(Rest::request($request)->getBody());
$node = $xml->Cube->Cube->Cube;
self::$ecbCurrencies = [];
foreach ($node as $key => $value) {
self::$ecbCurrencies[\strtoupper((string) $value->attributes()['currency'])] = (float) $value->attributes()['rate'];
}
} catch (\Throwable $t) {
self::$ecbCurrencies = [];
if (!isset($xml->Cube)) {
throw new \Exception('Invalid xml path'); // @codeCoverageIgnore
}
$node = $xml->Cube->Cube->Cube;
self::$ecbCurrencies = [];
foreach ($node as $key => $value) {
self::$ecbCurrencies[\strtoupper((string) $value->attributes()['currency'])] = (float) $value->attributes()['rate'];
}
} catch (\Throwable $t) {
self::$ecbCurrencies = []; // @codeCoverageIgnore
}
return self::$ecbCurrencies;

View File

@ -35,14 +35,14 @@ class CsvSettings
*
* @since 1.0.0
*/
public static function getFileDelimiter($file, int $checkLines = 2, array $delimiters = [',', '\t', ';', '|', ':']) : string
public static function getFileDelimiter($file, int $checkLines = 2, array $delimiters = [',', "\t", ';', '|', ':']) : string
{
$results = [];
$i = 0;
$line = \fgets($file);
if ($line === false) {
return ';';
return ';'; // @codeCoverageIgnore
}
while ($line !== false && $i < $checkLines) {
@ -53,7 +53,7 @@ class CsvSettings
$fields = \preg_split($regExp, $line);
if ($fields === false) {
return ';';
return ';'; // @codeCoverageIgnore
}
if (\count($fields) > 1) {

View File

@ -46,25 +46,18 @@ class Tar implements ArchiveInterface
* @var string $relative
*/
foreach ($sources as $source => $relative) {
if (\is_numeric($source) && \realpath($relative) !== false) {
$source = $relative;
$relative = '';
}
$source = \realpath($source);
if ($source === false) {
continue;
}
$source = \str_replace('\\', '/', $source);
if (!\file_exists($source)) {
if (($source = \realpath($source)) === false
|| ($source = \str_replace('\\', '/', $source)) === false
|| !\file_exists($source)
) {
continue;
}
if (\is_dir($source)) {
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source), \RecursiveIteratorIterator::SELF_FIRST);
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($source),
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($files as $file) {
$file = \str_replace('\\', '/', $file);
@ -103,12 +96,16 @@ class Tar implements ArchiveInterface
return false;
}
$destination = \str_replace('\\', '/', $destination);
$destination = \rtrim($destination, '/');
$tar = new \PharData($source);
try {
$destination = \str_replace('\\', '/', $destination);
$destination = \rtrim($destination, '/');
$tar = new \PharData($source);
$tar->extractTo($destination . '/');
$tar->extractTo($destination . '/');
return true;
return true;
} catch (\Throwable $t) {
return false;
}
}
}

View File

@ -49,25 +49,18 @@ class Zip implements ArchiveInterface
* @var string $relative
*/
foreach ($sources as $source => $relative) {
if (\is_numeric($source) && \realpath($relative) !== false) {
$source = $relative;
$relative = '';
}
$source = \realpath($source);
if ($source === false) {
continue;
}
$source = \str_replace('\\', '/', $source);
if (!\file_exists($source)) {
if (($source = \realpath($source)) === false
|| ($source = \str_replace('\\', '/', $source)) === false
|| !\file_exists($source)
) {
continue;
}
if (\is_dir($source)) {
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source), \RecursiveIteratorIterator::SELF_FIRST);
$files = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($source),
\RecursiveIteratorIterator::SELF_FIRST
);
foreach ($files as $file) {
$file = \str_replace('\\', '/', $file);
@ -109,13 +102,17 @@ class Zip implements ArchiveInterface
$destination = \str_replace('\\', '/', $destination);
$destination = \rtrim($destination, '/');
$zip = new \ZipArchive();
if (!$zip->open($source)) {
try {
$zip = new \ZipArchive();
if (!$zip->open($source)) {
return false;
}
$zip->extractTo($destination . '/');
return $zip->close();
} catch (\Throwable $t) {
return false;
}
$zip->extractTo($destination . '/');
return $zip->close();
}
}

View File

@ -94,6 +94,8 @@ final class StringCompare
*
* @return int
*
* @todo: Consider to remove previous matches in words2. Otherwise the distance may be checked against the same word over and over.
*
* @since 1.0.0
*/
public static function valueWords(string $s1, string $s2) : int

View File

@ -32,10 +32,12 @@ class WeightedTest extends \PHPUnit\Framework\TestCase
public function testNoOverlappingScheduling() : void
{
$jobs = [
new Job(10, new \DateTime('2000-01-01'), null, '0'),
new Job(20, new \DateTime('2003-01-01'), new \DateTime('2010-01-01'), 'A'),
new Job(50, new \DateTime('2001-01-01'), new \DateTime('2002-01-01'), 'B'),
new Job(100, new \DateTime('2006-01-01'), new \DateTime('2019-01-01'), 'C'),
new Job(200, new \DateTime('2002-01-01'), new \DateTime('2020-01-01'), 'D'),
new Job(300, new \DateTime('2004-01-01'), null, '1'),
];
$filtered = WeighteD::solve($jobs);
@ -48,11 +50,11 @@ class WeightedTest extends \PHPUnit\Framework\TestCase
$names[] = $job->getName();
}
self::assertEqualsWithDelta(250, $value, 0.01);
self::assertEqualsWithDelta(350, $value, 0.01);
self::assertTrue(
\in_array('B', $names)
&& \in_array('D', $names)
&& \in_array('1', $names)
);
}

View File

@ -16,6 +16,7 @@ namespace phpOMS\tests\Math\Statistic\Forecast;
use phpOMS\Math\Statistic\Forecast\Error;
use phpOMS\Math\Statistic\MeasureOfDispersion;
use phpOMS\Utils\ArrayUtils;
/**
* @internal
@ -93,6 +94,27 @@ class ErrorTest extends \PHPUnit\Framework\TestCase
self::assertEqualsWithDelta(0.0983, Error::getMeanAbsoluteScaledError($scaledErrors), 0.01);
}
public function testMSSE() : void
{
$observed = [
-2.9, -2.83, -0.95, -0.88, 1.21, -1.67, 0.83, -0.27, 1.36,
-0.34, 0.48, -2.83, -0.95, -0.88, 1.21, -1.67, -2.99, 1.24, 0.64,
];
$forecast = [
-2.95, -2.7, -1.00, -0.68, 1.50, -1.00, 0.90, -0.37, 1.26,
-0.54, 0.58, -2.13, -0.75, -0.89, 1.25, -1.65, -3.20, 1.29, 0.60,
];
$errors = Error::getForecastErrorArray($observed, $forecast);
$scaledErrors = Error::getScaledErrorArray($errors, $observed);
self::assertEqualsWithDelta(
Error::getMeanAbsoluteScaledError(ArrayUtils::powerInt($scaledErrors, 2)),
Error::getMeanSquaredScaledError($scaledErrors), 0.01
);
}
public function testScaledError() : void
{
self::assertEquals(
@ -117,4 +139,28 @@ class ErrorTest extends \PHPUnit\Framework\TestCase
self::assertEqualsWithDelta(0.922085138, Error::getAdjustedCoefficientOfDetermination(0.944346527, 8, 2), 0.001);
}
public function testMAPE() : void
{
self::assertEqualsWithDelta(0.17551, Error::getMeanAbsolutePercentageError(
[112.3, 108.4, 148.9, 117.4],
[124.7, 103.7, 116.6, 78.5],
), 0.001);
}
public function testSMAPE() : void
{
self::assertEqualsWithDelta(0.049338, Error::getSymmetricMeanAbsolutePercentageError(
[112.3, 108.4, 148.9, 117.4],
[124.7, 103.7, 116.6, 78.5],
), 0.001);
}
public function testMAD() : void
{
self::assertEqualsWithDelta(22.075, Error::getMeanAbsoulteDeviation(
[112.3, 108.4, 148.9, 117.4],
[124.7, 103.7, 116.6, 78.5],
), 0.001);
}
}

View File

@ -75,7 +75,7 @@ class PackageManagerTest extends \PHPUnit\Framework\TestCase
// create zip
Zip::pack(
[
__DIR__ . '/testPackage',
__DIR__ . '/testPackage' => __DIR__ . '/testPackage',
],
__DIR__ . '/testPackage.zip'
);

View File

@ -212,5 +212,6 @@ class LocationTest extends \PHPUnit\Framework\TestCase
$this->location->setGeo(['lat' => 12.1, 'long' => 11.2,]);
self::assertEquals($expected, $this->location->jsonSerialize());
self::assertEquals(\json_encode($this->location->jsonSerialize()), $this->location->serialize());
}
}

View File

@ -68,8 +68,25 @@ class FileUtilsTest extends \PHPUnit\Framework\TestCase
self::assertEquals(0742, FileUtils::permissionToOctal('rwxr---w-'));
}
/**
* @testdox The encoding of a file can be changed
* @covers phpOMS\System\File\FileUtils
* @group framework
*/
public function testChangeFileEncoding() : void
{
self::markTestIncomplete();
if (\file_exists(__DIR__ . '/UTF-8.txt')) {
\unlink(__DIR__ . '/UTF-8.txt');
}
FileUtils::changeFileEncoding(__DIR__ . '/Windows-1252.txt', __DIR__ . '/UTF-8.txt', 'UTF-8', 'Windows-1252');
self::assertFileExists(__DIR__ . '/UTF-8.txt');
self::assertNotEquals("This is a test file with some¶\ncontent Ø Æ.", \file_get_contents(__DIR__ . '/Windows-1252.txt'));
self::assertEquals("This is a test file with some¶\ncontent Ø Æ.", \file_get_contents(__DIR__ . '/UTF-8.txt'));
if (\file_exists(__DIR__ . '/UTF-8.txt')) {
\unlink(__DIR__ . '/UTF-8.txt');
}
}
}

View File

@ -37,6 +37,11 @@ class DirectoryTest extends \PHPUnit\Framework\TestCase
\rmdir($dirPath);
}
public function testStaticRemove() : void
{
self::markTestIncomplete();
}
/**
* @testdox A directory can be checked for existence
* @covers phpOMS\System\File\Local\Directory
@ -277,6 +282,36 @@ class DirectoryTest extends \PHPUnit\Framework\TestCase
Directory::delete(__DIR__ . '/newdirtest');
}
/**
* @testdox A directory can be forced to be copied to a different location even if the destination already exists
* @covers phpOMS\System\File\Local\Directory
* @group framework
*/
public function testStaticCopyOverwrite() : void
{
$dirTestPath = __DIR__ . '/dirtest';
self::assertTrue(Directory::copy($dirTestPath, __DIR__ . '/newdirtest'));
self::assertFalse(Directory::copy($dirTestPath, __DIR__ . '/newdirtest', false));
self::assertTrue(Directory::copy($dirTestPath, __DIR__ . '/newdirtest', true));
self::assertFileExists(__DIR__ . '/newdirtest/sub/path/test3.txt');
Directory::delete(__DIR__ . '/newdirtest');
}
/**
* @testdox By default a directory is not overwritten on copy
* @covers phpOMS\System\File\Local\Directory
* @group framework
*/
public function testStaticInvalidCopyOverwrite() : void
{
$dirTestPath = __DIR__ . '/dirtest';
self::assertTrue(Directory::copy($dirTestPath, __DIR__ . '/newdirtest'));
self::assertFalse(Directory::copy($dirTestPath, __DIR__ . '/newdirtest', false));
Directory::delete(__DIR__ . '/newdirtest');
}
/**
* @testdox A directory can be moved/renamed to a different path
* @covers phpOMS\System\File\Local\Directory
@ -286,8 +321,42 @@ class DirectoryTest extends \PHPUnit\Framework\TestCase
{
$dirTestPath = __DIR__ . '/dirtest';
self::assertTrue(Directory::move($dirTestPath, __DIR__ . '/parent/newdirtest'));
self::assertFileExists(__DIR__ . '/parent/newdirtest/sub/path/test3.txt');
Directory::move(__DIR__ . '/parent/newdirtest', $dirTestPath);
\rmdir(__DIR__ . '/parent');
}
/**
* @testdox By default a directory is not overwritten on move
* @covers phpOMS\System\File\Local\Directory
* @group framework
*/
public function testStaticInvalidMoveOverwrite() : void
{
$dirTestPath = __DIR__ . '/dirtest';
self::assertTrue(Directory::move($dirTestPath, __DIR__ . '/newdirtest'));
self::assertFileExists(__DIR__ . '/newdirtest/sub/path/test3.txt');
self::assertFalse(Directory::move(__DIR__ . '/newdirtest', __DIR__ . '/newdirtest', false));
Directory::move(__DIR__ . '/newdirtest', $dirTestPath);
}
/**
* @testdox A directory can be forced to be moved/renamed to a different path even if the destination already exists
* @covers phpOMS\System\File\Local\Directory
* @group framework
*/
public function testStaticMoveOverwrite() : void
{
$dirTestPath = __DIR__ . '/dirtest';
self::assertTrue(Directory::move($dirTestPath, __DIR__ . '/newdirtest'));
self::assertTrue(Directory::copy(__DIR__ . '/newdirtest', $dirTestPath));
self::assertFalse(Directory::move($dirTestPath, __DIR__ . '/newdirtest', false));
self::assertTrue(Directory::move($dirTestPath, __DIR__ . '/newdirtest', true));
Directory::move(__DIR__ . '/newdirtest', $dirTestPath);
}
@ -334,6 +403,7 @@ class DirectoryTest extends \PHPUnit\Framework\TestCase
{
$dirTestPath = __DIR__ . '/dirtest';
self::assertCount(6, Directory::list($dirTestPath));
self::assertEquals(['sub/test2.txt', 'sub/test4.md', 'sub/path/test3.txt'], Directory::list($dirTestPath, 'test[0-9]+.*'));
}
/**
@ -347,6 +417,27 @@ class DirectoryTest extends \PHPUnit\Framework\TestCase
self::assertCount(3, Directory::listByExtension($dirTestPath, 'txt'));
}
/**
* @testdox The owner of a directory can be returned
* @covers phpOMS\System\File\Local\Directory
* @group framework
*/
public function testStaticOwner() : void
{
$dirTestPath = __DIR__ . '/dirtest';
self::assertNotEmpty(Directory::owner($dirTestPath));
}
/**
* @testdox Invalid directory names and paths can be sanitized
* @covers phpOMS\System\File\Local\Directory
* @group framework
*/
public function testDirectoryNameSanitizing() : void
{
self::assertEquals(':/some/test/[path', Directory::sanitize(':#&^$/some%/test/[path!'));
}
/**
* @testdox A none-existing directory returns a empty list of files and sub-directories
* @covers phpOMS\System\File\Local\Directory
@ -422,4 +513,139 @@ class DirectoryTest extends \PHPUnit\Framework\TestCase
Directory::owner(__DIR__ . '/invalid');
}
public function testList() : void
{
$dirTestPath = __DIR__ . '/dirtest';
$dir = new Directory($dirTestPath);
self::assertEquals([
'sub',
'test.txt'
], $dir->getList());
}
public function testNodeOutput() : void
{
$dirTestPath = __DIR__ . '/dirtest';
$dir = new Directory($dirTestPath);
self::assertInstanceOf(Directory::class, $dir->getNode('sub'));
}
public function testNodeCreate() : void
{
$dir = new Directory(__DIR__);
$dir->addNode(new Directory(__DIR__ . '/nodedir'));
self::assertTrue(\file_exists(__DIR__ . '/nodedir'));
\rmdir(__DIR__ . '/nodedir');
$dir = new Directory(__DIR__ . '/nodedir2');
$dir->createNode();
self::assertTrue(\file_exists(__DIR__ . '/nodedir2'));
\rmdir(__DIR__ . '/nodedir2');
}
public function testNodeDelete() : void
{
$dir = new Directory(__DIR__);
$dir->addNode(new Directory(__DIR__ . '/nodedir'));
self::assertTrue(\file_exists(__DIR__ . '/nodedir'));
self::assertTrue($dir->deleteNode());
self::assertFalse(\file_exists(__DIR__ . '/nodedir'));
}
public function testNodeCopy() : void
{
self::markTestIncomplete();
}
public function testNodeMove() : void
{
self::markTestIncomplete();
}
public function testNodeExists() : void
{
self::markTestIncomplete();
}
public function testParentOutput() : void
{
self::markTestIncomplete();
}
public function testNodeNext() : void
{
self::markTestIncomplete();
}
public function testNodeCurrent() : void
{
self::markTestIncomplete();
}
public function testNodeKey() : void
{
self::markTestIncomplete();
}
public function testNodeArrayRead() : void
{
self::markTestIncomplete();
}
public function testNodeArraySet() : void
{
self::markTestIncomplete();
}
public function testNodeArrayRemove() : void
{
self::markTestIncomplete();
}
public function testNodeArrayExists() : void
{
self::markTestIncomplete();
}
public function testNodeCreatedAt() : void
{
self::markTestIncomplete();
}
public function testNodeChangedAt() : void
{
self::markTestIncomplete();
}
public function testNodeOwner() : void
{
self::markTestIncomplete();
}
public function testNodePermission() : void
{
self::markTestIncomplete();
}
public function testDirname() : void
{
self::markTestIncomplete();
}
public function testName() : void
{
self::markTestIncomplete();
}
public function testDirpath() : void
{
self::markTestIncomplete();
}
}

View File

@ -0,0 +1,2 @@
This is a test file with some¶
content Ø Æ.

View File

@ -33,5 +33,6 @@ class LZWTest extends \PHPUnit\Framework\TestCase
$expected = 'This is a test';
$compression = new LZW();
self::assertEquals($expected, $compression->decompress($compression->compress($expected)));
self::assertEquals('', $compression->decompress(''));
}
}

View File

@ -16,6 +16,10 @@ namespace phpOMS\tests\Utils\Converter;
use phpOMS\Localization\ISO4217CharEnum;
use phpOMS\Utils\Converter\Currency;
use phpOMS\Message\Http\HttpRequest;
use phpOMS\Uri\HttpUri;
use phpOMS\Message\Http\RequestMethod;
use phpOMS\Message\Http\Rest;
/**
* @testdox phpOMS\tests\Utils\Converter\CurrencyTest: Currency converter
@ -24,11 +28,33 @@ use phpOMS\Utils\Converter\Currency;
*/
class CurrencyTest extends \PHPUnit\Framework\TestCase
{
private static $reachable;
protected function setUp() : void
{
if (!isset(self::$reachable)) {
try {
$request = new HttpRequest(new HttpUri('https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'));
$request->setMethod(RequestMethod::GET);
Rest::request($request)->getBody();
self::$reachable = true;
} catch (\Throwable $t) {
self::$reachable = false;
}
}
if (!self::$reachable) {
$this->markTestSkipped(
'External currency conversion not available.'
);
}
}
/**
* @testdox A currency can be converted from euro to another currency
* @covers phpOMS\Utils\Converter\Currency
* @group framework
* @group maybe
*/
public function testCurrencyFromEur() : void
{
@ -39,7 +65,6 @@ class CurrencyTest extends \PHPUnit\Framework\TestCase
* @testdox A currency can be converted to euro from another currency
* @covers phpOMS\Utils\Converter\Currency
* @group framework
* @group maybe
*/
public function testCurrencyToEur() : void
{
@ -50,7 +75,6 @@ class CurrencyTest extends \PHPUnit\Framework\TestCase
* @testdox A currency can be converted from one currency to another currency
* @covers phpOMS\Utils\Converter\Currency
* @group framework
* @group maybe
*/
public function testCurrency() : void
{
@ -62,7 +86,6 @@ class CurrencyTest extends \PHPUnit\Framework\TestCase
* @testdox A currency conversion from eur to a invalid currency throws a InvalidArgumentException
* @covers phpOMS\Utils\Converter\Currency
* @group framework
* @group maybe
*/
public function testInvalidFromEur() : void
{
@ -75,7 +98,6 @@ class CurrencyTest extends \PHPUnit\Framework\TestCase
* @testdox A currency conversion from a invalid currency to eur throws a InvalidArgumentException
* @covers phpOMS\Utils\Converter\Currency
* @group framework
* @group maybe
*/
public function testInvalidToEur() : void
{
@ -88,7 +110,6 @@ class CurrencyTest extends \PHPUnit\Framework\TestCase
* @testdox A currency conversion from a invalid currency to a invalid currency throws a InvalidArgumentException
* @covers phpOMS\Utils\Converter\Currency
* @group framework
* @group maybe
*/
public function testInvalidConvert() : void
{

View File

@ -92,5 +92,7 @@ class NumericTest extends \PHPUnit\Framework\TestCase
self::assertEquals('123', Numeric::convertBase('173', '01234567', '0123456789'));
self::assertEquals('173', Numeric::convertBase('173', '01234567', '01234567'));
self::assertEquals('2', Numeric::convertBase('2', '0123456789', '0123456789ABCDEF'));
}
}

View File

@ -30,8 +30,14 @@ class CaesarTest extends \PHPUnit\Framework\TestCase
*/
public function testEncoding() : void
{
$raw = StringUtils::generateString(1, 100);
$key = StringUtils::generateString(1, 100);
$raw = StringUtils::generateString(11, 100);
$key = StringUtils::generateString(5, 10);
self::assertNotEquals($raw, Caesar::encode($raw, $key));
self::assertEquals($raw, Caesar::decode(Caesar::encode($raw, $key), $key));
$raw = StringUtils::generateString(5, 10);
$key = StringUtils::generateString(11, 100);
self::assertNotEquals($raw, Caesar::encode($raw, $key));
self::assertEquals($raw, Caesar::decode(Caesar::encode($raw, $key), $key));

View File

@ -109,6 +109,15 @@ class TarGzTest extends \PHPUnit\Framework\TestCase
));
\unlink(__DIR__ . '/test2.tar.gz');
self::assertFalse(TarGz::pack(
[
__DIR__ . '/test a.txt' => 'test a.txt',
__DIR__ . '/test b.md' => 'test b.md',
__DIR__ . '/test' => 'test',
],
__DIR__ . '/invalidpack.tar.gz'
));
}
/**
@ -139,7 +148,17 @@ class TarGzTest extends \PHPUnit\Framework\TestCase
TarGz::unpack(__DIR__ . '/abc/test3.tar.gz', __DIR__);
self::assertFalse(TarGz::unpack(__DIR__ . '/abc/test3.tar.gz', __DIR__));
\unlink(__DIR__ . '/test3.tar.gz');
self::assertTrue(TarGz::pack(
[
__DIR__ . '/test a.txt' => 'test a.txt',
__DIR__ . '/test b.md' => 'test b.md',
__DIR__ . '/test' => 'test',
],
__DIR__ . '/invalidunpack.tar.gz'
));
self::assertFalse(TarGz::unpack(__DIR__ . '/invalidunpack.tar.gz', __DIR__));
\unlink(__DIR__ . '/invalidunpack.tar.gz');
}
}

View File

@ -83,6 +83,24 @@ class TarTest extends \PHPUnit\Framework\TestCase
\unlink(__DIR__ . '/test.tar');
}
/**
* @testdox Extracting invalid tar files fail
* @covers phpOMS\Utils\IO\Zip\Tar
* @group framework
*/
public function testInvalidTarUnpack() : void
{
self::assertFalse(Tar::unpack(
__DIR__ . '/invalid.tar',
__DIR__
));
self::assertFalse(Tar::unpack(
__DIR__ . '/test a.txt',
__DIR__
));
}
/**
* @testdox A tar archive cannot be overwritten by default
* @covers phpOMS\Utils\IO\Zip\Tar

View File

@ -43,6 +43,7 @@ class ZipTest extends \PHPUnit\Framework\TestCase
[
__DIR__ . '/test a.txt' => 'test a.txt',
__DIR__ . '/test b.md' => 'test b.md',
__DIR__ . '/invalid.so' => 'invalid.so',
__DIR__ . '/test' => 'test',
],
__DIR__ . '/test.zip'
@ -83,6 +84,41 @@ class ZipTest extends \PHPUnit\Framework\TestCase
\unlink(__DIR__ . '/test.zip');
}
/**
* @testdox The output of the zip archive needs to be properly defined
* @covers phpOMS\Utils\IO\Zip\Zip
* @group framework
*/
public function testInvalidZipDestination() : void
{
self::assertFalse(Zip::pack(
[
__DIR__ . '/test a.txt' => 'test a.txt',
__DIR__ . '/test b.md' => 'test b.md',
__DIR__ . '/test' => 'test',
],
__DIR__
));
}
/**
* @testdox Extracting invalid zip files fail
* @covers phpOMS\Utils\IO\Zip\Zip
* @group framework
*/
public function testInvalidZipUnpack() : void
{
self::assertFalse(Zip::unpack(
__DIR__ . '/invalid.zip',
__DIR__
));
self::assertFalse(Zip::unpack(
__DIR__ . '/test a.txt',
__DIR__
));
}
/**
* @testdox A zip archive cannot be overwritten by default
* @covers phpOMS\Utils\IO\Zip\Zip

View File

View File

@ -80,4 +80,18 @@ class StringCompareTest extends \PHPUnit\Framework\TestCase
$this->dict->add('Carton');
self::assertEquals('Carton', $this->dict->matchDictionary('carton'));
}
/**
* @testdox Two texts can be compared on a per word basis for similarity
* @covers phpOMS\Utils\StringCompare
* @group framework
*/
public function testValueWords() : void
{
// every word in s1 is found in s2, therefore a "perfect" match
self::assertEquals(0, StringCompare::valueWords('This is a test', 'This is not a test'));
// a is compared to is which has a distance of 2
self::assertEquals(2, StringCompare::valueWords('This is a test', 'This is not test'));
}
}

View File

@ -27,7 +27,7 @@ class SchedulerFactoryTest extends \PHPUnit\Framework\TestCase
{
/**
* @testdox The correct scheduler is crated depending on the operating system
* @covers phpOMS\Utils\TaskSchedule\SchedulerAbstract
* @covers phpOMS\Utils\TaskSchedule\SchedulerFactory
* @group framework
*/
public function testCreate() : void

View File

@ -15,6 +15,7 @@ declare(strict_types=1);
namespace phpOMS\tests\Utils\TaskSchedule;
use phpOMS\Utils\TaskSchedule\TaskAbstract;
use phpOMS\Utils\TaskSchedule\TaskFactory;
/**
* @testdox phpOMS\tests\Utils\TaskSchedule\TaskAbstractTest: Job/task abstraction
@ -35,6 +36,7 @@ class TaskAbstractTest extends \PHPUnit\Framework\TestCase
public static function createWith(array $jobData) : TaskAbstract
{
return TaskFactory::create();
}
};
}
@ -66,6 +68,17 @@ class TaskAbstractTest extends \PHPUnit\Framework\TestCase
self::assertEquals('Command', $this->class->getCommand());
}
/**
* @testdox The interval can be set and returned
* @covers phpOMS\Utils\TaskSchedule\TaskAbstract
* @group framework
*/
public function testIntervalInputOutput() : void
{
$this->class->setInterval('Interval');
self::assertEquals('Interval', $this->class->getInterval());
}
/**
* @testdox The status can be set and returned
* @covers phpOMS\Utils\TaskSchedule\TaskAbstract

View File

@ -109,4 +109,21 @@ class ValidatorTest extends \PHPUnit\Framework\TestCase
self::assertTrue(Validator::matches('ThisTestVar', '/TestVar/'));
self::assertFalse(Validator::matches('ThisTestVar', '/ThisTest$/'));
}
public function testErrorMessage() : void
{
self::assertEquals('', Validator::getMessage());
}
public function testErrorCode() : void
{
self::assertEquals(0, Validator::getErrorCode());
}
public function testResetError() : void
{
Validator::resetError();
self::assertEquals('', Validator::getMessage());
self::assertEquals(0, Validator::getErrorCode());
}
}