Both editor integrations were outside the coverage regime entirely until now. .config/coverage/thresholds.json gated five packages (sharplsp, vscode-extension, and the three sidecars) and neither sharplsp-zed nor sharplsp-rider existed. This issue tracks the debt that remains after wiring them in.
What was wrong
Zed — 869 LOC, 23 unit tests that lived in the tree and were never executed. make _lint-zed runs cargo clippy --all-targets, which compiles the tests but never runs them, and make test had no Zed target. Because nobody ran them, nobody noticed that most were vacuous:
expected_version_matches_cargo_toml asserted EXPECTED_VERSION == env!("CARGO_PKG_VERSION") — and EXPECTED_VERSION is env!("CARGO_PKG_VERSION"). A tautology.
server_binary_name_is_sharplsp asserted a const equalled its own literal.
expected_version_matches_extension_toml_version claimed in its doc comment to prove extension.toml and Cargo.toml agree. It never opened extension.toml; it asserted the crate version was non-empty.
missing_binary_error_includes_version_and_install_instructions rebuilt the error string inside the test and asserted on its own copy, so changing the real message in resolve_binary could not have failed it.
Rider — 2961 LOC of Kotlin, zero tests and zero CI presence. build.gradle.kts declared junit-jupiter, testFramework(TestFrameworkType.Platform), and a tasks.test { useJUnitPlatform() } block; there was no src/test directory. No workflow built, linted, or tested it, so a Kotlin compile error could reach main behind a fully green pipeline.
What is now in place
| Package |
Tests |
Line coverage |
Gate |
sharplsp-zed |
31 |
85.04% |
make _test-zed |
sharplsp-rider |
8 |
4.09% |
make _test-rider (Kover) |
Both run in ci-editors.yml. RIDER_REQUIRED=1 in CI turns "no JDK 21+ found" from a local convenience skip into a hard failure, and failOnNoDiscoveredTests = true stops a zero-test test task from reporting BUILD SUCCESSFUL — the exact way this project's harness stayed green while empty.
Two build-level fixes were needed before any Rider test could run at all:
- The platform test framework registers
com.intellij.tests.JUnit5TestSessionListener, whose constructor loads junit.framework.TestCase. Without JUnit 4 on the runtime classpath it fails to instantiate and the task dies before a single test runs.
- That framework also puts
junit-vintage on the classpath, built against a newer junit-platform-commons than junit-jupiter 5.11.3 ships (support.scanning.ClassFilter). Vintage then throws during discovery, aborting the task. Discovery is now restricted to the Jupiter engine.
Remaining debt
Rider — 95.9% of the plugin is untested. The 8 tests cover NuGetState, the only branching logic that needs no running IDE. Untested:
ForgeLspServerDescriptor / ForgeLspServerSupportProvider — binary resolution and server startup
LspBridge — the 15s startup poll, the shutdown-state early exit, and the sendRequestSync timeout path
toolwindow/nodes/* — SolutionRootNode, ProjectTreeNode, DependenciesNode, SourceNode tree construction
nuget/PackageCardRenderer (239 LOC) and PackageDetailsPanel (407 LOC)
ForgeSettings / ForgeSettingsConfigurable persistence
Most of this needs BasePlatformTestCase fixtures, which the build is already configured for. Raising the floor meaningfully means either those fixtures or extracting more pure logic the way NuGetState already is.
Zed — 85.04% is close to its ceiling as structured. The uncovered remainder in lib.rs is the zed::Extension trait impl, the register_extension! macro, and every function taking a zed::Worktree, which exists only inside Zed's WASM host and cannot be constructed in a host unit test. The logic behind that shell was moved to pipeline.rs (98.41%) for exactly this reason. Going higher means a wasm integration harness, not more unit tests.
Also fixed here
default_threshold: 90 was dead config. check-coverage.mjs reads thresholds[project].line_percent and hard-fails on an unknown key; it never consulted default_threshold. Removed rather than made real — hard-failing on an ungated package is the correct strict behaviour and is what surfaced this whole gap.
- The JDK-21 discovery loop was duplicated per Rider make target; it now lives once in
tools/rider/gradle.sh.
Related
Both editor integrations were outside the coverage regime entirely until now.
.config/coverage/thresholds.jsongated five packages (sharplsp,vscode-extension, and the three sidecars) and neithersharplsp-zednorsharplsp-riderexisted. This issue tracks the debt that remains after wiring them in.What was wrong
Zed — 869 LOC, 23 unit tests that lived in the tree and were never executed.
make _lint-zedrunscargo clippy --all-targets, which compiles the tests but never runs them, andmake testhad no Zed target. Because nobody ran them, nobody noticed that most were vacuous:expected_version_matches_cargo_tomlassertedEXPECTED_VERSION == env!("CARGO_PKG_VERSION")— andEXPECTED_VERSIONisenv!("CARGO_PKG_VERSION"). A tautology.server_binary_name_is_sharplspasserted aconstequalled its own literal.expected_version_matches_extension_toml_versionclaimed in its doc comment to proveextension.tomlandCargo.tomlagree. It never openedextension.toml; it asserted the crate version was non-empty.missing_binary_error_includes_version_and_install_instructionsrebuilt the error string inside the test and asserted on its own copy, so changing the real message inresolve_binarycould not have failed it.Rider — 2961 LOC of Kotlin, zero tests and zero CI presence.
build.gradle.ktsdeclaredjunit-jupiter,testFramework(TestFrameworkType.Platform), and atasks.test { useJUnitPlatform() }block; there was nosrc/testdirectory. No workflow built, linted, or tested it, so a Kotlin compile error could reachmainbehind a fully green pipeline.What is now in place
sharplsp-zedmake _test-zedsharplsp-ridermake _test-rider(Kover)Both run in
ci-editors.yml.RIDER_REQUIRED=1in CI turns "no JDK 21+ found" from a local convenience skip into a hard failure, andfailOnNoDiscoveredTests = truestops a zero-testtesttask from reporting BUILD SUCCESSFUL — the exact way this project's harness stayed green while empty.Two build-level fixes were needed before any Rider test could run at all:
com.intellij.tests.JUnit5TestSessionListener, whose constructor loadsjunit.framework.TestCase. Without JUnit 4 on the runtime classpath it fails to instantiate and the task dies before a single test runs.junit-vintageon the classpath, built against a newerjunit-platform-commonsthanjunit-jupiter5.11.3 ships (support.scanning.ClassFilter). Vintage then throws during discovery, aborting the task. Discovery is now restricted to the Jupiter engine.Remaining debt
Rider — 95.9% of the plugin is untested. The 8 tests cover
NuGetState, the only branching logic that needs no running IDE. Untested:ForgeLspServerDescriptor/ForgeLspServerSupportProvider— binary resolution and server startupLspBridge— the 15s startup poll, the shutdown-state early exit, and thesendRequestSynctimeout pathtoolwindow/nodes/*—SolutionRootNode,ProjectTreeNode,DependenciesNode,SourceNodetree constructionnuget/PackageCardRenderer(239 LOC) andPackageDetailsPanel(407 LOC)ForgeSettings/ForgeSettingsConfigurablepersistenceMost of this needs
BasePlatformTestCasefixtures, which the build is already configured for. Raising the floor meaningfully means either those fixtures or extracting more pure logic the wayNuGetStatealready is.Zed — 85.04% is close to its ceiling as structured. The uncovered remainder in
lib.rsis thezed::Extensiontrait impl, theregister_extension!macro, and every function taking azed::Worktree, which exists only inside Zed's WASM host and cannot be constructed in a host unit test. The logic behind that shell was moved topipeline.rs(98.41%) for exactly this reason. Going higher means a wasm integration harness, not more unit tests.Also fixed here
default_threshold: 90was dead config.check-coverage.mjsreadsthresholds[project].line_percentand hard-fails on an unknown key; it never consulteddefault_threshold. Removed rather than made real — hard-failing on an ungated package is the correct strict behaviour and is what surfaced this whole gap.tools/rider/gradle.sh.Related
_check_covfail-fast masking)