mirror of
https://github.com/dorny/test-reporter.git
synced 2026-02-01 02:45:22 -08:00
Merge pull request #422 from mbeccati/phpunit-support
This commit is contained in:
10
README.md
10
README.md
@@ -20,6 +20,7 @@ This [Github Action](https://github.com/features/actions) displays test results
|
|||||||
- Java / [JUnit](https://junit.org/)
|
- Java / [JUnit](https://junit.org/)
|
||||||
- JavaScript / [JEST](https://jestjs.io/) / [Mocha](https://mochajs.org/)
|
- JavaScript / [JEST](https://jestjs.io/) / [Mocha](https://mochajs.org/)
|
||||||
- Python / [pytest](https://docs.pytest.org/en/stable/) / [unittest](https://docs.python.org/3/library/unittest.html)
|
- Python / [pytest](https://docs.pytest.org/en/stable/) / [unittest](https://docs.python.org/3/library/unittest.html)
|
||||||
|
- PHP / [PHPUnit](https://phpunit.de/)
|
||||||
- Ruby / [RSpec](https://rspec.info/)
|
- Ruby / [RSpec](https://rspec.info/)
|
||||||
- Swift / xUnit
|
- Swift / xUnit
|
||||||
|
|
||||||
@@ -147,6 +148,7 @@ jobs:
|
|||||||
# java-junit
|
# java-junit
|
||||||
# jest-junit
|
# jest-junit
|
||||||
# mocha-json
|
# mocha-json
|
||||||
|
# phpunit-junit
|
||||||
# python-xunit
|
# python-xunit
|
||||||
# rspec-json
|
# rspec-json
|
||||||
# swift-xunit
|
# swift-xunit
|
||||||
@@ -314,6 +316,14 @@ This is due to the fact Java stack traces don't contain a full path to the sourc
|
|||||||
Some heuristic was necessary to figure out the mapping between the line in the stack trace and an actual source file.
|
Some heuristic was necessary to figure out the mapping between the line in the stack trace and an actual source file.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>phpunit-junit</summary>
|
||||||
|
|
||||||
|
[PHPUnit](https://phpunit.de/) can generate JUnit XML via CLI:
|
||||||
|
`phpunit --log-junit reports/phpunit-junit.xml`
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>jest-junit</summary>
|
<summary>jest-junit</summary>
|
||||||
|
|
||||||
|
|||||||
30
__tests__/__outputs__/phpunit-junit-basic-results.md
Normal file
30
__tests__/__outputs__/phpunit-junit-basic-results.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|

|
||||||
|
|Report|Passed|Failed|Skipped|Time|
|
||||||
|
|:---|---:|---:|---:|---:|
|
||||||
|
|[fixtures/external/phpunit/junit-basic.xml](#user-content-r0)|8 ✅|1 ❌||16s|
|
||||||
|
## ❌ <a id="user-content-r0" href="#user-content-r0">fixtures/external/phpunit/junit-basic.xml</a>
|
||||||
|
**9** tests were completed in **16s** with **8** passed, **1** failed and **0** skipped.
|
||||||
|
|Test suite|Passed|Failed|Skipped|Time|
|
||||||
|
|:---|---:|---:|---:|---:|
|
||||||
|
|[Tests.Authentication](#user-content-r0s0)|2 ✅|1 ❌||9s|
|
||||||
|
|[Tests.Authentication.Login](#user-content-r0s1)|3 ✅|||4s|
|
||||||
|
|[Tests.Registration](#user-content-r0s2)|3 ✅|||7s|
|
||||||
|
### ❌ <a id="user-content-r0s0" href="#user-content-r0s0">Tests.Authentication</a>
|
||||||
|
```
|
||||||
|
✅ testCase7
|
||||||
|
✅ testCase8
|
||||||
|
❌ testCase9
|
||||||
|
AssertionError: Assertion error message
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s1" href="#user-content-r0s1">Tests.Authentication.Login</a>
|
||||||
|
```
|
||||||
|
✅ testCase4
|
||||||
|
✅ testCase5
|
||||||
|
✅ testCase6
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s2" href="#user-content-r0s2">Tests.Registration</a>
|
||||||
|
```
|
||||||
|
✅ testCase1
|
||||||
|
✅ testCase2
|
||||||
|
✅ testCase3
|
||||||
|
```
|
||||||
88
__tests__/__outputs__/phpunit-phpcheckstyle-results.md
Normal file
88
__tests__/__outputs__/phpunit-phpcheckstyle-results.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|

|
||||||
|
|Report|Passed|Failed|Skipped|Time|
|
||||||
|
|:---|---:|---:|---:|---:|
|
||||||
|
|[fixtures/external/phpunit/phpcheckstyle-phpunit.xml](#user-content-r0)|28 ✅|2 ❌||41ms|
|
||||||
|
## ❌ <a id="user-content-r0" href="#user-content-r0">fixtures/external/phpunit/phpcheckstyle-phpunit.xml</a>
|
||||||
|
**30** tests were completed in **41ms** with **28** passed, **2** failed and **0** skipped.
|
||||||
|
|Test suite|Passed|Failed|Skipped|Time|
|
||||||
|
|:---|---:|---:|---:|---:|
|
||||||
|
|[CommentsTest](#user-content-r0s0)|3 ✅|||7ms|
|
||||||
|
|[DeprecationTest](#user-content-r0s1)|1 ✅|||1ms|
|
||||||
|
|[GoodTest](#user-content-r0s2)|4 ✅|||5ms|
|
||||||
|
|[IndentationTest](#user-content-r0s3)|8 ✅|||8ms|
|
||||||
|
|[MetricsTest](#user-content-r0s4)|1 ✅|||4ms|
|
||||||
|
|[NamingTest](#user-content-r0s5)|2 ✅|||3ms|
|
||||||
|
|[OptimizationTest](#user-content-r0s6)|1 ✅|||1ms|
|
||||||
|
|[OtherTest](#user-content-r0s7)|2 ✅|2 ❌||7ms|
|
||||||
|
|[PHPTagsTest](#user-content-r0s8)|2 ✅|||1ms|
|
||||||
|
|[ProhibitedTest](#user-content-r0s9)|1 ✅|||1ms|
|
||||||
|
|[StrictCompareTest](#user-content-r0s10)|1 ✅|||2ms|
|
||||||
|
|[UnusedTest](#user-content-r0s11)|2 ✅|||2ms|
|
||||||
|
### ✅ <a id="user-content-r0s0" href="#user-content-r0s0">CommentsTest</a>
|
||||||
|
```
|
||||||
|
✅ testGoodDoc
|
||||||
|
✅ testComments
|
||||||
|
✅ testTODOs
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s1" href="#user-content-r0s1">DeprecationTest</a>
|
||||||
|
```
|
||||||
|
✅ testDeprecations
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s2" href="#user-content-r0s2">GoodTest</a>
|
||||||
|
```
|
||||||
|
✅ testGood
|
||||||
|
✅ testDoWhile
|
||||||
|
✅ testAnonymousFunction
|
||||||
|
✅ testException
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s3" href="#user-content-r0s3">IndentationTest</a>
|
||||||
|
```
|
||||||
|
✅ testTabIndentation
|
||||||
|
✅ testSpaceIndentation
|
||||||
|
✅ testSpaceIndentationArray
|
||||||
|
✅ testGoodSpaceIndentationArray
|
||||||
|
✅ testGoodIndentationNewLine
|
||||||
|
✅ testGoodIndentationSpaces
|
||||||
|
✅ testBadSpaces
|
||||||
|
✅ testBadSpaceAfterControl
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s4" href="#user-content-r0s4">MetricsTest</a>
|
||||||
|
```
|
||||||
|
✅ testMetrics
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s5" href="#user-content-r0s5">NamingTest</a>
|
||||||
|
```
|
||||||
|
✅ testNaming
|
||||||
|
✅ testFunctionNaming
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s6" href="#user-content-r0s6">OptimizationTest</a>
|
||||||
|
```
|
||||||
|
✅ testTextAfterClosingTag
|
||||||
|
```
|
||||||
|
### ❌ <a id="user-content-r0s7" href="#user-content-r0s7">OtherTest</a>
|
||||||
|
```
|
||||||
|
❌ testOther
|
||||||
|
PHPUnit\Framework\ExpectationFailedException
|
||||||
|
❌ testException
|
||||||
|
PHPUnit\Framework\ExpectationFailedException
|
||||||
|
✅ testEmpty
|
||||||
|
✅ testSwitchCaseNeedBreak
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s8" href="#user-content-r0s8">PHPTagsTest</a>
|
||||||
|
```
|
||||||
|
✅ testTextAfterClosingTag
|
||||||
|
✅ testClosingTagNotNeeded
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s9" href="#user-content-r0s9">ProhibitedTest</a>
|
||||||
|
```
|
||||||
|
✅ testProhibited
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s10" href="#user-content-r0s10">StrictCompareTest</a>
|
||||||
|
```
|
||||||
|
✅ testStrictCompare
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s11" href="#user-content-r0s11">UnusedTest</a>
|
||||||
|
```
|
||||||
|
✅ testGoodUnused
|
||||||
|
✅ testBadUnused
|
||||||
|
```
|
||||||
41
__tests__/__outputs__/phpunit-test-results.md
Normal file
41
__tests__/__outputs__/phpunit-test-results.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|

|
||||||
|
|Report|Passed|Failed|Skipped|Time|
|
||||||
|
|:---|---:|---:|---:|---:|
|
||||||
|
|[fixtures/phpunit/phpunit.xml](#user-content-r0)|10 ✅|2 ❌||148ms|
|
||||||
|
## ❌ <a id="user-content-r0" href="#user-content-r0">fixtures/phpunit/phpunit.xml</a>
|
||||||
|
**12** tests were completed in **148ms** with **10** passed, **2** failed and **0** skipped.
|
||||||
|
|Test suite|Passed|Failed|Skipped|Time|
|
||||||
|
|:---|---:|---:|---:|---:|
|
||||||
|
|[CLI Arguments](#user-content-r0s0)||2 ❌||140ms|
|
||||||
|
|[PHPUnit\Event\CollectingDispatcherTest](#user-content-r0s1)|2 ✅|||4ms|
|
||||||
|
|[PHPUnit\Event\DeferringDispatcherTest](#user-content-r0s2)|4 ✅|||3ms|
|
||||||
|
|[PHPUnit\Event\DirectDispatcherTest](#user-content-r0s3)|4 ✅|||1ms|
|
||||||
|
### ❌ <a id="user-content-r0s0" href="#user-content-r0s0">CLI Arguments</a>
|
||||||
|
```
|
||||||
|
❌ targeting-traits-with-coversclass-attribute-is-deprecated.phpt
|
||||||
|
PHPUnit\Framework\PhptAssertionFailedError
|
||||||
|
❌ targeting-traits-with-usesclass-attribute-is-deprecated.phpt
|
||||||
|
PHPUnit\Framework\PhptAssertionFailedError
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s1" href="#user-content-r0s1">PHPUnit\Event\CollectingDispatcherTest</a>
|
||||||
|
```
|
||||||
|
PHPUnit.Event.CollectingDispatcherTest
|
||||||
|
✅ testHasNoCollectedEventsWhenFlushedImmediatelyAfterCreation
|
||||||
|
✅ testCollectsDispatchedEventsUntilFlushed
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s2" href="#user-content-r0s2">PHPUnit\Event\DeferringDispatcherTest</a>
|
||||||
|
```
|
||||||
|
PHPUnit.Event.DeferringDispatcherTest
|
||||||
|
✅ testCollectsEventsUntilFlush
|
||||||
|
✅ testFlushesCollectedEvents
|
||||||
|
✅ testSubscriberCanBeRegistered
|
||||||
|
✅ testTracerCanBeRegistered
|
||||||
|
```
|
||||||
|
### ✅ <a id="user-content-r0s3" href="#user-content-r0s3">PHPUnit\Event\DirectDispatcherTest</a>
|
||||||
|
```
|
||||||
|
PHPUnit.Event.DirectDispatcherTest
|
||||||
|
✅ testDispatchesEventToKnownSubscribers
|
||||||
|
✅ testDispatchesEventToTracers
|
||||||
|
✅ testRegisterRejectsUnknownSubscriber
|
||||||
|
✅ testDispatchRejectsUnknownEventType
|
||||||
|
```
|
||||||
628
__tests__/__snapshots__/phpunit-junit.test.ts.snap
Normal file
628
__tests__/__snapshots__/phpunit-junit.test.ts.snap
Normal file
@@ -0,0 +1,628 @@
|
|||||||
|
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||||
|
|
||||||
|
exports[`phpunit-junit tests report from junit-basic.xml matches snapshot 1`] = `
|
||||||
|
TestRunResult {
|
||||||
|
"path": "fixtures/external/phpunit/junit-basic.xml",
|
||||||
|
"suites": [
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCase1",
|
||||||
|
"result": "success",
|
||||||
|
"time": 2113.871,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCase2",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1051,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCase3",
|
||||||
|
"result": "success",
|
||||||
|
"time": 3441,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "Tests.Registration",
|
||||||
|
"totalTime": 6605.870999999999,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCase4",
|
||||||
|
"result": "success",
|
||||||
|
"time": 2244,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCase5",
|
||||||
|
"result": "success",
|
||||||
|
"time": 781,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCase6",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1331,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "Tests.Authentication.Login",
|
||||||
|
"totalTime": 4356,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCase7",
|
||||||
|
"result": "success",
|
||||||
|
"time": 2508,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCase8",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1230.8159999999998,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": {
|
||||||
|
"details": "",
|
||||||
|
"line": undefined,
|
||||||
|
"message": "AssertionError: Assertion error message",
|
||||||
|
"path": undefined,
|
||||||
|
},
|
||||||
|
"name": "testCase9",
|
||||||
|
"result": "failed",
|
||||||
|
"time": 982,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "Tests.Authentication",
|
||||||
|
"totalTime": 9076.816,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"totalTime": 15682.687,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`phpunit-junit tests report from phpcheckstyle-phpunit.xml matches snapshot 1`] = `
|
||||||
|
TestRunResult {
|
||||||
|
"path": "fixtures/external/phpunit/phpcheckstyle-phpunit.xml",
|
||||||
|
"suites": [
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testGoodDoc",
|
||||||
|
"result": "success",
|
||||||
|
"time": 5.093,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testComments",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.921,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testTODOs",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.6880000000000001,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "CommentsTest",
|
||||||
|
"totalTime": 6.702,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testDeprecations",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.9740000000000001,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "DeprecationTest",
|
||||||
|
"totalTime": 0.9740000000000001,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testGood",
|
||||||
|
"result": "success",
|
||||||
|
"time": 2.6470000000000002,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testDoWhile",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1.0219999999999998,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testAnonymousFunction",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.8,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testException",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.888,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "GoodTest",
|
||||||
|
"totalTime": 5.357,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testTabIndentation",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.857,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testSpaceIndentation",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.929,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testSpaceIndentationArray",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.975,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testGoodSpaceIndentationArray",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1.212,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testGoodIndentationNewLine",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.859,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testGoodIndentationSpaces",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.78,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testBadSpaces",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1.1199999999999999,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testBadSpaceAfterControl",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.9219999999999999,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "IndentationTest",
|
||||||
|
"totalTime": 7.654,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testMetrics",
|
||||||
|
"result": "success",
|
||||||
|
"time": 4.146999999999999,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "MetricsTest",
|
||||||
|
"totalTime": 4.146999999999999,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testNaming",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1.426,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testFunctionNaming",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1.271,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "NamingTest",
|
||||||
|
"totalTime": 2.697,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testTextAfterClosingTag",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.9940000000000001,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "OptimizationTest",
|
||||||
|
"totalTime": 0.9940000000000001,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": {
|
||||||
|
"details": "OtherTest::testOther
|
||||||
|
We expect 20 warnings
|
||||||
|
Failed asserting that 19 matches expected 20.
|
||||||
|
|
||||||
|
/workspace/phpcheckstyle/test/OtherTest.php:24",
|
||||||
|
"line": 12,
|
||||||
|
"message": "PHPUnit\\Framework\\ExpectationFailedException",
|
||||||
|
"path": undefined,
|
||||||
|
},
|
||||||
|
"name": "testOther",
|
||||||
|
"result": "failed",
|
||||||
|
"time": 5.2509999999999994,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": {
|
||||||
|
"details": "OtherTest::testException
|
||||||
|
We expect 1 error
|
||||||
|
Failed asserting that 0 matches expected 1.
|
||||||
|
|
||||||
|
/workspace/phpcheckstyle/test/OtherTest.php:40",
|
||||||
|
"line": 31,
|
||||||
|
"message": "PHPUnit\\Framework\\ExpectationFailedException",
|
||||||
|
"path": undefined,
|
||||||
|
},
|
||||||
|
"name": "testException",
|
||||||
|
"result": "failed",
|
||||||
|
"time": 0.751,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testEmpty",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.42700000000000005,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testSwitchCaseNeedBreak",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.901,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "OtherTest",
|
||||||
|
"totalTime": 7.329,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testTextAfterClosingTag",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.641,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testClosingTagNotNeeded",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.631,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "PHPTagsTest",
|
||||||
|
"totalTime": 1.272,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testProhibited",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.9380000000000001,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "ProhibitedTest",
|
||||||
|
"totalTime": 0.9380000000000001,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testStrictCompare",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1.578,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "StrictCompareTest",
|
||||||
|
"totalTime": 1.578,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testGoodUnused",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.94,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testBadUnused",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.895,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "UnusedTest",
|
||||||
|
"totalTime": 1.835,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"totalTime": undefined,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`phpunit-junit tests report from phpunit test results matches snapshot 1`] = `
|
||||||
|
TestRunResult {
|
||||||
|
"path": "fixtures/phpunit/phpunit.xml",
|
||||||
|
"suites": [
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "PHPUnit.Event.CollectingDispatcherTest",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testHasNoCollectedEventsWhenFlushedImmediatelyAfterCreation",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1.441,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCollectsDispatchedEventsUntilFlushed",
|
||||||
|
"result": "success",
|
||||||
|
"time": 2.815,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "PHPUnit\\Event\\CollectingDispatcherTest",
|
||||||
|
"totalTime": 4.256,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "PHPUnit.Event.DeferringDispatcherTest",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testCollectsEventsUntilFlush",
|
||||||
|
"result": "success",
|
||||||
|
"time": 1.6720000000000002,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testFlushesCollectedEvents",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.661,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testSubscriberCanBeRegistered",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.33399999999999996,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testTracerCanBeRegistered",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.262,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "PHPUnit\\Event\\DeferringDispatcherTest",
|
||||||
|
"totalTime": 2.928,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "PHPUnit.Event.DirectDispatcherTest",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testDispatchesEventToKnownSubscribers",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.17,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testDispatchesEventToTracers",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.248,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testRegisterRejectsUnknownSubscriber",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.257,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": undefined,
|
||||||
|
"name": "testDispatchRejectsUnknownEventType",
|
||||||
|
"result": "success",
|
||||||
|
"time": 0.11900000000000001,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "PHPUnit\\Event\\DirectDispatcherTest",
|
||||||
|
"totalTime": 0.794,
|
||||||
|
},
|
||||||
|
TestSuiteResult {
|
||||||
|
"groups": [
|
||||||
|
TestGroupResult {
|
||||||
|
"name": "",
|
||||||
|
"tests": [
|
||||||
|
TestCaseResult {
|
||||||
|
"error": {
|
||||||
|
"details": "targeting-traits-with-coversclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
|
||||||
|
--- Expected
|
||||||
|
+++ Actual
|
||||||
|
@@ @@
|
||||||
|
PHPUnit Started (PHPUnit 11.2-g0c2333363 using PHP 8.2.17 (cli) on Linux)
|
||||||
|
Test Runner Configured
|
||||||
|
Test Suite Loaded (1 test)
|
||||||
|
+Test Runner Triggered Warning (No code coverage driver available)
|
||||||
|
Event Facade Sealed
|
||||||
|
Test Runner Started
|
||||||
|
Test Suite Sorted
|
||||||
|
@@ @@
|
||||||
|
Test Preparation Started (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest::testSomething)
|
||||||
|
Test Prepared (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest::testSomething)
|
||||||
|
Test Passed (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest::testSomething)
|
||||||
|
-Test Runner Triggered Deprecation (Targeting a trait such as PHPUnit\\TestFixture\\CoveredTrait with #[CoversClass] is deprecated, please refactor your test to use #[CoversTrait] instead.)
|
||||||
|
Test Finished (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest::testSomething)
|
||||||
|
Test Suite Finished (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithCoversClassTest, 1 test)
|
||||||
|
Test Runner Execution Finished
|
||||||
|
Test Runner Finished
|
||||||
|
-PHPUnit Finished (Shell Exit Code: 0)
|
||||||
|
+PHPUnit Finished (Shell Exit Code: 1)
|
||||||
|
|
||||||
|
/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-coversclass-attribute-is-deprecated.phpt:28
|
||||||
|
/home/matteo/OSS/phpunit/src/Framework/TestSuite.php:369
|
||||||
|
/home/matteo/OSS/phpunit/src/TextUI/TestRunner.php:62
|
||||||
|
/home/matteo/OSS/phpunit/src/TextUI/Application.php:200",
|
||||||
|
"line": undefined,
|
||||||
|
"message": "PHPUnit\\Framework\\PhptAssertionFailedError",
|
||||||
|
"path": undefined,
|
||||||
|
},
|
||||||
|
"name": "targeting-traits-with-coversclass-attribute-is-deprecated.phpt",
|
||||||
|
"result": "failed",
|
||||||
|
"time": 68.151,
|
||||||
|
},
|
||||||
|
TestCaseResult {
|
||||||
|
"error": {
|
||||||
|
"details": "targeting-traits-with-usesclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
|
||||||
|
--- Expected
|
||||||
|
+++ Actual
|
||||||
|
@@ @@
|
||||||
|
PHPUnit Started (PHPUnit 11.2-g0c2333363 using PHP 8.2.17 (cli) on Linux)
|
||||||
|
Test Runner Configured
|
||||||
|
Test Suite Loaded (1 test)
|
||||||
|
+Test Runner Triggered Warning (No code coverage driver available)
|
||||||
|
Event Facade Sealed
|
||||||
|
Test Runner Started
|
||||||
|
Test Suite Sorted
|
||||||
|
@@ @@
|
||||||
|
Test Preparation Started (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest::testSomething)
|
||||||
|
Test Prepared (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest::testSomething)
|
||||||
|
Test Passed (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest::testSomething)
|
||||||
|
-Test Runner Triggered Deprecation (Targeting a trait such as PHPUnit\\TestFixture\\CoveredTrait with #[UsesClass] is deprecated, please refactor your test to use #[UsesTrait] instead.)
|
||||||
|
Test Finished (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest::testSomething)
|
||||||
|
Test Suite Finished (PHPUnit\\DeprecatedAnnotationsTestFixture\\TraitTargetedWithUsesClassTest, 1 test)
|
||||||
|
Test Runner Execution Finished
|
||||||
|
Test Runner Finished
|
||||||
|
-PHPUnit Finished (Shell Exit Code: 0)
|
||||||
|
+PHPUnit Finished (Shell Exit Code: 1)
|
||||||
|
|
||||||
|
/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-usesclass-attribute-is-deprecated.phpt:28
|
||||||
|
/home/matteo/OSS/phpunit/src/Framework/TestSuite.php:369
|
||||||
|
/home/matteo/OSS/phpunit/src/TextUI/TestRunner.php:62
|
||||||
|
/home/matteo/OSS/phpunit/src/TextUI/Application.php:200",
|
||||||
|
"line": undefined,
|
||||||
|
"message": "PHPUnit\\Framework\\PhptAssertionFailedError",
|
||||||
|
"path": undefined,
|
||||||
|
},
|
||||||
|
"name": "targeting-traits-with-usesclass-attribute-is-deprecated.phpt",
|
||||||
|
"result": "failed",
|
||||||
|
"time": 64.268,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "CLI Arguments",
|
||||||
|
"totalTime": 140.397,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"totalTime": undefined,
|
||||||
|
}
|
||||||
|
`;
|
||||||
2
__tests__/fixtures/empty/phpunit-empty.xml
Normal file
2
__tests__/fixtures/empty/phpunit-empty.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<testsuites/>
|
||||||
28
__tests__/fixtures/external/phpunit/junit-basic.xml
vendored
Normal file
28
__tests__/fixtures/external/phpunit/junit-basic.xml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
This is a basic JUnit-style XML example to highlight the basis structure.
|
||||||
|
|
||||||
|
Example by Testmo. Copyright 2023 Testmo GmbH. All rights reserved.
|
||||||
|
Testmo test management software - https://www.testmo.com/
|
||||||
|
-->
|
||||||
|
<testsuites time="15.682687">
|
||||||
|
<testsuite name="Tests.Registration" time="6.605871">
|
||||||
|
<testcase name="testCase1" classname="Tests.Registration" time="2.113871" />
|
||||||
|
<testcase name="testCase2" classname="Tests.Registration" time="1.051" />
|
||||||
|
<testcase name="testCase3" classname="Tests.Registration" time="3.441" />
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="Tests.Authentication" time="9.076816">
|
||||||
|
<testsuite name="Tests.Authentication.Login" time="4.356">
|
||||||
|
<testcase name="testCase4" classname="Tests.Authentication.Login" time="2.244" />
|
||||||
|
<testcase name="testCase5" classname="Tests.Authentication.Login" time="0.781" />
|
||||||
|
<testcase name="testCase6" classname="Tests.Authentication.Login" time="1.331" />
|
||||||
|
</testsuite>
|
||||||
|
<testcase name="testCase7" classname="Tests.Authentication" time="2.508" />
|
||||||
|
<testcase name="testCase8" classname="Tests.Authentication" time="1.230816" />
|
||||||
|
<testcase name="testCase9" classname="Tests.Authentication" time="0.982">
|
||||||
|
<failure message="Assertion error message" type="AssertionError">
|
||||||
|
<!-- Call stack printed here -->
|
||||||
|
</failure>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
212
__tests__/fixtures/external/phpunit/phpcheckstyle-phpunit.xml
vendored
Normal file
212
__tests__/fixtures/external/phpunit/phpcheckstyle-phpunit.xml
vendored
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="/workspace/phpcheckstyle/phpunit.xml" tests="30" assertions="117" errors="0" failures="2" skipped="0" time="0.041478">
|
||||||
|
<testsuite name="PHPUnitTestSuite" tests="30" assertions="117" errors="0" failures="2" skipped="0" time="0.041478">
|
||||||
|
<testsuite name="CommentsTest" file="/workspace/phpcheckstyle/test/CommentsTest.php" tests="3" assertions="12" errors="0" failures="0" skipped="0" time="0.006702">
|
||||||
|
<testcase name="testGoodDoc" file="/workspace/phpcheckstyle/test/CommentsTest.php" line="12" class="CommentsTest" classname="CommentsTest" assertions="4" time="0.005093"/>
|
||||||
|
<testcase name="testComments" file="/workspace/phpcheckstyle/test/CommentsTest.php" line="30" class="CommentsTest" classname="CommentsTest" assertions="4" time="0.000921">
|
||||||
|
<system-out>File "./test/sample/bad_comments.php" warning, line 4 - Avoid Shell/Perl like comments.
|
||||||
|
File "./test/sample/bad_comments.php" warning, line 6 - The class Comments must have a docblock comment.
|
||||||
|
File "./test/sample/bad_comments.php" warning, line 10 - The function testComment must have a docblock comment.
|
||||||
|
File "./test/sample/bad_comments.php" warning, line 18 - The function testComment returns a value and must include @returns in its docblock.
|
||||||
|
File "./test/sample/bad_comments.php" warning, line 18 - The function testComment parameters must match those in its docblock @param.
|
||||||
|
File "./test/sample/bad_comments.php" warning, line 18 - The function testComment throws an exception and must include @throws in its docblock.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testTODOs" file="/workspace/phpcheckstyle/test/CommentsTest.php" line="48" class="CommentsTest" classname="CommentsTest" assertions="4" time="0.000688">
|
||||||
|
<system-out>File "./test/sample/todo.php" warning, line 3 - TODO: The todo message.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="DeprecationTest" file="/workspace/phpcheckstyle/test/DeprecationTest.php" tests="1" assertions="4" errors="0" failures="0" skipped="0" time="0.000974">
|
||||||
|
<testcase name="testDeprecations" file="/workspace/phpcheckstyle/test/DeprecationTest.php" line="12" class="DeprecationTest" classname="DeprecationTest" assertions="4" time="0.000974">
|
||||||
|
<system-out>File "./test/sample/bad_deprecation.php" warning, line 17 - split is deprecated since PHP 5.3. explode($pattern, $string) or preg_split('@'.$pattern.'@', $string) must be used instead.
|
||||||
|
File "./test/sample/bad_deprecation.php" warning, line 19 - ereg is deprecated since PHP 5.3. preg_match('@'.$pattern.'@', $string) must be used instead.
|
||||||
|
File "./test/sample/bad_deprecation.php" warning, line 21 - session_register is deprecated since PHP 5.3. $_SESSION must be used instead.
|
||||||
|
File "./test/sample/bad_deprecation.php" warning, line 23 - mysql_db_query is deprecated since PHP 5.3. mysql_select_db and mysql_query must be used instead.
|
||||||
|
File "./test/sample/bad_deprecation.php" warning, line 25 - $HTTP_GET_VARS is deprecated since PHP 5.3. $_GET must be used instead.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="GoodTest" file="/workspace/phpcheckstyle/test/GoodTest.php" tests="4" assertions="16" errors="0" failures="0" skipped="0" time="0.005357">
|
||||||
|
<testcase name="testGood" file="/workspace/phpcheckstyle/test/GoodTest.php" line="12" class="GoodTest" classname="GoodTest" assertions="4" time="0.002647"/>
|
||||||
|
<testcase name="testDoWhile" file="/workspace/phpcheckstyle/test/GoodTest.php" line="32" class="GoodTest" classname="GoodTest" assertions="4" time="0.001022"/>
|
||||||
|
<testcase name="testAnonymousFunction" file="/workspace/phpcheckstyle/test/GoodTest.php" line="50" class="GoodTest" classname="GoodTest" assertions="4" time="0.000800"/>
|
||||||
|
<testcase name="testException" file="/workspace/phpcheckstyle/test/GoodTest.php" line="68" class="GoodTest" classname="GoodTest" assertions="4" time="0.000888"/>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="IndentationTest" file="/workspace/phpcheckstyle/test/IndentationTest.php" tests="8" assertions="32" errors="0" failures="0" skipped="0" time="0.007654">
|
||||||
|
<testcase name="testTabIndentation" file="/workspace/phpcheckstyle/test/IndentationTest.php" line="12" class="IndentationTest" classname="IndentationTest" assertions="4" time="0.000857">
|
||||||
|
<system-out>File "./test/sample/bad_indentation.php" warning, line 8 - Whitespace indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 15 - Whitespace indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 17 - Whitespace indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 18 - Whitespace indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 19 - Whitespace indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 20 - Whitespace indentation must not be used.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testSpaceIndentation" file="/workspace/phpcheckstyle/test/IndentationTest.php" line="30" class="IndentationTest" classname="IndentationTest" assertions="4" time="0.000929">
|
||||||
|
<system-out>File "./test/sample/bad_indentation.php" warning, line 10 - Tab indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 10 - The indentation level must be 4 but was 1.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 13 - Tab indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 13 - The indentation level must be 4 but was 1.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 15 - The indentation level must be 8 but was 4.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 16 - Tab indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 16 - The indentation level must be 8 but was 1.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 17 - The indentation level must be 8 but was 3.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 18 - The indentation level must be 8 but was 5.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 19 - The indentation level must be 8 but was 6.
|
||||||
|
File "./test/sample/bad_indentation.php" warning, line 20 - The indentation level must be 4 but was 1.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testSpaceIndentationArray" file="/workspace/phpcheckstyle/test/IndentationTest.php" line="51" class="IndentationTest" classname="IndentationTest" assertions="4" time="0.000975">
|
||||||
|
<system-out>File "./test/sample/bad_indentation_array.php" warning, line 10 - Tab indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 10 - The indentation level must be 4 but was 1.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 13 - Tab indentation must not be used.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 13 - The indentation level must be 4 but was 1.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 16 - The indentation level must be 12 but was 8.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 24 - The indentation level must be 12 but was 8.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 29 - The indentation level must be 8 but was 12.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 15 - Undeclared or unused variable: $aVar.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 19 - Undeclared or unused variable: $bVar.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 23 - Undeclared or unused variable: $cVar.
|
||||||
|
File "./test/sample/bad_indentation_array.php" warning, line 27 - Undeclared or unused variable: $dVar.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testGoodSpaceIndentationArray" file="/workspace/phpcheckstyle/test/IndentationTest.php" line="72" class="IndentationTest" classname="IndentationTest" assertions="4" time="0.001212"/>
|
||||||
|
<testcase name="testGoodIndentationNewLine" file="/workspace/phpcheckstyle/test/IndentationTest.php" line="93" class="IndentationTest" classname="IndentationTest" assertions="4" time="0.000859"/>
|
||||||
|
<testcase name="testGoodIndentationSpaces" file="/workspace/phpcheckstyle/test/IndentationTest.php" line="116" class="IndentationTest" classname="IndentationTest" assertions="4" time="0.000780"/>
|
||||||
|
<testcase name="testBadSpaces" file="/workspace/phpcheckstyle/test/IndentationTest.php" line="137" class="IndentationTest" classname="IndentationTest" assertions="4" time="0.001120">
|
||||||
|
<system-out>File "./test/sample/bad_spaces.php" warning, line 17 - Whitespace must follow ,.
|
||||||
|
File "./test/sample/bad_spaces.php" warning, line 17 - Whitespace must precede {.
|
||||||
|
File "./test/sample/bad_spaces.php" warning, line 19 - Whitespace must follow if.
|
||||||
|
File "./test/sample/bad_spaces.php" warning, line 23 - Whitespace must precede =.
|
||||||
|
File "./test/sample/bad_spaces.php" warning, line 23 - Whitespace must follow =.
|
||||||
|
File "./test/sample/bad_spaces.php" warning, line 23 - Whitespace must precede +.
|
||||||
|
File "./test/sample/bad_spaces.php" warning, line 23 - Whitespace must follow +.
|
||||||
|
File "./test/sample/bad_spaces.php" info, line 25 - Whitespace must not precede ,.
|
||||||
|
File "./test/sample/bad_spaces.php" info, line 26 - Whitespace must not follow !.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testBadSpaceAfterControl" file="/workspace/phpcheckstyle/test/IndentationTest.php" line="155" class="IndentationTest" classname="IndentationTest" assertions="4" time="0.000922">
|
||||||
|
<system-out>File "./test/sample/bad_space_after_control.php" warning, line 19 - Whitespace must not follow if.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="MetricsTest" file="/workspace/phpcheckstyle/test/MetricsTest.php" tests="1" assertions="4" errors="0" failures="0" skipped="0" time="0.004147">
|
||||||
|
<testcase name="testMetrics" file="/workspace/phpcheckstyle/test/MetricsTest.php" line="12" class="MetricsTest" classname="MetricsTest" assertions="4" time="0.004147">
|
||||||
|
<system-out>File "./test/sample/bad_metrics.php" warning, line 21 - The function testMetrics's number of parameters (6) must not exceed 4.
|
||||||
|
File "./test/sample/bad_metrics.php" info, line 55 - Line is too long. [233/160]
|
||||||
|
File "./test/sample/bad_metrics.php" warning, line 21 - The Cyclomatic Complexity of function testMetrics is too high. [15/10]
|
||||||
|
File "./test/sample/bad_metrics.php" warning, line 244 - The testMetrics function body length is too long. [223/200]
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="NamingTest" file="/workspace/phpcheckstyle/test/NamingTest.php" tests="2" assertions="8" errors="0" failures="0" skipped="0" time="0.002697">
|
||||||
|
<testcase name="testNaming" file="/workspace/phpcheckstyle/test/NamingTest.php" line="12" class="NamingTest" classname="NamingTest" assertions="4" time="0.001426">
|
||||||
|
<system-out>File "./test/sample/_bad_naming.php" error, line 11 - Constant _badly_named_constant name should follow the pattern /^[A-Z][A-Z0-9_]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" error, line 13 - Constant bad_CONST name should follow the pattern /^[A-Z][A-Z0-9_]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" warning, line 17 - Top level variable $XXX name should follow the pattern /^[a-z_][a-zA-Z0-9]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" warning, line 20 - Variable x name length is too short.
|
||||||
|
File "./test/sample/_bad_naming.php" error, line 28 - Class badlynamedclass name should follow the pattern /^[A-Z][a-zA-Z0-9_]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" warning, line 32 - Member variable $YYY name should follow the pattern /^[a-z_][a-zA-Z0-9]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" warning, line 37 - The constructor name must be __construct().
|
||||||
|
File "./test/sample/_bad_naming.php" error, line 44 - Function Badlynamedfunction name should follow the pattern /^[a-z][a-zA-Z0-9]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" warning, line 47 - Local variable $ZZZ name should follow the pattern /^[a-z_][a-zA-Z0-9]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" error, line 54 - Protected function Badlynamedfunction2 name should follow the pattern /^[a-z][a-zA-Z0-9]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" error, line 61 - Private function badlynamedfunction3 name should follow the pattern /^_[a-z][a-zA-Z0-9]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" error, line 70 - Interface _badlynamedinterface name should follow the pattern /^[A-Z][a-zA-Z0-9_]*$/.
|
||||||
|
File "./test/sample/_bad_naming.php" error, line 75 - File _bad_naming.php name should follow the pattern /^[a-zA-Z][a-zA-Z0-9._]*$/.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testFunctionNaming" file="/workspace/phpcheckstyle/test/NamingTest.php" line="32" class="NamingTest" classname="NamingTest" assertions="4" time="0.001271"/>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="OptimizationTest" file="/workspace/phpcheckstyle/test/OptimizationTest.php" tests="1" assertions="4" errors="0" failures="0" skipped="0" time="0.000994">
|
||||||
|
<testcase name="testTextAfterClosingTag" file="/workspace/phpcheckstyle/test/OptimizationTest.php" line="12" class="OptimizationTest" classname="OptimizationTest" assertions="4" time="0.000994">
|
||||||
|
<system-out>File "./test/sample/bad_optimisation.php" warning, line 18 - count function must not be used inside a loop.
|
||||||
|
File "./test/sample/bad_optimisation.php" warning, line 23 - count function must not be used inside a loop.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="OtherTest" file="/workspace/phpcheckstyle/test/OtherTest.php" tests="4" assertions="13" errors="0" failures="2" skipped="0" time="0.007329">
|
||||||
|
<testcase name="testOther" file="/workspace/phpcheckstyle/test/OtherTest.php" line="12" class="OtherTest" classname="OtherTest" assertions="4" time="0.005251">
|
||||||
|
<failure type="PHPUnit\Framework\ExpectationFailedException">OtherTest::testOther
|
||||||
|
We expect 20 warnings
|
||||||
|
Failed asserting that 19 matches expected 20.
|
||||||
|
|
||||||
|
/workspace/phpcheckstyle/test/OtherTest.php:24</failure>
|
||||||
|
<system-out>File "./test/sample/bad_other.php" warning, line 17 - All arguments with default values must be at the end of the block or statement.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 21 - Errors must not be silenced when calling a function.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 23 - Prefer single-quoted strings when you don't need string interpolation.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 23 - Encapsed variables must not be used inside a string.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 23 - Encapsed variables must not be used inside a string.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 23 - Prefer single-quoted strings when you don't need string interpolation.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 37 - TODO: Show todos
|
||||||
|
File "./test/sample/bad_other.php" warning, line 40 - Avoid empty statements (;;).
|
||||||
|
File "./test/sample/bad_other.php" warning, line 42 - Boolean operators (&&) must be used instead of logical operators (AND).
|
||||||
|
File "./test/sample/bad_other.php" warning, line 42 - Empty if block.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 48 - Heredoc syntax must not be used.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 52 - The statement if must contain its code within a {} block.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 54 - Consider using a strict comparison operator instead of ==.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 54 - The statement while must contain its code within a {} block.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 66 - The switch statement must have a default case.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 79 - The default case of a switch statement must be located after all other cases.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 93 - Unary operators (++ or --) must not be used inside a control statement
|
||||||
|
File "./test/sample/bad_other.php" warning, line 95 - Assigments (=) must not be used inside a control statement.
|
||||||
|
File "./test/sample/bad_other.php" warning, line 106 - File ./test/sample/bad_other.php must not have multiple class declarations.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testException" file="/workspace/phpcheckstyle/test/OtherTest.php" line="31" class="OtherTest" classname="OtherTest" assertions="1" time="0.000751">
|
||||||
|
<failure type="PHPUnit\Framework\ExpectationFailedException">OtherTest::testException
|
||||||
|
We expect 1 error
|
||||||
|
Failed asserting that 0 matches expected 1.
|
||||||
|
|
||||||
|
/workspace/phpcheckstyle/test/OtherTest.php:40</failure>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testEmpty" file="/workspace/phpcheckstyle/test/OtherTest.php" line="50" class="OtherTest" classname="OtherTest" assertions="4" time="0.000427">
|
||||||
|
<system-out>File "./test/sample/empty.php" warning, line 1 - The file ./test/sample/empty.php is empty.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testSwitchCaseNeedBreak" file="/workspace/phpcheckstyle/test/OtherTest.php" line="69" class="OtherTest" classname="OtherTest" assertions="4" time="0.000901">
|
||||||
|
<system-out>File "./test/sample/switch_multi_case.php" warning, line 10 - The case statement must contain a break.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="PHPTagsTest" file="/workspace/phpcheckstyle/test/PHPTagsTest.php" tests="2" assertions="8" errors="0" failures="0" skipped="0" time="0.001272">
|
||||||
|
<testcase name="testTextAfterClosingTag" file="/workspace/phpcheckstyle/test/PHPTagsTest.php" line="12" class="PHPTagsTest" classname="PHPTagsTest" assertions="4" time="0.000641">
|
||||||
|
<system-out>File "./test/sample/bad_php_tags_text_after_end.php" warning, line 9 - A PHP close tag must not be included at the end of the file.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testClosingTagNotNeeded" file="/workspace/phpcheckstyle/test/PHPTagsTest.php" line="30" class="PHPTagsTest" classname="PHPTagsTest" assertions="4" time="0.000631">
|
||||||
|
<system-out>File "./test/sample/bad_php_tags_end_not_needed.php" warning, line 1 - PHP tag should be at the beginning of the line.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="ProhibitedTest" file="/workspace/phpcheckstyle/test/ProhibitedTest.php" tests="1" assertions="4" errors="0" failures="0" skipped="0" time="0.000938">
|
||||||
|
<testcase name="testProhibited" file="/workspace/phpcheckstyle/test/ProhibitedTest.php" line="13" class="ProhibitedTest" classname="ProhibitedTest" assertions="4" time="0.000938">
|
||||||
|
<system-out>File "./test/sample/bad_prohibited.php" warning, line 18 - The function exec must not be called.
|
||||||
|
File "./test/sample/bad_prohibited.php" warning, line 20 - Token T_PRINT must not be used.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="StrictCompareTest" file="/workspace/phpcheckstyle/test/StrictCompareTest.php" tests="1" assertions="4" errors="0" failures="0" skipped="0" time="0.001578">
|
||||||
|
<testcase name="testStrictCompare" file="/workspace/phpcheckstyle/test/StrictCompareTest.php" line="12" class="StrictCompareTest" classname="StrictCompareTest" assertions="4" time="0.001578">
|
||||||
|
<system-out>File "./test/sample/bad_strictcompare.php" warning, line 14 - Consider using a strict comparison operator instead of ==.
|
||||||
|
File "./test/sample/bad_strictcompare.php" warning, line 19 - Consider using a strict comparison operator instead of !=.
|
||||||
|
File "./test/sample/bad_strictcompare.php" warning, line 24 - Consider using a strict comparison operator instead of ==.
|
||||||
|
File "./test/sample/bad_strictcompare.php" warning, line 29 - Consider using a strict comparison operator instead of ==.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="UnusedTest" file="/workspace/phpcheckstyle/test/UnusedTest.php" tests="2" assertions="8" errors="0" failures="0" skipped="0" time="0.001835">
|
||||||
|
<testcase name="testGoodUnused" file="/workspace/phpcheckstyle/test/UnusedTest.php" line="13" class="UnusedTest" classname="UnusedTest" assertions="4" time="0.000940"/>
|
||||||
|
<testcase name="testBadUnused" file="/workspace/phpcheckstyle/test/UnusedTest.php" line="32" class="UnusedTest" classname="UnusedTest" assertions="4" time="0.000895">
|
||||||
|
<system-out>File "./test/sample/bad_unused.php" warning, line 23 - Function _testUnused has unused code after RETURN.
|
||||||
|
File "./test/sample/bad_unused.php" warning, line 27 - The function _testUnused parameter $b is not used.
|
||||||
|
File "./test/sample/bad_unused.php" warning, line 18 - Unused private function: _testUnused.
|
||||||
|
File "./test/sample/bad_unused.php" warning, line 20 - Undeclared or unused variable: $c.
|
||||||
|
</system-out>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
</testsuite>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
23
__tests__/fixtures/phpunit/phpunit-paths.xml
Normal file
23
__tests__/fixtures/phpunit/phpunit-paths.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="SampleSuite" tests="6" failures="6" time="0.006">
|
||||||
|
<testcase name="testFailure" classname="SampleSuite" file="/home/runner/work/repo/src/Fake.php" line="42" time="0.001">
|
||||||
|
<failure type="Exception" message="Boom">/home/runner/work/repo/src/Fake.php:42</failure>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testStringFailure" classname="SampleSuite" file="/home/runner/work/repo/src/Other.php" line="10" time="0.001">
|
||||||
|
<failure>/home/runner/work/repo/src/Other.php:10</failure>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testParenFailure" classname="SampleSuite" file="/home/runner/work/repo/src/Paren.php" line="123" time="0.001">
|
||||||
|
<failure>at /home/runner/work/repo/src/Paren.php(123)</failure>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testWindowsFailure" classname="SampleSuite" file="C:\repo\src\Win.php" line="77" time="0.001">
|
||||||
|
<failure>C:\repo\src\Win.php:77</failure>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testWindowsParenFailure" classname="SampleSuite" file="C:\repo\src\WinParen.php" line="88" time="0.001">
|
||||||
|
<failure>at C:\repo\src\WinParen.php(88)</failure>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="testPhptFailure" classname="SampleSuite" file="/home/runner/work/repo/tests/Sample.phpt" line="12" time="0.001">
|
||||||
|
<failure>/home/runner/work/repo/tests/Sample.phpt:12</failure>
|
||||||
|
</testcase>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
79
__tests__/fixtures/phpunit/phpunit.xml
Normal file
79
__tests__/fixtures/phpunit/phpunit.xml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="CLI Arguments" tests="12" assertions="12" errors="0" failures="2" skipped="0" time="0.140397">
|
||||||
|
<testcase name="targeting-traits-with-coversclass-attribute-is-deprecated.phpt" file="/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-coversclass-attribute-is-deprecated.phpt" assertions="1" time="0.068151">
|
||||||
|
<failure type="PHPUnit\Framework\PhptAssertionFailedError">targeting-traits-with-coversclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
|
||||||
|
--- Expected
|
||||||
|
+++ Actual
|
||||||
|
@@ @@
|
||||||
|
PHPUnit Started (PHPUnit 11.2-g0c2333363 using PHP 8.2.17 (cli) on Linux)
|
||||||
|
Test Runner Configured
|
||||||
|
Test Suite Loaded (1 test)
|
||||||
|
+Test Runner Triggered Warning (No code coverage driver available)
|
||||||
|
Event Facade Sealed
|
||||||
|
Test Runner Started
|
||||||
|
Test Suite Sorted
|
||||||
|
@@ @@
|
||||||
|
Test Preparation Started (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest::testSomething)
|
||||||
|
Test Prepared (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest::testSomething)
|
||||||
|
Test Passed (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest::testSomething)
|
||||||
|
-Test Runner Triggered Deprecation (Targeting a trait such as PHPUnit\TestFixture\CoveredTrait with #[CoversClass] is deprecated, please refactor your test to use #[CoversTrait] instead.)
|
||||||
|
Test Finished (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest::testSomething)
|
||||||
|
Test Suite Finished (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithCoversClassTest, 1 test)
|
||||||
|
Test Runner Execution Finished
|
||||||
|
Test Runner Finished
|
||||||
|
-PHPUnit Finished (Shell Exit Code: 0)
|
||||||
|
+PHPUnit Finished (Shell Exit Code: 1)
|
||||||
|
|
||||||
|
/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-coversclass-attribute-is-deprecated.phpt:28
|
||||||
|
/home/matteo/OSS/phpunit/src/Framework/TestSuite.php:369
|
||||||
|
/home/matteo/OSS/phpunit/src/TextUI/TestRunner.php:62
|
||||||
|
/home/matteo/OSS/phpunit/src/TextUI/Application.php:200</failure>
|
||||||
|
</testcase>
|
||||||
|
<testcase name="targeting-traits-with-usesclass-attribute-is-deprecated.phpt" file="/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-usesclass-attribute-is-deprecated.phpt" assertions="1" time="0.064268">
|
||||||
|
<failure type="PHPUnit\Framework\PhptAssertionFailedError">targeting-traits-with-usesclass-attribute-is-deprecated.phptFailed asserting that string matches format description.
|
||||||
|
--- Expected
|
||||||
|
+++ Actual
|
||||||
|
@@ @@
|
||||||
|
PHPUnit Started (PHPUnit 11.2-g0c2333363 using PHP 8.2.17 (cli) on Linux)
|
||||||
|
Test Runner Configured
|
||||||
|
Test Suite Loaded (1 test)
|
||||||
|
+Test Runner Triggered Warning (No code coverage driver available)
|
||||||
|
Event Facade Sealed
|
||||||
|
Test Runner Started
|
||||||
|
Test Suite Sorted
|
||||||
|
@@ @@
|
||||||
|
Test Preparation Started (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest::testSomething)
|
||||||
|
Test Prepared (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest::testSomething)
|
||||||
|
Test Passed (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest::testSomething)
|
||||||
|
-Test Runner Triggered Deprecation (Targeting a trait such as PHPUnit\TestFixture\CoveredTrait with #[UsesClass] is deprecated, please refactor your test to use #[UsesTrait] instead.)
|
||||||
|
Test Finished (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest::testSomething)
|
||||||
|
Test Suite Finished (PHPUnit\DeprecatedAnnotationsTestFixture\TraitTargetedWithUsesClassTest, 1 test)
|
||||||
|
Test Runner Execution Finished
|
||||||
|
Test Runner Finished
|
||||||
|
-PHPUnit Finished (Shell Exit Code: 0)
|
||||||
|
+PHPUnit Finished (Shell Exit Code: 1)
|
||||||
|
|
||||||
|
/home/matteo/OSS/phpunit/tests/end-to-end/metadata/targeting-traits-with-usesclass-attribute-is-deprecated.phpt:28
|
||||||
|
/home/matteo/OSS/phpunit/src/Framework/TestSuite.php:369
|
||||||
|
/home/matteo/OSS/phpunit/src/TextUI/TestRunner.php:62
|
||||||
|
/home/matteo/OSS/phpunit/src/TextUI/Application.php:200</failure>
|
||||||
|
</testcase>
|
||||||
|
<testsuite name="PHPUnit\Event\CollectingDispatcherTest" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/CollectingDispatcherTest.php" tests="2" assertions="2" errors="0" failures="0" skipped="0" time="0.004256">
|
||||||
|
<testcase name="testHasNoCollectedEventsWhenFlushedImmediatelyAfterCreation" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/CollectingDispatcherTest.php" line="20" class="PHPUnit\Event\CollectingDispatcherTest" classname="PHPUnit.Event.CollectingDispatcherTest" assertions="1" time="0.001441"/>
|
||||||
|
<testcase name="testCollectsDispatchedEventsUntilFlushed" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/CollectingDispatcherTest.php" line="27" class="PHPUnit\Event\CollectingDispatcherTest" classname="PHPUnit.Event.CollectingDispatcherTest" assertions="1" time="0.002815"/>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="PHPUnit\Event\DeferringDispatcherTest" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DeferringDispatcherTest.php" tests="4" assertions="4" errors="0" failures="0" skipped="0" time="0.002928">
|
||||||
|
<testcase name="testCollectsEventsUntilFlush" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DeferringDispatcherTest.php" line="22" class="PHPUnit\Event\DeferringDispatcherTest" classname="PHPUnit.Event.DeferringDispatcherTest" assertions="1" time="0.001672"/>
|
||||||
|
<testcase name="testFlushesCollectedEvents" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DeferringDispatcherTest.php" line="35" class="PHPUnit\Event\DeferringDispatcherTest" classname="PHPUnit.Event.DeferringDispatcherTest" assertions="1" time="0.000661"/>
|
||||||
|
<testcase name="testSubscriberCanBeRegistered" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DeferringDispatcherTest.php" line="53" class="PHPUnit\Event\DeferringDispatcherTest" classname="PHPUnit.Event.DeferringDispatcherTest" assertions="1" time="0.000334"/>
|
||||||
|
<testcase name="testTracerCanBeRegistered" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DeferringDispatcherTest.php" line="69" class="PHPUnit\Event\DeferringDispatcherTest" classname="PHPUnit.Event.DeferringDispatcherTest" assertions="1" time="0.000262"/>
|
||||||
|
</testsuite>
|
||||||
|
<testsuite name="PHPUnit\Event\DirectDispatcherTest" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DirectDispatcherTest.php" tests="4" assertions="4" errors="0" failures="0" skipped="0" time="0.000794">
|
||||||
|
<testcase name="testDispatchesEventToKnownSubscribers" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DirectDispatcherTest.php" line="24" class="PHPUnit\Event\DirectDispatcherTest" classname="PHPUnit.Event.DirectDispatcherTest" assertions="1" time="0.000170"/>
|
||||||
|
<testcase name="testDispatchesEventToTracers" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DirectDispatcherTest.php" line="43" class="PHPUnit\Event\DirectDispatcherTest" classname="PHPUnit.Event.DirectDispatcherTest" assertions="1" time="0.000248"/>
|
||||||
|
<testcase name="testRegisterRejectsUnknownSubscriber" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DirectDispatcherTest.php" line="62" class="PHPUnit\Event\DirectDispatcherTest" classname="PHPUnit.Event.DirectDispatcherTest" assertions="1" time="0.000257"/>
|
||||||
|
<testcase name="testDispatchRejectsUnknownEventType" file="/home/matteo/OSS/phpunit/tests/unit/Event/Dispatcher/DirectDispatcherTest.php" line="73" class="PHPUnit\Event\DirectDispatcherTest" classname="PHPUnit.Event.DirectDispatcherTest" assertions="1" time="0.000119"/>
|
||||||
|
</testsuite>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
347
__tests__/phpunit-junit.test.ts
Normal file
347
__tests__/phpunit-junit.test.ts
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
import * as fs from 'fs'
|
||||||
|
import * as path from 'path'
|
||||||
|
|
||||||
|
import {PhpunitJunitParser} from '../src/parsers/phpunit-junit/phpunit-junit-parser'
|
||||||
|
import {ParseOptions} from '../src/test-parser'
|
||||||
|
import {getReport} from '../src/report/get-report'
|
||||||
|
import {normalizeFilePath} from '../src/utils/path-utils'
|
||||||
|
|
||||||
|
describe('phpunit-junit tests', () => {
|
||||||
|
it('produces empty test run result when there are no test cases', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'empty', 'phpunit-empty.xml')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
expect(result.tests).toBe(0)
|
||||||
|
expect(result.result).toBe('success')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('report from phpunit test results matches snapshot', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'phpunit', 'phpunit.xml')
|
||||||
|
const outputPath = path.join(__dirname, '__outputs__', 'phpunit-test-results.md')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
expect(result).toMatchSnapshot()
|
||||||
|
|
||||||
|
const report = getReport([result])
|
||||||
|
fs.mkdirSync(path.dirname(outputPath), {recursive: true})
|
||||||
|
fs.writeFileSync(outputPath, report)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('parses nested test suites correctly', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'phpunit', 'phpunit.xml')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
|
||||||
|
// Should have 4 test suites (3 nested ones plus the parent with direct testcases)
|
||||||
|
expect(result.suites.length).toBe(4)
|
||||||
|
|
||||||
|
// Verify suite names
|
||||||
|
const suiteNames = result.suites.map(s => s.name)
|
||||||
|
expect(suiteNames).toContain('PHPUnit\\Event\\CollectingDispatcherTest')
|
||||||
|
expect(suiteNames).toContain('PHPUnit\\Event\\DeferringDispatcherTest')
|
||||||
|
expect(suiteNames).toContain('PHPUnit\\Event\\DirectDispatcherTest')
|
||||||
|
expect(suiteNames).toContain('CLI Arguments')
|
||||||
|
|
||||||
|
// Verify total test count
|
||||||
|
expect(result.tests).toBe(12)
|
||||||
|
expect(result.passed).toBe(10)
|
||||||
|
expect(result.failed).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('extracts error details from failures', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'phpunit', 'phpunit.xml')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
|
||||||
|
// Find the CLI Arguments suite which has failures
|
||||||
|
const cliSuite = result.suites.find(s => s.name === 'CLI Arguments')
|
||||||
|
expect(cliSuite).toBeDefined()
|
||||||
|
|
||||||
|
// Get the failed tests
|
||||||
|
const failedTests = cliSuite!.groups.flatMap(g => g.tests).filter(t => t.result === 'failed')
|
||||||
|
expect(failedTests.length).toBe(2)
|
||||||
|
|
||||||
|
// Verify error details are captured
|
||||||
|
for (const test of failedTests) {
|
||||||
|
expect(test.error).toBeDefined()
|
||||||
|
expect(test.error!.details).toContain('Failed asserting that string matches format description')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('maps absolute paths to tracked files for annotations', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'phpunit', 'phpunit-paths.xml')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: [
|
||||||
|
'src/Fake.php',
|
||||||
|
'src/Other.php',
|
||||||
|
'src/Paren.php',
|
||||||
|
'src/Win.php',
|
||||||
|
'src/WinParen.php',
|
||||||
|
'tests/Sample.phpt'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
|
||||||
|
const suite = result.suites.find(s => s.name === 'SampleSuite')
|
||||||
|
expect(suite).toBeDefined()
|
||||||
|
|
||||||
|
const tests = suite!.groups.flatMap(g => g.tests)
|
||||||
|
const fileFailure = tests.find(t => t.name === 'testFailure')
|
||||||
|
expect(fileFailure).toBeDefined()
|
||||||
|
expect(fileFailure!.error).toBeDefined()
|
||||||
|
expect(fileFailure!.error!.path).toBe('src/Fake.php')
|
||||||
|
expect(fileFailure!.error!.line).toBe(42)
|
||||||
|
|
||||||
|
const stringFailure = tests.find(t => t.name === 'testStringFailure')
|
||||||
|
expect(stringFailure).toBeDefined()
|
||||||
|
expect(stringFailure!.error).toBeDefined()
|
||||||
|
expect(stringFailure!.error!.path).toBe('src/Other.php')
|
||||||
|
expect(stringFailure!.error!.line).toBe(10)
|
||||||
|
|
||||||
|
const parenFailure = tests.find(t => t.name === 'testParenFailure')
|
||||||
|
expect(parenFailure).toBeDefined()
|
||||||
|
expect(parenFailure!.error).toBeDefined()
|
||||||
|
expect(parenFailure!.error!.path).toBe('src/Paren.php')
|
||||||
|
expect(parenFailure!.error!.line).toBe(123)
|
||||||
|
|
||||||
|
const windowsFailure = tests.find(t => t.name === 'testWindowsFailure')
|
||||||
|
expect(windowsFailure).toBeDefined()
|
||||||
|
expect(windowsFailure!.error).toBeDefined()
|
||||||
|
expect(windowsFailure!.error!.path).toBe('src/Win.php')
|
||||||
|
expect(windowsFailure!.error!.line).toBe(77)
|
||||||
|
|
||||||
|
const windowsParenFailure = tests.find(t => t.name === 'testWindowsParenFailure')
|
||||||
|
expect(windowsParenFailure).toBeDefined()
|
||||||
|
expect(windowsParenFailure!.error).toBeDefined()
|
||||||
|
expect(windowsParenFailure!.error!.path).toBe('src/WinParen.php')
|
||||||
|
expect(windowsParenFailure!.error!.line).toBe(88)
|
||||||
|
|
||||||
|
const phptFailure = tests.find(t => t.name === 'testPhptFailure')
|
||||||
|
expect(phptFailure).toBeDefined()
|
||||||
|
expect(phptFailure!.error).toBeDefined()
|
||||||
|
expect(phptFailure!.error!.path).toBe('tests/Sample.phpt')
|
||||||
|
expect(phptFailure!.error!.line).toBe(12)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('parses junit-basic.xml with nested suites and failure', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'external', 'phpunit', 'junit-basic.xml')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
|
||||||
|
// Verify test counts
|
||||||
|
expect(result.tests).toBe(9)
|
||||||
|
expect(result.passed).toBe(8)
|
||||||
|
expect(result.failed).toBe(1)
|
||||||
|
expect(result.result).toBe('failed')
|
||||||
|
|
||||||
|
// Verify suites - should have Tests.Registration, Tests.Authentication.Login, and Tests.Authentication
|
||||||
|
expect(result.suites.length).toBe(3)
|
||||||
|
|
||||||
|
const suiteNames = result.suites.map(s => s.name)
|
||||||
|
expect(suiteNames).toContain('Tests.Registration')
|
||||||
|
expect(suiteNames).toContain('Tests.Authentication.Login')
|
||||||
|
expect(suiteNames).toContain('Tests.Authentication')
|
||||||
|
|
||||||
|
// Verify the Registration suite has 3 tests
|
||||||
|
const registrationSuite = result.suites.find(s => s.name === 'Tests.Registration')
|
||||||
|
expect(registrationSuite).toBeDefined()
|
||||||
|
const registrationTests = registrationSuite!.groups.flatMap(g => g.tests)
|
||||||
|
expect(registrationTests.length).toBe(3)
|
||||||
|
|
||||||
|
// Verify the Authentication suite has 3 direct tests (not counting nested suite)
|
||||||
|
const authSuite = result.suites.find(s => s.name === 'Tests.Authentication')
|
||||||
|
expect(authSuite).toBeDefined()
|
||||||
|
const authTests = authSuite!.groups.flatMap(g => g.tests)
|
||||||
|
expect(authTests.length).toBe(3)
|
||||||
|
|
||||||
|
// Verify the Login nested suite has 3 tests
|
||||||
|
const loginSuite = result.suites.find(s => s.name === 'Tests.Authentication.Login')
|
||||||
|
expect(loginSuite).toBeDefined()
|
||||||
|
const loginTests = loginSuite!.groups.flatMap(g => g.tests)
|
||||||
|
expect(loginTests.length).toBe(3)
|
||||||
|
|
||||||
|
// Verify failure is captured
|
||||||
|
const failedTest = authTests.find(t => t.name === 'testCase9')
|
||||||
|
expect(failedTest).toBeDefined()
|
||||||
|
expect(failedTest!.result).toBe('failed')
|
||||||
|
expect(failedTest!.error).toBeDefined()
|
||||||
|
expect(failedTest!.error!.message).toBe('AssertionError: Assertion error message')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('parses phpcheckstyle-phpunit.xml with deeply nested suites', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'external', 'phpunit', 'phpcheckstyle-phpunit.xml')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
|
||||||
|
// Verify test counts from the XML: tests="30", failures="2"
|
||||||
|
expect(result.tests).toBe(30)
|
||||||
|
expect(result.passed).toBe(28)
|
||||||
|
expect(result.failed).toBe(2)
|
||||||
|
expect(result.result).toBe('failed')
|
||||||
|
|
||||||
|
// Verify the number of test suites extracted (leaf suites with testcases)
|
||||||
|
// CommentsTest, DeprecationTest, GoodTest, IndentationTest, MetricsTest,
|
||||||
|
// NamingTest, OptimizationTest, OtherTest, PHPTagsTest, ProhibitedTest,
|
||||||
|
// StrictCompareTest, UnusedTest = 12 suites
|
||||||
|
expect(result.suites.length).toBe(12)
|
||||||
|
|
||||||
|
const suiteNames = result.suites.map(s => s.name)
|
||||||
|
expect(suiteNames).toContain('CommentsTest')
|
||||||
|
expect(suiteNames).toContain('GoodTest')
|
||||||
|
expect(suiteNames).toContain('IndentationTest')
|
||||||
|
expect(suiteNames).toContain('OtherTest')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('extracts test data from phpcheckstyle-phpunit.xml', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'external', 'phpunit', 'phpcheckstyle-phpunit.xml')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
|
||||||
|
// Find the CommentsTest suite
|
||||||
|
const commentsSuite = result.suites.find(s => s.name === 'CommentsTest')
|
||||||
|
expect(commentsSuite).toBeDefined()
|
||||||
|
|
||||||
|
// Verify tests are extracted correctly
|
||||||
|
const tests = commentsSuite!.groups.flatMap(g => g.tests)
|
||||||
|
expect(tests.length).toBe(3)
|
||||||
|
|
||||||
|
const testGoodDoc = tests.find(t => t.name === 'testGoodDoc')
|
||||||
|
expect(testGoodDoc).toBeDefined()
|
||||||
|
expect(testGoodDoc!.result).toBe('success')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('captures failure details from phpcheckstyle-phpunit.xml', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'external', 'phpunit', 'phpcheckstyle-phpunit.xml')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
|
||||||
|
// Find the OtherTest suite which has failures
|
||||||
|
const otherSuite = result.suites.find(s => s.name === 'OtherTest')
|
||||||
|
expect(otherSuite).toBeDefined()
|
||||||
|
|
||||||
|
const failedTests = otherSuite!.groups.flatMap(g => g.tests).filter(t => t.result === 'failed')
|
||||||
|
expect(failedTests.length).toBe(2)
|
||||||
|
|
||||||
|
// Verify failure details
|
||||||
|
const testOther = failedTests.find(t => t.name === 'testOther')
|
||||||
|
expect(testOther).toBeDefined()
|
||||||
|
expect(testOther!.error).toBeDefined()
|
||||||
|
expect(testOther!.error!.details).toContain('We expect 20 warnings')
|
||||||
|
expect(testOther!.error!.details).toContain('Failed asserting that 19 matches expected 20')
|
||||||
|
|
||||||
|
const testException = failedTests.find(t => t.name === 'testException')
|
||||||
|
expect(testException).toBeDefined()
|
||||||
|
expect(testException!.error).toBeDefined()
|
||||||
|
expect(testException!.error!.details).toContain('We expect 1 error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('report from junit-basic.xml matches snapshot', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'external', 'phpunit', 'junit-basic.xml')
|
||||||
|
const outputPath = path.join(__dirname, '__outputs__', 'phpunit-junit-basic-results.md')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
expect(result).toMatchSnapshot()
|
||||||
|
|
||||||
|
const report = getReport([result])
|
||||||
|
fs.mkdirSync(path.dirname(outputPath), {recursive: true})
|
||||||
|
fs.writeFileSync(outputPath, report)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('report from phpcheckstyle-phpunit.xml matches snapshot', async () => {
|
||||||
|
const fixturePath = path.join(__dirname, 'fixtures', 'external', 'phpunit', 'phpcheckstyle-phpunit.xml')
|
||||||
|
const outputPath = path.join(__dirname, '__outputs__', 'phpunit-phpcheckstyle-results.md')
|
||||||
|
const filePath = normalizeFilePath(path.relative(__dirname, fixturePath))
|
||||||
|
const fileContent = fs.readFileSync(fixturePath, {encoding: 'utf8'})
|
||||||
|
|
||||||
|
const opts: ParseOptions = {
|
||||||
|
parseErrors: true,
|
||||||
|
trackedFiles: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new PhpunitJunitParser(opts)
|
||||||
|
const result = await parser.parse(filePath, fileContent)
|
||||||
|
expect(result).toMatchSnapshot()
|
||||||
|
|
||||||
|
const report = getReport([result])
|
||||||
|
fs.mkdirSync(path.dirname(outputPath), {recursive: true})
|
||||||
|
fs.writeFileSync(outputPath, report)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -32,6 +32,7 @@ inputs:
|
|||||||
- java-junit
|
- java-junit
|
||||||
- jest-junit
|
- jest-junit
|
||||||
- mocha-json
|
- mocha-json
|
||||||
|
- phpunit-junit
|
||||||
- python-xunit
|
- python-xunit
|
||||||
- rspec-json
|
- rspec-json
|
||||||
- swift-xunit
|
- swift-xunit
|
||||||
|
|||||||
238
dist/index.js
generated
vendored
238
dist/index.js
generated
vendored
@@ -277,6 +277,7 @@ const golang_json_parser_1 = __nccwpck_require__(5162);
|
|||||||
const java_junit_parser_1 = __nccwpck_require__(8342);
|
const java_junit_parser_1 = __nccwpck_require__(8342);
|
||||||
const jest_junit_parser_1 = __nccwpck_require__(1042);
|
const jest_junit_parser_1 = __nccwpck_require__(1042);
|
||||||
const mocha_json_parser_1 = __nccwpck_require__(5402);
|
const mocha_json_parser_1 = __nccwpck_require__(5402);
|
||||||
|
const phpunit_junit_parser_1 = __nccwpck_require__(2674);
|
||||||
const python_xunit_parser_1 = __nccwpck_require__(6578);
|
const python_xunit_parser_1 = __nccwpck_require__(6578);
|
||||||
const rspec_json_parser_1 = __nccwpck_require__(9768);
|
const rspec_json_parser_1 = __nccwpck_require__(9768);
|
||||||
const swift_xunit_parser_1 = __nccwpck_require__(7330);
|
const swift_xunit_parser_1 = __nccwpck_require__(7330);
|
||||||
@@ -494,6 +495,8 @@ class TestReporter {
|
|||||||
return new jest_junit_parser_1.JestJunitParser(options);
|
return new jest_junit_parser_1.JestJunitParser(options);
|
||||||
case 'mocha-json':
|
case 'mocha-json':
|
||||||
return new mocha_json_parser_1.MochaJsonParser(options);
|
return new mocha_json_parser_1.MochaJsonParser(options);
|
||||||
|
case 'phpunit-junit':
|
||||||
|
return new phpunit_junit_parser_1.PhpunitJunitParser(options);
|
||||||
case 'python-xunit':
|
case 'python-xunit':
|
||||||
return new python_xunit_parser_1.PythonXunitParser(options);
|
return new python_xunit_parser_1.PythonXunitParser(options);
|
||||||
case 'rspec-json':
|
case 'rspec-json':
|
||||||
@@ -1666,6 +1669,241 @@ class MochaJsonParser {
|
|||||||
exports.MochaJsonParser = MochaJsonParser;
|
exports.MochaJsonParser = MochaJsonParser;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 2674:
|
||||||
|
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
exports.PhpunitJunitParser = void 0;
|
||||||
|
const xml2js_1 = __nccwpck_require__(758);
|
||||||
|
const path_utils_1 = __nccwpck_require__(9132);
|
||||||
|
const test_results_1 = __nccwpck_require__(613);
|
||||||
|
class PhpunitJunitParser {
|
||||||
|
options;
|
||||||
|
trackedFiles;
|
||||||
|
trackedFilesList;
|
||||||
|
assumedWorkDir;
|
||||||
|
constructor(options) {
|
||||||
|
this.options = options;
|
||||||
|
this.trackedFilesList = options.trackedFiles.map(f => (0, path_utils_1.normalizeFilePath)(f));
|
||||||
|
this.trackedFiles = new Set(this.trackedFilesList);
|
||||||
|
}
|
||||||
|
async parse(filePath, content) {
|
||||||
|
const reportOrSuite = await this.getPhpunitReport(filePath, content);
|
||||||
|
const isReport = reportOrSuite.testsuites !== undefined;
|
||||||
|
// XML might contain:
|
||||||
|
// - multiple suites under <testsuites> root node
|
||||||
|
// - single <testsuite> as root node
|
||||||
|
let report;
|
||||||
|
if (isReport) {
|
||||||
|
report = reportOrSuite;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Make it behave the same way as if suite was inside <testsuites> root node
|
||||||
|
const suite = reportOrSuite.testsuite;
|
||||||
|
report = {
|
||||||
|
testsuites: {
|
||||||
|
$: { time: suite.$.time },
|
||||||
|
testsuite: [suite]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return this.getTestRunResult(filePath, report);
|
||||||
|
}
|
||||||
|
async getPhpunitReport(filePath, content) {
|
||||||
|
try {
|
||||||
|
return await (0, xml2js_1.parseStringPromise)(content);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
throw new Error(`Invalid XML at ${filePath}\n\n${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getTestRunResult(filePath, report) {
|
||||||
|
const suites = [];
|
||||||
|
this.collectSuites(suites, report.testsuites.testsuite ?? []);
|
||||||
|
const seconds = parseFloat(report.testsuites.$?.time ?? '');
|
||||||
|
const time = isNaN(seconds) ? undefined : seconds * 1000;
|
||||||
|
return new test_results_1.TestRunResult(filePath, suites, time);
|
||||||
|
}
|
||||||
|
collectSuites(results, testsuites) {
|
||||||
|
for (const ts of testsuites) {
|
||||||
|
// Recursively process nested test suites first (depth-first)
|
||||||
|
if (ts.testsuite) {
|
||||||
|
this.collectSuites(results, ts.testsuite);
|
||||||
|
}
|
||||||
|
// Only add suites that have direct test cases
|
||||||
|
// This avoids adding container suites that only hold nested suites
|
||||||
|
if (ts.testcase && ts.testcase.length > 0) {
|
||||||
|
const name = ts.$.name.trim();
|
||||||
|
const time = parseFloat(ts.$.time) * 1000;
|
||||||
|
results.push(new test_results_1.TestSuiteResult(name, this.getGroups(ts), time));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getGroups(suite) {
|
||||||
|
if (!suite.testcase || suite.testcase.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const groups = [];
|
||||||
|
for (const tc of suite.testcase) {
|
||||||
|
// Use classname (PHPUnit style) for grouping
|
||||||
|
// If classname matches suite name, use empty string to avoid redundancy
|
||||||
|
const className = tc.$.classname ?? tc.$.class ?? '';
|
||||||
|
const groupName = className === suite.$.name ? '' : className;
|
||||||
|
let grp = groups.find(g => g.name === groupName);
|
||||||
|
if (grp === undefined) {
|
||||||
|
grp = { name: groupName, tests: [] };
|
||||||
|
groups.push(grp);
|
||||||
|
}
|
||||||
|
grp.tests.push(tc);
|
||||||
|
}
|
||||||
|
return groups.map(grp => {
|
||||||
|
const tests = grp.tests.map(tc => {
|
||||||
|
const name = tc.$.name.trim();
|
||||||
|
const result = this.getTestCaseResult(tc);
|
||||||
|
const time = parseFloat(tc.$.time) * 1000;
|
||||||
|
const error = this.getTestCaseError(tc);
|
||||||
|
return new test_results_1.TestCaseResult(name, result, time, error);
|
||||||
|
});
|
||||||
|
return new test_results_1.TestGroupResult(grp.name, tests);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getTestCaseResult(test) {
|
||||||
|
if (test.failure || test.error)
|
||||||
|
return 'failed';
|
||||||
|
if (test.skipped)
|
||||||
|
return 'skipped';
|
||||||
|
return 'success';
|
||||||
|
}
|
||||||
|
getTestCaseError(tc) {
|
||||||
|
if (!this.options.parseErrors) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// We process <error> and <failure> the same way
|
||||||
|
const failures = tc.failure ?? tc.error;
|
||||||
|
if (!failures || failures.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const failure = failures[0];
|
||||||
|
const details = typeof failure === 'string' ? failure : failure._ ?? '';
|
||||||
|
// PHPUnit provides file path directly in testcase attributes
|
||||||
|
let filePath;
|
||||||
|
let line;
|
||||||
|
if (tc.$.file) {
|
||||||
|
const relativePath = this.getRelativePath(tc.$.file);
|
||||||
|
if (this.trackedFiles.has(relativePath)) {
|
||||||
|
filePath = relativePath;
|
||||||
|
}
|
||||||
|
if (tc.$.line) {
|
||||||
|
line = parseInt(tc.$.line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If file not in tracked files, try to extract from error details
|
||||||
|
if (!filePath && details) {
|
||||||
|
const extracted = this.extractFileAndLine(details);
|
||||||
|
if (extracted) {
|
||||||
|
filePath = extracted.filePath;
|
||||||
|
line = extracted.line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let message;
|
||||||
|
if (typeof failure !== 'string' && failure.$) {
|
||||||
|
message = failure.$.message;
|
||||||
|
if (failure.$.type) {
|
||||||
|
message = message ? `${failure.$.type}: ${message}` : failure.$.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
path: filePath,
|
||||||
|
line,
|
||||||
|
details,
|
||||||
|
message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
extractFileAndLine(details) {
|
||||||
|
// PHPUnit stack traces typically have format: /path/to/file.php:123
|
||||||
|
const lines = details.split(/\r?\n/);
|
||||||
|
for (const str of lines) {
|
||||||
|
// Match patterns like /path/to/file.php:123 or at /path/to/file.php(123)
|
||||||
|
const matchColon = str.match(/((?:[A-Za-z]:)?[^\s:()]+?\.(?:php|phpt)):(\d+)/);
|
||||||
|
if (matchColon) {
|
||||||
|
const relativePath = this.getRelativePath(matchColon[1]);
|
||||||
|
if (this.trackedFiles.has(relativePath)) {
|
||||||
|
return { filePath: relativePath, line: parseInt(matchColon[2]) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const matchParen = str.match(/((?:[A-Za-z]:)?[^\s:()]+?\.(?:php|phpt))\((\d+)\)/);
|
||||||
|
if (matchParen) {
|
||||||
|
const relativePath = this.getRelativePath(matchParen[1]);
|
||||||
|
if (this.trackedFiles.has(relativePath)) {
|
||||||
|
return { filePath: relativePath, line: parseInt(matchParen[2]) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Converts an absolute file path to a relative path by stripping the working directory prefix.
|
||||||
|
*
|
||||||
|
* @param path - The absolute file path from PHPUnit output (e.g., `/home/runner/work/repo/src/Test.php`)
|
||||||
|
* @returns The relative path (e.g., `src/Test.php`) if a working directory can be determined,
|
||||||
|
* otherwise returns the normalized original path
|
||||||
|
*/
|
||||||
|
getRelativePath(path) {
|
||||||
|
path = (0, path_utils_1.normalizeFilePath)(path);
|
||||||
|
const workDir = this.getWorkDir(path);
|
||||||
|
if (workDir !== undefined && path.startsWith(workDir)) {
|
||||||
|
path = path.substr(workDir.length);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Determines the working directory prefix to strip from absolute file paths.
|
||||||
|
*
|
||||||
|
* The working directory is resolved using the following priority:
|
||||||
|
*
|
||||||
|
* 1. **Explicit configuration** - If `options.workDir` is set, it takes precedence.
|
||||||
|
* This allows users to explicitly specify the working directory.
|
||||||
|
*
|
||||||
|
* 2. **Cached assumption** - If we've previously determined a working directory
|
||||||
|
* (`assumedWorkDir`) and the current path starts with it, we reuse that value.
|
||||||
|
* This avoids redundant computation for subsequent paths.
|
||||||
|
*
|
||||||
|
* 3. **Heuristic detection** - Uses `getBasePath()` to find the common prefix between
|
||||||
|
* the absolute path and the list of tracked files in the repository. For example:
|
||||||
|
* - Absolute path: `/home/runner/work/repo/src/Test.php`
|
||||||
|
* - Tracked file: `src/Test.php`
|
||||||
|
* - Detected workDir: `/home/runner/work/repo/`
|
||||||
|
*
|
||||||
|
* Once detected, the working directory is cached in `assumedWorkDir` for efficiency.
|
||||||
|
*
|
||||||
|
* @param path - The normalized absolute file path to analyze
|
||||||
|
* @returns The working directory prefix (with trailing slash), or `undefined` if it cannot be determined
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // With tracked file 'src/Foo.php' and path '/home/runner/work/repo/src/Foo.php'
|
||||||
|
* // Returns: '/home/runner/work/repo/'
|
||||||
|
*/
|
||||||
|
getWorkDir(path) {
|
||||||
|
if (this.options.workDir) {
|
||||||
|
return this.options.workDir;
|
||||||
|
}
|
||||||
|
if (this.assumedWorkDir && path.startsWith(this.assumedWorkDir)) {
|
||||||
|
return this.assumedWorkDir;
|
||||||
|
}
|
||||||
|
const basePath = (0, path_utils_1.getBasePath)(path, this.trackedFilesList);
|
||||||
|
if (basePath !== undefined) {
|
||||||
|
this.assumedWorkDir = basePath;
|
||||||
|
}
|
||||||
|
return basePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.PhpunitJunitParser = PhpunitJunitParser;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 6578:
|
/***/ 6578:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {GolangJsonParser} from './parsers/golang-json/golang-json-parser'
|
|||||||
import {JavaJunitParser} from './parsers/java-junit/java-junit-parser'
|
import {JavaJunitParser} from './parsers/java-junit/java-junit-parser'
|
||||||
import {JestJunitParser} from './parsers/jest-junit/jest-junit-parser'
|
import {JestJunitParser} from './parsers/jest-junit/jest-junit-parser'
|
||||||
import {MochaJsonParser} from './parsers/mocha-json/mocha-json-parser'
|
import {MochaJsonParser} from './parsers/mocha-json/mocha-json-parser'
|
||||||
|
import {PhpunitJunitParser} from './parsers/phpunit-junit/phpunit-junit-parser'
|
||||||
import {PythonXunitParser} from './parsers/python-xunit/python-xunit-parser'
|
import {PythonXunitParser} from './parsers/python-xunit/python-xunit-parser'
|
||||||
import {RspecJsonParser} from './parsers/rspec-json/rspec-json-parser'
|
import {RspecJsonParser} from './parsers/rspec-json/rspec-json-parser'
|
||||||
import {SwiftXunitParser} from './parsers/swift-xunit/swift-xunit-parser'
|
import {SwiftXunitParser} from './parsers/swift-xunit/swift-xunit-parser'
|
||||||
@@ -271,6 +272,8 @@ class TestReporter {
|
|||||||
return new JestJunitParser(options)
|
return new JestJunitParser(options)
|
||||||
case 'mocha-json':
|
case 'mocha-json':
|
||||||
return new MochaJsonParser(options)
|
return new MochaJsonParser(options)
|
||||||
|
case 'phpunit-junit':
|
||||||
|
return new PhpunitJunitParser(options)
|
||||||
case 'python-xunit':
|
case 'python-xunit':
|
||||||
return new PythonXunitParser(options)
|
return new PythonXunitParser(options)
|
||||||
case 'rspec-json':
|
case 'rspec-json':
|
||||||
|
|||||||
258
src/parsers/phpunit-junit/phpunit-junit-parser.ts
Normal file
258
src/parsers/phpunit-junit/phpunit-junit-parser.ts
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
import {ParseOptions, TestParser} from '../../test-parser'
|
||||||
|
import {parseStringPromise} from 'xml2js'
|
||||||
|
|
||||||
|
import {PhpunitReport, SingleSuiteReport, TestCase, TestSuite} from './phpunit-junit-types'
|
||||||
|
import {getBasePath, normalizeFilePath} from '../../utils/path-utils'
|
||||||
|
|
||||||
|
import {
|
||||||
|
TestExecutionResult,
|
||||||
|
TestRunResult,
|
||||||
|
TestSuiteResult,
|
||||||
|
TestGroupResult,
|
||||||
|
TestCaseResult,
|
||||||
|
TestCaseError
|
||||||
|
} from '../../test-results'
|
||||||
|
|
||||||
|
export class PhpunitJunitParser implements TestParser {
|
||||||
|
readonly trackedFiles: Set<string>
|
||||||
|
readonly trackedFilesList: string[]
|
||||||
|
private assumedWorkDir: string | undefined
|
||||||
|
|
||||||
|
constructor(readonly options: ParseOptions) {
|
||||||
|
this.trackedFilesList = options.trackedFiles.map(f => normalizeFilePath(f))
|
||||||
|
this.trackedFiles = new Set(this.trackedFilesList)
|
||||||
|
}
|
||||||
|
|
||||||
|
async parse(filePath: string, content: string): Promise<TestRunResult> {
|
||||||
|
const reportOrSuite = await this.getPhpunitReport(filePath, content)
|
||||||
|
const isReport = (reportOrSuite as PhpunitReport).testsuites !== undefined
|
||||||
|
|
||||||
|
// XML might contain:
|
||||||
|
// - multiple suites under <testsuites> root node
|
||||||
|
// - single <testsuite> as root node
|
||||||
|
let report: PhpunitReport
|
||||||
|
if (isReport) {
|
||||||
|
report = reportOrSuite as PhpunitReport
|
||||||
|
} else {
|
||||||
|
// Make it behave the same way as if suite was inside <testsuites> root node
|
||||||
|
const suite = (reportOrSuite as SingleSuiteReport).testsuite
|
||||||
|
report = {
|
||||||
|
testsuites: {
|
||||||
|
$: {time: suite.$.time},
|
||||||
|
testsuite: [suite]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getTestRunResult(filePath, report)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getPhpunitReport(filePath: string, content: string): Promise<PhpunitReport | SingleSuiteReport> {
|
||||||
|
try {
|
||||||
|
return await parseStringPromise(content)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(`Invalid XML at ${filePath}\n\n${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTestRunResult(filePath: string, report: PhpunitReport): TestRunResult {
|
||||||
|
const suites: TestSuiteResult[] = []
|
||||||
|
this.collectSuites(suites, report.testsuites.testsuite ?? [])
|
||||||
|
|
||||||
|
const seconds = parseFloat(report.testsuites.$?.time ?? '')
|
||||||
|
const time = isNaN(seconds) ? undefined : seconds * 1000
|
||||||
|
return new TestRunResult(filePath, suites, time)
|
||||||
|
}
|
||||||
|
|
||||||
|
private collectSuites(results: TestSuiteResult[], testsuites: TestSuite[]): void {
|
||||||
|
for (const ts of testsuites) {
|
||||||
|
// Recursively process nested test suites first (depth-first)
|
||||||
|
if (ts.testsuite) {
|
||||||
|
this.collectSuites(results, ts.testsuite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only add suites that have direct test cases
|
||||||
|
// This avoids adding container suites that only hold nested suites
|
||||||
|
if (ts.testcase && ts.testcase.length > 0) {
|
||||||
|
const name = ts.$.name.trim()
|
||||||
|
const time = parseFloat(ts.$.time) * 1000
|
||||||
|
results.push(new TestSuiteResult(name, this.getGroups(ts), time))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getGroups(suite: TestSuite): TestGroupResult[] {
|
||||||
|
if (!suite.testcase || suite.testcase.length === 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups: {name: string; tests: TestCase[]}[] = []
|
||||||
|
for (const tc of suite.testcase) {
|
||||||
|
// Use classname (PHPUnit style) for grouping
|
||||||
|
// If classname matches suite name, use empty string to avoid redundancy
|
||||||
|
const className = tc.$.classname ?? tc.$.class ?? ''
|
||||||
|
const groupName = className === suite.$.name ? '' : className
|
||||||
|
let grp = groups.find(g => g.name === groupName)
|
||||||
|
if (grp === undefined) {
|
||||||
|
grp = {name: groupName, tests: []}
|
||||||
|
groups.push(grp)
|
||||||
|
}
|
||||||
|
grp.tests.push(tc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups.map(grp => {
|
||||||
|
const tests = grp.tests.map(tc => {
|
||||||
|
const name = tc.$.name.trim()
|
||||||
|
const result = this.getTestCaseResult(tc)
|
||||||
|
const time = parseFloat(tc.$.time) * 1000
|
||||||
|
const error = this.getTestCaseError(tc)
|
||||||
|
return new TestCaseResult(name, result, time, error)
|
||||||
|
})
|
||||||
|
return new TestGroupResult(grp.name, tests)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTestCaseResult(test: TestCase): TestExecutionResult {
|
||||||
|
if (test.failure || test.error) return 'failed'
|
||||||
|
if (test.skipped) return 'skipped'
|
||||||
|
return 'success'
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTestCaseError(tc: TestCase): TestCaseError | undefined {
|
||||||
|
if (!this.options.parseErrors) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// We process <error> and <failure> the same way
|
||||||
|
const failures = tc.failure ?? tc.error
|
||||||
|
if (!failures || failures.length === 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const failure = failures[0]
|
||||||
|
const details = typeof failure === 'string' ? failure : failure._ ?? ''
|
||||||
|
|
||||||
|
// PHPUnit provides file path directly in testcase attributes
|
||||||
|
let filePath: string | undefined
|
||||||
|
let line: number | undefined
|
||||||
|
|
||||||
|
if (tc.$.file) {
|
||||||
|
const relativePath = this.getRelativePath(tc.$.file)
|
||||||
|
if (this.trackedFiles.has(relativePath)) {
|
||||||
|
filePath = relativePath
|
||||||
|
}
|
||||||
|
if (tc.$.line) {
|
||||||
|
line = parseInt(tc.$.line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If file not in tracked files, try to extract from error details
|
||||||
|
if (!filePath && details) {
|
||||||
|
const extracted = this.extractFileAndLine(details)
|
||||||
|
if (extracted) {
|
||||||
|
filePath = extracted.filePath
|
||||||
|
line = extracted.line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let message: string | undefined
|
||||||
|
if (typeof failure !== 'string' && failure.$) {
|
||||||
|
message = failure.$.message
|
||||||
|
if (failure.$.type) {
|
||||||
|
message = message ? `${failure.$.type}: ${message}` : failure.$.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
path: filePath,
|
||||||
|
line,
|
||||||
|
details,
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractFileAndLine(details: string): {filePath: string; line: number} | undefined {
|
||||||
|
// PHPUnit stack traces typically have format: /path/to/file.php:123
|
||||||
|
const lines = details.split(/\r?\n/)
|
||||||
|
|
||||||
|
for (const str of lines) {
|
||||||
|
// Match patterns like /path/to/file.php:123 or at /path/to/file.php(123)
|
||||||
|
const matchColon = str.match(/((?:[A-Za-z]:)?[^\s:()]+?\.(?:php|phpt)):(\d+)/)
|
||||||
|
if (matchColon) {
|
||||||
|
const relativePath = this.getRelativePath(matchColon[1])
|
||||||
|
if (this.trackedFiles.has(relativePath)) {
|
||||||
|
return {filePath: relativePath, line: parseInt(matchColon[2])}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchParen = str.match(/((?:[A-Za-z]:)?[^\s:()]+?\.(?:php|phpt))\((\d+)\)/)
|
||||||
|
if (matchParen) {
|
||||||
|
const relativePath = this.getRelativePath(matchParen[1])
|
||||||
|
if (this.trackedFiles.has(relativePath)) {
|
||||||
|
return {filePath: relativePath, line: parseInt(matchParen[2])}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an absolute file path to a relative path by stripping the working directory prefix.
|
||||||
|
*
|
||||||
|
* @param path - The absolute file path from PHPUnit output (e.g., `/home/runner/work/repo/src/Test.php`)
|
||||||
|
* @returns The relative path (e.g., `src/Test.php`) if a working directory can be determined,
|
||||||
|
* otherwise returns the normalized original path
|
||||||
|
*/
|
||||||
|
private getRelativePath(path: string): string {
|
||||||
|
path = normalizeFilePath(path)
|
||||||
|
const workDir = this.getWorkDir(path)
|
||||||
|
if (workDir !== undefined && path.startsWith(workDir)) {
|
||||||
|
path = path.substr(workDir.length)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the working directory prefix to strip from absolute file paths.
|
||||||
|
*
|
||||||
|
* The working directory is resolved using the following priority:
|
||||||
|
*
|
||||||
|
* 1. **Explicit configuration** - If `options.workDir` is set, it takes precedence.
|
||||||
|
* This allows users to explicitly specify the working directory.
|
||||||
|
*
|
||||||
|
* 2. **Cached assumption** - If we've previously determined a working directory
|
||||||
|
* (`assumedWorkDir`) and the current path starts with it, we reuse that value.
|
||||||
|
* This avoids redundant computation for subsequent paths.
|
||||||
|
*
|
||||||
|
* 3. **Heuristic detection** - Uses `getBasePath()` to find the common prefix between
|
||||||
|
* the absolute path and the list of tracked files in the repository. For example:
|
||||||
|
* - Absolute path: `/home/runner/work/repo/src/Test.php`
|
||||||
|
* - Tracked file: `src/Test.php`
|
||||||
|
* - Detected workDir: `/home/runner/work/repo/`
|
||||||
|
*
|
||||||
|
* Once detected, the working directory is cached in `assumedWorkDir` for efficiency.
|
||||||
|
*
|
||||||
|
* @param path - The normalized absolute file path to analyze
|
||||||
|
* @returns The working directory prefix (with trailing slash), or `undefined` if it cannot be determined
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // With tracked file 'src/Foo.php' and path '/home/runner/work/repo/src/Foo.php'
|
||||||
|
* // Returns: '/home/runner/work/repo/'
|
||||||
|
*/
|
||||||
|
private getWorkDir(path: string): string | undefined {
|
||||||
|
if (this.options.workDir) {
|
||||||
|
return this.options.workDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.assumedWorkDir && path.startsWith(this.assumedWorkDir)) {
|
||||||
|
return this.assumedWorkDir
|
||||||
|
}
|
||||||
|
|
||||||
|
const basePath = getBasePath(path, this.trackedFilesList)
|
||||||
|
if (basePath !== undefined) {
|
||||||
|
this.assumedWorkDir = basePath
|
||||||
|
}
|
||||||
|
return basePath
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/parsers/phpunit-junit/phpunit-junit-types.ts
Normal file
52
src/parsers/phpunit-junit/phpunit-junit-types.ts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
export interface PhpunitReport {
|
||||||
|
testsuites: TestSuites
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SingleSuiteReport {
|
||||||
|
testsuite: TestSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestSuites {
|
||||||
|
$?: {
|
||||||
|
time?: string
|
||||||
|
}
|
||||||
|
testsuite?: TestSuite[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestSuite {
|
||||||
|
$: {
|
||||||
|
name: string
|
||||||
|
tests?: string
|
||||||
|
assertions?: string
|
||||||
|
errors?: string
|
||||||
|
failures?: string
|
||||||
|
skipped?: string
|
||||||
|
time: string
|
||||||
|
file?: string
|
||||||
|
}
|
||||||
|
testcase?: TestCase[]
|
||||||
|
testsuite?: TestSuite[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TestCase {
|
||||||
|
$: {
|
||||||
|
name: string
|
||||||
|
class?: string
|
||||||
|
classname?: string
|
||||||
|
file?: string
|
||||||
|
line?: string
|
||||||
|
assertions?: string
|
||||||
|
time: string
|
||||||
|
}
|
||||||
|
failure?: Failure[]
|
||||||
|
error?: Failure[]
|
||||||
|
skipped?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Failure {
|
||||||
|
_: string
|
||||||
|
$?: {
|
||||||
|
type?: string
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user