Fucini Coverage Studio ProDid those tests actually exercise your code? Native VS Code coverage for
TypeScript, JavaScript, C, C++, Java, Ruby and anything else that can emit a
tracefile: gutters and overview ruler, a Covered Files view, per-function
CodeLens, branch & MC/DC hover, diff coverage, a composite safety-weighted
score, Annotated Source, and self-hosted HTML reports — all driven from the
tracefiles your build already produces. Reads lcov, llvm-cov JSON, Cobertura,
JaCoCo, Istanbul/nyc, SimpleCov, Clover, OpenCover, coverage.py, gcov/gcovr
JSON and plain-text
One workspace, every project in one view. A multi-root workspace with C, C++, .NET, TypeScript, JavaScript, Python and Visual Basic in it: the Coverage view holds them all — collapse the tree and you read eleven projects' coverage on one screen, open a folder and you are at its files, open a file and you are on its lines.
On the command line too. The same reader as this extension, free, for the places an editor is not — a CI gate, a badge, an HTML report on a build server, an MCP server for an agent:
30-second quick start
From then on it reloads by itself whenever the tracefile changes. Bars on
uncovered lines only, a hit-count heatmap and inline The same six steps, with a button for each, are the Get started with
Coverage Studio walkthrough: Help → Welcome → Walkthroughs, or Command
Palette → A short tourThe user guide has every feature with its settings; these are the ones you meet first. Gaps in the editor. A red bar on a line that never ran, amber on a line
with a branch arm never taken, the execution count after each line.
CodeLens and hover. Above every function: its coverage, and its complexity and CRAP score where the report carries them. On a decision: the branch arms and the MC/DC test vectors.
Heatmap. Every executed line, warmer the more often it ran — on dark and light themes.
Every metric the reports carry. Lines, functions, regions, branches, MC/DC and instantiations, and function calls, object code and a mutation score when a report brings them — merged onto the same file and judged against your thresholds. Object-code coverage is the unusual one, because it measures the built binary rather than the source: what it tells you that statement coverage cannot, and the three ways to get it off an embedded target, are in OBJECT-CODE-COVERAGE.md — which ships with the extension — and in User Guide §11a.
Mutants, where they are. A mutation score says 71%; the editor says which injected fault on which line no test noticed — from PIT and Stryker reports, with the change itself and the tests that ran it on hover.
Condition coverage. The level between branches and MC/DC, as a metric of
its own for the reports that carry it (BullseyeCoverage, TRACE32, GCC 14).
For GCC 14's
Thresholds that fit your project. The defaults suit an ordinary project
(lines 80, functions 80, regions 75, branches 70);
Annotated source and HTML reports. The whole file colour-coded with its findings in place, and a self-hosted report to share.
Every metric beside every file. The Coverage view's columns are always there, heaviest-weight first, with an em dash where a metric does not apply to that language rather than a gap you have to interpret. Hide the ones you do not use; the order stays.
The build finished while you were reading code. A watched tracefile reloads without asking, and the Coverage icon says how many reports moved while you were somewhere else. Open it and the count is gone; the tooltip names them, so three reports rebuilt is one number and not three interruptions.
Which tests reached this line. Hover a covered line and the hit count names the tests behind it — the answer to "can I change this?" that a percentage never gives you.
Straight to the next gap. Next Uncovered and Next Partly-Tested Decision walk the findings without scrolling and squinting.
Updates come from us, and you can be early if you want. The extension checks fucini.software for new builds. Open the licence panel in the Coverage view and you can volunteer for the pre-release channel, which is offered builds before they reach the Marketplace. They are tested, but they are the builds we are least sure of — you would be volunteering to find what we missed, and leaving puts you back on the ordinary path at the next stable release.
Especially when an AI wrote the testsA language model will write you a test suite in seconds, and it will look thorough. The question it cannot answer for you is whether those tests reach anything that matters — and line coverage cannot answer it either, because a test with no assertions at all still lights up every line it touches. Three things here answer it:
None of this proves a test is good — nothing can. It tells you, precisely and per line, what was never run, which is the part a human reviewer cannot hold in their head and an AI will not volunteer. And the agent can ask. The coverage on screen answers six questions an agent has while it writes tests, in words it can act on:
VS Code's own agent has them as tools with nothing to set up ( By Mario Fucini — Fucini Consulting
The generated report. How it comparesVS Code itself and several free extensions show coverage too. This is what each one's own documentation says it does, as of September 2026 — where a README does not mention something, the table says so rather than guessing.
It is not either/or with VS Code's own view: every load is published to the native Test Coverage panel as well, filter by test included. Something here out of date? Say so on the contact page.
Editions: Community and ProTwo listings, one codebase. Community is the permanently free tier; it is also what the Pro listing settles onto after its 14-day trial. Pro (and the trial) is everything, uncapped. The line is drawn so that the free tier always sees every finding and only pays for convenience, proof and scale — a gate you cannot see past teaches nothing about what it is missing.
A week runs seven days from the first use that opened it, so the message at a limit names the day the count starts again. Nothing that only reads coverage is counted. An install that was already in use when the weekly allowance arrived (0.2026.83) keeps its previous caps for 30 days. Not ready for a subscription? A 7-day pass is everything in the right column for a week, bought once, with nothing to cancel — redeemed with Fucini Coverage: Enter Subscription Code, in either edition. The same code activates Fucini Coverage Studio for Visual Studio, 2022 and 2026 — there under Tools → Coverage Studio → Licence…. One machine is one seat: activate VS Code or Visual Studio, and the other editor on the same machine follows by itself, with nothing to enter twice. Fucini Coverage: What Community Includes shows this list inside VS Code, with the current counts against each cap and the day each one resets. Plans, the pass and prices are on the pricing page.
Requirements
Supported formats
Multiple tracefiles are merged deterministically into one run with recorded
provenance. A file measured by more than one loaded tracefile takes its
numbers from whichever tracefile has the newest mtime on disk — a module's
own rerun replaces what a wider, older run said about it rather than adding
to it, regardless of where either path sits in When the newest is not the right answer, Gzipped tracefiles load as they are — Is this coverage still about this file?Coverage read from a tracefile describes the source as it was when the tests ran. Nothing in most formats says so, which is how a green gutter ends up describing a version of a file that no longer exists. Every file in the Coverage panel is therefore in one of three states:
Only two formats can earn the tick, because only two record anything about the source they measured:
The other thirteen say nothing about the content of the source, so a file read from them can never be more than not disproved — and this extension will not pretend otherwise. The warning does not need either. It fires on contradictions any format can
produce: coverage recorded past the end of the file, a named function that is
now somewhere other than where the report puts it, or executed lines that are
blank in the file. Timestamps are never enough on their own — a Switch all of it off with Unmeasured versus uncoveredA file the run never mentions is unmeasured: no test loaded it, so no tool wrote a line for it. That is not the same as uncovered — measured, with zero hits — and this extension has never scored an absent file as 0%. What it did not do, until 0.2026.69, was show the absent ones at all: a module with no tests whatsoever never appeared in the tree, the totals or the threshold check, and a reader saw "92%" for a project half of whose files had never been exercised. Now, with a run loaded, the per-file status-bar item says not measured
for a source file of the run's own kind that the run does not contain — "of
the run's kind" meaning it has one of the file extensions the run measured, so
a C project's headers-and-sources are candidates and its Python scripts are
not. Everything beyond that is opt-in through
Header files are the known false positive in C: they have the run's
extension family and no tracefile will ever name one, because on its own it
compiles to nothing. Ada (GNATcoverage)
Loading GNATcoverage states obligations rather than execution counts, so a satisfied
line reads as one hit. A partially covered line ( .NET on MSTest /
|
| Command | What it does |
|---|---|
| Load Coverage | Discover & load coverage for the workspace. Alt+Shift+R (Ctrl+Alt+R on macOS). |
| Pick Coverage File | Choose tracefiles — one, several at once, or a further one each time; picks add up, are merged, and are written to coverageFile.paths so they load by themselves from then on. |
| Show Loaded Coverage Files | List the tracefiles in the active run. The Coverage view lists them too, each with an unload button and a pick button beside them. |
| Toggle Tracefile Watch | Watch the tracefile(s) for changes and reload automatically. |
| Run Tests with Coverage | Recognise the test runner from the workspace root, run it with coverage on in a terminal task, load what it writes, and remember that path in coverageFile.paths. |
| Forget the Test Runner | Put the "which test run?" question back, for a workspace where more than one runner is recognised. |
| Run App with Coverage… | The app itself, under coverage, for testing by hand: the folder's .NET app (an Exe, a WinExe or a web project; a pick where there are several) is started in the terminal under Microsoft's dotnet-coverage — dotnet-coverage collect -f cobertura -o coverage/app/coverage.cobertura.xml dotnet run --project … — and runs until you close it; what you walked through is then loaded like any run. In a coverage session (Start Coverage Session) it is added to what the tests ran. Needs dotnet tool install --global dotnet-coverage. |
| Run Mutation Tests Here | Whether a test would notice this code being wrong: the function the caret is in — or the lines selected, or the file — is mutated by the folder's mutation runner, and the report is loaded beside the coverage on screen, where the surviving mutants are marked. Stryker.NET for C# (dotnet stryker --mutate "**/Quote.cs{512..918}", aimed at the test project that reaches the file's project), StrykerJS for JavaScript and TypeScript (npx stryker run --mutate "src/cart.ts:12-30"), PIT with Maven for Java (the whole class: PIT narrows no further). A run of one function takes a minute where the project's takes an hour. Also on the editor's context menu. |
| Copy MCP Server Configuration | What an MCP client (Claude Code, Cursor, …) needs to ask about the coverage on screen: the server's address and your token, as the entry of an mcp.json, on the clipboard. With the server off (fuciniCoverage.mcp.enabled), the offer to turn it on. |
| Run This Test with Coverage | What is under the caret — a test method, the test class or describe the caret is in, or the file — run by the folder's recipe narrowed to it (dotnet test --filter, jest/vitest -t, a pytest node id, go test ./pkg -run "^TestX$", cargo llvm-cov -- "mod::tests::x" --exact), then loaded like any run. Read off the text: C#, VB, F#, JavaScript, TypeScript, Python, Go and Rust. Also on the editor's context menu. |
| Run Tests Affected by My Changes… | The tests that ran a line the working tree changes against fuciniCoverage.testImpact.baseRef (HEAD), read off the report's per-test lines and git's diff, the one that ran most of the change first. A test that only ran lines the change moved down or up is listed too — its report would name the wrong lines otherwise. Pick a test to open it; the first entry runs them — one dotnet test per affected class, where the reports came from a per-test run (testRun.perTest) — and loads their reports beside the rest. |
| Show Minimal Test Set… | The fewest tests that still run every line the suite runs — a greedy cover of the report's per-test lines — and under them the tests that add no line of their own. Each says what it adds and how many lines only it runs; the list goes to the output too. Redundant by lines: a test that adds none may still be the only one that checks something. The test you pick opens where it is written. |
| Exclude Selection from Coverage | Take the selected lines out of the count where they are written: [ExcludeFromCodeCoverage] above a .NET class, method or property, LCOV_EXCL_LINE at the end of one line, LCOV_EXCL_START/STOP around several — in the file's own comment sign. Also the light bulb on a selection or an uncovered line, and the editor's context menu. Never doubles a marker that is there. |
| Show Tests That Ran This Line | The tests the report names for the caret's line — or the file, when the line names none — busiest first; the one you pick opens where it is written in the workspace. Needs a report that says which test ran what: coverage.py contexts, OpenCover, an lcov tracefile written per test, or a testRun.perTest run. Also on the editor's context menu. |
| Next Uncovered / Previous Uncovered | Jump between uncovered regions. Alt+N / Alt+P by default (Ctrl+Alt+N / Ctrl+Alt+P on macOS); remap under Keyboard Shortcuts. |
| Next Partly-Tested Decision / Previous Partly-Tested Decision | Jump between decisions some of whose arms ran and some of which never did — the gap uncovered-line navigation cannot show, since a line whose statement ran stays covered however many of its branches were never taken. Needs a report that records branches. |
| Reload Coverage | Re-read the tracefiles already loaded, in place — including ones picked by hand, which discovery would not find again. |
| Choose Editor Gutter Mode... | Off, uncovered-only bars, red-amber-green bars, or a hit-count heatmap in the editor's gutter — for this session; editorGutter.mode in the settings is what the workspace opens with. |
| Cycle Editor Gutter Mode | One step on through off, uncovered, coverage and heatmap — Alt+G, or the mode button in the status bar (statusBar.showModeToggle) — for this session, like the picker. |
| Choose Coverage Source for This File... | When more than one loaded report measures the active file, pick which one its numbers come from — each report is offered with its own figures — and whether the choice covers this file, its folder or a pattern. Writes a coverageFile.sources rule to the workspace. |
| Clear Coverage Source for This File... | Take out the workspace's coverageFile.sources rules for the active file, so it goes back to coverageFile.mergeStrategy. |
| Exclude This File from Coverage | Put the active file's workspace-relative path into fuciniCoverage.ignore in the folder's settings, so it leaves every total and every page at the reload. The Coverage view's rows have the same as Exclude from Coverage, where a folder row writes its folder/**. |
| Toggle Coverage Painting | The gutter bars and the line highlight off and on together, from the editor title bar — for reading the code as written, then reading the coverage again. Turning them back on restores the modes each was last on. For this session, like the picker: the settings keep what the workspace opens with. |
| Toggle Inline Hit Counts | Show or hide ×N execution counts after each line — flips hitCounts.inline. |
| Show in Test Coverage View | Publish the active run to VS Code's own Test Coverage panel (even with testCoverage.publish off) and open it. |
| Suggest Path Remap Rule | Infer the pathRemap rule that would match the active run's unmatched files to the workspace, and offer to add it. |
| Open HTML Report | Open the rendered static report. |
| Generate Full HTML Report | Build the multi-page report. |
| Open Annotated Source / Open Annotated Source to the Side | Annotated coverage view for a file. |
| Toggle Annotated Source / Show Source | Swap between source and annotated view. Alt+A (Ctrl+Alt+A on macOS). |
| Show Worst-Covered Files / Show Worst-Covered Folders / Show Worst-Covered Functions | Ranked lowest-first Quick Pick; the folder one reveals the folder in the Explorer. |
| Copy Uncovered Lines | Markdown summary of every uncovered range. |
| Copy Coverage Summary | The whole run as a Markdown table, worst-covered file first, with a totals row — for pasting into a pull request. Branch and MC/DC columns appear only when the report measured them. |
| Compare Two History Runs / Compare with Last Run | Per-file regression diff. |
| Compare with CI Baseline | The loaded run against the report fuciniCoverage.baseline.report names — the main branch's from CI, a URL or a downloaded artifact — file by file, as Compare with Last Run lists them; the status bar's card carries the one-line delta after every load. |
| Show Indirect Coverage Changes… | The lines whose coverage moved though nobody edited them: code that stopped running, or started, outside the lines the working tree changes against fuciniCoverage.gitDiff.baseRef. Against the CI baseline where baseline.report names one, else since the run before this one. Listed per file, in the output as well; the file you pick opens at its first such line. |
| Start Coverage Session / End Coverage Session | While a session is on, every run loaded is added to the one before it as a union — the unit tests, then the integration suite, then a walk through the UI — and the gutter and the tree show what they reached together. A bar over the tree says how many runs since when, with the way to end it. |
| Check Coverage Thresholds | Evaluate the compliance gates; with compliance.diagnostics on, the failures also stay in Problems. |
| Configure Coverage Compliance & Thresholds... | Set the gates themselves, per metric. |
| Tune Colour Scale | Try colorScale.* colours and thresholds live against the open Coverage view, then keep or revert. |
| Export Fucini JSON | Export the current run as Fucini JSON. |
| Export Coverage Report… | Pick a format — CSV (files/functions), Markdown, LCOV, Cobertura XML, SARIF or an SVG badge — and a destination. |
| Generate Coverage Badge | Write the SVG badge for the active run to badge.outputPath, showing badge.metric, and offer to open it. |
| Show Status Bar Menu | Open the quick-action menu. |
| Browse for External HTML Report Directory... / Browse for Full HTML Report Output Directory... | Pick the folders those two settings name. |
| Enter Subscription Code | Bought a subscription, or been given a seat? Paste the code from the email — the plan is known from it — and the licence key is issued and applied for you. No questionnaire. The same code activates Coverage Studio for Visual Studio on the same machine, or the other way round: one machine is one seat. |
| Request Activation Key | Ask for an evaluation key — the short questionnaire. Also where a FCB1-… code can be redeemed alongside the form. |
| Enter Activation Key / Copy Activation Request Code | Paste an FCK1.… key you were sent by hand, or copy the code support asks for. |
| Check for Updates | Ask the update feed what the current version is. |
| Feedback Survey | Open the survey in a browser. |
CodeLens, branch/MC-DC hover, dead-code warnings, badges and the status-bar items all work automatically once coverage is loaded.
Keyboard shortcuts. The four defaults — Alt+N, Alt+P, Alt+A and
Alt+Shift+R (Ctrl+Alt+… on macOS) — clash with nothing VS Code binds
itself, but Alt+A and Alt+P are popular with other extensions. If one of
yours already uses them, rebind under File → Preferences → Keyboard
Shortcuts (search Fucini Coverage); the commands work from the palette
and the status-bar menu either way.
Settings
Configure in the Settings UI (search Fucini Coverage) or settings.json.
Every option, with its default:
Prefer a file you can copy straight into your own settings? Download the complete settings reference — the same options listed below, as commented JSONC with every one of them at the default the packaged extension uses, so leaving a block as-is is identical to not setting it at all. It is regenerated from this extension on every release and names the version it came from in its header comment.
Discovery & loading
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.coverageFile.defaultLocation |
"" |
Base folder for discovery and file-pick dialogs. Variables: ${workspaceFolder}, ${workspaceFolderBasename}, ${userHome}, ${env:NAME}, ${command:id}, ${config:section.key}. Per folder. |
fuciniCoverage.language |
auto |
Language for the extension’s own text: auto follows VS Code’s display language. |
fuciniCoverage.coverageFile.paths |
[] |
Tracefile(s) to load & merge, relative to the base folder, absolute (the same variables as above), or an http(s):// URL fetched on every load — add #lcov.info (or another file name) as the URL fragment when the URL itself has no extension. A relative * glob recurses under the base folder; an absolute entry may hold a wildcard too (${workspaceFolder}/build/coverage-merged/*.lcov.info, ** for any depth), searched under the folders before it. Per folder. |
fuciniCoverage.coverageFile.rememberPicks |
true |
Remember every picked file, and every report a test run writes, for this workspace — kept for you, not written into the settings file. |
fuciniCoverage.coverageFile.sources |
[] |
Which loaded report a file's numbers come from, when more than one measures it: an ordered list of { "files": glob, "report": glob } rules — e.g. llvm-cov's coverage.json (with MC/DC) for everything and a GCC gcov.info for a few files. The first rule whose files match and whose report measures the file decides. With "combine": "union" (covered where any report ran the line) or "agree" (covered only where every report did; the disagreements marked in teal) the matching reports are combined instead of one being picked. Paid plans. Per folder. |
fuciniCoverage.coverageFile.mergeStrategy |
newest |
For files no rule decides: newest (the report written last wins) or order (the later entry in coverageFile.paths wins, whatever the times). Per folder. |
fuciniCoverage.watch.enabled |
true |
Reload coverage when a tracefile changes — or appears, for an absolute entry outside the workspace. Per folder. |
fuciniCoverage.watch.reloadAfterTasks |
[] |
Task names (label, or source: name like npm: test) whose ending reloads coverage, whether or not watch.enabled is on. Per folder. |
fuciniCoverage.remote.headers |
[] |
Name: value headers sent with every URL fetch; variables expand, so Authorization: Bearer ${env:CI_TOKEN} keeps the token out of settings.json. Per folder. |
fuciniCoverage.baseline.report |
"" |
The report to hold every loaded run against — the main branch's, as the build server publishes it: a URL (fetched with remote.headers and cached, as a coverageFile.paths URL is) or the path of a downloaded artifact, absolute or relative to the folder. The status bar's card then says where this run stands against it, and Compare with CI Baseline lists the files that moved. Empty: no baseline. Per folder. |
fuciniCoverage.remote.refreshMinutes |
0 |
Re-fetch URL entries every N minutes (conditional GET; 0 = only on load). Per folder. |
Colour scale & badges
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.colorScale.preset |
default |
colorblind: vermillion, orange and blue (Okabe–Ito) instead of red, amber and green — for the gradient, gutter bars, line highlight, Explorer badges and since-last-run marks. A colour you changed below keeps precedence. |
fuciniCoverage.colorScale.lowColor |
#8b1a1a |
Low end of the coverage gradient. |
fuciniCoverage.colorScale.midColor |
#b8860b |
Pivot colour of the gradient. |
fuciniCoverage.colorScale.highColor |
#228b22 |
High end (100%) of the gradient. |
fuciniCoverage.colorScale.lowThreshold |
80 |
% at which the badge steps low → mid. |
fuciniCoverage.colorScale.highThreshold |
90 |
% at which the badge steps mid → high (and gradient pivot). |
Composite score (Weighted Harmonic Mean)
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.whmWeights.lines |
1 |
Weight for line/statement coverage. |
fuciniCoverage.whmWeights.functions |
1 |
Weight for function coverage. |
fuciniCoverage.whmWeights.regions |
1.5 |
Weight for region coverage (llvm-cov). |
fuciniCoverage.whmWeights.branches |
2 |
Weight for branch coverage. |
fuciniCoverage.whmWeights.mcdc |
4 |
Weight for MC/DC coverage. |
fuciniCoverage.whmWeights.conditions |
3 |
Weight for condition coverage, between branches and MC/DC. |
fuciniCoverage.whmWeights.instantiations |
0.5 |
Weight for C++ template instantiations (llvm-cov). |
fuciniCoverage.whmWeights.functionCalls |
1 |
Weight for function-call coverage (VectorCAST, TRACE32, Cantata). |
fuciniCoverage.whmWeights.objectCode |
0 |
Weight for object-code coverage (TRACE32). Off by default: it subsumes statement coverage and has its own gate. |
fuciniCoverage.whmWeights.mutation |
0 |
Weight for the mutation score (PIT, Stryker). Off by default: it is not coverage and has its own gate. |
Thresholds & compliance
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.threshold.profile |
standard |
standard | safety. What a threshold you have not changed stands for: the defaults below, or 100 for every gate bar the mutation score (the defaults up to 0.2026.81). A threshold you changed wins over the profile. |
fuciniCoverage.threshold.statement |
80 |
Minimum statement %. 0 disables the gate. |
fuciniCoverage.threshold.branch |
70 |
Minimum branch %. |
fuciniCoverage.threshold.mcdc |
0 |
Minimum MC/DC %. |
fuciniCoverage.threshold.conditions |
0 |
Minimum condition % (Bullseye, TRACE32 cond, GCC 14). 0 disables the gate; safety profile: 100. |
fuciniCoverage.threshold.functions |
80 |
Minimum function %. |
fuciniCoverage.threshold.regions |
75 |
Minimum region % (llvm-cov). |
fuciniCoverage.threshold.instantiations |
0 |
Minimum instantiation % (llvm-cov). |
fuciniCoverage.threshold.functionCalls |
0 |
Minimum function-call % (Testwell CTC++). |
fuciniCoverage.threshold.objectCode |
0 |
Minimum object-code %, where the report carries it. |
fuciniCoverage.threshold.mutation |
0 |
Minimum mutation score (PIT, Stryker). 0 disables the gate. |
fuciniCoverage.threshold.weighted |
75 |
Minimum composite score. |
fuciniCoverage.threshold.newCode |
0 |
Minimum line coverage (%) of new code: of the lines changed against gitDiff.baseRef that can be measured, the share that ran. Checked beside the gates on the whole run, which stays on screen — gitDiff.enabled shows the changed lines in its place instead. 0 does not check. |
fuciniCoverage.exclusions.requireReason |
false |
Every exclusion written in the code has to say why: the text after LCOV_EXCL_LINE / LCOV_EXCL_START, or the Justification of [ExcludeFromCodeCoverage]. One that gives no reason is a warning in Problems at its line and fails Check Coverage Thresholds; Exclude Selection from Coverage asks for the reason and writes it. |
fuciniCoverage.threshold.unmeasuredFiles |
-1 |
Maximum number of unmeasured files (see unmeasured.mode, which must be list or count). -1 does not check; 0 fails on the first file no test loaded. |
fuciniCoverage.compliance.mode |
strict |
strict | whm | both | either. |
fuciniCoverage.compliance.diagnostics |
false |
One Problems error per failing threshold, on the tracefile. |
fuciniCoverage.problems.enabled |
false |
One Problems warning per source file below threshold.statement, on that file, with a quick fix that asks the installed chat extension for a test. |
fuciniCoverage.problems.perLine |
off |
off | uncovered | uncoveredAndPartial: an Information entry (a squiggle in the editor) per block of consecutive uncovered lines in each open document, optionally plus one per line with a branch arm never taken. Independent of problems.enabled. Per folder. |
Quick-action menu
Each switch puts one command in the status-bar menu (Show Status Bar Menu).
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.quickActions.toggleWatch |
false |
Offer Toggle Tracefile Watch in the menu. |
fuciniCoverage.quickActions.openAnnotatedSource |
false |
Offer Open Annotated Source in the menu. |
fuciniCoverage.quickActions.openAnnotatedSourceToSide |
false |
Offer Open Annotated Source to the Side in the menu. |
fuciniCoverage.quickActions.toggleAnnotatedSource |
false |
Offer Toggle Annotated Source in the menu. |
fuciniCoverage.quickActions.nextUncovered |
false |
Offer Next Uncovered in the menu. |
fuciniCoverage.quickActions.previousUncovered |
false |
Offer Previous Uncovered in the menu. |
fuciniCoverage.quickActions.worstCoveredFiles |
false |
Offer Show Worst-Covered Files in the menu. |
fuciniCoverage.quickActions.worstCoveredFolders |
false |
Offer Show Worst-Covered Folders in the menu. |
fuciniCoverage.quickActions.worstCoveredFunctions |
false |
Offer Show Worst-Covered Functions in the menu. |
fuciniCoverage.quickActions.copyUncoveredLines |
false |
Offer Copy Uncovered Lines in the menu. |
fuciniCoverage.quickActions.compareHistoryRuns |
false |
Offer Compare Two History Runs in the menu. |
fuciniCoverage.quickActions.compareWithLastRun |
false |
Offer Compare with Last Run in the menu. |
fuciniCoverage.quickActions.checkThresholds |
false |
Offer Check Coverage Thresholds in the menu. |
fuciniCoverage.quickActions.exportJson |
false |
Offer Export Fucini JSON in the menu. |
fuciniCoverage.quickActions.exportReport |
false |
Offer Export Coverage Report in the menu. |
fuciniCoverage.quickActions.chooseGutterMode |
true |
Offer Choose Editor Gutter Mode... in the menu. |
fuciniCoverage.quickActions.toggleInlineHitCounts |
true |
Offer Show/Hide Inline Hit Counts in the menu. |
fuciniCoverage.quickActions.generateBadge |
false |
Offer Generate Coverage Badge in the menu. |
fuciniCoverage.quickActions.showTestCoverage |
false |
Offer Show in Test Coverage View in the menu. |
Diff, remap & reports
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.pathRemap |
[] |
Ordered {from,to} path rewrites; to may use ${workspaceRoot}. When a load leaves files unmatched, the load result offers Apply Suggested Remap with the rule inferred from the unmatched paths and their namesakes in the workspace (also Suggest Path Remap Rule in the palette); nothing is written until you click it. Per folder. |
fuciniCoverage.ignore.generated |
true |
Leave out the files nobody writes and nobody tests: designer files, .g.cs and its kin, assembly attributes, Entity Framework migrations, and any *.generated.* file. Off keeps them in the run. |
fuciniCoverage.ignore.namespaces |
[] |
Namespaces to leave out of every total and every page, for .NET sources: a file whose namespace declaration matches one of these is not counted. A pattern is a namespace prefix with * as a wildcard: Acme.Generated, Acme.*.Migrations, *Tests. Per folder. |
fuciniCoverage.ignore.attributes |
["ExcludeFromCodeCoverage"] |
Attributes that take a .NET class, method or property out of the count, read off the source: the member below one of these is excluded, as LCOV_EXCL_START/STOP would exclude it. Names with or without the Attribute suffix, with or without a namespace. Add GeneratedCode, Obsolete or your own. Per folder. |
fuciniCoverage.testRun.skipWhenTestsFail |
false |
When Run Tests with Coverage ends with a failing test run, leave the coverage unloaded rather than show numbers from a red run. |
fuciniCoverage.testRun.perTest |
false |
Run Tests with Coverage runs the .NET tests one class at a time and tags each result with the class, so the hover, the lens and Show Tests That Ran This Line can say which tests ran a line. Slower: one dotnet test per test class. Per folder. |
fuciniCoverage.testRun.onSave |
false |
Saving a source file runs the tests that cover it, with coverage, and the gutter follows: for a .NET solution the test projects that reference the saved file's project, one dotnet test each; for any other runner the whole recipe. Debounced, and never while a run is already going. Per folder. |
fuciniCoverage.testImpact.baseRef |
HEAD |
The git ref Run Tests Affected by My Changes compares the working tree with: HEAD for what is not committed yet, or a branch such as origin/main for everything the branch changes. |
fuciniCoverage.testResults.enabled |
true |
Read the test results beside the loaded reports and mark the lines a failing test ran: a red bar at the start of the line, a stripe in the overview ruler, and the failing tests in the hover. Needs a report that says which tests ran a line. Run Tests with Coverage asks dotnet test for a test-results.trx in the report's folder. |
fuciniCoverage.testResults.paths |
[] |
Further test results files to read, as paths or globs relative to the workspace folder: TRX or JUnit XML, e.g. build/test-results/**/*.xml. The files beside the loaded reports are read without being named here. |
fuciniCoverage.ignore |
[] |
Glob patterns for whole files to leave out of the run (vendor/**, **/*.generated.ts, build/**) — dropped at load, so every view, gate and export counts without them. Matched against the workspace-relative and the absolute path with forward slashes; a pattern with no / matches a name at any depth, like .gitignore. **, *, ?, {a,b}, [abc]. Per folder. |
fuciniCoverage.unmeasured.mode |
off |
off | list | count: what to do about workspace files of the run's kind that no test loaded (see Unmeasured versus uncovered). list names them in the Coverage view and the load result; count also scores each as an uncovered file of its code lines. Changing it reloads. |
fuciniCoverage.unmeasured.exclude |
["**/*.h", "**/*.hpp", "**/*.hh", "**/*.inl", "**/*.d.ts"] |
Files never called unmeasured, in ignore's glob syntax — header-only and declaration-only files by default. Per folder. |
fuciniCoverage.gitDiff.enabled |
false |
Restrict coverage to lines changed vs a base ref. Per folder. |
fuciniCoverage.gitDiff.baseRef |
origin/master |
Git base ref for diff coverage and the new-code gate: a branch, a commit or a tag (v1.4.0 for everything since that release). Per folder. |
fuciniCoverage.gitDiff.author |
New code by who wrote it: part of an author's name as git has it. Diff coverage and the new-code gate then count the lines git blame gives to that author, instead of the lines changed against gitDiff.baseRef. Lines not committed yet count too. Per folder. |
|
fuciniCoverage.gitDiff.since |
New code by date: a day as YYYY-MM-DD. The lines last changed on that day or later, by git blame; with gitDiff.author, that author's lines since that day. Per folder. |
|
fuciniCoverage.externalHtmlReport.dir |
"" |
Read a pre-built llvm-cov show HTML dir for Annotated Source. Per folder. |
fuciniCoverage.htmlReport.outputDir |
"" |
Output dir for Generate Full HTML Report. Per folder. |
fuciniCoverage.badge.outputPath |
coverage-badge.svg |
Where Generate Coverage Badge writes the SVG — relative to the workspace folder or absolute, same variables as coverageFile.defaultLocation; parent folders are created. Per folder. |
fuciniCoverage.badge.metric |
lines |
lines | branches | mcdc | conditions | weighted: the run-wide number on the badge (weighted is the status bar's composite score). No data for it → a grey n/a. Per folder. |
fuciniCoverage.badge.autoUpdate |
false |
Rewrite the badge after every successful load, silently (logged as [FUCINI_BADGE]). Per folder. |
The badge is a plain file in your repository, so a README embeds it with no service behind it:

Commit the SVG with the code and the picture on the repository page follows
the number in the editor — coloured by the same colorScale.* thresholds, and
kept current by badge.autoUpdate if you would rather not run the command.
Display & analysis
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.mcdcGutter.enabled |
false |
Coloured MC/DC margin gutter. |
fuciniCoverage.statusBar.enabled |
true |
Show run-wide & per-file status-bar items. |
fuciniCoverage.statusBar.showLauncher |
true |
Always-available quick-menu launcher. |
fuciniCoverage.statusBar.showModeToggle |
true |
A status-bar button naming the gutter mode; a click moves it on by one (off → uncovered → coverage → heatmap). |
fuciniCoverage.codeLens.enabled |
true |
Per-function coverage CodeLens. |
fuciniCoverage.deadCode.enabled |
true |
Warn on zero-coverage functions. |
fuciniCoverage.staleWarning.enabled |
true |
Mark files whose contents no longer match their run. |
fuciniCoverage.explorerBadges.enabled |
false |
Coverage badges and hover next to files in the Explorer. Off: the Coverage view lists the same files, and the Explorer stays as its owner left it. |
fuciniCoverage.coverageView.sortBy |
name |
How the Coverage view orders folders and files: name groups folders before files alphabetically; score/lines/branches/mcdc interleave them worst-first by that metric. Also changeable from the view's own toolbar, which writes the choice back here. |
fuciniCoverage.coverageView.perspective |
folders |
Which tree the Coverage view shows: the workspace's folders and files, or — for a .NET report that names its classes (Cobertura, Coverlet, OpenCover) — namespaces, classes and methods, each with its own numbers. The dropdown in the view writes this; offered only when the run carries class names. |
fuciniCoverage.coverageView.hideColumns |
[] |
Metric columns to leave out of the Coverage view's table — any of lines, functions, regions, branches, conditions, mcdc, instantiations, functionCalls, objectCode, mutation, maxCrap. The columns are ordered heaviest-first by your whmWeights, so the metrics that matter most stay visible on a narrow panel; hide the ones you never read to make room for the rest. Anything hidden is still in every row's tooltip, and the composite score is unaffected. Paid editions and student licences; the free tier shows the columns once the panel is wide enough. |
fuciniCoverage.hotspots.crapThreshold |
30 |
A function with this CRAP score or more is listed under Risk hotspots in the Coverage view: complex code with little coverage, where a change is most likely to break something no test will notice. 30 is the figure the metric's authors proposed; 0 turns the CRAP rule off. Community sees the count and the worst score; the ranked list is Pro. |
fuciniCoverage.hotspots.complexityThreshold |
0 |
A function with this cyclomatic complexity or more is listed under Risk hotspots as well, whether or not its tests reach it. 0 lists by CRAP alone. |
fuciniCoverage.coverageView.followActiveEditor |
true |
The Coverage view follows the editor: the file in front is highlighted in the tree, its folders opened and its row scrolled into view. Off leaves the tree where you put it. |
fuciniCoverage.coverageView.mergedMarks |
true |
With several tracefiles loaded: outline the tracefile badge on every file whose numbers overruled an older report, and let a click on a tracefile row highlight the files it measured. Off: plain badges, no highlight; the tracefile rows still count what was overruled. |
fuciniCoverage.sinceLastRun.enabled |
true |
Gutter marks on lines whose coverage changed since the previous run. |
fuciniCoverage.dirtyMarks.enabled |
true |
Grey gutter dot on lines edited since the run was loaded. |
fuciniCoverage.editorGutter.mode |
off |
off | uncovered | coverage | heatmap: nothing beyond the findings above, bars on uncovered (and partially covered) lines only, those plus a green bar on every covered line (red, amber and green in the colour scale's colours), or those plus a warm shade on every executed line by hit count. Per folder. |
fuciniCoverage.lineHighlight |
off |
off | uncovered | coverage | heatmap: a translucent wash behind the line itself, for what the gutter marks. Its own setting, so the gutter can stay clean while the gaps are tinted, or the other way round. Per folder. |
fuciniCoverage.heatmap.colors |
five shades, pale yellow → brown | The heatmap's shades on a light theme, from the line that ran least to the line that ran most (#rgb/#rrggbb). An entry that is not a colour keeps its shipped shade. Set in the settings panel with a swatch per shade and a live preview. Per folder. |
fuciniCoverage.heatmap.colorsDark |
five shades, dim olive → vivid orange | The same for a dark theme, where brighter reads as more. A changed shade is also the colour of that level's line wash. Per folder. |
fuciniCoverage.overviewRuler |
follow |
follow | uncovered | off: whether uncovered lines get a stripe in the scrollbar's overview ruler. uncovered paints it even with the gutter off — a map of the file's gaps down the scrollbar and nothing beside the code; off keeps the bars and drops the stripe. Per folder. |
fuciniCoverage.hitCounts.inline |
false |
×N after every line a statement starts on, ×0 included. Per folder. |
fuciniCoverage.testCoverage.publish |
true |
Publish every loaded run to VS Code's own Test Coverage view (Test Explorer panel, Explorer percentages, Show Inline Coverage). Off: no Fucini Coverage entry in the Test Explorer; Show in Test Coverage View still publishes on request. |
fuciniCoverage.excludeComments |
true |
Exclude comment/blank & LCOV_EXCL_* lines (GCOVR_EXCL_* and GCOV_EXCL_* read the same; LCOV_EXCL_BR_* takes the branches on its lines and leaves the lines counted, LCOV_EXCL_EXCEPTION_BR_* only the arms an exception reaches), and preprocessor directives in C, C++, Objective-C, CUDA and C# — gcov charges a macro's uses to its #define, which made a header of constants read 0% or 100%. Branches and functions wholly on such lines go with them, and a file left with nothing measurable is not shown (named in the output as FUCINI_EXCLUDE_001). |
fuciniCoverage.excluded.includeInTotals |
false |
Count excluded lines toward totals. |
History
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.history.enabled |
false |
Store runs locally for trend & comparison. |
fuciniCoverage.history.retention |
20 |
Max stored runs to keep. |
AI agents
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.mcp.enabled |
false |
Run an MCP server for AI agents on this machine: the gaps of a file or function, the tests that ran a line, the coverage of a diff, what moved since the last run. 127.0.0.1 only, and only for a call that carries your token (Copy MCP Server Configuration). VS Code's own agent has the same tools without it. |
fuciniCoverage.mcp.port |
39217 |
The port it listens on. Where that is taken — a second window — the next free one is used, and Copy MCP Server Configuration says which. |
Status-bar quick actions
All default false except the last two; enable to add the matching action
to the quick menu: quickActions.compareWithLastRun, openAnnotatedSource,
openAnnotatedSourceToSide, toggleAnnotatedSource, toggleWatch,
worstCoveredFiles, worstCoveredFolders, worstCoveredFunctions, nextUncovered,
previousUncovered, checkThresholds, copyUncoveredLines,
compareHistoryRuns, exportJson, exportReport, generateBadge,
showTestCoverage, chooseGutterMode, toggleInlineHitCounts.
Telemetry
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.telemetry.enabled |
false |
Anonymous usage telemetry. Off by default; apart from the one-time install ping and the daily licence message described under Privacy, nothing is sent, and nothing is counted locally, until it is turned on. When on, a summary of each day of use goes out, including which extension settings you changed from their defaults (never paths or other text). The daily summary, the removal notice and the daily licence message are also gated by VS Code's own telemetry.telemetryLevel, which must be all; the install ping is not. Separately, a content-free error report is sent when something fails whenever telemetry.telemetryLevel is all or error, even with this setting off — see Privacy. |
Updates
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.update.checkForUpdates |
true |
Periodically check both fucini.software and the VS Code Marketplace for a newer release, and offer to install whichever is newer. A newer release also shows as a bar at the top of the Coverage view and a badge on its icon until it is installed or skipped; turning this off removes both. |
fuciniCoverage.update.preRelease |
false |
Be offered pre-release builds from fucini.software as well as stable ones. Pre-releases carry new work before it reaches the Marketplace: they are tested, but they are the builds we are least sure of, and you are volunteering to find what we missed. Joining tells us this install is on the pre-release channel, so we know how many testers there are and can say what changed. Join or leave from the Test pre-release builds… button in the Coverage view's licence panel; leaving puts you back on the ordinary path at the next stable release. |
Licensing
| Setting | Default | What it does |
|---|---|---|
fuciniCoverage.license.key |
"" |
Activation key for the evaluation pre-release. Paste the FCK1.… key you receive (see the evaluation note at the top). Bound to this machine/user. |
See the User Guide for worked examples, path-remapping recipes, CI/CMake integration, and the full
FUCINI_*error-code reference.
Privacy
A few small pings go out regardless of this extension's telemetry.enabled setting: one the first
time the extension activates after being installed, and a licence message once
a day from every install. The daily message says which licence the install
holds and how many days its trial or key still runs — its state, and for a key
its plan, request code and expiry; a Community install without a key says only
that it is Community — plus what changed since the last one, when something
did: a set of formats loaded for the first time, a tracefile that would not
parse, a licence change, an update. It carries no usage counts and no settings.
These pings carry a random install id, the extension and VS Code versions and
edition, and the operating system; the install ping adds whether that is the
desktop or the web build, the kind of remote window if there is one (WSL, SSH, a
container — the kind, never an address or a host name), your editor's display
language, the extension host's Node.js version, and how this build was
installed. Which editor you chose and how you have set either telemetry option
are not sent. The server also records the address the ping came from.
Telemetry is deleted within 90 days, with one exception: a single row per install is kept, so that the install history does not vanish every 90 days along with the pings it was counted from. That row holds the install id, the day it first reported, the country, region and city the address resolved to, the versions, edition and operating system, and how the build was installed — not the address itself, and nothing about your work. Ask and it is deleted.
Error reports. When something fails — a tracefile that will not parse or
cannot be read, a remote fetch, path remapping, the unmeasured-file search, the
badge, chat, git diff, and every other FUCINI_* error — a content-free error
report is sent, whatever telemetry.enabled says, as long as VS Code's own
telemetry.telemetryLevel is all or error (never at crash or off). A
source file that no longer matches its coverage (FUCINI_MISMATCH_001 and
_002) is not a failure — editing covered code is normal use — so it is
written to the output channel and marked in the editor, but never reported. A
report carries the error code and where it happened; the error's message with every
path, URL, file name, workspace or folder name, user and computer name, email
address and number longer than six digits replaced by a placeholder such as
<path>; the parser that claimed the file, and — for a file nothing claimed —
how each parser turned it down, as codes; and facts about the file, never its
content: its extension, a size range, its encoding, whether it was gzipped or
binary, the XML root element's name or up to twenty top-level JSON key names
(sanitised the same way), and the shape of its first line with every letter
replaced by a and every digit by 9. Reports are batched — at most one post
every ten minutes and fifty distinct problems a day, each counted rather than
repeated. An install that made the Unlock Full Features trade, or took the
student grant, and accepted the current terms also sends the first 4 KB of a
coverage file that fails to load, as those terms say; no other install ever
sends file content on its own.
Error notifications also offer Send diagnostic report. It opens a preview
of exactly what would be sent — the same content-free facts — with an unticked
option to include the first 4 KB of the file, an optional comment and an
optional reply address, and nothing is sent until you click Send. It works
whatever telemetry.enabled says, but not while telemetry.telemetryLevel is
off: then the preview offers Copy to clipboard instead, for an email to
support@fucini.software.
Your computer name, user name, home directory and network interfaces are never
sent, at any setting, and neither are your project name, your licence key, the
remote.headers you configure or the text of any setting. Your source code and
file paths are not sent either, with two exceptions: the Community tier's
Unlock Full Features trade sends the 4 KB error sample described above, and
a diagnostic report carries the first 4 KB of a file when you tick that box.
That trade used to send one randomly chosen file's annotated source, with its
path, every month; the collection was withdrawn in September 2026 — no install
sends it, nothing new is stored, and what had been kept has been deleted. An
install that made the trade — or took the student grant — under the earlier
terms keeps a monthly summary without settings until it accepts the current
ones; it is asked once.
Checking for updates asks both fucini.software and the VS Code Marketplace for
the newest version, so both see the address the request comes from; turn
update.checkForUpdates off to stop it.
Everything else is controlled by the telemetry.enabled setting above and
is off by default — nothing further is sent, and nothing is counted
locally either, until you turn it on.
With it on, the install ping adds your hardware (CPU and memory), and a
summary of each day the extension was used goes out the day after: how often
coverage was loaded, in which formats, for which languages (by file extension),
how often the same tracefile was reloaded, which commands were run, and which
extension settings you changed from their defaults. A setting that is a switch,
a number, a choice from a list or a colour is sent with its value; one that
holds text, a path or a list — a folder, a glob, a branch or task name, a path
mapping — is sent only as "changed", with the number of entries for a list.
Your licence key and remote.headers are not mentioned at all. A day without
use sends no summary. Coverage loads are counted locally and reported once — a
ping per load meant a ping per save with watch mode on. Uninstalling sends a
removal notice with how many days the extension was installed. The daily
summary, the removal notice and the daily licence message also need VS Code's
own telemetry.telemetryLevel to be all; the install ping does not.
Seat-pool licences (Pro Plus, Business, Enterprise) additionally check in every twelve hours so the seat can be verified and its key renewed; that is licence administration and is not affected by this setting. An individual Pro licence holds no seat and checks in nowhere — it works offline — with one exception: for the seven days before its key expires and the forty-five after, and only then, it asks the licence server every twelve hours for the renewed key, sending its request code, machine fingerprint, extension and VS Code versions, operating system and editor language.
The trial and the Community tier keep a usage ledger with the licence server: a hash of VS Code's machine id — taken together with a fixed label of ours so that it matches no value another product holds, that label being part of the extension rather than a secret — the edition, the day the evaluation began and the counts of the free tier's metered actions, so that a new VS Code profile is neither a new trial nor a new week. No name, email, path or coverage figure. The ledger has no field for the address a report arrives from and never reads it; the web server's access log records it as for any request. A paid licence never reports to it. From 2.2026.2 an install also sends a second hash, made the same way, of the operating system's own machine identifier with the computer's name — or of its network cards' hardware addresses where the system has none — because VS Code's own identifier is a value in a settings file; only the hash leaves the machine, and none is sent from a container. A new install's trial begins with the ledger's first answer, and until then it runs on the Community tier; after that nothing waits for the ledger: offline, the extension runs on its own counters. In a development container or a Codespace no trial begins.
Every install, on every plan, reads the minimum supported version from the
licence server about once a week (from 1.2026.1). It is a download of one
public, signed file and sends nothing: no request code, no install id, no
version, no key — the server cannot tell a paid install from a free one, or
either from a browser. When a build is older than the oldest version still
supported, the trial and the free tier stop and ask for an update, and a paid
licence is told the date and keeps working for a grace period first. An install
that cannot reach the server is never stopped for that reason; a policy it has
already read stays in force offline. This is not an update check and is not
switched off by update.checkForUpdates.
The full list is in the privacy policy, alongside NOTICE.
Contact
Questions, bug reports, or alternate licensing terms — reach Coverage Studio support via the contact page or by email at support@fucini.software.
License
Proprietary — © Fucini Consulting, all rights reserved. You may install and use
the published build within the limits of your plan; you may not copy, modify,
redistribute or reverse engineer it. What the extension produces from your own
data is yours, with no attribution required. See
LICENSE and the
Terms. The open-source
packages bundled into the extension keep their own licences; they are reproduced
in THIRD-PARTY-NOTICES.txt, which ships with it.
















