public static function runDirectory(
            string $path, 
            string $testSuffix = '/Test', 
            ?string $filter = null,
            ?string $exclude = '/vendor/'): bool
    {
        foreach (new \RecursiveIteratorIterator(
        new \RecursiveDirectoryIterator($path,
        \RecursiveDirectoryIterator::KEY_AS_PATHNAME),
        \RecursiveIteratorIterator::CHILD_FIRST) as $file => $info) {
            if (!preg_match('#^' . $path . '.*' . $testSuffix . '\.php$#', $file) ||
                ($exclude !== null && strpos($file, $exclude) !== false)) {
                continue;
            }
            $matches = [];
            if (!preg_match('/^.*\nnamespace ([a-zA-Z\\\\]+); *\n.*$/m', file_get_contents($file), $matches)) {
                trigger_error($file . ' namespace not found');
            }
            $className = '\\' . strtr($matches[1] . '/' . basename($file, '.php'), '/', '\\');
            self::runClass($className, $filter);
        }
        echo "- " . self::$totalCountFiles . " test file(s), ";
        echo self::$totalCount . ' tests passed, ';
        if (self::$totalCountErrors > 0) {
            echo chr(033) . '[1;31m' . self::$totalCountErrors . ' failed' . chr(033) . '[0;0m';
        } else {
            echo chr(033) . '[1;32msuccess ^^' . chr(033) . '[0;0m';
        }
        echo "\n";
        
        return !self::$totalCountErrors;
    }