diff --git a/Ai/Ocr/BasicOcr.php b/Ai/Ocr/BasicOcr.php index 77caef568..37bfd8df8 100644 --- a/Ai/Ocr/BasicOcr.php +++ b/Ai/Ocr/BasicOcr.php @@ -205,7 +205,7 @@ final class BasicOcr foreach ($candidateLabels as $i => $label) { $predictedLabels[] = [ 'label' => $label, - 'prob' => $countedCandidates[$candidateLabels[$i]] / $k, + 'prob' => $countedCandidates[$label] / $k, ]; } } diff --git a/Algorithm/Maze/MazeGenerator.php b/Algorithm/Maze/MazeGenerator.php index bff925ede..c572d5c08 100644 --- a/Algorithm/Maze/MazeGenerator.php +++ b/Algorithm/Maze/MazeGenerator.php @@ -97,7 +97,7 @@ class MazeGenerator $pos = \array_pop($path); if ($pos === null) { - break; + break; // @codeCoverageIgnore } } } diff --git a/Algorithm/Sort/CycleSort.php b/Algorithm/Sort/CycleSort.php index 11ea847c6..0382ecf21 100644 --- a/Algorithm/Sort/CycleSort.php +++ b/Algorithm/Sort/CycleSort.php @@ -64,14 +64,16 @@ final class CycleSort implements SortInterface ++$pos; } - $old = $list[$pos]; - $list[$pos] = $item; - $item = $old; + if ($pos !== $start) { + $old = $list[$pos]; + $list[$pos] = $item; + $item = $old; + } while ($pos !== $start) { - $pos = $start; - $length1 = \count($list); - for ($i = $start + 1; $i < $length1; ++$i) { + $pos = $start; + + for ($i = $start + 1; $i < $n; ++$i) { if (!$list[$i]->compare($item, $order)) { ++$pos; } @@ -81,9 +83,11 @@ final class CycleSort implements SortInterface ++$pos; } - $old = $list[$pos]; - $list[$pos] = $item; - $item = $old; + if (!$item->equals($list[$pos])) { + $old = $list[$pos]; + $list[$pos] = $item; + $item = $old; + } } } diff --git a/Algorithm/Sort/QuickSort.php b/Algorithm/Sort/QuickSort.php index 294200a67..69dcd6a00 100644 --- a/Algorithm/Sort/QuickSort.php +++ b/Algorithm/Sort/QuickSort.php @@ -67,7 +67,7 @@ final class QuickSort implements SortInterface { if ($lo < $hi) { $i = self::partition($list, $lo, $hi, $order); - self::qsort($list, $lo, $i, $order); + self::qsort($list, $lo, $i - 1, $order); self::qsort($list, $i + 1, $hi, $order); } } @@ -86,26 +86,22 @@ final class QuickSort implements SortInterface */ private static function partition(array &$list, int $lo, int $hi, int $order) : int { - $pivot = $list[$lo + ((int) (($hi - $lo) / 2))]; - while (true) { - while (!$list[$lo]->compare($pivot, $order)) { - ++$lo; + $pivot = $list[$hi]; + $i = $lo - 1; + + for ($j = $lo; $j <= $hi - 1; ++$j) { + if (!$list[$j]->compare($pivot, $order)) { + ++$i; + $old = $list[$i]; + $list[$i] = $list[$j]; + $list[$j] = $old; } - - while ($list[$hi]->compare($pivot, $order)) { - --$hi; - } - - if ($lo >= $hi) { - return $hi; - } - - $old = $list[$lo]; - $list[$lo] = $list[$hi]; - $list[$hi] = $old; - - ++$lo; - --$hi; } + + $old = $list[$i + 1]; + $list[$i + 1] = $list[$hi]; + $list[$hi] = $old; + + return $i + 1; } } diff --git a/Application/ApplicationManager.php b/Application/ApplicationManager.php index e4641a6ad..5b59e5598 100644 --- a/Application/ApplicationManager.php +++ b/Application/ApplicationManager.php @@ -98,11 +98,7 @@ final class ApplicationManager $destination = \rtrim($destination, '\\/'); $source = \rtrim($source, '/\\'); - if (!\is_dir($source) || \is_dir($destination)) { - return false; - } - - if (!\is_file($source . '/Admin/Installer.php')) { + if (!\is_dir($source) || \is_dir($destination) || !\is_file($source . '/Admin/Installer.php')) { return false; } @@ -124,6 +120,73 @@ final class ApplicationManager } } + /** + * Uninstall the application + * + * @param string $source Source of the application + * + * @return bool + * + * @since 1.0.0 + */ + public function uninstall(string $source) : bool + { + $source = \rtrim($source, '/\\'); + if (!\is_dir($source) || !\is_file($source . '/Admin/Uninstaller.php')) { + return false; + } + + try { + $info = $this->loadInfo($source . '/info.json'); + $this->installed[$info->getInternalName()] = $info; + + $classPath = \substr(\realpath($source) . '/Admin/Uninstaller', $t = \strlen(\realpath(__DIR__ . '/../../'))); + + $class = \str_replace('/', '\\', $classPath); + $class::uninstall($this->app->dbPool, $info, $this->app->appSettings); + + $this->uninstallFiles($source); + + return true; + } catch (\Throwable $t) { + return false; // @codeCoverageIgnore + } + } + + /** + * Re-init application. + * + * @param string $appPath App path + * + * @return void + * + * @throws InvalidModuleException Throws this exception in case the installer doesn't exist + * + * @since 1.0.0 + */ + public function reInit(string $appPath) : void + { + $info = $this->loadInfo($appPath . '/info.json'); + if ($info === null) { + return; + } + + $classPath = \substr(\realpath($appPath) . '/Admin/Installer', \strlen(\realpath(__DIR__ . '/../../'))); + $class = \str_replace('/', '\\', $classPath); + + /** @var $class InstallerAbstract */ + $class::reInit($info); + } + + /** + * Get all applications who are providing for a specific module. + * + * @param string $module Module to check providings for + * + * @return array + * + * @since 1.0.0 + */ public function getProvidingForModule(string $module) : array { $providing = []; @@ -189,6 +252,20 @@ final class ApplicationManager Directory::copy($source, $destination); } + /** + * Uninstall files + * + * @param string $source Source path + * + * @return void + * + * @since 1.0.0 + */ + private function uninstallFiles(string $source) : void + { + Directory::delete($source); + } + /** * Replace placeholder string (application placeholder name) * diff --git a/Application/InstallerAbstract.php b/Application/InstallerAbstract.php index 318464559..9cb33bdfb 100644 --- a/Application/InstallerAbstract.php +++ b/Application/InstallerAbstract.php @@ -143,6 +143,10 @@ abstract class InstallerAbstract $classPath = \substr(\realpath(static::PATH) . '/Status', \strlen(\realpath(__DIR__ . '/../../'))); $class = \str_replace('/', '\\', $classPath); + + $class::clearRoutes(); + $class::clearHooks(); + $class::activateRoutes($info); $class::activateHooks($info); } diff --git a/Application/StatusAbstract.php b/Application/StatusAbstract.php index 1d40bdc9f..5275edb0b 100644 --- a/Application/StatusAbstract.php +++ b/Application/StatusAbstract.php @@ -63,7 +63,7 @@ abstract class StatusAbstract */ public static function activateRoutes(ApplicationInfo $appInfo = null) : void { - self::installRoutes(static::PATH . '/../Routes.php', static::PATH . '/../Admin/Install/Application/Routes.php'); + self::installRoutesHooks(static::PATH . '/../Routes.php', static::PATH . '/../Admin/Install/Application/Routes.php'); } /** @@ -79,7 +79,7 @@ abstract class StatusAbstract */ public static function activateHooks(ApplicationInfo $appInfo = null) : void { - self::installRoutes(static::PATH . '/../Hooks.php', static::PATH . '/../Admin/Install/Application/Hooks.php'); + self::installRoutesHooks(static::PATH . '/../Hooks.php', static::PATH . '/../Admin/Install/Application/Hooks.php'); } /** @@ -94,7 +94,7 @@ abstract class StatusAbstract * * @since 1.0.0 */ - protected static function installRoutes(string $destRoutePath, string $srcRoutePath) : void + protected static function installRoutesHooks(string $destRoutePath, string $srcRoutePath) : void { if (!\is_file($destRoutePath)) { \file_put_contents($destRoutePath, 'getInternalName() . '\Admin\Status'; + $classPath = \substr(\realpath(static::PATH) . '/Status', \strlen(\realpath(__DIR__ . '/../../'))); + + $class = \str_replace('/', '\\', $classPath); $class::deactivate($dbPool, $info); - } + }*/ /** * Drop tables of app. @@ -74,8 +76,7 @@ abstract class UninstallerAbstract */ public static function dropTables(DatabasePool $dbPool, ApplicationInfo $info) : void { - $path = __DIR__ . '/../../Web/' . $info->getInternalName() . '/Admin/Install/db.json'; - + $path = static::PATH . '/Install/db.json'; if (!\is_file($path)) { return; } @@ -88,8 +89,8 @@ abstract class UninstallerAbstract $definitions = \json_decode($content, true); $builder = new SchemaBuilder($dbPool->get('schema')); - foreach ($definitions as $definition) { - $builder->dropTable($definition['table'] ?? ''); + foreach ($definitions as $name => $definition) { + $builder->dropTable($name ?? ''); } $builder->execute(); diff --git a/Business/Marketing/CustomerValue.php b/Business/Marketing/CustomerValue.php index 73661210c..be6d45f20 100644 --- a/Business/Marketing/CustomerValue.php +++ b/Business/Marketing/CustomerValue.php @@ -27,7 +27,7 @@ final class CustomerValue /** * Simple customer lifetime value * - * Hazard Model, same as $margin * (1 + $discountRate) / (1 + $discountRate - $retentionRate) + * Hazard Model * * @param float $margin Margin per period (or revenue/sales) * @param float $retentionRate Rate of remaining customers per period (= average lifetime / (1 + average lifetime)) @@ -42,32 +42,6 @@ final class CustomerValue return $margin * $retentionRate / (1 + $discountRate - $retentionRate); } - /** - * Basic customer lifetime value - * - * Hazard Model, same as $margin * (1 + $discountRate) / (1 + $discountRate - $retentionRate) - * - * @param array $margins Margin per period (or revenue/sales) - * @param float $retentionRate Rate of remaining customers per period (= average lifetime / (1 + average lifetime)) - * @param float $discountRate Cost of capital to discount future revenue - * - * @return float - * - * @since 1.0.0 - */ - public static function getBasicCLV(array $margins, float $retentionRate, float $discountRate) : float - { - $clv = 0.0; - $c = 1; - foreach ($margins as $margin) { - $clv += ($retentionRate ** $c) * $margin * \pow(1 / (1 + $discountRate), $c); - - ++$c; - } - - return $clv; - } - /** * Normalized measure of recurring revenue * diff --git a/Business/Marketing/Metrics.php b/Business/Marketing/Metrics.php index 4dc9dcf01..2407469d7 100644 --- a/Business/Marketing/Metrics.php +++ b/Business/Marketing/Metrics.php @@ -90,26 +90,12 @@ final class Metrics return $rc * (1 - \exp(-$r * $t)); } - /** - * Calculate the average lifetime duration - * - * @param array $retainedCustomers Retained customers per period - * - * @return float - * - * @since 1.0.0 - */ - public static function averageLifetimeDuration(array $retainedCustomers) : float - { - return \array_sum($retainedCustomers) / \count($retainedCustomers); - } - /** * Calculate the probability of a customer being active * * @param int $purchases Number of purchases during the periods * @param int $periods Number of periods (e.g. number of months) - * @param int $lastPurchase In which period was the last purchase + * @param int $lastPurchase In which period was the last purchase (lastPurchase = periods: means customer purchased in this period) * * @return float * diff --git a/DataStorage/Cache/Connection/ConnectionInterface.php b/DataStorage/Cache/Connection/ConnectionInterface.php index 8f7f35c75..f91ad297a 100644 --- a/DataStorage/Cache/Connection/ConnectionInterface.php +++ b/DataStorage/Cache/Connection/ConnectionInterface.php @@ -45,11 +45,11 @@ interface ConnectionInterface extends DataStorageConnectionInterface * @param int|string $key Unique cache key * @param int $value By value * - * @return void + * @return bool * * @since 1.0.0 */ - public function increment(int | string $key, int $value = 1) : void; + public function increment(int | string $key, int $value = 1) : bool; /** * Decrement value. @@ -57,11 +57,11 @@ interface ConnectionInterface extends DataStorageConnectionInterface * @param int|string $key Unique cache key * @param int $value By value * - * @return void + * @return bool * * @since 1.0.0 */ - public function decrement(int | string $key, int $value = 1) : void; + public function decrement(int | string $key, int $value = 1) : bool; /** * Rename cache key. diff --git a/DataStorage/Cache/Connection/FileCache.php b/DataStorage/Cache/Connection/FileCache.php index 1f409f7b2..8731efca8 100644 --- a/DataStorage/Cache/Connection/FileCache.php +++ b/DataStorage/Cache/Connection/FileCache.php @@ -238,7 +238,7 @@ final class FileCache extends ConnectionAbstract return ''; } - throw new InvalidEnumValue($type); + throw new InvalidEnumValue($type); // @codeCoverageIgnore } /** @@ -281,7 +281,7 @@ final class FileCache extends ConnectionAbstract $raw = \file_get_contents($path); if ($raw === false) { - return null; + return null; // @codeCoverageIgnore } $type = (int) $raw[0]; @@ -289,7 +289,7 @@ final class FileCache extends ConnectionAbstract $expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1); if ($expireStart < 0 || $expireEnd < 0) { - return null; + return null; // @codeCoverageIgnore } $cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1)); @@ -337,7 +337,7 @@ final class FileCache extends ConnectionAbstract $namespace = \substr($raw, $namespaceStart + 1, $namespaceEnd - $namespaceStart - 1); if ($namespace === false) { - return null; + return null; // @codeCoverageIgnore } return new $namespace(); @@ -347,7 +347,7 @@ final class FileCache extends ConnectionAbstract $namespace = \substr($raw, $namespaceStart + 1, $namespaceEnd - $namespaceStart - 1); if ($namespace === false) { - return null; + return null; // @codeCoverageIgnore } $obj = new $namespace(); @@ -385,7 +385,7 @@ final class FileCache extends ConnectionAbstract $raw = \file_get_contents($path); if ($raw === false) { - return false; + return false; // @codeCoverageIgnore } $cacheExpire = $this->getExpire($raw); @@ -426,7 +426,7 @@ final class FileCache extends ConnectionAbstract $raw = \file_get_contents($path); if ($raw === false) { - return false; + return false; // @codeCoverageIgnore } $type = (int) $raw[0]; @@ -434,7 +434,7 @@ final class FileCache extends ConnectionAbstract $expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1); if ($expireStart < 0 || $expireEnd < 0) { - return false; + return false; // @codeCoverageIgnore } $cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1)); @@ -452,11 +452,11 @@ final class FileCache extends ConnectionAbstract /** * {@inheritdoc} */ - public function increment(int | string $key, int $value = 1) : void + public function increment(int | string $key, int $value = 1) : bool { $path = $this->getPath($key); if (!File::exists($path)) { - return; + return false; } $created = File::created($path)->getTimestamp(); @@ -464,7 +464,7 @@ final class FileCache extends ConnectionAbstract $raw = \file_get_contents($path); if ($raw === false) { - return; + return false; // @codeCoverageIgnore } $type = (int) $raw[0]; @@ -472,7 +472,7 @@ final class FileCache extends ConnectionAbstract $expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1); if ($expireStart < 0 || $expireEnd < 0) { - return; + return false; // @codeCoverageIgnore } $cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1)); @@ -480,16 +480,18 @@ final class FileCache extends ConnectionAbstract $val = $this->reverseValue($type, $raw, $expireEnd); $this->set($key, $val + $value, $cacheExpire); + + return true; } /** * {@inheritdoc} */ - public function decrement(int | string $key, int $value = 1) : void + public function decrement(int | string $key, int $value = 1) : bool { $path = $this->getPath($key); if (!File::exists($path)) { - return; + return false; } $created = File::created($path)->getTimestamp(); @@ -497,7 +499,7 @@ final class FileCache extends ConnectionAbstract $raw = \file_get_contents($path); if ($raw === false) { - return; + return false; // @codeCoverageIgnore } $type = (int) $raw[0]; @@ -505,7 +507,7 @@ final class FileCache extends ConnectionAbstract $expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1); if ($expireStart < 0 || $expireEnd < 0) { - return; + return false; // @codeCoverageIgnore } $cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1)); @@ -513,6 +515,8 @@ final class FileCache extends ConnectionAbstract $val = $this->reverseValue($type, $raw, $expireEnd); $this->set($key, $val - $value, $cacheExpire); + + return true; } /** @@ -548,7 +552,7 @@ final class FileCache extends ConnectionAbstract $raw = \file_get_contents($path); if ($raw === false) { - continue; + continue; // @codeCoverageIgnore } $type = (int) $raw[0]; @@ -556,7 +560,7 @@ final class FileCache extends ConnectionAbstract $expireEnd = (int) \strpos($raw, self::DELIM, $expireStart + 1); if ($expireStart < 0 || $expireEnd < 0) { - continue; + continue; // @codeCoverageIgnore } $cacheExpire = \substr($raw, $expireStart + 1, $expireEnd - ($expireStart + 1)); @@ -600,7 +604,7 @@ final class FileCache extends ConnectionAbstract $raw = \file_get_contents($path); if ($raw === false) { - continue; + continue; // @codeCoverageIgnore } $cacheExpire = $this->getExpire($raw); diff --git a/DataStorage/Cache/Connection/MemCached.php b/DataStorage/Cache/Connection/MemCached.php index 63db78bf1..eb59a78bd 100644 --- a/DataStorage/Cache/Connection/MemCached.php +++ b/DataStorage/Cache/Connection/MemCached.php @@ -154,17 +154,21 @@ final class MemCached extends ConnectionAbstract /** * {@inheritdoc} */ - public function increment(int | string $key, int $value = 1) : void + public function increment(int | string $key, int $value = 1) : bool { $this->con->increment($key, $value); + + return true; } /** * {@inheritdoc} */ - public function decrement(int | string $key, int $value = 1) : void + public function decrement(int | string $key, int $value = 1) : bool { $this->con->decrement($key, $value); + + return true; } /** diff --git a/DataStorage/Cache/Connection/NullCache.php b/DataStorage/Cache/Connection/NullCache.php index 22b4ea3f5..2c02d42f3 100644 --- a/DataStorage/Cache/Connection/NullCache.php +++ b/DataStorage/Cache/Connection/NullCache.php @@ -41,15 +41,17 @@ final class NullCache extends ConnectionAbstract /** * {@inheritdoc} */ - public function increment(int | string $key, int $value = 1) : void + public function increment(int | string $key, int $value = 1) : bool { + return true; } /** * {@inheritdoc} */ - public function decrement(int | string $key, int $value = 1) : void + public function decrement(int | string $key, int $value = 1) : bool { + return true; } /** diff --git a/DataStorage/Cache/Connection/RedisCache.php b/DataStorage/Cache/Connection/RedisCache.php index e74768ce0..2cf37630a 100644 --- a/DataStorage/Cache/Connection/RedisCache.php +++ b/DataStorage/Cache/Connection/RedisCache.php @@ -182,17 +182,21 @@ final class RedisCache extends ConnectionAbstract /** * {@inheritdoc} */ - public function increment(int | string $key, int $value = 1) : void + public function increment(int | string $key, int $value = 1) : bool { $this->con->incrBy($key, $value); + + return true; } /** * {@inheritdoc} */ - public function decrement(int | string $key, int $value = 1) : void + public function decrement(int | string $key, int $value = 1) : bool { $this->con->decrBy($key, $value); + + return true; } /** diff --git a/DataStorage/Database/Schema/Builder.php b/DataStorage/Database/Schema/Builder.php index 0cd6f0016..f77965a1f 100644 --- a/DataStorage/Database/Schema/Builder.php +++ b/DataStorage/Database/Schema/Builder.php @@ -54,10 +54,10 @@ class Builder extends QueryBuilder /** * Table to drop. * - * @var string + * @var array * @since 1.0.0 */ - public string $dropTable = ''; + public array $dropTable = []; /** * Tables. @@ -192,8 +192,8 @@ class Builder extends QueryBuilder */ public function dropTable(string $table) : self { - $this->type = QueryType::DROP_TABLE; - $this->dropTable = $table; + $this->type = QueryType::DROP_TABLE; + $this->dropTable[] = $table; return $this; } diff --git a/DataStorage/Database/Schema/Grammar/Grammar.php b/DataStorage/Database/Schema/Grammar/Grammar.php index 30240713e..9ce234ffd 100644 --- a/DataStorage/Database/Schema/Grammar/Grammar.php +++ b/DataStorage/Database/Schema/Grammar/Grammar.php @@ -232,16 +232,16 @@ class Grammar extends QueryGrammar /** * Compile drop query. * - * @param BuilderAbstract $query Query - * @param string $table Tables to drop + * @param BuilderAbstract $query Query + * @param string $database Tables to drop * * @return string * * @since 1.0.0 */ - protected function compileDropDatabase(BuilderAbstract $query, string $table) : string + protected function compileDropDatabase(BuilderAbstract $query, string $database) : string { - $expression = $this->expressionizeTableColumn([$table]); + $expression = $this->expressionizeTableColumn([$database]); if ($expression === '') { $expression = '*'; @@ -253,16 +253,16 @@ class Grammar extends QueryGrammar /** * Compile drop query. * - * @param BuilderAbstract $query Query - * @param string $table Tables to drop + * @param BuilderAbstract $query Query + * @param array $tables Tables to drop * * @return string * * @since 1.0.0 */ - protected function compileDropTable(BuilderAbstract $query, string $table) : string + protected function compileDropTable(BuilderAbstract $query, array $tables) : string { - $expression = $this->expressionizeTableColumn([$table]); + $expression = $this->expressionizeTableColumn($tables); if ($expression === '') { $expression = '*'; diff --git a/Math/Number/Prime.php b/Math/Number/Prime.php index 887e43ff0..46d865f80 100644 --- a/Math/Number/Prime.php +++ b/Math/Number/Prime.php @@ -142,7 +142,7 @@ final class Prime $primes = \array_combine($range, $range); if ($primes === false) { - return []; + return []; // @codeCoverageIgnore } while ($number * $number < $n) { diff --git a/Module/ModuleAbstract.php b/Module/ModuleAbstract.php index 1e68e93a0..9f23b88c6 100644 --- a/Module/ModuleAbstract.php +++ b/Module/ModuleAbstract.php @@ -121,7 +121,7 @@ abstract class ModuleAbstract public static function getLocalization(string $language, string $destination) : array { $lang = []; - if (\is_file($oldPath = static::PATH . static::NAME . '/Theme/' . $destination . '/Lang/' . $language . '.lang.php')) { + if (\is_file($oldPath = static::PATH . '/Theme/' . $destination . '/Lang/' . $language . '.lang.php')) { /** @noinspection PhpIncludeInspection */ return include $oldPath; } diff --git a/Module/StatusAbstract.php b/Module/StatusAbstract.php index 89ce3accc..9c4893ff9 100644 --- a/Module/StatusAbstract.php +++ b/Module/StatusAbstract.php @@ -77,7 +77,7 @@ abstract class StatusAbstract continue; } - self::installRoutes(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath()); + self::installRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath()); } } elseif ($child instanceof File) { if (!\is_dir(__DIR__ . '/../../' . $child->getName()) @@ -86,7 +86,7 @@ abstract class StatusAbstract continue; } - self::installRoutes(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath()); + self::installRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath()); } } } @@ -103,7 +103,7 @@ abstract class StatusAbstract * * @since 1.0.0 */ - protected static function installRoutes(string $destRoutePath, string $srcRoutePath) : void + protected static function installRoutesHooks(string $destRoutePath, string $srcRoutePath) : void { if (!\is_file($destRoutePath)) { \file_put_contents($destRoutePath, 'getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath()); + self::installRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath()); } } elseif ($child instanceof File) { if (!\is_dir(__DIR__ . '/../../' . $child->getName()) @@ -166,52 +166,11 @@ abstract class StatusAbstract continue; } - self::installHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath()); + self::installRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath()); } } } - /** - * Install hooks. - * - * @param string $destHookPath Destination hook path - * @param string $srcHookPath Source hook path - * - * @return void - * - * @throws PathException This exception is thrown if the hook file doesn't exist - * @throws PermissionException This exception is thrown if the hook file couldn't be updated (no write permission) - * - * @since 1.0.0 - */ - protected static function installHooks(string $destHookPath, string $srcHookPath) : void - { - if (!\is_file($destHookPath)) { - \file_put_contents($destHookPath, 'getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath()); + self::uninstallRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Routes.php', $file->getPath()); } } elseif ($child instanceof File) { if (!\is_dir(__DIR__ . '/../../' . $child->getName()) @@ -261,7 +220,7 @@ abstract class StatusAbstract continue; } - self::uninstallRoutes(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath()); + self::uninstallRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/Routes.php', $child->getPath()); } } } @@ -278,7 +237,7 @@ abstract class StatusAbstract * * @since 1.0.0 */ - public static function uninstallRoutes(string $destRoutePath, string $srcRoutePath) : void + public static function uninstallRoutesHooks(string $destRoutePath, string $srcRoutePath) : void { if (!\is_file($destRoutePath) || !\is_file($srcRoutePath) @@ -328,7 +287,7 @@ abstract class StatusAbstract continue; } - self::uninstallHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath()); + self::uninstallRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/' . \basename($file->getName(), '.php') . '/Hooks.php', $file->getPath()); } } elseif ($child instanceof File) { if (!\is_dir(__DIR__ . '/../../' . $child->getName()) @@ -337,46 +296,8 @@ abstract class StatusAbstract continue; } - self::uninstallHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath()); + self::uninstallRoutesHooks(__DIR__ . '/../../' . $child->getName() . '/Hooks.php', $child->getPath()); } } } - - /** - * Uninstall hooks. - * - * @param string $destHookPath Destination hook path - * @param string $srcHookPath Source hook path - * - * @return void - * - * @throws PermissionException - * - * @since 1.0.0 - */ - protected static function uninstallHooks(string $destHookPath, string $srcHookPath) : void - { - if (!\is_file($destHookPath) - || !\is_file($srcHookPath) - ) { - return; - } - - if (!\is_file($destHookPath)) { - throw new PathException($destHookPath); - } - - if (!\is_writable($destHookPath)) { - throw new PermissionException($destHookPath); - } - - /** @noinspection PhpIncludeInspection */ - $appHooks = include $destHookPath; - /** @noinspection PhpIncludeInspection */ - $moduleHooks = include $srcHookPath; - - $appHooks = ArrayUtils::array_diff_assoc_recursive($appHooks, $moduleHooks); - - \file_put_contents($destHookPath, 'get('schema')); - foreach ($definitions as $definition) { - $builder->dropTable($definition['table'] ?? ''); + foreach ($definitions as $name => $definition) { + $builder->dropTable($name ?? ''); } $builder->execute(); diff --git a/tests/Account/AccountTest.php b/tests/Account/AccountTest.php index a3a7fec0a..6bf4ab26c 100644 --- a/tests/Account/AccountTest.php +++ b/tests/Account/AccountTest.php @@ -18,6 +18,7 @@ use phpOMS\Account\Account; use phpOMS\Account\AccountStatus; use phpOMS\Account\AccountType; use phpOMS\Account\Group; +use phpOMS\Account\NullGroup; use phpOMS\Account\PermissionAbstract; use phpOMS\Account\PermissionType; use phpOMS\Localization\L11nManager; @@ -107,6 +108,7 @@ class AccountTest extends \PHPUnit\Framework\TestCase self::assertEquals(AccountType::USER, $account->getType()); self::assertEquals([], $account->getPermissions()); + self::assertFalse($account->hasGroup(2)); self::assertInstanceOf('\DateTimeInterface', $account->getLastActive()); self::assertInstanceOf('\DateTimeImmutable', $account->createdAt); @@ -151,8 +153,9 @@ class AccountTest extends \PHPUnit\Framework\TestCase $account = new Account(); $account->generatePassword('abcd'); - $account->addGroup(new Group()); + $account->addGroup(new NullGroup(2)); self::assertCount(1, $account->getGroups()); + self::assertTrue($account->hasGroup(2)); } /** diff --git a/tests/Algorithm/JobScheduling/WeightedTest.php b/tests/Algorithm/JobScheduling/WeightedTest.php index 701b01605..fc9cfc42a 100644 --- a/tests/Algorithm/JobScheduling/WeightedTest.php +++ b/tests/Algorithm/JobScheduling/WeightedTest.php @@ -32,9 +32,9 @@ 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(10, new \DateTime('2000-01-01'), null, '0'), 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'), diff --git a/tests/Algorithm/PathFinding/AStarTest.php b/tests/Algorithm/PathFinding/AStarTest.php index 5d5f619ba..b7b3a0184 100644 --- a/tests/Algorithm/PathFinding/AStarTest.php +++ b/tests/Algorithm/PathFinding/AStarTest.php @@ -17,6 +17,7 @@ namespace phpOMS\tests\Algorithm\PathFinding; use phpOMS\Algorithm\PathFinding\AStar; use phpOMS\Algorithm\PathFinding\AStarNode; use phpOMS\Algorithm\PathFinding\Grid; +use phpOMS\Algorithm\PathFinding\Path; use phpOMS\Algorithm\PathFinding\HeuristicType; use phpOMS\Algorithm\PathFinding\MovementType; @@ -249,4 +250,23 @@ class AStarTest extends \PHPUnit\Framework\TestCase [0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], ], $this->gridArray); } + + /** + * @testdox A invalid start or end node returns the grid + * @covers phpOMS\Algorithm\PathFinding\AStar + * @group framework + */ + public function testInvalidStartEndNode() : void + { + $grid = Grid::createGridFromArray($this->gridArray, AStarNode::class); + + self::assertEquals( + new Path($grid), + $path = AStar::findPath( + 999, 999, + -999, -999, + $grid, HeuristicType::EUCLIDEAN, MovementType::DIAGONAL_NO_OBSTACLE + ) + ); + } } diff --git a/tests/Algorithm/PathFinding/JumpPointSearchTest.php b/tests/Algorithm/PathFinding/JumpPointSearchTest.php index dbe54e6b9..cde4bc275 100644 --- a/tests/Algorithm/PathFinding/JumpPointSearchTest.php +++ b/tests/Algorithm/PathFinding/JumpPointSearchTest.php @@ -15,6 +15,7 @@ declare(strict_types=1); namespace phpOMS\tests\Algorithm\PathFinding; use phpOMS\Algorithm\PathFinding\Grid; +use phpOMS\Algorithm\PathFinding\Path; use phpOMS\Algorithm\PathFinding\HeuristicType; use phpOMS\Algorithm\PathFinding\JumpPointNode; use phpOMS\Algorithm\PathFinding\JumpPointSearch; @@ -249,4 +250,23 @@ class JumpPointSearchTest extends \PHPUnit\Framework\TestCase [0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], ], $this->gridArray); } + + /** + * @testdox A invalid start or end node returns the grid + * @covers phpOMS\Algorithm\PathFinding\JumpPointSearch + * @group framework + */ + public function testInvalidStartEndNode() : void + { + $grid = Grid::createGridFromArray($this->gridArray, JumpPointNode::class); + + self::assertEquals( + new Path($grid), + $path = JumpPointSearch::findPath( + 999, 999, + -999, -999, + $grid, HeuristicType::EUCLIDEAN, MovementType::DIAGONAL_NO_OBSTACLE + ) + ); + } } diff --git a/tests/Algorithm/Sort/CycleSortTest.php b/tests/Algorithm/Sort/CycleSortTest.php index 7aedb14ee..af9bd980c 100644 --- a/tests/Algorithm/Sort/CycleSortTest.php +++ b/tests/Algorithm/Sort/CycleSortTest.php @@ -38,6 +38,7 @@ class CycleSortTest extends \PHPUnit\Framework\TestCase new NumericElement(1), new NumericElement(4), new NumericElement(2), + new NumericElement(2), new NumericElement(8), ]; } @@ -62,11 +63,11 @@ class CycleSortTest extends \PHPUnit\Framework\TestCase { $newList = CycleSort::sort($this->list); self::assertEquals( - [1, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,] + [1, 2, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value, $newList[5]->value,] ); self::assertEquals( - [5, 1, 4, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value,] + [5, 1, 4, 2, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value, $this->list[5]->value,] ); } @@ -78,11 +79,11 @@ class CycleSortTest extends \PHPUnit\Framework\TestCase { $newList = CycleSort::sort($this->list, SortOrder::DESC); self::assertEquals( - [8, 5, 4, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,] + [8, 5, 4, 2, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value, $newList[5]->value,] ); self::assertEquals( - [5, 1, 4, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value,] + [5, 1, 4, 2, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value, $this->list[5]->value,] ); } } diff --git a/tests/Algorithm/Sort/NumericElement.php b/tests/Algorithm/Sort/NumericElement.php index 3cad47053..1d3a023c4 100644 --- a/tests/Algorithm/Sort/NumericElement.php +++ b/tests/Algorithm/Sort/NumericElement.php @@ -32,7 +32,7 @@ float $value = 0; public function compare(SortableInterface $obj, int $order = SortOrder::ASC) : bool { - return $order === SortOrder::ASC ? $this->value > $obj->value : $this->value < $obj->value; + return $order === SortOrder::ASC ? $this->value >= $obj->value : $this->value <= $obj->value; } public function equals(SortableInterface $obj) : bool diff --git a/tests/Algorithm/Sort/StoogeSortTest.php b/tests/Algorithm/Sort/StoogeSortTest.php index 61e5bbca5..cce5827ac 100644 --- a/tests/Algorithm/Sort/StoogeSortTest.php +++ b/tests/Algorithm/Sort/StoogeSortTest.php @@ -38,6 +38,7 @@ class StoogeSortTest extends \PHPUnit\Framework\TestCase new NumericElement(1), new NumericElement(4), new NumericElement(2), + new NumericElement(2), new NumericElement(8), ]; } @@ -62,11 +63,11 @@ class StoogeSortTest extends \PHPUnit\Framework\TestCase { $newList = StoogeSort::sort($this->list); self::assertEquals( - [1, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,] + [1, 2, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value, $newList[5]->value,] ); self::assertEquals( - [5, 1, 4, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value,] + [5, 1, 4, 2, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value, $this->list[5]->value,] ); } @@ -78,11 +79,11 @@ class StoogeSortTest extends \PHPUnit\Framework\TestCase { $newList = StoogeSort::sort($this->list, SortOrder::DESC); self::assertEquals( - [8, 5, 4, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,] + [8, 5, 4, 2, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value, $newList[5]->value,] ); self::assertEquals( - [5, 1, 4, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value,] + [5, 1, 4, 2, 2, 8], [$this->list[0]->value, $this->list[1]->value, $this->list[2]->value, $this->list[3]->value, $this->list[4]->value, $this->list[5]->value,] ); } } diff --git a/tests/Application/ApplicationManagerTest.php b/tests/Application/ApplicationManagerTest.php index 8df0fc9c5..2ac8d3f2f 100644 --- a/tests/Application/ApplicationManagerTest.php +++ b/tests/Application/ApplicationManagerTest.php @@ -76,10 +76,11 @@ class ApplicationManagerTest extends \PHPUnit\Framework\TestCase /** * @covers phpOMS\Application\ApplicationManager * @covers phpOMS\Application\InstallerAbstract + * @covers phpOMS\Application\UninstallerAbstract * @covers phpOMS\Application\StatusAbstract * @group framework */ - public function testInstall() : void + public function testInstallUninstall() : void { self::assertTrue($this->appManager->install(__DIR__ . '/Testapp', __DIR__ . '/Apps/Testapp')); self::assertTrue(\is_dir(__DIR__ . '/Apps/Testapp')); @@ -89,10 +90,32 @@ class ApplicationManagerTest extends \PHPUnit\Framework\TestCase self::assertTrue(isset($apps['Testapp'])); $providing = $this->appManager->getProvidingForModule('Navigation'); - Directory::delete(__DIR__ . '/Apps/Testapp'); self::assertTrue(isset($providing['Testapp'])); self::assertTrue(\in_array('Navigation', $providing['Testapp'])); + + $this->appManager->uninstall(__DIR__ . '/Apps/Testapp'); + self::assertFalse(\is_dir(__DIR__ . '/Apps/Testapp')); + } + + /** + * @testdox A module can be re-initialized + * @covers phpOMS\Application\ApplicationManager + * @covers phpOMS\Application\InstallerAbstract + * @covers phpOMS\Application\StatusAbstract + * @group framework + */ + public function testReInit() : void + { + Directory::delete(__DIR__ . '/Apps/Testapp'); + + $this->appManager->install(__DIR__ . '/Testapp', __DIR__ . '/Apps/Testapp'); + + $this->appManager->reInit(__DIR__ . '/Apps/Testapp'); + self::assertEquals($r1 = include __DIR__ . '/Testapp/Admin/Install/Application/Routes.php', $r2 = include __DIR__ . '/Apps/Testapp/Routes.php'); + self::assertEquals($h1 = include __DIR__ . '/Testapp/Admin/Install/Application/Hooks.php', $h2 = include __DIR__ . '/Apps/Testapp/Hooks.php'); + + Directory::delete(__DIR__ . '/Apps/Testapp'); } /** @@ -105,12 +128,40 @@ class ApplicationManagerTest extends \PHPUnit\Framework\TestCase self::assertFalse($this->appManager->install(__DIR__, __DIR__)); } + /** + * @covers phpOMS\Application\ApplicationManager + * @group framework + */ + public function testMissingInstallerPath() : void + { + self::assertFalse($this->appManager->install(__DIR__ . '/MissingInstaller', __DIR__ . '/Apps/MissingInstaller')); + } + /** * @covers phpOMS\Application\ApplicationManager * @group framework */ public function testMissingApplicationInfoFile() : void { - self::assertFalse($this->appManager->install(__DIR__, __DIR__ . '/newapp', __DIR__ . '/Apps/newapp')); + self::assertFalse($this->appManager->install(__DIR__ . '/MissingInfo', __DIR__ . '/Apps/MissingInfo')); + } + + /** + * @covers phpOMS\Application\ApplicationManager + * @group framework + */ + public function testInvalidSourceUninstallPath() : void + { + self::assertFalse($this->appManager->uninstall(__DIR__ . '/invalid', __DIR__)); + self::assertFalse($this->appManager->uninstall(__DIR__, __DIR__)); + } + + /** + * @covers phpOMS\Application\ApplicationManager + * @group framework + */ + public function testMissingUninstallerPath() : void + { + self::assertFalse($this->appManager->uninstall(__DIR__ . '/Apps/MissingInstaller')); } } diff --git a/tests/Application/InstallerAbstractTest.php b/tests/Application/InstallerAbstractTest.php new file mode 100644 index 000000000..8818440ca --- /dev/null +++ b/tests/Application/InstallerAbstractTest.php @@ -0,0 +1,49 @@ +installer = new class() extends InstallerAbstract { + public const PATH = __DIR__ . '/Invalid'; + }; + } + + /** + * @covers phpOMS\Application\InstallerAbstract + * @group framework + */ + public function testInvalidTheme() : void + { + $this->installer::installTheme(__DIR__, 'Invalid'); + self::assertFalse(\is_dir(__DIR__ . '/css')); + } +} diff --git a/tests/Application/MissingInfo/Admin/Installer.php b/tests/Application/MissingInfo/Admin/Installer.php new file mode 100644 index 000000000..a81436628 --- /dev/null +++ b/tests/Application/MissingInfo/Admin/Installer.php @@ -0,0 +1 @@ +status = new class() extends StatusAbstract { + public const PATH = __DIR__ . '/Invalid'; + }; + } + + /** + * @covers phpOMS\Application\StatusAbstract + * @group framework + */ + public function testInvalidAppPathActivation() : void + { + $this->status::activateRoutes(); + $this->status::activateHooks(); + + self::assertFalse(\is_file(__DIR__ . '/Routes.php')); + } +} diff --git a/tests/Application/Testapp/Admin/Installer.php b/tests/Application/Testapp/Admin/Installer.php index 1787e50aa..aff5dec23 100644 --- a/tests/Application/Testapp/Admin/Installer.php +++ b/tests/Application/Testapp/Admin/Installer.php @@ -4,7 +4,7 @@ * * PHP Version 8.0 * - * @package Web\Api\Admin + * @package phpOMS\tests\Application\Apps\{APPNAME}\Admin * @copyright Dennis Eichhorn * @license OMS License 1.0 * @version 1.0.0 @@ -19,7 +19,7 @@ use phpOMS\Application\InstallerAbstract; /** * Installer class. * - * @package Web\Api\Admin + * @package phpOMS\tests\Application\Apps\{APPNAME}\Admin * @license OMS License 1.0 * @link https://orange-management.org * @since 1.0.0 diff --git a/tests/Application/Testapp/Admin/Status.php b/tests/Application/Testapp/Admin/Status.php index 98fc44e05..9ee98dc7f 100644 --- a/tests/Application/Testapp/Admin/Status.php +++ b/tests/Application/Testapp/Admin/Status.php @@ -4,7 +4,7 @@ * * PHP Version 8.0 * - * @package Web\Api\Admin + * @package phpOMS\tests\Application\Apps\{APPNAME}\Admin * @copyright Dennis Eichhorn * @license OMS License 1.0 * @version 1.0.0 @@ -19,7 +19,7 @@ use phpOMS\Application\StatusAbstract; /** * Status class. * - * @package Web\Api\Admin + * @package phpOMS\tests\Application\Apps\{APPNAME}\Admin * @license OMS License 1.0 * @link https://orange-management.org * @since 1.0.0 diff --git a/tests/Application/Testapp/Admin/Uninstaller.php b/tests/Application/Testapp/Admin/Uninstaller.php new file mode 100644 index 000000000..a6c987390 --- /dev/null +++ b/tests/Application/Testapp/Admin/Uninstaller.php @@ -0,0 +1,30 @@ +cache->get('addKey')); } + public function testExists() : void + { + self::assertTrue($this->cache->add('addKey', 'testValAdd')); + self::assertTrue($this->cache->exists('addKey')); + self::assertFalse($this->cache->exists('invalid')); + } + + public function testExpiredExists() : void + { + $this->cache->set('key2', 'testVal2', 2); + \sleep(1); + self::assertTrue($this->cache->exists('key2')); + self::assertFalse($this->cache->exists('key2', 0)); + \sleep(3); + self::assertFalse($this->cache->exists('key2')); + } + + public function testExistsInvalidStatus() : void + { + TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE); + self::assertFalse($this->cache->exists('invalid')); + } + public function testGetLike() : void { $this->cache->set('key1', 'testVal1'); @@ -134,20 +157,47 @@ class FileCacheTest extends \PHPUnit\Framework\TestCase self::assertEquals([], \array_diff(['testVal1', 'testVal2'], $this->cache->getLike('key\d'))); } + public function testExpiredGetLike() : void + { + $this->cache->set('key1', 'testVal1', 2); + $this->cache->set('key2', 'testVal2', 2); + \sleep(1); + self::assertEquals([], \array_diff(['testVal1', 'testVal2'], $this->cache->getLike('key\d'))); + self::assertEquals([], $this->cache->getLike('key\d', 0)); + \sleep(3); + self::assertEquals([], $this->cache->getLike('key\d')); + } + + public function testGetLikeInvalidStatus() : void + { + TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE); + self::assertEquals([], $this->cache->getLike('key\d')); + } + public function testIncrement() : void { $this->cache->set(1, 1); - $this->cache->increment(1, 2); + self::assertTrue($this->cache->increment(1, 2)); self::assertEquals(3, $this->cache->get(1)); } + public function testInvalidKeyIncrement() : void + { + self::assertFalse($this->cache->increment('invalid', 2)); + } + public function testDecrement() : void { $this->cache->set(1, 3); - $this->cache->decrement(1, 2); + self::assertTrue($this->cache->decrement(1, 2)); self::assertEquals(1, $this->cache->get(1)); } + public function testInvalidKeyDecrement() : void + { + self::assertFalse($this->cache->decrement('invalid', 2)); + } + public function testRename() : void { $this->cache->set('a', 'testVal1'); @@ -163,6 +213,23 @@ class FileCacheTest extends \PHPUnit\Framework\TestCase self::assertEquals([], $this->cache->getLike('key\d')); } + public function testExpiredDelteLike() : void + { + $this->cache->set('key1', 'testVal1', 2); + $this->cache->set('key2', 'testVal2', 2); + + \sleep(1); + + self::assertTrue($this->cache->deleteLike('key\d', 0)); + self::assertEquals([], $this->cache->getLike('key\d')); + } + + public function testDeleteLikeInvalidStatus() : void + { + TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE); + self::assertFalse($this->cache->deleteLike('key\d')); + } + public function testUpdateExpire() : void { $this->cache->set('key2', 'testVal2', 1); @@ -222,6 +289,11 @@ class FileCacheTest extends \PHPUnit\Framework\TestCase self::assertNull($this->cache->get('key4')); } + public function testInvalidKeyDelete() : void + { + self::assertTrue($this->cache->delete('invalid')); + } + /** * @testdox The cache correctly handles general cache information * @covers phpOMS\DataStorage\Cache\Connection\FileCache diff --git a/tests/DataStorage/Cache/Connection/MemCachedTest.php b/tests/DataStorage/Cache/Connection/MemCachedTest.php index b9a9e81d9..86ec16f3c 100644 --- a/tests/DataStorage/Cache/Connection/MemCachedTest.php +++ b/tests/DataStorage/Cache/Connection/MemCachedTest.php @@ -128,6 +128,28 @@ class MemCachedTest extends \PHPUnit\Framework\TestCase self::assertEquals('testValAdd', $this->cache->get('addKey')); } + public function testExists() : void + { + self::assertTrue($this->cache->add('addKey', 'testValAdd')); + self::assertTrue($this->cache->exists('addKey')); + } + + public function testExpiredExists() : void + { + $this->cache->set('key2', 'testVal2', 2); + \sleep(1); + self::assertTrue($this->cache->exists('key2')); + self::assertFalse($this->cache->exists('key2', 0)); + \sleep(3); + self::assertFalse($this->cache->exists('key2')); + } + + public function testExistsInvalidStatus() : void + { + TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE); + self::assertFalse($this->cache->exists('invalid')); + } + public function testGetLike() : void { $this->cache->set('key1', 'testVal1'); @@ -135,20 +157,47 @@ class MemCachedTest extends \PHPUnit\Framework\TestCase self::assertEquals(['testVal1', 'testVal2'], $this->cache->getLike('key\d')); } + public function testExpiredGetLike() : void + { + $this->cache->set('key1', 'testVal1', 2); + $this->cache->set('key2', 'testVal2', 2); + \sleep(1); + self::assertEquals([], \array_diff(['testVal1', 'testVal2'], $this->cache->getLike('key\d'))); + self::assertEquals([], $this->cache->getLike('key\d', 0)); + \sleep(3); + self::assertEquals([], $this->cache->getLike('key\d')); + } + + public function testGetLikeInvalidStatus() : void + { + TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE); + self::assertEquals([], $this->cache->getLike('key\d')); + } + public function testIncrement() : void { $this->cache->set(1, 1); - $this->cache->increment(1, 2); + self::assertTrue($this->cache->increment(1, 2)); self::assertEquals(3, $this->cache->get(1)); } + public function testInvalidKeyIncrement() : void + { + self::assertFalse($this->cache->increment('invalid', 2)); + } + public function testDecrement() : void { $this->cache->set(1, 3); - $this->cache->decrement(1, 2); + self::assertTrue($this->cache->decrement(1, 2)); self::assertEquals(1, $this->cache->get(1)); } + public function testInvalidKeyDecrement() : void + { + self::assertFalse($this->cache->decrement('invalid', 2)); + } + public function testRename() : void { $this->cache->set('a', 'testVal1'); @@ -164,6 +213,12 @@ class MemCachedTest extends \PHPUnit\Framework\TestCase self::assertEquals([], $this->cache->getLike('key\d')); } + public function testDeleteLikeInvalidStatus() : void + { + TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE); + self::assertFalse($this->cache->deleteLike('key\d')); + } + public function testUpdateExpire() : void { $this->cache->set('key2', 'testVal2', 1); @@ -223,6 +278,11 @@ class MemCachedTest extends \PHPUnit\Framework\TestCase self::assertNull($this->cache->get('key4')); } + public function testInvalidKeyDelete() : void + { + self::assertTrue($this->cache->delete('invalid')); + } + /** * @testdox The cache correctly handles general cache information * @covers phpOMS\DataStorage\Cache\Connection\MemCached diff --git a/tests/DataStorage/Cache/Connection/NullCacheTest.php b/tests/DataStorage/Cache/Connection/NullCacheTest.php index 4684f4dd4..e50b4c46d 100644 --- a/tests/DataStorage/Cache/Connection/NullCacheTest.php +++ b/tests/DataStorage/Cache/Connection/NullCacheTest.php @@ -78,7 +78,7 @@ final class NullCacheTest extends \PHPUnit\Framework\TestCase public function testDecrement() : void { - $this->cache->increment(1, 1); + $this->cache->decrement(1, 1); self::assertNull($this->cache->get(1)); } diff --git a/tests/DataStorage/Cache/Connection/RedisCacheTest.php b/tests/DataStorage/Cache/Connection/RedisCacheTest.php index 32a5e4479..7d8e2b416 100644 --- a/tests/DataStorage/Cache/Connection/RedisCacheTest.php +++ b/tests/DataStorage/Cache/Connection/RedisCacheTest.php @@ -124,6 +124,28 @@ class RedisCacheTest extends \PHPUnit\Framework\TestCase self::assertEquals('testValAdd', $this->cache->get('addKey')); } + public function testExists() : void + { + self::assertTrue($this->cache->add('addKey', 'testValAdd')); + self::assertTrue($this->cache->exists('addKey')); + } + + public function testExpiredExists() : void + { + $this->cache->set('key2', 'testVal2', 2); + \sleep(1); + self::assertTrue($this->cache->exists('key2')); + self::assertFalse($this->cache->exists('key2', 0)); + \sleep(3); + self::assertFalse($this->cache->exists('key2')); + } + + public function testExistsInvalidStatus() : void + { + TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE); + self::assertFalse($this->cache->exists('invalid')); + } + public function testGetLike() : void { $this->cache->set('key1', 'testVal1'); @@ -131,20 +153,47 @@ class RedisCacheTest extends \PHPUnit\Framework\TestCase self::assertEquals(['testVal1', 'testVal2'], $this->cache->getLike('key\d')); } + public function testExpiredGetLike() : void + { + $this->cache->set('key1', 'testVal1', 2); + $this->cache->set('key2', 'testVal2', 2); + \sleep(1); + self::assertEquals([], \array_diff(['testVal1', 'testVal2'], $this->cache->getLike('key\d'))); + self::assertEquals([], $this->cache->getLike('key\d', 0)); + \sleep(3); + self::assertEquals([], $this->cache->getLike('key\d')); + } + + public function testGetLikeInvalidStatus() : void + { + TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE); + self::assertEquals([], $this->cache->getLike('key\d')); + } + public function testIncrement() : void { $this->cache->set(1, 1); - $this->cache->increment(1, 2); + self::assertTrue($this->cache->increment(1, 2)); self::assertEquals(3, $this->cache->get(1)); } + public function testInvalidKeyIncrement() : void + { + self::assertFalse($this->cache->increment('invalid', 2)); + } + public function testDecrement() : void { $this->cache->set(1, 3); - $this->cache->decrement(1, 2); + self::assertTrue($this->cache->decrement(1, 2)); self::assertEquals(1, $this->cache->get(1)); } + public function testInvalidKeyDecrement() : void + { + self::assertFalse($this->cache->decrement('invalid', 2)); + } + public function testRename() : void { $this->cache->set('a', 'testVal1'); @@ -160,6 +209,12 @@ class RedisCacheTest extends \PHPUnit\Framework\TestCase self::assertEquals([], $this->cache->getLike('key\d')); } + public function testDeleteLikeInvalidStatus() : void + { + TestUtils::setMember($this->cache, 'status', CacheStatus::FAILURE); + self::assertFalse($this->cache->deleteLike('key\d')); + } + public function testUpdateExpire() : void { $this->cache->set('key2', 'testVal2', 1); @@ -219,6 +274,11 @@ class RedisCacheTest extends \PHPUnit\Framework\TestCase self::assertNull($this->cache->get('key4')); } + public function testInvalidKeyDelete() : void + { + self::assertTrue($this->cache->delete('invalid')); + } + /** * @testdox The cache correctly handles general cache information * @covers phpOMS\DataStorage\Cache\Connection\RedisCache diff --git a/tests/DataStorage/Database/Schema/BuilderTest.php b/tests/DataStorage/Database/Schema/BuilderTest.php index b011ceba4..003ab3184 100644 --- a/tests/DataStorage/Database/Schema/BuilderTest.php +++ b/tests/DataStorage/Database/Schema/BuilderTest.php @@ -108,29 +108,19 @@ class BuilderTest extends \PHPUnit\Framework\TestCase );*/ } + /** + * @testdox The grammar correctly deletes a table + * @covers phpOMS\DataStorage\Database\Schema\Grammar\MysqlGrammar + * @group framework + */ public function testMysqlCreateFromSchema() : void { - Builder::createFromSchema( - \json_decode( - \file_get_contents(__DIR__ . '/Grammar/testSchema.json'), true - )['test_foreign'], - $this->con - )->execute(); + $query = new Builder($this->con); + $sql = 'DROP TABLE `test`, `test_foreign`;'; - Builder::createFromSchema( - \json_decode( - \file_get_contents(__DIR__ . '/Grammar/testSchema.json'), true - )['test'], - $this->con - )->execute(); - - $table = new Builder($this->con); - $tables = $table->selectTables()->execute()->fetchAll(\PDO::FETCH_COLUMN); - self::assertContains('test', $tables); - self::assertContains('test_foreign', $tables); - - $delete = new Builder($this->con); - $delete->dropTable('test')->execute(); - $delete->dropTable('test_foreign')->execute(); + self::assertEquals( + $sql, + $query->dropTable('test')->dropTable('test_foreign')->toSql() + ); } } diff --git a/tests/DataStorage/Database/Schema/Grammar/MysqlGrammarTest.php b/tests/DataStorage/Database/Schema/Grammar/MysqlGrammarTest.php index 359088619..30adfba1f 100644 --- a/tests/DataStorage/Database/Schema/Grammar/MysqlGrammarTest.php +++ b/tests/DataStorage/Database/Schema/Grammar/MysqlGrammarTest.php @@ -15,7 +15,7 @@ declare(strict_types=1); namespace phpOMS\tests\DataStorage\Database\Schema\Grammar; use phpOMS\DataStorage\Database\Connection\MysqlConnection; -use phpOMS\DataStorage\Database\Schema\Builder as SchemaBuilder; +use phpOMS\DataStorage\Database\Schema\Builder; use phpOMS\DataStorage\Database\Schema\Grammar\MysqlGrammar; use phpOMS\Utils\ArrayUtils; use phpOMS\Utils\TestUtils; @@ -58,15 +58,15 @@ class MysqlGrammarTest extends \PHPUnit\Framework\TestCase { $definitions = \json_decode(\file_get_contents(__DIR__ . '/testSchema.json'), true); foreach ($definitions as $definition) { - SchemaBuilder::createFromSchema($definition, $this->con)->execute(); + Builder::createFromSchema($definition, $this->con)->execute(); } - $table = new SchemaBuilder($this->con); + $table = new Builder($this->con); $tables = $table->selectTables()->execute()->fetchAll(\PDO::FETCH_COLUMN); self::assertContains('test', $tables); self::assertContains('test_foreign', $tables); - $field = new SchemaBuilder($this->con); + $field = new Builder($this->con); $fields = $field->selectFields('test')->execute()->fetchAll(); foreach ($definitions['test']['fields'] as $key => $field) { @@ -75,6 +75,11 @@ class MysqlGrammarTest extends \PHPUnit\Framework\TestCase 'Couldn\'t find "' . $key . '" in array' ); } + + $delete = new Builder($this->con); + $delete->dropTable('test') + ->dropTable('test_foreign') + ->execute(); } /** @@ -84,13 +89,22 @@ class MysqlGrammarTest extends \PHPUnit\Framework\TestCase */ public function testDelete() : void { - $table = new SchemaBuilder($this->con); + $definitions = \json_decode(\file_get_contents(__DIR__ . '/testSchema.json'), true); + foreach ($definitions as $definition) { + Builder::createFromSchema($definition, $this->con)->execute(); + } + + $table = new Builder($this->con); $tables = $table->selectTables()->execute()->fetchAll(\PDO::FETCH_COLUMN); + self::assertContains('test', $tables); + self::assertContains('test_foreign', $tables); - $delete = new SchemaBuilder($this->con); - $delete->dropTable('test')->execute(); - $delete->dropTable('test_foreign')->execute(); + $delete = new Builder($this->con); + $delete->dropTable('test') + ->dropTable('test_foreign') + ->execute(); + $table = new Builder($this->con); $tables = $table->selectTables()->execute()->fetchAll(); self::assertNotContains('test', $tables); self::assertNotContains('test_foreign', $tables); diff --git a/tests/Localization/L11nManagerTest.php b/tests/Localization/L11nManagerTest.php index f16cc5663..799340294 100644 --- a/tests/Localization/L11nManagerTest.php +++ b/tests/Localization/L11nManagerTest.php @@ -91,6 +91,16 @@ class L11nManagerTest extends \PHPUnit\Framework\TestCase self::assertEquals('Test strin&g2', $this->l11nManager->getHtml('en', 'Admin', 'RandomThemeDoesNotMatterAlreadyLoaded', 'Test2')); } + /** + * @testdox An invalid localization source returns an error string + * @covers phpOMS\Localization\L11nManager + * @group framework + */ + public function testInvalidControllerSource() : void + { + self::assertEquals('ERROR-Key', $this->l11nManager->getText('en', 'InvalidSource', 'RandomThemeDoesNotMatterAlreadyLoaded', 'Key')); + } + /** * @testdox Language data can be loaded from a file * @covers phpOMS\Localization\L11nManager diff --git a/tests/Math/Statistic/Forecast/ForecastsTest.php b/tests/Math/Statistic/Forecast/ForecastsTest.php new file mode 100644 index 000000000..d4b48dfac --- /dev/null +++ b/tests/Math/Statistic/Forecast/ForecastsTest.php @@ -0,0 +1,32 @@ +module = new class() extends ModuleAbstract { - public const PATH = __DIR__ . '/'; + public const PATH = __DIR__ . '/Test'; const VERSION = '1.2.3'; diff --git a/tests/Module/ModuleManagerTest.php b/tests/Module/ModuleManagerTest.php index 2e350fe40..b7e798279 100644 --- a/tests/Module/ModuleManagerTest.php +++ b/tests/Module/ModuleManagerTest.php @@ -211,6 +211,18 @@ class ModuleManagerTest extends \PHPUnit\Framework\TestCase /** * @testdox A module can be re-initialized * @covers phpOMS\Module\ModuleManager + * @group framework + */ + public function testInvalidModuleReInit() : void + { + $this->moduleManager->reInit('Invalid'); + self::assertFalse($this->moduleManager->isActive('Invalid')); + } + + /** + * @testdox A module can be re-initialized + * @covers phpOMS\Module\ModuleManager + * @covers phpOMS\Module\InstallerAbstract * @covers phpOMS\Module\StatusAbstract * @group framework */