addPsr4('Akeeba\\MiniTest\\', __DIR__); function getAllTestClasses(): array { static $testClasses = []; if (!empty($testClasses)) { return $testClasses; } $folder = __DIR__ . '/Test'; $di = new DirectoryIterator($folder); foreach ($di as $entry) { if ($entry->isDot() || !$entry->isFile()) { continue; } $baseName = $entry->getBasename('.php'); $className = '\\Akeeba\\MiniTest\\Test\\' . $baseName; if (!class_exists($className)) { continue; } $reflectedClass = new ReflectionClass($className); if ($reflectedClass->isAbstract()) { continue; } $testClasses[] = $className; } return $testClasses; } function getTestMethods(string $className): array { static $classMethodMap = []; if (isset($classMethodMap[$className])) { return $classMethodMap[$className]; } $classMethodMap[$className] = []; if (!class_exists($className)) { return $classMethodMap[$className]; } $reflectedClass = new ReflectionClass($className); $methods = $reflectedClass->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_STATIC); $classMethodMap[$className] = array_map(function (ReflectionMethod $refMethod) { if ($refMethod->isPrivate() || $refMethod->isProtected() || $refMethod->isAbstract()) { return null; } if (!$refMethod->isStatic()) { return null; } return $refMethod->getName(); }, $methods); $classMethodMap[$className] = array_filter($classMethodMap[$className], function ($method) { if (is_null($method)) { return false; } if (in_array($method, ['setup', 'teardown'])) { return false; } return true; }); return $classMethodMap[$className]; } function simplifyClassName(?string $className): string { if (empty($className)) { return ''; } $namespace = __NAMESPACE__ . '\\Test\\'; if (strpos($className, $namespace) === 0) { return substr($className, strlen($namespace)); } return $className; } if (!file_exists(__DIR__ . '/config.php')) { die ('Please rename config.dist.php to config.php and customise it before running the mini test suite.'); } require __DIR__ . '/config.php'; global $testConfigurations; $total = 0; $broken = 0; $failed = 0; $successful = 0; foreach ($testConfigurations as $description => $setup) { echo "▶ " . $description . PHP_EOL; echo str_repeat('〰', 80) . PHP_EOL . PHP_EOL; // Extract the configuration options if (!isset($setup['configuration'])) { $setup['configuration'] = []; } $configOptions = array_merge([ 'access' => DEFAULT_ACCESS_KEY, 'secret' => DEFAULT_SECRET_KEY, 'region' => DEFAULT_REGION, 'bucket' => DEFAULT_BUCKET, 'signature' => DEFAULT_SIGNATURE, 'dualstack' => DEFAULT_DUALSTACK, 'path_access' => DEFAULT_PATH_ACCESS, 'ssl' => DEFAULT_SSL, 'endpoint' => null, ], $setup['configuration']); // Extract the test classes/methods to run if (!isset($setup['tests'])) { $setup['tests'] = getAllTestClasses(); } $tests = $setup['tests']; if (!is_array($tests) || (is_array($tests) && in_array('*', $tests))) { $tests = getAllTestClasses(); } // Create the S3 configuration object $s3Configuration = new Configuration($configOptions['access'], $configOptions['secret'], $configOptions['signature'], $configOptions['region']); $s3Configuration->setUseDualstackUrl($configOptions['dualstack']); $s3Configuration->setUseLegacyPathStyle($configOptions['path_access']); $s3Configuration->setSSL($configOptions['ssl']); if (!is_null($configOptions['endpoint'])) { $s3Configuration->setEndpoint($configOptions['endpoint']); } // Create the connector object $s3Connector = new Connector($s3Configuration); // Run the tests foreach ($tests as $testInfo) { if (!is_array($testInfo)) { $className = $testInfo; if (!class_exists($className)) { $className = '\\Akeeba\\MiniTest\\Test\\' . $className; } if (!class_exists($className)) { $total++; $broken++; echo " ⁉️ Test class {$className} not found." . PHP_EOL; continue; } $testInfo = array_map(function ($method) use ($className) { return [$className, $method]; }, getTestMethods($className)); } else { [$className, $method] = $testInfo; if (!class_exists($className)) { $className = '\\Akeeba\\MiniTest\\Test\\' . $className; } if (!class_exists($className)) { $total++; $broken++; echo " ⁉️ Test class {$className} not found." . PHP_EOL; continue; } $testInfo = [ [$className, $method], ]; } $firstOne = false; $className = null; $callableSetup = null; $callableTeardown = null; $simplifiedClassname = simplifyClassName($className); if (!empty($testInfo)) { $firstOne = array_shift($testInfo); array_unshift($testInfo, $firstOne); } if ($firstOne) { [$className,] = $firstOne; if ($className) { $callableSetup = [$className, 'setup']; $callableTeardown = [$className, 'teardown']; } } if (is_callable($callableSetup)) { [$classNameSetup, $method] = $callableSetup; $simplifiedClassname = simplifyClassName($classNameSetup); echo " ⏱ Preparing {$simplifiedClassname}:{$method}…"; call_user_func($callableSetup, $s3Connector, $configOptions); echo "\r Prepared {$simplifiedClassname} " . PHP_EOL; } foreach ($testInfo as $callable) { $total++; [$className, $method] = $callable; if (!class_exists($className)) { $broken++; echo " ⁉️ Test class {$className} not found." . PHP_EOL; continue; } if (!method_exists($className, $method)) { $broken++; echo " ⁉️ Method {$method} not found in test class {$className}." . PHP_EOL; continue; } echo " ⏱ {$simplifiedClassname}:{$method}…"; $errorException = null; try { $result = call_user_func([$className, $method], $s3Connector, $configOptions); } catch (Exception $e) { $result = false; $errorException = $e; } echo "\r " . ($result ? '✔' : '🚨') . " {$simplifiedClassname}:{$method} " . PHP_EOL; if ($result) { $successful++; continue; } $failed++; if (is_null($errorException)) { echo " Returned false" . PHP_EOL; continue; } echo " {$errorException->getCode()} – {$errorException->getMessage()}" . PHP_EOL; echo " {$errorException->getFile()}({$errorException->getLine()})" . PHP_EOL . PHP_EOL; $errorLines = explode("\n", $errorException->getTraceAsString()); foreach ($errorLines as $line) { echo " $line" . PHP_EOL; } } if (is_callable($callableTeardown)) { [$className, $method] = $callableSetup; echo " ⏱ Tearing down {$className}:{$method}…"; call_user_func($callableTeardown, $s3Connector, $configOptions); echo "\r Teared down {$className} " . PHP_EOL; } } echo PHP_EOL; } echo PHP_EOL; echo str_repeat('⎺', 80) . PHP_EOL; echo PHP_EOL; echo "Summary:" . PHP_EOL; if ($broken) { echo " Broken : $broken" . PHP_EOL; } if ($failed) { echo " Failed : $failed" . PHP_EOL; } if ($successful) { echo " Successful : $successful" . PHP_EOL; } echo " Total : $total" . PHP_EOL . PHP_EOL; echo "Conclusion: " . PHP_EOL . " "; if ($failed > 0) { echo "❌ FAILED 😭😭😭" . PHP_EOL; exit(1); } if ($successful === 0) { echo "🔥 No tests executed! 🤪" . PHP_EOL; exit (3); } if ($broken > 0) { echo "⁉️ SUCCESS but some tests are broken 🤦" . PHP_EOL; exit (2); } echo "✅ PASSED" . PHP_EOL;