├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ └── config.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .stylua.toml
├── LICENSE
├── README.md
├── composer.json
├── composer.lock
├── lua
└── neotest-phpunit
│ ├── config.lua
│ ├── init.lua
│ └── utils.lua
├── phpunit.xml
├── scripts
└── test
├── specs
├── init.vim
└── utils_spec.lua
├── src
├── User.php
└── autoload.php
└── tests
├── Examples
├── UserTest.php
└── some
│ └── deep
│ └── nesting
│ └── NestingTest.php
└── Unit
├── AttributeTest.php
└── ExampleTest.php
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: If you've already asked for help with a problem and confirmed something is broken with the plugin itself, create a bug report.
3 | title: "[Bug]: "
4 | labels: ["bug"]
5 | assignees: olimorris
6 |
7 | body:
8 | - type: markdown
9 | attributes:
10 | value: |
11 | Thanks for taking the time to fill out this bug report! This form may seem onerous but it makes a resolution much quicker for you and I.
12 | - type: markdown
13 | attributes:
14 | value: |
15 | ## Test with a minimal.lua file
16 | Firstly, test and run Neovim with the minimal config below. Be sure to add your own config, saving to a `minimal.lua` file and adding any additional plugins you may need:
17 |
18 | ```lua
19 | local root = vim.fn.fnamemodify("./.repro", ":p")
20 |
21 | -- set stdpaths to use .repro
22 | for _, name in ipairs({ "config", "data", "state", "cache" }) do
23 | vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
24 | end
25 |
26 | -- bootstrap lazy
27 | local lazypath = root .. "/plugins/lazy.nvim"
28 | if not vim.loop.fs_stat(lazypath) then
29 | vim.fn.system({
30 | "git",
31 | "clone",
32 | "--filter=blob:none",
33 | "--single-branch",
34 | "https://github.com/folke/lazy.nvim.git",
35 | lazypath,
36 | })
37 | end
38 | vim.opt.runtimepath:prepend(lazypath)
39 |
40 | -- install plugins
41 | local plugins = {
42 | {
43 | "nvim-treesitter/nvim-treesitter",
44 | build = ":TSUpdate",
45 | config = function()
46 | require("nvim-treesitter.configs").setup({
47 | ensure_installed = "php",
48 | })
49 | end,
50 | },
51 | {
52 | "nvim-neotest/neotest",
53 | lazy = true,
54 | dependencies = {
55 | "nvim-neotest/nvim-nio",
56 | "nvim-lua/plenary.nvim",
57 | "antoinemadec/FixCursorHold.nvim",
58 | "nvim-treesitter/nvim-treesitter",
59 | "olimorris/neotest-phpunit",
60 | },
61 | config = function()
62 | log_level = vim.log.levels.DEBUG,
63 | require("neotest").setup({
64 | adapters = {
65 | require("neotest-phpunit"),
66 | },
67 | })
68 | end,
69 | },
70 | }
71 |
72 | require("lazy").setup(plugins, {
73 | root = root .. "/plugins",
74 | })
75 | ```
76 |
77 | Once you"ve updated for your config, run Neovim with this command:
78 | ```sh
79 | nvim --clean -u minimal.lua
80 | ```
81 | > **Note:** You may need to do this a couple of times for Treesitter to fully download and sync
82 |
83 | - type: textarea
84 | id: phpunit-test
85 | attributes:
86 | label: PHPUnit test
87 | description: Please share the PHPUnit test you're testing (optional)
88 | placeholder: |
89 | ```php
90 | -- PHPUnit test here
91 | ```
92 | validations:
93 | required: false
94 |
95 | - type: textarea
96 | id: errors
97 | attributes:
98 | label: Error messages
99 | description: Please paste any error messages you receive
100 | placeholder: |
101 | ```lua
102 | -- Error messages here
103 | ```
104 | validations:
105 | required: false
106 |
107 | - type: textarea
108 | id: logs
109 | attributes:
110 | label: Neotest logs
111 | description: |
112 | 1. Wipe the `neotest.log` file in `stdpath("log")` or `stdpath("data")`.
113 | 2. Ensure `log_level = vim.log.levels.DEBUG` is set in your minimal config.
114 | 3. Reproduce the issue.
115 | 4. Provide the new logs.
116 | placeholder: |
117 | ### Neotest logs
118 | validations:
119 | required: true
120 |
121 | - type: textarea
122 | id: bug
123 | attributes:
124 | label: Describe the bug
125 | description: Please describe the bug and include any screenshots
126 | placeholder: |
127 | ### What I expect to happen
128 | [Your text here]
129 |
130 | ### What actually happens
131 | [Your text here]
132 | validations:
133 | required: true
134 |
135 | - type: textarea
136 | id: reproduce
137 | attributes:
138 | label: Reproduce the bug
139 | description: Please include the steps to reproduce the bug
140 | placeholder: |
141 | Steps to reproduce:
142 | 1.
143 | 2.
144 | 3.
145 | validations:
146 | required: false
147 |
148 | - type: checkboxes
149 | id: final-checks
150 | attributes:
151 | label: Final checks
152 | description: |
153 | Before you submit, please make sure you have completed the following steps:
154 | options:
155 | - label: I have made sure this issue exists in the latest version of the plugin
156 | required: true
157 | - label: I have tested with the `minimal.lua` config file above and still get the issue
158 | required: true
159 | - label: I have confirmed that Treesitter is working correctly by running `:checkhealth treesitter`
160 | required: true
161 | - label: I have made sure this is not a duplicate issue
162 | required: true
163 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Get Help
4 | url: https://github.com/olimorris/neotest-phpunit/discussions/new?category=q-a
5 | about: If you can't get something to work the way you expect, open a question in our discussion forums.
6 | - name: Feature Request
7 | url: https://github.com/olimorris/neotest-phpunit/discussions/new?category=ideas
8 | about: Suggest any ideas you have using our discussion forums.
9 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on: [push, pull_request]
4 |
5 | # Cancel any in-progress CI runs for a PR if it is updated
6 | concurrency:
7 | group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
8 | cancel-in-progress: true
9 |
10 | jobs:
11 | unit_tests:
12 | name: unit tests
13 | runs-on: ${{ matrix.os }}
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | include:
18 | - os: ubuntu-20.04
19 | url: https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz
20 | - os: ubuntu-20.04
21 | url: https://github.com/neovim/neovim/releases/download/v0.9.0/nvim-linux64.tar.gz
22 | - os: ubuntu-20.04
23 | url: https://github.com/neovim/neovim/releases/download/v0.8.0/nvim-linux64.tar.gz
24 | - os: ubuntu-20.04
25 | url: https://github.com/neovim/neovim/releases/download/v0.7.0/nvim-linux64.tar.gz
26 |
27 | steps:
28 | - uses: actions/checkout@v2
29 | - run: date +%F > todays-date
30 | - name: Restore cache for today's nightly.
31 | uses: actions/cache@v2
32 | with:
33 | path: _neovim
34 | key: ${{ runner.os }}-x64-${{ hashFiles('todays-date') }}
35 |
36 | - name: Prepare dependencies
37 | run: |
38 | test -d _neovim || {
39 | mkdir -p _neovim
40 | curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
41 | }
42 | mkdir -p ~/.local/share/nvim/site/pack/vendor/start
43 | git clone --depth 1 https://github.com/nvim-lua/plenary.nvim.git ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
44 | git clone --depth 1 https://github.com/nvim-neotest/neotest.git ~/.local/share/nvim/site/pack/vendor/start/neotest
45 | git clone --depth 1 https://github.com/nvim-treesitter/nvim-treesitter.git ~/.local/share/nvim/site/pack/vendor/start/nvim-treesitter
46 | ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start
47 | export PATH="${PWD}/_neovim/bin:${PATH}"
48 | export VIM="${PWD}/_neovim/share/nvim/runtime"
49 | nvim --headless -c 'TSInstallSync php | quit'
50 |
51 | - name: Run tests
52 | run: |
53 | export PATH="${PWD}/_neovim/bin:${PATH}"
54 | export VIM="${PWD}/_neovim/share/nvim/runtime"
55 | nvim --version
56 | ./scripts/test
57 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Lua sources
2 | luac.out
3 |
4 | # luarocks build files
5 | *.src.rock
6 | *.zip
7 | *.tar.gz
8 |
9 | # Object files
10 | *.o
11 | *.os
12 | *.ko
13 | *.obj
14 | *.elf
15 |
16 | # Precompiled Headers
17 | *.gch
18 | *.pch
19 |
20 | # Libraries
21 | *.lib
22 | *.a
23 | *.la
24 | *.lo
25 | *.def
26 | *.exp
27 |
28 | # Shared objects (inc. Windows DLLs)
29 | *.dll
30 | *.so
31 | *.so.*
32 | *.dylib
33 |
34 | # Executables
35 | *.exe
36 | *.out
37 | *.app
38 | *.i*86
39 | *.x86_64
40 | *.hex
41 |
42 | # Others
43 | .luarc.json
44 | /vendor
45 | .phpunit.result.cache
46 | .php-cs-fixer.cache
47 |
--------------------------------------------------------------------------------
/.stylua.toml:
--------------------------------------------------------------------------------
1 | column_width = 120
2 | indent_type = 'Spaces'
3 | indent_width = 2
4 | no_call_parentheses = false
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022-present Oli Morris
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # neotest-phpunit
2 |
3 | [](https://github.com/olimorris/neotest-phpunit/actions/workflows/ci.yml)
4 |
5 | This plugin provides a [PHPUnit](https://phpunit.de) adapter for the [Neotest](https://github.com/nvim-neotest/neotest) framework.
6 |
7 |
8 |
9 | ## :package: Installation
10 |
11 | Install with the package manager of your choice:
12 |
13 | **Lazy**
14 |
15 | ```lua
16 | {
17 | "nvim-neotest/neotest",
18 | lazy = true,
19 | dependencies = {
20 | ...,
21 | "olimorris/neotest-phpunit",
22 | },
23 | config = function()
24 | require("neotest").setup({
25 | ...,
26 | adapters = {
27 | require("neotest-phpunit")
28 | },
29 | }
30 | end
31 | }
32 | ```
33 |
34 | **Packer**
35 |
36 | ```lua
37 | use({
38 | "nvim-neotest/neotest",
39 | requires = {
40 | ...,
41 | "olimorris/neotest-phpunit",
42 | },
43 | config = function()
44 | require("neotest").setup({
45 | ...,
46 | adapters = {
47 | require("neotest-phpunit"),
48 | }
49 | })
50 | end
51 | })
52 | ```
53 |
54 | ## :wrench: Configuration
55 |
56 | ### Default configuration
57 |
58 | > [!NOTE]
59 | > You only need to the call the `setup` function if you wish to change any of the defaults.
60 |
61 |
62 | Click to see the default configuration
63 |
64 | ```lua
65 | adapters = {
66 | require("neotest-phpunit")({
67 | phpunit_cmd = function()
68 | return "vendor/bin/phpunit" -- for `dap` strategy then it must return string (table values will cause validation error)
69 | end,
70 | root_files = { "composer.json", "phpunit.xml", ".gitignore" },
71 | filter_dirs = { ".git", "node_modules" },
72 | env = {}, -- for example {XDEBUG_CONFIG = 'idekey=neotest'}
73 | dap = nil, -- to configure `dap` strategy put single element from `dap.configurations.php`
74 | }),
75 | }
76 | ```
77 |
78 |
79 |
80 | ### The test command
81 |
82 | The command used to run tests can be changed via the `phpunit_cmd` option:
83 |
84 | ```lua
85 | require("neotest-phpunit")({
86 | phpunit_cmd = function()
87 | return "vendor/bin/phpunit"
88 | end
89 | })
90 | ```
91 |
92 | ### Setting the root directory
93 |
94 | For Neotest adapters to work, they need to define a project root whereby the process of discovering tests can take place. By default, the adapter looks for a `composer.json`, `phpunit.xml` or `.gitignore` file. These can be changed with:
95 |
96 | ```lua
97 | require("neotest-phpunit")({
98 | root_files = { "README.md" }
99 | })
100 | ```
101 |
102 | You can even set `root_files` with a function which returns a table:
103 |
104 | ```lua
105 | require("neotest-phpunit")({
106 | root_files = function() return { "README.md" } end
107 | })
108 | ```
109 |
110 | If there are projects you don't want discovered, you can instead set `root_ignore_files` to ignore any matching projects.
111 |
112 | For example, if your project uses Pest and the appropriate [neotest adapter](https://github.com/V13Axel/neotest-pest), you'll need to set:
113 |
114 | ```lua
115 | require("neotest-phpunit")({
116 | root_ignore_files = { "tests/Pest.php" }
117 | })
118 | ```
119 |
120 | ### Filtering directories
121 |
122 | By default, the adapter will search test files in all dirs in the root with the exception of `node_modules` and `.git`. You can change this with:
123 |
124 | ```lua
125 | require("neotest-phpunit")({
126 | filter_dirs = { "vendor" }
127 | })
128 | ```
129 |
130 | You can even set `filter_dirs` with a function which returns a table:
131 |
132 | ```lua
133 | require("neotest-phpunit")({
134 | filter_dirs = function() return { "vendor" } end
135 | })
136 | ```
137 |
138 | ### Debugging
139 |
140 | The plugin can also be used for debugging via a dap strategy.
141 |
142 | Firstly, install and configure [nvim-dap](https://github.com/mfussenegger/nvim-dap) with [vscode-php-debug](https://github.com/xdebug/vscode-php-debug). Then set the following dap configuration:
143 |
144 | ```lua
145 | dap.configurations.php = {
146 | {
147 | log = true,
148 | type = "php",
149 | request = "launch",
150 | name = "Listen for XDebug",
151 | port = 9003,
152 | stopOnEntry = false,
153 | xdebugSettings = {
154 | max_children = 512,
155 | max_data = 1024,
156 | max_depth = 4,
157 | },
158 | breakpoints = {
159 | exception = {
160 | Notice = false,
161 | Warning = false,
162 | Error = false,
163 | Exception = false,
164 | ["*"] = false,
165 | },
166 | },
167 | }
168 | }
169 | ```
170 |
171 | Then in the plugin's config, add:
172 |
173 | ```lua
174 | require("neotest-phpunit")({
175 | env = {
176 | XDEBUG_CONFIG = "idekey=neotest",
177 | },
178 | dap = dap.configurations.php[1],
179 | })
180 | ```
181 |
182 | > [!NOTE]
183 | > If you run a test with the `dap` strategy from the summary window (by default by `d`) and see that the window content has been replaced by debugger content then consider setting `dap.defaults.fallback.switchbuf = "useopen"` or Neovim level [`switchbuf`](https://neovim.io/doc/user/options.html#'switchbuf')
184 |
185 | ## :rocket: Usage
186 |
187 | #### Test single method
188 |
189 | To test a single test, hover over the test and run `lua require("neotest").run.run()`
190 |
191 | #### Test file
192 |
193 | To test a file run `lua require("neotest").run.run(vim.fn.expand("%"))`
194 |
195 | #### Test directory
196 |
197 | To test a directory run `lua require("neotest").run.run("path/to/directory")`
198 |
199 | #### Test suite
200 |
201 | To test the full test suite run `lua require("neotest").run.run({ suite = true })`
202 |
203 | ## :gift: Contributing
204 |
205 | This project is maintained by the Neovim PHP community. Please raise a PR if you are interested in adding new functionality or fixing any bugs. When submitting a bug, please include an example test that we can test against.
206 |
207 | To trigger the tests for the adapter, run:
208 |
209 | ```sh
210 | ./scripts/test
211 | ```
212 |
213 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require-dev": {
3 | "phpunit/phpunit": "^10"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 | "This file is @generated automatically"
6 | ],
7 | "content-hash": "657b85759b97fae05fc2983bd4abadf7",
8 | "packages": [],
9 | "packages-dev": [
10 | {
11 | "name": "myclabs/deep-copy",
12 | "version": "1.11.1",
13 | "source": {
14 | "type": "git",
15 | "url": "https://github.com/myclabs/DeepCopy.git",
16 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
17 | },
18 | "dist": {
19 | "type": "zip",
20 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
21 | "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
22 | "shasum": ""
23 | },
24 | "require": {
25 | "php": "^7.1 || ^8.0"
26 | },
27 | "conflict": {
28 | "doctrine/collections": "<1.6.8",
29 | "doctrine/common": "<2.13.3 || >=3,<3.2.2"
30 | },
31 | "require-dev": {
32 | "doctrine/collections": "^1.6.8",
33 | "doctrine/common": "^2.13.3 || ^3.2.2",
34 | "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
35 | },
36 | "type": "library",
37 | "autoload": {
38 | "files": [
39 | "src/DeepCopy/deep_copy.php"
40 | ],
41 | "psr-4": {
42 | "DeepCopy\\": "src/DeepCopy/"
43 | }
44 | },
45 | "notification-url": "https://packagist.org/downloads/",
46 | "license": [
47 | "MIT"
48 | ],
49 | "description": "Create deep copies (clones) of your objects",
50 | "keywords": [
51 | "clone",
52 | "copy",
53 | "duplicate",
54 | "object",
55 | "object graph"
56 | ],
57 | "support": {
58 | "issues": "https://github.com/myclabs/DeepCopy/issues",
59 | "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
60 | },
61 | "funding": [
62 | {
63 | "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
64 | "type": "tidelift"
65 | }
66 | ],
67 | "time": "2023-03-08T13:26:56+00:00"
68 | },
69 | {
70 | "name": "nikic/php-parser",
71 | "version": "v5.0.2",
72 | "source": {
73 | "type": "git",
74 | "url": "https://github.com/nikic/PHP-Parser.git",
75 | "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13"
76 | },
77 | "dist": {
78 | "type": "zip",
79 | "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13",
80 | "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13",
81 | "shasum": ""
82 | },
83 | "require": {
84 | "ext-ctype": "*",
85 | "ext-json": "*",
86 | "ext-tokenizer": "*",
87 | "php": ">=7.4"
88 | },
89 | "require-dev": {
90 | "ircmaxell/php-yacc": "^0.0.7",
91 | "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
92 | },
93 | "bin": [
94 | "bin/php-parse"
95 | ],
96 | "type": "library",
97 | "extra": {
98 | "branch-alias": {
99 | "dev-master": "5.0-dev"
100 | }
101 | },
102 | "autoload": {
103 | "psr-4": {
104 | "PhpParser\\": "lib/PhpParser"
105 | }
106 | },
107 | "notification-url": "https://packagist.org/downloads/",
108 | "license": [
109 | "BSD-3-Clause"
110 | ],
111 | "authors": [
112 | {
113 | "name": "Nikita Popov"
114 | }
115 | ],
116 | "description": "A PHP parser written in PHP",
117 | "keywords": [
118 | "parser",
119 | "php"
120 | ],
121 | "support": {
122 | "issues": "https://github.com/nikic/PHP-Parser/issues",
123 | "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2"
124 | },
125 | "time": "2024-03-05T20:51:40+00:00"
126 | },
127 | {
128 | "name": "phar-io/manifest",
129 | "version": "2.0.4",
130 | "source": {
131 | "type": "git",
132 | "url": "https://github.com/phar-io/manifest.git",
133 | "reference": "54750ef60c58e43759730615a392c31c80e23176"
134 | },
135 | "dist": {
136 | "type": "zip",
137 | "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
138 | "reference": "54750ef60c58e43759730615a392c31c80e23176",
139 | "shasum": ""
140 | },
141 | "require": {
142 | "ext-dom": "*",
143 | "ext-libxml": "*",
144 | "ext-phar": "*",
145 | "ext-xmlwriter": "*",
146 | "phar-io/version": "^3.0.1",
147 | "php": "^7.2 || ^8.0"
148 | },
149 | "type": "library",
150 | "extra": {
151 | "branch-alias": {
152 | "dev-master": "2.0.x-dev"
153 | }
154 | },
155 | "autoload": {
156 | "classmap": [
157 | "src/"
158 | ]
159 | },
160 | "notification-url": "https://packagist.org/downloads/",
161 | "license": [
162 | "BSD-3-Clause"
163 | ],
164 | "authors": [
165 | {
166 | "name": "Arne Blankerts",
167 | "email": "arne@blankerts.de",
168 | "role": "Developer"
169 | },
170 | {
171 | "name": "Sebastian Heuer",
172 | "email": "sebastian@phpeople.de",
173 | "role": "Developer"
174 | },
175 | {
176 | "name": "Sebastian Bergmann",
177 | "email": "sebastian@phpunit.de",
178 | "role": "Developer"
179 | }
180 | ],
181 | "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
182 | "support": {
183 | "issues": "https://github.com/phar-io/manifest/issues",
184 | "source": "https://github.com/phar-io/manifest/tree/2.0.4"
185 | },
186 | "funding": [
187 | {
188 | "url": "https://github.com/theseer",
189 | "type": "github"
190 | }
191 | ],
192 | "time": "2024-03-03T12:33:53+00:00"
193 | },
194 | {
195 | "name": "phar-io/version",
196 | "version": "3.2.1",
197 | "source": {
198 | "type": "git",
199 | "url": "https://github.com/phar-io/version.git",
200 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
201 | },
202 | "dist": {
203 | "type": "zip",
204 | "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
205 | "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
206 | "shasum": ""
207 | },
208 | "require": {
209 | "php": "^7.2 || ^8.0"
210 | },
211 | "type": "library",
212 | "autoload": {
213 | "classmap": [
214 | "src/"
215 | ]
216 | },
217 | "notification-url": "https://packagist.org/downloads/",
218 | "license": [
219 | "BSD-3-Clause"
220 | ],
221 | "authors": [
222 | {
223 | "name": "Arne Blankerts",
224 | "email": "arne@blankerts.de",
225 | "role": "Developer"
226 | },
227 | {
228 | "name": "Sebastian Heuer",
229 | "email": "sebastian@phpeople.de",
230 | "role": "Developer"
231 | },
232 | {
233 | "name": "Sebastian Bergmann",
234 | "email": "sebastian@phpunit.de",
235 | "role": "Developer"
236 | }
237 | ],
238 | "description": "Library for handling version information and constraints",
239 | "support": {
240 | "issues": "https://github.com/phar-io/version/issues",
241 | "source": "https://github.com/phar-io/version/tree/3.2.1"
242 | },
243 | "time": "2022-02-21T01:04:05+00:00"
244 | },
245 | {
246 | "name": "phpunit/php-code-coverage",
247 | "version": "10.1.14",
248 | "source": {
249 | "type": "git",
250 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
251 | "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b"
252 | },
253 | "dist": {
254 | "type": "zip",
255 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
256 | "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b",
257 | "shasum": ""
258 | },
259 | "require": {
260 | "ext-dom": "*",
261 | "ext-libxml": "*",
262 | "ext-xmlwriter": "*",
263 | "nikic/php-parser": "^4.18 || ^5.0",
264 | "php": ">=8.1",
265 | "phpunit/php-file-iterator": "^4.0",
266 | "phpunit/php-text-template": "^3.0",
267 | "sebastian/code-unit-reverse-lookup": "^3.0",
268 | "sebastian/complexity": "^3.0",
269 | "sebastian/environment": "^6.0",
270 | "sebastian/lines-of-code": "^2.0",
271 | "sebastian/version": "^4.0",
272 | "theseer/tokenizer": "^1.2.0"
273 | },
274 | "require-dev": {
275 | "phpunit/phpunit": "^10.1"
276 | },
277 | "suggest": {
278 | "ext-pcov": "PHP extension that provides line coverage",
279 | "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
280 | },
281 | "type": "library",
282 | "extra": {
283 | "branch-alias": {
284 | "dev-main": "10.1-dev"
285 | }
286 | },
287 | "autoload": {
288 | "classmap": [
289 | "src/"
290 | ]
291 | },
292 | "notification-url": "https://packagist.org/downloads/",
293 | "license": [
294 | "BSD-3-Clause"
295 | ],
296 | "authors": [
297 | {
298 | "name": "Sebastian Bergmann",
299 | "email": "sebastian@phpunit.de",
300 | "role": "lead"
301 | }
302 | ],
303 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
304 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
305 | "keywords": [
306 | "coverage",
307 | "testing",
308 | "xunit"
309 | ],
310 | "support": {
311 | "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
312 | "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
313 | "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14"
314 | },
315 | "funding": [
316 | {
317 | "url": "https://github.com/sebastianbergmann",
318 | "type": "github"
319 | }
320 | ],
321 | "time": "2024-03-12T15:33:41+00:00"
322 | },
323 | {
324 | "name": "phpunit/php-file-iterator",
325 | "version": "4.1.0",
326 | "source": {
327 | "type": "git",
328 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
329 | "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
330 | },
331 | "dist": {
332 | "type": "zip",
333 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
334 | "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
335 | "shasum": ""
336 | },
337 | "require": {
338 | "php": ">=8.1"
339 | },
340 | "require-dev": {
341 | "phpunit/phpunit": "^10.0"
342 | },
343 | "type": "library",
344 | "extra": {
345 | "branch-alias": {
346 | "dev-main": "4.0-dev"
347 | }
348 | },
349 | "autoload": {
350 | "classmap": [
351 | "src/"
352 | ]
353 | },
354 | "notification-url": "https://packagist.org/downloads/",
355 | "license": [
356 | "BSD-3-Clause"
357 | ],
358 | "authors": [
359 | {
360 | "name": "Sebastian Bergmann",
361 | "email": "sebastian@phpunit.de",
362 | "role": "lead"
363 | }
364 | ],
365 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
366 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
367 | "keywords": [
368 | "filesystem",
369 | "iterator"
370 | ],
371 | "support": {
372 | "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
373 | "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
374 | "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0"
375 | },
376 | "funding": [
377 | {
378 | "url": "https://github.com/sebastianbergmann",
379 | "type": "github"
380 | }
381 | ],
382 | "time": "2023-08-31T06:24:48+00:00"
383 | },
384 | {
385 | "name": "phpunit/php-invoker",
386 | "version": "4.0.0",
387 | "source": {
388 | "type": "git",
389 | "url": "https://github.com/sebastianbergmann/php-invoker.git",
390 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7"
391 | },
392 | "dist": {
393 | "type": "zip",
394 | "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
395 | "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
396 | "shasum": ""
397 | },
398 | "require": {
399 | "php": ">=8.1"
400 | },
401 | "require-dev": {
402 | "ext-pcntl": "*",
403 | "phpunit/phpunit": "^10.0"
404 | },
405 | "suggest": {
406 | "ext-pcntl": "*"
407 | },
408 | "type": "library",
409 | "extra": {
410 | "branch-alias": {
411 | "dev-main": "4.0-dev"
412 | }
413 | },
414 | "autoload": {
415 | "classmap": [
416 | "src/"
417 | ]
418 | },
419 | "notification-url": "https://packagist.org/downloads/",
420 | "license": [
421 | "BSD-3-Clause"
422 | ],
423 | "authors": [
424 | {
425 | "name": "Sebastian Bergmann",
426 | "email": "sebastian@phpunit.de",
427 | "role": "lead"
428 | }
429 | ],
430 | "description": "Invoke callables with a timeout",
431 | "homepage": "https://github.com/sebastianbergmann/php-invoker/",
432 | "keywords": [
433 | "process"
434 | ],
435 | "support": {
436 | "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
437 | "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0"
438 | },
439 | "funding": [
440 | {
441 | "url": "https://github.com/sebastianbergmann",
442 | "type": "github"
443 | }
444 | ],
445 | "time": "2023-02-03T06:56:09+00:00"
446 | },
447 | {
448 | "name": "phpunit/php-text-template",
449 | "version": "3.0.1",
450 | "source": {
451 | "type": "git",
452 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
453 | "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
454 | },
455 | "dist": {
456 | "type": "zip",
457 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
458 | "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
459 | "shasum": ""
460 | },
461 | "require": {
462 | "php": ">=8.1"
463 | },
464 | "require-dev": {
465 | "phpunit/phpunit": "^10.0"
466 | },
467 | "type": "library",
468 | "extra": {
469 | "branch-alias": {
470 | "dev-main": "3.0-dev"
471 | }
472 | },
473 | "autoload": {
474 | "classmap": [
475 | "src/"
476 | ]
477 | },
478 | "notification-url": "https://packagist.org/downloads/",
479 | "license": [
480 | "BSD-3-Clause"
481 | ],
482 | "authors": [
483 | {
484 | "name": "Sebastian Bergmann",
485 | "email": "sebastian@phpunit.de",
486 | "role": "lead"
487 | }
488 | ],
489 | "description": "Simple template engine.",
490 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
491 | "keywords": [
492 | "template"
493 | ],
494 | "support": {
495 | "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
496 | "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
497 | "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1"
498 | },
499 | "funding": [
500 | {
501 | "url": "https://github.com/sebastianbergmann",
502 | "type": "github"
503 | }
504 | ],
505 | "time": "2023-08-31T14:07:24+00:00"
506 | },
507 | {
508 | "name": "phpunit/php-timer",
509 | "version": "6.0.0",
510 | "source": {
511 | "type": "git",
512 | "url": "https://github.com/sebastianbergmann/php-timer.git",
513 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d"
514 | },
515 | "dist": {
516 | "type": "zip",
517 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d",
518 | "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d",
519 | "shasum": ""
520 | },
521 | "require": {
522 | "php": ">=8.1"
523 | },
524 | "require-dev": {
525 | "phpunit/phpunit": "^10.0"
526 | },
527 | "type": "library",
528 | "extra": {
529 | "branch-alias": {
530 | "dev-main": "6.0-dev"
531 | }
532 | },
533 | "autoload": {
534 | "classmap": [
535 | "src/"
536 | ]
537 | },
538 | "notification-url": "https://packagist.org/downloads/",
539 | "license": [
540 | "BSD-3-Clause"
541 | ],
542 | "authors": [
543 | {
544 | "name": "Sebastian Bergmann",
545 | "email": "sebastian@phpunit.de",
546 | "role": "lead"
547 | }
548 | ],
549 | "description": "Utility class for timing",
550 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
551 | "keywords": [
552 | "timer"
553 | ],
554 | "support": {
555 | "issues": "https://github.com/sebastianbergmann/php-timer/issues",
556 | "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0"
557 | },
558 | "funding": [
559 | {
560 | "url": "https://github.com/sebastianbergmann",
561 | "type": "github"
562 | }
563 | ],
564 | "time": "2023-02-03T06:57:52+00:00"
565 | },
566 | {
567 | "name": "phpunit/phpunit",
568 | "version": "10.5.20",
569 | "source": {
570 | "type": "git",
571 | "url": "https://github.com/sebastianbergmann/phpunit.git",
572 | "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3"
573 | },
574 | "dist": {
575 | "type": "zip",
576 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/547d314dc24ec1e177720d45c6263fb226cc2ae3",
577 | "reference": "547d314dc24ec1e177720d45c6263fb226cc2ae3",
578 | "shasum": ""
579 | },
580 | "require": {
581 | "ext-dom": "*",
582 | "ext-json": "*",
583 | "ext-libxml": "*",
584 | "ext-mbstring": "*",
585 | "ext-xml": "*",
586 | "ext-xmlwriter": "*",
587 | "myclabs/deep-copy": "^1.10.1",
588 | "phar-io/manifest": "^2.0.3",
589 | "phar-io/version": "^3.0.2",
590 | "php": ">=8.1",
591 | "phpunit/php-code-coverage": "^10.1.5",
592 | "phpunit/php-file-iterator": "^4.0",
593 | "phpunit/php-invoker": "^4.0",
594 | "phpunit/php-text-template": "^3.0",
595 | "phpunit/php-timer": "^6.0",
596 | "sebastian/cli-parser": "^2.0",
597 | "sebastian/code-unit": "^2.0",
598 | "sebastian/comparator": "^5.0",
599 | "sebastian/diff": "^5.0",
600 | "sebastian/environment": "^6.0",
601 | "sebastian/exporter": "^5.1",
602 | "sebastian/global-state": "^6.0.1",
603 | "sebastian/object-enumerator": "^5.0",
604 | "sebastian/recursion-context": "^5.0",
605 | "sebastian/type": "^4.0",
606 | "sebastian/version": "^4.0"
607 | },
608 | "suggest": {
609 | "ext-soap": "To be able to generate mocks based on WSDL files"
610 | },
611 | "bin": [
612 | "phpunit"
613 | ],
614 | "type": "library",
615 | "extra": {
616 | "branch-alias": {
617 | "dev-main": "10.5-dev"
618 | }
619 | },
620 | "autoload": {
621 | "files": [
622 | "src/Framework/Assert/Functions.php"
623 | ],
624 | "classmap": [
625 | "src/"
626 | ]
627 | },
628 | "notification-url": "https://packagist.org/downloads/",
629 | "license": [
630 | "BSD-3-Clause"
631 | ],
632 | "authors": [
633 | {
634 | "name": "Sebastian Bergmann",
635 | "email": "sebastian@phpunit.de",
636 | "role": "lead"
637 | }
638 | ],
639 | "description": "The PHP Unit Testing framework.",
640 | "homepage": "https://phpunit.de/",
641 | "keywords": [
642 | "phpunit",
643 | "testing",
644 | "xunit"
645 | ],
646 | "support": {
647 | "issues": "https://github.com/sebastianbergmann/phpunit/issues",
648 | "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
649 | "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.20"
650 | },
651 | "funding": [
652 | {
653 | "url": "https://phpunit.de/sponsors.html",
654 | "type": "custom"
655 | },
656 | {
657 | "url": "https://github.com/sebastianbergmann",
658 | "type": "github"
659 | },
660 | {
661 | "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
662 | "type": "tidelift"
663 | }
664 | ],
665 | "time": "2024-04-24T06:32:35+00:00"
666 | },
667 | {
668 | "name": "sebastian/cli-parser",
669 | "version": "2.0.1",
670 | "source": {
671 | "type": "git",
672 | "url": "https://github.com/sebastianbergmann/cli-parser.git",
673 | "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
674 | },
675 | "dist": {
676 | "type": "zip",
677 | "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
678 | "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
679 | "shasum": ""
680 | },
681 | "require": {
682 | "php": ">=8.1"
683 | },
684 | "require-dev": {
685 | "phpunit/phpunit": "^10.0"
686 | },
687 | "type": "library",
688 | "extra": {
689 | "branch-alias": {
690 | "dev-main": "2.0-dev"
691 | }
692 | },
693 | "autoload": {
694 | "classmap": [
695 | "src/"
696 | ]
697 | },
698 | "notification-url": "https://packagist.org/downloads/",
699 | "license": [
700 | "BSD-3-Clause"
701 | ],
702 | "authors": [
703 | {
704 | "name": "Sebastian Bergmann",
705 | "email": "sebastian@phpunit.de",
706 | "role": "lead"
707 | }
708 | ],
709 | "description": "Library for parsing CLI options",
710 | "homepage": "https://github.com/sebastianbergmann/cli-parser",
711 | "support": {
712 | "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
713 | "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
714 | "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1"
715 | },
716 | "funding": [
717 | {
718 | "url": "https://github.com/sebastianbergmann",
719 | "type": "github"
720 | }
721 | ],
722 | "time": "2024-03-02T07:12:49+00:00"
723 | },
724 | {
725 | "name": "sebastian/code-unit",
726 | "version": "2.0.0",
727 | "source": {
728 | "type": "git",
729 | "url": "https://github.com/sebastianbergmann/code-unit.git",
730 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503"
731 | },
732 | "dist": {
733 | "type": "zip",
734 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503",
735 | "reference": "a81fee9eef0b7a76af11d121767abc44c104e503",
736 | "shasum": ""
737 | },
738 | "require": {
739 | "php": ">=8.1"
740 | },
741 | "require-dev": {
742 | "phpunit/phpunit": "^10.0"
743 | },
744 | "type": "library",
745 | "extra": {
746 | "branch-alias": {
747 | "dev-main": "2.0-dev"
748 | }
749 | },
750 | "autoload": {
751 | "classmap": [
752 | "src/"
753 | ]
754 | },
755 | "notification-url": "https://packagist.org/downloads/",
756 | "license": [
757 | "BSD-3-Clause"
758 | ],
759 | "authors": [
760 | {
761 | "name": "Sebastian Bergmann",
762 | "email": "sebastian@phpunit.de",
763 | "role": "lead"
764 | }
765 | ],
766 | "description": "Collection of value objects that represent the PHP code units",
767 | "homepage": "https://github.com/sebastianbergmann/code-unit",
768 | "support": {
769 | "issues": "https://github.com/sebastianbergmann/code-unit/issues",
770 | "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0"
771 | },
772 | "funding": [
773 | {
774 | "url": "https://github.com/sebastianbergmann",
775 | "type": "github"
776 | }
777 | ],
778 | "time": "2023-02-03T06:58:43+00:00"
779 | },
780 | {
781 | "name": "sebastian/code-unit-reverse-lookup",
782 | "version": "3.0.0",
783 | "source": {
784 | "type": "git",
785 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
786 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d"
787 | },
788 | "dist": {
789 | "type": "zip",
790 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
791 | "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
792 | "shasum": ""
793 | },
794 | "require": {
795 | "php": ">=8.1"
796 | },
797 | "require-dev": {
798 | "phpunit/phpunit": "^10.0"
799 | },
800 | "type": "library",
801 | "extra": {
802 | "branch-alias": {
803 | "dev-main": "3.0-dev"
804 | }
805 | },
806 | "autoload": {
807 | "classmap": [
808 | "src/"
809 | ]
810 | },
811 | "notification-url": "https://packagist.org/downloads/",
812 | "license": [
813 | "BSD-3-Clause"
814 | ],
815 | "authors": [
816 | {
817 | "name": "Sebastian Bergmann",
818 | "email": "sebastian@phpunit.de"
819 | }
820 | ],
821 | "description": "Looks up which function or method a line of code belongs to",
822 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
823 | "support": {
824 | "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
825 | "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0"
826 | },
827 | "funding": [
828 | {
829 | "url": "https://github.com/sebastianbergmann",
830 | "type": "github"
831 | }
832 | ],
833 | "time": "2023-02-03T06:59:15+00:00"
834 | },
835 | {
836 | "name": "sebastian/comparator",
837 | "version": "5.0.1",
838 | "source": {
839 | "type": "git",
840 | "url": "https://github.com/sebastianbergmann/comparator.git",
841 | "reference": "2db5010a484d53ebf536087a70b4a5423c102372"
842 | },
843 | "dist": {
844 | "type": "zip",
845 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372",
846 | "reference": "2db5010a484d53ebf536087a70b4a5423c102372",
847 | "shasum": ""
848 | },
849 | "require": {
850 | "ext-dom": "*",
851 | "ext-mbstring": "*",
852 | "php": ">=8.1",
853 | "sebastian/diff": "^5.0",
854 | "sebastian/exporter": "^5.0"
855 | },
856 | "require-dev": {
857 | "phpunit/phpunit": "^10.3"
858 | },
859 | "type": "library",
860 | "extra": {
861 | "branch-alias": {
862 | "dev-main": "5.0-dev"
863 | }
864 | },
865 | "autoload": {
866 | "classmap": [
867 | "src/"
868 | ]
869 | },
870 | "notification-url": "https://packagist.org/downloads/",
871 | "license": [
872 | "BSD-3-Clause"
873 | ],
874 | "authors": [
875 | {
876 | "name": "Sebastian Bergmann",
877 | "email": "sebastian@phpunit.de"
878 | },
879 | {
880 | "name": "Jeff Welch",
881 | "email": "whatthejeff@gmail.com"
882 | },
883 | {
884 | "name": "Volker Dusch",
885 | "email": "github@wallbash.com"
886 | },
887 | {
888 | "name": "Bernhard Schussek",
889 | "email": "bschussek@2bepublished.at"
890 | }
891 | ],
892 | "description": "Provides the functionality to compare PHP values for equality",
893 | "homepage": "https://github.com/sebastianbergmann/comparator",
894 | "keywords": [
895 | "comparator",
896 | "compare",
897 | "equality"
898 | ],
899 | "support": {
900 | "issues": "https://github.com/sebastianbergmann/comparator/issues",
901 | "security": "https://github.com/sebastianbergmann/comparator/security/policy",
902 | "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1"
903 | },
904 | "funding": [
905 | {
906 | "url": "https://github.com/sebastianbergmann",
907 | "type": "github"
908 | }
909 | ],
910 | "time": "2023-08-14T13:18:12+00:00"
911 | },
912 | {
913 | "name": "sebastian/complexity",
914 | "version": "3.2.0",
915 | "source": {
916 | "type": "git",
917 | "url": "https://github.com/sebastianbergmann/complexity.git",
918 | "reference": "68ff824baeae169ec9f2137158ee529584553799"
919 | },
920 | "dist": {
921 | "type": "zip",
922 | "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
923 | "reference": "68ff824baeae169ec9f2137158ee529584553799",
924 | "shasum": ""
925 | },
926 | "require": {
927 | "nikic/php-parser": "^4.18 || ^5.0",
928 | "php": ">=8.1"
929 | },
930 | "require-dev": {
931 | "phpunit/phpunit": "^10.0"
932 | },
933 | "type": "library",
934 | "extra": {
935 | "branch-alias": {
936 | "dev-main": "3.2-dev"
937 | }
938 | },
939 | "autoload": {
940 | "classmap": [
941 | "src/"
942 | ]
943 | },
944 | "notification-url": "https://packagist.org/downloads/",
945 | "license": [
946 | "BSD-3-Clause"
947 | ],
948 | "authors": [
949 | {
950 | "name": "Sebastian Bergmann",
951 | "email": "sebastian@phpunit.de",
952 | "role": "lead"
953 | }
954 | ],
955 | "description": "Library for calculating the complexity of PHP code units",
956 | "homepage": "https://github.com/sebastianbergmann/complexity",
957 | "support": {
958 | "issues": "https://github.com/sebastianbergmann/complexity/issues",
959 | "security": "https://github.com/sebastianbergmann/complexity/security/policy",
960 | "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
961 | },
962 | "funding": [
963 | {
964 | "url": "https://github.com/sebastianbergmann",
965 | "type": "github"
966 | }
967 | ],
968 | "time": "2023-12-21T08:37:17+00:00"
969 | },
970 | {
971 | "name": "sebastian/diff",
972 | "version": "5.1.1",
973 | "source": {
974 | "type": "git",
975 | "url": "https://github.com/sebastianbergmann/diff.git",
976 | "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
977 | },
978 | "dist": {
979 | "type": "zip",
980 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
981 | "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
982 | "shasum": ""
983 | },
984 | "require": {
985 | "php": ">=8.1"
986 | },
987 | "require-dev": {
988 | "phpunit/phpunit": "^10.0",
989 | "symfony/process": "^6.4"
990 | },
991 | "type": "library",
992 | "extra": {
993 | "branch-alias": {
994 | "dev-main": "5.1-dev"
995 | }
996 | },
997 | "autoload": {
998 | "classmap": [
999 | "src/"
1000 | ]
1001 | },
1002 | "notification-url": "https://packagist.org/downloads/",
1003 | "license": [
1004 | "BSD-3-Clause"
1005 | ],
1006 | "authors": [
1007 | {
1008 | "name": "Sebastian Bergmann",
1009 | "email": "sebastian@phpunit.de"
1010 | },
1011 | {
1012 | "name": "Kore Nordmann",
1013 | "email": "mail@kore-nordmann.de"
1014 | }
1015 | ],
1016 | "description": "Diff implementation",
1017 | "homepage": "https://github.com/sebastianbergmann/diff",
1018 | "keywords": [
1019 | "diff",
1020 | "udiff",
1021 | "unidiff",
1022 | "unified diff"
1023 | ],
1024 | "support": {
1025 | "issues": "https://github.com/sebastianbergmann/diff/issues",
1026 | "security": "https://github.com/sebastianbergmann/diff/security/policy",
1027 | "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
1028 | },
1029 | "funding": [
1030 | {
1031 | "url": "https://github.com/sebastianbergmann",
1032 | "type": "github"
1033 | }
1034 | ],
1035 | "time": "2024-03-02T07:15:17+00:00"
1036 | },
1037 | {
1038 | "name": "sebastian/environment",
1039 | "version": "6.1.0",
1040 | "source": {
1041 | "type": "git",
1042 | "url": "https://github.com/sebastianbergmann/environment.git",
1043 | "reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
1044 | },
1045 | "dist": {
1046 | "type": "zip",
1047 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
1048 | "reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
1049 | "shasum": ""
1050 | },
1051 | "require": {
1052 | "php": ">=8.1"
1053 | },
1054 | "require-dev": {
1055 | "phpunit/phpunit": "^10.0"
1056 | },
1057 | "suggest": {
1058 | "ext-posix": "*"
1059 | },
1060 | "type": "library",
1061 | "extra": {
1062 | "branch-alias": {
1063 | "dev-main": "6.1-dev"
1064 | }
1065 | },
1066 | "autoload": {
1067 | "classmap": [
1068 | "src/"
1069 | ]
1070 | },
1071 | "notification-url": "https://packagist.org/downloads/",
1072 | "license": [
1073 | "BSD-3-Clause"
1074 | ],
1075 | "authors": [
1076 | {
1077 | "name": "Sebastian Bergmann",
1078 | "email": "sebastian@phpunit.de"
1079 | }
1080 | ],
1081 | "description": "Provides functionality to handle HHVM/PHP environments",
1082 | "homepage": "https://github.com/sebastianbergmann/environment",
1083 | "keywords": [
1084 | "Xdebug",
1085 | "environment",
1086 | "hhvm"
1087 | ],
1088 | "support": {
1089 | "issues": "https://github.com/sebastianbergmann/environment/issues",
1090 | "security": "https://github.com/sebastianbergmann/environment/security/policy",
1091 | "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
1092 | },
1093 | "funding": [
1094 | {
1095 | "url": "https://github.com/sebastianbergmann",
1096 | "type": "github"
1097 | }
1098 | ],
1099 | "time": "2024-03-23T08:47:14+00:00"
1100 | },
1101 | {
1102 | "name": "sebastian/exporter",
1103 | "version": "5.1.2",
1104 | "source": {
1105 | "type": "git",
1106 | "url": "https://github.com/sebastianbergmann/exporter.git",
1107 | "reference": "955288482d97c19a372d3f31006ab3f37da47adf"
1108 | },
1109 | "dist": {
1110 | "type": "zip",
1111 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf",
1112 | "reference": "955288482d97c19a372d3f31006ab3f37da47adf",
1113 | "shasum": ""
1114 | },
1115 | "require": {
1116 | "ext-mbstring": "*",
1117 | "php": ">=8.1",
1118 | "sebastian/recursion-context": "^5.0"
1119 | },
1120 | "require-dev": {
1121 | "phpunit/phpunit": "^10.0"
1122 | },
1123 | "type": "library",
1124 | "extra": {
1125 | "branch-alias": {
1126 | "dev-main": "5.1-dev"
1127 | }
1128 | },
1129 | "autoload": {
1130 | "classmap": [
1131 | "src/"
1132 | ]
1133 | },
1134 | "notification-url": "https://packagist.org/downloads/",
1135 | "license": [
1136 | "BSD-3-Clause"
1137 | ],
1138 | "authors": [
1139 | {
1140 | "name": "Sebastian Bergmann",
1141 | "email": "sebastian@phpunit.de"
1142 | },
1143 | {
1144 | "name": "Jeff Welch",
1145 | "email": "whatthejeff@gmail.com"
1146 | },
1147 | {
1148 | "name": "Volker Dusch",
1149 | "email": "github@wallbash.com"
1150 | },
1151 | {
1152 | "name": "Adam Harvey",
1153 | "email": "aharvey@php.net"
1154 | },
1155 | {
1156 | "name": "Bernhard Schussek",
1157 | "email": "bschussek@gmail.com"
1158 | }
1159 | ],
1160 | "description": "Provides the functionality to export PHP variables for visualization",
1161 | "homepage": "https://www.github.com/sebastianbergmann/exporter",
1162 | "keywords": [
1163 | "export",
1164 | "exporter"
1165 | ],
1166 | "support": {
1167 | "issues": "https://github.com/sebastianbergmann/exporter/issues",
1168 | "security": "https://github.com/sebastianbergmann/exporter/security/policy",
1169 | "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2"
1170 | },
1171 | "funding": [
1172 | {
1173 | "url": "https://github.com/sebastianbergmann",
1174 | "type": "github"
1175 | }
1176 | ],
1177 | "time": "2024-03-02T07:17:12+00:00"
1178 | },
1179 | {
1180 | "name": "sebastian/global-state",
1181 | "version": "6.0.2",
1182 | "source": {
1183 | "type": "git",
1184 | "url": "https://github.com/sebastianbergmann/global-state.git",
1185 | "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
1186 | },
1187 | "dist": {
1188 | "type": "zip",
1189 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
1190 | "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
1191 | "shasum": ""
1192 | },
1193 | "require": {
1194 | "php": ">=8.1",
1195 | "sebastian/object-reflector": "^3.0",
1196 | "sebastian/recursion-context": "^5.0"
1197 | },
1198 | "require-dev": {
1199 | "ext-dom": "*",
1200 | "phpunit/phpunit": "^10.0"
1201 | },
1202 | "type": "library",
1203 | "extra": {
1204 | "branch-alias": {
1205 | "dev-main": "6.0-dev"
1206 | }
1207 | },
1208 | "autoload": {
1209 | "classmap": [
1210 | "src/"
1211 | ]
1212 | },
1213 | "notification-url": "https://packagist.org/downloads/",
1214 | "license": [
1215 | "BSD-3-Clause"
1216 | ],
1217 | "authors": [
1218 | {
1219 | "name": "Sebastian Bergmann",
1220 | "email": "sebastian@phpunit.de"
1221 | }
1222 | ],
1223 | "description": "Snapshotting of global state",
1224 | "homepage": "https://www.github.com/sebastianbergmann/global-state",
1225 | "keywords": [
1226 | "global state"
1227 | ],
1228 | "support": {
1229 | "issues": "https://github.com/sebastianbergmann/global-state/issues",
1230 | "security": "https://github.com/sebastianbergmann/global-state/security/policy",
1231 | "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2"
1232 | },
1233 | "funding": [
1234 | {
1235 | "url": "https://github.com/sebastianbergmann",
1236 | "type": "github"
1237 | }
1238 | ],
1239 | "time": "2024-03-02T07:19:19+00:00"
1240 | },
1241 | {
1242 | "name": "sebastian/lines-of-code",
1243 | "version": "2.0.2",
1244 | "source": {
1245 | "type": "git",
1246 | "url": "https://github.com/sebastianbergmann/lines-of-code.git",
1247 | "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
1248 | },
1249 | "dist": {
1250 | "type": "zip",
1251 | "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
1252 | "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
1253 | "shasum": ""
1254 | },
1255 | "require": {
1256 | "nikic/php-parser": "^4.18 || ^5.0",
1257 | "php": ">=8.1"
1258 | },
1259 | "require-dev": {
1260 | "phpunit/phpunit": "^10.0"
1261 | },
1262 | "type": "library",
1263 | "extra": {
1264 | "branch-alias": {
1265 | "dev-main": "2.0-dev"
1266 | }
1267 | },
1268 | "autoload": {
1269 | "classmap": [
1270 | "src/"
1271 | ]
1272 | },
1273 | "notification-url": "https://packagist.org/downloads/",
1274 | "license": [
1275 | "BSD-3-Clause"
1276 | ],
1277 | "authors": [
1278 | {
1279 | "name": "Sebastian Bergmann",
1280 | "email": "sebastian@phpunit.de",
1281 | "role": "lead"
1282 | }
1283 | ],
1284 | "description": "Library for counting the lines of code in PHP source code",
1285 | "homepage": "https://github.com/sebastianbergmann/lines-of-code",
1286 | "support": {
1287 | "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
1288 | "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
1289 | "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
1290 | },
1291 | "funding": [
1292 | {
1293 | "url": "https://github.com/sebastianbergmann",
1294 | "type": "github"
1295 | }
1296 | ],
1297 | "time": "2023-12-21T08:38:20+00:00"
1298 | },
1299 | {
1300 | "name": "sebastian/object-enumerator",
1301 | "version": "5.0.0",
1302 | "source": {
1303 | "type": "git",
1304 | "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1305 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906"
1306 | },
1307 | "dist": {
1308 | "type": "zip",
1309 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906",
1310 | "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906",
1311 | "shasum": ""
1312 | },
1313 | "require": {
1314 | "php": ">=8.1",
1315 | "sebastian/object-reflector": "^3.0",
1316 | "sebastian/recursion-context": "^5.0"
1317 | },
1318 | "require-dev": {
1319 | "phpunit/phpunit": "^10.0"
1320 | },
1321 | "type": "library",
1322 | "extra": {
1323 | "branch-alias": {
1324 | "dev-main": "5.0-dev"
1325 | }
1326 | },
1327 | "autoload": {
1328 | "classmap": [
1329 | "src/"
1330 | ]
1331 | },
1332 | "notification-url": "https://packagist.org/downloads/",
1333 | "license": [
1334 | "BSD-3-Clause"
1335 | ],
1336 | "authors": [
1337 | {
1338 | "name": "Sebastian Bergmann",
1339 | "email": "sebastian@phpunit.de"
1340 | }
1341 | ],
1342 | "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1343 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1344 | "support": {
1345 | "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
1346 | "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0"
1347 | },
1348 | "funding": [
1349 | {
1350 | "url": "https://github.com/sebastianbergmann",
1351 | "type": "github"
1352 | }
1353 | ],
1354 | "time": "2023-02-03T07:08:32+00:00"
1355 | },
1356 | {
1357 | "name": "sebastian/object-reflector",
1358 | "version": "3.0.0",
1359 | "source": {
1360 | "type": "git",
1361 | "url": "https://github.com/sebastianbergmann/object-reflector.git",
1362 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957"
1363 | },
1364 | "dist": {
1365 | "type": "zip",
1366 | "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957",
1367 | "reference": "24ed13d98130f0e7122df55d06c5c4942a577957",
1368 | "shasum": ""
1369 | },
1370 | "require": {
1371 | "php": ">=8.1"
1372 | },
1373 | "require-dev": {
1374 | "phpunit/phpunit": "^10.0"
1375 | },
1376 | "type": "library",
1377 | "extra": {
1378 | "branch-alias": {
1379 | "dev-main": "3.0-dev"
1380 | }
1381 | },
1382 | "autoload": {
1383 | "classmap": [
1384 | "src/"
1385 | ]
1386 | },
1387 | "notification-url": "https://packagist.org/downloads/",
1388 | "license": [
1389 | "BSD-3-Clause"
1390 | ],
1391 | "authors": [
1392 | {
1393 | "name": "Sebastian Bergmann",
1394 | "email": "sebastian@phpunit.de"
1395 | }
1396 | ],
1397 | "description": "Allows reflection of object attributes, including inherited and non-public ones",
1398 | "homepage": "https://github.com/sebastianbergmann/object-reflector/",
1399 | "support": {
1400 | "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
1401 | "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0"
1402 | },
1403 | "funding": [
1404 | {
1405 | "url": "https://github.com/sebastianbergmann",
1406 | "type": "github"
1407 | }
1408 | ],
1409 | "time": "2023-02-03T07:06:18+00:00"
1410 | },
1411 | {
1412 | "name": "sebastian/recursion-context",
1413 | "version": "5.0.0",
1414 | "source": {
1415 | "type": "git",
1416 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
1417 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712"
1418 | },
1419 | "dist": {
1420 | "type": "zip",
1421 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712",
1422 | "reference": "05909fb5bc7df4c52992396d0116aed689f93712",
1423 | "shasum": ""
1424 | },
1425 | "require": {
1426 | "php": ">=8.1"
1427 | },
1428 | "require-dev": {
1429 | "phpunit/phpunit": "^10.0"
1430 | },
1431 | "type": "library",
1432 | "extra": {
1433 | "branch-alias": {
1434 | "dev-main": "5.0-dev"
1435 | }
1436 | },
1437 | "autoload": {
1438 | "classmap": [
1439 | "src/"
1440 | ]
1441 | },
1442 | "notification-url": "https://packagist.org/downloads/",
1443 | "license": [
1444 | "BSD-3-Clause"
1445 | ],
1446 | "authors": [
1447 | {
1448 | "name": "Sebastian Bergmann",
1449 | "email": "sebastian@phpunit.de"
1450 | },
1451 | {
1452 | "name": "Jeff Welch",
1453 | "email": "whatthejeff@gmail.com"
1454 | },
1455 | {
1456 | "name": "Adam Harvey",
1457 | "email": "aharvey@php.net"
1458 | }
1459 | ],
1460 | "description": "Provides functionality to recursively process PHP variables",
1461 | "homepage": "https://github.com/sebastianbergmann/recursion-context",
1462 | "support": {
1463 | "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
1464 | "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0"
1465 | },
1466 | "funding": [
1467 | {
1468 | "url": "https://github.com/sebastianbergmann",
1469 | "type": "github"
1470 | }
1471 | ],
1472 | "time": "2023-02-03T07:05:40+00:00"
1473 | },
1474 | {
1475 | "name": "sebastian/type",
1476 | "version": "4.0.0",
1477 | "source": {
1478 | "type": "git",
1479 | "url": "https://github.com/sebastianbergmann/type.git",
1480 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf"
1481 | },
1482 | "dist": {
1483 | "type": "zip",
1484 | "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf",
1485 | "reference": "462699a16464c3944eefc02ebdd77882bd3925bf",
1486 | "shasum": ""
1487 | },
1488 | "require": {
1489 | "php": ">=8.1"
1490 | },
1491 | "require-dev": {
1492 | "phpunit/phpunit": "^10.0"
1493 | },
1494 | "type": "library",
1495 | "extra": {
1496 | "branch-alias": {
1497 | "dev-main": "4.0-dev"
1498 | }
1499 | },
1500 | "autoload": {
1501 | "classmap": [
1502 | "src/"
1503 | ]
1504 | },
1505 | "notification-url": "https://packagist.org/downloads/",
1506 | "license": [
1507 | "BSD-3-Clause"
1508 | ],
1509 | "authors": [
1510 | {
1511 | "name": "Sebastian Bergmann",
1512 | "email": "sebastian@phpunit.de",
1513 | "role": "lead"
1514 | }
1515 | ],
1516 | "description": "Collection of value objects that represent the types of the PHP type system",
1517 | "homepage": "https://github.com/sebastianbergmann/type",
1518 | "support": {
1519 | "issues": "https://github.com/sebastianbergmann/type/issues",
1520 | "source": "https://github.com/sebastianbergmann/type/tree/4.0.0"
1521 | },
1522 | "funding": [
1523 | {
1524 | "url": "https://github.com/sebastianbergmann",
1525 | "type": "github"
1526 | }
1527 | ],
1528 | "time": "2023-02-03T07:10:45+00:00"
1529 | },
1530 | {
1531 | "name": "sebastian/version",
1532 | "version": "4.0.1",
1533 | "source": {
1534 | "type": "git",
1535 | "url": "https://github.com/sebastianbergmann/version.git",
1536 | "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17"
1537 | },
1538 | "dist": {
1539 | "type": "zip",
1540 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17",
1541 | "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17",
1542 | "shasum": ""
1543 | },
1544 | "require": {
1545 | "php": ">=8.1"
1546 | },
1547 | "type": "library",
1548 | "extra": {
1549 | "branch-alias": {
1550 | "dev-main": "4.0-dev"
1551 | }
1552 | },
1553 | "autoload": {
1554 | "classmap": [
1555 | "src/"
1556 | ]
1557 | },
1558 | "notification-url": "https://packagist.org/downloads/",
1559 | "license": [
1560 | "BSD-3-Clause"
1561 | ],
1562 | "authors": [
1563 | {
1564 | "name": "Sebastian Bergmann",
1565 | "email": "sebastian@phpunit.de",
1566 | "role": "lead"
1567 | }
1568 | ],
1569 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1570 | "homepage": "https://github.com/sebastianbergmann/version",
1571 | "support": {
1572 | "issues": "https://github.com/sebastianbergmann/version/issues",
1573 | "source": "https://github.com/sebastianbergmann/version/tree/4.0.1"
1574 | },
1575 | "funding": [
1576 | {
1577 | "url": "https://github.com/sebastianbergmann",
1578 | "type": "github"
1579 | }
1580 | ],
1581 | "time": "2023-02-07T11:34:05+00:00"
1582 | },
1583 | {
1584 | "name": "theseer/tokenizer",
1585 | "version": "1.2.3",
1586 | "source": {
1587 | "type": "git",
1588 | "url": "https://github.com/theseer/tokenizer.git",
1589 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
1590 | },
1591 | "dist": {
1592 | "type": "zip",
1593 | "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
1594 | "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
1595 | "shasum": ""
1596 | },
1597 | "require": {
1598 | "ext-dom": "*",
1599 | "ext-tokenizer": "*",
1600 | "ext-xmlwriter": "*",
1601 | "php": "^7.2 || ^8.0"
1602 | },
1603 | "type": "library",
1604 | "autoload": {
1605 | "classmap": [
1606 | "src/"
1607 | ]
1608 | },
1609 | "notification-url": "https://packagist.org/downloads/",
1610 | "license": [
1611 | "BSD-3-Clause"
1612 | ],
1613 | "authors": [
1614 | {
1615 | "name": "Arne Blankerts",
1616 | "email": "arne@blankerts.de",
1617 | "role": "Developer"
1618 | }
1619 | ],
1620 | "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
1621 | "support": {
1622 | "issues": "https://github.com/theseer/tokenizer/issues",
1623 | "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
1624 | },
1625 | "funding": [
1626 | {
1627 | "url": "https://github.com/theseer",
1628 | "type": "github"
1629 | }
1630 | ],
1631 | "time": "2024-03-03T12:36:25+00:00"
1632 | }
1633 | ],
1634 | "aliases": [],
1635 | "minimum-stability": "stable",
1636 | "stability-flags": [],
1637 | "prefer-stable": false,
1638 | "prefer-lowest": false,
1639 | "platform": [],
1640 | "platform-dev": [],
1641 | "plugin-api-version": "2.6.0"
1642 | }
1643 |
--------------------------------------------------------------------------------
/lua/neotest-phpunit/config.lua:
--------------------------------------------------------------------------------
1 | local M = {}
2 |
3 | M.get_phpunit_cmd = function()
4 | return "vendor/bin/phpunit"
5 | end
6 |
7 | M.get_env = function()
8 | return {}
9 | end
10 |
11 | M.get_root_ignore_files = function()
12 | return {}
13 | end
14 |
15 | M.get_root_files = function()
16 | return { "composer.json", "phpunit.xml", ".gitignore" }
17 | end
18 |
19 | M.get_filter_dirs = function()
20 | return { ".git", "node_modules" }
21 | end
22 |
23 | return M
24 |
--------------------------------------------------------------------------------
/lua/neotest-phpunit/init.lua:
--------------------------------------------------------------------------------
1 | local ok, async = pcall(require, "nio")
2 | if not ok then
3 | async = require("neotest.async")
4 | end
5 |
6 | local lib = require("neotest.lib")
7 | local logger = require("neotest.logging")
8 | local utils = require("neotest-phpunit.utils")
9 | local config = require("neotest-phpunit.config")
10 |
11 | local dap_configuration
12 |
13 | local function get_strategy_config(strategy, program, args)
14 | local cfg = {
15 | dap = function()
16 | vim.validate({
17 | dap = {
18 | dap_configuration,
19 | function(val)
20 | local valid = type(val) == "table" and not vim.tbl_isempty(val)
21 |
22 | return valid, "Configure `dap` field (like in dap.configurations.php) before using this strategy"
23 | end,
24 | "not empty table",
25 | },
26 | })
27 | vim.validate({
28 | phpunit_cmd = {
29 | program,
30 | function(val)
31 | return type(val) == "string", "For `dap` strategy `phpunit_cmd` must be (or return) string."
32 | end,
33 | "string",
34 | },
35 | })
36 |
37 | return vim.tbl_extend("keep", {
38 | type = "php",
39 | name = "Neotest Debugger",
40 | cwd = async.fn.getcwd(),
41 | program = program,
42 | args = args,
43 | runtimeArgs = { "-dzend_extension=xdebug.so" },
44 | }, dap_configuration or {})
45 | end,
46 | }
47 | if cfg[strategy] then
48 | return cfg[strategy]()
49 | end
50 | end
51 |
52 | ---@class neotest.Adapter
53 | ---@field name string
54 | local NeotestAdapter = { name = "neotest-phpunit" }
55 |
56 | ---Find the project root directory given a current directory to work from.
57 | ---Should no root be found, the adapter can still be used in a non-project context if a test file matches.
58 | ---@async
59 | ---@param dir string @Directory to treat as cwd
60 | ---@return string | nil @Absolute root dir of test suite
61 | function NeotestAdapter.root(dir)
62 | local result = nil
63 |
64 | for _, root_ignore_file in ipairs(config.get_root_ignore_files()) do
65 | result = lib.files.match_root_pattern(root_ignore_file)(dir)
66 | if result then
67 | return nil
68 | end
69 | end
70 |
71 | for _, root_file in ipairs(config.get_root_files()) do
72 | result = lib.files.match_root_pattern(root_file)(dir)
73 | if result then
74 | break
75 | end
76 | end
77 |
78 | return result
79 | end
80 |
81 | ---@async
82 | ---@param file_path string
83 | ---@return boolean
84 | function NeotestAdapter.is_test_file(file_path)
85 | return vim.endswith(file_path, "Test.php")
86 | end
87 |
88 | ---Filter directories when searching for test files
89 | ---@async
90 | ---@param name string Name of directory
91 | ---@return boolean
92 | function NeotestAdapter.filter_dir(name)
93 | for _, filter_dir in ipairs(config.get_filter_dirs()) do
94 | if name == filter_dir then
95 | return false
96 | end
97 | end
98 |
99 | return true
100 | end
101 |
102 | ---Given a file path, parse all the tests within it.
103 | ---@async
104 | ---@param file_path string Absolute file path
105 | ---@return neotest.Tree | nil
106 | function NeotestAdapter.discover_positions(path)
107 | if not NeotestAdapter.is_test_file(path) then
108 | return nil
109 | end
110 |
111 | local query = [[
112 | ((class_declaration
113 | name: (name) @namespace.name (#match? @namespace.name "Test")
114 | )) @namespace.definition
115 |
116 | ((method_declaration
117 | (attribute_list
118 | (attribute_group
119 | (attribute) @test_attribute (#match? @test_attribute "Test")
120 | )
121 | )
122 | (
123 | (visibility_modifier)
124 | (name) @test.name
125 | ) @test.definition
126 | ))
127 |
128 | ((method_declaration
129 | (name) @test.name (#match? @test.name "test")
130 | )) @test.definition
131 |
132 | (((comment) @test_comment (#match? @test_comment "\\@test") .
133 | (method_declaration
134 | (name) @test.name
135 | ) @test.definition
136 | ))
137 | ]]
138 |
139 | return lib.treesitter.parse_positions(path, query, {
140 | position_id = "require('neotest-phpunit.utils').make_test_id",
141 | })
142 | end
143 |
144 | ---@param args neotest.RunArgs
145 | ---@return neotest.RunSpec | nil
146 | function NeotestAdapter.build_spec(args)
147 | local position = args.tree:data()
148 | local results_path = async.fn.tempname()
149 | local program = config.get_phpunit_cmd()
150 |
151 | local script_args = {
152 | position.name ~= "tests" and position.path,
153 | "--log-junit=" .. results_path,
154 | }
155 |
156 | if position.type == "test" then
157 | local filter_args = vim.tbl_flatten({
158 | "--filter",
159 | "::" .. position.name .. "( with data set .*)?$",
160 | })
161 |
162 | logger.info("position.path:", { position.path })
163 | logger.info("--filter position.name:", { position.name })
164 |
165 | script_args = vim.tbl_flatten({
166 | script_args,
167 | filter_args,
168 | })
169 | end
170 |
171 | local command = vim.tbl_flatten({
172 | program,
173 | script_args,
174 | })
175 |
176 | logger.trace("PHPUnit command: ", { command })
177 |
178 | ---@type neotest.RunSpec
179 | return {
180 | command = command,
181 | context = {
182 | results_path = results_path,
183 | },
184 | strategy = get_strategy_config(args.strategy, program, script_args),
185 | env = args.env or config.get_env(),
186 | }
187 | end
188 |
189 | ---@async
190 | ---@param spec neotest.RunSpec
191 | ---@param result neotest.StrategyResult
192 | ---@param tree neotest.Tree
193 | ---@return neotest.Result[]
194 | function NeotestAdapter.results(test, result, tree)
195 | local output_file = test.context.results_path
196 |
197 | local ok, data = pcall(lib.files.read, output_file)
198 | if not ok then
199 | logger.error("No test output file found:", output_file)
200 | return {}
201 | end
202 |
203 | local ok, parsed_data = pcall(lib.xml.parse, data)
204 | if not ok then
205 | logger.error("Failed to parse test output:", output_file)
206 | return {}
207 | end
208 |
209 | local ok, results = pcall(utils.get_test_results, parsed_data, output_file)
210 | if not ok then
211 | logger.error("Could not get test results", output_file)
212 | return {}
213 | end
214 |
215 | logger.trace("Results:", results)
216 | return results
217 | end
218 |
219 | local is_callable = function(obj)
220 | return type(obj) == "function" or (type(obj) == "table" and obj.__call)
221 | end
222 |
223 | setmetatable(NeotestAdapter, {
224 | __call = function(_, opts)
225 | if is_callable(opts.phpunit_cmd) then
226 | config.get_phpunit_cmd = opts.phpunit_cmd
227 | elseif opts.phpunit_cmd then
228 | config.get_phpunit_cmd = function()
229 | return opts.phpunit_cmd
230 | end
231 | end
232 | if is_callable(opts.root_ignore_files) then
233 | config.get_root_ignore_files = opts.root_ignore_files
234 | elseif opts.root_ignore_files then
235 | config.get_root_ignore_files = function()
236 | return opts.root_ignore_files
237 | end
238 | end
239 | if is_callable(opts.root_files) then
240 | config.get_root_files = opts.root_files
241 | elseif opts.root_files then
242 | config.get_root_files = function()
243 | return opts.root_files
244 | end
245 | end
246 | if is_callable(opts.filter_dirs) then
247 | config.get_filter_dirs = opts.filter_dirs
248 | elseif opts.filter_dirs then
249 | config.get_filter_dirs = function()
250 | return opts.filter_dirs
251 | end
252 | end
253 | if is_callable(opts.env) then
254 | config.get_env = opts.env
255 | elseif type(opts.env) == "table" then
256 | config.get_env = function()
257 | return opts.env
258 | end
259 | end
260 | if type(opts.dap) == "table" then
261 | dap_configuration = opts.dap
262 | end
263 | return NeotestAdapter
264 | end,
265 | })
266 |
267 | return NeotestAdapter
268 |
--------------------------------------------------------------------------------
/lua/neotest-phpunit/utils.lua:
--------------------------------------------------------------------------------
1 | local logger = require("neotest.logging")
2 |
3 | local M = {}
4 | local separator = "::"
5 |
6 | ---Generate an id which we can use to match Treesitter queries and PHPUnit tests
7 | ---@param position neotest.Position The position to return an ID for
8 | ---@param namespace neotest.Position[] Any namespaces the position is within
9 | ---@return string
10 | M.make_test_id = function(position)
11 | -- Treesitter starts line numbers from 0 so we add 1
12 | local id = position.path .. separator .. (tonumber(position.range[1]) + 1)
13 |
14 | logger.info("Path to test file:", { position.path })
15 | logger.info("Treesitter id:", { id })
16 |
17 | return id
18 | end
19 |
20 | ---Recursively iterate through a deeply nested table to obtain specified keys
21 | ---@param data_table table
22 | ---@param key string
23 | ---@param output_table table
24 | ---@return table
25 | local function iterate_key(data_table, key, output_table)
26 | if type(data_table) == "table" then
27 | for k, v in pairs(data_table) do
28 | if key == k then
29 | table.insert(output_table, v)
30 | end
31 | iterate_key(v, key, output_table)
32 | end
33 | end
34 | return output_table
35 | end
36 |
37 | ---Extract the failure messages from the tests
38 | ---@param tests table,
39 | ---@return boolean|table
40 | local function errors_or_fails(tests)
41 | local errors_fails = {}
42 |
43 | iterate_key(tests, "error", errors_fails)
44 | iterate_key(tests, "failure", errors_fails)
45 |
46 | if #errors_fails == 0 then
47 | return false
48 | end
49 |
50 | return errors_fails
51 | end
52 |
53 | ---Make the outputs for a given test
54 | ---@param test table
55 | ---@param output_file string
56 | ---@return table
57 | local function make_outputs(test, output_file)
58 | local test_attr = test["_attr"] or test[1]["_attr"]
59 |
60 | local test_id = test_attr.file .. separator .. test_attr.line
61 | logger.info("PHPUnit id:", { test_id })
62 |
63 | local classname = test_attr.classname or test_attr.class
64 | local test_output = {
65 | status = "passed",
66 | short = string.upper(classname) .. "\n-> " .. "PASSED" .. " - " .. test_attr.name,
67 | output_file = output_file,
68 | }
69 |
70 | local test_failed = errors_or_fails(test)
71 | if test_failed then
72 | test_output.status = "failed"
73 | test_output.short = test_failed[1]["failure"] or test_failed[1]["errors"]
74 | test_output.errors = {
75 | {
76 | line = test_attr.line,
77 | },
78 | }
79 | end
80 |
81 | return test_id, test_output
82 | end
83 |
84 | ---Iterate through test results and create a table of test IDs and outputs
85 | ---@param tests table
86 | ---@param output_file string
87 | ---@param output_table table
88 | ---@return table
89 | local function iterate_test_outputs(tests, output_file, output_table)
90 | for i = 1, #tests, 1 do
91 | if #tests[i] == 0 then
92 | local test_id, test_output = make_outputs(tests[i], output_file)
93 | output_table[test_id] = test_output
94 | else
95 | iterate_test_outputs(tests[i], output_file, output_table)
96 | end
97 | end
98 | return output_table
99 | end
100 |
101 | ---Get the test results from the parsed xml
102 | ---@param parsed_xml_output table
103 | ---@param output_file string
104 | ---@return neotest.Result[]
105 | M.get_test_results = function(parsed_xml_output, output_file)
106 | local tests = iterate_key(parsed_xml_output, "testcase", {})
107 | return iterate_test_outputs(tests, output_file, {})
108 | end
109 |
110 | return M
111 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 | ./tests/Unit
10 |
11 |
12 | ./tests/Examples
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/scripts/test:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | tempfile=".test_output.tmp"
3 |
4 | if [[ -n $1 ]]; then
5 | nvim --headless --noplugin -u specs/init.vim -c "PlenaryBustedFile $1" | tee "${tempfile}"
6 | else
7 | nvim --headless --noplugin -u specs/init.vim -c "PlenaryBustedDirectory specs/ {minimal_init = 'specs/init.vim'}" | tee "${tempfile}"
8 | fi
9 |
10 | # Plenary doesn't emit exit code 1 when tests have errors during setup
11 | errors=$(sed 's/\x1b\[[0-9;]*m//g' "${tempfile}" | awk '/(Errors|Failed) :/ {print $3}' | grep -v '0')
12 |
13 | rm "${tempfile}"
14 |
15 | if [[ -n $errors ]]; then
16 | echo "Tests failed"
17 | exit 1
18 | fi
19 |
20 | exit 0
21 |
--------------------------------------------------------------------------------
/specs/init.vim:
--------------------------------------------------------------------------------
1 | set rtp+=.
2 | set rtp+=../plenary.nvim
3 | runtime! plugin/plenary.vim
4 |
--------------------------------------------------------------------------------
/specs/utils_spec.lua:
--------------------------------------------------------------------------------
1 | local utils = require("neotest-phpunit.utils")
2 |
3 | describe("get_test_results", function()
4 | it("parses output when testing a method", function()
5 | local output_file = "/tmp/nvimhYaIPj/3"
6 | local xml_output = {
7 | testsuites = {
8 | testsuite = {
9 | _attr = {
10 | assertions = "1",
11 | errors = "0",
12 | failures = "0",
13 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php",
14 | name = "ExampleTest",
15 | skipped = "0",
16 | tests = "1",
17 | time = "0.002292",
18 | warnings = "0",
19 | },
20 | testcase = {
21 | _attr = {
22 | assertions = "1",
23 | class = "ExampleTest",
24 | classname = "ExampleTest",
25 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php",
26 | line = "7",
27 | name = "test_that_true_is_true",
28 | time = "0.002292",
29 | },
30 | },
31 | },
32 | },
33 | }
34 |
35 | local expected = {
36 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php::7"] = {
37 | ["output_file"] = "/tmp/nvimhYaIPj/3",
38 | ["short"] = [[EXAMPLETEST
39 | -> PASSED - test_that_true_is_true]],
40 | ["status"] = "passed",
41 | },
42 | }
43 |
44 | assert.are.same(utils.get_test_results(xml_output, output_file), expected)
45 | end)
46 |
47 | it("parses output when testing a file", function()
48 | local output_file = "/tmp/nvimhYaIPj/3"
49 | local xml_output = {
50 | testsuites = {
51 | testsuite = {
52 | _attr = {
53 | assertions = "2",
54 | errors = "0",
55 | failures = "0",
56 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php",
57 | name = "TestProject\\UserTest",
58 | skipped = "0",
59 | tests = "2",
60 | time = "0.001525",
61 | warnings = "0",
62 | },
63 | testcase = {
64 | {
65 | _attr = {
66 | assertions = "3",
67 | class = "TestProject\\UserTest",
68 | classname = "TestProject.UserTest",
69 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php",
70 | line = "13",
71 | name = "testClassConstructor",
72 | time = "0.000949",
73 | },
74 | },
75 | {
76 | _attr = {
77 | assertions = "2",
78 | class = "TestProject\\UserTest",
79 | classname = "TestProject.UserTest",
80 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php",
81 | line = "22",
82 | name = "testTellName",
83 | time = "0.000135",
84 | },
85 | },
86 | },
87 | },
88 | },
89 | }
90 |
91 | local expected = {
92 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php::13"] = {
93 | ["output_file"] = "/tmp/nvimhYaIPj/3",
94 | ["short"] = [[TESTPROJECT.USERTEST
95 | -> PASSED - testClassConstructor]],
96 | ["status"] = "passed",
97 | },
98 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php::22"] = {
99 | ["output_file"] = "/tmp/nvimhYaIPj/3",
100 | ["short"] = [[TESTPROJECT.USERTEST
101 | -> PASSED - testTellName]],
102 | ["status"] = "passed",
103 | },
104 | }
105 |
106 | assert.are.same(utils.get_test_results(xml_output, output_file), expected)
107 | end)
108 |
109 | it("parses a file even if there is a failure", function()
110 | local output_file = "/tmp/nvimhYaIPj/3"
111 | local xml_output = {
112 | testsuites = {
113 | testsuite = {
114 | _attr = {
115 | assertions = "2",
116 | errors = "0",
117 | failures = "1",
118 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php",
119 | name = "ExampleTest",
120 | skipped = "0",
121 | tests = "2",
122 | time = "0.001008",
123 | warnings = "0",
124 | },
125 | testcase = {
126 | {
127 | _attr = {
128 | assertions = "1",
129 | class = "ExampleTest",
130 | classname = "ExampleTest",
131 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php",
132 | line = "12",
133 | name = "test_that_false_is_true",
134 | time = "0.000141",
135 | },
136 | failure = {
137 | [[ExampleTest::test_that_false_is_true
138 | Failed asserting that false is true.
139 |
140 | /Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php:14]],
141 | _attr = {
142 | type = "PHPUnit\\Framework\\ExpectationFailedException",
143 | },
144 | },
145 | },
146 | },
147 | },
148 | },
149 | }
150 |
151 | local expected = {
152 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php::12"] = {
153 | errors = {
154 | {
155 | line = "12",
156 | },
157 | },
158 | output_file = "/tmp/nvimhYaIPj/3",
159 | status = "failed",
160 | },
161 | }
162 |
163 | assert.are.same(utils.get_test_results(xml_output, output_file), expected)
164 | end)
165 |
166 | it("parses multiple files", function()
167 | local output_file = "/tmp/nvimhYaIPj/3"
168 | local xml_output = {
169 | testsuites = {
170 | testsuite = {
171 | _attr = {
172 | assertions = "18",
173 | errors = "0",
174 | failures = "1",
175 | name = "",
176 | skipped = "0",
177 | tests = "8",
178 | time = "0.001904",
179 | warnings = "0",
180 | },
181 | testsuite = {
182 | {
183 | _attr = {
184 | assertions = "2",
185 | errors = "0",
186 | failures = "1",
187 | name = "Unit",
188 | skipped = "0",
189 | tests = "2",
190 | time = "0.001233",
191 | warnings = "0",
192 | },
193 | testsuite = {
194 | _attr = {
195 | assertions = "2",
196 | errors = "0",
197 | failures = "1",
198 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php",
199 | name = "ExampleTest",
200 | skipped = "0",
201 | tests = "2",
202 | time = "0.001233",
203 | warnings = "0",
204 | },
205 | testcase = {
206 | {
207 | _attr = {
208 | assertions = "1",
209 | class = "ExampleTest",
210 | classname = "ExampleTest",
211 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php",
212 | line = "7",
213 | name = "test_that_true_is_true",
214 | time = "0.001027",
215 | },
216 | },
217 | {
218 | _attr = {
219 | assertions = "1",
220 | class = "ExampleTest",
221 | classname = "ExampleTest",
222 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php",
223 | line = "13",
224 | name = "this_should_fail",
225 | time = "0.000207",
226 | },
227 | failure = {
228 | [[ExampleTest::this_should_fail
229 | Failed asserting that false is true.
230 |
231 | /Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php:15]],
232 | _attr = {
233 | type = "PHPUnit\\Framework\\ExpectationFailedException",
234 | },
235 | },
236 | },
237 | },
238 | },
239 | },
240 | {
241 | _attr = {
242 | assertions = "16",
243 | errors = "0",
244 | failures = "0",
245 | name = "Examples",
246 | skipped = "0",
247 | tests = "6",
248 | time = "0.000671",
249 | warnings = "0",
250 | },
251 | testsuite = {
252 | {
253 | _attr = {
254 | assertions = "15",
255 | errors = "0",
256 | failures = "0",
257 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php",
258 | name = "TestProject\\UserTest",
259 | skipped = "0",
260 | tests = "5",
261 | time = "0.000646",
262 | warnings = "0",
263 | },
264 | testcase = {
265 | {
266 | _attr = {
267 | assertions = "3",
268 | class = "TestProject\\UserTest",
269 | classname = "TestProject.UserTest",
270 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php",
271 | line = "13",
272 | name = "testClassConstructor",
273 | time = "0.000145",
274 | },
275 | },
276 | {
277 | _attr = {
278 | assertions = "2",
279 | class = "TestProject\\UserTest",
280 | classname = "TestProject.UserTest",
281 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php",
282 | line = "22",
283 | name = "testTellName",
284 | time = "0.000143",
285 | },
286 | },
287 | {
288 | _attr = {
289 | assertions = "2",
290 | class = "TestProject\\UserTest",
291 | classname = "TestProject.UserTest",
292 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php",
293 | line = "30",
294 | name = "testTellAge",
295 | time = "0.000026",
296 | },
297 | },
298 | {
299 | _attr = {
300 | assertions = "3",
301 | class = "TestProject\\UserTest",
302 | classname = "TestProject.UserTest",
303 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php",
304 | line = "38",
305 | name = "testAddFavoriteMovie",
306 | time = "0.000149",
307 | },
308 | },
309 | {
310 | _attr = {
311 | assertions = "5",
312 | class = "TestProject\\UserTest",
313 | classname = "TestProject.UserTest",
314 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php",
315 | line = "47",
316 | name = "testRemoveFavoriteMovie",
317 | time = "0.000183",
318 | },
319 | },
320 | },
321 | },
322 | {
323 | _attr = {
324 | assertions = "1",
325 | errors = "0",
326 | failures = "0",
327 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/some/deep/nesting/NestingTest.php",
328 | name = "NestingTest",
329 | skipped = "0",
330 | tests = "1",
331 | time = "0.000025",
332 | warnings = "0",
333 | },
334 | testcase = {
335 | _attr = {
336 | assertions = "1",
337 | class = "NestingTest",
338 | classname = "NestingTest",
339 | file = "/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/some/deep/nesting/NestingTest.php",
340 | line = "7",
341 | name = "test_something_that_is_true",
342 | time = "0.000025",
343 | },
344 | },
345 | },
346 | },
347 | },
348 | },
349 | },
350 | },
351 | }
352 |
353 | local expected = {
354 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php::13"] = {
355 | output_file = "/tmp/nvimhYaIPj/3",
356 | short = [[TESTPROJECT.USERTEST
357 | -> PASSED - testClassConstructor]],
358 | status = "passed",
359 | },
360 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php::22"] = {
361 | output_file = "/tmp/nvimhYaIPj/3",
362 | short = [[TESTPROJECT.USERTEST
363 | -> PASSED - testTellName]],
364 | status = "passed",
365 | },
366 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php::30"] = {
367 | output_file = "/tmp/nvimhYaIPj/3",
368 | short = [[TESTPROJECT.USERTEST
369 | -> PASSED - testTellAge]],
370 | status = "passed",
371 | },
372 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php::38"] = {
373 | output_file = "/tmp/nvimhYaIPj/3",
374 | short = [[TESTPROJECT.USERTEST
375 | -> PASSED - testAddFavoriteMovie]],
376 | status = "passed",
377 | },
378 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/UserTest.php::47"] = {
379 | output_file = "/tmp/nvimhYaIPj/3",
380 | short = [[TESTPROJECT.USERTEST
381 | -> PASSED - testRemoveFavoriteMovie]],
382 | status = "passed",
383 | },
384 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Examples/some/deep/nesting/NestingTest.php::7"] = {
385 | output_file = "/tmp/nvimhYaIPj/3",
386 | short = [[NESTINGTEST
387 | -> PASSED - test_something_that_is_true]],
388 | status = "passed",
389 | },
390 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php::13"] = {
391 | errors = {
392 | {
393 | line = "13",
394 | },
395 | },
396 | output_file = "/tmp/nvimhYaIPj/3",
397 | status = "failed",
398 | },
399 | ["/Users/Oli/Code/Projects/neotest-phpunit/tests/Unit/ExampleTest.php::7"] = {
400 | output_file = "/tmp/nvimhYaIPj/3",
401 | short = [[EXAMPLETEST
402 | -> PASSED - test_that_true_is_true]],
403 | status = "passed",
404 | },
405 | }
406 |
407 | assert.are.same(utils.get_test_results(xml_output, output_file), expected)
408 | end)
409 | end)
410 |
--------------------------------------------------------------------------------
/src/User.php:
--------------------------------------------------------------------------------
1 | age = $age;
20 | $this->name = $name;
21 | }
22 |
23 | public function tellName(): string
24 | {
25 | return "My name is " . $this->name . ".";
26 | }
27 |
28 | public function tellAge(): string
29 | {
30 | return "I am " . $this->age . " years old.";
31 | }
32 |
33 | public function addFavoriteMovie(string $movie): bool
34 | {
35 | $this->favorite_movies[] = $movie;
36 |
37 | return true;
38 | }
39 |
40 | public function removeFavoriteMovie(string $movie): bool
41 | {
42 | if (!in_array($movie, $this->favorite_movies)) throw new InvalidArgumentException("Unknown movie: " . $movie);
43 |
44 | unset($this->favorite_movies[array_search($movie, $this->favorite_movies)]);
45 |
46 | return true;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/autoload.php:
--------------------------------------------------------------------------------
1 | assertSame('John', $user->name);
18 | $this->assertSame(18, $user->age);
19 | $this->assertEmpty($user->favorite_movies);
20 | }
21 |
22 | public function testTellName()
23 | {
24 | $user = new User(18, 'John');
25 |
26 | $this->assertIsString($user->tellName());
27 | $this->assertStringContainsStringIgnoringCase('John', $user->tellName());
28 | }
29 |
30 | public function testTellAge()
31 | {
32 | $user = new User(18, 'John');
33 |
34 | $this->assertIsString($user->tellAge());
35 | $this->assertStringContainsStringIgnoringCase('18', $user->tellAge());
36 | }
37 |
38 | public function testAddFavoriteMovie()
39 | {
40 | $user = new User(18, 'John');
41 |
42 | $this->assertTrue($user->addFavoriteMovie('Avengers'));
43 | $this->assertContains('Avengers', $user->favorite_movies);
44 | $this->assertCount(1, $user->favorite_movies);
45 | }
46 |
47 | public function testRemoveFavoriteMovie()
48 | {
49 | $user = new User(18, 'John');
50 |
51 | $this->assertTrue($user->addFavoriteMovie('Avengers'));
52 | $this->assertTrue($user->addFavoriteMovie('Justice League'));
53 |
54 | $this->assertTrue($user->removeFavoriteMovie('Avengers'));
55 | $this->assertNotContains('Avengers', $user->favorite_movies);
56 | $this->assertCount(1, $user->favorite_movies);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/tests/Examples/some/deep/nesting/NestingTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/tests/Unit/AttributeTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/tests/Unit/ExampleTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
10 | }
11 |
12 | public function test_that_true_is_true_again()
13 | {
14 | $this->assertTrue(true);
15 | }
16 |
17 | /** @test */
18 | public function test_this_should_fail()
19 | {
20 | $this->assertTrue(false);
21 | }
22 |
23 | /** @test */
24 | public function test_this_should_fail_also()
25 | {
26 | $this->assertTrue(false);
27 | }
28 |
29 | /** @test */
30 | public function this_should_fail_as_well()
31 | {
32 | $this->assertTrue(false);
33 | }
34 |
35 | public function this_should_not_run()
36 | {
37 | $this->assertTrue(true);
38 | }
39 |
40 | /**
41 | * @dataProvider myProvider
42 | */
43 | public function testWithDataProvider(bool $val): void
44 | {
45 | $this->assertTrue($val);
46 | }
47 |
48 | public function myProvider(): array
49 | {
50 | return [
51 | 'set one' => [true],
52 | 'set two' => [false],
53 | ];
54 | }
55 | }
56 |
--------------------------------------------------------------------------------