From fad2fac39c3ed8a60933a9d70229891afac337b9 Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Tue, 18 Jan 2022 15:05:33 -0600 Subject: [PATCH] feat: move all library code to ramsey/composer-repl-lib --- .github/dependabot.yml | 4 - .github/workflows/continuous-integration.yml | 168 ------------- CHANGELOG.md | 22 ++ CONTRIBUTING.md | 71 +----- LICENSE | 2 +- README.md | 19 +- bin/repl | 72 ------ build/.gitignore | 6 - build/cache/.gitkeep | 0 build/logs/.gitkeep | 0 captainhook.json | 26 +- codecov.yml | 29 --- composer.json | 28 +-- conventional-commits.json | 1 - docs/.gitkeep | 0 phpcs.xml.dist | 13 - phpstan.neon.dist | 6 - phpunit.xml.dist | 21 -- psalm-baseline.xml | 2 - psalm.xml | 17 -- repl.php | 13 - src/Composer/ReplCommand.php | 88 ------- src/Composer/ReplPlugin.php | 87 ------- src/Process/Process.php | 95 -------- src/Process/ProcessFactory.php | 46 ---- src/Psy/ContextAwareCommand.php | 60 ----- src/Psy/ElephpantCommand.php | 79 ------ src/Psy/PhpunitRunCommand.php | 243 ------------------- src/Psy/PhpunitTestCommand.php | 107 -------- src/Repl.php | 144 ----------- tests/Composer/ReplCommandTest.php | 68 ------ tests/Composer/ReplPluginTest.php | 92 ------- tests/Process/ProcessFactoryTest.php | 19 -- tests/Process/ProcessMock.php | 20 -- tests/Process/ProcessTest.php | 33 --- tests/Psy/ElephpantCommandTest.php | 30 --- tests/Psy/PhpunitRunCommandTest.php | 218 ----------------- tests/Psy/PhpunitTestCommandTest.php | 181 -------------- 38 files changed, 42 insertions(+), 2088 deletions(-) delete mode 100644 .github/workflows/continuous-integration.yml delete mode 100755 bin/repl delete mode 100644 build/.gitignore delete mode 100644 build/cache/.gitkeep delete mode 100644 build/logs/.gitkeep delete mode 100644 codecov.yml delete mode 100644 docs/.gitkeep delete mode 100644 phpcs.xml.dist delete mode 100644 phpstan.neon.dist delete mode 100644 phpunit.xml.dist delete mode 100644 psalm-baseline.xml delete mode 100644 psalm.xml delete mode 100644 repl.php delete mode 100644 src/Composer/ReplCommand.php delete mode 100644 src/Composer/ReplPlugin.php delete mode 100644 src/Process/Process.php delete mode 100644 src/Process/ProcessFactory.php delete mode 100644 src/Psy/ContextAwareCommand.php delete mode 100644 src/Psy/ElephpantCommand.php delete mode 100644 src/Psy/PhpunitRunCommand.php delete mode 100644 src/Psy/PhpunitTestCommand.php delete mode 100644 src/Repl.php delete mode 100644 tests/Composer/ReplCommandTest.php delete mode 100644 tests/Composer/ReplPluginTest.php delete mode 100644 tests/Process/ProcessFactoryTest.php delete mode 100644 tests/Process/ProcessMock.php delete mode 100644 tests/Process/ProcessTest.php delete mode 100644 tests/Psy/ElephpantCommandTest.php delete mode 100644 tests/Psy/PhpunitRunCommandTest.php delete mode 100644 tests/Psy/PhpunitTestCommandTest.php diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b093e5f..220288b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,3 @@ updates: schedule: interval: "monthly" versioning-strategy: "increase-if-necessary" - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "monthly" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml deleted file mode 100644 index 727f894..0000000 --- a/.github/workflows/continuous-integration.yml +++ /dev/null @@ -1,168 +0,0 @@ -# GitHub Actions Documentation: https://docs.github.com/en/actions - -name: "build" - -on: - push: - branches: - - "main" - tags: - - "*" - pull_request: - branches: - - "main" - -# Cancels all previous workflow runs for the same branch that have not yet completed. -concurrency: - # The concurrency group contains the workflow name and the branch name. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - COMPOSER_ROOT_VERSION: "1.99.99" - -jobs: - coding-standards: - name: "Coding standards" - runs-on: "ubuntu-latest" - steps: - - name: "Checkout repository" - uses: "actions/checkout@v2" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "latest" - coverage: "none" - - - name: "Install dependencies (Composer)" - uses: "ramsey/composer-install@v2" - - - name: "Check syntax (php-parallel-lint)" - shell: "bash" - run: "composer dev:lint:syntax" - - - name: "Check coding standards (PHP_CodeSniffer)" - shell: "bash" - run: "composer dev:lint:style" - - static-analysis: - name: "Static analysis" - runs-on: "ubuntu-latest" - steps: - - name: "Checkout repository" - uses: "actions/checkout@v2" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "latest" - coverage: "none" - - - name: "Install dependencies (Composer)" - uses: "ramsey/composer-install@v2" - - - name: "Statically analyze code (PHPStan)" - shell: "bash" - run: "composer dev:analyze:phpstan" - - - name: "Statically analyze code (Psalm)" - shell: "bash" - run: "composer dev:analyze:psalm -- --shepherd" - - security-analysis: - name: "Security analysis" - needs: ["coding-standards", "static-analysis"] - runs-on: "ubuntu-latest" - steps: - - name: "Checkout repository" - uses: "actions/checkout@v2" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "latest" - coverage: "none" - - - name: "Install dependencies (Composer)" - uses: "ramsey/composer-install@v2" - - - name: "Analyze security of code (Psalm)" - shell: "bash" - run: "./vendor/bin/psalm --taint-analysis --report=build/logs/psalm.sarif" - - - name: "Upload security analysis results to GitHub" - uses: "github/codeql-action/upload-sarif@v1" - with: - sarif_file: "build/logs/psalm.sarif" - - code-coverage: - name: "Code coverage" - needs: ["coding-standards", "static-analysis"] - runs-on: "ubuntu-latest" - steps: - - name: "Checkout repository" - uses: "actions/checkout@v2" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "latest" - coverage: "pcov" - ini-values: "memory_limit=-1" - - - name: "Install dependencies (Composer)" - uses: "ramsey/composer-install@v2" - - - name: "Run unit tests (PHPUnit)" - shell: "bash" - run: "composer dev:test:coverage:ci" - - - name: "Publish coverage report to Codecov" - uses: "codecov/codecov-action@v2.1.0" - - unit-tests: - name: "Unit tests" - needs: ["code-coverage", "security-analysis"] - runs-on: ${{ matrix.operating-system }} - - strategy: - fail-fast: false - matrix: - php-version: - - "7.4" - - "8.0" - - "8.1" - operating-system: - - "macos-latest" - - "ubuntu-latest" - - "windows-latest" - dependencies: - - "lowest" - - "highest" - - steps: - - name: "Configure Git (for Windows)" - if: ${{ matrix.operating-system == 'windows-latest' }} - shell: "bash" - run: | - git config --system core.autocrlf false - git config --system core.eol lf - - - name: "Checkout repository" - uses: "actions/checkout@v2" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - php-version: "${{ matrix.php-version }}" - coverage: "none" - - - name: "Install dependencies (Composer)" - uses: "ramsey/composer-install@v2" - with: - dependency-versions: "${{ matrix.dependencies }}" - - - name: "Run unit tests (PHPUnit)" - shell: "bash" - run: "composer dev:test:unit" diff --git a/CHANGELOG.md b/CHANGELOG.md index 47f3962..94ebf44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## 1.4.0 - 2022-01-18 + +### Added + +- Nothing. + +### Changed + +- Move all library code to [ramsey/composer-repl-lib](https://github.com/ramsey/composer-repl-lib). No class names or namespaces have changed, and since this package requires ramsey/composer-repl-lib, there are no BC breaks. + +### Deprecated + +- Nothing. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 1.3.0 - 2022-01-02 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f12214c..918ddc6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,8 @@ # Contributing +ℹī¸ _**NOTICE:** First, check out [ramsey/composer-repl-lib](https://github.com/ramsey/composer-repl-lib) +to decide whether your contribution or bug report is best suited for that library._ + Contributions are welcome. This project accepts pull requests on [GitHub][]. This project adheres to a [code of conduct](CODE_OF_CONDUCT.md). By @@ -72,12 +75,6 @@ When you do begin working on your feature, here are some guidelines to consider: We will use this description to update the CHANGELOG. If there is no description, or it does not adequately describe your feature, we may ask you to update the description. -* ramsey/composer-repl follows a superset of **[PSR-12 coding standard][psr-12]**. - Please ensure your code does, too. _Hint: run `composer dev:lint` to check._ -* Please **write tests** for any new features you add. -* Please **ensure that tests pass** before submitting your pull request. - ramsey/composer-repl automatically runs tests for pull requests. However, - running the tests locally will help save time. _Hint: run `composer test`._ * **Use topic/feature branches.** Please do not ask to pull from your main branch. * For more information, see "[Understanding the GitHub flow][gh-flow]." * **Submit one feature per pull request.** If you have multiple features you @@ -107,68 +104,6 @@ Now, you are ready to develop! This project uses [CaptainHook](https://github.com/CaptainHookPhp/captainhook) to validate all staged changes prior to commit. -### Commands - -To see all the commands available for contributing to this project: - -``` bash -composer list dev -``` - -### Coding Standards - -This project follows a superset of [PSR-12](https://www.php-fig.org/psr/psr-12/) -coding standards, enforced by [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer). - -CaptainHook will run coding standards checks before committing. - -You may lint the codebase manually using the following commands: - -``` bash -# Lint -composer dev:lint - -# Attempt to auto-fix coding standards issues -composer dev:lint:fix -``` - -### Static Analysis - -This project uses a combination of [PHPStan](https://github.com/phpstan/phpstan) -and [Psalm](https://github.com/vimeo/psalm) to provide static analysis of PHP -code. - -CaptainHook will run static analysis checks before committing. - -You may run static analysis manually across the whole codebase with the -following command: - -``` bash -# Static analysis -composer dev:analyze -``` - -### Project Structure - -This project uses [pds/skeleton](https://github.com/php-pds/skeleton) as its -base folder structure and layout. - -### Running Tests - -The following must pass before we will accept a pull request. If this does not -pass, it will result in a complete build failure. Before you can run this, be -sure to `composer install`. - -To run all the tests and coding standards checks, execute the following from the -command line, while in the project root directory: - -``` -composer test -``` - -CaptainHook will automatically run all tests before pushing to the remote -repository. - [github]: https://github.com/ramsey/composer-repl [issues]: https://github.com/ramsey/composer-repl/issues [pull requests]: https://github.com/ramsey/composer-repl/pulls diff --git a/LICENSE b/LICENSE index b46cccf..ac1ed66 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020-2021 Ben Ramsey +Copyright (c) 2020-2022 Ben Ramsey Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 86401e5..b0ff5ac 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Download Package PHP Programming Language Read License - Build Status - Codecov Code Coverage - Psalm Type Coverage + Build Status + Codecov Code Coverage + Psalm Type Coverage

## About @@ -36,6 +36,9 @@ for WordPress, [CakePHP console](https://book.cakephp.org/3/en/console-and-shell and [Yii shell](https://github.com/yiisoft/yii2-shell) are a few of the projects using PsySH. +> 💡 You may use this REPL without the Composer plugin functionality by requiring +> [ramsey/composer-repl-lib](https://github.com/ramsey/composer-repl-lib) instead. + This project adheres to a [code of conduct](CODE_OF_CONDUCT.md). By participating in this project and its community, you are expected to uphold this code. @@ -57,7 +60,7 @@ if you prefer. You'll see something similar to this: ``` -Psy Shell v0.10.8 (PHP 8.0.9 — cli) by Justin Hileman +Psy Shell v0.11.1 (PHP 8.1.1 — cli) by Justin Hileman ------------------------------------------------------------------------ Welcome to the development console (REPL). To learn more about what you can do in PsySH, type `help`. @@ -95,9 +98,9 @@ Test passed! >>> phpunit -PHPUnit 9.3.7 by Sebastian Bergmann and contributors. +PHPUnit 9.5.11 by Sebastian Bergmann and contributors. -Runtime: PHP 7.4.9 +Runtime: PHP 8.1.1 Configuration: /path/to/ramsey/conventional-commits/phpunit.xml.dist ............................................................... 63 / 221 ( 28%) @@ -110,7 +113,7 @@ Time: 00:00.064, Memory: 12.00 MB OK (221 tests, 484 assertions) ``` -This implementation of PsySH has Super ElePHPant Powers. +✨🐘 This implementation of PsySH has Super ElePHPant Powers. 🐘✨ ## Environment Bootstrapping @@ -181,6 +184,6 @@ security issue in software that is maintained in this repository, please read ## Copyright and License -The ramsey/composer-repl library is copyright Š [Ben Ramsey](https://benramsey.com) +The ramsey/composer-repl plugin is copyright Š [Ben Ramsey](https://benramsey.com) and licensed for use under the terms of the MIT License (MIT). Please see [LICENSE](LICENSE) for more information. diff --git a/bin/repl b/bin/repl deleted file mode 100755 index f7d882a..0000000 --- a/bin/repl +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env php - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -use Composer\Factory; -use Composer\IO\ConsoleIO; -use Ramsey\Dev\Repl\Process\ProcessFactory; -use Ramsey\Dev\Repl\Repl; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Input\StringInput; - -(static function (): void { - $composerAutoloadLocations = [ - __DIR__ . '/../autoload.php', - __DIR__ . '/../vendor/autoload.php', - __DIR__ . '/../../../autoload.php', - ]; - - foreach ($composerAutoloadLocations as $file) { - if (file_exists($file)) { - $composerAutoloader = $file; - - break; - } - } - unset($file); - - if (!isset($composerAutoloader)) { - fwrite( - STDERR, - 'To execute this command, please install Composer and run \'composer install\'.' . PHP_EOL - . 'For more information, go to https://getcomposer.org' . PHP_EOL, - ); - - exit(1); - } - - require $composerAutoloader; - - $input = new StringInput(''); - $output = Factory::createOutput(); - $helperSet = new HelperSet(); - $io = new ConsoleIO($input, $output, $helperSet); - - $composerFile = (string) Factory::getComposerFile(); - $composer = Factory::create($io, $composerFile); - - $repositoryRoot = (string) realpath(dirname($composerFile)); - $processFactory = new ProcessFactory(); - - $repl = new Repl($repositoryRoot, $processFactory, $composer); - $repl->run(); -})(); diff --git a/build/.gitignore b/build/.gitignore deleted file mode 100644 index 1375c16..0000000 --- a/build/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -* -!.gitignore -!cache -!cache/.gitkeep -!logs -!logs/.gitkeep diff --git a/build/cache/.gitkeep b/build/cache/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/build/logs/.gitkeep b/build/logs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/captainhook.json b/captainhook.json index 3068ac8..bd2c5b1 100644 --- a/captainhook.json +++ b/captainhook.json @@ -14,12 +14,8 @@ ] }, "pre-push": { - "enabled": true, - "actions": [ - { - "action": "composer test" - } - ] + "enabled": false, + "actions": [] }, "pre-commit": { "enabled": true, @@ -41,24 +37,6 @@ "args": [["composer.json"]] } ] - }, - { - "action": "composer dev:lint:syntax -- {$STAGED_FILES|of-type:php}", - "conditions": [ - { - "exec": "\\CaptainHook\\App\\Hook\\Condition\\FileStaged\\OfType", - "args": ["php"] - } - ] - }, - { - "action": "composer dev:lint:style -- {$STAGED_FILES|of-type:php}", - "conditions": [ - { - "exec": "\\CaptainHook\\App\\Hook\\Condition\\FileStaged\\OfType", - "args": ["php"] - } - ] } ] }, diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 047a8a9..0000000 --- a/codecov.yml +++ /dev/null @@ -1,29 +0,0 @@ -codecov: - require_ci_to_pass: yes - -coverage: - precision: 2 - round: down - range: "70...100" - status: - project: - default: - target: auto - threshold: 0% - patch: - default: - target: auto - threshold: 0% - -parsers: - gcov: - branch_detection: - conditional: yes - loop: yes - method: no - macro: no - -comment: - layout: "reach,diff,flags,tree" - behavior: default - require_changes: false diff --git a/composer.json b/composer.json index 1a7c340..62a3b0b 100644 --- a/composer.json +++ b/composer.json @@ -18,38 +18,21 @@ "require": { "php": "^7.4 || ^8", "composer-plugin-api": "^1.1 || ^2", - "phpunit/phpunit": "^6 || ^7 || ^8 || ^9", - "psy/psysh": "^0.11.0", - "symfony/console": "^4.4.30 || ^5.3.7 || ^6", - "symfony/process": "^4.4.30 || ^5.3.7 || ^6" + "ramsey/composer-repl-lib": "^1.0" }, "require-dev": { - "composer/composer": "^1.10.23 || ^2.1.9", "ramsey/devtools": "^1.3" }, "minimum-stability": "dev", "prefer-stable": true, - "autoload": { - "psr-4": { - "Ramsey\\Dev\\Repl\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Ramsey\\Test\\Dev\\Repl\\": "tests/" - } - }, - "bin": [ - "bin/repl" - ], "config": { "allow-plugins": { + "composer/package-versions-deprecated": true, "phpstan/extension-installer": true, "dealerdirect/phpcodesniffer-composer-installer": true, "ergebnis/composer-normalize": true, "captainhook/plugin-composer": true, - "ramsey/devtools": true, - "composer/package-versions-deprecated": true + "ramsey/devtools": true }, "sort-packages": true }, @@ -61,11 +44,6 @@ "force-install": true }, "class": "Ramsey\\Dev\\Repl\\Composer\\ReplPlugin", - "ramsey/composer-repl": { - "includes": [ - "repl.php" - ] - }, "ramsey/conventional-commits": { "configFile": "conventional-commits.json" }, diff --git a/conventional-commits.json b/conventional-commits.json index b5a0823..5fe21d2 100644 --- a/conventional-commits.json +++ b/conventional-commits.json @@ -3,7 +3,6 @@ "types": [ "chore", "ci", - "deps", "docs", "feat", "fix", diff --git a/docs/.gitkeep b/docs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index f76cd66..0000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - ./src - ./tests - - - - diff --git a/phpstan.neon.dist b/phpstan.neon.dist deleted file mode 100644 index 7cad6b1..0000000 --- a/phpstan.neon.dist +++ /dev/null @@ -1,6 +0,0 @@ -parameters: - tmpDir: ./build/cache/phpstan - level: max - paths: - - ./src - - ./tests diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index 32ffda6..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - ./tests - - - - - - ./src - - - - diff --git a/psalm-baseline.xml b/psalm-baseline.xml deleted file mode 100644 index ca62028..0000000 --- a/psalm-baseline.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index cba5289..0000000 --- a/psalm.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - diff --git a/repl.php b/repl.php deleted file mode 100644 index fe93c16..0000000 --- a/repl.php +++ /dev/null @@ -1,13 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace Ramsey\Dev\Repl\Composer; - -use Composer\Command\BaseCommand; -use Composer\Composer; -use Composer\Config; -use Ramsey\Dev\Repl\Process\ProcessFactory; -use Ramsey\Dev\Repl\Repl; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Composer command to launch a PsySH REPL - */ -class ReplCommand extends BaseCommand -{ - private string $repositoryRoot; - private ProcessFactory $processFactory; - private bool $isInteractive; - - public function __construct( - string $repositoryRoot, - ProcessFactory $processFactory, - Composer $composer, - bool $isInteractive = true - ) { - parent::__construct(null); - - $this->repositoryRoot = $repositoryRoot; - $this->processFactory = $processFactory; - $this->setComposer($composer); - $this->isInteractive = $isInteractive; - } - - protected function configure(): void - { - $this - ->setName('repl') - ->setDescription('Launches a development console (REPL) for PHP.') - ->setAliases(['shell']); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - Config::disableProcessTimeout(); - $this->loadEnvironment(); - - /** @var Composer $composer */ - $composer = $this->getComposer(true); - - $repl = new Repl($this->repositoryRoot, $this->processFactory, $composer, $this->isInteractive); - - return $repl->run(); - } - - private function loadEnvironment(): void - { - /** @var Composer $composer */ - $composer = $this->getComposer(true); - - /** @var string $vendorDir */ - $vendorDir = $composer->getConfig()->get('vendor-dir'); - - /** @psalm-suppress UnresolvableInclude */ - require $vendorDir . '/autoload.php'; - } -} diff --git a/src/Composer/ReplPlugin.php b/src/Composer/ReplPlugin.php deleted file mode 100644 index c5875df..0000000 --- a/src/Composer/ReplPlugin.php +++ /dev/null @@ -1,87 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace Ramsey\Dev\Repl\Composer; - -use Composer\Command\BaseCommand; -use Composer\Composer; -use Composer\Factory; -use Composer\IO\IOInterface; -use Composer\Plugin\Capability\CommandProvider; -use Composer\Plugin\Capable; -use Composer\Plugin\PluginInterface; -use Ramsey\Dev\Repl\Process\ProcessFactory; - -use function dirname; -use function realpath; - -/** - * Composer plugin providing REPL functionality - */ -class ReplPlugin implements Capable, CommandProvider, PluginInterface -{ - private static Composer $composer; - - private ProcessFactory $processFactory; - private string $repoRoot; - - public function __construct() - { - $composerFile = Factory::getComposerFile(); - - $this->repoRoot = (string) realpath(dirname($composerFile)); - $this->processFactory = new ProcessFactory(); - } - - /** - * @return array - */ - public function getCapabilities(): array - { - return [ - CommandProvider::class => self::class, - ]; - } - - /** - * @return BaseCommand[] - */ - public function getCommands(): array - { - return [ - new ReplCommand($this->repoRoot, $this->processFactory, self::$composer), - ]; - } - - public function activate(Composer $composer, IOInterface $io): void - { - self::$composer = $composer; - } - - public function deactivate(Composer $composer, IOInterface $io): void - { - } - - public function uninstall(Composer $composer, IOInterface $io): void - { - } -} diff --git a/src/Process/Process.php b/src/Process/Process.php deleted file mode 100644 index 9f8fc5d..0000000 --- a/src/Process/Process.php +++ /dev/null @@ -1,95 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace Ramsey\Dev\Repl\Process; - -use IteratorAggregate; -use ReflectionClass; -use ReflectionException; -use ReflectionMethod; -use ReflectionNamedType; -use Symfony\Component\Process\Process as SymfonyProcess; - -use function array_map; -use function array_shift; -use function escapeshellarg; -use function implode; - -/** - * @internal - * - * @template TKey - * @template TValue - * @implements IteratorAggregate - */ -class Process extends SymfonyProcess implements IteratorAggregate -{ - /** - * @param string[] $command - * - * @throws ReflectionException - * - * @psalm-suppress PossiblyInvalidArgument - */ - public function __construct(array $command, ?string $cwd = null) - { - // @phpstan-ignore-next-line - parent::__construct($this->useCorrectCommand($command), $cwd); - } - - /** - * @param string[] $command - * - * @return string[]|string - * - * @throws ReflectionException - */ - protected function useCorrectCommand(array $command) - { - $reflectedProcess = new ReflectionClass($this->getProcessClassName()); - - /** @var ReflectionMethod $reflectedConstructor */ - $reflectedConstructor = $reflectedProcess->getConstructor(); - $reflectedConstructorType = $reflectedConstructor->getParameters()[0]->getType(); - - if ($reflectedConstructorType instanceof ReflectionNamedType) { - if ($reflectedConstructorType->getName() === 'array') { - return $command; - } - } - - $commandLine = array_shift($command) . ' '; - $commandLine .= implode(' ', array_map(fn ($v) => escapeshellarg($v), $command)); - - return $commandLine; - } - - /** - * For internal test-mocking purposes only - * - * @return class-string - */ - protected function getProcessClassName(): string - { - return SymfonyProcess::class; - } -} diff --git a/src/Process/ProcessFactory.php b/src/Process/ProcessFactory.php deleted file mode 100644 index 77e899e..0000000 --- a/src/Process/ProcessFactory.php +++ /dev/null @@ -1,46 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace Ramsey\Dev\Repl\Process; - -use ReflectionException; - -/** - * Factory to create a Process instance for running commands - * - * @internal - */ -class ProcessFactory -{ - /** - * @param string[] $command - * - * @return Process - * - * @throws ReflectionException - */ - public function factory(array $command, ?string $cwd = null): Process - { - /** @var Process */ - return new Process($command, $cwd); - } -} diff --git a/src/Psy/ContextAwareCommand.php b/src/Psy/ContextAwareCommand.php deleted file mode 100644 index 82d8c05..0000000 --- a/src/Psy/ContextAwareCommand.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace Ramsey\Dev\Repl\Psy; - -use Psy\Command\Command; -use Psy\Context; -use Psy\ContextAware; -use Psy\Shell; -use RuntimeException; - -abstract class ContextAwareCommand extends Command implements ContextAware -{ - protected ?Context $context = null; - - public function setContext(Context $context): void - { - $this->context = $context; - } - - public function getApplication(): Shell - { - /** @var Shell | null $application */ - $application = parent::getApplication(); - - if ($application === null) { - throw new RuntimeException('Application is not set'); - } - - return $application; - } - - public function getContext(): Context - { - if ($this->context === null) { - throw new RuntimeException('Context is not set'); - } - - return $this->context; - } -} diff --git a/src/Psy/ElephpantCommand.php b/src/Psy/ElephpantCommand.php deleted file mode 100644 index 02462de..0000000 --- a/src/Psy/ElephpantCommand.php +++ /dev/null @@ -1,79 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace Ramsey\Dev\Repl\Psy; - -use Psy\Command\Command; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; - -class ElephpantCommand extends Command -{ - protected function configure(): void - { - $this - ->setName('🐘') - ->setAliases(['elephpant']) - ->setDescription('¯\_(ツ)_/¯') - ->setHelp('¯\_(ツ)_/¯'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $output->writeln( - <<<'EOF' - - ^;)|` `-;;-` - -uwu/;lPMMMMMMa^ - .-/>lv_\fWMMMMMMMMMMQ-|NNBWQQQMMMMMQBmD0v=-` - ;JXMMMMMMMMMMMMMMMMMMMMMMm XMMMMMMMMMMMMMMMMMMMMP1. -;^ - lWMMWGPPAQMMMMMMMMMMMMMMMMMM:>MMMMMMMMMMMMMMMMMMMMMMMw- -::^ `;1rvl^ - aMMD-\JwGPdMMMMMMMMMMMMMMMMMMr-MMMMMMM@DWMMMMMMMMMMMMMMMc `:=)` - ;MMMQNMQ0raMMMMMMMMMMMMMMMMMMMu.MMMMMMv XMMMMMMMMMMMMMMMMy `` - cMMMMMq.)l7vMMMMMMMMMMMMMMMMMMl`;|lXMM^ ::;|lRMd;:::;\>oQMl - 0MMMMMWrW0_*MMMQMMMTMMMMMMMMMW.}s: aD ]TaI \M+ 0kac :MW - TMMMMMMMKKqMMMM@r770MMMMMMMMD,1MMi x| `QMML oQ` /MMMB` -MM, - FMMMMMMMMMMMMMMMM:GMMMMFr=:` 7vl: `zW )MMM- `Qz *ct)` :qMM- - FMMMMMMMMMMMMMMMP/MMMM:/GV :)=>}xQMg==KMMM>)2M, `))=?cAMMMN - FMMMMMMMMMMMMMQc=QMMMM2NM; `WMMMMMMMMMMMMMMMMMX cMMMMMMMMQ: - FMMMMMMMMD>-^. -=v07QMMMMWNWMMMMMMMMMMMMMMMMMMMNNMMMMMMMMMQ~ - FMMMMMMM= MQ@v WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM, - FMMMMMMD MMMK WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM, - FMMMMMMl MMMK WMMMMMMMMMMMMM@`T@AA@Pa:oMMMMMMMMMMMMMM, - FMMMMMM: KKMK WMMMMMMMMMMMMMP :>z2.tMMMMMMMMMMMMMM, - FMMMMMM. bXVK WMMMMMMMMMMMMMP |qcM-tMMWXMMMMMMMMMM, - FMMMMMQ [Qcyv qPLPkPMMMMMMMMP #waJ,lkoTqoNMMMMMMMM, - FMMMMM@ ?2NMt uVKMqvaqMMMMMMP `QvMq /EoMMLasMMMMMMM, - 0MMMMMu .7Vv_MvMMvMMvMMMMMMa ^-ka^mzQMnNMXTMMMMMM. - `*rv}/` `oiQWzMMu#MMMQf. `-7cM3BMmYMMMEo_ - `\^;;-)/:^ `^`^`,~.` - - - This implementation of PsySH has Super ElePHPant Powers! - https://afieldguidetoelephpants.net - - EOF, - ); - - return 0; - } -} diff --git a/src/Psy/PhpunitRunCommand.php b/src/Psy/PhpunitRunCommand.php deleted file mode 100644 index b86b95c..0000000 --- a/src/Psy/PhpunitRunCommand.php +++ /dev/null @@ -1,243 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace Ramsey\Dev\Repl\Psy; - -use Composer\Composer; -use Ramsey\Dev\Repl\Process\ProcessFactory; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; - -use function array_merge; -use function count; -use function implode; - -use const DIRECTORY_SEPARATOR; - -/** - * PsySH command to allow running the PHPUnit CLI from the development console - */ -class PhpunitRunCommand extends ContextAwareCommand -{ - private string $repositoryRoot; - private ProcessFactory $processFactory; - private Composer $composer; - - public function __construct( - string $repositoryRoot, - ProcessFactory $processFactory, - Composer $composer - ) { - parent::__construct(null); - - $this->repositoryRoot = $repositoryRoot; - $this->processFactory = $processFactory; - $this->composer = $composer; - } - - protected function configure(): void - { - $this - ->setName('phpunit:run') - ->setAliases(['phpunit']) - ->setDefinition([ - new InputArgument( - 'tests', - InputArgument::OPTIONAL | InputArgument::IS_ARRAY, - 'Space-separated list of tests to run.', - [], - ), - new InputOption( - 'group', - 'g', - InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, - 'Only run tests from the specified group(s).', - [], - ), - new InputOption( - 'exclude-group', - 'x', - InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, - 'Exclude tests from the specified group(s).', - [], - ), - new InputOption( - 'testsuite', - 's', - InputOption::VALUE_REQUIRED, - 'Filter which testsuite to run.', - ), - new InputOption( - 'filter', - 'f', - InputOption::VALUE_REQUIRED, - 'Filter which tests to run.', - ), - new InputOption( - 'list-groups', - '', - InputOption::VALUE_NONE, - 'List available test groups.', - ), - new InputOption( - 'list-suites', - '', - InputOption::VALUE_NONE, - 'List available test suites.', - ), - new InputOption( - 'list-tests', - '', - InputOption::VALUE_NONE, - 'List available tests.', - ), - new InputOption( - 'testdox', - '', - InputOption::VALUE_NONE, - 'Report test progress in TestDox format.', - ), - ]) - ->setDescription('Run PHPUnit tests from the dev console.') - ->setHelp( - <<<'HELP' - Use PHPUnit tests from the development console. - - This command provides a subset of features available to the phpunit - command line interface. From the development console, you may run all - tests or filter tests on groups, test suites, and more. - - e.g. - >>> phpunit:run - >>> phpunit:run FooTest BarTest - >>> phpunit --group foo --group bar - >>> phpunit --testdox - HELP, - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - /** @var string $binDir */ - $binDir = $this->composer->getConfig()->get('bin-dir'); - $phpunit = $binDir . DIRECTORY_SEPARATOR . 'phpunit'; - - $process = $this->processFactory->factory( - array_merge([$phpunit, '--colors=always'], $this->buildArguments($input)), - $this->repositoryRoot, - ); - - $process->start(); - - $output->writeln(''); - - $exitCode = $process->wait( - function (string $_type, string $buffer) use ($output): void { - $output->write($buffer); - }, - ); - - $output->writeln(''); - - return $exitCode; - } - - /** - * @return string[] - */ - private function buildArguments(InputInterface $input): array - { - /** @var string[] $testsArgument */ - $testsArgument = $input->getArgument('tests'); - - /** @var string[] $groupOption */ - $groupOption = $input->getOption('group'); - - /** @var string[] $excludeGroupOption */ - $excludeGroupOption = $input->getOption('exclude-group'); - - /** @var string $testsuiteOption */ - $testsuiteOption = $input->getOption('testsuite') ?? ''; - - /** @var string $filterOption */ - $filterOption = $input->getOption('filter') ?? ''; - - /** @var bool $listGroupsOption */ - $listGroupsOption = $input->getOption('list-groups'); - - /** @var bool $listSuitesOption */ - $listSuitesOption = $input->getOption('list-suites'); - - /** @var bool $listTestsOption */ - $listTestsOption = $input->getOption('list-tests'); - - /** @var bool $testdoxOption */ - $testdoxOption = $input->getOption('testdox'); - - $arguments = []; - - if (count($testsArgument) > 0 && $filterOption === '') { - $arguments[] = '--filter'; - $arguments[] = '/' . implode('|', $testsArgument) . '/i'; - } - - if (count($groupOption) > 0) { - $arguments[] = '--group'; - $arguments[] = implode(',', $groupOption); - } - - if (count($excludeGroupOption) > 0) { - $arguments[] = '--exclude-group'; - $arguments[] = implode(',', $excludeGroupOption); - } - - if ($testsuiteOption !== '') { - $arguments[] = '--testsuite'; - $arguments[] = $testsuiteOption; - } - - if ($filterOption !== '') { - $arguments[] = '--filter'; - $arguments[] = $filterOption; - } - - if ($listGroupsOption) { - $arguments[] = '--list-groups'; - } - - if ($listSuitesOption) { - $arguments[] = '--list-suites'; - } - - if ($listTestsOption) { - $arguments[] = '--list-tests'; - } - - if ($testdoxOption) { - $arguments[] = '--testdox'; - } - - return $arguments; - } -} diff --git a/src/Psy/PhpunitTestCommand.php b/src/Psy/PhpunitTestCommand.php deleted file mode 100644 index bd4bec4..0000000 --- a/src/Psy/PhpunitTestCommand.php +++ /dev/null @@ -1,107 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace Ramsey\Dev\Repl\Psy; - -use InvalidArgumentException; -use PHPUnit\Framework\TestCase; -use Psy\Input\CodeArgument; -use Symfony\Component\Console\Input\InputArgument; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; -use Throwable; - -use function method_exists; -use function preg_match; -use function trim; - -/** - * PsySH command to provide PHPUnit testing features in the interactive console - */ -class PhpunitTestCommand extends ContextAwareCommand -{ - protected function configure(): void - { - $this - ->setName('phpunit:test') - ->setAliases(['t']) - ->setDefinition([ - new CodeArgument( - 'assertion', - InputArgument::REQUIRED, - 'The PHPUnit assertion to evaluate.', - ), - ]) - ->setDescription('Use PHPUnit assertions to write tests in the console.') - ->setHelp( - <<<'HELP' - Use PHPUnit assertions to write tests in the development console. - - The assertion must be a valid PHPUnit assertion method call. Any - variables set in the console are available for use in the assertion. - - e.g. - >>> $value = 1 + 1 - >>> $date = new DateTimeImmutable() - >>> phpunit:test assertTrue(false) - >>> t assertSame(2, $value) - >>> t assertInstanceOf(DateTimeInterface::class, $date) - HELP, - ); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - /** @var TestCase $phpunitTestCase */ - $phpunitTestCase = $this->getContext()->get('phpunit'); - - $assertion = $this->getAssertion($input); - - if (!preg_match('/^(assert[a-zA-Z0-9_\x80-\xff]+)\(.+\);?$/', $assertion, $matches)) { - throw new InvalidArgumentException('The assertion provided is not valid'); - } - - $functionName = $matches[1] ?? ''; - - if (!method_exists($phpunitTestCase, $functionName)) { - throw new InvalidArgumentException("{$functionName} is not a PHPUnit assertion method"); - } - - try { - $code = '$phpunit->' . $assertion; - $this->getApplication()->execute($code, true); - $output->writeln('Test passed!'); - } catch (Throwable $exception) { - $output->writeln("Test failed: {$exception->getMessage()}"); - } - - return 0; - } - - private function getAssertion(InputInterface $input): string - { - /** @var string $assertion */ - $assertion = $input->getArgument('assertion') ?? ''; - - return trim($assertion); - } -} diff --git a/src/Repl.php b/src/Repl.php deleted file mode 100644 index a978f51..0000000 --- a/src/Repl.php +++ /dev/null @@ -1,144 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace Ramsey\Dev\Repl; - -use Composer\Composer; -use PHPUnit\Framework\TestCase; -use Psy\Configuration; -use Psy\Shell; -use Ramsey\Dev\Repl\Process\ProcessFactory; -use Ramsey\Dev\Repl\Psy\ElephpantCommand; -use Ramsey\Dev\Repl\Psy\PhpunitRunCommand; -use Ramsey\Dev\Repl\Psy\PhpunitTestCommand; - -use function getenv; -use function sprintf; - -/** - * Customizes and controls an instance of PsySH Shell - * - * @psalm-type ComposerReplIncludesType = array{includes?: array} - * @psalm-type ComposerExtrasType = array{"ramsey/composer-repl"?: ComposerReplIncludesType} - */ -class Repl -{ - private string $repositoryRoot; - private ProcessFactory $processFactory; - private Composer $composer; - private bool $isInteractive; - - public function __construct( - string $repositoryRoot, - ProcessFactory $processFactory, - Composer $composer, - bool $isInteractive = true - ) { - $this->repositoryRoot = $repositoryRoot; - $this->processFactory = $processFactory; - $this->composer = $composer; - $this->isInteractive = $isInteractive; - } - - public function run(): int - { - $shell = new Shell($this->getPsyConfig()); - $shell->setScopeVariables($this->getScopeVariables()); - $shell->add(new PhpunitTestCommand()); - $shell->add(new PhpunitRunCommand( - $this->repositoryRoot, - $this->processFactory, - $this->composer, - )); - $shell->add(new ElephpantCommand()); - - return $shell->run(); - } - - private function getPsyConfig(): Configuration - { - $config = new Configuration([ - 'startupMessage' => $this->getStartupMessage(), - 'colorMode' => Configuration::COLOR_MODE_FORCED, - 'updateCheck' => 'never', - 'useBracketedPaste' => true, - 'defaultIncludes' => $this->getDefaultIncludes(), - ]); - - if ($this->isInteractive === false) { - $config->setInteractiveMode(Configuration::INTERACTIVE_MODE_DISABLED); - } - - return $config; - } - - private function getStartupMessage(): string - { - $startupMessage = <<<'EOD' - ------------------------------------------------------------------------ - Welcome to the development console (REPL)%s. - To learn more about what you can do in PsySH, type `help`. - ------------------------------------------------------------------------ - EOD; - - $packageName = $this->composer->getPackage()->getPrettyName(); - $forPackage = ''; - - if ($packageName !== '__root__') { - $forPackage = " for {$packageName}"; - } - - return sprintf($startupMessage, $forPackage); - } - - /** - * @return array{env: array, phpunit: TestCase} - */ - private function getScopeVariables(): array - { - return [ - 'env' => getenv(), - 'phpunit' => $this->getPhpUnitTestCase(), - ]; - } - - /** - * @return string[] - */ - private function getDefaultIncludes(): array - { - /** @var ComposerExtrasType $extra */ - $extra = $this->composer->getPackage()->getExtra(); - - return $extra['ramsey/composer-repl']['includes'] ?? []; - } - - /** - * @psalm-suppress PropertyNotSetInConstructor - * @psalm-suppress InternalMethod - */ - private function getPhpUnitTestCase(): TestCase - { - return new class extends TestCase { - }; - } -} diff --git a/tests/Composer/ReplCommandTest.php b/tests/Composer/ReplCommandTest.php deleted file mode 100644 index 0374154..0000000 --- a/tests/Composer/ReplCommandTest.php +++ /dev/null @@ -1,68 +0,0 @@ -mockery(EventDispatcher::class)->shouldIgnoreMissing(); - - /** @var RootPackageInterface & MockInterface $package */ - $package = $this->mockery(RootPackageInterface::class, [ - 'getPrettyName' => 'foo/bar', - 'getFullPrettyVersion' => 'dev-test', - 'getExtra' => [ - 'ramsey/composer-repl' => [ - 'includes' => [ - __DIR__ . '/../../repl.php', - ], - ], - ], - ]); - - $config = new Config(); - $config->merge([ - 'config' => [ - 'vendor-dir' => realpath(dirname(__FILE__) . '/../../vendor'), - ], - ]); - - $composer = new Composer(); - $composer->setEventDispatcher($dispatcher); - $composer->setConfig($config); - $composer->setPackage($package); - - /** @var InputInterface & MockInterface $input */ - $input = $this->mockery(InputInterface::class)->shouldIgnoreMissing(); - - /** @var OutputInterface & MockInterface $output */ - $output = $this->mockery(OutputInterface::class); - - $command = new ReplCommand( - (string) realpath(dirname(__FILE__) . '/../../'), - new ProcessFactory(), - $composer, - false, - ); - - $command->run($input, $output); - } -} diff --git a/tests/Composer/ReplPluginTest.php b/tests/Composer/ReplPluginTest.php deleted file mode 100644 index dd6bb0b..0000000 --- a/tests/Composer/ReplPluginTest.php +++ /dev/null @@ -1,92 +0,0 @@ -assertSame( - [ - CommandProvider::class => ReplPlugin::class, - ], - $plugin->getCapabilities(), - ); - } - - public function testGetCommands(): void - { - /** @var Composer & MockInterface $composer */ - $composer = Mockery::mock(Composer::class); - - /** @var IOInterface & MockInterface $io */ - $io = Mockery::spy(IOInterface::class); - - $plugin = new ReplPlugin(); - $plugin->activate($composer, $io); - - $commands = $plugin->getCommands(); - - $this->assertIsArray($commands); - $this->assertCount(1, $commands); - $this->assertInstanceOf(ReplCommand::class, $commands[0]); - } - - public function testActivate(): void - { - /** @var Composer & MockInterface $composer */ - $composer = Mockery::mock(Composer::class); - - /** @var IOInterface & MockInterface $io */ - $io = Mockery::spy(IOInterface::class); - - $plugin = new ReplPlugin(); - $plugin->activate($composer, $io); - - $composer->shouldNotHaveBeenCalled(); - $io->shouldNotHaveBeenCalled(); - } - - public function testDeactivate(): void - { - /** @var Composer & MockInterface $composer */ - $composer = Mockery::mock(Composer::class); - - /** @var IOInterface & MockInterface $io */ - $io = Mockery::spy(IOInterface::class); - - $plugin = new ReplPlugin(); - $plugin->deactivate($composer, $io); - - $composer->shouldNotHaveBeenCalled(); - $io->shouldNotHaveBeenCalled(); - } - - public function testUninstall(): void - { - /** @var Composer & MockInterface $composer */ - $composer = Mockery::mock(Composer::class); - - /** @var IOInterface & MockInterface $io */ - $io = Mockery::spy(IOInterface::class); - - $plugin = new ReplPlugin(); - $plugin->uninstall($composer, $io); - - $composer->shouldNotHaveBeenCalled(); - $io->shouldNotHaveBeenCalled(); - } -} diff --git a/tests/Process/ProcessFactoryTest.php b/tests/Process/ProcessFactoryTest.php deleted file mode 100644 index 47abcaa..0000000 --- a/tests/Process/ProcessFactoryTest.php +++ /dev/null @@ -1,19 +0,0 @@ -assertInstanceOf(Process::class, $factory->factory(['ls'])); - } -} diff --git a/tests/Process/ProcessMock.php b/tests/Process/ProcessMock.php deleted file mode 100644 index e599483..0000000 --- a/tests/Process/ProcessMock.php +++ /dev/null @@ -1,20 +0,0 @@ -mockery(Process::class); - $process->shouldAllowMockingProtectedMethods(); - $process->shouldReceive('useCorrectCommand')->passthru(); - $process->expects()->getProcessClassName()->andReturn(ProcessMock::class); - - // @phpstan-ignore-next-line - $commandLine = $process->useCorrectCommand(['foo', '--bar', '--baz']); - - if (strcasecmp(substr(PHP_OS, 0, 3), 'WIN') === 0) { - $this->assertSame('foo "--bar" "--baz"', $commandLine); - } else { - $this->assertSame("foo '--bar' '--baz'", $commandLine); - } - } -} diff --git a/tests/Psy/ElephpantCommandTest.php b/tests/Psy/ElephpantCommandTest.php deleted file mode 100644 index e52150d..0000000 --- a/tests/Psy/ElephpantCommandTest.php +++ /dev/null @@ -1,30 +0,0 @@ -mockery(OutputInterface::class); - $output->shouldReceive('writeln')->once(); - - $command = new ElephpantCommand(); - - $this->assertSame('🐘', $command->getName()); - $this->assertSame(['elephpant'], $command->getAliases()); - - $command->run($input, $output); - } -} diff --git a/tests/Psy/PhpunitRunCommandTest.php b/tests/Psy/PhpunitRunCommandTest.php deleted file mode 100644 index 1b53dd8..0000000 --- a/tests/Psy/PhpunitRunCommandTest.php +++ /dev/null @@ -1,218 +0,0 @@ -mockery(Config::class); - $config->allows()->get('bin-dir')->andReturn('/path/to/vendor/bin'); - - $this->composer = $this->mockery(Composer::class, [ - 'getConfig' => $config, - ]); - } - - public function testGetApplication(): void - { - /** @var ProcessFactory & MockInterface $processFactory */ - $processFactory = $this->mockery(ProcessFactory::class); - - /** @var Shell & MockInterface $application */ - $application = $this->mockery(Shell::class, [ - 'getHelperSet' => $this->mockery(HelperSet::class), - ]); - - $command = new PhpunitRunCommand('/path/to/repo', $processFactory, $this->composer); - $command->setApplication($application); - - $this->assertSame($application, $command->getApplication()); - } - - public function testGetApplicationThrowsException(): void - { - /** @var ProcessFactory & MockInterface $processFactory */ - $processFactory = $this->mockery(ProcessFactory::class); - - $command = new PhpunitRunCommand('/path/to/repo', $processFactory, $this->composer); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Application is not set'); - - $command->getApplication(); - } - - public function testGetContext(): void - { - /** @var ProcessFactory & MockInterface $processFactory */ - $processFactory = $this->mockery(ProcessFactory::class); - - /** @var Context & MockInterface $context */ - $context = $this->mockery(Context::class); - - $command = new PhpunitRunCommand('/path/to/repo', $processFactory, $this->composer); - $command->setContext($context); - - $this->assertSame($context, $command->getContext()); - } - - public function testGetContextThrowsException(): void - { - /** @var ProcessFactory & MockInterface $processFactory */ - $processFactory = $this->mockery(ProcessFactory::class); - - $command = new PhpunitRunCommand('/path/to/repo', $processFactory, $this->composer); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Context is not set'); - - $command->getContext(); - } - - /** - * @param string[] $expectedParams - * - * @dataProvider provideCommandInput - */ - public function testCommand( - string $commandLine, - array $expectedParams, - int $expectedExitCode - ): void { - $input = new StringInput($commandLine); - - /** @var OutputInterface & MockInterface $output */ - $output = $this->mockery(OutputInterface::class); - $output->expects()->writeln('')->twice(); - $output->expects()->write('this is some output'); - - $process = $this->mockery(Process::class); - $process->expects()->start(); - $process->shouldReceive('wait')->andReturnUsing( - function (callable $callback) use ($expectedExitCode): int { - $callback('type', 'this is some output'); - - return $expectedExitCode; - }, - ); - - /** @var ProcessFactory & MockInterface $processFactory */ - $processFactory = $this->mockery(ProcessFactory::class); - $processFactory - ->expects() - ->factory($expectedParams, '/path/to/repo') - ->andReturn($process); - - $command = new PhpunitRunCommand('/path/to/repo', $processFactory, $this->composer); - - $this->assertSame($expectedExitCode, $command->run($input, $output)); - } - - /** - * @return array> - */ - public function provideCommandInput(): array - { - $phpunit = '/path/to/vendor/bin' . DIRECTORY_SEPARATOR . 'phpunit'; - - return [ - [ - 'commandLine' => '', - 'expectedParams' => [$phpunit, '--colors=always'], - 'expectedExitCode' => 0, - ], - [ - 'commandLine' => 'foo bar baz', - 'expectedParams' => [$phpunit, '--colors=always', '--filter', '/foo|bar|baz/i'], - 'expectedExitCode' => 1, - ], - [ - 'commandLine' => 'foo bar baz --filter "/qux|quux/"', - 'expectedParams' => [$phpunit, '--colors=always', '--filter', '/qux|quux/'], - 'expectedExitCode' => 0, - ], - [ - 'commandLine' => '--group foo --group bar', - 'expectedParams' => [$phpunit, '--colors=always', '--group', 'foo,bar'], - 'expectedExitCode' => 0, - ], - [ - 'commandLine' => '--exclude-group foo --exclude-group bar', - 'expectedParams' => [$phpunit, '--colors=always', '--exclude-group', 'foo,bar'], - 'expectedExitCode' => 0, - ], - [ - 'commandLine' => '--testsuite unittests', - 'expectedParams' => [$phpunit, '--colors=always', '--testsuite', 'unittests'], - 'expectedExitCode' => 0, - ], - [ - 'commandLine' => '--list-groups', - 'expectedParams' => [$phpunit, '--colors=always', '--list-groups'], - 'expectedExitCode' => 0, - ], - [ - 'commandLine' => '--list-suites', - 'expectedParams' => [$phpunit, '--colors=always', '--list-suites'], - 'expectedExitCode' => 0, - ], - [ - 'commandLine' => '--list-tests', - 'expectedParams' => [$phpunit, '--colors=always', '--list-tests'], - 'expectedExitCode' => 0, - ], - [ - 'commandLine' => '--testdox', - 'expectedParams' => [$phpunit, '--colors=always', '--testdox'], - 'expectedExitCode' => 127, - ], - [ - 'commandLine' => '--group foo --exclude-group bar --testsuite ' - . 'unit --filter "/mytest/" --list-groups --list-suites ' - . '--list-tests --testdox all the things', - 'expectedParams' => [ - $phpunit, - '--colors=always', - '--group', - 'foo', - '--exclude-group', - 'bar', - '--testsuite', - 'unit', - '--filter', - '/mytest/', - '--list-groups', - '--list-suites', - '--list-tests', - '--testdox', - ], - 'expectedExitCode' => 0, - ], - ]; - } -} diff --git a/tests/Psy/PhpunitTestCommandTest.php b/tests/Psy/PhpunitTestCommandTest.php deleted file mode 100644 index 7cd858a..0000000 --- a/tests/Psy/PhpunitTestCommandTest.php +++ /dev/null @@ -1,181 +0,0 @@ -mockery(Context::class); - $context->allows()->get('phpunit')->andReturn(new class extends PHPUnitTestCase { - }); - - $this->context = $context; - } - - public function testGetApplication(): void - { - /** @var Shell & MockInterface $application */ - $application = $this->mockery(Shell::class, [ - 'getHelperSet' => $this->mockery(HelperSet::class), - ]); - - $command = new PhpunitTestCommand(); - $command->setApplication($application); - - $this->assertSame($application, $command->getApplication()); - } - - public function testGetApplicationThrowsException(): void - { - $command = new PhpunitTestCommand(); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Application is not set'); - - $command->getApplication(); - } - - public function testGetContext(): void - { - /** @var Context & MockInterface $context */ - $context = $this->mockery(Context::class); - - $command = new PhpunitTestCommand(); - $command->setContext($context); - - $this->assertSame($context, $command->getContext()); - } - - public function testGetContextThrowsException(): void - { - $command = new PhpunitTestCommand(); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Context is not set'); - - $command->getContext(); - } - - /** - * @dataProvider provideInvalidAssertions - */ - public function testCommandThrowsExceptionForInvalidAssertions( - string $invalidAssertion, - string $exceptionMessage - ): void { - $input = new ShellInput($invalidAssertion); - - /** @var OutputInterface & MockInterface $output */ - $output = $this->mockery(OutputInterface::class); - - $command = new PhpunitTestCommand(); - $command->setContext($this->context); - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage($exceptionMessage); - - $command->run($input, $output); - } - - /** - * @return array> - */ - public function provideInvalidAssertions(): array - { - return [ - [ - 'invalidAssertion' => 'foo()', - 'exceptionMessage' => 'The assertion provided is not valid', - ], - [ - 'invalidAssertion' => 'foo($bar)', - 'exceptionMessage' => 'The assertion provided is not valid', - ], - [ - 'invalidAssertion' => 'assertFoo($bar)', - 'exceptionMessage' => 'assertFoo is not a PHPUnit assertion method', - ], - [ - 'invalidAssertion' => 'assertFoo()', - 'exceptionMessage' => 'The assertion provided is not valid', - ], - [ - 'invalidAssertion' => 'assert($foo)', - 'exceptionMessage' => 'The assertion provided is not valid', - ], - ]; - } - - public function testCommandRunsWithPassingTest(): void - { - $input = new ShellInput('assertSame(2, $bar)'); - - /** @var Shell & MockInterface $application */ - $application = $this->mockery(Shell::class, [ - 'getHelperSet' => $this->mockery(HelperSet::class), - 'getDefinition' => $this->mockery(InputDefinition::class, [ - 'getArguments' => [], - 'getOptions' => [], - ]), - ]); - $application->expects()->execute('$phpunit->assertSame(2, $bar)', true); - - /** @var OutputInterface & MockInterface $output */ - $output = $this->mockery(OutputInterface::class); - $output->expects()->writeln('Test passed!'); - - $command = new PhpunitTestCommand(); - $command->setApplication($application); - $command->setContext($this->context); - - $this->assertSame(0, $command->run($input, $output)); - } - - public function testCommandRunsWithFailingTest(): void - { - $input = new ShellInput('assertIsArray($bar);'); - - /** @var Shell & MockInterface $application */ - $application = $this->mockery(Shell::class, [ - 'getHelperSet' => $this->mockery(HelperSet::class), - 'getDefinition' => $this->mockery(InputDefinition::class, [ - 'getArguments' => [], - 'getOptions' => [], - ]), - ]); - $application - ->expects() - ->execute('$phpunit->assertIsArray($bar);', true) - ->andThrow(new Exception('something bad happened')); - - /** @var OutputInterface & MockInterface $output */ - $output = $this->mockery(OutputInterface::class); - $output->expects()->writeln('Test failed: something bad happened'); - - $command = new PhpunitTestCommand(); - $command->setApplication($application); - $command->setContext($this->context); - - $this->assertSame(0, $command->run($input, $output)); - } -}