diff --git a/.actrc b/.actrc
deleted file mode 100644
index 99e6b7ecc..000000000
--- a/.actrc
+++ /dev/null
@@ -1,3 +0,0 @@
-# Configuration file for nektos/act.
-# See https://github.com/nektos/act#configuration
--P ubuntu-latest=shivammathur/node:latest
diff --git a/.distignore b/.distignore
deleted file mode 100644
index 2f9c78d4b..000000000
--- a/.distignore
+++ /dev/null
@@ -1,18 +0,0 @@
-.DS_Store
-.git
-.gitignore
-.gitlab-ci.yml
-.editorconfig
-.travis.yml
-behat.yml
-circle.yml
-phpcs.xml.dist
-phpunit.xml.dist
-bin/
-features/
-utils/
-*.zip
-*.tar.gz
-*.swp
-*.txt
-*.log
diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 84f918ed5..000000000
--- a/.editorconfig
+++ /dev/null
@@ -1,26 +0,0 @@
-# This file is for unifying the coding style for different editors and IDEs
-# editorconfig.org
-
-# WordPress Coding Standards
-# https://make.wordpress.org/core/handbook/coding-standards/
-
-# From https://github.com/WordPress/wordpress-develop/blob/trunk/.editorconfig with a couple of additions.
-
-root = true
-
-[*]
-charset = utf-8
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-indent_style = tab
-
-[{*.yml,*.feature,.jshintrc,*.json}]
-indent_style = space
-indent_size = 2
-
-[*.md]
-trim_trailing_whitespace = false
-
-[{*.txt,wp-config-sample.php}]
-end_of_line = crlf
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 97cb2461f..000000000
--- a/.gitattributes
+++ /dev/null
@@ -1,3 +0,0 @@
-# Auto detect text files and perform EOL normalization
-* text=auto eol=lf
-tests/data/*-win.php eol=crlf
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
deleted file mode 100644
index f69375fb2..000000000
--- a/.github/CODEOWNERS
+++ /dev/null
@@ -1 +0,0 @@
-* @wp-cli/committers
diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE
deleted file mode 100644
index 6c06a141b..000000000
--- a/.github/ISSUE_TEMPLATE
+++ /dev/null
@@ -1,11 +0,0 @@
-
diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE
deleted file mode 100644
index 66079d9da..000000000
--- a/.github/PULL_REQUEST_TEMPLATE
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
deleted file mode 100644
index 46928e312..000000000
--- a/.github/SUPPORT.md
+++ /dev/null
@@ -1,6 +0,0 @@
-Support
-=======
-
-Hi there,
-
-GitHub issues are meant for enhancement requests and specific, reproducible bugs, not for general support questions. For support options, please review https://wp-cli.org/#support
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index d6c7b8b04..000000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-version: 2
-updates:
- - package-ecosystem: composer
- directory: "/"
- schedule:
- interval: daily
- open-pull-requests-limit: 10
- labels:
- - scope:distribution
- - package-ecosystem: github-actions
- directory: "/"
- schedule:
- interval: daily
- open-pull-requests-limit: 10
- labels:
- - scope:distribution
-
diff --git a/.github/workflows/check-branch-alias.yml b/.github/workflows/check-branch-alias.yml
deleted file mode 100644
index 78da63710..000000000
--- a/.github/workflows/check-branch-alias.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: Check Branch Alias
-
-on:
- release:
- types: [released]
- workflow_dispatch:
-
-permissions:
- contents: write
- pull-requests: write
-
-jobs:
- check-branch-alias:
- uses: wp-cli/.github/.github/workflows/reusable-check-branch-alias.yml@main
diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml
deleted file mode 100644
index 07e4fd1f7..000000000
--- a/.github/workflows/code-quality.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Code Quality Checks
-
-on:
- pull_request:
- push:
- branches:
- - main
- - master
-
-jobs:
- code-quality:
- uses: wp-cli/.github/.github/workflows/reusable-code-quality.yml@main
diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml
deleted file mode 100644
index 5158ca683..000000000
--- a/.github/workflows/copilot-setup-steps.yml
+++ /dev/null
@@ -1,46 +0,0 @@
-name: "Copilot Setup Steps"
-
-on:
- workflow_dispatch:
- push:
- paths:
- - .github/workflows/copilot-setup-steps.yml
- pull_request:
- paths:
- - .github/workflows/copilot-setup-steps.yml
-
-jobs:
- copilot-setup-steps:
- runs-on: ubuntu-latest
- permissions:
- contents: read
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
-
- - name: Check existence of composer.json file
- id: check_composer_file
- uses: andstor/file-existence-action@v3
- with:
- files: "composer.json"
-
- - name: Set up PHP environment
- if: steps.check_composer_file.outputs.files_exists == 'true'
- uses: shivammathur/setup-php@v2
- with:
- php-version: 'latest'
- ini-values: zend.assertions=1, error_reporting=-1, display_errors=On
- coverage: 'none'
- tools: composer,cs2pr
- env:
- COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Install Composer dependencies & cache dependencies
- if: steps.check_composer_file.outputs.files_exists == 'true'
- uses: ramsey/composer-install@v3
- env:
- COMPOSER_ROOT_VERSION: dev-${{ github.event.repository.default_branch }}
- with:
- # Bust the cache at least once a month - output format: YYYY-MM.
- custom-cache-suffix: $(date -u "+%Y-%m")
diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml
deleted file mode 100644
index 14dffc540..000000000
--- a/.github/workflows/issue-triage.yml
+++ /dev/null
@@ -1,26 +0,0 @@
----
-name: Issue and PR Triage
-
-'on':
- issues:
- types: [opened]
- pull_request_target:
- types: [opened]
- workflow_dispatch:
- inputs:
- issue_number:
- description: 'Issue/PR number to triage (leave empty to process all)'
- required: false
- type: string
-
-jobs:
- issue-triage:
- uses: wp-cli/.github/.github/workflows/reusable-issue-triage.yml@main
- with:
- issue_number: >-
- ${{
- (github.event_name == 'workflow_dispatch' && inputs.issue_number) ||
- (github.event_name == 'pull_request_target' && github.event.pull_request.number) ||
- (github.event_name == 'issues' && github.event.issue.number) ||
- ''
- }}
diff --git a/.github/workflows/manage-labels.yml b/.github/workflows/manage-labels.yml
deleted file mode 100644
index 45711bded..000000000
--- a/.github/workflows/manage-labels.yml
+++ /dev/null
@@ -1,19 +0,0 @@
----
-name: Manage Labels
-
-'on':
- workflow_dispatch:
- push:
- branches:
- - main
- - master
- paths:
- - 'composer.json'
-
-permissions:
- issues: write
- contents: read
-
-jobs:
- manage-labels:
- uses: wp-cli/.github/.github/workflows/reusable-manage-labels.yml@main
diff --git a/.github/workflows/regenerate-readme.yml b/.github/workflows/regenerate-readme.yml
deleted file mode 100644
index c633d9d46..000000000
--- a/.github/workflows/regenerate-readme.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: Regenerate README file
-
-on:
- workflow_dispatch:
- push:
- branches:
- - main
- - master
- paths-ignore:
- - "features/**"
- - "README.md"
-
-jobs:
- regenerate-readme:
- uses: wp-cli/.github/.github/workflows/reusable-regenerate-readme.yml@main
diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
deleted file mode 100644
index bf67592d8..000000000
--- a/.github/workflows/testing.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: Testing
-
-on:
- workflow_dispatch:
- pull_request:
- push:
- branches:
- - main
- - master
- schedule:
- - cron: '17 1 * * *' # Run every day on a seemly random time.
-
-jobs:
- test:
- uses: wp-cli/.github/.github/workflows/reusable-testing.yml@main
diff --git a/.github/workflows/welcome-new-contributors.yml b/.github/workflows/welcome-new-contributors.yml
deleted file mode 100644
index c38e033b2..000000000
--- a/.github/workflows/welcome-new-contributors.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-name: Welcome New Contributors
-
-on:
- pull_request_target:
- types: [opened]
- branches:
- - main
- - master
-
-jobs:
- welcome:
- uses: wp-cli/.github/.github/workflows/reusable-welcome-new-contributors.yml@main
diff --git a/.github/workflows/wp-versions-data-fetcher.yml b/.github/workflows/wp-versions-data-fetcher.yml
deleted file mode 100644
index f5732f797..000000000
--- a/.github/workflows/wp-versions-data-fetcher.yml
+++ /dev/null
@@ -1,126 +0,0 @@
----
-name: WP Versions Data Fetcher
-
-on:
- schedule:
- - cron: '0 5 * * *'
- workflow_dispatch:
- inputs:
- max_retries:
- description: 'Max retries to fetch data from WPOrg API'
- default: 3
- required: false
- type: number
-
-concurrency:
- group: ${{ github.repository }}-${{ github.workflow }}
- cancel-in-progress: true
-
-permissions: {}
-
-env:
- ARTIFACTS_BRANCH: 'artifacts'
- FEATURE_BRANCH: 'sync-wp-versions'
- FILE_PATH: 'wp-versions.json'
- MAX_RETRIES: ${{ inputs.max_retries || 3 }}
- WP_ORG_API_URL: 'https://api.wordpress.org/core/stable-check/1.0/'
-
-jobs:
- fetch-versions:
- runs-on: ubuntu-latest
- timeout-minutes: 10
- permissions:
- contents: write
- pull-requests: write
- steps:
- - name: Checkout repository
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- with:
- ref: ${{ env.ARTIFACTS_BRANCH }}
-
- - name: Check if feature branch exists
- id: remote-branch
- run: |
- echo "exists=$([[ -z $(git ls-remote --heads origin ${{ env.FEATURE_BRANCH }}) ]] && echo "0" || echo "1")" >> $GITHUB_OUTPUT
-
- - name: Create feature branch
- if: steps.remote-branch.outputs.exists == '0'
- run: |
- git checkout -b ${{ env.FEATURE_BRANCH }}
-
- - name: Checkout feature branch
- if: steps.remote-branch.outputs.exists == '1'
- run: |
- git fetch --all --prune
- git checkout ${{ env.FEATURE_BRANCH }}
- git pull --no-rebase
-
- - name: Fetch WP Versions
- run: |
- BACKOFF=10
- for i in $(seq 1 $MAX_RETRIES); do
- echo "Fetching WP versions, attempt $i"
- RES_CODE=$(curl -sL -w "%{http_code}" $WP_ORG_API_URL -o /tmp/wp-versions.json)
- if [ $RES_CODE -eq 200 ]; then
- mv /tmp/wp-versions.json $FILE_PATH
- echo "::notice::WP versions data has been fetched successfully."
- break
- fi
- echo "Failed to fetch WP versions, attempt $i"
- if [ $i -eq $MAX_RETRIES ]; then
- echo "::error::Failed to fetch WP versions from $WP_ORG_API_URL after $MAX_RETRIES attempts"
- exit 1
- fi
- echo "Retrying in $BACKOFF seconds"
- sleep $BACKOFF
- BACKOFF=$((BACKOFF * 2))
- done
-
- - name: Track modified files
- id: version-file
- run: |
- IS_MODIFIED=$(git ls-files --modified wp-versions.json)
- if [ -n "$IS_MODIFIED" ]; then
- DIFF=$(git diff ${{ env.ARTIFACTS_BRANCH }} -- $FILE_PATH)
- echo "# Modified WP Versions Data" >> $GITHUB_STEP_SUMMARY
- echo "WP Versions Data Diff
" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo "\`\`\`diff" >> $GITHUB_STEP_SUMMARY
- echo "$DIFF" >> $GITHUB_STEP_SUMMARY
- echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
- echo " " >> $GITHUB_STEP_SUMMARY
- echo "modified=true" >> $GITHUB_OUTPUT
- else
- echo "::notice::WP versions data has not been modified."
- exit 0
- fi
-
- - name: Set git user
- if: steps.version-file.outputs.modified == 'true'
- run: |
- # See: https://api.github.com/users/github-actions[bot]
- git config --global user.name "github-actions[bot]"
- git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
-
- - name: Commit WP Versions Data
- if: steps.version-file.outputs.modified == 'true'
- run: |
- git add $FILE_PATH
- git commit -m "Update $FILE_PATH on $(date -u)" --signoff
-
- - name: Push changes
- if: steps.version-file.outputs.modified == 'true'
- run: |
- git push origin ${{ env.FEATURE_BRANCH }}
-
- - name: Create Pull Request
- run: |
- body="## Summary
- Update WP versions data file with the \`${WP_ORG_API_URL}\` endpoint response."
- delimiter="${body//$'\n'/'%0A'}"
- echo "body<<${delimiter}" >> $GITHUB_OUTPUT
- echo "$body" >> $GITHUB_OUTPUT
- echo "${delimiter}" >> $GITHUB_OUTPUT
- gh pr create --base ${{ env.ARTIFACTS_BRANCH }} --head ${{ env.FEATURE_BRANCH }} --title "Update WP Versions Data" --body "$body" --label "scope:artifacts" || true
- env:
- GH_TOKEN: ${{ github.token }}
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 33c01b909..000000000
--- a/.gitignore
+++ /dev/null
@@ -1,16 +0,0 @@
-.DS_Store
-wp-cli.local.yml
-node_modules/
-vendor/
-*.zip
-*.tar.gz
-*.swp
-*.txt
-*.log
-composer.lock
-phpunit.xml
-phpcs.xml
-.phpcs.xml
-.phpunit.result.cache
-.phpunit.cache
-build/logs
diff --git a/.readme-partials/USING.md b/.readme-partials/USING.md
deleted file mode 100644
index c739aa715..000000000
--- a/.readme-partials/USING.md
+++ /dev/null
@@ -1,209 +0,0 @@
-To make use of the WP-CLI testing framework, you need to complete the following steps from within the package you want to add them to:
-
-1. Add the testing framework as a development requirement:
- ```bash
- composer require --dev wp-cli/wp-cli-tests
- ```
-
-2. Add the required test scripts to the `composer.json` file:
- ```json
- "scripts": {
- "behat": "run-behat-tests",
- "behat-rerun": "rerun-behat-tests",
- "lint": "run-linter-tests",
- "phpcs": "run-phpcs-tests",
- "phpcbf": "run-phpcbf-cleanup",
- "phpunit": "run-php-unit-tests",
- "prepare-tests": "install-package-tests",
- "test": [
- "@lint",
- "@phpcs",
- "@phpunit",
- "@behat"
- ]
- }
- ```
- You can of course remove the ones you don't need.
-
-3. Optionally add a modified process timeout to the `composer.json` file to make sure scripts can run until their work is completed:
- ```json
- "config": {
- "process-timeout": 1800
- },
- ```
- The timeout is expressed in seconds.
-
-4. Optionally add a `behat.yml` file to the package root with the following content:
- ```yaml
- default:
- suites:
- default:
- contexts:
- - WP_CLI\Tests\Context\FeatureContext
- paths:
- - features
- ```
- This will make sure that the automated Behat system works across all platforms. This is needed on Windows.
-
-5. Optionally add a `phpcs.xml.dist` file to the package root to enable code style and best practice checks using PHP_CodeSniffer.
-
- Example of a minimal custom ruleset based on the defaults set in the WP-CLI testing framework:
- ```xml
-
-
- Custom ruleset for WP-CLI PROJECT NAME
-
-
- .
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ```
-
- All other [PHPCS configuration options](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-Ruleset) are, of course, available.
-6. Update your composer dependencies and regenerate your autoloader and binary folders:
- ```bash
- composer update
- ```
-
-You are now ready to use the testing framework from within your package.
-
-### Launching the tests
-
-You can use the following commands to control the tests:
-
-* `composer prepare-tests` - Set up the database that is needed for running the functional tests. This is only needed once.
-* `composer test` - Run all test suites.
-* `composer lint` - Run only the linting test suite.
-* `composer phpcs` - Run only the code sniffer test suite.
-* `composer phpcbf` - Run only the code sniffer cleanup.
-* `composer phpunit` - Run only the unit test suite.
-* `composer behat` - Run only the functional test suite.
-
-### Controlling what to test
-
-To send one or more arguments to one of the test tools, prepend the argument(s) with a double dash. As an example, here's how to run the functional tests for a specific feature file only:
-```bash
-composer behat -- features/cli-info.feature
-```
-
-Prepending with the double dash is needed because the arguments would otherwise be sent to Composer itself, not the tool that Composer executes.
-
-### Controlling the test environment
-
-#### WordPress Version
-
-You can run the tests against a specific version of WordPress by setting the `WP_VERSION` environment variable.
-
-This variable understands any numeric version, as well as the special terms `latest` and `trunk`.
-
-Note: This only applies to the Behat functional tests. All other tests never load WordPress.
-
-Here's how to run your tests against the latest trunk version of WordPress:
-```bash
-WP_VERSION=trunk composer behat
-```
-
-#### WP-CLI Binary
-
-You can run the tests against a specific WP-CLI binary, instead of using the one that has been built in your project's `vendor/bin` folder.
-
-This can be useful to run your tests against a specific Phar version of WP_CLI.
-
-To do this, you can set the `WP_CLI_BIN_DIR` environment variable to point to a folder that contains an executable `wp` binary. Note: the binary has to be named `wp` to be properly recognized.
-
-As an example, here's how to run your tests against a specific Phar version you've downloaded.
-```bash
-# Prepare the binary you've downloaded into the ~/wp-cli folder first.
-mv ~/wp-cli/wp-cli-1.2.0.phar ~/wp-cli/wp
-chmod +x ~/wp-cli/wp
-
-WP_CLI_BIN_DIR=~/wp-cli composer behat
-```
-
-### Setting up the tests in Travis CI
-
-Basic rules for setting up the test framework with Travis CI:
-
-* `composer prepare-tests` needs to be called once per environment.
-* `linting and sniffing` is a static analysis, so it shouldn't depend on any specific environment. You should do this only once, as a separate stage, instead of per environment.
-* `composer behat || composer behat-rerun` causes the Behat tests to run in their entirety first, and in case their were failed scenarios, a second run is done with only the failed scenarios. This usually gets around intermittent issues like timeouts or similar.
-
-Here's a basic setup of how you can configure Travis CI to work with the test framework (extract):
-```yml
-install:
- - composer install
- - composer prepare-tests
-
-script:
- - composer phpunit
- - composer behat || composer behat-rerun
-
-jobs:
- include:
- - stage: sniff
- script:
- - composer lint
- - composer phpcs
- env: BUILD=sniff
- - stage: test
- php: 7.2
- env: WP_VERSION=latest
- - stage: test
- php: 7.2
- env: WP_VERSION=3.7.11
- - stage: test
- php: 7.2
- env: WP_VERSION=trunk
-```
-
-#### WP-CLI version
-
-You can point the tests to a specific version of WP-CLI through the `WP_CLI_BIN_DIR` constant:
-```bash
-WP_CLI_BIN_DIR=~/my-custom-wp-cli/bin composer behat
-```
-
-#### WordPress version
-
-If you want to run the feature tests against a specific WordPress version, you can use the `WP_VERSION` constant:
-```bash
-WP_VERSION=4.2 composer behat
-```
-
-The `WP_VERSION` constant also understands the `latest` and `trunk` as valid version targets.
-
-#### The database credentials
-
-By default, the tests are run in a database named `wp_cli_test` with the user also named `wp_cli_test` with password `password1`.
-This should be set up via the `composer prepare-tests` command.
-
-The following environment variables can be set to override the default database credentials.
-
- - `WP_CLI_TEST_DBHOST` is the host to use and can include a port, i.e "127.0.0.1:33060" (defaults to "localhost")
- - `WP_CLI_TEST_DBROOTUSER` is the user that has permission to administer databases and users (defaults to "root").
- - `WP_CLI_TEST_DBROOTPASS` is the password to use for the above user (defaults to an empty password).
- - `WP_CLI_TEST_DBNAME` is the database that the tests run under (defaults to "wp_cli_test").
- - `WP_CLI_TEST_DBUSER` is the user that the tests run under (defaults to "wp_cli_test").
- - `WP_CLI_TEST_DBPASS` is the password to use for the above user (defaults to "password1").
- - `WP_CLI_TEST_DBTYPE` is the database engine type to use, i.e. "sqlite" for running tests on SQLite instead of MySQL (defaults to "mysql").
-
-Environment variables can be set for the whole session via the following syntax: `export WP_CLI_TEST_DBNAME=custom_db`.
-
-They can also be set for a single execution by prepending them before the Behat command: `WP_CLI_TEST_DBNAME=custom_db composer behat`.
-
diff --git a/AGENTS.md b/AGENTS.md
deleted file mode 100644
index 1ff84f6d1..000000000
--- a/AGENTS.md
+++ /dev/null
@@ -1,121 +0,0 @@
-# Instructions
-
-This package is part of WP-CLI, the official command line interface for WordPress. For a detailed explanation of the project structure and development workflow, please refer to the main @README.md file.
-
-## Best Practices for Code Contributions
-
-When contributing to this package, please adhere to the following guidelines:
-
-* **Follow Existing Conventions:** Before writing any code, analyze the existing codebase in this package to understand the coding style, naming conventions, and architectural patterns.
-* **Focus on the Package's Scope:** All changes should be relevant to the functionality of the package.
-* **Write Tests:** All new features and bug fixes must be accompanied by acceptance tests using Behat. You can find the existing tests in the `features/` directory. There may be PHPUnit unit tests as well in the `tests/` directory.
-* **Update Documentation:** If your changes affect the user-facing functionality, please update the relevant inline code documentation.
-
-### Building and running
-
-Before submitting any changes, it is crucial to validate them by running the full suite of static code analysis and tests. To run the full suite of checks, execute the following command: `composer test`.
-
-This single command ensures that your changes meet all the quality gates of the project. While you can run the individual steps separately, it is highly recommended to use this single command to ensure a comprehensive validation.
-
-### Useful Composer Commands
-
-The project uses Composer to manage dependencies and run scripts. The following commands are available:
-
-* `composer install`: Install dependencies.
-* `composer test`: Run the full test suite, including linting, code style checks, static analysis, and unit/behavior tests.
-* `composer lint`: Check for syntax errors.
-* `composer phpcs`: Check for code style violations.
-* `composer phpcbf`: Automatically fix code style violations.
-* `composer phpstan`: Run static analysis.
-* `composer phpunit`: Run unit tests.
-* `composer behat`: Run behavior-driven tests.
-
-### Coding Style
-
-The project follows the `WP_CLI_CS` coding standard, which is enforced by PHP_CodeSniffer. The configuration can be found in `phpcs.xml.dist`. Before submitting any code, please run `composer phpcs` to check for violations and `composer phpcbf` to automatically fix them.
-
-## Documentation
-
-The `README.md` file might be generated dynamically from the project's codebase using `wp scaffold package-readme` ([doc](https://github.com/wp-cli/scaffold-package-command#wp-scaffold-package-readme)). In that case, changes need to be made against the corresponding part of the codebase.
-
-### Inline Documentation
-
-Only write high-value comments if at all. Avoid talking to the user through comments.
-
-## Testing
-
-The project has a comprehensive test suite that includes unit tests, behavior-driven tests, and static analysis.
-
-* **Unit tests** are written with PHPUnit and can be found in the `tests/` directory. The configuration is in `phpunit.xml.dist`.
-* **Behavior-driven tests** are written with Behat and can be found in the `features/` directory. The configuration is in `behat.yml`.
-* **Static analysis** is performed with PHPStan.
-
-All tests are run on GitHub Actions for every pull request.
-
-When writing tests, aim to follow existing patterns. Key conventions include:
-
-* When adding tests, first examine existing tests to understand and conform to established conventions.
-* For unit tests, extend the base `WP_CLI\Tests\TestCase` test class.
-* For Behat tests, only WP-CLI commands installed in `composer.json` can be run.
-
-### Behat Steps
-
-WP-CLI makes use of a Behat-based testing framework and provides a set of custom step definitions to write feature tests.
-
-> **Note:** If you are expecting an error output in a test, you need to use `When I try ...` instead of `When I run ...` .
-
-#### Given
-
-* `Given an empty directory` - Creates an empty directory.
-* `Given /^an? (empty|non-existent) ([^\s]+) directory$/` - Creates or deletes a specific directory.
-* `Given an empty cache` - Clears the WP-CLI cache directory.
-* `Given /^an? ([^\s]+) (file|cache file):$/` - Creates a file with the given contents.
-* `Given /^"([^"]+)" replaced with "([^"]+)" in the ([^\s]+) file$/` - Search and replace a string in a file using regex.
-* `Given /^that HTTP requests to (.*?) will respond with:$/` - Mock HTTP requests to a given URL.
-* `Given WP files` - Download WordPress files without installing.
-* `Given wp-config.php` - Create a wp-config.php file using `wp config create`.
-* `Given a database` - Creates an empty database.
-* `Given a WP install(ation)` - Installs WordPress.
-* `Given a WP install(ation) in :subdir` - Installs WordPress in a given directory.
-* `Given a WP install(ation) with Composer` - Installs WordPress with Composer.
-* `Given a WP install(ation) with Composer and a custom vendor directory :vendor_directory` - Installs WordPress with Composer and a custom vendor directory.
-* `Given /^a WP multisite (subdirectory|subdomain)?\s?(install|installation)$/` - Installs WordPress Multisite.
-* `Given these installed and active plugins:` - Installs and activates one or more plugins.
-* `Given a custom wp-content directory` - Configure a custom `wp-content` directory.
-* `Given download:` - Download multiple files into the given destinations.
-* `Given /^save (STDOUT|STDERR) ([\'].+[^\'])?\s?as \{(\w+)\}$/` - Store STDOUT or STDERR contents in a variable.
-* `Given /^a new Phar with (?:the same version|version "([^"]+)")$/` - Build a new WP-CLI Phar file with a given version.
-* `Given /^a downloaded Phar with (?:the same version|version "([^"]+)")$/` - Download a specific WP-CLI Phar version from GitHub.
-* `Given /^save the (.+) file ([\'].+[^\'])? as \{(\w+)\}$/` - Stores the contents of the given file in a variable.
-* `Given a misconfigured WP_CONTENT_DIR constant directory` - Modify wp-config.php to set `WP_CONTENT_DIR` to an empty string.
-* `Given a dependency on current wp-cli` - Add `wp-cli/wp-cli` as a Composer dependency.
-* `Given a PHP built-in web server` - Start a PHP built-in web server in the current directory.
-* `Given a PHP built-in web server to serve :subdir` - Start a PHP built-in web server in the given subdirectory.
-
-#### When
-
-* ``When /^I launch in the background `([^`]+)`$/`` - Launch a given command in the background.
-* ``When /^I (run|try) `([^`]+)`$/`` - Run or try a given command.
-* ``When /^I (run|try) `([^`]+)` from '([^\s]+)'$/`` - Run or try a given command in a subdirectory.
-* `When /^I (run|try) the previous command again$/` - Run or try the previous command again.
-
-#### Then
-
-* `Then /^the return code should( not)? be (\d+)$/` - Expect a specific exit code of the previous command.
-* `Then /^(STDOUT|STDERR) should( strictly)? (be|contain|not contain):$/` - Check the contents of STDOUT or STDERR.
-* `Then /^(STDOUT|STDERR) should be a number$/` - Expect STDOUT or STDERR to be a numeric value.
-* `Then /^(STDOUT|STDERR) should not be a number$/` - Expect STDOUT or STDERR to not be a numeric value.
-* `Then /^STDOUT should be a table containing rows:$/` - Expect STDOUT to be a table containing the given rows.
-* `Then /^STDOUT should end with a table containing rows:$/` - Expect STDOUT to end with a table containing the given rows.
-* `Then /^STDOUT should be JSON containing:$/` - Expect valid JSON output in STDOUT.
-* `Then /^STDOUT should be a JSON array containing:$/` - Expect valid JSON array output in STDOUT.
-* `Then /^STDOUT should be CSV containing:$/` - Expect STDOUT to be CSV containing certain values.
-* `Then /^STDOUT should be YAML containing:$/` - Expect STDOUT to be YAML containing certain content.
-* `Then /^(STDOUT|STDERR) should be empty$/` - Expect STDOUT or STDERR to be empty.
-* `Then /^(STDOUT|STDERR) should not be empty$/` - Expect STDOUT or STDERR not to be empty.
-* `Then /^(STDOUT|STDERR) should be a version string (<|<=|>|>=|==|=|<>) ([+\w.{}-]+)$/` - Expect STDOUT or STDERR to be a version string comparing to the given version.
-* `Then /^the (.+) (file|directory) should( strictly)? (exist|not exist|be:|contain:|not contain):$/` - Expect a certain file or directory to (not) exist or (not) contain certain contents.
-* `Then /^the contents of the (.+) file should( not)? match (((\/.*\/)|(#.#))([a-z]+)?)$/` - Match file contents against a regex.
-* `Then /^(STDOUT|STDERR) should( not)? match (((\/.*\/)|(#.#))([a-z]+)?)$/` - Match STDOUT or STDERR against a regex.
-* `Then /^an email should (be sent|not be sent)$/` - Expect an email to be sent (or not).
-* `Then the HTTP status code should be :code` - Expect the HTTP status code for visiting `http://localhost:8080`.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index fa86aa52d..000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,8 +0,0 @@
-Contributing
-============
-
-We appreciate you taking the initiative to contribute to this project.
-
-Contributing isn’t limited to just code. We encourage you to contribute in the way that best fits your abilities, by writing tutorials, giving a demo at your local meetup, helping other users with their support questions, or revising our documentation.
-
-For a more thorough introduction, [check out WP-CLI's guide to contributing](https://make.wordpress.org/cli/handbook/contributing/). This package follows those policy and guidelines.
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 91eaef17d..000000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (C) 2011-2018 WP-CLI Development Group (https://github.com/wp-cli/wp-cli/contributors)
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/README.md b/README.md
deleted file mode 100644
index 3be233521..000000000
--- a/README.md
+++ /dev/null
@@ -1,247 +0,0 @@
-wp-cli/wp-cli-tests
-===================
-
-WP-CLI testing framework
-
-[](https://github.com/wp-cli/wp-cli-tests/actions/workflows/testing.yml)
-
-Quick links: [Using](#using) | [Contributing](#contributing) | [Support](#support)
-
-## Using
-
-To make use of the WP-CLI testing framework, you need to complete the following steps from within the package you want to add them to:
-
-1. Add the testing framework as a development requirement:
- ```bash
- composer require --dev wp-cli/wp-cli-tests
- ```
-
-2. Add the required test scripts to the `composer.json` file:
- ```json
- "scripts": {
- "behat": "run-behat-tests",
- "behat-rerun": "rerun-behat-tests",
- "lint": "run-linter-tests",
- "phpcs": "run-phpcs-tests",
- "phpcbf": "run-phpcbf-cleanup",
- "phpunit": "run-php-unit-tests",
- "prepare-tests": "install-package-tests",
- "test": [
- "@lint",
- "@phpcs",
- "@phpunit",
- "@behat"
- ]
- }
- ```
- You can of course remove the ones you don't need.
-
-3. Optionally add a modified process timeout to the `composer.json` file to make sure scripts can run until their work is completed:
- ```json
- "config": {
- "process-timeout": 1800
- },
- ```
- The timeout is expressed in seconds.
-
-4. Optionally add a `behat.yml` file to the package root with the following content:
- ```yaml
- default:
- suites:
- default:
- contexts:
- - WP_CLI\Tests\Context\FeatureContext
- paths:
- - features
- ```
- This will make sure that the automated Behat system works across all platforms. This is needed on Windows.
-
-5. Optionally add a `phpcs.xml.dist` file to the package root to enable code style and best practice checks using PHP_CodeSniffer.
-
- Example of a minimal custom ruleset based on the defaults set in the WP-CLI testing framework:
- ```xml
-
-
- Custom ruleset for WP-CLI PROJECT NAME
-
-
- .
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ```
-
- All other [PHPCS configuration options](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-Ruleset) are, of course, available.
-6. Update your composer dependencies and regenerate your autoloader and binary folders:
- ```bash
- composer update
- ```
-
-You are now ready to use the testing framework from within your package.
-
-### Launching the tests
-
-You can use the following commands to control the tests:
-
-* `composer prepare-tests` - Set up the database that is needed for running the functional tests. This is only needed once.
-* `composer test` - Run all test suites.
-* `composer lint` - Run only the linting test suite.
-* `composer phpcs` - Run only the code sniffer test suite.
-* `composer phpcbf` - Run only the code sniffer cleanup.
-* `composer phpunit` - Run only the unit test suite.
-* `composer behat` - Run only the functional test suite.
-
-### Controlling what to test
-
-To send one or more arguments to one of the test tools, prepend the argument(s) with a double dash. As an example, here's how to run the functional tests for a specific feature file only:
-```bash
-composer behat -- features/cli-info.feature
-```
-
-Prepending with the double dash is needed because the arguments would otherwise be sent to Composer itself, not the tool that Composer executes.
-
-### Controlling the test environment
-
-#### WordPress Version
-
-You can run the tests against a specific version of WordPress by setting the `WP_VERSION` environment variable.
-
-This variable understands any numeric version, as well as the special terms `latest` and `trunk`.
-
-Note: This only applies to the Behat functional tests. All other tests never load WordPress.
-
-Here's how to run your tests against the latest trunk version of WordPress:
-```bash
-WP_VERSION=trunk composer behat
-```
-
-#### WP-CLI Binary
-
-You can run the tests against a specific WP-CLI binary, instead of using the one that has been built in your project's `vendor/bin` folder.
-
-This can be useful to run your tests against a specific Phar version of WP_CLI.
-
-To do this, you can set the `WP_CLI_BIN_DIR` environment variable to point to a folder that contains an executable `wp` binary. Note: the binary has to be named `wp` to be properly recognized.
-
-As an example, here's how to run your tests against a specific Phar version you've downloaded.
-```bash
-# Prepare the binary you've downloaded into the ~/wp-cli folder first.
-mv ~/wp-cli/wp-cli-1.2.0.phar ~/wp-cli/wp
-chmod +x ~/wp-cli/wp
-
-WP_CLI_BIN_DIR=~/wp-cli composer behat
-```
-
-### Setting up the tests in Travis CI
-
-Basic rules for setting up the test framework with Travis CI:
-
-* `composer prepare-tests` needs to be called once per environment.
-* `linting and sniffing` is a static analysis, so it shouldn't depend on any specific environment. You should do this only once, as a separate stage, instead of per environment.
-* `composer behat || composer behat-rerun` causes the Behat tests to run in their entirety first, and in case their were failed scenarios, a second run is done with only the failed scenarios. This usually gets around intermittent issues like timeouts or similar.
-
-Here's a basic setup of how you can configure Travis CI to work with the test framework (extract):
-```yml
-install:
- - composer install
- - composer prepare-tests
-
-script:
- - composer phpunit
- - composer behat || composer behat-rerun
-
-jobs:
- include:
- - stage: sniff
- script:
- - composer lint
- - composer phpcs
- env: BUILD=sniff
- - stage: test
- php: 7.2
- env: WP_VERSION=latest
- - stage: test
- php: 7.2
- env: WP_VERSION=3.7.11
- - stage: test
- php: 7.2
- env: WP_VERSION=trunk
-```
-
-#### WP-CLI version
-
-You can point the tests to a specific version of WP-CLI through the `WP_CLI_BIN_DIR` constant:
-```bash
-WP_CLI_BIN_DIR=~/my-custom-wp-cli/bin composer behat
-```
-
-#### WordPress version
-
-If you want to run the feature tests against a specific WordPress version, you can use the `WP_VERSION` constant:
-```bash
-WP_VERSION=4.2 composer behat
-```
-
-The `WP_VERSION` constant also understands the `latest` and `trunk` as valid version targets.
-
-#### The database credentials
-
-By default, the tests are run in a database named `wp_cli_test` with the user also named `wp_cli_test` with password `password1`.
-This should be set up via the `composer prepare-tests` command.
-
-The following environment variables can be set to override the default database credentials.
-
- - `WP_CLI_TEST_DBHOST` is the host to use and can include a port, i.e "127.0.0.1:33060" (defaults to "localhost")
- - `WP_CLI_TEST_DBROOTUSER` is the user that has permission to administer databases and users (defaults to "root").
- - `WP_CLI_TEST_DBROOTPASS` is the password to use for the above user (defaults to an empty password).
- - `WP_CLI_TEST_DBNAME` is the database that the tests run under (defaults to "wp_cli_test").
- - `WP_CLI_TEST_DBUSER` is the user that the tests run under (defaults to "wp_cli_test").
- - `WP_CLI_TEST_DBPASS` is the password to use for the above user (defaults to "password1").
- - `WP_CLI_TEST_DBTYPE` is the database engine type to use, i.e. "sqlite" for running tests on SQLite instead of MySQL (defaults to "mysql").
-
-Environment variables can be set for the whole session via the following syntax: `export WP_CLI_TEST_DBNAME=custom_db`.
-
-They can also be set for a single execution by prepending them before the Behat command: `WP_CLI_TEST_DBNAME=custom_db composer behat`.
-
-## Contributing
-
-We appreciate you taking the initiative to contribute to this project.
-
-Contributing isn’t limited to just code. We encourage you to contribute in the way that best fits your abilities, by writing tutorials, giving a demo at your local meetup, helping other users with their support questions, or revising our documentation.
-
-For a more thorough introduction, [check out WP-CLI's guide to contributing](https://make.wordpress.org/cli/handbook/contributing/). This package follows those policy and guidelines.
-
-### Reporting a bug
-
-Think you’ve found a bug? We’d love for you to help us get it fixed.
-
-Before you create a new issue, you should [search existing issues](https://github.com/wp-cli/wp-cli-tests/issues?q=label%3Abug%20) to see if there’s an existing resolution to it, or if it’s already been fixed in a newer version.
-
-Once you’ve done a bit of searching and discovered there isn’t an open or fixed issue for your bug, please [create a new issue](https://github.com/wp-cli/wp-cli-tests/issues/new). Include as much detail as you can, and clear steps to reproduce if possible. For more guidance, [review our bug report documentation](https://make.wordpress.org/cli/handbook/bug-reports/).
-
-### Creating a pull request
-
-Want to contribute a new feature? Please first [open a new issue](https://github.com/wp-cli/wp-cli-tests/issues/new) to discuss whether the feature is a good fit for the project.
-
-Once you've decided to commit the time to seeing your pull request through, [please follow our guidelines for creating a pull request](https://make.wordpress.org/cli/handbook/pull-requests/) to make sure it's a pleasant experience. See "[Setting up](https://make.wordpress.org/cli/handbook/pull-requests/#setting-up)" for details specific to working on this package locally.
-
-## Support
-
-GitHub issues aren't for general support questions, but there are other venues you can try: https://wp-cli.org/#support
-
-
diff --git a/WP_CLI_CS/ruleset.xml b/WP_CLI_CS/ruleset.xml
deleted file mode 100644
index 1b2da5c98..000000000
--- a/WP_CLI_CS/ruleset.xml
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
-
- Coding standard for WP-CLI projects
-
-
-
-
-
-
- */.git/*
- */node_modules/*
- */vendor/*
-
-
-
-
-
-
- *\.php$
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/behat.yml b/behat.yml
deleted file mode 100644
index d6ee86224..000000000
--- a/behat.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-default:
- suites:
- default:
- contexts:
- - WP_CLI\Tests\Context\FeatureContext
- paths:
- - features
diff --git a/bin/install-package-tests b/bin/install-package-tests
deleted file mode 100755
index aa31ca68a..000000000
--- a/bin/install-package-tests
+++ /dev/null
@@ -1,183 +0,0 @@
-#!/bin/sh
-
-# Database credentials can be provided via environment variables:
-# - WP_CLI_TEST_DBHOST is the host to use and can include a port or a socket after a colon, i.e "127.0.0.1:33060" (defaults to "localhost")
-# - WP_CLI_TEST_DBROOTUSER is the user that has permission to administer databases and users (defaults to "root").
-# - WP_CLI_TEST_DBROOTPASS is the password to use for the above user (defaults to an empty password).
-# - WP_CLI_TEST_DBNAME is the database that the tests run under (defaults to "wp_cli_test").
-# - WP_CLI_TEST_DBUSER is the user that the tests run under (defaults to "wp_cli_test").
-# - WP_CLI_TEST_DBPASS is the password to use for the above user (defaults to "password1").
-
-# POSIX compliant function to check if a string is numeric.
-is_numeric() {
- case $1 in
- ''|*[!0-9]*) return 1;; # returns 1 if not numeric
- *) return 0;; # returns 0 if numeric
- esac
-}
-# Promt color vars.
-C_RED="\033[31m"
-C_BLUE="\033[34m"
-NO_FORMAT="\033[0m"
-
-HOST=localhost
-PORT=""
-HOST_STRING=''
-if [ -n "${WP_CLI_TEST_DBHOST}" ]; then
- case ${WP_CLI_TEST_DBHOST##*[]]} in
- (*:*) HOST=${WP_CLI_TEST_DBHOST%:*} PORT=${WP_CLI_TEST_DBHOST##*:};;
- (*) HOST=${WP_CLI_TEST_DBHOST};;
- esac
- HOST_STRING="-h${HOST}"
- if [ -n "${PORT}" ]; then
- # If the port is not numeric, then we assume it is a socket path.
- if is_numeric "${PORT}"; then
- echo "Connecting to custom host: ${C_BLUE}${HOST}${NO_FORMAT} on port ${C_BLUE}${PORT}${NO_FORMAT}"
- HOST_STRING="${HOST_STRING} --port=${PORT} --protocol=tcp"
- else
- echo "Connecting to custom host: ${C_BLUE}${HOST}${NO_FORMAT} on socket ${C_BLUE}${PORT}${NO_FORMAT}"
- HOST_STRING="${HOST_STRING} --socket=${PORT} --protocol=socket"
- fi
- else
- echo "Connecting to custom host: ${C_BLUE}${HOST}${NO_FORMAT}"
- fi
-else
- echo "Connecting to default host: ${C_BLUE}${HOST}${NO_FORMAT}"
-fi
-
-USER=root
-if [ -n "${WP_CLI_TEST_DBROOTUSER}" ]; then
- echo "Connecting with custom root user: ${C_BLUE}${WP_CLI_TEST_DBROOTUSER}${NO_FORMAT}"
- USER="${WP_CLI_TEST_DBROOTUSER}"
-else
- echo "Connecting with default root user: ${C_BLUE}${USER}${NO_FORMAT}"
-fi
-
-PASSWORD_STRING=""
-if [ -n "${WP_CLI_TEST_DBROOTPASS}" ]; then
- echo "Connecting with custom root password: ${C_BLUE}${WP_CLI_TEST_DBROOTPASS}${NO_FORMAT}"
- PASSWORD_STRING="-p${WP_CLI_TEST_DBROOTPASS}"
-else
- echo "Connecting with default root password: ${C_BLUE}empty${NO_FORMAT}"
-fi
-
-TEST_DB=wp_cli_test
-if [ -n "${WP_CLI_TEST_DBNAME}" ]; then
- echo "Using custom test database: ${C_BLUE}${WP_CLI_TEST_DBNAME}${NO_FORMAT}"
- TEST_DB="${WP_CLI_TEST_DBNAME}"
-else
- echo "Using default test database: ${C_BLUE}${TEST_DB}${NO_FORMAT}"
-fi
-
-TEST_USER=wp_cli_test
-if [ -n "${WP_CLI_TEST_DBUSER}" ]; then
- echo "Using custom test user: ${C_BLUE}${WP_CLI_TEST_DBUSER}${NO_FORMAT}"
- TEST_USER="${WP_CLI_TEST_DBUSER}"
-else
- echo "Using default test user: ${C_BLUE}${TEST_USER}${NO_FORMAT}"
-fi
-
-TEST_PASSWORD=password1
-if [ -n "${WP_CLI_TEST_DBPASS}" ]; then
- echo "Using custom test password: ${C_BLUE}${WP_CLI_TEST_DBPASS}${NO_FORMAT}"
- TEST_PASSWORD="${WP_CLI_TEST_DBPASS}"
-else
- echo "Using default test password: ${C_BLUE}${TEST_PASSWORD}${NO_FORMAT}"
-fi
-
-echo "Detecting database version..."
-
-# Detect which client binary is available.
-CLIENT_BINARY=""
-if command -v mysql >/dev/null 2>&1; then
- CLIENT_BINARY="mysql"
-elif command -v mariadb >/dev/null 2>&1; then
- CLIENT_BINARY="mariadb"
-else
- echo "${C_RED}Error: Neither 'mysql' nor 'mariadb' client binary found.${NO_FORMAT}"
- exit 1
-fi
-
-if [ -z "$PS1" ]; then
- # These vars are because github actions gave problems in the past.
- MYSQL_TRIES=36
- MYSQL_WAIT=5
-else
- MYSQL_TRIES=1
- MYSQL_WAIT=0
-fi
-
-# Use the detected client binary to query the server version.
-SERVER_VERSION=$(${CLIENT_BINARY} -e "SELECT VERSION()" --skip-column-names ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}" 2>/dev/null)
-
-# Detect the database type from the server version string.
-TYPE="MySQL"
-case "${SERVER_VERSION}" in
- *"MariaDB"*|*"mariadb"*)
- TYPE="MariaDB"
- ;;
-esac
-
-VERSION=$(echo "${SERVER_VERSION}" | grep -o '^[^-]*')
-MAJOR=$(echo "${VERSION}" | cut -d. -f1)
-MINOR=$(echo "${VERSION}" | cut -d. -f2)
-
-echo "Detected ${TYPE} at version ${MAJOR}.${MINOR}"
-
-echo 'Checking if database is ready...'
-
-i=0
-while ! ${CLIENT_BINARY} ${HOST_STRING} --user="${USER}" "${PASSWORD_STRING}" --execute="SHOW DATABASES;" 2>/dev/null | grep 'information_schema' >/dev/null;
-do
- i=$((i+1))
- if [ "${MYSQL_TRIES}" -gt 1 ]; then
- echo "Waiting for database server (${i}/${MYSQL_TRIES})..."
- sleep ${MYSQL_WAIT}
- fi
-
- if [ $i -ge $MYSQL_TRIES ]; then
- echo "${C_RED}Database server failed to start. Aborting.${NO_FORMAT}"
- echo "Cannot connect to database server. For all available variables, check the documentation at:"
- echo " ${C_BLUE}https://github.com/wp-cli/wp-cli-tests?tab=readme-ov-file#the-database-credentials${NO_FORMAT}"
- exit 1
- fi
-done
-
-# Prepare the database for running the tests with a MySQL version 8.0 or higher.
-install_mysql_db_8_0_plus() {
- set -ex # print all the commands.
- ${CLIENT_BINARY} -e "CREATE DATABASE IF NOT EXISTS \`${TEST_DB}\`;" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- ${CLIENT_BINARY} -e "CREATE USER IF NOT EXISTS \`${TEST_USER}\`@'%' IDENTIFIED WITH caching_sha2_password BY '${TEST_PASSWORD}'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- ${CLIENT_BINARY} -e "GRANT ALL PRIVILEGES ON \`${TEST_DB}\`.* TO '${TEST_USER}'@'%'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- ${CLIENT_BINARY} -e "GRANT ALL PRIVILEGES ON \`${TEST_DB}_scaffold\`.* TO '${TEST_USER}'@'%'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- { set +ex; } 2> /dev/null # stop printing the commands
-}
-
-# Prepare the database for running the tests with a MySQL version lower than 8.0.
-install_mysql_db_lower_than_8_0() {
- set -ex # print all the commands.
- ${CLIENT_BINARY} -e "CREATE DATABASE IF NOT EXISTS \`${TEST_DB}\`;" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- ${CLIENT_BINARY} -e "GRANT ALL ON \`${TEST_DB}\`.* TO '${TEST_USER}'@'%' IDENTIFIED BY '${TEST_PASSWORD}'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- ${CLIENT_BINARY} -e "GRANT ALL ON \`${TEST_DB}_scaffold\`.* TO '${TEST_USER}'@'%' IDENTIFIED BY '${TEST_PASSWORD}'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- { set +ex; } 2> /dev/null # stop printing the commands
-}
-
-# Prepare the database for running the tests with MariaDB
-install_mariadb() {
- set -ex
- ${CLIENT_BINARY} -e "CREATE DATABASE IF NOT EXISTS \`${TEST_DB}\`;" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- ${CLIENT_BINARY} -e "CREATE USER IF NOT EXISTS \`${TEST_USER}\`@'%' IDENTIFIED BY '${TEST_PASSWORD}'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- ${CLIENT_BINARY} -e "GRANT ALL PRIVILEGES ON \`${TEST_DB}\`.* TO '${TEST_USER}'@'%'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
- ${CLIENT_BINARY} -e "GRANT ALL PRIVILEGES ON \`${TEST_DB}_scaffold\`.* TO '${TEST_USER}'@'%'" ${HOST_STRING} -u"${USER}" "${PASSWORD_STRING}"
-}
-
-if [ "${TYPE}" = "MariaDB" ]; then
- install_mariadb
-elif [ "${MAJOR}" -ge 8 ]; then
- install_mysql_db_8_0_plus
-else
- install_mysql_db_lower_than_8_0
-fi
-
-echo "Successfully prepared the database for running tests."
-echo "This command does not have to be run again."
diff --git a/bin/rerun-behat-tests b/bin/rerun-behat-tests
deleted file mode 100755
index 0d4547ac9..000000000
--- a/bin/rerun-behat-tests
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-# To retrieve the WP-CLI tests package root folder, we start with this scripts
-# location.
-SOURCE="${BASH_SOURCE[0]}"
-
-# Resolve $SOURCE until the file is no longer a symlink.
-while [ -h "$SOURCE" ]; do
- DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
- SOURCE="$(readlink "$SOURCE")"
- # If $SOURCE was a relative symlink, we need to resolve it relative to the
- # path where the symlink file was located.
- [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
-done
-
-# Fetch the root folder of the WP-CLI tests package.
-WP_CLI_TESTS_ROOT="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
-export WP_CLI_TESTS_ROOT
-
-"$WP_CLI_TESTS_ROOT"/bin/run-behat-tests --rerun "$@"
diff --git a/bin/run-behat-tests b/bin/run-behat-tests
deleted file mode 100755
index 3ee00892e..000000000
--- a/bin/run-behat-tests
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/bin/bash
-
-# Run the Behat tests only if a Behat config file is found.
-if [ ! -f "behat.yml" ]; then
- echo 'Did not detect "behat.yml" file, skipping Behat tests.'
- exit 0;
-fi
-
-if ! command -v jq &> /dev/null
-then
- echo 'The required "jq" command was not found, please install it to run the Behat tests.'
- echo "See https://stedolan.github.io/jq/download/ for installation instructions."
- exit 1;
-fi
-
-if [[ "$@" == *"--help"* ]]; then
- vendor/bin/behat "$@"
- ret=$?
- exit $ret
-fi
-
-# POSIX compliant function to check if a string is numeric.
-is_numeric() {
- case $1 in
- ''|*[!0-9]*) return 1;; # returns 1 if not numeric
- *) return 0;; # returns 0 if numeric
- esac
-}
-
-# If DB type is already set to SQLite, there's nothing to do.
-if [ "${WP_CLI_TEST_DBTYPE-}" = "sqlite" ]; then
- echo "WP_CLI_TEST_DBTYPE is set to 'sqlite', skipping database check."
-else
- # Check for database client and connectivity.
- DB_CLIENT=""
- if command -v mysql &> /dev/null; then
- DB_CLIENT="mysql"
- elif command -v mariadb &> /dev/null; then
- DB_CLIENT="mariadb"
- fi
-
- if [ -z "${DB_CLIENT}" ]; then
- echo "Warning: Could not find 'mysql' or 'mariadb' client."
- echo "The tests will continue to be run, but with WP_CLI_TEST_DBTYPE=sqlite."
- export WP_CLI_TEST_DBTYPE=sqlite
- else
- HOST_STRING=''
- if [ -n "${WP_CLI_TEST_DBHOST}" ]; then
- case ${WP_CLI_TEST_DBHOST##*[]]} in
- (*:*) HOST=${WP_CLI_TEST_DBHOST%:*} PORT=${WP_CLI_TEST_DBHOST##*:};;
- (*) HOST=${WP_CLI_TEST_DBHOST};;
- esac
- HOST_STRING="-h${HOST}"
- if [ -n "${PORT}" ]; then
- # If the port is not numeric, then we assume it is a socket path.
- if is_numeric "${PORT}"; then
- HOST_STRING="${HOST_STRING} --port=${PORT} --protocol=tcp"
- else
- HOST_STRING="${HOST_STRING} --socket=${PORT} --protocol=socket"
- fi
- fi
- fi
-
- USER=${WP_CLI_TEST_DBUSER:-wp_cli_test}
-
- if [ -z "${WP_CLI_TEST_DBPASS+x}" ]; then
- # not set, use default
- PASSWORD="password1"
- else
- # is set, use its value (could be empty)
- PASSWORD="${WP_CLI_TEST_DBPASS}"
- fi
-
- PASSWORD_ARG=""
- if [ -n "${PASSWORD}" ]; then
- PASSWORD_ARG="-p${PASSWORD}"
- fi
-
- DBNAME=${WP_CLI_TEST_DBNAME:-wp_cli_test}
-
- # We need to test the connection.
- # Let's try to connect to the specific test database.
- if ! ${DB_CLIENT} ${HOST_STRING} --user="${USER}" ${PASSWORD_ARG} --execute="USE \`${DBNAME}\`;" 2>/dev/null; then
- echo "Warning: Could not connect to the MySQL/MariaDB database."
- echo "Please make sure the database is running and run 'composer prepare-tests' once to set it up."
- echo "The tests will continue to be run, but with WP_CLI_TEST_DBTYPE=sqlite."
- export WP_CLI_TEST_DBTYPE=sqlite
- fi
- fi
-fi
-
-# Turn WP_VERSION into an actual number to make sure our tags work correctly.
-if [ "${WP_VERSION-latest}" = "latest" ]; then
- export WP_VERSION=$(curl -s https://api.wordpress.org/core/version-check/1.7/ | jq -r ".offers[0].current")
-fi
-
-# To retrieve the WP-CLI tests package root folder, we start with this scripts
-# location.
-SOURCE="${BASH_SOURCE[0]}"
-
-# Resolve $SOURCE until the file is no longer a symlink.
-while [ -h "$SOURCE" ]; do
- DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
- SOURCE="$(readlink "$SOURCE")"
- # If $SOURCE was a relative symlink, we need to resolve it relative to the
- # path where the symlink file was located.
- [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
-done
-
-# Fetch the root folder of the WP-CLI tests package.
-WP_CLI_TESTS_ROOT="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
-export WP_CLI_TESTS_ROOT
-
-# Generate the tags to apply environment-specific filters.
-BEHAT_TAGS=$(php "$WP_CLI_TESTS_ROOT"/utils/behat-tags.php)
-
-BEHAT_EXTRA_ARGS=()
-if [[ "${WP_CLI_TEST_COVERAGE}" == "true" ]] && vendor/bin/behat --help 2>/dev/null | grep -q 'xdebug'; then
- BEHAT_EXTRA_ARGS+=('--xdebug')
-fi
-
-# Run the functional tests.
-vendor/bin/behat --snippets-for="WP_CLI\Tests\Context\FeatureContext" --format progress "$BEHAT_TAGS" --strict "${BEHAT_EXTRA_ARGS[@]}" "$@"
diff --git a/bin/run-linter-tests b/bin/run-linter-tests
deleted file mode 100755
index add8ec336..000000000
--- a/bin/run-linter-tests
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-vendor/bin/parallel-lint -j 10 --colors --exclude vendor . "$@"
diff --git a/bin/run-php-unit-tests b/bin/run-php-unit-tests
deleted file mode 100755
index 6182d0746..000000000
--- a/bin/run-php-unit-tests
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-
-# Run the unit tests, if they exist
-if [ -f "phpunit.xml" ] || [ -f "phpunit.xml.dist" ] || [ -f ".phpunit.xml" ] || [ -f ".phpunit.xml.dist" ]
-then
- PHPUNIT_VERSION=$(vendor/bin/phpunit --version | grep --only-matching --max-count=1 --extended-regexp '\b[0-9]+\.[0-9]+')
- EXTRA_ARGS=""
-
- if [ "${PHPUNIT_VERSION#11}" != "$PHPUNIT_VERSION" ] || [ "${PHPUNIT_VERSION#10}" != "$PHPUNIT_VERSION" ]; then
- EXTRA_ARGS="--display-warnings --fail-on-warning --display-notices --fail-on-notice --display-deprecations --fail-on-deprecation"
- fi
-
- if [ -f "./vendor/wp-cli/wp-cli-tests/tests/bootstrap.php" ]; then
- vendor/bin/phpunit --color=always "$@" $EXTRA_ARGS --bootstrap ./vendor/wp-cli/wp-cli-tests/tests/bootstrap.php
- else
- vendor/bin/phpunit --color=always "$@" $EXTRA_ARGS
- fi
-fi
diff --git a/bin/run-phpcbf-cleanup b/bin/run-phpcbf-cleanup
deleted file mode 100755
index f7a4d8fb1..000000000
--- a/bin/run-phpcbf-cleanup
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-# Run the code style check only if a configuration file exists.
-if [ -f ".phpcs.xml" ] || [ -f "phpcs.xml" ] || [ -f ".phpcs.xml.dist" ] || [ -f "phpcs.xml.dist" ]
-then
- vendor/bin/phpcbf "$@"
-fi
diff --git a/bin/run-phpcs-tests b/bin/run-phpcs-tests
deleted file mode 100755
index 82d96b4e5..000000000
--- a/bin/run-phpcs-tests
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-# Run the code style check only if a configuration file exists.
-if [ -f ".phpcs.xml" ] || [ -f "phpcs.xml" ] || [ -f ".phpcs.xml.dist" ] || [ -f "phpcs.xml.dist" ]
-then
- vendor/bin/phpcs "$@"
-fi
diff --git a/bin/run-phpstan-tests b/bin/run-phpstan-tests
deleted file mode 100755
index 21f276900..000000000
--- a/bin/run-phpstan-tests
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-# Run the code style check only if a configuration file exists.
-if [ -f "phpstan.dist.neon" ] || [ -f "phpstan.neon.dist" ] || [ -f "phpstan.neon" ]
-then
- vendor/bin/phpstan --memory-limit=2048M analyse "$@"
-fi
diff --git a/composer.json b/composer.json
deleted file mode 100644
index 4695b5f5e..000000000
--- a/composer.json
+++ /dev/null
@@ -1,109 +0,0 @@
-{
- "name": "wp-cli/wp-cli-tests",
- "description": "WP-CLI testing framework",
- "keywords": [
- "cli",
- "wordpress"
- ],
- "homepage": "https://wp-cli.org",
- "license": "MIT",
- "type": "phpcodesniffer-standard",
- "require": {
- "php": ">=7.2.24",
- "behat/behat": "^v3.15.0",
- "dealerdirect/phpcodesniffer-composer-installer": "^0.4.3 || ^0.5 || ^0.6.2 || ^0.7.1 || ^1.0.0",
- "php-parallel-lint/php-console-highlighter": "^1.0",
- "php-parallel-lint/php-parallel-lint": "^1.3.1",
- "phpcompatibility/php-compatibility": "^10.0",
- "phpstan/extension-installer": "^1.4",
- "phpstan/phpstan": "^1.12.26 || ^2.0",
- "phpstan/phpstan-deprecation-rules": "^1.2 || ^2.0",
- "phpstan/phpstan-phpunit": "^1.4 || ^2.0",
- "phpstan/phpstan-strict-rules": "^1.6 || ^2.0",
- "swissspidy/phpstan-no-private": "^0.2.1 || ^1.0",
- "szepeviktor/phpstan-wordpress": "^v1.3.5",
- "wp-cli/config-command": "^1 || ^2",
- "wp-cli/core-command": "^1 || ^2",
- "wp-cli/eval-command": "^1 || ^2",
- "wp-cli/wp-cli": "^2.12",
- "wp-coding-standards/wpcs": "dev-develop",
- "yoast/phpunit-polyfills": "^1.0 || ^2.0 || ^3.0 || ^4.0"
- },
- "require-dev": {
- "roave/security-advisories": "dev-latest"
- },
- "config": {
- "allow-plugins": {
- "dealerdirect/phpcodesniffer-composer-installer": true,
- "johnpbloch/wordpress-core-installer": true,
- "phpstan/extension-installer": true
- },
- "sort-packages": true,
- "lock": false
- },
- "extra": {
- "branch-alias": {
- "dev-main": "5.1.x-dev"
- },
- "phpstan": {
- "includes": [
- "extension.neon"
- ]
- },
- "readme": {
- "sections": [
- "Using",
- "Contributing",
- "Support"
- ],
- "using": {
- "body": ".readme-partials/USING.md"
- },
- "show_powered_by": false
- }
- },
- "autoload": {
- "psr-4": {
- "WP_CLI\\Tests\\": "src"
- }
- },
- "autoload-dev": {
- "psr-4": {
- "WP_CLI\\Tests\\Tests\\": "tests/tests"
- }
- },
- "minimum-stability": "dev",
- "prefer-stable": true,
- "bin": [
- "bin/install-package-tests",
- "bin/rerun-behat-tests",
- "bin/run-behat-tests",
- "bin/run-linter-tests",
- "bin/run-php-unit-tests",
- "bin/run-phpcs-tests",
- "bin/run-phpcbf-cleanup",
- "bin/run-phpstan-tests"
- ],
- "scripts": {
- "behat": "run-behat-tests",
- "behat-rerun": "rerun-behat-tests",
- "lint": "run-linter-tests",
- "phpcs": "run-phpcs-tests",
- "phpcbf": "run-phpcbf-cleanup",
- "phpstan": "run-phpstan-tests",
- "phpunit": "run-php-unit-tests",
- "prepare-tests": "install-package-tests",
- "test": [
- "@lint",
- "@phpcs",
- "@phpstan",
- "@phpunit",
- "@behat"
- ]
- },
- "support": {
- "issues": "https://github.com/wp-cli/wp-cli-tests/issues",
- "source": "https://github.com/wp-cli/wp-cli-tests",
- "docs": "https://make.wordpress.org/cli/handbook/"
- }
-}
diff --git a/extension.neon b/extension.neon
deleted file mode 100644
index c231d5465..000000000
--- a/extension.neon
+++ /dev/null
@@ -1,51 +0,0 @@
-services:
- -
- class: WP_CLI\Tests\PHPStan\ParseUrlFunctionDynamicReturnTypeExtension
- tags:
- - phpstan.broker.dynamicFunctionReturnTypeExtension
- -
- class: WP_CLI\Tests\PHPStan\GetFlagValueFunctionDynamicReturnTypeExtension
- tags:
- - phpstan.broker.dynamicFunctionReturnTypeExtension
- -
- class: WP_CLI\Tests\PHPStan\WPCliRuncommandDynamicReturnTypeExtension
- tags:
- - phpstan.broker.dynamicStaticMethodReturnTypeExtension
-parameters:
- dynamicConstantNames:
- - FOO
- strictRules:
- allRules: false
- disallowedLooseComparison: false
- booleansInConditions: false
- uselessCast: false
- requireParentConstructorCall: false
- disallowedConstructs: false
- overwriteVariablesWithLoop: false
- closureUsesThis: false
- matchingInheritedMethodNames: false
- numericOperandsInArithmeticOperators: false
- strictCalls: false
- switchConditionsMatchingType: false
- noVariableVariables: false
- strictArrayFilter: false
-
-# Add the schema from phpstan-strict-rules so it's available without loading the extension
-# and the above configuration works.
-parametersSchema:
- strictRules: structure([
- allRules: anyOf(bool(), arrayOf(bool())),
- disallowedLooseComparison: anyOf(bool(), arrayOf(bool())),
- booleansInConditions: anyOf(bool(), arrayOf(bool()))
- uselessCast: anyOf(bool(), arrayOf(bool()))
- requireParentConstructorCall: anyOf(bool(), arrayOf(bool()))
- disallowedConstructs: anyOf(bool(), arrayOf(bool()))
- overwriteVariablesWithLoop: anyOf(bool(), arrayOf(bool()))
- closureUsesThis: anyOf(bool(), arrayOf(bool()))
- matchingInheritedMethodNames: anyOf(bool(), arrayOf(bool()))
- numericOperandsInArithmeticOperators: anyOf(bool(), arrayOf(bool()))
- strictCalls: anyOf(bool(), arrayOf(bool()))
- switchConditionsMatchingType: anyOf(bool(), arrayOf(bool()))
- noVariableVariables: anyOf(bool(), arrayOf(bool()))
- strictArrayFilter: anyOf(bool(), arrayOf(bool()))
- ])
diff --git a/features/behat-steps.feature b/features/behat-steps.feature
deleted file mode 100644
index 6ad8c2b6f..000000000
--- a/features/behat-steps.feature
+++ /dev/null
@@ -1,691 +0,0 @@
-Feature: Test that WP-CLI Behat steps work as expected
-
- This feature file contains functional tests for all the Behat steps
- provided by the WP-CLI testing framework. It ensures that each Given,
- When, and Then step definition works correctly.
-
- The tests are organized by step type and functionality:
- - Basic file and directory operations
- - Command execution and output validation
- - Variable management and replacement
- - WordPress installation and configuration
- - HTTP mocking and network operations
- - Output format validation (JSON, CSV, YAML, tables)
-
- Each scenario tests a specific step or combination of steps to verify
- they produce the expected behavior.
-
- Scenario: Test "Given an empty directory" step
- Given an empty directory
- Then the {RUN_DIR} directory should exist
-
- Scenario: Test "Given a specific directory" steps
- Given an empty directory
- And an empty test-dir directory
- Then the test-dir directory should exist
-
- Given a non-existent test-dir directory
- Then the test-dir directory should not exist
-
- Scenario: Test "Given an empty cache" step
- Given an empty cache
- Then the {SUITE_CACHE_DIR} directory should exist
-
- Scenario: Test "Given a file" step
- Given an empty directory
- And a test.txt file:
- """
- Hello World
- """
- Then the test.txt file should exist
- And the test.txt file should contain:
- """
- Hello World
- """
-
- Scenario: Test "Given a cache file" step
- Given an empty cache
- And a test-cache.txt cache file:
- """
- Cached content
- """
- Then the {SUITE_CACHE_DIR}/test-cache.txt file should exist
- And the {SUITE_CACHE_DIR}/test-cache.txt file should contain:
- """
- Cached content
- """
-
- Scenario: Test string replacement in file
- Given an empty directory
- And a config.txt file:
- """
- foo bar baz
- """
- And "foo" replaced with "hello" in the config.txt file
- Then the config.txt file should contain:
- """
- hello bar baz
- """
-
- Scenario: Test "When I run" step with basic command
- When I run `echo "test output"`
- Then STDOUT should be:
- """
- test output
- """
- And STDERR should be empty
- And the return code should be 0
-
- Scenario: Test "When I try" step with failing command
- When I try `false`
- Then the return code should be 1
-
- Scenario: Test "save STDOUT as" variable
- When I run `echo "saved value"`
- Then save STDOUT as {MY_VAR}
-
- When I run `echo {MY_VAR}`
- Then STDOUT should be:
- """
- saved value
- """
-
- Scenario: Test "save STDOUT with pattern" as variable
- When I run `echo "Version 1.2.3 downloaded"`
- Then save STDOUT 'Version ([\d\.]+)' as {VERSION}
-
- When I run `echo {VERSION}`
- Then STDOUT should be:
- """
- 1.2.3
- """
-
- Scenario: Test "STDOUT should contain" step
- When I run `echo "hello world"`
- Then STDOUT should contain:
- """
- world
- """
-
- Scenario: Test "STDOUT should not contain" step
- When I run `echo "hello world"`
- Then STDOUT should not contain:
- """
- goodbye
- """
-
- Scenario: Test "STDOUT should be a number" step
- When I run `echo 42`
- Then STDOUT should be a number
-
- Scenario: Test "STDOUT should not be a number" step
- When I run `echo "not a number"`
- Then STDOUT should not be a number
-
- Scenario: Test "STDOUT should not be empty" step
- When I run `echo "something"`
- Then STDOUT should not be empty
-
- Scenario: Test "STDERR should be empty" step
- When I run `echo "test"`
- Then STDERR should be empty
-
- Scenario: Test "STDOUT should match" regex
- When I run `echo "test-123"`
- Then STDOUT should match /^test-\d+$/
-
- Scenario: Test "STDOUT should not match" regex
- When I run `echo "hello"`
- Then STDOUT should not match /^\d+$/
-
- Scenario: Test "the return code should be" step
- When I run `true`
- Then the return code should be 0
-
- When I try `false`
- Then the return code should be 1
-
- Scenario: Test "the return code should not be" step
- When I run `true`
- Then the return code should not be 1
-
- Scenario: Test "file should exist" step
- Given an empty directory
- And a myfile.txt file:
- """
- content
- """
- Then the myfile.txt file should exist
-
- Scenario: Test "file should not exist" step
- Given an empty directory
- Then the missing.txt file should not exist
-
- Scenario: Test "directory should exist" step
- Given an empty directory
- And an empty subdir directory
- Then the subdir directory should exist
-
- Scenario: Test "directory should not exist" step
- Given an empty directory
- Then the nonexistent directory should not exist
-
- Scenario: Test "file should contain" step
- Given an empty directory
- And a content.txt file:
- """
- Line 1
- Line 2
- """
- Then the content.txt file should contain:
- """
- Line 1
- """
-
- Scenario: Test "file should not contain" step
- Given an empty directory
- And a content.txt file:
- """
- Some content
- """
- Then the content.txt file should not contain:
- """
- Missing text
- """
-
- Scenario: Test "contents of file should match" regex
- Given an empty directory
- And a pattern.txt file:
- """
- Version: 1.2.3
- """
- Then the contents of the pattern.txt file should match /Version:\s+\d+\.\d+\.\d+/
-
- Scenario: Test "contents of file should not match" regex
- Given an empty directory
- And a text.txt file:
- """
- No version here
- """
- Then the contents of the text.txt file should not match /Version:\s+\d+/
-
- Scenario: Test "directory should contain" files
- Given an empty directory
- And a file1.txt file:
- """
- content1
- """
- And a file2.txt file:
- """
- content2
- """
- Then the {RUN_DIR} directory should contain:
- """
- file1.txt
- """
- And the {RUN_DIR} directory should contain:
- """
- file2.txt
- """
-
- Scenario: Test "I run the previous command again" step
- When I run `echo "test"`
- Then STDOUT should contain:
- """
- test
- """
-
- When I run the previous command again
- Then STDOUT should contain:
- """
- test
- """
-
- Scenario: Test variable replacement in commands
- When I run `echo "myvalue"`
- Then save STDOUT as {TEST_VAR}
-
- When I run `echo "Value is: {TEST_VAR}"`
- Then STDOUT should contain:
- """
- myvalue
- """
-
- Scenario: Test STDOUT strictly be
- When I run `echo "exact"`
- Then STDOUT should strictly be:
- """
- exact
- """
-
- Scenario: Test file strictly contain
- Given an empty directory
- And a strict.txt file:
- """
- exact content
- """
- Then the strict.txt file should strictly contain:
- """
- exact content
- """
-
- @require-wp
- Scenario: Test WP installation steps
- Given a WP installation
- When I run `wp core version`
- Then STDOUT should not be empty
- And the return code should be 0
-
- @require-wp
- Scenario: Test WP installation steps
- Given a WP 6.8 installation
- When I run `wp core version`
- Then STDOUT should contain:
- """
- 6.8
- """
-
- @require-wp
- Scenario: Test WP files and wp-config.php steps
- Given an empty directory
- And WP files
- Then the wp-settings.php file should exist
-
- Given wp-config.php
- Then the wp-config.php file should exist
- And the wp-config.php file should contain:
- """
- DB_NAME
- """
-
- @require-wp
- Scenario: Test WP installation in subdirectory
- Given a WP installation in 'subdir'
- When I run `wp core version` from 'subdir'
- Then STDOUT should not be empty
- And the return code should be 0
-
- @require-wp
- Scenario: Test version string comparison
- Given a WP installation
- When I run `wp core version`
- Then STDOUT should be a version string >= 4.0
-
- Scenario: Test STDOUT as table containing rows
- When I run `printf "name\tversion\nfoo\t1.0\nbar\t2.0"`
- Then STDOUT should be a table containing rows:
- | name | version |
- | foo | 1.0 |
-
- Scenario: Test JSON output
- When I run `echo '{"name":"test","value":"example.com"}'`
- Then STDOUT should be JSON containing:
- """
- {"name":"test"}
- """
-
- Scenario: Test CSV output
- When I run `printf "user_login,user_email\nadmin,admin@example.com"`
- Then STDOUT should contain:
- """
- user_login
- """
-
- Scenario: Test YAML output
- When I run `printf "name: test\nversion: 1.0"`
- Then STDOUT should be YAML containing:
- """
- name: test
- """
-
- @require-wp
- Scenario: Test save file as variable
- Given a WP installation
- And a composer.json file:
- """
- {"name": "test"}
- """
- And save the {RUN_DIR}/composer.json file as {COMPOSER}
- When I run `echo '{COMPOSER}'`
- Then STDOUT should contain:
- """
- test
- """
-
- Scenario: Test HTTP request mocking
- Given that HTTP requests to https://example.com/test will respond with:
- """
- HTTP/1.1 200
- Content-Type: text/plain
-
- Mock response
- """
- Then the wp-cli.yml file should exist
- And the mock-requests.php file should exist
-
- @require-wp
- Scenario: Test background process launch
- Given a WP installation
- When I launch in the background `wp eval 'sleep(2); echo "done";'`
- # Background process should not block, continuing immediately
-
- @require-wp
- Scenario: Test custom wp-content directory
- Given a WP installation
- And a custom wp-content directory
- Then the my-content directory should exist
- And the my-plugins directory should exist
- And the my-mu-plugins directory should exist
- And the wp-config.php file should contain:
- """
- WP_CONTENT_DIR
- """
-
- @require-wp
- Scenario: Test WP multisite installation
- Given a WP multisite installation
- When I run `wp core is-installed`
- Then the return code should be 0
-
- @require-wp
- Scenario: Test WP multisite subdirectory installation
- Given a WP multisite subdirectory installation
- When I run `wp core is-installed --network`
- Then the return code should be 0
-
- @require-wp
- Scenario: Test WP multisite subdomain installation
- Given a WP multisite subdomain installation
- When I run `wp core is-installed --network`
- Then the return code should be 0
-
- @require-wp
- Scenario: Test misconfigured WP_CONTENT_DIR
- Given a WP installation
- And a misconfigured WP_CONTENT_DIR constant directory
- Then the wp-config.php file should contain:
- """
- define( 'WP_CONTENT_DIR', '' );
- """
-
- @require-download
- Scenario: Test download step
- Given an empty cache
- And download:
- | path | url |
- | {SUITE_CACHE_DIR}/test.txt | https://www.iana.org/robots.txt |
- Then the {SUITE_CACHE_DIR}/test.txt file should exist
-
- @require-wp @require-composer
- Scenario: Test WP installation with Composer
- Given a WP installation with Composer
- Then the composer.json file should exist
- And the vendor directory should exist
- When I run `wp core version`
- Then STDOUT should not be empty
-
- @require-wp @require-composer
- Scenario: Test WP installation with Composer and custom vendor directory
- Given a WP installation with Composer and a custom vendor directory 'custom-vendor'
- Then the composer.json file should exist
- And the custom-vendor directory should exist
-
- @require-wp @require-composer
- Scenario: Test dependency on current wp-cli
- Given a WP installation with Composer
- And a dependency on current wp-cli
- Then the composer.json file should contain:
- """
- wp-cli/wp-cli
- """
-
-
-
- Scenario: Test STDOUT should be empty
- When I run `echo -n ""`
- Then STDOUT should be empty
-
- Scenario: Test running command from subdirectory
- Given an empty directory
- And an empty testdir directory
- When I run `pwd` from 'testdir'
- Then STDOUT should contain:
- """
- testdir
- """
-
- Scenario: Test STDOUT strictly contain
- When I run `echo "exact"`
- Then STDOUT should strictly contain:
- """
- exact
- """
-
- Scenario: Test STDERR output
- When I try `bash -c 'echo "error message" >&2'`
- Then STDERR should contain:
- """
- error message
- """
- And STDERR should not be empty
-
- Scenario: Test file path with nested directory
- Given an empty directory
- And a nested/path/file.txt file:
- """
- content
- """
- Then the nested/path/file.txt file should contain:
- """
- content
- """
-
- Scenario: Test multiple files in directory
- Given an empty directory
- And a file-a.txt file:
- """
- A
- """
- And a file-b.txt file:
- """
- B
- """
- And a file-c.txt file:
- """
- C
- """
- Then the {RUN_DIR} directory should contain:
- """
- file-a.txt
- """
- And the {RUN_DIR} directory should contain:
- """
- file-b.txt
- """
- And the {RUN_DIR} directory should contain:
- """
- file-c.txt
- """
-
- Scenario: Test special characters in file content
- Given an empty directory
- And a special.txt file:
- """
- Line with "quotes"
- Line with 'apostrophes'
- Line with $variable
- """
- Then the special.txt file should contain:
- """
- quotes
- """
-
- Scenario: Test version comparison operators
- When I run `echo "5.6.2"`
- Then STDOUT should be a version string > 5.6.1
- And STDOUT should be a version string >= 5.6.2
- And STDOUT should be a version string < 5.6.3
- And STDOUT should be a version string <= 5.6.2
- And STDOUT should be a version string == 5.6.2
- And STDOUT should be a version string != 5.6.3
-
- @require-wp
- Scenario: Test JSON array containing
- Given a WP installation
- When I run `wp eval 'echo json_encode(["apple", "banana", "cherry"]);'`
- Then STDOUT should be a JSON array containing:
- """
- ["apple", "banana"]
- """
-
- @require-wp
- Scenario: Test email sending detection
- Given a WP installation
- And a send-email.php file:
- """
- &2'`
- Then STDOUT should contain:
- """
- stdout
- """
- And STDERR should contain:
- """
- stderr
- """
-
- Scenario: Test file with multiline content
- Given an empty directory
- And a multiline.txt file:
- """
- First line
- Second line
- Third line
- """
- Then the multiline.txt file should contain:
- """
- First line
- """
- And the multiline.txt file should contain:
- """
- Second line
- """
- And the multiline.txt file should contain:
- """
- Third line
- """
-
- Scenario: Test return code not be assertion
- When I run `true`
- Then the return code should not be 1
- And the return code should not be 2
-
- Scenario: Test CSV containing with headers
- When I run `printf "user_login,user_email\nadmin,admin@example.com\nuser2,user2@example.com"`
- Then STDOUT should be CSV containing:
- | user_login | user_email |
- | admin | admin@example.com |
diff --git a/features/http-mocking.feature b/features/http-mocking.feature
deleted file mode 100644
index d970936c8..000000000
--- a/features/http-mocking.feature
+++ /dev/null
@@ -1,121 +0,0 @@
-Feature: HTTP request mocking
-
- Scenario: Mock HTTP request in WP-CLI
- Given that HTTP requests to https://api.github.com/repos/wp-cli/wp-cli/releases?per_page=100 will respond with:
- """
- HTTP/1.1 200
- Content-Type: application/json
-
- [
- {
- "url": "https://api.github.com/repos/wp-cli/wp-cli/releases/169243978",
- "assets_url": "https://api.github.com/repos/wp-cli/wp-cli/releases/169243978/assets",
- "upload_url": "https://uploads.github.com/repos/wp-cli/wp-cli/releases/169243978/assets{?name,label}",
- "html_url": "https://github.com/wp-cli/wp-cli/releases/tag/v999.9.9",
- "id": 169243978,
- "author": {
- "login": "schlessera",
- "id": 83631,
- "node_id": "MDQ6VXNlcjgzNjMx",
- "avatar_url": "https://avatars.githubusercontent.com/u/83631?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/schlessera",
- "html_url": "https://github.com/schlessera",
- "followers_url": "https://api.github.com/users/schlessera/followers",
- "following_url": "https://api.github.com/users/schlessera/following{/other_user}",
- "gists_url": "https://api.github.com/users/schlessera/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/schlessera/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/schlessera/subscriptions",
- "organizations_url": "https://api.github.com/users/schlessera/orgs",
- "repos_url": "https://api.github.com/users/schlessera/repos",
- "events_url": "https://api.github.com/users/schlessera/events{/privacy}",
- "received_events_url": "https://api.github.com/users/schlessera/received_events",
- "type": "User",
- "user_view_type": "public",
- "site_admin": false
- },
- "node_id": "RE_kwDOACQFs84KFnVK",
- "tag_name": "v999.9.9",
- "target_commitish": "main",
- "name": "Version 999.9.9",
- "draft": false,
- "prerelease": false,
- "created_at": "2024-08-08T03:04:55Z",
- "published_at": "2024-08-08T03:51:13Z",
- "assets": [
- {
- "url": "https://api.github.com/repos/wp-cli/wp-cli/releases/assets/184590231",
- "id": 184590231,
- "node_id": "RA_kwDOACQFs84LAJ-X",
- "name": "wp-cli-999.9.9.phar",
- "label": null,
- "content_type": "application/octet-stream",
- "state": "uploaded",
- "size": 7048108,
- "download_count": 722639,
- "created_at": "2024-08-08T03:51:05Z",
- "updated_at": "2024-08-08T03:51:08Z",
- "browser_download_url": "https://github.com/wp-cli/wp-cli/releases/download/v999.9.9/wp-cli-999.9.9.phar"
- }
- ],
- "tarball_url": "https://api.github.com/repos/wp-cli/wp-cli/tarball/v999.9.9",
- "zipball_url": "https://api.github.com/repos/wp-cli/wp-cli/zipball/v999.9.9",
- "body": "- Allow manually dispatching tests workflow [[#5965](https://github.com/wp-cli/wp-cli/pull/5965)]\r\n- Add fish shell completion [[#5954](https://github.com/wp-cli/wp-cli/pull/5954)]\r\n- Add defaults and accepted values for runcommand() options in doc [[#5953](https://github.com/wp-cli/wp-cli/pull/5953)]\r\n- Address warnings with filenames ending in fullstop on Windows [[#5951](https://github.com/wp-cli/wp-cli/pull/5951)]\r\n- Fix unit tests [[#5950](https://github.com/wp-cli/wp-cli/pull/5950)]\r\n- Update copyright year in license [[#5942](https://github.com/wp-cli/wp-cli/pull/5942)]\r\n- Fix breaking multi-line CSV values on reading [[#5939](https://github.com/wp-cli/wp-cli/pull/5939)]\r\n- Fix broken Gutenberg test [[#5938](https://github.com/wp-cli/wp-cli/pull/5938)]\r\n- Update docker runner to resolve docker path using `/usr/bin/env` [[#5936](https://github.com/wp-cli/wp-cli/pull/5936)]\r\n- Fix `inherit` path in nested directory [[#5930](https://github.com/wp-cli/wp-cli/pull/5930)]\r\n- Minor docblock improvements [[#5929](https://github.com/wp-cli/wp-cli/pull/5929)]\r\n- Add Signup fetcher [[#5926](https://github.com/wp-cli/wp-cli/pull/5926)]\r\n- Ensure the alias has the leading `@` symbol when added [[#5924](https://github.com/wp-cli/wp-cli/pull/5924)]\r\n- Include any non default hook information in CompositeCommand [[#5921](https://github.com/wp-cli/wp-cli/pull/5921)]\r\n- Correct completion case when ends in = [[#5913](https://github.com/wp-cli/wp-cli/pull/5913)]\r\n- Docs: Fixes for inline comments [[#5912](https://github.com/wp-cli/wp-cli/pull/5912)]\r\n- Update Inline comments [[#5910](https://github.com/wp-cli/wp-cli/pull/5910)]\r\n- Add a real-world example for `wp cli has-command` [[#5908](https://github.com/wp-cli/wp-cli/pull/5908)]\r\n- Fix typos [[#5901](https://github.com/wp-cli/wp-cli/pull/5901)]\r\n- Avoid PHP deprecation notices in PHP 8.1.x [[#5899](https://github.com/wp-cli/wp-cli/pull/5899)]",
- "reactions": {
- "url": "https://api.github.com/repos/wp-cli/wp-cli/releases/169243978/reactions",
- "total_count": 9,
- "+1": 4,
- "-1": 0,
- "laugh": 0,
- "hooray": 1,
- "confused": 0,
- "heart": 0,
- "rocket": 4,
- "eyes": 0
- }
- }
- ]
- """
-
- When I try `wp cli check-update --format=csv`
- Then STDOUT should contain:
- """
- 999.9.9,major,https://github.com/wp-cli/wp-cli/releases/download/v999.9.9/wp-cli-999.9.9.phar,available
- """
-
- Scenario: Mock HTTP request in WordPress
- Given a WP install
- And that HTTP requests to https://api.wordpress.org/core/version-check/1.7/ will respond with:
- """
- HTTP/1.1 200
- Content-Type: application/json
-
- {
- "offers": [
- {
- "response": "latest",
- "download": "https:\/\/downloads.wordpress.org\/release\/wordpress-999.9.9.zip",
- "locale": "en_US",
- "packages": {
- "full": "https:\/\/downloads.wordpress.org\/release\/wordpress-999.9.9.zip",
- "no_content": "https:\/\/downloads.wordpress.org\/release\/wordpress-999.9.9-no-content.zip",
- "new_bundled": "https:\/\/downloads.wordpress.org\/release\/wordpress-999.9.9-new-bundled.zip",
- "partial": false,
- "rollback": false
- },
- "current": "999.9.9",
- "version": "999.9.9",
- "php_version": "7.2.24",
- "mysql_version": "5.5.5",
- "new_bundled": "6.7",
- "partial_version": false
- }
- ],
- "translations": []
- }
- """
-
- When I run `wp core check-update`
- Then STDOUT should be a table containing rows:
- | version | update_type | package_url |
- | 999.9.9 | major | https://downloads.wordpress.org/release/wordpress-999.9.9.zip |
diff --git a/features/steps.feature b/features/steps.feature
deleted file mode 100644
index a1575f40f..000000000
--- a/features/steps.feature
+++ /dev/null
@@ -1,86 +0,0 @@
-Feature: Make sure "Given", "When", "Then" steps work as expected
-
- Scenario: Variable names can only contain uppercase letters, digits and underscores and cannot begin with a digit.
-
- When I run `echo value`
- Then save STDOUT as {VARIABLE_NAME}
- And save STDOUT as {V}
- And save STDOUT as {_VARIABLE_NAME_STARTING_WITH_UNDERSCORE}
- And save STDOUT as {_}
- And save STDOUT as {VARIABLE_NAME_WITH_DIGIT_2}
- And save STDOUT as {V2}
- And save STDOUT as {_2}
- And save STDOUT as {2_VARIABLE_NAME_STARTING_WITH_DIGIT}
- And save STDOUT as {2}
- And save STDOUT as {VARIABLE_NAME_WITH_lowercase}
- And save STDOUT as {v}
- # Note this would give behat "undefined step" message as "save" step uses "\w+"
- #And save STDOUT as {VARIABLE_NAME_WITH_PERCENT_%}
-
- When I run `echo {VARIABLE_NAME}`
- Then STDOUT should match /^value$/
- And STDOUT should be:
- """
- value
- """
-
- When I run `echo {V}`
- Then STDOUT should match /^value$/
-
- When I run `echo {_VARIABLE_NAME_STARTING_WITH_UNDERSCORE}`
- Then STDOUT should match /^value$/
-
- When I run `echo {_}`
- Then STDOUT should match /^value$/
-
- When I run `echo {VARIABLE_NAME_WITH_DIGIT_2}`
- Then STDOUT should match /^value$/
-
- When I run `echo {V2}`
- Then STDOUT should match /^value$/
-
- When I run `echo {_2}`
- Then STDOUT should match /^value$/
-
- When I run `echo {2_VARIABLE_NAME_STARTING_WITH_DIGIT}`
- Then STDOUT should match /^\{2_VARIABLE_NAME_STARTING_WITH_DIGIT}$/
- And STDOUT should contain:
- """
- {
- """
-
- When I run `echo {2}`
- Then STDOUT should match /^\{2}$/
-
- When I run `echo {VARIABLE_NAME_WITH_lowercase}`
- Then STDOUT should match /^\{VARIABLE_NAME_WITH_lowercase}$/
-
- When I run `echo {v}`
- Then STDOUT should match /^\{v}$/
-
- Scenario: Special variables
-
- When I run `echo {INVOKE_WP_CLI_WITH_PHP_ARGS-} cli info`
- Then STDOUT should match /wp cli info/
- And STDERR should be empty
-
- When I run `echo {WP_VERSION-latest}`
- Then STDOUT should match /\d\.\d/
- And STDERR should be empty
-
- Scenario: Nested special variables
- Given an empty directory
- When I run `echo {INVOKE_WP_CLI_WITH_PHP_ARGS--dopen_basedir={RUN_DIR}} cli info`
- Then STDOUT should match /^WP_CLI_PHP_ARGS=-dopen_basedir=.* ?wp cli info/
- And STDERR should be empty
-
- When I run `echo {INVOKE_WP_CLI_WITH_PHP_ARGS--dopen_basedir={RUN_DIR}} eval 'echo "{RUN_DIR}";'`
- Then STDOUT should match /^WP_CLI_PHP_ARGS=-dopen_basedir=(.*)(.*) ?wp eval echo "\1";/
-
- @require-mysql-or-mariadb
- Scenario: SQL related variables
- When I run `echo {MYSQL_BINARY}`
- Then STDOUT should match /(mysql|mariadb)/
-
- When I run `echo {SQL_DUMP_COMMAND}`
- Then STDOUT should match /(mysqldump|mariadb-dump)/
diff --git a/features/testing.feature b/features/testing.feature
deleted file mode 100644
index 47b0377dd..000000000
--- a/features/testing.feature
+++ /dev/null
@@ -1,82 +0,0 @@
-Feature: Test that WP-CLI loads.
-
- Scenario: WP-CLI loads for your tests
- Given a WP install
-
- When I run `wp eval 'echo "Hello world.";'`
- Then STDOUT should contain:
- """
- Hello world.
- """
-
- Scenario: WP Cron is disabled by default
- Given a WP install
- And a test_cron.php file:
- """
-
-
- Custom ruleset for WP-CLI-tests
-
-
-
-
- .
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- tests/phpstan/scan-files.php
-
-
- tests/data/*
- src/PHPStan/*
- tests/tests/PHPStan/*
-
-
-
- tests/data/*
-
-
-
- src/PHPStan/*
-
-
-
-
- */utils/behat-tags\.php$
-
-
- */utils/behat-tags\.php$
-
-
-
-
- */utils/polyfills\.php$
-
-
-
-
- */generate-coverage\.php$
-
-
- */generate-coverage\.php$
-
-
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
deleted file mode 100644
index 3c2a0976c..000000000
--- a/phpstan.neon.dist
+++ /dev/null
@@ -1,27 +0,0 @@
-includes:
- - extension.neon
-parameters:
- level: 6
- paths:
- - src
- - tests
- excludePaths:
- - tests/data
- scanDirectories:
- - vendor/wp-cli/wp-cli
- - vendor/phpunit/php-code-coverage
- - vendor/behat/behat
- scanFiles:
- - tests/phpstan/scan-files.php
- treatPhpDocTypesAsCertain: false
- dynamicConstantNames:
- - WP_DEBUG
- - WP_DEBUG_LOG
- - WP_DEBUG_DISPLAY
- ignoreErrors:
- # Needs fixing in WP-CLI.
- - message: '#Parameter \#1 \$cmd of function WP_CLI\\Utils\\esc_cmd expects array#'
- - message: '#Dynamic call to static method#'
- path: 'tests/tests'
- strictRules:
- strictCalls: true
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
deleted file mode 100644
index b7619f503..000000000
--- a/phpunit.xml.dist
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
- tests/
- tests/tests
-
-
-
-
-
- src
-
-
-
diff --git a/src/Context/FeatureContext.php b/src/Context/FeatureContext.php
deleted file mode 100644
index 3c9dbb0e3..000000000
--- a/src/Context/FeatureContext.php
+++ /dev/null
@@ -1,1833 +0,0 @@
-
- */
- private static $db_settings = [
- 'dbname' => 'wp_cli_test',
- 'dbuser' => 'wp_cli_test',
- 'dbpass' => 'password1',
- 'dbhost' => '127.0.0.1',
- ];
-
- /**
- * What type of database should WordPress use for the test installations. Default to MySQL
- *
- * @var string
- */
- private static $db_type = 'mysql';
-
- /**
- * Name of mysql binary to use (mysql or mariadb). Default to mysql
- *
- * @var string
- */
- private static $mysql_binary = 'mysql';
-
- /**
- * Array of background process ids started by the current scenario. Used to terminate them at the end of the scenario.
- *
- * @var array
- */
- private $running_procs = [];
-
- /**
- * Array of variables available as {VARIABLE_NAME}. Some are always set: CORE_CONFIG_SETTINGS, DB_USER, DB_PASSWORD, DB_HOST, SRC_DIR, CACHE_DIR, WP_VERSION-version-latest.
- * Some are step-dependent: RUN_DIR, SUITE_CACHE_DIR, COMPOSER_LOCAL_REPOSITORY, PHAR_PATH. One is set on use: INVOKE_WP_CLI_WITH_PHP_ARGS-args.
- * Scenarios can define their own variables using "Given save" steps. Variables are reset for each scenario.
- *
- * @var array
- */
- public $variables = [];
-
- /**
- * The current feature file and scenario line number as '.'. Used in RUN_DIR and SUITE_CACHE_DIR directory names. Set at the start of each scenario.
- *
- * @var ?string
- */
- private static $temp_dir_infix;
-
- /**
- * Whether to log run times - WP_CLI_TEST_LOG_RUN_TIMES env var. Set on `@BeforeScenario'.
- *
- * @var false|string
- */
- private static $log_run_times;
-
- /**
- * When the suite started, set on `@BeforeScenario'.
- *
- * @var float
- */
- private static $suite_start_time;
-
- /**
- * Where to output log - stdout|error_log. Set on `@BeforeSuite`.
- *
- * @var string
- */
- private static $output_to;
-
- /**
- * Number of processes/methods to output by longest run times. Set on `@BeforeSuite`.
- *
- * @var int
- */
- private static $num_top_processes;
-
- /**
- * Number of scenarios to output by longest run times. Set on `@BeforeSuite`.
- *
- * @var int
- */
- private static $num_top_scenarios;
-
- /**
- * Scenario run times (top `self::$num_top_scenarios` only).
- *
- * @var array
- */
- private static $scenario_run_times = [];
-
- /**
- * Scenario count, incremented on `@AfterScenario`.
- *
- * @var int
- */
- private static $scenario_count = 0;
-
- /**
- * Array of run time info for proc methods, keyed by method name and arg, each a 2-element array containing run time and run count.
- *
- * @var array
- */
- private static $proc_method_run_times = [];
-
- /**
- * @var array
- */
- private $mocked_requests = [];
-
- /**
- * The current feature.
- *
- * @var \Behat\Gherkin\Node\FeatureNode|null
- */
- private static $feature;
-
- /**
- * The current scenario.
- *
- * @var \Behat\Gherkin\Node\ScenarioInterface|null
- */
- private $scenario;
-
- /**
- * Line of the current step.
- *
- * @var int
- */
- private $step_line = 0;
-
- /**
- * @BeforeFeature
- */
- public static function store_feature( BeforeFeatureScope $scope ): void {
- self::$feature = $scope->getFeature();
- }
-
- /**
- * @BeforeScenario
- */
- public function store_scenario( BeforeScenarioScope $scope ): void {
- $this->scenario = $scope->getScenario();
- }
-
- /**
- * @BeforeStep
- */
- public function store_step( BeforeStepScope $scope ): void {
- $this->step_line = $scope->getStep()->getLine();
- }
-
- /**
- * @AfterScenario
- */
- public function forget_scenario( AfterScenarioScope $scope ): void {
- $this->step_line = 0;
- $this->scenario = null;
- }
-
- /**
- * @AfterFeature
- */
- public static function forget_feature( AfterFeatureScope $scope ): void {
- self::$feature = null;
- }
-
- /**
- * Whether tests are currently running with code coverage collection.
- *
- * @return bool
- */
- private static function running_with_code_coverage() {
- $with_code_coverage = (string) getenv( 'WP_CLI_TEST_COVERAGE' );
-
- return \in_array( $with_code_coverage, [ 'true', '1' ], true );
- }
-
- public static function merge_coverage_reports(): void {
- if ( ! self::running_with_code_coverage() ) {
- return;
- }
-
- $filter = new Filter();
- $coverage = new CodeCoverage(
- // Selector class was only added in v9.1 of the php-code-coverage library.
- class_exists( Selector::class ) ? ( new Selector() )->forLineCoverage( $filter ) : ( new Xdebug() ),
- $filter
- );
-
- foreach ( new DirectoryIterator( self::$behat_run_dir . '/build/logs' ) as $file ) {
- if ( ! $file->isFile() || 'cov' !== $file->getExtension() ) {
- continue;
- }
-
- $coverage->merge( include $file->getPathname() );
- unlink( $file->getPathname() );
- }
-
- ( new Clover() )->process( $coverage, self::$behat_run_dir . '/build/logs/behat-coverage.xml' );
- }
-
- /**
- * Get the path to the Composer vendor folder.
- *
- * @return string Absolute path to the Composer vendor folder.
- */
- public static function get_vendor_dir(): ?string {
- static $vendor_folder = null;
-
- if ( null !== $vendor_folder ) {
- return $vendor_folder;
- }
-
- // We try to detect the vendor folder in the most probable locations.
- $vendor_locations = [
- // wp-cli/wp-cli-tests is a dependency of the current working dir.
- getcwd() . '/vendor',
- // wp-cli/wp-cli-tests is the root project.
- dirname( __DIR__, 2 ) . '/vendor',
- // wp-cli/wp-cli-tests is a dependency.
- dirname( __DIR__, 4 ),
- ];
-
- $vendor_folder = '';
- foreach ( $vendor_locations as $location ) {
- if (
- is_dir( $location )
- && is_readable( $location )
- && is_file( "{$location}/autoload.php" )
- ) {
- $vendor_folder = $location;
- break;
- }
- }
-
- return $vendor_folder;
- }
-
- /**
- * Get the path to the WP-CLI framework folder.
- *
- * @return string Absolute path to the WP-CLI framework folder.
- */
- public static function get_framework_dir(): ?string {
- static $framework_folder = null;
-
- if ( null !== $framework_folder ) {
- return $framework_folder;
- }
-
- $vendor_folder = self::get_vendor_dir();
-
- // Now we need to detect the location of wp-cli/wp-cli package.
- $framework_locations = [
- // wp-cli/wp-cli is the root project.
- dirname( $vendor_folder ),
- // wp-cli/wp-cli is a dependency.
- "{$vendor_folder}/wp-cli/wp-cli",
- ];
-
- $framework_folder = '';
- foreach ( $framework_locations as $location ) {
- if (
- is_dir( $location )
- && is_readable( $location )
- && is_file( "{$location}/php/utils.php" )
- ) {
- $framework_folder = $location;
- break;
- }
- }
-
- return $framework_folder;
- }
-
- /**
- * Get the path to the WP-CLI binary.
- *
- * @return string Absolute path to the WP-CLI binary.
- */
- public static function get_bin_path(): ?string {
- static $bin_path = null;
-
- if ( null !== $bin_path ) {
- return $bin_path;
- }
-
- $bin_path = getenv( 'WP_CLI_BIN_DIR' );
-
- if ( ! empty( $bin_path ) ) {
- return $bin_path;
- }
-
- $bin_paths = [
- self::get_vendor_dir() . '/bin',
- self::get_framework_dir() . '/bin',
- ];
-
- foreach ( $bin_paths as $path ) {
- if ( is_file( "{$path}/wp" ) && is_executable( "{$path}/wp" ) ) {
- $bin_path = $path;
- break;
- }
- }
-
- return $bin_path;
- }
-
- /**
- * Get the environment variables required for launched `wp` processes.
- *
- * @return array
- */
- private static function get_process_env_variables(): array {
- static $env = null;
-
- if ( null !== $env ) {
- return $env;
- }
-
- // Ensure we're using the expected `wp` binary.
- $bin_path = self::get_bin_path();
- self::debug( "WP-CLI binary path: {$bin_path}" );
-
- if ( ! file_exists( "{$bin_path}/wp" ) ) {
- self::debug( "WARNING: No file named 'wp' found in the provided/detected binary path." );
- }
-
- if ( ! is_executable( "{$bin_path}/wp" ) ) {
- self::debug( "WARNING: File named 'wp' found in the provided/detected binary path is not executable." );
- }
-
- $path_separator = Utils\is_windows() ? ';' : ':';
- $env = [
- 'PATH' => $bin_path . $path_separator . getenv( 'PATH' ),
- 'BEHAT_RUN' => 1,
- 'HOME' => sys_get_temp_dir() . '/wp-cli-home',
- 'TEST_RUN_DIR' => self::$behat_run_dir,
- ];
-
- if ( self::running_with_code_coverage() ) {
- $has_coverage_driver = ( new Runtime() )->hasXdebug() || ( new Runtime() )->hasPCOV();
-
- if ( ! $has_coverage_driver ) {
- throw new RuntimeException( 'No coverage driver available. Re-run script with `--xdebug` flag, i.e. `composer behat -- --xdebug`.' );
- }
-
- $coverage_require_file = self::$behat_run_dir . '/vendor/wp-cli/wp-cli-tests/utils/generate-coverage.php';
- if ( ! file_exists( $coverage_require_file ) ) {
- // This file is not vendored inside the wp-cli-tests project
- $coverage_require_file = self::$behat_run_dir . '/utils/generate-coverage.php';
- }
-
- $current = getenv( 'WP_CLI_REQUIRE' );
- $updated = $current ? "{$current},{$coverage_require_file}" : $coverage_require_file;
- $env['WP_CLI_REQUIRE'] = $updated;
- }
-
- $config_path = getenv( 'WP_CLI_CONFIG_PATH' );
- if ( false !== $config_path ) {
- $env['WP_CLI_CONFIG_PATH'] = $config_path;
- }
-
- $allow_root = getenv( 'WP_CLI_ALLOW_ROOT' );
- if ( false !== $allow_root ) {
- $env['WP_CLI_ALLOW_ROOT'] = $allow_root;
- }
-
- $term = getenv( 'TERM' );
- if ( false !== $term ) {
- $env['TERM'] = $term;
- }
-
- $php_args = getenv( 'WP_CLI_PHP_ARGS' );
- if ( false !== $php_args ) {
- $env['WP_CLI_PHP_ARGS'] = $php_args;
- }
-
- $php_used = getenv( 'WP_CLI_PHP_USED' );
- if ( false !== $php_used ) {
- $env['WP_CLI_PHP_USED'] = $php_used;
- }
-
- $php = getenv( 'WP_CLI_PHP' );
- if ( false !== $php ) {
- $env['WP_CLI_PHP'] = $php;
- }
-
- $travis_build_dir = getenv( 'TRAVIS_BUILD_DIR' );
- if ( false !== $travis_build_dir ) {
- $env['TRAVIS_BUILD_DIR'] = $travis_build_dir;
- }
-
- // Dump environment for debugging purposes, but before adding the GitHub token.
- self::debug( 'Environment:' );
- foreach ( $env as $key => $value ) {
- self::debug( " [{$key}] => {$value}" );
- }
-
- $github_token = getenv( 'GITHUB_TOKEN' );
- if ( false !== $github_token ) {
- $env['GITHUB_TOKEN'] = $github_token;
- }
-
- return $env;
- }
-
- /**
- * Get the internal variables to use within tests.
- *
- * @return array Associative array of internal variables that will be mapped
- * into tests.
- */
- private static function get_behat_internal_variables(): array {
- static $variables = null;
-
- if ( null !== $variables ) {
- return $variables;
- }
-
- $paths = [
- dirname( __DIR__, 4 ) . '/wp-cli/wp-cli/VERSION',
- dirname( __DIR__, 5 ) . '/VERSION',
- dirname( __DIR__, 2 ) . '/vendor/wp-cli/wp-cli/VERSION',
- ];
-
- $framework_root = dirname( __DIR__, 2 );
- foreach ( $paths as $path ) {
- if ( file_exists( $path ) ) {
- $framework_root = (string) realpath( dirname( $path ) );
- break;
- }
- }
-
- $variables = [
- 'FRAMEWORK_ROOT' => realpath( $framework_root ),
- 'SRC_DIR' => realpath( dirname( __DIR__, 2 ) ),
- 'PROJECT_DIR' => realpath( dirname( __DIR__, 5 ) ),
- ];
-
- return $variables;
- }
-
- /**
- * Download and extract a single copy of the sqlite-database-integration plugin
- * for use in subsequent WordPress copies
- *
- * @param string $dir
- */
- private static function download_sqlite_plugin( $dir ): void {
- $download_url = 'https://downloads.wordpress.org/plugin/sqlite-database-integration.zip';
- $download_location = $dir . '/sqlite-database-integration.zip';
-
- if ( ! is_dir( $dir ) ) {
- mkdir( $dir );
- }
-
- Process::create(
- Utils\esc_cmd(
- 'curl -sSfL %1$s > %2$s',
- $download_url,
- $download_location
- )
- )->run_check();
-
- $zip = new \ZipArchive();
- $new_zip_file = $download_location;
-
- if ( $zip->open( $new_zip_file ) === true ) {
- if ( $zip->extractTo( $dir ) ) {
- $zip->close();
- unlink( $new_zip_file );
- } else {
- $error_message = $zip->getStatusString();
- throw new RuntimeException( sprintf( 'Failed to extract files from the zip: %s', $error_message ) );
- }
- } else {
- $error_message = $zip->getStatusString();
- throw new RuntimeException( sprintf( 'Failed to open the zip file: %s', $error_message ) );
- }
- }
-
- /**
- * Given a WordPress installation with the sqlite-database-integration plugin,
- * configure it to use SQLite as the database by placing the db.php dropin file
- *
- * @param string $dir
- */
- private static function configure_sqlite( $dir ): void {
- $db_copy = $dir . '/wp-content/mu-plugins/sqlite-database-integration/db.copy';
- $db_dropin = $dir . '/wp-content/db.php';
-
- /* similar to https://github.com/WordPress/sqlite-database-integration/blob/3306576c9b606bc23bbb26c15383fef08e03ab11/activate.php#L95 */
- $file_contents = str_replace(
- array(
- '\'{SQLITE_IMPLEMENTATION_FOLDER_PATH}\'',
- '{SQLITE_PLUGIN}',
- '/plugins/',
- ),
- array(
- '__DIR__ . \'mu-plugins/sqlite-database-integration\'',
- 'sqlite-database-integration/load.php',
- '/mu-plugins/',
- ),
- file_get_contents( $db_copy )
- );
-
- file_put_contents( $db_dropin, $file_contents );
- }
-
- /**
- * We cache the results of `wp core download` to improve test performance.
- * Ideally, we'd cache at the HTTP layer for more reliable tests.
- *
- * @param string $version
- */
- private static function cache_wp_files( $version = '' ): void {
- $wp_version = $version ?: getenv( 'WP_VERSION' );
- $wp_version_suffix = $wp_version ? "-$wp_version" : '';
- self::$cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-download-cache' . $wp_version_suffix;
- self::$sqlite_cache_dir = sys_get_temp_dir() . '/wp-cli-test-sqlite-integration-cache';
-
- if ( 'sqlite' === getenv( 'WP_CLI_TEST_DBTYPE' ) ) {
- if ( ! is_readable( self::$sqlite_cache_dir . '/sqlite-database-integration/db.copy' ) ) {
- self::download_sqlite_plugin( self::$sqlite_cache_dir );
- }
- }
-
- if ( is_readable( self::$cache_dir . '/wp-config-sample.php' ) ) {
- return;
- }
-
- $cmd = Utils\esc_cmd( 'wp core download --force --path=%s', self::$cache_dir );
- if ( $wp_version ) {
- $cmd .= Utils\esc_cmd( ' --version=%s', $wp_version );
- }
- Process::create( $cmd, null, self::get_process_env_variables() )->run_check();
- }
-
- /**
- * @BeforeSuite
- */
- public static function prepare( BeforeSuiteScope $scope ): void {
- self::bootstrap_feature_context();
-
- // Test performance statistics - useful for detecting slow tests.
- self::$log_run_times = getenv( 'WP_CLI_TEST_LOG_RUN_TIMES' );
- if ( false !== self::$log_run_times ) {
- self::log_run_times_before_suite( $scope );
- }
- self::$behat_run_dir = getcwd();
- self::$mysql_binary = Utils\get_mysql_binary_path();
-
- $result = Process::create( 'wp cli info', null, self::get_process_env_variables() )->run_check();
- echo "{$result->stdout}\n";
-
- // Remove install cache if any (not setting the static var).
- $wp_version = getenv( 'WP_VERSION' );
- $wp_version_suffix = ( false !== $wp_version ) ? "-$wp_version" : '';
- $install_cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-install-cache' . $wp_version_suffix;
- if ( file_exists( $install_cache_dir ) ) {
- self::remove_dir( $install_cache_dir );
- }
-
- if ( getenv( 'WP_CLI_TEST_DEBUG_BEHAT_ENV' ) ) {
- exit;
- }
- }
-
- /**
- * @AfterSuite
- */
- public static function afterSuite( AfterSuiteScope $scope ): void {
- if ( self::$composer_local_repository ) {
- self::remove_dir( self::$composer_local_repository );
- self::$composer_local_repository = null;
- }
-
- if ( self::$log_run_times ) {
- self::log_run_times_after_suite( $scope );
- }
-
- self::merge_coverage_reports();
- }
-
- /**
- * @BeforeScenario
- */
- public function beforeScenario( BeforeScenarioScope $scope ): void {
- if ( self::$log_run_times ) {
- self::log_run_times_before_scenario( $scope );
- }
- $this->variables = array_merge(
- $this->variables,
- self::get_behat_internal_variables()
- );
-
- $mysql_binary = Utils\get_mysql_binary_path();
- $sql_dump_command = Utils\get_sql_dump_command();
-
- $this->variables['MYSQL_BINARY'] = $mysql_binary;
- $this->variables['SQL_DUMP_COMMAND'] = $sql_dump_command;
-
- // Used in the names of the RUN_DIR and SUITE_CACHE_DIR directories.
- self::$temp_dir_infix = null;
- $file = self::get_event_file( $scope, $line );
- if ( isset( $file ) ) {
- self::$temp_dir_infix = basename( $file ) . '.' . $line;
- }
- }
-
- /**
- * @AfterScenario
- */
- public function afterScenario( AfterScenarioScope $scope ): void {
-
- if ( self::$run_dir ) {
- // Remove altered WP install, unless there's an error.
- if ( $scope->getTestResult()->getResultCode() <= 10 ) {
- self::remove_dir( self::$run_dir );
- }
- self::$run_dir = null;
- }
-
- // Remove WP-CLI package directory if any. Set to `wp package path` by package-command and scaffold-package-command features, and by cli-info.feature.
- if ( isset( $this->variables['PACKAGE_PATH'] ) ) {
- self::remove_dir( $this->variables['PACKAGE_PATH'] );
- }
-
- // Remove SUITE_CACHE_DIR if any.
- if ( self::$suite_cache_dir ) {
- self::remove_dir( self::$suite_cache_dir );
- self::$suite_cache_dir = null;
- }
-
- // Remove global config file if any.
- $env = self::get_process_env_variables();
- if ( isset( $env['HOME'] ) && file_exists( "{$env['HOME']}/.wp-cli/config.yml" ) ) {
- unlink( "{$env['HOME']}/.wp-cli/config.yml" );
- }
-
- // Remove any background processes.
- foreach ( $this->running_procs as $proc ) {
- $status = proc_get_status( $proc );
- self::terminate_proc( $status['pid'] );
- }
-
- if ( self::$log_run_times ) {
- self::log_run_times_after_scenario( $scope );
- }
- }
-
- /**
- * Terminate a process and any of its children.
- *
- * @param int $master_pid
- */
- private static function terminate_proc( $master_pid ): void {
-
- $output = shell_exec( "ps -o ppid,pid,command | grep $master_pid" );
-
- foreach ( explode( PHP_EOL, $output ? $output : '' ) as $line ) {
- if ( preg_match( '/^\s*(\d+)\s+(\d+)/', $line, $matches ) ) {
- $parent = $matches[1];
- $child = $matches[2];
-
- if ( (int) $parent === (int) $master_pid ) {
- self::terminate_proc( (int) $child );
- }
- }
- }
-
- if ( ! posix_kill( (int) $master_pid, 9 ) ) {
- $errno = posix_get_last_error();
- // Ignore "No such process" error as that's what we want.
- if ( 3 /*ESRCH*/ !== $errno ) {
- throw new RuntimeException( posix_strerror( $errno ) );
- }
- }
- }
-
- /**
- * Create a temporary WP_CLI_CACHE_DIR. Exposed as SUITE_CACHE_DIR in "Given an empty cache" step.
- */
- public static function create_cache_dir(): string {
- if ( self::$suite_cache_dir ) {
- self::remove_dir( self::$suite_cache_dir );
- }
- self::$suite_cache_dir = sys_get_temp_dir() . '/' . uniqid( 'wp-cli-test-suite-cache-' . self::$temp_dir_infix . '-', true );
- mkdir( self::$suite_cache_dir );
- return self::$suite_cache_dir;
- }
-
- /**
- * Initializes context.
- * Every scenario gets its own context object.
- */
- public function __construct() {
- if ( getenv( 'WP_CLI_TEST_DBROOTUSER' ) ) {
- $this->variables['DB_ROOT_USER'] = getenv( 'WP_CLI_TEST_DBROOTUSER' );
- }
-
- if ( false !== getenv( 'WP_CLI_TEST_DBROOTPASS' ) ) {
- $this->variables['DB_ROOT_PASSWORD'] = getenv( 'WP_CLI_TEST_DBROOTPASS' );
- }
-
- if ( getenv( 'WP_CLI_TEST_DBNAME' ) ) {
- $this->variables['DB_NAME'] = getenv( 'WP_CLI_TEST_DBNAME' );
- } else {
- $this->variables['DB_NAME'] = 'wp_cli_test';
- }
-
- if ( getenv( 'WP_CLI_TEST_DBUSER' ) ) {
- $this->variables['DB_USER'] = getenv( 'WP_CLI_TEST_DBUSER' );
- } else {
- $this->variables['DB_USER'] = 'wp_cli_test';
- }
-
- if ( false !== getenv( 'WP_CLI_TEST_DBPASS' ) ) {
- $this->variables['DB_PASSWORD'] = getenv( 'WP_CLI_TEST_DBPASS' );
- } else {
- $this->variables['DB_PASSWORD'] = 'password1';
- }
-
- if ( getenv( 'WP_CLI_TEST_DBHOST' ) ) {
- $this->variables['DB_HOST'] = getenv( 'WP_CLI_TEST_DBHOST' );
- } else {
- $this->variables['DB_HOST'] = 'localhost';
- }
-
- if ( getenv( 'WP_CLI_TEST_DBTYPE' ) ) {
- $this->variables['DB_TYPE'] = getenv( 'WP_CLI_TEST_DBTYPE' );
- } else {
- $this->variables['DB_TYPE'] = 'mysql';
- }
-
- if ( getenv( 'MYSQL_TCP_PORT' ) ) {
- $this->variables['MYSQL_PORT'] = getenv( 'MYSQL_TCP_PORT' );
- }
-
- if ( getenv( 'MYSQL_HOST' ) ) {
- $this->variables['MYSQL_HOST'] = getenv( 'MYSQL_HOST' );
- }
-
- if ( getenv( 'WP_CLI_TEST_DBSOCKET' ) ) {
- $this->variables['DB_SOCKET'] = getenv( 'WP_CLI_TEST_DBSOCKET' );
- }
-
- self::$db_settings['dbname'] = $this->variables['DB_NAME'];
- self::$db_settings['dbuser'] = $this->variables['DB_USER'];
- self::$db_settings['dbpass'] = $this->variables['DB_PASSWORD'];
- self::$db_settings['dbhost'] = $this->variables['DB_HOST'];
-
- self::$db_type = $this->variables['DB_TYPE'];
-
- $this->variables['CORE_CONFIG_SETTINGS'] = Utils\assoc_args_to_str( self::$db_settings );
-
- $this->test_connection();
- $this->drop_db();
- $this->set_cache_dir();
- }
-
- /**
- * @param string $message
- */
- protected static function debug( $message ): void { // phpcs:ignore Universal.Files.SeparateFunctionsFromOO.Mixed
- if ( ! getenv( 'WP_CLI_TEST_DEBUG_BEHAT_ENV' ) ) {
- return;
- }
-
- echo "{$message}\n";
- }
-
- /**
- * Load required support files as needed before heading into the Behat context.
- */
- protected static function bootstrap_feature_context(): void {
- $vendor_folder = self::get_vendor_dir();
- self::debug( "Vendor folder location: {$vendor_folder}" );
-
- // Didn't manage to detect a valid vendor folder.
- if ( empty( $vendor_folder ) ) {
- return;
- }
-
- // We assume the vendor folder is located in the project root folder.
- $project_folder = dirname( $vendor_folder );
-
- $framework_folder = self::get_framework_dir();
- self::debug( "Framework folder location: {$framework_folder}" );
-
- // Load helper functionality that is needed for the tests.
- require_once "{$framework_folder}/php/utils.php";
- require_once "{$framework_folder}/php/WP_CLI/Process.php";
- require_once "{$framework_folder}/php/WP_CLI/ProcessRun.php";
-
- // Manually load Composer file includes by generating a config with require:
- // statements for each file.
- $project_composer = "{$project_folder}/composer.json";
- if ( ! file_exists( $project_composer ) ) {
- return;
- }
-
- $composer = json_decode( file_get_contents( $project_composer ) );
- if ( empty( $composer->autoload->files ) ) {
- return;
- }
-
- $contents = "require:\n";
- foreach ( $composer->autoload->files as $file ) {
- $contents .= " - {$project_folder}/{$file}\n";
- }
-
- $temp_folder = sys_get_temp_dir() . '/wp-cli-package-test';
- if (
- ! is_dir( $temp_folder )
- && ! mkdir( $temp_folder )
- && ! is_dir( $temp_folder )
- ) {
- return;
- }
-
- $project_config = "{$temp_folder}/config.yml";
- file_put_contents( $project_config, $contents );
- putenv( 'WP_CLI_CONFIG_PATH=' . $project_config );
-
- self::debug( "Project config file location: {$project_config}" );
- self::debug( "Project config:\n{$contents}" );
- }
-
- /**
- * Replace standard {VARIABLE_NAME} variables and the special {INVOKE_WP_CLI_WITH_PHP_ARGS-args} and {WP_VERSION-version-latest} variables.
- * Note that standard variable names can only contain uppercase letters, digits and underscores and cannot begin with a digit.
- *
- * @param string $str
- * @return string
- */
- public function replace_variables( $str ) {
- $str = preg_replace_callback( '/\{([A-Z_][A-Z_0-9]*)\}/', [ $this, 'replace_var' ], $str );
- if ( false !== strpos( $str, '{INVOKE_WP_CLI_WITH_PHP_ARGS-' ) ) {
- $str = $this->replace_invoke_wp_cli_with_php_args( $str );
- }
- if ( false !== strpos( $str, '{WP_VERSION-' ) ) {
- $str = $this->replace_wp_versions( $str );
- }
- return $str;
- }
-
- /**
- * Substitute {INVOKE_WP_CLI_WITH_PHP_ARGS-args} variables.
- *
- * @param string $str
- * @return string
- */
- private function replace_invoke_wp_cli_with_php_args( $str ) {
- static $phar_path = null, $shell_path = null;
-
- if ( null === $phar_path ) {
- $phar_path = false;
- $phar_begin = '#!/usr/bin/env php';
- $phar_begin_len = strlen( $phar_begin );
- $bin_dir = getenv( 'WP_CLI_BIN_DIR' );
- if ( false !== $bin_dir && file_exists( $bin_dir . '/wp' ) && file_get_contents( $bin_dir . '/wp', false, null, 0, $phar_begin_len ) === $phar_begin ) {
- $phar_path = $bin_dir . '/wp';
- } else {
- $src_dir = dirname( __DIR__, 2 );
- $bin_path = $src_dir . '/bin/wp';
- $vendor_bin_path = $src_dir . '/vendor/bin/wp';
- if ( file_exists( $bin_path ) && is_executable( $bin_path ) ) {
- $shell_path = $bin_path;
- } elseif ( file_exists( $vendor_bin_path ) && is_executable( $vendor_bin_path ) ) {
- $shell_path = $vendor_bin_path;
- } else {
- $shell_path = 'wp';
- }
- }
- }
-
- $str = preg_replace_callback(
- '/{INVOKE_WP_CLI_WITH_PHP_ARGS-(.*)}/',
- static function ( $matches ) use ( $phar_path, $shell_path ) {
- return $phar_path ? "php {$matches[1]} {$phar_path}" : ( 'WP_CLI_PHP_ARGS=' . escapeshellarg( $matches[1] ) . ' ' . $shell_path );
- },
- $str
- );
-
- return $str;
- }
-
- /**
- * Replace variables callback.
- *
- * @param array $matches
- * @return string
- */
- private function replace_var( $matches ) {
- $str = $matches[0];
-
- foreach ( array_slice( $matches, 1 ) as $key ) {
- $str = str_replace(
- '{' . $key . '}',
- array_key_exists( $key, $this->variables ) ? $this->variables[ $key ] : '',
- $str
- );
- }
-
- return $str;
- }
-
- /**
- * Substitute {WP_VERSION-version-latest} variables.
- *
- * @param string $str
- * @return string
- */
- private function replace_wp_versions( $str ): string {
- static $wp_versions = null;
- if ( null === $wp_versions ) {
- $wp_versions = [];
-
- $wp_org_api = new WpOrgApi();
- $result = $wp_org_api->get_core_version_check();
-
- if ( is_array( $result ) && ! empty( $result['offers'] ) ) {
- // Latest version alias.
- $wp_versions['{WP_VERSION-latest}'] = count( $result['offers'] ) ? $result['offers'][0]['version'] : '';
- foreach ( $result['offers'] as $offer ) {
- $sub_ver = preg_replace( '/(^[0-9]+\.[0-9]+)\.[0-9]+$/', '$1', $offer['version'] );
- $sub_ver_key = "{WP_VERSION-{$sub_ver}-latest}";
-
- $main_ver = preg_replace( '/(^[0-9]+)\.[0-9]+$/', '$1', $sub_ver );
- $main_ver_key = "{WP_VERSION-{$main_ver}-latest}";
-
- if ( ! isset( $wp_versions[ $main_ver_key ] ) ) {
- $wp_versions[ $main_ver_key ] = $offer['version'];
- }
- if ( ! isset( $wp_versions[ $sub_ver_key ] ) ) {
- $wp_versions[ $sub_ver_key ] = $offer['version'];
- }
- }
- }
- }
- return strtr( $str, $wp_versions );
- }
-
- /**
- * Get the file and line number for the current behat scope.
- *
- * @param ScenarioScope|FeatureScope|OutlineTested $scope
- * @param int $line
- *
- * @param-out int $line
- */
- private static function get_event_file( $scope, &$line ): ?string {
- if ( method_exists( $scope, 'getScenario' ) ) {
- $scenario_feature = $scope->getScenario();
- } elseif ( method_exists( $scope, 'getFeature' ) ) {
- $scenario_feature = $scope->getFeature();
- } elseif ( method_exists( $scope, 'getOutline' ) ) {
- $scenario_feature = $scope->getOutline();
- } else {
- return null;
- }
-
- $line = $scenario_feature->getLine();
-
- if ( ! method_exists( $scenario_feature, 'getFile' ) ) {
- return null;
- }
-
- return $scenario_feature->getFile();
- }
-
- /**
- * Create the RUN_DIR directory, unless already set for this scenario.
- */
- public function create_run_dir(): void {
- if ( ! isset( $this->variables['RUN_DIR'] ) ) {
- self::$run_dir = sys_get_temp_dir() . '/' . uniqid( 'wp-cli-test-run-' . self::$temp_dir_infix . '-', true );
- $this->variables['RUN_DIR'] = self::$run_dir;
- mkdir( $this->variables['RUN_DIR'] );
- }
- }
-
- /**
- * @param string $version
- */
- public function build_phar( $version = 'same' ): void {
- $this->variables['PHAR_PATH'] = $this->variables['RUN_DIR'] . '/' . uniqid( 'wp-cli-build-', true ) . '.phar';
-
- $is_bundle = false;
-
- // Test running against a package installed as a WP-CLI dependency
- // WP-CLI bundle installed as a project dependency
- $make_phar_path = self::get_vendor_dir() . '/wp-cli/wp-cli-bundle/utils/make-phar.php';
- if ( ! file_exists( $make_phar_path ) ) {
- // Running against WP-CLI bundle proper
- $is_bundle = true;
-
- $make_phar_path = self::get_vendor_dir() . '/../utils/make-phar.php';
- }
-
- // Temporarily modify the Composer autoloader used within the Phar
- // so that it doesn't clash if autoloading is already happening outside of it,
- // for example when generating code coverage.
- // This modifies composer.json.
- if ( $is_bundle && self::running_with_code_coverage() ) {
- $this->composer_command( 'config autoloader-suffix "WpCliTestsPhar" --working-dir=' . dirname( self::get_vendor_dir() ) );
- $this->composer_command( 'dump-autoload --working-dir=' . dirname( self::get_vendor_dir() ) );
- }
-
- $this->proc(
- Utils\esc_cmd(
- 'php -dphar.readonly=0 %1$s %2$s --version=%3$s && chmod +x %2$s',
- $make_phar_path,
- $this->variables['PHAR_PATH'],
- $version
- )
- )->run_check();
-
- // Revert the suffix change again
- if ( $is_bundle && self::running_with_code_coverage() ) {
- $this->composer_command( 'config autoloader-suffix "WpCliBundle" --working-dir=' . dirname( self::get_vendor_dir() ) );
- $this->composer_command( 'dump-autoload --working-dir=' . dirname( self::get_vendor_dir() ) );
- }
- }
-
- /**
- * @param string $version
- */
- public function download_phar( $version = 'same' ): void {
- if ( 'same' === $version ) {
- $version = WP_CLI_VERSION;
- }
-
- $download_url = sprintf(
- 'https://github.com/wp-cli/wp-cli/releases/download/v%1$s/wp-cli-%1$s.phar',
- $version
- );
-
- $this->variables['PHAR_PATH'] = $this->variables['RUN_DIR'] . '/'
- . uniqid( 'wp-cli-download-', true )
- . '.phar';
-
- Process::create(
- Utils\esc_cmd(
- 'curl -sSfL %1$s > %2$s && chmod +x %2$s',
- $download_url,
- $this->variables['PHAR_PATH']
- )
- )->run_check();
- }
-
- /**
- * CACHE_DIR is a cache for downloaded test data such as images. Lives until manually deleted.
- */
- private function set_cache_dir(): void {
- $path = sys_get_temp_dir() . '/wp-cli-test-cache';
- if ( ! file_exists( $path ) ) {
- mkdir( $path );
- }
- $this->variables['CACHE_DIR'] = $path;
- }
-
- /**
- * Run a MySQL command with `$db_settings`.
- *
- * @param string $sql_cmd Command to run.
- * @param array $assoc_args Optional. Associative array of options. Default empty.
- * @param bool $add_database Optional. Whether to add dbname to the $sql_cmd. Default false.
- * @return array{stdout: string, stderr: string, exit_code: int}
- */
- private static function run_sql( $sql_cmd, $assoc_args = [], $add_database = false ) {
- $default_assoc_args = [
- 'host' => self::$db_settings['dbhost'],
- 'user' => self::$db_settings['dbuser'],
- 'pass' => self::$db_settings['dbpass'],
- ];
- if ( $add_database ) {
- $sql_cmd .= ' ' . escapeshellarg( self::$db_settings['dbname'] );
- }
- $send_to_shell = true;
- if ( isset( $assoc_args['send_to_shell'] ) ) {
- $send_to_shell = (bool) $assoc_args['send_to_shell'];
- unset( $assoc_args['send_to_shell'] );
- }
-
- $start_time = microtime( true );
- $result = Utils\run_mysql_command( $sql_cmd, array_merge( $assoc_args, $default_assoc_args ), null, $send_to_shell );
- if ( self::$log_run_times ) {
- self::log_proc_method_run_time( 'run_sql ' . $sql_cmd, $start_time );
- }
-
- return array_combine( [ 'stdout', 'stderr', 'exit_code' ], $result );
- }
-
- public function create_db(): void {
- if ( 'sqlite' === self::$db_type ) {
- return;
- }
-
- $dbname = self::$db_settings['dbname'];
- self::run_sql( self::$mysql_binary . ' --no-defaults', [ 'execute' => "CREATE DATABASE IF NOT EXISTS $dbname" ] );
- }
-
- /**
- * Test if the database connection is working.
- */
- public function test_connection(): void {
- if ( 'sqlite' === self::$db_type ) {
- return;
- }
-
- $sql_result = self::run_sql(
- self::$mysql_binary . ' --no-defaults',
- [
- 'execute' => 'SELECT 1',
- 'send_to_shell' => false,
- ]
- );
-
- if ( 0 !== $sql_result['exit_code'] ) {
- # WP_CLI output functions are suppressed in behat context.
- echo 'There was an error connecting to the database:' . \PHP_EOL;
- if ( ! empty( $sql_result['stderr'] ) ) {
- echo ' ' . trim( $sql_result['stderr'] ) . \PHP_EOL;
- }
- echo 'run `composer prepare-tests` to connect to the database.' . \PHP_EOL;
- die( $sql_result['exit_code'] );
- } elseif ( ! empty( $sql_result['stderr'] ) ) {
- // There is "error" output but not an exit code.
- // Probably a warning, still display it.
- echo trim( $sql_result['stderr'] ) . \PHP_EOL;
- }
- }
-
- public function drop_db(): void {
- if ( 'sqlite' === self::$db_type ) {
- return;
- }
- $dbname = self::$db_settings['dbname'];
- self::run_sql( self::$mysql_binary . ' --no-defaults', [ 'execute' => "DROP DATABASE IF EXISTS $dbname" ] );
- }
-
- /**
- * @param string $command
- * @param array $assoc_args
- * @param string $path
- * @return Process
- */
- public function proc( $command, $assoc_args = [], $path = '' ): Process {
- if ( ! empty( $assoc_args ) ) {
- $command .= Utils\assoc_args_to_str( $assoc_args );
- }
-
- $env = self::get_process_env_variables();
-
- if ( isset( $this->variables['SUITE_CACHE_DIR'] ) ) {
- $env['WP_CLI_CACHE_DIR'] = $this->variables['SUITE_CACHE_DIR'];
- }
-
- if ( isset( $this->variables['PROJECT_DIR'] ) ) {
- $env['BEHAT_PROJECT_DIR'] = $this->variables['PROJECT_DIR'];
- }
-
- if ( self::$feature ) {
- $env['BEHAT_FEATURE_TITLE'] = self::$feature->getTitle();
- }
-
- if ( $this->scenario ) {
- $env['BEHAT_SCENARIO_TITLE'] = $this->scenario->getTitle();
- }
-
- $env['BEHAT_STEP_LINE'] = $this->step_line;
-
- $env['WP_CLI_TEST_DBTYPE'] = self::$db_type;
-
- if ( isset( $this->variables['RUN_DIR'] ) ) {
- $cwd = "{$this->variables['RUN_DIR']}/{$path}";
- } else {
- $cwd = null;
- }
-
- return Process::create( $command, $cwd, $env );
- }
-
- /**
- * Start a background process. Will automatically be closed when the tests finish.
- *
- * @param string $cmd
- */
- public function background_proc( $cmd ): void {
- $descriptors = [
- 0 => STDIN,
- 1 => [ 'pipe', 'w' ],
- 2 => [ 'pipe', 'w' ],
- ];
-
- $proc = proc_open( $cmd, $descriptors, $pipes, $this->variables['RUN_DIR'], self::get_process_env_variables() );
-
- sleep( 1 );
-
- $status = proc_get_status( $proc );
-
- if ( ! $status['running'] ) {
- $stderr = is_resource( $pipes[2] ) ? ( ': ' . stream_get_contents( $pipes[2] ) ) : '';
- throw new RuntimeException( sprintf( "Failed to start background process '%s'%s.", $cmd, $stderr ) );
- }
-
- $this->running_procs[] = $proc;
- }
-
- /**
- * @param string $src
- * @param string $dest
- */
- public function move_files( $src, $dest ): void {
- rename( $this->variables['RUN_DIR'] . "/$src", $this->variables['RUN_DIR'] . "/$dest" );
- }
-
- /**
- * Remove a directory (recursive).
- *
- * @param string $dir
- */
- public static function remove_dir( $dir ): void {
- Process::create( Utils\esc_cmd( 'rm -rf %s', $dir ) )->run_check();
- }
-
- /**
- * Copy a directory (recursive). Destination directory must exist.
- *
- * @param string $src_dir
- * @param string $dest_dir
- */
- public static function copy_dir( $src_dir, $dest_dir ): void {
- $shell_command = ( 'Darwin' === PHP_OS )
- ? Utils\esc_cmd( 'cp -R %s/* %s', $src_dir, $dest_dir )
- : Utils\esc_cmd( 'cp -r %s/* %s', $src_dir, $dest_dir );
- Process::create( $shell_command )->run_check();
- }
-
- /**
- * @param string $wp_config_code
- * @param string $line
- */
- public function add_line_to_wp_config( &$wp_config_code, $line ): void {
- $token = "/* That's all, stop editing!";
-
- $wp_config_code = str_replace( $token, "$line\n\n$token", $wp_config_code );
- }
-
- /**
- * @param string $subdir
- * @param string $version
- */
- public function download_wp( $subdir = '', $version = '' ): void {
- $wp_version = $version ?: getenv( 'WP_VERSION' );
- $wp_version_suffix = $wp_version ? "-$wp_version" : '';
- $expected_cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-download-cache' . $wp_version_suffix;
-
- if ( ! self::$cache_dir || self::$cache_dir !== $expected_cache_dir ) {
- self::cache_wp_files( $version );
-
- $result = Process::create( Utils\esc_cmd( 'wp core version --debug --path=%s', self::$cache_dir ), null, self::get_process_env_variables() )->run_check();
- echo "[Debug messages]\n";
- echo "{$result->stderr}\n";
-
- echo "WordPress {$result->stdout}\n";
- }
-
- $dest_dir = $this->variables['RUN_DIR'] . "/$subdir";
-
- if ( $subdir ) {
- mkdir( $dest_dir );
- }
-
- self::copy_dir( self::$cache_dir, $dest_dir );
-
- if ( ! is_dir( $dest_dir . '/wp-content/mu-plugins' ) ) {
- mkdir( $dest_dir . '/wp-content/mu-plugins' );
- }
-
- // Disable emailing.
- copy( dirname( __DIR__, 2 ) . '/utils/no-mail.php', $dest_dir . '/wp-content/mu-plugins/no-mail.php' );
-
- // Add polyfills.
- copy( dirname( __DIR__, 2 ) . '/utils/polyfills.php', $dest_dir . '/wp-content/mu-plugins/polyfills.php' );
-
- if ( 'sqlite' === self::$db_type ) {
- self::copy_dir( self::$sqlite_cache_dir, $dest_dir . '/wp-content/mu-plugins' );
- self::configure_sqlite( $dest_dir );
- }
- }
-
- /**
- * Create a wp-config.php file.
- *
- * @param string $subdir
- * @param string|false $extra_php
- */
- public function create_config( $subdir = '', $extra_php = false ): void {
- $params = self::$db_settings;
-
- // Replaces all characters that are not alphanumeric or an underscore into an underscore.
- $params['dbprefix'] = $subdir ? preg_replace( '#[^a-zA-Z\_0-9]#', '_', $subdir ) : 'wp_';
-
- $params['skip-salts'] = true;
-
- // Do not check database connection if running SQLite as the check would fail.
- if ( 'sqlite' === self::$db_type ) {
- $params['skip-check'] = true;
- }
-
- if ( false !== $extra_php ) {
- $params['extra-php'] = $extra_php;
- }
-
- $run_dir = '' !== $subdir ? ( $this->variables['RUN_DIR'] . "/$subdir" ) : $this->variables['RUN_DIR'];
- $config_cache_path = '';
- if ( self::$install_cache_dir ) {
- $config_cache_path = self::$install_cache_dir . '/config_' . md5( implode( ':', $params ) . ':subdir=' . $subdir );
- }
-
- if ( $config_cache_path && file_exists( $config_cache_path ) ) {
- copy( $config_cache_path, $run_dir . '/wp-config.php' );
- } else {
- $this->proc( 'wp config create', $params, $subdir )->run_check();
- if ( $config_cache_path && file_exists( $run_dir . '/wp-config.php' ) ) {
- copy( $run_dir . '/wp-config.php', $config_cache_path );
- }
- }
- }
-
- /**
- * @param string $subdir
- * @param string $version
- */
- public function install_wp( $subdir = '', $version = '' ): void {
- $wp_version = $version ?: getenv( 'WP_VERSION' );
- $wp_version_suffix = $wp_version ? "-$wp_version" : '';
- self::$install_cache_dir = sys_get_temp_dir() . '/wp-cli-test-core-install-cache' . $wp_version_suffix;
- if ( ! file_exists( self::$install_cache_dir ) ) {
- mkdir( self::$install_cache_dir );
- }
-
- $subdir = $this->replace_variables( $subdir );
-
- // Disable WP Cron by default to avoid bogus HTTP requests in CLI context.
- $config_extra_php = "if ( ! defined( 'DISABLE_WP_CRON' ) ) { define( 'DISABLE_WP_CRON', true ); }\n";
-
- if ( 'sqlite' !== self::$db_type ) {
- $this->create_db();
- }
- $this->create_run_dir();
- $this->download_wp( $subdir, $version );
- $this->create_config( $subdir, $config_extra_php );
-
- $install_args = [
- 'url' => 'https://example.com',
- 'title' => 'WP CLI Site',
- 'admin_user' => 'admin',
- 'admin_email' => 'admin@example.com',
- 'admin_password' => 'password1',
- 'skip-email' => true,
- ];
-
- $run_dir = '' !== $subdir ? ( $this->variables['RUN_DIR'] . "/$subdir" ) : $this->variables['RUN_DIR'];
-
- $install_cache_path = self::$install_cache_dir . '/install_' . md5( implode( ':', $install_args ) . ':subdir=' . $subdir );
-
- if ( file_exists( $install_cache_path ) ) {
- self::copy_dir( $install_cache_path, $run_dir );
-
- // This is the sqlite equivalent of restoring a database dump in MySQL
- if ( 'sqlite' === self::$db_type ) {
- copy( "{$install_cache_path}.sqlite", "$run_dir/wp-content/database/.ht.sqlite" );
- } else {
- self::run_sql( self::$mysql_binary . ' --no-defaults', [ 'execute' => "source {$install_cache_path}.sql" ], true /*add_database*/ );
- }
- } else {
- $this->proc( 'wp core install', $install_args, $subdir )->run_check();
-
- mkdir( $install_cache_path );
-
- self::dir_diff_copy( $run_dir, self::$cache_dir, $install_cache_path );
-
- if ( 'sqlite' !== self::$db_type ) {
- $mysqldump_binary = Utils\get_sql_dump_command();
- $mysqldump_binary = Utils\force_env_on_nix_systems( $mysqldump_binary );
- $support_column_statistics = exec( "{$mysqldump_binary} --help | grep 'column-statistics'" );
- $command = "{$mysqldump_binary} --no-defaults --no-tablespaces";
- if ( $support_column_statistics ) {
- $command .= ' --skip-column-statistics';
- }
- self::run_sql( $command, [ 'result-file' => "{$install_cache_path}.sql" ], true /*add_database*/ );
- }
-
- if ( 'sqlite' === self::$db_type ) {
- // This is the sqlite equivalent of creating a database dump in MySQL
- copy( "$run_dir/wp-content/database/.ht.sqlite", "{$install_cache_path}.sqlite" );
- }
- }
- }
-
- /**
- * @param string $vendor_directory
- */
- public function install_wp_with_composer( $vendor_directory = 'vendor' ): void {
- $this->create_run_dir();
- $this->create_db();
-
- $yml_path = $this->variables['RUN_DIR'] . '/wp-cli.yml';
- file_put_contents( $yml_path, 'path: WordPress' );
-
- $this->composer_command( 'init --name="wp-cli/composer-test" --type="project"' );
- $this->composer_command( 'config vendor-dir ' . $vendor_directory );
- $this->composer_command( 'config extra.wordpress-install-dir WordPress' );
-
- // Allow for all Composer plugins to run to avoid warnings.
- $this->composer_command( 'config --no-plugins allow-plugins true' );
- $this->composer_command( 'require johnpbloch/wordpress-core-installer johnpbloch/wordpress-core --optimize-autoloader' );
-
- // Disable WP Cron by default to avoid bogus HTTP requests in CLI context.
- $config_extra_php = "if ( ! defined( 'DISABLE_WP_CRON' ) ) { define( 'DISABLE_WP_CRON', true ); }\n";
-
- $config_extra_php .= "require_once dirname(__DIR__) . '/" . $vendor_directory . "/autoload.php';\n";
-
- $this->create_config( 'WordPress', $config_extra_php );
-
- $install_args = [
- 'url' => 'http://localhost:8080',
- 'title' => 'WP CLI Site with both WordPress and wp-cli as Composer dependencies',
- 'admin_user' => 'admin',
- 'admin_email' => 'admin@example.com',
- 'admin_password' => 'password1',
- 'skip-email' => true,
- ];
-
- if ( ! is_dir( $this->variables['RUN_DIR'] . '/WordPress/wp-content/mu-plugins' ) ) {
- mkdir( $this->variables['RUN_DIR'] . '/WordPress/wp-content/mu-plugins' );
- }
-
- if ( 'sqlite' === self::$db_type ) {
- mkdir( $this->variables['RUN_DIR'] . '/WordPress/wp-content/mu-plugins/sqlite-database-integration' );
- self::copy_dir( self::$sqlite_cache_dir, $this->variables['RUN_DIR'] . '/WordPress/wp-content/mu-plugins' );
- self::configure_sqlite( $this->variables['RUN_DIR'] . '/WordPress' );
- }
-
- $this->proc( 'wp core install', $install_args )->run_check();
- }
-
- public function composer_add_wp_cli_local_repository(): void {
- if ( ! self::$composer_local_repository ) {
- self::$composer_local_repository = sys_get_temp_dir() . '/' . uniqid( 'wp-cli-composer-local-', true );
- mkdir( self::$composer_local_repository );
-
- $env = self::get_process_env_variables();
- $src = $env['TRAVIS_BUILD_DIR'] ?? realpath( self::get_vendor_dir() . '/../' );
-
- self::copy_dir( $src, self::$composer_local_repository . '/' );
- self::remove_dir( self::$composer_local_repository . '/.git' );
- self::remove_dir( self::$composer_local_repository . '/vendor' );
- }
- $dest = self::$composer_local_repository . '/';
- $this->composer_command( "config repositories.wp-cli '{\"type\": \"path\", \"url\": \"$dest\", \"options\": {\"symlink\": false, \"versions\": { \"wp-cli/wp-cli\": \"dev-main\"}}}'" );
- $this->variables['COMPOSER_LOCAL_REPOSITORY'] = self::$composer_local_repository;
- }
-
- public function composer_require_current_wp_cli(): void {
- $this->composer_add_wp_cli_local_repository();
- // TODO: Specific alias version should be deduced to keep up-to-date.
- $this->composer_command( 'require "wp-cli/wp-cli:dev-main as 2.5.x-dev" --optimize-autoloader' );
- }
-
- /**
- * @param string $subdir
- */
- public function start_php_server( $subdir = '' ): void {
- $dir = $this->variables['RUN_DIR'] . '/';
- if ( $subdir ) {
- $dir .= trim( $subdir, '/' ) . '/';
- }
- $cmd = Utils\esc_cmd(
- '%s -S %s -t %s -c %s %s',
- Utils\get_php_binary(),
- 'localhost:8080',
- $dir,
- get_cfg_var( 'cfg_file_path' ),
- $this->variables['RUN_DIR'] . '/vendor/wp-cli/server-command/router.php'
- );
- $this->background_proc( $cmd );
- }
-
- /**
- * @param string $cmd
- */
- private function composer_command( $cmd ): void {
- if ( ! isset( $this->variables['COMPOSER_PATH'] ) ) {
- $this->variables['COMPOSER_PATH'] = exec( 'which composer' );
- }
- $this->proc( $this->variables['COMPOSER_PATH'] . ' --no-interaction ' . $cmd )->run_check();
- }
-
- /**
- * Initialize run time logging.
- *
- * @param BeforeSuiteScope $scope
- */
- private static function log_run_times_before_suite( BeforeSuiteScope $scope ): void {
- self::$suite_start_time = microtime( true );
-
- Process::$log_run_times = true;
-
- $travis = getenv( 'TRAVIS' );
-
- // Default output settings.
- self::$output_to = 'stdout';
- self::$num_top_processes = $travis ? 10 : 40;
- self::$num_top_scenarios = $travis ? 10 : 20;
-
- // Allow setting of above with "WP_CLI_TEST_LOG_RUN_TIMES=[,][,]" formatted env var.
- if ( preg_match( '/^(stdout|error_log)?(,[0-9]+)?(,[0-9]+)?$/i', self::$log_run_times, $matches ) ) {
- if ( isset( $matches[1] ) ) {
- self::$output_to = strtolower( $matches[1] );
- }
- if ( isset( $matches[2] ) ) {
- self::$num_top_processes = max( (int) substr( $matches[2], 1 ), 1 );
- }
- if ( isset( $matches[3] ) ) {
- self::$num_top_scenarios = max( (int) substr( $matches[3], 1 ), 1 );
- }
- }
- }
-
- /**
- * Record the start time of the scenario into the `$scenario_run_times` array.
- *
- * @param ScenarioScope|FeatureScope|OutlineTested $scope
- */
- private static function log_run_times_before_scenario( $scope ): void {
- $scenario_key = self::get_scenario_key( $scope );
- if ( $scenario_key ) {
- self::$scenario_run_times[ $scenario_key ] = -microtime( true );
- }
- }
-
- /**
- * Save the run time of the scenario into the `$scenario_run_times` array. Only the top `self::$num_top_scenarios` are kept.
- *
- * @param ScenarioScope|FeatureScope|OutlineTested $scope
- */
- private static function log_run_times_after_scenario( $scope ): void {
- $scenario_key = self::get_scenario_key( $scope );
- if ( $scenario_key ) {
- self::$scenario_run_times[ $scenario_key ] += microtime( true );
- ++self::$scenario_count;
- if ( count( self::$scenario_run_times ) > self::$num_top_scenarios ) {
- arsort( self::$scenario_run_times );
- array_pop( self::$scenario_run_times );
- }
- }
- }
-
- /**
- * Copy files in updated directory that are not in source directory to copy directory. ("Incremental backup".)
- * Note: does not deal with changed files (ie does not compare file contents for changes), for speed reasons.
- *
- * @param string $upd_dir The directory to search looking for files/directories not in `$src_dir`.
- * @param string $src_dir The directory to be compared to `$upd_dir`.
- * @param string $cop_dir Where to copy any files/directories in `$upd_dir` but not in `$src_dir` to.
- */
- private static function dir_diff_copy( $upd_dir, $src_dir, $cop_dir ): void {
- $files = scandir( $upd_dir );
- if ( false === $files ) {
- $error = error_get_last();
- throw new RuntimeException( sprintf( "Failed to open updated directory '%s': %s. " . __FILE__ . ':' . __LINE__, $upd_dir, $error['message'] ) );
- }
- foreach ( array_diff( $files, [ '.', '..' ] ) as $file ) {
- $upd_file = $upd_dir . '/' . $file;
- $src_file = $src_dir . '/' . $file;
- $cop_file = $cop_dir . '/' . $file;
- if ( ! file_exists( $src_file ) ) {
- if ( is_dir( $upd_file ) ) {
- if ( ! file_exists( $cop_file ) && ! mkdir( $cop_file, 0777, true /*recursive*/ ) ) {
- $error = error_get_last();
- throw new RuntimeException( sprintf( "Failed to create copy directory '%s': %s. " . __FILE__ . ':' . __LINE__, $cop_file, $error['message'] ) );
- }
- self::copy_dir( $upd_file, $cop_file );
- } elseif ( ! copy( $upd_file, $cop_file ) ) {
- $error = error_get_last();
- throw new RuntimeException( sprintf( "Failed to copy '%s' to '%s': %s. " . __FILE__ . ':' . __LINE__, $upd_file, $cop_file, $error['message'] ) );
- }
- } elseif ( is_dir( $upd_file ) ) {
- self::dir_diff_copy( $upd_file, $src_file, $cop_file );
- }
- }
- }
-
- /**
- * Get the scenario key used for `$scenario_run_times` array.
- * Format " :", eg "core-command core-update.feature:221".
- *
- * @param ScenarioScope|FeatureScope|OutlineTested $scope
- */
- private static function get_scenario_key( $scope ): string {
- $scenario_key = '';
- $file = self::get_event_file( $scope, $line );
- if ( isset( $file ) ) {
- $scenario_grandparent = Utils\basename( dirname( $file, 2 ) );
- $scenario_key = $scenario_grandparent . ' ' . Utils\basename( $file ) . ':' . $line;
- }
- return $scenario_key;
- }
-
- /**
- * Print out stats on the run times of processes and scenarios.
- */
- private static function log_run_times_after_suite( AfterSuiteScope $scope ): void {
-
- $suite = '';
- if ( self::$scenario_run_times ) {
- // Grandparent directory is first part of key.
- $keys = array_keys( self::$scenario_run_times );
- $suite = substr( $keys[0], 0, strpos( $keys[0], ' ' ) );
- }
-
- $run_from = Utils\basename( dirname( __DIR__, 2 ) );
-
- // Format same as Behat, if have minutes.
- $fmt = static function ( $time ) {
- $mins = floor( $time / 60 );
- return round( $time, 3 ) . ( $mins ? ( ' (' . $mins . 'm' . round( $time - ( $mins * 60 ), 3 ) . 's)' ) : '' );
- };
-
- $time = microtime( true ) - self::$suite_start_time;
-
- $log = PHP_EOL . str_repeat( '(', 80 ) . PHP_EOL;
-
- // Process and proc method run times.
- $run_times = array_merge( Process::$run_times, self::$proc_method_run_times );
- $reduce_callback = static function ( $carry, $item ) {
- return [ $carry[0] + $item[0], $carry[1] + $item[1] ];
- };
-
- list( $ptime, $calls ) = array_reduce( $run_times, $reduce_callback, [ 0, 0 ] );
-
- $overhead = $time - $ptime;
- $pct = round( ( $overhead / $time ) * 100 );
- $unique = count( $run_times );
-
- $log .= sprintf(
- PHP_EOL . "Total process run time %s (tests %s, overhead %.3f %d%%), calls %d (%d unique) for '%s' run from '%s'" . PHP_EOL,
- $fmt( $ptime ),
- $fmt( $time ),
- $overhead,
- $pct,
- $calls,
- $unique,
- $suite,
- $run_from
- );
-
- $sort_callback = static function ( $a, $b ) {
- return $a[0] === $b[0] ? 0 : ( $a[0] < $b[0] ? 1 : -1 ); // Reverse sort.
- };
- uasort( $run_times, $sort_callback );
-
- $tops = array_slice( $run_times, 0, self::$num_top_processes, true );
-
- $runtime_callback = static function ( $k, $v, $i ) {
- return sprintf( ' %3d. %7.3f %3d %s', $i + 1, round( $v[0], 3 ), $v[1], $k );
- };
-
- $log .= PHP_EOL . 'Top ' . self::$num_top_processes . " process run times for '$suite'";
- $log .= PHP_EOL . implode(
- PHP_EOL,
- array_map(
- $runtime_callback,
- array_keys( $tops ),
- $tops,
- array_keys( array_keys( $tops ) )
- )
- ) . PHP_EOL;
-
- // Scenario run times.
- arsort( self::$scenario_run_times );
-
- $tops = array_slice( self::$scenario_run_times, 0, self::$num_top_scenarios, true );
-
- $scenario_runtime_callback = static function ( $k, $v, $i ) {
- return sprintf( ' %3d. %7.3f %s', $i + 1, round( $v, 3 ), substr( $k, strpos( $k, ' ' ) + 1 ) );
- };
-
- $log .= PHP_EOL . 'Top ' . self::$num_top_scenarios . ' (of ' . self::$scenario_count . ") scenario run times for '$suite'";
-
- $log .= PHP_EOL . implode(
- PHP_EOL,
- array_map(
- $scenario_runtime_callback,
- array_keys( $tops ),
- $tops,
- array_keys( array_keys( $tops ) )
- )
- ) . PHP_EOL;
-
- $log .= PHP_EOL . str_repeat( ')', 80 );
-
- if ( 'error_log' === self::$output_to ) {
- error_log( $log );
- } else {
- echo PHP_EOL . $log;
- }
- }
-
- /**
- * Log the run time of a proc method (one that doesn't use Process but does (use a function that does) a `proc_open()`).
- *
- * @param string $key
- * @param int|float $start_time
- */
- private static function log_proc_method_run_time( $key, $start_time ): void {
- $run_time = microtime( true ) - $start_time;
- if ( ! isset( self::$proc_method_run_times[ $key ] ) ) {
- self::$proc_method_run_times[ $key ] = [ 0, 0 ];
- }
- self::$proc_method_run_times[ $key ][0] += $run_time;
- ++self::$proc_method_run_times[ $key ][1];
- }
-}
diff --git a/src/Context/GivenStepDefinitions.php b/src/Context/GivenStepDefinitions.php
deleted file mode 100644
index 0aa0ded0a..000000000
--- a/src/Context/GivenStepDefinitions.php
+++ /dev/null
@@ -1,812 +0,0 @@
-create_run_dir();
- }
-
- /**
- * Creates or deletes a specific directory.
- *
- * ```
- * Scenario: My example scenario
- * Given an empty foo-plugin directory
- * And a non-existent bar-plugin directory
- * ...
- * ```
- *
- * @access public
- *
- * @Given /^an? (empty|non-existent) ([^\s]+) directory$/
- *
- * @param string $empty_or_nonexistent
- * @param string $dir
- */
- public function given_a_specific_directory( $empty_or_nonexistent, $dir ): void {
- $dir = $this->replace_variables( $dir );
- if ( ! Utils\is_path_absolute( $dir ) ) {
- $dir = $this->variables['RUN_DIR'] . "/$dir";
- }
-
- // Mac OS X can prefix the `/var` folder to turn it into `/private/var`.
- $dir = preg_replace( '|^/private/var/|', '/var/', $dir );
-
- $temp_dir = sys_get_temp_dir();
-
- // Also check for temp dir prefixed with `/private` for Mac OS X.
- if ( 0 !== strpos( $dir, $temp_dir ) && 0 !== strpos( $dir, "/private{$temp_dir}" ) ) {
- throw new RuntimeException(
- sprintf(
- "Attempted to delete directory '%s' that is not in the temp directory '%s'. " . __FILE__ . ':' . __LINE__,
- $dir,
- $temp_dir
- )
- );
- }
-
- self::remove_dir( $dir );
- if ( 'empty' === $empty_or_nonexistent ) {
- mkdir( $dir, 0777, true /*recursive*/ );
- }
- }
-
- /**
- * Clears the WP-CLI cache directory.
- *
- * ```
- * Scenario: My example scenario
- * Given an empty cache
- * ...
- * ```
- *
- * @access public
- *
- * @Given an empty cache
- */
- public function given_an_empty_cache(): void {
- $this->variables['SUITE_CACHE_DIR'] = FeatureContext::create_cache_dir();
- }
-
- /**
- * Creates a file with the given contents.
- *
- * The file can be created either in the current working directory
- * or in the cache directory.
- *
- * ```
- * Scenario: My example scenario
- * Given a wp-cli.yml file:
- * """
- * @foo:
- * path: foo
- * user: admin
- * """
- * ```
- *
- * @access public
- *
- * @Given /^an? ([^\s]+) (file|cache file):$/
- *
- * @param string $path
- * @param string $type
- * @param PyStringNode $content
- */
- public function given_a_specific_file( $path, $type, PyStringNode $content ): void {
- $path = $this->replace_variables( (string) $path );
- $content = $this->replace_variables( (string) $content ) . "\n";
- $full_path = 'cache file' === $type
- ? $this->variables['SUITE_CACHE_DIR'] . "/$path"
- : $this->variables['RUN_DIR'] . "/$path";
- $dir = dirname( $full_path );
- if ( ! file_exists( $dir ) ) {
- mkdir( $dir, 0777, true /*recursive*/ );
- }
- file_put_contents( $full_path, $content );
- }
-
- /**
- * Search and replace a string in a file using regex.
- *
- * ```
- * Scenario: My example scenario
- * Given "Foo" replaced with "Bar" in the readme.html file
- * ...
- * ```
- *
- * @access public
- *
- * @Given /^"([^"]+)" replaced with "([^"]+)" in the ([^\s]+) file$/
- *
- * @param string $search
- * @param string $replace
- * @param string $path
- */
- public function given_string_replaced_with_string_in_a_specific_file( $search, $replace, $path ): void {
- $full_path = $this->variables['RUN_DIR'] . "/$path";
- $contents = file_get_contents( $full_path );
- $contents = str_replace( $search, $replace, $contents );
- file_put_contents( $full_path, $contents );
- }
-
- /**
- * Mock HTTP requests to a given URL.
- *
- * ```
- * Scenario: My example scenario
- * Given that HTTP requests to https://api.github.com/repos/wp-cli/wp-cli/releases?per_page=100 will respond with:
- * """
- * HTTP/1.1 200
- * Content-Type: application/json
- *
- * { "foo": "bar" }
- * """
- * ```
- *
- * @access public
- *
- * @Given /^that HTTP requests to (.*?) will respond with:$/
- *
- * @param string $url_or_pattern
- * @param PyStringNode $content
- */
- public function given_a_request_to_a_url_respond_with_file( $url_or_pattern, PyStringNode $content ): void {
- if ( ! isset( $this->variables['RUN_DIR'] ) ) {
- $this->create_run_dir();
- }
-
- $config_file = $this->variables['RUN_DIR'] . '/wp-cli.yml';
- $mock_file = $this->variables['RUN_DIR'] . '/mock-requests.php';
- $dir = dirname( $config_file );
-
- if ( ! file_exists( $dir ) ) {
- mkdir( $dir, 0777, true /*recursive*/ );
- }
-
- $config_file_contents = <<<'FILE'
-require:
- - mock-requests.php
-FILE;
-
- file_put_contents(
- $config_file,
- $config_file_contents
- );
-
- $this->mocked_requests[ $url_or_pattern ] = (string) $content;
-
- $mocked_requests = var_export( $this->mocked_requests, true /* return */ );
-
- $mock_file_contents = << \$response ) {
- \$pattern = '/' . preg_quote( \$pattern, '/' ) . '/';
- if ( 1 === preg_match( \$pattern, \$url ) ) {
- \$pos = strpos( \$response, "\\n\\n");
- if ( false !== \$pos ) {
- \$response = substr( \$response, 0, \$pos ) . "\\r\\n\\r\\n" . substr( \$response, \$pos + 2 );
- }
- return \$response;
- }
- }
-
- if ( class_exists( '\WpOrg\Requests\Transport\Curl' ) ) {
- return ( new \WpOrg\Requests\Transport\Curl() )->request( \$url, \$headers, \$data, \$options );
- }
-
- return ( new \Requests_Transport_cURL() )->request( \$url, \$headers, \$data, \$options );
- }
-
- public function request_multiple( \$requests, \$options ) {
- throw new Exception( 'Method not implemented: ' . __METHOD__ );
- }
-
- public static function test( \$capabilities = array() ) {
- return true;
- }
-}
-
-if ( interface_exists( '\WpOrg\Requests\Transport' ) ) {
- class WP_CLI_Tests_Mock_Requests_Transport implements \WpOrg\Requests\Transport {
- use WP_CLI_Tests_Mock_Requests_Trait;
- }
-} else {
- class WP_CLI_Tests_Mock_Requests_Transport implements \Requests_Transport {
- use WP_CLI_Tests_Mock_Requests_Trait;
- }
-}
-
-WP_CLI::add_hook(
- 'http_request_options',
- static function( \$options ) {
- \$options['transport'] = new WP_CLI_Tests_Mock_Requests_Transport();
- return \$options;
- }
-);
-
-WP_CLI::add_wp_hook(
- 'pre_http_request',
- static function( \$pre, \$parsed_args, \$url ) {
- \$mocked_requests = $mocked_requests;
-
- foreach ( \$mocked_requests as \$pattern => \$response ) {
- \$pattern = '/' . preg_quote( \$pattern, '/' ) . '/';
- if ( 1 === preg_match( \$pattern, \$url ) ) {
- \$pos = strpos( \$response, "\n\n");
- if ( false !== \$pos ) {
- \$response = substr( \$response, 0, \$pos ) . "\r\n\r\n" . substr( \$response, \$pos + 2 );
- }
-
- if ( class_exists( '\WpOrg\Requests\Requests' ) ) {
- WpOrg\Requests\Requests::parse_multiple(
- \$response,
- array(
- 'url' => \$url,
- 'headers' => array(),
- 'data' => array(),
- 'options' => array_merge(
- WpOrg\Requests\Requests::OPTION_DEFAULTS,
- array(
- 'hooks' => new WpOrg\Requests\Hooks(),
- )
- ),
- )
- );
- } else {
- \Requests::parse_multiple(
- \$response,
- array(
- 'url' => \$url,
- 'headers' => array(),
- 'data' => array(),
- 'options' => array(
- 'blocking' => true,
- 'filename' => false,
- 'follow_redirects' => true,
- 'redirected' => 0,
- 'redirects' => 10,
- 'hooks' => new Requests_Hooks(),
- ),
- )
- );
- }
-
- return array(
- 'headers' => \$response->headers->getAll(),
- 'body' => \$response->body,
- 'response' => array(
- 'code' => \$response->status_code,
- 'message' => get_status_header_desc( \$response->status_code ),
- ),
- 'cookies' => array(),
- 'filename' => '',
- );
- }
- }
-
- return \$pre;
- },
- 10,
- 3
-);
-FILE;
-
- file_put_contents(
- $mock_file,
- $mock_file_contents
- );
- }
-
- /**
- * Download WordPress files without installing.
- *
- * ```
- * Scenario: My example scenario
- * Given an empty directory
- * And WP files
- * ```
- *
- * @access public
- *
- * @Given WP files
- */
- public function given_wp_files(): void {
- $this->download_wp();
- }
-
- /**
- * Create a wp-config.php file using `wp config create`.
- *
- * ```
- * Scenario: My example scenario
- * Given an empty directory
- * And WP files
- * And wp-config.php
- * ```
- *
- * @access public
- *
- * @Given wp-config.php
- */
- public function given_wp_config_php(): void {
- $this->create_config();
- }
-
- /**
- * Creates an empty database.
- *
- * Has no effect when tests run with SQLite.
- *
- * ```
- * Scenario: My example scenario
- * Given a database
- * ...
- * ```
- *
- * @access public
- *
- * @Given a database
- */
- public function given_a_database(): void {
- $this->create_db();
- }
-
- /**
- * Installs WordPress.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation
- * ...
- *
- * Scenario: My other scenario
- * Given a WP install
- * ...
- *
- * Scenario: My version-specific scenario
- * Given a WP 6.4.2 installation
- * ...
- * ```
- *
- * @access public
- *
- * @Given /^a WP( \d[^\s]+)? install(?:ation)?$/
- *
- * @param string $version Optional version number (may include leading space)
- */
- public function given_a_wp_installation( $version = '' ): void {
- $this->install_wp( '', trim( $version ) );
- }
-
- /**
- * Installs WordPress in a given directory.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation in 'foo'
- * ...
- *
- * Scenario: My other scenario
- * Given a WP install in 'bar'
- * ...
- *
- * Scenario: My version-specific scenario
- * Given a WP 6.4.2 installation in 'subdir'
- * ...
- * ```
- *
- * @access public
- *
- * @Given /^a WP( [^\s]+)? install(?:ation)? in ['"]?([^'"]+)['"]?$/
- *
- * @param string $version Optional version number (may include leading space)
- * @param string $subdir
- */
- public function given_a_wp_installation_in_a_specific_folder( $version = '', $subdir = '' ): void {
- $this->install_wp( $subdir, trim( $version ) );
- }
-
- /**
- * Installs WordPress with Composer.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation with Composer
- * ...
- *
- * Scenario: My other scenario
- * Given a WP install with Composer
- * ...
- * ```
- *
- * @access public
- *
- * @Given a WP install(ation) with Composer
- */
- public function given_a_wp_installation_with_composer(): void {
- $this->install_wp_with_composer();
- }
-
- /**
- * Installs WordPress with Composer and a custom vendor directory.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation with Composer and a custom vendor directory 'vendor-custom'
- * ...
- *
- * Scenario: My other scenario
- * Given a WP install with Composer with Composer and a custom vendor directory 'vendor-custom'
- * ...
- * ```
- *
- * @access public
- *
- * @Given a WP install(ation) with Composer and a custom vendor directory :vendor_directory
- *
- * @param string $vendor_directory
- */
- public function given_a_wp_installation_with_composer_and_a_custom_vendor_folder( $vendor_directory ): void {
- $this->install_wp_with_composer( $vendor_directory );
- }
-
- /**
- * Installs WordPress Multisite.
- *
- * Supports either subdirectory or subdomain installation.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP multisite subdomain installation
- * ...
- *
- * Scenario: My other scenario
- * Given a WP subdirectory install
- * ...
- * ```
- *
- * @access public
- *
- * @Given /^a WP multisite (subdirectory|subdomain)?\s?(install|installation)$/
- *
- * @param string $type Multisite installation type.
- */
- public function given_a_wp_multisite_installation( $type = 'subdirectory' ): void {
- $this->install_wp();
- $subdomains = ! empty( $type ) && 'subdomain' === $type ? 1 : 0;
- $this->proc(
- 'wp core install-network',
- array(
- 'title' => 'WP CLI Network',
- 'subdomains' => $subdomains,
- )
- )->run_check();
- }
-
- /**
- * Installs and activates one or more plugins.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation
- * And these installed and active plugins:
- * """
- * akismet
- * wordpress-importer
- * """
- * ```
- *
- * @access public
- *
- * @Given these installed and active plugins:
- *
- * @param string $stream
- */
- public function given_these_installed_and_active_plugins( $stream ): void {
- $plugins = implode( ' ', array_map( 'trim', explode( PHP_EOL, (string) $stream ) ) );
- $plugins = $this->replace_variables( $plugins );
-
- $this->proc( "wp plugin install $plugins --activate" )->run_check();
- }
-
- /**
- * Configure a custom `wp-content` directory.
- *
- * Defines the `WP_CONTENT_DIR`, `WP_PLUGIN_DIR`, and `WPMU_PLUGIN_DIR` constants.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP install
- * And a custom wp-content directory
- * ```
- *
- * @access public
- *
- * @Given a custom wp-content directory
- */
- public function given_a_custom_wp_directory(): void {
- $wp_config_path = $this->variables['RUN_DIR'] . '/wp-config.php';
-
- $wp_config_code = file_get_contents( $wp_config_path );
-
- $this->move_files( 'wp-content', 'my-content' );
- $this->add_line_to_wp_config(
- $wp_config_code,
- "define( 'WP_CONTENT_DIR', dirname(__FILE__) . '/my-content' );"
- );
-
- $this->move_files( 'my-content/plugins', 'my-plugins' );
- $this->add_line_to_wp_config(
- $wp_config_code,
- "define( 'WP_PLUGIN_DIR', __DIR__ . '/my-plugins' );"
- );
-
- $this->move_files( 'my-content/mu-plugins', 'my-mu-plugins' );
- $this->add_line_to_wp_config(
- $wp_config_code,
- "define( 'WPMU_PLUGIN_DIR', __DIR__ . '/my-mu-plugins' );"
- );
-
- file_put_contents( $wp_config_path, $wp_config_code );
-
- if ( 'sqlite' === self::$db_type ) {
- $db_dropin = $this->variables['RUN_DIR'] . '/my-content/db.php';
-
- /* similar to https://github.com/WordPress/sqlite-database-integration/blob/3306576c9b606bc23bbb26c15383fef08e03ab11/activate.php#L95 */
- $file_contents = str_replace(
- 'mu-plugins/',
- '../my-mu-plugins/',
- file_get_contents( $db_dropin )
- );
-
- file_put_contents( $db_dropin, $file_contents );
- }
- }
-
- /**
- * Download multiple files into the given destinations.
- *
- * ```
- * Scenario: My example scenario
- * Given download:
- * | path | url |
- * | {CACHE_DIR}/foo.jpg | https://example.com/foo.jpg |
- * | {CACHE_DIR}/bar.png | https://example.com/another-image.png |
- * ```
- *
- * @access public
- *
- * @Given download:
- */
- public function given_a_download( TableNode $table ): void {
- foreach ( $table->getHash() as $row ) {
- $path = $this->replace_variables( $row['path'] );
- if ( file_exists( $path ) ) {
- // Assume it's the same file and skip re-download.
- continue;
- }
-
- Process::create( Utils\esc_cmd( 'curl -sSL %s > %s', $row['url'], $path ) )->run_check();
- }
- }
-
- /**
- * Store STDOUT or STDERR contents in a variable.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp package path`
- * Then save STDOUT as {PACKAGE_PATH}
- *
- * Scenario: My other scenario
- * When I run `wp core download`
- * Then save STDOUT 'Downloading WordPress ([\d\.]+)' as {VERSION}
- * ```
- *
- * @access public
- *
- * @Given /^save (STDOUT|STDERR) ([\'].+[^\'])?\s?as \{(\w+)\}$/
- *
- * @param string $stream
- * @param string $output_filter
- * @param string $key
- */
- public function given_saved_stdout_stderr( $stream, $output_filter, $key ): void {
- $stream = strtolower( $stream );
-
- if ( $output_filter ) {
- $output_filter = '/' . trim( str_replace( '%s', '(.+[^\b])', $output_filter ), "' " ) . '/';
- if ( false !== preg_match( $output_filter, $this->result->$stream, $matches ) ) {
- $output = array_pop( $matches );
- } else {
- $output = '';
- }
- } else {
- $output = $this->result->$stream;
- }
- $this->variables[ $key ] = trim( $output, "\n" );
- }
-
- /**
- * Build a new WP-CLI Phar file with a given version.
- *
- * ```
- * Scenario: My example scenario
- * Given an empty directory
- * And a new Phar with version "2.11.0"
- * ```
- *
- * @access public
- *
- * @Given /^a new Phar with (?:the same version|version "([^"]+)")$/
- *
- * @param string $version
- */
- public function given_a_new_phar_with_a_specific_version( $version = 'same' ): void {
- $this->build_phar( $version );
- }
-
- /**
- * Download a specific WP-CLI Phar version from GitHub.
- *
- * ```
- * Scenario: My example scenario
- * Given an empty directory
- * And a downloaded Phar with version "2.11.0"
- *
- * Scenario: My other scenario
- * Given an empty directory
- * And a downloaded Phar with the same version
- * ```
- *
- * @access public
- *
- * @Given /^a downloaded Phar with (?:the same version|version "([^"]+)")$/
- *
- * @param string $version
- */
- public function given_a_downloaded_phar_with_a_specific_version( $version = 'same' ): void {
- $this->download_phar( $version );
- }
-
- /**
- * Stores the contents of the given file in a variable.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation with Composer
- * And save the {RUN_DIR}/composer.json file as {COMPOSER_JSON}
- * ```
- *
- * @access public
- *
- * @Given /^save the (.+) file ([\'].+[^\'])?as \{(\w+)\}$/
- *
- * @param string $filepath
- * @param string $output_filter
- * @param string $key
- */
- public function given_saved_a_specific_file( $filepath, $output_filter, $key ): void {
- $full_file = file_get_contents( $this->replace_variables( $filepath ) );
-
- if ( $output_filter ) {
- $output_filter = '/' . trim( str_replace( '%s', '(.+[^\b])', $output_filter ), "' " ) . '/';
- if ( false !== preg_match( $output_filter, $full_file, $matches ) ) {
- $output = array_pop( $matches );
- } else {
- $output = '';
- }
- } else {
- $output = $full_file;
- }
- $this->variables[ $key ] = trim( $output, "\n" );
- }
-
- /**
- * Modify wp-config.php to set `WP_CONTENT_DIR` to an empty string.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP install
- * And a misconfigured WP_CONTENT_DIR constant directory
- * ```
- *
- * @access public
- *
- * @Given a misconfigured WP_CONTENT_DIR constant directory
- */
- public function given_a_misconfigured_wp_content_dir_constant_directory(): void {
- $wp_config_path = $this->variables['RUN_DIR'] . '/wp-config.php';
-
- $wp_config_code = file_get_contents( $wp_config_path );
-
- $this->add_line_to_wp_config(
- $wp_config_code,
- "define( 'WP_CONTENT_DIR', '' );"
- );
-
- file_put_contents( $wp_config_path, $wp_config_code );
- }
-
- /**
- * Add `wp-cli/wp-cli` as a Composer dependency.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation with Composer
- * And a dependency on current wp-cli
- * ```
- *
- * @access public
- *
- * @Given a dependency on current wp-cli
- */
- public function given_a_dependency_on_wp_cli(): void {
- $this->composer_require_current_wp_cli();
- }
-
- /**
- * Start a PHP built-in web server in the current directory.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation
- * And a PHP built-in web server
- * ```
- *
- * @access public
- *
- * @Given a PHP built-in web server
- */
- public function given_a_php_built_in_web_server(): void {
- $this->start_php_server();
- }
-
- /**
- * Start a PHP built-in web server in the given subdirectory.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation
- * And a PHP built-in web server to serve 'WordPress'
- * ```
- *
- * @access public
- *
- * @Given a PHP built-in web server to serve :subdir
- *
- * @param string $subdir
- */
- public function given_a_php_built_in_web_server_to_serve_a_specific_folder( $subdir ): void {
- $this->start_php_server( $subdir );
- }
-}
diff --git a/src/Context/Support.php b/src/Context/Support.php
deleted file mode 100644
index c68e2879c..000000000
--- a/src/Context/Support.php
+++ /dev/null
@@ -1,323 +0,0 @@
-generate_diff( $expected, rtrim( $output, "\n" ) );
- if ( ! empty( $diff ) ) {
- $message .= "\n\n" . $diff;
- }
-
- throw new Exception( $message );
- }
- }
-
- /**
- * @param array $expected_rows
- * @param array $actual_rows
- * @param string $output
- * @throws Exception
- */
- protected function compare_tables( $expected_rows, $actual_rows, $output ): void {
- // The first row is the header and must be present.
- if ( $expected_rows[0] !== $actual_rows[0] ) {
- $expected_table = implode( "\n", $expected_rows );
- $actual_table = implode( "\n", $actual_rows );
- $diff = $this->generate_diff( $expected_table, $actual_table );
- throw new Exception( $output . "\n\n" . $diff );
- }
-
- unset( $actual_rows[0] );
- unset( $expected_rows[0] );
-
- $missing_rows = array_diff( $expected_rows, $actual_rows );
- if ( ! empty( $missing_rows ) ) {
- $expected_table = implode( "\n", $expected_rows );
- $actual_table = implode( "\n", $actual_rows );
- $diff = $this->generate_diff( $expected_table, $actual_table );
- throw new Exception( $output . "\n\n" . $diff );
- }
- }
-
- /**
- * @param mixed $expected
- * @param mixed $actual
- * @return bool
- */
- protected function compare_contents( $expected, $actual ) {
- if ( gettype( $expected ) !== gettype( $actual ) ) {
- return false;
- }
-
- if ( is_object( $expected ) ) {
- foreach ( get_object_vars( $expected ) as $name => $value ) {
- if ( ! $this->compare_contents( $value, $actual->$name ) ) {
- return false;
- }
- }
- } elseif ( is_array( $expected ) ) {
- foreach ( $expected as $key => $value ) {
- if ( ! $this->compare_contents( $value, $actual[ $key ] ) ) {
- return false;
- }
- }
- } else {
- return $expected === $actual;
- }
-
- return true;
- }
-
- /**
- * Compare two strings containing JSON to ensure that $actualJson contains at
- * least what the JSON string $expectedJson contains.
- *
- * @param string $actual_json the JSON string to be tested
- * @param string $expected_json the expected JSON string
- *
- * @return bool Whether or not $actual_json contains $expected_json.
- *
- * Examples:
- * expected: {'a':1,'array':[1,3,5]}
- *
- * 1 )
- * actual: {'a':1,'b':2,'c':3,'array':[1,2,3,4,5]}
- * return: true
- *
- * 2 )
- * actual: {'b':2,'c':3,'array':[1,2,3,4,5]}
- * return: false
- * element 'a' is missing from the root object
- *
- * 3 )
- * actual: {'a':0,'b':2,'c':3,'array':[1,2,3,4,5]}
- * return: false
- * the value of element 'a' is not 1
- *
- * 4 )
- * actual: {'a':1,'b':2,'c':3,'array':[1,2,4,5]}
- * return: false
- * the contents of 'array' does not include 3
- */
- protected function check_that_json_string_contains_json_string( $actual_json, $expected_json ) {
- $actual_value = json_decode( $actual_json );
- $expected_value = json_decode( $expected_json );
-
- if ( ! $actual_value ) {
- return false;
- }
-
- return $this->compare_contents( $expected_value, $actual_value );
- }
-
- /**
- * Compare two strings to confirm $actualCSV contains $expectedCSV
- * Both strings are expected to have headers for their CSVs.
- * $actualCSV must match all data rows in $expectedCSV
- *
- * @param string $actual_csv A CSV string
- * @param array> $expected_csv A nested array of values
- * @return bool Whether $actual_csv contains $expected_csv
- */
- protected function check_that_csv_string_contains_values( $actual_csv, $expected_csv ) {
- $actual_csv = array_map(
- static function ( $str ) {
- return str_getcsv( $str, ',', '"', '\\' );
- },
- explode( PHP_EOL, $actual_csv )
- );
-
- if ( empty( $actual_csv ) ) {
- return false;
- }
-
- /**
- * @var array> $actual_csv
- */
-
- // Each sample must have headers.
- $actual_headers = array_values( array_shift( $actual_csv ) );
- $expected_headers = array_values( array_shift( $expected_csv ) );
-
- // Each expected_csv must exist somewhere in actual_csv in the proper column.
- $expected_result = 0;
- foreach ( $expected_csv as $expected_row ) {
- $expected_row = array_combine( $expected_headers, $expected_row );
- foreach ( $actual_csv as $actual_row ) {
- if ( count( $actual_headers ) !== count( $actual_row ) ) {
- continue;
- }
-
- $actual_row = array_intersect_key(
- array_combine(
- $actual_headers,
- $actual_row
- ),
- $expected_row
- );
-
- if ( $actual_row === $expected_row ) {
- ++$expected_result;
- }
- }
- }
-
- return $expected_result >= count( $expected_csv );
- }
-
- /**
- * Compare two strings containing YAML to ensure that $actualYaml contains at
- * least what the YAML string $expectedYaml contains.
- *
- * @param string $actual_yaml the YAML string to be tested
- * @param string $expected_yaml the expected YAML string
- *
- * @return bool whether or not $actual_yaml contains $expected_json
- */
- protected function check_that_yaml_string_contains_yaml_string( $actual_yaml, $expected_yaml ) {
- $actual_value = Spyc::YAMLLoad( $actual_yaml );
- $expected_value = Spyc::YAMLLoad( $expected_yaml );
-
- if ( ! $actual_value ) {
- return false;
- }
-
- return $this->compare_contents( $expected_value, $actual_value );
- }
-
- /**
- * Generate a unified diff between two strings.
- *
- * @param string $expected The expected string.
- * @param string $actual The actual string.
- * @return string The unified diff output.
- */
- protected function generate_diff( string $expected, string $actual ): string {
- $builder = new UnifiedDiffOutputBuilder(
- "--- Expected\n+++ Actual\n",
- false
- );
- $differ = new Differ( $builder );
- return $differ->diff( $expected, $actual );
- }
-}
diff --git a/src/Context/ThenStepDefinitions.php b/src/Context/ThenStepDefinitions.php
deleted file mode 100644
index bc2cb3f5b..000000000
--- a/src/Context/ThenStepDefinitions.php
+++ /dev/null
@@ -1,614 +0,0 @@
-result->return_code )
- || ( $not && (int) $return_code === $this->result->return_code )
- ) {
- throw new RuntimeException( $this->result );
- }
- }
-
- /**
- * Check the contents of STDOUT or STDERR.
- *
- * ```
- * Scenario: My example scenario
- * Given an empty directory
- * When I run `wp core is-installed`
- * Then STDOUT should be empty
- *
- * Scenario: My other scenario
- * Given a WP install
- * When I run `wp plugin install akismet`
- * Then STDOUT should contain:
- * """
- * Plugin installed successfully.
- * """
- * And STDERR should be empty
- * ```
- *
- * @access public
- *
- * @Then /^(STDOUT|STDERR) should( strictly)? (be|contain|not contain):$/
- *
- * @param string $stream
- * @param bool $strictly
- * @param string $action
- * @param PyStringNode $expected
- */
- public function then_stdout_stderr_should_contain( $stream, $strictly, $action, PyStringNode $expected ): void {
-
- $stream = strtolower( $stream );
-
- $expected = $this->replace_variables( (string) $expected );
-
- $this->check_string( $this->result->$stream, $expected, $action, $this->result, (bool) $strictly );
- }
-
- /**
- * Expect STDOUT or STDERR to be a numeric value.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation
- * When I run `wp db size --size_format=b`
- * Then STDOUT should be a number
- * ```
- *
- * @access public
- *
- * @Then /^(STDOUT|STDERR) should be a number$/
- *
- * @param string $stream
- */
- public function then_stdout_stderr_should_be_a_number( $stream ): void {
-
- $stream = strtolower( $stream );
-
- $this->assert_numeric( trim( $this->result->$stream, "\n" ) );
- }
-
- /**
- * Expect STDOUT or STDERR to not be a numeric value.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation
- * When I run `wp post list --format=json`
- * Then STDOUT should not be a number
- * ```
- *
- * @access public
- *
- * @Then /^(STDOUT|STDERR) should not be a number$/
- *
- * @param string $stream
- */
- public function then_stdout_stderr_should_not_be_a_number( $stream ): void {
-
- $stream = strtolower( $stream );
-
- $this->assert_not_numeric( trim( $this->result->$stream, "\n" ) );
- }
-
- /**
- * Expect STDOUT to be a table containing the given rows.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation
- * When I run `wp config list --fields=name,type`
- * Then STDOUT should be a table containing rows:
- * | name | type |
- * | DB_NAME | constant |
- * | DB_USER | constant |
- * ```
- *
- * @access public
- *
- * @Then /^STDOUT should be a table containing rows:$/
- */
- public function then_stdout_should_be_a_table_containing_rows( TableNode $expected ): void {
- $output = $this->result->stdout;
- $actual_rows = explode( "\n", rtrim( $output, "\n" ) );
-
- $expected_rows = array();
- foreach ( $expected->getRows() as $row ) {
- $expected_rows[] = $this->replace_variables( implode( "\t", $row ) );
- }
-
- $this->compare_tables( $expected_rows, $actual_rows, $output );
- }
-
- /**
- * Expect STDOUT to end with a table containing the given rows.
- *
- * Useful when the table is preceded by some other output.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation
- * When I run `wp search-replace foo bar --report-changed-only`
- * Then STDOUT should contain:
- * """
- * Success: Made 3 replacements.
- * """
- * And STDOUT should end with a table containing rows:
- * | Table | Column | Replacements | Type |
- * | wp_options | option_value | 1 | PHP |
- * | wp_postmeta | meta_value | 1 | SQL |
- * | wp_posts | post_title | 1 | SQL |
- * ```
- *
- * @access public
- *
- * @Then /^STDOUT should end with a table containing rows:$/
- */
- public function then_stdout_should_end_with_a_table_containing_rows( TableNode $expected ): void {
- $output = $this->result->stdout;
- $actual_rows = explode( "\n", rtrim( $output, "\n" ) );
-
- $expected_rows = array();
- foreach ( $expected->getRows() as $row ) {
- $expected_rows[] = $this->replace_variables( implode( "\t", $row ) );
- }
-
- $start = array_search( $expected_rows[0], $actual_rows, true );
-
- if ( false === $start ) {
- throw new Exception( $this->result );
- }
-
- $this->compare_tables( $expected_rows, array_slice( $actual_rows, $start ), $output );
- }
-
- /**
- * Expect valid JSON output in STDOUT.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp post meta get 1 meta-key --format=json`
- * Then STDOUT should be JSON containing:
- * """
- * {
- * "foo": "baz"
- * }
- * """
- * ```
- *
- * @access public
- *
- * @Then /^STDOUT should be JSON containing:$/
- */
- public function then_stdout_should_be_json_containing( PyStringNode $expected ): void {
- $output = $this->result->stdout;
- $expected = $this->replace_variables( (string) $expected );
-
- if ( ! $this->check_that_json_string_contains_json_string( $output, $expected ) ) {
- $message = (string) $this->result;
- // Pretty print JSON for better diff readability.
- $expected_decoded = json_decode( $expected );
- $actual_decoded = json_decode( $output );
- if ( null !== $expected_decoded && null !== $actual_decoded ) {
- $expected_json = json_encode( $expected_decoded, JSON_PRETTY_PRINT );
- $actual_json = json_encode( $actual_decoded, JSON_PRETTY_PRINT );
- if ( false !== $expected_json && false !== $actual_json ) {
- $diff = $this->generate_diff( $expected_json, $actual_json );
- if ( ! empty( $diff ) ) {
- $message .= "\n\n" . $diff;
- }
- }
- }
- throw new Exception( $message );
- }
- }
-
- /**
- * Expect valid JSON array output in STDOUT.
- *
- * Errors when some items are missing from the expected array.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp plugin list --field=name --format=json`
- * Then STDOUT should be a JSON array containing:
- * """
- * ["akismet", "hello-dolly"]
- * """
- * ```
- *
- * @access public
- *
- * @Then /^STDOUT should be a JSON array containing:$/
- */
- public function then_stdout_should_be_a_json_array_containing( PyStringNode $expected ): void {
- $output = $this->result->stdout;
- $expected = $this->replace_variables( (string) $expected );
-
- $actual_values = json_decode( $output );
- $expected_values = json_decode( $expected );
-
- $missing = array_diff( $expected_values, $actual_values );
- if ( ! empty( $missing ) ) {
- $message = (string) $this->result;
- // Pretty print JSON arrays for better diff readability.
- if ( null !== $expected_values && null !== $actual_values ) {
- $expected_json = json_encode( $expected_values, JSON_PRETTY_PRINT );
- $actual_json = json_encode( $actual_values, JSON_PRETTY_PRINT );
- if ( false !== $expected_json && false !== $actual_json ) {
- $diff = $this->generate_diff( $expected_json, $actual_json );
- if ( ! empty( $diff ) ) {
- $message .= "\n\n" . $diff;
- }
- }
- }
- throw new Exception( $message );
- }
- }
-
- /**
- * Expect STDOUT to be CSV containing certain values.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp term list post_tag --fields=name,slug --format=csv`
- * Then STDOUT should be CSV containing:
- * | name | slug |
- * | Test term | test |
- * ```
- *
- * @access public
- *
- * @Then /^STDOUT should be CSV containing:$/
- */
- public function then_stdout_should_be_csv_containing( TableNode $expected ): void {
- $output = $this->result->stdout;
-
- $expected_rows = $expected->getRows();
- foreach ( $expected_rows as &$row ) {
- foreach ( $row as &$value ) {
- $value = $this->replace_variables( $value );
- }
- }
-
- if ( ! $this->check_that_csv_string_contains_values( $output, $expected_rows ) ) {
- $message = (string) $this->result;
- // Convert expected rows to CSV format for diff.
- $expected_csv = '';
- foreach ( $expected_rows as $row ) {
- $expected_csv .= implode( ',', array_map( 'trim', $row ) ) . "\n";
- }
- $diff = $this->generate_diff( trim( $expected_csv ), trim( $output ) );
- if ( ! empty( $diff ) ) {
- $message .= "\n\n" . $diff;
- }
- throw new Exception( $message );
- }
- }
-
- /**
- * Expect STDOUT to be YAML containing certain content.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp cli alias list`
- * Then STDOUT should be YAML containing:
- * """
- * @all: Run command against every registered alias.
- * @foo:
- * path: {TEST_DIR}/foo
- * """
- * ```
- *
- * @access public
- *
- * @Then /^STDOUT should be YAML containing:$/
- */
- public function then_stdout_should_be_yaml_containing( PyStringNode $expected ): void {
- $output = $this->result->stdout;
- $expected = $this->replace_variables( (string) $expected );
-
- if ( ! $this->check_that_yaml_string_contains_yaml_string( $output, $expected ) ) {
- $message = (string) $this->result;
- $diff = $this->generate_diff( $expected, $output );
- if ( ! empty( $diff ) ) {
- $message .= "\n\n" . $diff;
- }
- throw new Exception( $message );
- }
- }
-
- /**
- * Expect STDOUT or STDERR to be empty.
- *
- * ```
- * Scenario: My other scenario
- * Given a WP install
- * When I run `wp plugin install akismet`
- * Then STDERR should be empty
- * ```
- *
- * @access public
- *
- * @Then /^(STDOUT|STDERR) should be empty$/
- *
- * @param string $stream
- */
- public function then_stdout_stderr_should_be_empty( $stream ): void {
-
- $stream = strtolower( $stream );
-
- if ( ! empty( $this->result->$stream ) ) {
- throw new Exception( $this->result );
- }
- }
-
- /**
- * Expect STDOUT or STDERR not to be empty.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp user create examplejane jane@example.com`
- * Then STDOUT should not be empty
- * ```
- *
- * @access public
- *
- * @Then /^(STDOUT|STDERR) should not be empty$/
- *
- * @param string $stream
- */
- public function then_stdout_stderr_should_not_be_empty( $stream ): void {
-
- $stream = strtolower( $stream );
-
- if ( '' === rtrim( $this->result->$stream, "\n" ) ) {
- throw new Exception( $this->result );
- }
- }
-
- /**
- * Expect STDOUT or STDERR to be a version string comparing to the given version.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP install
- * When I run `wp core version
- * Then STDOUT should be a version string >= 6.8
- * ```
- *
- * @access public
- *
- * @Then /^(STDOUT|STDERR) should be a version string (<|<=|>|>=|==|=|!=|<>) ([+\w.{}-]+)$/
- *
- * @param string $stream
- * @param string $operator
- * @param string $goal_ver
- */
- public function then_stdout_stderr_should_be_a_specific_version_string( $stream, $operator, $goal_ver ): void {
- $goal_ver = $this->replace_variables( $goal_ver );
- $stream = strtolower( $stream );
- if ( false === version_compare( trim( $this->result->$stream, "\n" ), $goal_ver, $operator ) ) {
- throw new Exception( $this->result );
- }
- }
-
- /**
- * Expect a certain file or directory to (not) exist or (not) contain certain contents.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp core download`
- * Then the wp-settings.php file should exist
- * And the wp-content directory should exist
- * And the {RUN_DIR} directory should contain:
- * """
- * index.php
- * license.txt
- * """
- * And the wp-config.php file should contain:
- * """
- * That's all, stop editing! Happy publishing.
- * """
- * ```
- *
- * @access public
- *
- * @Then /^the (.+) (file|directory) should( strictly)? (exist|not exist|be:|contain:|not contain:)$/
- *
- * @param string $path File/directory path.
- * @param string $type Type, either 'file' or 'directory'.
- * @param string $strictly Whether it's a strict check.
- * @param string $action Expected status.
- * @param string $expected Expected content.
- */
- public function then_a_specific_file_folder_should_exist( $path, $type, $strictly, $action, $expected = null ): void {
- $path = $this->replace_variables( $path );
-
- // If it's a relative path, make it relative to the current test dir.
- if ( '/' !== $path[0] ) {
- $path = $this->variables['RUN_DIR'] . "/$path";
- }
-
- $exists = static function ( $path ) use ( $type ) {
- // Clear the stat cache for the path first to avoid
- // potentially inaccurate results when files change outside of PHP.
- // See https://www.php.net/manual/en/function.clearstatcache.php
- clearstatcache( false, $path );
-
- if ( 'directory' === $type ) {
- return is_dir( $path );
- }
-
- return file_exists( $path );
- };
-
- switch ( $action ) {
- case 'exist':
- if ( ! $exists( $path ) ) {
- throw new Exception( "$path doesn't exist." );
- }
- break;
- case 'not exist':
- if ( $exists( $path ) ) {
- throw new Exception( "$path exists." );
- }
- break;
- default:
- if ( ! $exists( $path ) ) {
- throw new Exception( "$path doesn't exist." );
- }
- $action = substr( $action, 0, -1 );
- $expected = $this->replace_variables( (string) $expected );
- $contents = '';
- if ( 'file' === $type ) {
- $contents = file_get_contents( $path );
- } elseif ( 'directory' === $type ) {
- $files = glob( rtrim( $path, '/' ) . '/*' );
- foreach ( $files as &$file ) {
- $file = str_replace( $path . '/', '', $file );
- }
- $contents = implode( PHP_EOL, $files );
- }
- $this->check_string( $contents, $expected, $action, false, (bool) $strictly );
- }
- }
-
- /**
- * Match file contents against a regex.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp scaffold plugin hello-world`
- * Then the contents of the wp-content/plugins/hello-world/languages/hello-world.pot file should match /X-Generator:\s/
- * ```
- *
- * @access public
- *
- * @Then /^the contents of the (.+) file should( not)? match (((\/.+\/)|(#.+#))([a-z]+)?)$/
- *
- * @param string $path
- * @param bool $not
- * @param string $expected
- */
- public function then_the_contents_of_a_specific_file_should_match( $path, $not, $expected ): void {
- $path = $this->replace_variables( $path );
- $expected = $this->replace_variables( $expected );
-
- // If it's a relative path, make it relative to the current test dir.
- if ( '/' !== $path[0] ) {
- $path = $this->variables['RUN_DIR'] . "/$path";
- }
- $contents = file_get_contents( $path );
- if ( $not ) {
- $this->assert_not_regex( $expected, $contents );
- } else {
- $this->assert_regex( $expected, $contents );
- }
- }
-
- /**
- * Match STDOUT or STDERR against a regex.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp dist-archive wp-content/plugins/hello-world`
- * Then STDOUT should match /^Success: Created hello-world.0.1.0.zip \(Size: \d+(?:\.\d*)? [a-zA-Z]{1,3}\)$/
- * ```
- *
- * @access public
- *
- * @Then /^(STDOUT|STDERR) should( not)? match (((\/.+\/)|(#.+#))([a-z]+)?)$/
- *
- * @param string $stream
- * @param bool $not
- * @param string $expected
- */
- public function then_stdout_stderr_should_match_a_string( $stream, $not, $expected ): void {
- $expected = $this->replace_variables( $expected );
-
- $stream = strtolower( $stream );
- if ( $not ) {
- $this->assert_not_regex( $expected, $this->result->$stream );
- } else {
- $this->assert_regex( $expected, $this->result->$stream );
- }
- }
-
- /**
- * Expect an email to be sent (or not).
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp user reset-password 1`
- * Then an email should be sent
- * ```
- *
- * @access public
- *
- * @Then /^an email should (be sent|not be sent)$/
- *
- * @param string $expected Expected status, either 'be sent' or 'not be sent'.
- */
- public function then_an_email_should_be_sent( $expected ): void {
- if ( 'be sent' === $expected ) {
- $this->assert_not_equals( 0, $this->email_sends );
- } elseif ( 'not be sent' === $expected ) {
- $this->assert_equals( 0, $this->email_sends );
- } else {
- throw new Exception( 'Invalid expectation' );
- }
- }
-
- /**
- * Expect the HTTP status code for visiting `http://localhost:8080`.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP installation with Composer
- * And a PHP built-in web server to serve 'WordPress'
- * Then the HTTP status code should be 200
- * ```
- *
- * @access public
- *
- * @Then the HTTP status code should be :code
- *
- * @param int $return_code Expected HTTP status code.
- */
- public function then_the_http_status_code_should_be( $return_code ): void {
- // @phpstan-ignore staticMethod.deprecatedClass
- $response = Requests::request( 'http://localhost:8080' );
- $this->assert_equals( $return_code, $response->status_code );
- }
-}
diff --git a/src/Context/WhenStepDefinitions.php b/src/Context/WhenStepDefinitions.php
deleted file mode 100644
index 2de8b3e2e..000000000
--- a/src/Context/WhenStepDefinitions.php
+++ /dev/null
@@ -1,157 +0,0 @@
- 'run_check_stderr',
- 'try' => 'run',
- );
- $method = $map[ $mode ];
-
- return $proc->$method();
- }
-
- /**
- * Capture the number of sent emails by parsing STDOUT.
- *
- * @param string $stdout
- * @return array{string, int}
- */
- public function wpcli_tests_capture_email_sends( $stdout ): array {
- $stdout = preg_replace( '#WP-CLI test suite: Sent email to.+\n?#', '', $stdout, -1, $email_sends );
-
- return array( $stdout, $email_sends );
- }
-
- /**
- * Launch a given command in the background.
- *
- * ```
- * Scenario: My example scenario
- * Given a WP install
- * And I launch in the background `wp server --host=localhost --port=8181`
- * ...
- * ```
- *
- * @access public
- *
- * @When /^I launch in the background `([^`]+)`$/
- *
- * @param string $cmd Command to run.
- */
- public function when_i_launch_in_the_background( $cmd ): void {
- $this->background_proc( $cmd );
- }
-
- /**
- * Run or try a given command.
- *
- * `run` expects an exit code 0, whereas `try` allows for non-zero exit codes.
- *
- * So if using `run` and the command errors, the step will fail.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp core version`
- * Then STDOUT should contain:
- * """
- * 6.8
- * """
- *
- * Scenario: My other scenario
- * When I try `wp i18n make-pot foo bar/baz.pot`
- * Then STDERR should contain:
- * """
- * Error: Not a valid source directory.
- * """
- * And the return code should be 1
- * ```
- *
- * @access public
- *
- * @When /^I (run|try) `([^`]+)`$/
- *
- * @param string $mode Mode, either 'run' or 'try'.
- * @param string $cmd Command to execute.
- */
- public function when_i_run( $mode, $cmd ): void {
- $cmd = $this->replace_variables( $cmd );
- $this->result = $this->wpcli_tests_invoke_proc( $this->proc( $cmd ), $mode );
- list( $this->result->stdout, $this->email_sends ) = $this->wpcli_tests_capture_email_sends( $this->result->stdout );
- }
-
- /**
- * Run or try a given command in a subdirectory.
- *
- * `run` expects an exit code 0, whereas `try` allows for non-zero exit codes.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp core is-installed`
- * Then STDOUT should be empty
- *
- * When I run `wp core is-installed` from 'foo/wp-content'
- * Then STDOUT should be empty
- * ```
- *
- * @access public
- *
- * @When /^I (run|try) `([^`]+)` from '([^\s]+)'$/
- *
- * @param string $mode Mode, either 'run' or 'try'.
- * @param string $cmd Command to execute.
- * @param string $subdir Directory.
- */
- public function when_i_run_from_a_subfolder( $mode, $cmd, $subdir ): void {
- $cmd = $this->replace_variables( $cmd );
- $this->result = $this->wpcli_tests_invoke_proc( $this->proc( $cmd, array(), $subdir ), $mode );
- list( $this->result->stdout, $this->email_sends ) = $this->wpcli_tests_capture_email_sends( $this->result->stdout );
- }
-
- /**
- * Run or try the previous command again.
- *
- * `run` expects an exit code 0, whereas `try` allows for non-zero exit codes.
- *
- * ```
- * Scenario: My example scenario
- * When I run `wp site option update admin_user_id 1`
- * Then STDOUT should contain:
- * """
- * Success: Updated 'admin_user_id' site option.
- * """
- *
- * When I run the previous command again
- * Then STDOUT should contain:
- * """
- * Success: Value passed for 'admin_user_id' site option is unchanged.
- * """
- * ```
- *
- * @access public
- *
- * @When /^I (run|try) the previous command again$/
- *
- * @param string $mode Mode, either 'run' or 'try'
- */
- public function when_i_run_the_previous_command_again( $mode ): void {
- if ( ! isset( $this->result ) ) {
- throw new Exception( 'No previous command.' );
- }
-
- $proc = Process::create( $this->result->command, $this->result->cwd, $this->result->env );
- $this->result = $this->wpcli_tests_invoke_proc( $proc, $mode );
- list( $this->result->stdout, $this->email_sends ) = $this->wpcli_tests_capture_email_sends( $this->result->stdout );
- }
-}
diff --git a/src/PHPStan/GetFlagValueFunctionDynamicReturnTypeExtension.php b/src/PHPStan/GetFlagValueFunctionDynamicReturnTypeExtension.php
deleted file mode 100644
index 57941e980..000000000
--- a/src/PHPStan/GetFlagValueFunctionDynamicReturnTypeExtension.php
+++ /dev/null
@@ -1,114 +0,0 @@
-getName() === 'WP_CLI\Utils\get_flag_value';
- }
-
- public function getTypeFromFunctionCall(
- FunctionReflection $functionReflection,
- FuncCall $functionCall,
- Scope $scope
- ): Type {
- $args = $functionCall->getArgs();
-
- if ( count( $args ) < 2 ) {
- // Not enough arguments, fall back to the function's declared return type.
- return $functionReflection->getVariants()[0]->getReturnType();
- }
-
- $assocArgsType = $scope->getType( $args[0]->value );
- $flagArgType = $scope->getType( $args[1]->value );
-
- // 2. Determine the default type
- $defaultType = isset( $args[2] ) ? $scope->getType( $args[2]->value ) : new NullType();
-
- $flagConstantStrings = $flagArgType->getConstantStrings();
-
- if ( count( $flagConstantStrings ) !== 1 ) {
- // Flag name is dynamic or not a string.
- // Return type is a union of all possible values in $assoc_args + default type.
- return $this->getDynamicFlagFallbackType( $assocArgsType, $defaultType );
- }
-
- // 4. Flag is a single constant string.
- $flagValue = $flagConstantStrings[0]->getValue();
-
- // 4.a. If $assoc_args is a single ConstantArray:
- $assocConstantArrays = $assocArgsType->getConstantArrays();
- if ( count( $assocConstantArrays ) === 1 ) {
- $assocArgsConstantArray = $assocConstantArrays[0];
- $keyTypes = $assocArgsConstantArray->getKeyTypes();
- $valueTypes = $assocArgsConstantArray->getValueTypes();
- $resolvedValueType = null;
-
- foreach ( $keyTypes as $index => $keyType ) {
- $keyConstantStrings = $keyType->getConstantStrings();
- if ( count( $keyConstantStrings ) === 1 && $keyConstantStrings[0]->getValue() === $flagValue ) {
- $resolvedValueType = $valueTypes[ $index ];
- break;
- }
- }
-
- if ( null !== $resolvedValueType ) {
- // Key definitely exists and has a resolved type.
- return $resolvedValueType;
- } else {
- // Key definitely does not exist in this constant array.
- return $defaultType;
- }
- }
-
- // 4.b. $assoc_args is not a single ConstantArray (but $flagValue is known):
- // Use getOffsetValueType for other array-like types.
- $valueForKeyType = $assocArgsType->getOffsetValueType( new ConstantStringType( $flagValue ) );
-
- // The key might exist, or its presence is unknown.
- // The function returns $assoc_args[$flag] if set, otherwise $default.
- return TypeCombinator::union( $valueForKeyType, $defaultType );
- }
-
- /**
- * Handles the case where the flag name is not a single known constant string.
- * The return type is a union of all possible values in $assocArgsType and $defaultType.
- */
- private function getDynamicFlagFallbackType( Type $assocArgsType, Type $defaultType ): Type {
- $possibleValueTypes = [];
-
- $assocConstantArrays = $assocArgsType->getConstantArrays();
- if ( count( $assocConstantArrays ) === 1 ) { // It's one specific constant array
- $constantArray = $assocConstantArrays[0];
- if ( count( $constantArray->getValueTypes() ) > 0 ) {
- $possibleValueTypes = $constantArray->getValueTypes();
- }
- } else {
- $possibleValueTypes[] = new MixedType();
- }
-
- if ( empty( $possibleValueTypes ) ) {
- return $defaultType;
- }
-
- return TypeCombinator::union( $defaultType, ...$possibleValueTypes );
- }
-}
diff --git a/src/PHPStan/ParseUrlFunctionDynamicReturnTypeExtension.php b/src/PHPStan/ParseUrlFunctionDynamicReturnTypeExtension.php
deleted file mode 100644
index 55fef8974..000000000
--- a/src/PHPStan/ParseUrlFunctionDynamicReturnTypeExtension.php
+++ /dev/null
@@ -1,169 +0,0 @@
-|null */
- private $componentTypesPairedConstants = null;
-
- /** @var array|null */
- private $componentTypesPairedStrings = null;
-
- /** @var \PHPStan\Type\Type|null */
- private $allComponentsTogetherType = null;
-
- public function isFunctionSupported( FunctionReflection $functionReflection ): bool {
- return $functionReflection->getName() === 'WP_CLI\Utils\parse_url';
- }
-
- public function getTypeFromFunctionCall( FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope ): ?Type {
- if ( count( $functionCall->getArgs() ) < 1 ) {
- return null;
- }
-
- $this->cacheReturnTypes();
-
- $componentType = new ConstantIntegerType( -1 );
-
- if ( count( $functionCall->getArgs() ) > 1 ) {
- $componentType = $scope->getType( $functionCall->getArgs()[1]->value );
-
- if ( ! $componentType->isConstantValue()->yes() ) {
- return $this->createAllComponentsReturnType();
- }
-
- $componentType = $componentType->toInteger();
-
- if ( ! $componentType instanceof ConstantIntegerType ) {
- return $this->createAllComponentsReturnType();
- }
- }
-
- $urlType = $scope->getType( $functionCall->getArgs()[0]->value );
- if ( count( $urlType->getConstantStrings() ) > 0 ) {
- $types = [];
- foreach ( $urlType->getConstantStrings() as $constantString ) {
- try {
- // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
- $result = @parse_url( $constantString->getValue(), $componentType->getValue() );
- } catch ( \Error $e ) {
- $types[] = new ConstantBooleanType( false );
- continue;
- }
-
- $types[] = $scope->getTypeFromValue( $result );
- }
-
- return TypeCombinator::union( ...$types );
- }
-
- if ( $componentType->getValue() === -1 ) {
- return TypeCombinator::union( $this->createComponentsArray(), new ConstantBooleanType( false ) );
- }
-
- return $this->componentTypesPairedConstants[ $componentType->getValue() ] ?? new ConstantBooleanType( false );
- }
-
- private function createAllComponentsReturnType(): Type {
- if ( null === $this->allComponentsTogetherType ) {
- $returnTypes = [
- new ConstantBooleanType( false ),
- new NullType(),
- IntegerRangeType::fromInterval( 0, 65535 ),
- new StringType(),
- $this->createComponentsArray(),
- ];
-
- $this->allComponentsTogetherType = TypeCombinator::union( ...$returnTypes );
- }
-
- return $this->allComponentsTogetherType;
- }
-
- private function createComponentsArray(): Type {
- $builder = ConstantArrayTypeBuilder::createEmpty();
-
- if ( null === $this->componentTypesPairedStrings ) {
- throw new \PHPStan\ShouldNotHappenException();
- }
-
- foreach ( $this->componentTypesPairedStrings as $componentName => $componentValueType ) {
- $builder->setOffsetValueType( new ConstantStringType( $componentName ), $componentValueType, true );
- }
-
- return $builder->getArray();
- }
-
- private function cacheReturnTypes(): void {
- if ( null !== $this->componentTypesPairedConstants ) {
- return;
- }
-
- $stringType = new StringType();
- $port = IntegerRangeType::fromInterval( 0, 65535 );
- $falseType = new ConstantBooleanType( false );
- $nullType = new NullType();
-
- $stringOrFalseOrNull = TypeCombinator::union( $stringType, $falseType, $nullType );
- $portOrFalseOrNull = TypeCombinator::union( $port, $falseType, $nullType );
-
- $this->componentTypesPairedConstants = [
- PHP_URL_SCHEME => $stringOrFalseOrNull,
- PHP_URL_HOST => $stringOrFalseOrNull,
- PHP_URL_PORT => $portOrFalseOrNull,
- PHP_URL_USER => $stringOrFalseOrNull,
- PHP_URL_PASS => $stringOrFalseOrNull,
- PHP_URL_PATH => $stringOrFalseOrNull,
- PHP_URL_QUERY => $stringOrFalseOrNull,
- PHP_URL_FRAGMENT => $stringOrFalseOrNull,
- ];
-
- $this->componentTypesPairedStrings = [
- 'scheme' => $stringType,
- 'host' => $stringType,
- 'port' => $port,
- 'user' => $stringType,
- 'pass' => $stringType,
- 'path' => $stringType,
- 'query' => $stringType,
- 'fragment' => $stringType,
- ];
- }
-}
diff --git a/src/PHPStan/WPCliRuncommandDynamicReturnTypeExtension.php b/src/PHPStan/WPCliRuncommandDynamicReturnTypeExtension.php
deleted file mode 100644
index 379e03b5a..000000000
--- a/src/PHPStan/WPCliRuncommandDynamicReturnTypeExtension.php
+++ /dev/null
@@ -1,192 +0,0 @@
-getName() === 'runcommand';
- }
-
- public function getTypeFromStaticMethodCall(
- MethodReflection $methodReflection,
- StaticCall $methodCall,
- Scope $scope
- ): Type {
- $args = $methodCall->getArgs();
-
- /** @var ConstantBooleanType|ConstantStringType $returnOption */
- $returnOption = new ConstantBooleanType( true );
- /** @var ConstantBooleanType|ConstantStringType $parseOption */
- $parseOption = new ConstantBooleanType( false );
- /** @var ConstantBooleanType $exitOnErrorOption */
- $exitOnErrorOption = new ConstantBooleanType( true );
-
- $optionsAreStaticallyKnown = true;
-
- if ( isset( $args[1] ) && $args[1] instanceof Arg ) {
- $optionsNode = $args[1]->value;
- $optionsType = $scope->getType( $optionsNode );
-
- if ( $optionsType->isConstantArray()->yes() ) {
- $constantArrayTypes = $optionsType->getConstantArrays();
- if ( count( $constantArrayTypes ) === 1 ) {
- $constantArrayType = $constantArrayTypes[0];
- $keyTypes = $constantArrayType->getKeyTypes();
- $valueTypes = $constantArrayType->getValueTypes();
-
- foreach ( $keyTypes as $i => $keyType ) {
- $keyConstantStrings = $keyType->getConstantStrings();
- if ( count( $keyConstantStrings ) !== 1 ) {
- $optionsAreStaticallyKnown = false;
- break;
- }
- $keyName = $keyConstantStrings[0]->getValue();
- $currentOptionValueType = $valueTypes[ $i ];
-
- switch ( $keyName ) {
- case 'return':
- $valueConstantStrings = $currentOptionValueType->getConstantStrings();
- if ( count( $valueConstantStrings ) === 1 && $currentOptionValueType->isScalar()->yes() ) {
- $returnOption = $valueConstantStrings[0];
- } elseif ( $currentOptionValueType->isTrue()->yes() ) {
- $returnOption = new ConstantBooleanType( true );
- } elseif ( $currentOptionValueType->isFalse()->yes() ) {
- $returnOption = new ConstantBooleanType( false );
- } else {
- $optionsAreStaticallyKnown = false;
- }
- break;
- case 'parse':
- $valueConstantStrings = $currentOptionValueType->getConstantStrings();
- $isExactlyJsonString = ( count( $valueConstantStrings ) === 1 && $valueConstantStrings[0]->getValue() === 'json' && $currentOptionValueType->isScalar()->yes() );
-
- if ( $isExactlyJsonString ) {
- $parseOption = $valueConstantStrings[0];
- } elseif ( $$currentOptionValueType->isFalse()->yes() ) {
- $parseOption = new ConstantBooleanType( false );
- } else {
- // Not a single, clear constant we handle for a "known" path
- $parseOption = new ConstantBooleanType( false ); // Default effect
- $optionsAreStaticallyKnown = false;
- }
- break;
- case 'exit_error':
- if ( $currentOptionValueType->isTrue()->yes() ) {
- $exitOnErrorOption = new ConstantBooleanType( true );
- } elseif ( $currentOptionValueType->isFalse()->yes() ) {
- $exitOnErrorOption = new ConstantBooleanType( false );
- } else {
- $optionsAreStaticallyKnown = false;
- }
- break;
- }
- if ( ! $optionsAreStaticallyKnown ) {
- break;
- }
- }
- } else {
- $optionsAreStaticallyKnown = false;
- }
- } else {
- $optionsAreStaticallyKnown = false;
- }
- }
-
- if ( ! $optionsAreStaticallyKnown ) {
- return TypeCombinator::union( $this->getFallbackUnionTypeWithoutNever(), new NeverType() );
- }
-
- $normalReturnType = $this->determineNormalReturnType( $returnOption, $parseOption );
-
- if ( $exitOnErrorOption->getValue() === true ) {
- if ( $normalReturnType instanceof NeverType ) {
- return $normalReturnType;
- }
- return TypeCombinator::union( $normalReturnType, new NeverType() );
- }
-
- return $normalReturnType;
- }
-
- /**
- * @param ConstantBooleanType|ConstantStringType $returnOptionValue
- * @param ConstantBooleanType|ConstantStringType $parseOptionValue
- */
- private function determineNormalReturnType( Type $returnOptionValue, Type $parseOptionValue ): Type {
- $returnConstantStrings = $returnOptionValue->getConstantStrings();
- $return_val = count( $returnConstantStrings ) === 1 ? $returnConstantStrings[0]->getValue() : null;
-
- $parseConstantStrings = $parseOptionValue->getConstantStrings();
- $parseIsJson = count( $parseConstantStrings ) === 1 && $parseConstantStrings[0]->getValue() === 'json';
-
- if ( 'all' === $return_val ) {
- return $this->createAllObjectType();
- }
- if ( 'return_code' === $return_val ) {
- return new IntegerType();
- }
- if ( 'stderr' === $return_val ) {
- return new StringType();
- }
- if ( $returnOptionValue->isTrue()->yes() || 'stdout' === $return_val ) {
- if ( $parseIsJson ) {
- return TypeCombinator::union(
- new ArrayType( new MixedType(), new MixedType() ),
- new NullType()
- );
- }
- return new StringType();
- }
- if ( $returnOptionValue->isFalse()->yes() ) {
- return new NullType();
- }
-
- return new MixedType( true );
- }
-
- private function createAllObjectType(): Type {
- $propertyTypes = [
- 'stdout' => new StringType(),
- 'stderr' => new StringType(),
- 'return_code' => new IntegerType(),
- ];
- $optionalProperties = [];
- return new ObjectShapeType( $propertyTypes, $optionalProperties );
- }
-
- private function getFallbackUnionTypeWithoutNever(): Type {
- return TypeCombinator::union(
- new StringType(),
- new IntegerType(),
- $this->createAllObjectType(),
- new ArrayType( new MixedType(), new MixedType() ),
- new ObjectWithoutClassType(),
- new NullType()
- );
- }
-}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
deleted file mode 100644
index 80babbe09..000000000
--- a/tests/bootstrap.php
+++ /dev/null
@@ -1,65 +0,0 @@
-[^"]*)"/';
- $result = preg_match( $pattern, $config, $matches );
- if ( isset( $matches['bootstrap'] ) && file_exists( $matches['bootstrap'] ) ) {
- include_once PACKAGE_ROOT . '/' . $matches['bootstrap'];
- }
- }
-}
-
-wpcli_tests_include_config(
- [
- 'phpunit.xml',
- '.phpunit.xml',
- 'phpunit.xml.dist',
- '.phpunit.xml.dist',
- ]
-);
diff --git a/tests/data/get_flag_value.php b/tests/data/get_flag_value.php
deleted file mode 100644
index 9a6b39c26..000000000
--- a/tests/data/get_flag_value.php
+++ /dev/null
@@ -1,62 +0,0 @@
- 'bar',
- 'baz' => 'qux',
- ],
- 'foo'
-);
-assertType( "'bar'", $value );
-
-$value = get_flag_value(
- [
- 'foo' => 'bar',
- 'baz' => 'qux',
- ],
- 'bar'
-);
-assertType( 'null', $value );
-
-$value = get_flag_value(
- [
- 'foo' => 'bar',
- 'baz' => 'qux',
- ],
- 'bar',
- 123
-);
-assertType( '123', $value );
-
-$value = get_flag_value(
- [
- 'foo' => 'bar',
- 'baz' => true,
- ],
- 'baz',
- 123
-);
-assertType( 'true', $value );
-
-$assoc_args = [
- 'foo' => 'bar',
- 'baz' => true,
-];
-$key = 'baz';
-
-$value = get_flag_value( $assoc_args, $key, 123 );
-assertType( 'true', $value );
-
-$value = get_flag_value( $assoc_args, $key2, 123 );
-assertType( "123|'bar'|true", $value );
diff --git a/tests/data/parse_url.php b/tests/data/parse_url.php
deleted file mode 100644
index dd5ecaded..000000000
--- a/tests/data/parse_url.php
+++ /dev/null
@@ -1,53 +0,0 @@
-, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|int<0, 65535>|string|false|null', $value );
-
-$value = parse_url( 'http://def.abc', PHP_URL_FRAGMENT );
-assertType( 'null', $value );
-
-$value = parse_url( 'http://def.abc#this-is-fragment', PHP_URL_FRAGMENT );
-assertType( "'this-is-fragment'", $value );
-
-$value = parse_url( 'http://def.abc#this-is-fragment', 9999 );
-assertType( 'false', $value );
-
-$value = parse_url( $string, 9999 );
-assertType( 'false', $value );
-
-$value = parse_url( $string, PHP_URL_PORT );
-assertType( 'int<0, 65535>|false|null', $value );
-
-$value = parse_url( $string );
-assertType( 'array{scheme?: string, host?: string, port?: int<0, 65535>, user?: string, pass?: string, path?: string, query?: string, fragment?: string}|false', $value );
-
-/** @var 'http://def.abc'|'https://example.com' $union */
-$union = $union;
-assertType( "array{scheme: 'http', host: 'def.abc'}|array{scheme: 'https', host: 'example.com'}", parse_url( $union ) );
-
-/** @var 'http://def.abc#fragment1'|'https://example.com#fragment2' $union */
-$union = $union;
-assertType( "'fragment1'|'fragment2'", parse_url( $union, PHP_URL_FRAGMENT ) );
diff --git a/tests/data/runcommand.php b/tests/data/runcommand.php
deleted file mode 100644
index 3f1828bbe..000000000
--- a/tests/data/runcommand.php
+++ /dev/null
@@ -1,67 +0,0 @@
- true ] );
-assertType( 'string', $value );
-
-$value = WP_CLI::runcommand( 'plugin list --format=json', [ 'return' => false ] );
-assertType( 'null', $value );
-
-$value = WP_CLI::runcommand( 'plugin list --format=json', [ 'return' => 'all' ] );
-assertType( 'object{stdout: string, stderr: string, return_code: int}', $value );
-
-$value = WP_CLI::runcommand( 'plugin list --format=json', [ 'return' => 'stdout' ] );
-assertType( 'string', $value );
-
-$value = WP_CLI::runcommand( 'plugin list --format=json', [ 'return' => 'stderr' ] );
-assertType( 'string', $value );
-
-$value = WP_CLI::runcommand( 'plugin list --format=json', [ 'return' => 'return_code' ] );
-assertType( 'int', $value );
-
-$value = WP_CLI::runcommand(
- 'plugin list --format=json',
- [
- 'return' => true,
- 'parse' => 'json',
- ]
-);
-assertType( 'array|null', $value );
-
-$value = WP_CLI::runcommand(
- 'plugin list --format=json',
- [
- 'return' => 'stdout',
- 'parse' => 'json',
- ]
-);
-assertType( 'array|null', $value );
-
-$value = WP_CLI::runcommand(
- 'plugin list --format=json',
- [
- 'return' => 'stdout',
- 'exit_error' => true,
- ]
-);
-assertType( 'string', $value );
-
-$value = WP_CLI::runcommand(
- 'plugin list --format=json',
- [
- 'return' => 'stdout',
- 'exit_error' => false,
- ]
-);
-assertType( 'string', $value );
diff --git a/tests/includes/TestCase.php b/tests/includes/TestCase.php
deleted file mode 100644
index 1d44ee14d..000000000
--- a/tests/includes/TestCase.php
+++ /dev/null
@@ -1,12 +0,0 @@
-
- */
- public static function dataFileAsserts(): iterable {
- // Path to a file with actual asserts of expected types:
- yield from self::gatherAssertTypes( dirname( __DIR__, 2 ) . '/data/parse_url.php' );
- yield from self::gatherAssertTypes( dirname( __DIR__, 2 ) . '/data/get_flag_value.php' );
- yield from self::gatherAssertTypes( dirname( __DIR__, 2 ) . '/data/runcommand.php' );
- }
-
- /**
- * @dataProvider dataFileAsserts
- * @param array ...$args
- */
- #[DataProvider( 'dataFileAsserts' )] // phpcs:ignore PHPCompatibility.Attributes.NewAttributes.PHPUnitAttributeFound
- public function testFileAsserts( string $assertType, string $file, ...$args ): void {
- $this->assertFileAsserts( $assertType, $file, ...$args );
- }
-
- public static function getAdditionalConfigFiles(): array {
- return [ dirname( __DIR__, 3 ) . '/extension.neon' ];
- }
-}
diff --git a/tests/tests/TestBehatTags.php b/tests/tests/TestBehatTags.php
deleted file mode 100644
index 5b3ca49c1..000000000
--- a/tests/tests/TestBehatTags.php
+++ /dev/null
@@ -1,258 +0,0 @@
-temp_dir = Utils\get_temp_dir() . uniqid( 'wp-cli-test-behat-tags-', true );
- mkdir( $this->temp_dir );
- mkdir( $this->temp_dir . '/features' );
- }
-
- protected function tear_down(): void {
-
- if ( $this->temp_dir && file_exists( $this->temp_dir ) ) {
- foreach ( glob( $this->temp_dir . '/features/*' ) as $feature_file ) {
- unlink( $feature_file );
- }
- rmdir( $this->temp_dir . '/features' );
- rmdir( $this->temp_dir );
- }
-
- parent::tear_down();
- }
-
- /**
- * @dataProvider data_behat_tags_wp_version_github_token
- *
- * @param string $env
- * @param string $expected
- */
- #[DataProvider( 'data_behat_tags_wp_version_github_token' )] // phpcs:ignore PHPCompatibility.Attributes.NewAttributes.PHPUnitAttributeFound
- public function test_behat_tags_wp_version_github_token( $env, $expected ): void {
- $env_wp_version = getenv( 'WP_VERSION' );
- $env_github_token = getenv( 'GITHUB_TOKEN' );
- $db_type = getenv( 'WP_CLI_TEST_DBTYPE' );
-
- putenv( 'WP_VERSION' );
- putenv( 'GITHUB_TOKEN' );
-
- $behat_tags = dirname( dirname( __DIR__ ) ) . '/utils/behat-tags.php';
-
- $contents = '@require-wp-4.6 @require-wp-4.8 @require-wp-4.9 @less-than-wp-4.6 @less-than-wp-4.8 @less-than-wp-4.9';
- file_put_contents( $this->temp_dir . '/features/wp_version.feature', $contents );
-
- $output = exec( "cd {$this->temp_dir}; $env php $behat_tags" );
-
- $expected .= '&&~@broken';
- if ( in_array( $env, array( 'WP_VERSION=trunk', 'WP_VERSION=nightly' ), true ) ) {
- $expected .= '&&~@broken-trunk';
- }
-
- switch ( $db_type ) {
- case 'mariadb':
- $expected .= '&&~@require-mysql';
- $expected .= '&&~@require-sqlite';
- break;
- case 'sqlite':
- $expected .= '&&~@require-mariadb';
- $expected .= '&&~@require-mysql';
- $expected .= '&&~@require-mysql-or-mariadb';
- break;
- case 'mysql':
- default:
- $expected .= '&&~@require-mariadb';
- $expected .= '&&~@require-sqlite';
- break;
- }
-
- $this->assertSame( '--tags=' . $expected, $output );
-
- putenv( false === $env_wp_version ? 'WP_VERSION' : "WP_VERSION=$env_wp_version" );
- putenv( false === $env_github_token ? 'GITHUB_TOKEN' : "GITHUB_TOKEN=$env_github_token" );
- }
-
- /**
- * @return array
- */
- public static function data_behat_tags_wp_version_github_token(): array {
- return array(
- array( 'WP_VERSION=4.5', '~@require-wp-4.6&&~@require-wp-4.8&&~@require-wp-4.9&&~@github-api' ),
- array( 'WP_VERSION=4.6', '~@require-wp-4.8&&~@require-wp-4.9&&~@less-than-wp-4.6&&~@github-api' ),
- array( 'WP_VERSION=4.7', '~@require-wp-4.8&&~@require-wp-4.9&&~@less-than-wp-4.6&&~@github-api' ),
- array( 'WP_VERSION=4.8', '~@require-wp-4.9&&~@less-than-wp-4.6&&~@less-than-wp-4.8&&~@github-api' ),
- array( 'WP_VERSION=4.9', '~@less-than-wp-4.6&&~@less-than-wp-4.8&&~@less-than-wp-4.9&&~@github-api' ),
- array( 'WP_VERSION=5.0', '~@less-than-wp-4.6&&~@less-than-wp-4.8&&~@less-than-wp-4.9&&~@github-api' ),
- array( 'WP_VERSION=latest', '~@less-than-wp-4.6&&~@less-than-wp-4.8&&~@less-than-wp-4.9&&~@github-api' ),
- array( 'WP_VERSION=trunk', '~@less-than-wp-4.6&&~@less-than-wp-4.8&&~@less-than-wp-4.9&&~@github-api' ),
- array( 'WP_VERSION=nightly', '~@less-than-wp-4.6&&~@less-than-wp-4.8&&~@less-than-wp-4.9&&~@github-api' ),
- array( '', '~@less-than-wp-4.6&&~@less-than-wp-4.8&&~@less-than-wp-4.9&&~@github-api' ),
- array( 'GITHUB_TOKEN=blah', '~@less-than-wp-4.6&&~@less-than-wp-4.8&&~@less-than-wp-4.9' ),
- );
- }
-
- public function test_behat_tags_php_version(): void {
- $env_github_token = getenv( 'GITHUB_TOKEN' );
-
- putenv( 'GITHUB_TOKEN' );
-
- $behat_tags = dirname( dirname( __DIR__ ) ) . '/utils/behat-tags.php';
-
- $php_version = substr( PHP_VERSION, 0, 3 );
- $contents = '';
- $expected = '';
-
- if ( '7.2' === $php_version ) {
- $contents = '@require-php-7.1 @require-php-7.2 @require-php-7.3 @less-than-php-7.1 @less-than-php-7.2 @less-than-php-7.3';
- $expected = '~@require-php-7.3&&~@less-than-php-7.1&&~@less-than-php-7.2';
- } elseif ( '7.3' === $php_version ) {
- $contents = '@require-php-7.2 @require-php-7.3 @require-php-7.4 @less-than-php-7.2 @less-than-php-7.3 @less-than-php-7.4';
- $expected = '~@require-php-7.4&&~@less-than-php-7.2&&~@less-than-php-7.3';
- } elseif ( '7.4' === $php_version ) {
- $contents = '@require-php-7.3 @require-php-7.4 @require-php-8.0 @less-than-php-7.3 @less-than-php-7.4 @less-than-php-8.0';
- $expected = '~@require-php-8.0&&~@less-than-php-7.3&&~@less-than-php-7.4';
- } elseif ( '8.0' === $php_version ) {
- $contents = '@require-php-7.4 @require-php-8.0 @require-php-8.1 @less-than-php-7.4 @less-than-php-8.0 @less-than-php-8.1';
- $expected = '~@require-php-8.1&&~@less-than-php-7.4&&~@less-than-php-8.0';
- } elseif ( '8.1' === $php_version ) {
- $contents = '@require-php-8.0 @require-php-8.1 @require-php-8.2 @less-than-php-8.0 @less-than-php-8.1 @less-than-php-8.2';
- $expected = '~@require-php-8.2&&~@less-than-php-8.0&&~@less-than-php-8.1';
- } elseif ( '8.2' === $php_version ) {
- $contents = '@require-php-8.0 @require-php-8.1 @require-php-8.2 @require-php-8.3 @less-than-php-8.0 @less-than-php-8.1 @less-than-php-8.2 @less-than-php-8.3 @less-than-php-8.4';
- $expected = '~@require-php-8.3&&~@less-than-php-8.0&&~@less-than-php-8.1&&~@less-than-php-8.2';
- } elseif ( '8.3' === $php_version ) {
- $contents = '@require-php-8.1 @require-php-8.2 @require-php-8.3 @require-php-8.4 @less-than-php-8.0 @less-than-php-8.1 @less-than-php-8.2 @less-than-php-8.3 @less-than-php-8.4';
- $expected = '~@require-php-8.4&&~@less-than-php-8.0&&~@less-than-php-8.1&&~@less-than-php-8.2&&~@less-than-php-8.3';
- } elseif ( '8.4' === $php_version ) {
- $contents = '@require-php-8.2 @require-php-8.3 @require-php-8.4 @require-php-8.5 @less-than-php-8.0 @less-than-php-8.1 @less-than-php-8.2 @less-than-php-8.3 @less-than-php-8.4 @less-than-php-8.5';
- $expected = '~@require-php-8.5&&~@less-than-php-8.0&&~@less-than-php-8.1&&~@less-than-php-8.2&&~@less-than-php-8.3&&~@less-than-php-8.4';
- } else {
- $this->markTestSkipped( "No test for PHP_VERSION $php_version." );
- }
-
- $expected .= '&&~@github-api&&~@broken';
-
- $db_type = getenv( 'WP_CLI_TEST_DBTYPE' );
- switch ( $db_type ) {
- case 'mariadb':
- $expected .= '&&~@require-mysql';
- $expected .= '&&~@require-sqlite';
- break;
- case 'sqlite':
- $expected .= '&&~@require-mariadb';
- $expected .= '&&~@require-mysql';
- $expected .= '&&~@require-mysql-or-mariadb';
- break;
- case 'mysql':
- default:
- $expected .= '&&~@require-mariadb';
- $expected .= '&&~@require-sqlite';
- break;
- }
-
- file_put_contents( $this->temp_dir . '/features/php_version.feature', $contents );
-
- $output = exec( "cd {$this->temp_dir}; php $behat_tags" );
- $this->assertSame( '--tags=' . $expected, $output );
-
- putenv( false === $env_github_token ? 'GITHUB_TOKEN' : "GITHUB_TOKEN=$env_github_token" );
- }
-
- public function test_behat_tags_extension(): void {
- $env_github_token = getenv( 'GITHUB_TOKEN' );
- $db_type = getenv( 'WP_CLI_TEST_DBTYPE' );
-
- putenv( 'GITHUB_TOKEN' );
-
- $behat_tags = dirname( dirname( __DIR__ ) ) . '/utils/behat-tags.php';
-
- file_put_contents( $this->temp_dir . '/features/extension.feature', '@require-extension-imagick @require-extension-curl' );
-
- $expecteds = array();
-
- switch ( $db_type ) {
- case 'mariadb':
- $expecteds[] = '~@require-mysql';
- $expecteds[] = '~@require-sqlite';
- break;
- case 'sqlite':
- $expecteds[] = '~@require-mariadb';
- $expecteds[] = '~@require-mysql';
- $expecteds[] = '~@require-mysql-or-mariadb';
- break;
- case 'mysql':
- default:
- $expecteds[] = '~@require-mariadb';
- $expecteds[] = '~@require-sqlite';
- break;
- }
-
- if ( ! extension_loaded( 'imagick' ) ) {
- $expecteds[] = '~@require-extension-imagick';
- }
- if ( ! extension_loaded( 'curl' ) ) {
- $expecteds[] = '~@require-extension-curl';
- }
-
- $expected = '--tags=' . implode( '&&', array_merge( array( '~@github-api', '~@broken' ), $expecteds ) );
- $output = exec( "cd {$this->temp_dir}; php $behat_tags" );
- $this->assertSame( $expected, $output );
-
- putenv( false === $env_github_token ? 'GITHUB_TOKEN' : "GITHUB_TOKEN=$env_github_token" );
- }
-
- public function test_behat_tags_db_version(): void {
- $db_type = getenv( 'WP_CLI_TEST_DBTYPE' );
-
- $behat_tags = dirname( dirname( __DIR__ ) ) . '/utils/behat-tags.php';
- require $behat_tags;
- // @phpstan-ignore-next-line
- $db_version = get_db_version();
- $minimum_db_version = $db_version . '.1';
-
- $contents = '';
- $expecteds = array();
-
- switch ( $db_type ) {
- case 'mariadb':
- $contents = "@require-mariadb-$minimum_db_version @less-than-mariadb-$db_version";
- $expecteds[] = '~@require-mysql';
- $expecteds[] = '~@require-sqlite';
- $expecteds[] = "~@require-mariadb-$minimum_db_version";
- $expecteds[] = "~@less-than-mariadb-$db_version";
- break;
- case 'sqlite':
- $expecteds[] = '~@require-mariadb';
- $expecteds[] = '~@require-mysql';
- $expecteds[] = '~@require-mysql-or-mariadb';
- break;
- case 'mysql':
- default:
- $contents = "@require-mysql-$minimum_db_version @less-than-mysql-$db_version";
- $expecteds[] = '~@require-mariadb';
- $expecteds[] = '~@require-sqlite';
- $expecteds[] = "~@require-mysql-$minimum_db_version";
- $expecteds[] = "~@less-than-mysql-$db_version";
- break;
- }
-
- file_put_contents( $this->temp_dir . '/features/extension.feature', $contents );
-
- $expected = '--tags=' . implode( '&&', array_merge( array( '~@github-api', '~@broken' ), $expecteds ) );
- $output = exec( "cd {$this->temp_dir}; php $behat_tags" );
- $this->assertSame( $expected, $output );
- }
-}
diff --git a/utils/behat-tags.php b/utils/behat-tags.php
deleted file mode 100644
index 4da5af53a..000000000
--- a/utils/behat-tags.php
+++ /dev/null
@@ -1,222 +0,0 @@
-/dev/null', $output, $mysql_exit_code );
- if ( 0 === $mysql_exit_code ) {
- $client_binary = 'mysql';
- } else {
- $output = array();
- exec( 'command -v mariadb 2>/dev/null', $output, $mariadb_exit_code );
- if ( 0 === $mariadb_exit_code ) {
- $client_binary = 'mariadb';
- }
- }
-
- if ( null === $client_binary ) {
- // No client binary found, return defaults.
- return array(
- 'type' => 'mysql',
- 'version' => '',
- );
- }
-
- // Build connection parameters from environment variables.
- $host = getenv( 'WP_CLI_TEST_DBHOST' ) ?: 'localhost';
- $user = getenv( 'WP_CLI_TEST_DBROOTUSER' ) ?: 'root';
- $pass = getenv( 'WP_CLI_TEST_DBROOTPASS' );
-
- // Build the command to get the server version.
- $host_parts = explode( ':', $host );
- $host_arg = '-h' . escapeshellarg( $host_parts[0] );
-
- $port_arg = '';
- if ( isset( $host_parts[1] ) ) {
- // Check if it's a port number or socket path.
- if ( is_numeric( $host_parts[1] ) ) {
- $port_arg = ' --port=' . escapeshellarg( $host_parts[1] ) . ' --protocol=tcp';
- } else {
- $port_arg = ' --socket=' . escapeshellarg( $host_parts[1] ) . ' --protocol=socket';
- }
- }
-
- $pass_arg = false !== $pass && '' !== $pass ? '-p' . escapeshellarg( $pass ) : '';
-
- $cmd = sprintf(
- '%s %s %s -u%s %s -e "SELECT VERSION()" --skip-column-names 2>/dev/null',
- escapeshellcmd( $client_binary ),
- $host_arg,
- $port_arg,
- escapeshellarg( $user ),
- $pass_arg
- );
-
- $output = array();
- $return_code = 0;
- exec( $cmd, $output, $return_code );
- $version_string = isset( $output[0] ) ? $output[0] : '';
-
- // If the connection failed, fall back to client binary version.
- if ( 0 !== $return_code || empty( $version_string ) ) {
- $client_version_cmd = sprintf( '%s --version 2>/dev/null', escapeshellcmd( $client_binary ) );
- $version_string = exec( $client_version_cmd );
- }
-
- // Detect database type from server version string.
- $db_type = 'mysql';
- if ( false !== stripos( $version_string, 'mariadb' ) ) {
- $db_type = 'mariadb';
- }
-
- preg_match( '@[0-9]+\.[0-9]+\.[0-9]+@', $version_string, $version );
- $db_version = isset( $version[0] ) ? $version[0] : '';
-
- return array(
- 'type' => $db_type,
- 'version' => $db_version,
- );
-}
-
-function get_db_version() {
- $db_info = get_db_type_and_version();
- return $db_info['version'];
-}
-
-$features_folder = getenv( 'BEHAT_FEATURES_FOLDER' ) ?: 'features';
-$wp_version = getenv( 'WP_VERSION' );
-$wp_version_reqs = array();
-// Only apply @require-wp tags when WP_VERSION isn't 'latest', 'nightly' or 'trunk'.
-// 'latest', 'nightly' and 'trunk' are expected to work with all features.
-if ( $wp_version &&
- ! in_array( $wp_version, array( 'latest', 'nightly', 'trunk' ), true ) ) {
- $wp_version_reqs = array_merge(
- version_tags( 'require-wp', $wp_version, '<', $features_folder ),
- version_tags( 'less-than-wp', $wp_version, '>=', $features_folder )
- );
-} else {
- // But make sure @less-than-wp tags always exist for those special cases. (Note: @less-than-wp-latest etc won't work and shouldn't be used).
- $wp_version_reqs = array_merge(
- $wp_version_reqs,
- version_tags( 'less-than-wp', '9999', '>=', $features_folder )
- );
-}
-
-$skip_tags = array_merge(
- $wp_version_reqs,
- version_tags( 'require-php', PHP_VERSION, '<', $features_folder ),
- // Note: this was '>' prior to WP-CLI 1.5.0 but the change is unlikely to
- // cause BC issues as usually compared against major.minor only.
- version_tags( 'less-than-php', PHP_VERSION, '>=', $features_folder )
-);
-
-// Skip GitHub API tests if `GITHUB_TOKEN` not available because of rate
-// limiting. See https://github.com/wp-cli/wp-cli/issues/1612
-if ( ! getenv( 'GITHUB_TOKEN' ) ) {
- $skip_tags[] = '@github-api';
-}
-# Skip tests known to be broken.
-$skip_tags[] = '@broken';
-
-if ( $wp_version && in_array( $wp_version, array( 'nightly', 'trunk' ), true ) ) {
- $skip_tags[] = '@broken-trunk';
-}
-
-$db_info = get_db_type_and_version();
-$db_version = $db_info['version'];
-// Use detected database type from server, unless WP_CLI_TEST_DBTYPE is 'sqlite'.
-$env_db_type = getenv( 'WP_CLI_TEST_DBTYPE' );
-$db_type = 'sqlite' === $env_db_type ? 'sqlite' : $db_info['type'];
-
-switch ( $db_type ) {
- case 'mariadb':
- $skip_tags = array_merge(
- $skip_tags,
- [ '@require-mysql', '@require-sqlite' ],
- version_tags( 'require-mariadb', $db_version, '<', $features_folder ),
- version_tags( 'less-than-mariadb', $db_version, '>=', $features_folder )
- );
- break;
- case 'sqlite':
- $skip_tags[] = '@require-mariadb';
- $skip_tags[] = '@require-mysql';
- $skip_tags[] = '@require-mysql-or-mariadb';
- break;
- case 'mysql':
- default:
- $skip_tags = array_merge(
- $skip_tags,
- [ '@require-mariadb', '@require-sqlite' ],
- version_tags( 'require-mysql', $db_version, '<', $features_folder ),
- version_tags( 'less-than-mysql', $db_version, '>=', $features_folder )
- );
- break;
-}
-
-# Require PHP extension, eg 'imagick'.
-function extension_tags( $features_folder = 'features' ) {
- $extension_tags = array();
- exec(
- "grep '@require-extension-[A-Za-z_]*' -h -o {$features_folder}/*.feature | uniq",
- $extension_tags
- );
-
- $skip_tags = array();
-
- $substr_start = strlen( '@require-extension-' );
- foreach ( $extension_tags as $tag ) {
- $extension = substr( $tag, $substr_start );
- if ( ! extension_loaded( $extension ) ) {
- $skip_tags[] = $tag;
- }
- }
-
- return $skip_tags;
-}
-
-$skip_tags = array_merge( $skip_tags, extension_tags( $features_folder ) );
-
-if ( ! empty( $skip_tags ) ) {
- echo '--tags=~' . implode( '&&~', $skip_tags );
-}
diff --git a/utils/generate-coverage.php b/utils/generate-coverage.php
deleted file mode 100644
index 007e66ae1..000000000
--- a/utils/generate-coverage.php
+++ /dev/null
@@ -1,108 +0,0 @@
-isDir() && in_array( $file->getFilename(), [ 'php', 'src' ], true ) ) {
- return true;
- }
-
- // Allow top-level files ending in "-command.php"
- if ( $file->isFile() && false !== strpos( $file->getFilename(), '-command.php' ) ) {
- return true;
- }
-
- return false;
- }
-);
-
-$files = [];
-
-foreach ( $filtered_items as $item ) {
- if ( $item->isDir() ) {
- foreach (
- new RecursiveIteratorIterator(
- new RecursiveDirectoryIterator( $item->getPathname(), RecursiveDirectoryIterator::SKIP_DOTS )
- ) as $file
- ) {
- if ( $file->isFile() && $file->getExtension() === 'php' ) {
- $files[] = $file->getPathname();
- }
- }
- } else {
- $files[] = $item->getPathname();
- }
-}
-
-$filter = new Filter();
-
-if ( method_exists( $filter, 'includeFiles' ) ) {
- $filter->includeFiles( $files );
-} else {
- $filter->addFilesToWhitelist( $files );
-}
-
-$coverage = new CodeCoverage(
- // Selector class was only added in v9.1 of the php-code-coverage library.
- class_exists( Selector::class ) ? ( new Selector() )->forLineCoverage( $filter ) : ( new Xdebug() ),
- $filter
-);
-
-$coverage->start( $name );
-
-register_shutdown_function(
- static function () use ( $coverage, $feature, $scenario, $step_line, $name, $project_dir ) {
- $coverage->stop();
-
- $feature_suffix = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $feature ) );
- $scenario_suffix = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $scenario ) );
- $db_type = strtolower( getenv( 'WP_CLI_TEST_DBTYPE' ) );
- $destination = "$project_dir/build/logs/$feature_suffix-$scenario_suffix-$step_line-$db_type.cov";
-
- $dir = dirname( $destination );
- if ( ! file_exists( $dir ) ) {
- mkdir( $dir, 0777, true /*recursive*/ );
- }
-
- ( new PHPReport() )->process( $coverage, $destination );
- }
-);
diff --git a/utils/no-mail.php b/utils/no-mail.php
deleted file mode 100644
index b554709e5..000000000
--- a/utils/no-mail.php
+++ /dev/null
@@ -1,26 +0,0 @@
-