Skip to content

Commit ec88833

Browse files
committed
Merge branch 'coverage'
2 parents 711d997 + 61566b0 commit ec88833

7 files changed

Lines changed: 146 additions & 10 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ Provides full IDE integration: test discovery, run configurations, code generati
1717
|----------------------|---------------------------|
1818
| Language | Kotlin 2.3.0 |
1919
| JVM Toolchain | Java 21 |
20-
| IntelliJ Platform | 2024.3.4 (IU — Ultimate) |
21-
| Min platform build | 243 (2024.3.x) |
20+
| IntelliJ Platform | 2025.2.6 (IU — Ultimate) |
21+
| Min platform build | 252 (2025.2.x) |
2222
| Build system | Gradle 9.3.0 |
2323
| IntelliJ Plugin SDK | `org.jetbrains.intellij.platform` 2.11.0 |
2424
| Changelog plugin | `org.jetbrains.changelog` 2.5.0 |
@@ -204,7 +204,7 @@ A PHP element is recognized as a Testo test when:
204204
## Constraints & Important Notes
205205

206206
- **Platform:** IntelliJ IDEA Ultimate or PhpStorm only (requires `com.jetbrains.php` plugin)
207-
- **Min IDE version:** 2024.3 (build 243+)
207+
- **Min IDE version:** 2025.2 (build 252+)
208208
- **Kotlin stdlib is NOT bundled** (`kotlin.stdlib.default.dependency = false`) — uses the one shipped with IntelliJ
209209
- **Gradle Configuration Cache** and **Build Cache** are enabled
210210
- **Code and comments language:** English

gradle.properties

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,25 @@ pluginGroup = com.github.xepozz.testo
44
pluginName = Testo
55
pluginRepositoryUrl = https://github.com/j-plugins/testo-plugin
66
# SemVer format -> https://semver.org
7-
pluginVersion = 2026.2.0
7+
pluginVersion = 2026.2.1
88

99
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
10-
pluginSinceBuild = 243
10+
pluginSinceBuild = 252
1111

1212
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
1313
platformType = IU
14-
platformVersion = 2024.3.4
14+
platformVersion = 2025.2.6
1515

1616
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
1717
# Example: platformPlugins = com.jetbrains.php:203.4449.22, org.intellij.scala:2023.3.27@EAP
18-
platformPlugins=com.jetbrains.php:243.25659.59,com.jetbrains.hackathon.indices.viewer:1.28,org.jetbrains.plugins.phpstorm-remote-interpreter:243.21565.23,com.jetbrains.hackathon.indices.viewer:1.28,com.jetbrains.php.codeception:243.25659.39,com.jetbrains.php.behat:243.21565.129,gherkin:243.21565.122
18+
platformPlugins=com.jetbrains.php:252.28539.13,com.jetbrains.hackathon.indices.viewer:1.31,org.jetbrains.plugins.phpstorm-remote-interpreter:252.23892.360,com.jetbrains.php.codeception:252.28238.9,com.jetbrains.php.behat:252.23892.360,gherkin:252.25557.23,com.github.xepozz.ide.introspector:2026.0.2
1919
# Example: platformBundledPlugins = com.intellij.java
2020
platformBundledPlugins =
2121
# Example: platformBundledModules = intellij.spellchecker
22-
platformBundledModules =
22+
platformBundledModules = intellij.platform.coverage,intellij.spellchecker
2323

2424
# Gradle Releases -> https://github.com/gradle/gradle/releases
25-
gradleVersion = 9.3.0
25+
gradleVersion = 9.5.0
2626

2727
# Opt-out flag for bundling Kotlin standard library -> https://jb.gg/intellij-platform-kotlin-stdlib
2828
kotlin.stdlib.default.dependency = false

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.github.xepozz.testo.coverage
2+
3+
import com.github.xepozz.testo.tests.run.TestoRunConfiguration
4+
import com.intellij.coverage.CoverageFileProvider
5+
import com.intellij.coverage.CoverageRunner
6+
import com.intellij.coverage.CoverageSuite
7+
import com.intellij.execution.configurations.RunConfigurationBase
8+
import com.intellij.execution.configurations.coverage.CoverageEnabledConfiguration
9+
import com.jetbrains.php.phpunit.coverage.PhpCoverageSuite
10+
import com.jetbrains.php.phpunit.coverage.PhpUnitCoverageEngine
11+
import com.jetbrains.php.phpunit.coverage.PhpUnitCoverageRunner
12+
13+
14+
class TestoCoverageEnabledConfiguration(
15+
configuration: TestoRunConfiguration
16+
) : CoverageEnabledConfiguration(configuration, CoverageRunner.getInstance(PhpUnitCoverageRunner::class.java)) {
17+
override fun coverageFileNameSeparator(): String = "@"
18+
19+
// Testo writes the Clover report to a path hardcoded inside the project's `testo.php`
20+
// (see `new CloverReport(__DIR__ . '/runtime/clover.xml')`). Until Testo exposes a
21+
// `--coverage-clover=<path>` CLI flag, we mirror that convention so the IDE reads
22+
// coverage from the same place Testo writes it.
23+
override fun getCoverageFilePath(): String =
24+
configuration.project.basePath?.let { "$it/runtime/clover.xml" } ?: super.getCoverageFilePath()!!
25+
}
26+
27+
class TestoCoverageEngine : PhpUnitCoverageEngine() {
28+
override fun isApplicableTo(conf: RunConfigurationBase<*>) = conf is TestoRunConfiguration
29+
30+
override fun createCoverageEnabledConfiguration(conf: RunConfigurationBase<*>) =
31+
TestoCoverageEnabledConfiguration(conf as TestoRunConfiguration)
32+
33+
override fun createCoverageSuite(
34+
covRunner: CoverageRunner,
35+
name: String,
36+
coverageDataFileProvider: CoverageFileProvider,
37+
config: CoverageEnabledConfiguration
38+
): CoverageSuite? {
39+
if (config is TestoCoverageEnabledConfiguration) {
40+
return PhpCoverageSuite(
41+
name,
42+
config.configuration.project,
43+
covRunner,
44+
coverageDataFileProvider,
45+
config.createTimestamp()
46+
)
47+
}
48+
49+
return null
50+
}
51+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.github.xepozz.testo.coverage
2+
3+
import com.github.xepozz.testo.tests.run.TestoRunConfiguration
4+
import com.intellij.execution.configurations.RunProfile
5+
import com.intellij.execution.configurations.RunProfileState
6+
import com.intellij.execution.runners.ExecutionEnvironment
7+
import com.jetbrains.php.config.commandLine.PhpCommandSettings
8+
import com.jetbrains.php.config.interpreters.PhpInterpreter
9+
import com.jetbrains.php.debug.xdebug.options.XdebugConfigurationOptionsManager
10+
import com.jetbrains.php.phpunit.coverage.PhpCoverageRunner
11+
import com.jetbrains.php.phpunit.coverage.PhpUnitCoverageEngine.CoverageEngine
12+
import com.jetbrains.php.run.PhpConfigurationOption
13+
import com.jetbrains.php.run.PhpRunConfigurationHolder
14+
15+
open class TestoCoverageProgramRunner : PhpCoverageRunner() {
16+
companion object {
17+
const val EXECUTOR_ID: String = "Coverage"
18+
const val RUNNER_ID: String = "TestoCoverageRunner"
19+
}
20+
21+
override fun canRun(executorId: String, profile: RunProfile) =
22+
executorId == EXECUTOR_ID && profile is TestoRunConfiguration
23+
24+
override fun createCoverageArguments(targetCoverage: String?) = mutableListOf("--coverage")
25+
26+
override fun getRunnerId(): String = RUNNER_ID
27+
28+
override fun createState(
29+
env: ExecutionEnvironment,
30+
interpreter: PhpInterpreter,
31+
runConfigurationHolder: PhpRunConfigurationHolder<*>,
32+
coverageArguments: MutableList<String>,
33+
localCoverage: String,
34+
targetCoverage: String
35+
): RunProfileState? {
36+
val runConfiguration = runConfigurationHolder.runConfiguration as TestoRunConfiguration
37+
38+
val command = createTestoCoverageCommand(
39+
runConfiguration,
40+
interpreter,
41+
coverageArguments,
42+
localCoverage,
43+
targetCoverage,
44+
)
45+
46+
runConfiguration.checkConfiguration()
47+
return runConfiguration.getState(env, command, null)
48+
}
49+
50+
fun createTestoCoverageCommand(
51+
runConfiguration: TestoRunConfiguration,
52+
interpreter: PhpInterpreter,
53+
coverageArguments: List<String>,
54+
localCoverage: String,
55+
targetCoverage: String
56+
): PhpCommandSettings {
57+
val command = runConfiguration.createCommand(
58+
interpreter,
59+
mutableMapOf<String, String>(),
60+
coverageArguments.toMutableList(),
61+
true,
62+
)
63+
64+
val options = when (runConfiguration.testoSettings.getTestoRunnerSettings().coverageEngine) {
65+
CoverageEngine.XDEBUG -> XdebugConfigurationOptionsManager
66+
.getConfigurationOptionsProvider(runConfiguration.project, interpreter)
67+
.enableCoverage()
68+
.createXdebugConfigurations()
69+
70+
CoverageEngine.PCOV -> listOf(PhpConfigurationOption("pcov.enabled", 1))
71+
else -> throw IllegalArgumentException("Unsupported coverage engine.")
72+
}
73+
command.addConfigurationOptions(options)
74+
setAdditionalMapping(localCoverage, targetCoverage, command)
75+
76+
return command
77+
}
78+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<idea-plugin>
2+
<extensions defaultExtensionNs="com.intellij">
3+
<coverageEngine implementation="com.github.xepozz.testo.coverage.TestoCoverageEngine"/>
4+
<programRunner implementation="com.github.xepozz.testo.coverage.TestoCoverageProgramRunner"/>
5+
</extensions>
6+
</idea-plugin>

src/main/resources/META-INF/plugin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
<depends>com.intellij.modules.platform</depends>
88
<depends>com.jetbrains.php</depends>
9+
<depends optional="true" config-file="coverage.xml">com.intellij.modules.coverage</depends>
910

1011
<resource-bundle>messages.TestoBundle</resource-bundle>
1112

0 commit comments

Comments
 (0)