diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3710514..dfe2c6a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,34 +2,41 @@ name: build on: push: branches: [master] + pull_request: + branches: [master] jobs: - run: - runs-on: ${{ matrix.operating-system }} + test: + runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - operating-system: [ubuntu-latest] - php-versions: - ["7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3", "8.4"] - name: PHP ${{ matrix.php-versions }} + php-version: ["7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3", "8.4"] + name: PHP ${{ matrix.php-version }} steps: - - uses: actions/checkout@v4 - - name: Update advanced packaging tools - run: sudo apt update + - uses: actions/checkout@v5.0.1 + - name: Install Ghostscript - run: sudo apt install ghostscript - - name: Install PHP + run: sudo apt-get update -qq && sudo apt-get install -y ghostscript + + - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: ${{ matrix.php-versions }} + php-version: ${{ matrix.php-version }} coverage: xdebug + tools: composer:v2 + - name: Install dependencies - run: composer self-update && composer install && composer dump-autoload - - name: Copy .env.example to .env + run: composer install --no-interaction --prefer-dist + + - name: Copy .env run: cp .env.example .env + - name: Run tests run: composer test + - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + if: matrix.php-version == '8.4' + uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.xml diff --git a/.gitignore b/.gitignore index 7cb9e80..b1bda8c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,16 @@ vendor/ report/ files/split/parts/ files/merge/res.pdf +files/merge/single.pdf files/to-image/images/ composer.lock coverage.xml phpunit.xml .phpunit.result.cache .DS_Store -.env \ No newline at end of file +.env +.repomix/ +repomix-output.txt +.claude/ +Dockerfile +docker-compose.yml \ No newline at end of file diff --git a/src/Configs/Config.php b/src/Configs/Config.php index 6f468b2..08c540d 100644 --- a/src/Configs/Config.php +++ b/src/Configs/Config.php @@ -1,5 +1,7 @@ BaseHandler::class, + 'convert' => ConvertHandler::class, + 'guess' => GuessHandler::class, + 'getTotalPages' => GetTotalPagesHandler::class, + 'merge' => MergeHandler::class, + 'split' => SplitHandler::class, + 'toImage' => ToImageHandler::class, + ]; + /** * @param string $type - * + * * @return HandlerInterface - * + * * @throws NotFoundException */ public function create(string $type): HandlerInterface { - $class = 'Ordinary9843\\Handlers\\' . ucfirst($type) . 'Handler'; - if (!class_exists($class)) { - throw new NotFoundException('Class "' . $class . '" does not exist.', NotFoundException::CODE_CLASS); + if (!isset(self::HANDLER_MAP[$type])) { + throw new NotFoundException('Handler "' . $type . '" does not exist.', NotFoundException::CODE_CLASS); } + $class = self::HANDLER_MAP[$type]; + return new $class(); } } diff --git a/src/Ghostscript.php b/src/Ghostscript.php index 4d44e95..01f1175 100644 --- a/src/Ghostscript.php +++ b/src/Ghostscript.php @@ -1,27 +1,15 @@ arguments); } + public function convert(string $file, float $version): string + { + return $this->createHandler('convert')->execute($file, $version); + } + + public function guess(string $file): float + { + return $this->createHandler('guess')->execute($file); + } + + public function merge(string $path, string $filename, array $files, bool $isAutoConvert = true): string + { + return $this->createHandler('merge')->execute($path, $filename, $files, $isAutoConvert); + } + + public function split(string $file, string $path): array + { + return $this->createHandler('split')->execute($file, $path); + } + + public function toImage(string $file, string $path, string $type = ImageTypeConstant::JPEG): array + { + return $this->createHandler('toImage')->execute($file, $path, $type); + } + + public function getTotalPages(string $file): int + { + return $this->createHandler('getTotalPages')->execute($file); + } + + public function clearTmpFiles(bool $isForceClear = false, int $days = 7): void + { + $this->createBaseHandler()->clearTmpFiles($isForceClear, $days); + } + + public function setBinPath(string $binPath): void + { + $this->createBaseHandler()->setBinPath($binPath); + } + + public function getBinPath(): string + { + return $this->createBaseHandler()->getBinPath(); + } + + public function setTmpPath(string $tmpPath): void + { + $this->createBaseHandler()->setTmpPath($tmpPath); + } + + public function getTmpPath(): string + { + return $this->createBaseHandler()->getTmpPath(); + } + + public function setOptions(array $options): void + { + $this->createBaseHandler()->setOptions($options); + } + + public function getOptions(): array + { + return $this->createBaseHandler()->getOptions(); + } + /** * @param string $name * @param array $arguments - * + * * @return mixed - * + * * @throws InvalidException */ public function __call(string $name, array $arguments) { - switch ($name) { - case 'convert': - case 'guess': - case 'merge': - case 'split': - case 'toImage': - case 'getTotalPages': - $handler = $this->createHandler($name); - - return $handler->execute(...$arguments); - case 'getBinPath': - case 'getTmpPath': - case 'getOptions': - case 'clearTmpFiles': - $handler = $this->createBaseHandler(); - - return $handler->{$name}(); - case 'setBinPath': - case 'setTmpPath': - $handler = $this->createBaseHandler(); - - return $handler->{$name}(current($arguments)); - case 'setOptions': - $handler = $this->createBaseHandler(); - - return $handler->{$name}(...$arguments); - default: - throw new InvalidException('Invalid method: "' . $name . '".', InvalidException::CODE_METHOD, [ - 'name' => $name, - 'arguments' => $arguments - ]); - } + throw new InvalidException('Invalid method: "' . $name . '".', InvalidException::CODE_METHOD, [ + 'name' => $name, + 'arguments' => $arguments + ]); } /** * @param string $name - * + * * @return HandlerInterface */ private function createHandler(string $name): HandlerInterface diff --git a/src/Handlers/BaseHandler.php b/src/Handlers/BaseHandler.php index 8276680..68ee6c0 100644 --- a/src/Handlers/BaseHandler.php +++ b/src/Handlers/BaseHandler.php @@ -1,5 +1,7 @@ isFile($file)) { + return false; + } + if (strcasecmp(pathinfo($file, PATHINFO_EXTENSION), 'pdf') !== 0) { return false; } diff --git a/src/Handlers/ConvertHandler.php b/src/Handlers/ConvertHandler.php index 14f91da..57b60d5 100644 --- a/src/Handlers/ConvertHandler.php +++ b/src/Handlers/ConvertHandler.php @@ -1,5 +1,7 @@ validateBinPath(); $this->mapArguments($arguments); + $file = ''; + try { $file = PathHelper::convertPathSeparator($arguments['file']); if (!$this->isFile($file)) { diff --git a/src/Handlers/GuessHandler.php b/src/Handlers/GuessHandler.php index 418cdda..7806bc4 100644 --- a/src/Handlers/GuessHandler.php +++ b/src/Handlers/GuessHandler.php @@ -1,5 +1,7 @@ convertHandler = (new HandlerFactory())->create('convert'); $this->guessHandler = (new HandlerFactory())->create('guess'); } @@ -42,6 +45,8 @@ public function execute(...$arguments): string $this->validateBinPath(); $this->mapArguments($arguments); + $file = ''; + try { $path = PathHelper::convertPathSeparator($arguments['path']); $filename = PathHelper::convertPathSeparator($arguments['filename']); @@ -59,6 +64,11 @@ public function execute(...$arguments): string return true; }); + + if (empty($files)) { + throw new HandlerException('No valid PDF files to merge.', HandlerException::CODE_EXECUTE); + } + $output = shell_exec( $this->optionsToCommand( sprintf( @@ -77,7 +87,9 @@ public function execute(...$arguments): string return $file; } catch (BaseException $exception) { - $this->delete($file); + if ($file !== '') { + $this->delete($file); + } throw new HandlerException($exception->getMessage(), HandlerException::CODE_EXECUTE, [ 'arguments' => $arguments diff --git a/src/Handlers/SplitHandler.php b/src/Handlers/SplitHandler.php index ad03a99..f138f3d 100644 --- a/src/Handlers/SplitHandler.php +++ b/src/Handlers/SplitHandler.php @@ -1,5 +1,7 @@ getTotalPagesHandler = (new HandlerFactory())->create('getTotalPages'); } /** * @param array ...$arguments - * + * * @return array - * + * * @throws HandlerException * @throws InvalidException */ @@ -49,7 +52,7 @@ public function execute(...$arguments): array return array_map(function ($i) use ($path, $pdfFormatPath) { return $path . sprintf($pdfFormatPath, $i); - }, range(0, $totalPages - 1)); + }, range(1, $totalPages)); } catch (BaseException $exception) { throw new HandlerException($exception->getMessage(), HandlerException::CODE_EXECUTE, [ 'arguments' => $arguments diff --git a/src/Handlers/ToImageHandler.php b/src/Handlers/ToImageHandler.php index 797a192..98959fb 100644 --- a/src/Handlers/ToImageHandler.php +++ b/src/Handlers/ToImageHandler.php @@ -1,5 +1,7 @@ getTotalPagesHandler = (new HandlerFactory())->create('getTotalPages'); } /** * @param array ...$arguments - * + * * @return array - * + * * @throws HandlerException * @throws InvalidException */ @@ -41,6 +47,14 @@ public function execute(...$arguments): array $file = PathHelper::convertPathSeparator($arguments['file']); $path = PathHelper::convertPathSeparator($arguments['path']); $type = $arguments['type'] ? $arguments['type'] : ImageTypeConstant::JPEG; + + if (!in_array($type, self::ALLOWED_TYPES, true)) { + throw new InvalidException( + 'Invalid image type "' . $type . '". Allowed: ' . implode(', ', self::ALLOWED_TYPES) . '.', + InvalidException::CODE_FILE_TYPE + ); + } + $totalPages = $this->getTotalPagesHandler->execute($file); (!$this->isDir($path)) && $this->makeDir($path); $imageFormatPath = ($totalPages > 1) ? '/image_%d.' . $type : '/' . pathinfo($file, PATHINFO_FILENAME) . '.' . $type; diff --git a/src/Helpers/PathHelper.php b/src/Helpers/PathHelper.php index fa7b4cd..3b9ecd1 100644 --- a/src/Helpers/PathHelper.php +++ b/src/Helpers/PathHelper.php @@ -1,5 +1,7 @@ expectExceptionCode(NotFoundException::CODE_CLASS); (new HandlerFactory)->create(''); } + + /** + * @return void + */ + public function testCreateGetTotalPagesHandlerShouldSucceed(): void + { + $handler = (new HandlerFactory())->create('getTotalPages'); + $this->assertInstanceOf(GetTotalPagesHandler::class, $handler); + } + + /** + * @return void + */ + public function testCreateWithUppercaseTypeShouldThrowNotFoundException(): void + { + $this->expectException(NotFoundException::class); + $this->expectExceptionCode(NotFoundException::CODE_CLASS); + (new HandlerFactory())->create('Convert'); + } + + /** + * @return void + */ + public function testCreateWithUnknownTypeShouldThrowNotFoundException(): void + { + $this->expectException(NotFoundException::class); + $this->expectExceptionCode(NotFoundException::CODE_CLASS); + (new HandlerFactory())->create('nonExistentHandler'); + } } diff --git a/tests/Handlers/BaseHandlerTest.php b/tests/Handlers/BaseHandlerTest.php index 0d48ca8..53350e6 100644 --- a/tests/Handlers/BaseHandlerTest.php +++ b/tests/Handlers/BaseHandlerTest.php @@ -200,4 +200,108 @@ public function testArgumentsMappingWhenProvidedInputs() 'arg2' => 'value2' ], $arguments); } + + /** + * @return void + */ + public function testIsPdfReturnsFalseForNonExistentFile(): void + { + $handler = new BaseHandler(); + $this->assertFalse($handler->isPdf('/nonexistent/path/file.pdf')); + } + + /** + * @return void + */ + public function testIsPdfReturnsFalseForEmptyString(): void + { + $handler = new BaseHandler(); + $this->assertFalse($handler->isPdf('')); + } + + /** + * @return void + */ + public function testIsPdfReturnsFalseForDirectoryPath(): void + { + $handler = new BaseHandler(); + $this->assertFalse($handler->isPdf(sys_get_temp_dir())); + } + + /** + * @return void + */ + public function testGetTmpFileContainsTmpPath(): void + { + $handler = new BaseHandler(); + $tmpFile = $handler->getTmpFile(); + $this->assertStringStartsWith($handler->getTmpPath(), $tmpFile); + } + + /** + * @return void + */ + public function testGetTmpFileWithCustomFilenameContainsPrefix(): void + { + $handler = new BaseHandler(); + $tmpFile = $handler->getTmpFile('custom'); + $this->assertStringContainsString('ghostscript_tmp_file_custom', $tmpFile); + $this->assertStringEndsWith('.pdf', $tmpFile); + } + + /** + * @return void + */ + public function testForceClearDeletesTmpPrefixedFiles(): void + { + $handler = new BaseHandler(); + $tmpFile = tempnam($handler->getTmpPath(), BaseHandler::TMP_FILE_PREFIX); + @rename($tmpFile, $tmpFile . '.pdf'); + $tmpFile .= '.pdf'; + $handler->clearTmpFiles(true); + $this->assertFalse(file_exists($tmpFile)); + } + + /** + * @return void + */ + public function testOptionsToCommandWithKeyValuePairs(): void + { + $handler = new BaseHandler(); + $handler->setOptions(['-dCompatibilityLevel' => '1.4']); + $result = $handler->optionsToCommand('gs'); + $this->assertEquals('gs -dCompatibilityLevel=1.4', $result); + } + + /** + * @return void + */ + public function testOptionsToCommandWithNumericKeys(): void + { + $handler = new BaseHandler(); + $handler->setOptions(['-dSAFER', '-dBATCH']); + $result = $handler->optionsToCommand('gs'); + $this->assertEquals('gs -dSAFER -dBATCH', $result); + } + + /** + * @return void + */ + public function testOptionsToCommandWithEmptyOptionsReturnsOriginal(): void + { + $handler = new BaseHandler(); + $this->assertEquals('gs -sDEVICE=pdfwrite', $handler->optionsToCommand('gs -sDEVICE=pdfwrite')); + } + + /** + * @return void + */ + public function testValidateBinPathThrowsForNonExistentPath(): void + { + $this->expectException(InvalidException::class); + $this->expectExceptionCode(InvalidException::CODE_FILEPATH); + $handler = new BaseHandler(); + $handler->setBinPath('/nonexistent/gs'); + $handler->validateBinPath(); + } } diff --git a/tests/Handlers/ConvertHandlerTest.php b/tests/Handlers/ConvertHandlerTest.php index 5b6aea7..75c622c 100644 --- a/tests/Handlers/ConvertHandlerTest.php +++ b/tests/Handlers/ConvertHandlerTest.php @@ -91,4 +91,29 @@ public function testExecuteFailedShouldThrowHandlerException(): void ]); $handler->execute($file, 1.5); } + + /** + * @return void + */ + public function testExecuteReturnsSameFilePathOnSuccess(): void + { + $file = dirname(__DIR__, 2) . '/files/convert/test.pdf'; + $handler = new ConvertHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $result = $handler->execute($file, 1.4); + $this->assertEquals($file, $result); + $this->assertFileExists($result); + } + + /** + * @return void + */ + public function testExecuteWithEmptyFilePathThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $this->expectExceptionCode(HandlerException::CODE_EXECUTE); + $handler = new ConvertHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute('', 1.4); + } } diff --git a/tests/Handlers/GetTotalPagesHandlerTest.php b/tests/Handlers/GetTotalPagesHandlerTest.php index 11186ac..4169150 100644 --- a/tests/Handlers/GetTotalPagesHandlerTest.php +++ b/tests/Handlers/GetTotalPagesHandlerTest.php @@ -74,4 +74,28 @@ public function testExecuteWhenFileTypeNotMatchShouldThrowHandlerException(): vo $handler->setBinPath($this->getEnv('GS_BIN_PATH')); $handler->execute($file); } + + /** + * @return void + */ + public function testExecuteWithEmptyFilePathThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $this->expectExceptionCode(HandlerException::CODE_EXECUTE); + $handler = new GetTotalPagesHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute(''); + } + + /** + * @return void + */ + public function testExecuteWithNonExistentFileThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $this->expectExceptionCode(HandlerException::CODE_EXECUTE); + $handler = new GetTotalPagesHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute('/nonexistent/file.pdf'); + } } diff --git a/tests/Handlers/GuessHandlerTest.php b/tests/Handlers/GuessHandlerTest.php index 71c06ad..7e970b5 100644 --- a/tests/Handlers/GuessHandlerTest.php +++ b/tests/Handlers/GuessHandlerTest.php @@ -47,4 +47,33 @@ public function testExecuteShouldThrowInvalidException(): void $handler->setBinPath($this->getEnv('GS_BIN_PATH')); $handler->execute($file); } + + /** + * @return void + */ + public function testExecuteReturnsZeroForPdfWithoutVersionHeader(): void + { + $file = tempnam(sys_get_temp_dir(), 'test'); + @rename($file, $file .= '.pdf'); + @file_put_contents($file, 'This is not a real PDF, no version header.'); + $handler = new GuessHandler(); + + try { + $version = $handler->execute($file); + $this->assertEquals(0.0, $version); + } finally { + @unlink($file); + } + } + + /** + * @return void + */ + public function testExecuteWithEmptyFilePathThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $this->expectExceptionCode(HandlerException::CODE_EXECUTE); + $handler = new GuessHandler(); + $handler->execute(''); + } } diff --git a/tests/Handlers/MergeHandlerTest.php b/tests/Handlers/MergeHandlerTest.php index 2ea7d7a..e625cdd 100644 --- a/tests/Handlers/MergeHandlerTest.php +++ b/tests/Handlers/MergeHandlerTest.php @@ -124,4 +124,46 @@ public function testExecuteFailedShouldThrowHandlerException(): void dirname(__DIR__, 2) . '/files/merge/part_3.pdf' ]); } + + /** + * @return void + */ + public function testExecuteWithEmptyFilesArrayThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $this->expectExceptionCode(HandlerException::CODE_EXECUTE); + $handler = new MergeHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute(dirname(__DIR__, 2) . '/files/merge', 'res.pdf', []); + } + + /** + * @return void + */ + public function testExecuteWithAllInvalidFilesThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $this->expectExceptionCode(HandlerException::CODE_EXECUTE); + $handler = new MergeHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute(dirname(__DIR__, 2) . '/files/merge', 'res.pdf', [ + dirname(__DIR__, 2) . '/files/merge/nonexistent1.pdf', + dirname(__DIR__, 2) . '/files/merge/nonexistent2.pdf', + ]); + } + + /** + * @return void + */ + public function testExecuteWithOnlyOneValidFileMergesSuccessfully(): void + { + $path = dirname(__DIR__, 2) . '/files/merge'; + $handler = new MergeHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $result = $handler->execute($path, 'single.pdf', [ + dirname(__DIR__, 2) . '/files/merge/part_1.pdf', + dirname(__DIR__, 2) . '/files/merge/nonexistent.pdf', + ]); + $this->assertFileExists($result); + } } diff --git a/tests/Handlers/SplitHandlerTest.php b/tests/Handlers/SplitHandlerTest.php index ad35fc1..e217850 100644 --- a/tests/Handlers/SplitHandlerTest.php +++ b/tests/Handlers/SplitHandlerTest.php @@ -42,4 +42,58 @@ public function testExecuteFailedShouldShouldThrowHandlerException(): void ]); $handler->execute(dirname(__DIR__, 2) . '/files/split/test.pdf', '/tmp/mock/files'); } + + /** + * @return void + */ + public function testSplitOutputFilesStartAtPartOne(): void + { + $handler = new SplitHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $outputPath = sys_get_temp_dir() . '/gs_split_test_' . uniqid(); + $parts = $handler->execute(dirname(__DIR__, 2) . '/files/split/test.pdf', $outputPath); + + $this->assertCount(3, $parts); + $this->assertStringEndsWith('/part_1.pdf', $parts[0]); + $this->assertStringEndsWith('/part_2.pdf', $parts[1]); + $this->assertStringEndsWith('/part_3.pdf', $parts[2]); + } + + /** + * @return void + */ + public function testSplitOutputFilesAreCreated(): void + { + $handler = new SplitHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $outputPath = sys_get_temp_dir() . '/gs_split_test_' . uniqid(); + $parts = $handler->execute(dirname(__DIR__, 2) . '/files/split/test.pdf', $outputPath); + + foreach ($parts as $part) { + $this->assertFileExists($part); + } + } + + /** + * @return void + */ + public function testExecuteWithNonExistentFileThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $this->expectExceptionCode(HandlerException::CODE_EXECUTE); + $handler = new SplitHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute('/nonexistent/file.pdf', sys_get_temp_dir()); + } + + /** + * @return void + */ + public function testExecuteWithEmptyFilePathThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $handler = new SplitHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute('', sys_get_temp_dir()); + } } diff --git a/tests/Handlers/ToImageHandlerTest.php b/tests/Handlers/ToImageHandlerTest.php index af50a34..e575654 100644 --- a/tests/Handlers/ToImageHandlerTest.php +++ b/tests/Handlers/ToImageHandlerTest.php @@ -63,4 +63,43 @@ public function testExecuteFailedShouldThrowHandlerException(): void ]); $handler->execute(dirname(__DIR__, 2) . '/files/to-image/test.pdf', '/tmp/mock/files'); } + + /** + * @return void + */ + public function testExecuteWithInvalidImageTypeThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $this->expectExceptionCode(HandlerException::CODE_EXECUTE); + $handler = new ToImageHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute( + dirname(__DIR__, 2) . '/files/to-image/test.pdf', + sys_get_temp_dir(), + 'bmp' + ); + } + + /** + * @return void + */ + public function testExecuteWithEmptyFilePathThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $handler = new ToImageHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute('', sys_get_temp_dir(), 'jpeg'); + } + + /** + * @return void + */ + public function testExecuteWithNonExistentFileThrowsHandlerException(): void + { + $this->expectException(HandlerException::class); + $this->expectExceptionCode(HandlerException::CODE_EXECUTE); + $handler = new ToImageHandler(); + $handler->setBinPath($this->getEnv('GS_BIN_PATH')); + $handler->execute('/nonexistent/file.pdf', sys_get_temp_dir(), 'jpeg'); + } } diff --git a/tests/Helpers/PathHelperTest.php b/tests/Helpers/PathHelperTest.php index f7b58ea..03e1964 100644 --- a/tests/Helpers/PathHelperTest.php +++ b/tests/Helpers/PathHelperTest.php @@ -14,4 +14,48 @@ public function testPathShouldEqualOriginPathAfterConversion(): void { $this->assertEquals(implode(DIRECTORY_SEPARATOR, ['usr', 'bin', 'gs']), PathHelper::convertPathSeparator('usr/bin/gs')); } + + /** + * @return void + */ + public function testEmptyStringReturnsEmptyString(): void + { + $this->assertEquals('', PathHelper::convertPathSeparator('')); + } + + /** + * @return void + */ + public function testBackslashesAreConverted(): void + { + $result = PathHelper::convertPathSeparator('usr\\bin\\gs'); + $this->assertEquals(implode(DIRECTORY_SEPARATOR, ['usr', 'bin', 'gs']), $result); + } + + /** + * @return void + */ + public function testMixedSeparatorsAreNormalized(): void + { + $result = PathHelper::convertPathSeparator('usr/bin\\gs'); + $this->assertEquals(implode(DIRECTORY_SEPARATOR, ['usr', 'bin', 'gs']), $result); + } + + /** + * @return void + */ + public function testAlreadyNormalizedPathIsUnchanged(): void + { + $path = implode(DIRECTORY_SEPARATOR, ['usr', 'bin', 'gs']); + $this->assertEquals($path, PathHelper::convertPathSeparator($path)); + } + + /** + * @return void + */ + public function testPathWithSpacesIsPreserved(): void + { + $result = PathHelper::convertPathSeparator('path/with spaces/file.pdf'); + $this->assertEquals(implode(DIRECTORY_SEPARATOR, ['path', 'with spaces', 'file.pdf']), $result); + } }