ПОЛНОЕ РУКОВОДСТВО

Руководство пользователя

От вашего первого файла трассировки до контроля соответствия стандартам безопасности — всё, что умеет Fucini Coverage Studio, и как именно этим управлять.

Скачать VSIX

1. Введение

Fucini Coverage Studio превращает трассировочные файлы покрытия, которые ваша сборка уже создаёт, в полноценное покрытие в редакторе — в VS Code и в Visual Studio. Он читает двадцать пять форматов, нормализует их в одну каноническую модель Fucini, детерминированно объединяет несколько прогонов и показывает всё в самом редакторе — поля, CodeLens, подсказки и окно Covered Files — так что нет отдельного просмотрщика, ради которого надо покидать редактор. Обе редакции делят один читатель: один и тот же отчёт даёт одни и те же числа, до цифры, в обеих. Это руководство написано с точки зрения VS Code; там, где Visual Studio отличается, врезка In Visual Studio говорит как.

Он создан с учётом процессов функциональной безопасности: покрытие MC/DC, покрытие регионов и инстанциаций C++, взвешенная по безопасности комплексная оценка и настраиваемый вердикт COMPLIANT / NON-COMPLIANT, согласованный с подходами IEC 61508, ISO 26262 и DO-178C.

Fucini Coverage Studio in VS Code: a file opened from the Coverage view, uncovered lines and half-taken branches in the editor, the MC/DC hover, the heatmap, the status-bar card and the annotated sourceScreenshot pending — add assets/img/marketplace-demo.gif (see SCREENSHOTS.md).

2. Установка и требования

  • VS Code ≥ 1.88 — минимальная версия, которую поддерживает это расширение — либо Visual Studio 2022 или 2026, Community, Professional или Enterprise (см. врезку ниже).
  • Трассировочный файл покрытия, который ваш проект уже создаёт (см. §4).

Установите из Marketplace или из файла .vsix через Extensions → ⋯ → Install from VSIX…. Расширение активируется при запуске и добавляет значок Coverage на панель активности.

В Visual Studio. Тот же продукт выходит как второе расширение, Fucini Coverage Studio для Visual Studio, для Visual Studio 2022 и 2026 — Community, Professional и Enterprise. Он не использует собственное покрытие кода Visual Studio: в Visual Studio 2022 это функция Enterprise, так что в Community и Professional это единственное покрытие, которое IDE покажет. Установите .vsix, открыв его — VSIX Installer спросит, в какую Visual Studio — или через Extensions → Manage Extensions; расширение загружается при открытии решения или папки. Окну Coverage нужен Microsoft Edge WebView2 Runtime, который Windows 11 и Visual Studio 2026 уже содержат; метки в редакторе работают и без него.

3. Быстрый старт

Хотите увидеть, как это работает, прежде чем подключать собственный проект? Скачайте демонстрационный проект (~6 КБ — крошечный C-проект с готовым lcov.info):

  1. Распакуйте его в любое место, затем откройте coverage-studio-demo.code-workspace в VS Code (File → Open Workspace from File…).
  2. Установите Fucini Coverage Studio, если ещё не сделали этого (см. §2 выше).
  3. Собственный .vscode/settings.json рабочей области уже указывает на встроенный coverage/lcov.info, поэтому полосы покрытия и представление Covered Files появляются сразу после активации расширения — шаг Load Coverage не требуется.
  4. Хотите перегенерировать файл трассировки самостоятельно (например, другим компилятором)? Запустите scripts/gen-coverage.sh (или .ps1 в Windows) — доступные цели компиляторов смотрите в собственном README.md рабочей области.

В Visual Studio. Откройте решение или папку (File → Open → Folder — так открывают проект CMake) — демо выше открывается как папка, и его .vscode/settings.json читается и там. Отчёт в месте, которое называют настройки, загружается сам, а панель Fucini Coverage окна Output говорит, какие файлы; иначе — Tools → Coverage Studio → Load Coverage File…, один раз — это запоминается. Откройте исходный файл и выберите Show Coverage in Editor: красное — строка, которая никогда не выполнялась, янтарное — решение, принятое только в одну сторону, а Ctrl+Shift+U, N переходит к следующей непокрытой строке. View → Other Windows → Coverage — это дерево с числами каждого файла и одним баллом по всем, который строка состояния показывает для прогона и для файла, в котором вы находитесь. Tools → Coverage Studio → Getting Started открывает эти шаги как страницу с кнопкой для каждого; в первый раз она открывается сама.

  1. Создайте файл трассировки покрытия с помощью обычного тестового процесса.
  2. Command Palette (Ctrl+Shift+P) → Fucini Coverage: Load Coverage. При включённом watch.enabled (по умолчанию) подходящий файл загружается автоматически.
  3. Откройте покрытый исходный файл: строка состояния показывает его процент, заполняется представление «Покрытие», наведение на строку показывает число её попаданий, а прогон появляется в нативной панели Test Coverage. Полосы на непокрытых строках, тепловая карта попаданий и встроенные числа ×N включаются одной настройкой (editorGutter.mode, hitCounts.inline).
Covered (green) and uncovered (red) gutters in the editorScreenshot pending — add assets/img/gutters.png (see SCREENSHOTS.md).

4. Создание файлов трассировки

Любой из следующих форматов автоматически определяется при загрузке:

Полные рецепты «скопировать-вставить» для каждого компилятора — включая Swift, Rust, Go, Python, .NET, PHP и другие — на странице Рецепты покрытия: Swift · Clang · GCC · Rust · Go · Python · Java/Kotlin · JS/TS · .NET · PHP · Ruby.

C / C++ — LLVM (llvm-cov)

clang --coverage -fprofile-instr-generate -fcoverage-mapping test.c -o test
LLVM_PROFILE_FILE=test.profraw ./test
llvm-profdata merge -sparse test.profraw -o test.profdata
llvm-cov export ./test -instr-profile=test.profdata --format=json > coverage.json

llvm-cov JSON — самый богатый вход: помимо строк, ветвей и функций он несёт регионы, MC/DC и инстанциации шаблонов C++.

C / C++ — GCC (gcov → lcov)

gcc --coverage calc.c calc_test.c -o test && ./test
lcov --capture --directory . --output-file lcov.info
# or gcovr --cobertura coverage.xml
# or skip the conversion entirely: gcovr --json coverage.json

JavaScript / TypeScript — Istanbul / nyc

nyc --reporter=json mocha        # emits coverage/coverage-final.json

Java — JaCoCo

mvn test        # emits target/site/jacoco/jacoco.xml

Ruby — SimpleCov

# .resultset.json (built-in) or coverage.json via the simplecov-json gem

Go

go test ./... -coverprofile=coverage.out    # read directly, no conversion

Python — coverage.py

coverage run --branch -m pytest && coverage json    # emits coverage.json

PHP — PHPUnit (Clover)

XDEBUG_MODE=coverage phpunit --coverage-clover clover.xml

.NET / C# — Coverlet (OpenCover)

dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover
ФорматТипичный файлДополнительные метрики
LCOVlcov.infoстроки, ветви, функции
llvm-cov JSONcoverage.json+ регионы, MC/DC, инстанцирования
Cobertura XMLcoverage.xmlстроки, ветви
JaCoCo XMLjacoco.xmlстроки, ветви, функции
Istanbul / nyccoverage-final.jsonинструкции, ветви, функции
SimpleCov.resultset.jsonстроки
Clover XMLclover.xmlстроки, ветви, функции
OpenCover XMLcoverage.opencover.xmlстроки, ветви, функции
coverage.py JSONcoverage.jsonстроки, ветви
gcov / gcovr JSONcoverage.jsonстроки, ветви, функции
Go coverage profilecoverage.outстроки
SonarQube genericcoverage.xmlстроки, ветви
GNATcoverage XML (Ada)*.xmlстроки, ветви, MC/DC
BullseyeCoverage XMLcoverage.xmlстроки, условие/решение
Visual Studio*.coveragexmlстроки, ветви
Coveralls JSONcoveralls.jsonстроки, ветви
Xcode xccov JSONcoverage.jsonфункции и итоги по файлам — без данных по строкам
gcov plain text*.gcovстроки, ветви, вызовы функций
VectorCASTcoverage.xmlстроки, ветви, пары MC/DC, функции, вызовы функций
Testwell CTC++*.txtстроки, условие/решение
Lauterbach TRACE32*.xmlоператоры, решения, условия, MC/DC, функции, вызовы, объектный код
EMMA XML (Java)coverage.xmlстроки
UCIS / Aldec UCDB*.xmlоператоры, ветви решений
NCover XMLCoverage.xmlстроки, функции
dotCover DetailedXMLdotCover.xmlстроки, функции
PIT (mutation)mutations.xmlоценка мутаций — без покрытия
Stryker (mutation)mutation-report.jsonоценка мутаций — без покрытия

Нативный проект C++, измеренный из Visual Studio

В Visual Studio. Собственное покрытие проекта C++ в Visual Studio было только в Enterprise, а бесплатный плагин OpenCppCoverage, закрывавший этот пробел, снят с поддержки. Tools → Coverage Studio → Run Native Project with Coverage берёт стартовый проект — .vcxproj — спрашивает у MSBuild, куда он собирается и на каком наборе инструментов, и измеряет его одним из двух способов. На наборе инструментов ClangCL, с llvm-profdata и llvm-cov из LLVM на машине (их приносит компонент Visual Studio C++ Clang tools for Windows), он собирает проект заново в coverage\native\ с -fprofile-instr-generate -fcoverage-mapping, через props-файл, который MSBuild велено импортировать первым — в проекте ничего не меняется, — запускает программу с заданным LLVM_PROFILE_FILE, объединяет профиль и экспортирует coverage\native\lcov.info, с точностью до региона. На любом другом наборе инструментов, с установленным OpenCppCoverage, он собирает проект как есть — у сборки Debug есть PDB — и запускает его под OpenCppCoverage, который пишет coverage\native\opencppcoverage.xml. Каждая команда попадает в панель Fucini Coverage, а отчёт загружается и запоминается, как отчёт тестового прогона. Проекту, который нельзя измерить, сообщается почему и что установить. MSBuild 17.8 (Visual Studio 2022 17.8) или новее — ради вопроса, который он задаёт.

Visual Studio: Run Native Project with Coverage on a C++ project — the instrumented build, the run, and the gutter filling inScreenshot pending — add assets/img/vs-native-run.gif (see SCREENSHOTS.md).
В Visual Studio: Run Native Project with Coverage на проекте C++ — сборка, запуск и заполняющийся редактор.

5. Загрузка и обнаружение

По умолчанию расширение сканирует от корня рабочего пространства. Уточните это двумя настройками:

{
  "fuciniCoverage.coverageFile.defaultLocation": "${workspaceFolder}/build",
  "fuciniCoverage.coverageFile.paths": [
    "coverage.json",
    "reports/*.info",
    "${env:CI_ARTIFACTS}/lcov.info",
    "https://ci.example.com/artifacts/latest/coverage.xml"
  ]
}
  • Каждая запись paths указывается относительно defaultLocation (или корня рабочего пространства, если не задано).
  • Запись, содержащая *, ищется рекурсивно; простое имя файла соответствует только напрямую в базовой папке.
  • Когда paths пуст, расширение ищет coverage.json, затем lcov.info, загружая то, что найдёт первым.
  • Pick Coverage File использует выбранные вами файлы вместо этих путей — каждый выбор добавляется к набору, и все они объединяются — пока вы их не выгрузите или не измените эти параметры.
  • Переменные — каждая настройка путей раскрывает ${workspaceFolder}, ${workspaceFolderBasename}, ${userHome}, ${env:NAME}, ${command:id} и ${config:section.key}. Переменная, которую расширение не знает, остаётся как записана, так что опечатка появляется в журнале обнаружения как путь, по которому искали, а не исчезает бесследно. ${command:id} сначала выполняет эту команду — только в доверенной рабочей области и не дольше пяти секунд, — поэтому ${command:cmake.buildDirectory} следует за выбранным набором CMake.
  • Записи-URL — запись http(s):// скачивается в хранилище расширения при каждой загрузке и загружается оттуда; представление «Покрытие» называет URL. Каждое скачивание условное (ETag / Last-Modified), так что неизменившийся артефакт ничего не передаёт, а когда скачивание не удаётся, вместо него загружается последняя скачанная копия, с причиной в выводе. remote.headers добавляет заголовки Name: value с раскрытием переменных, remote.refreshMinutes скачивает заново по таймеру, а фрагмент #lcov.info называет формат, когда сам URL не заканчивается именем файла.
  • Glob-шаблоны исключения — fuciniCoverage.ignore отбрасывает целые файлы (вендорные, сгенерированные, сторонние) из прогона при его загрузке, так что каждое представление, каждый барьер и каждый экспорт считают без них; результат загрузки говорит, сколько исключено, а вывод их называет. Шаблоны сопоставляются с путём относительно рабочей области и с абсолютным путём, а шаблон без / соответствует имени папки или файла на любой глубине, как в .gitignore.
  • Многокорневые рабочие области — каждая папка обнаруживается на собственных условиях: defaultLocation, paths, pathRemap, ignore, watch.* и remote.* читаются для каждой папки отдельно, так что действует собственный .vscode/settings.json папки или её запись в .code-workspace, а ${workspaceFolder} в записи означает папку самой записи. Файлы трассировки всех папок сливаются в один прогон, представление «Покрытие» группирует файлы по папкам, а папка, добавленная в рабочую область или удалённая из неё, сама перезагружает покрытие.
  • Перезагрузка после задачи — watch.reloadAfterTasks называет задачи (label из tasks.json или source: name, как показывает список Run Task), завершение которых перезагружает покрытие, независимо от того, включён ли watch.enabled, — для сборок, за которыми наблюдателю не уследить. Абсолютная запись paths вне рабочей области тоже опрашивается, так что появившийся там файл загружается.
  • Ход выполнения с возможностью отмены — загрузка десяти и более файлов трассировки, или 8 МБ и более, идёт под уведомлением о ходе выполнения с кнопкой «Отмена», которое называет читаемый файл и фазу; меньшая лишь крутит индикатор в строке состояния. Отмена оставляет предыдущий прогон загруженным и сообщает об этом.

Начиная с 1.2026.6 абсолютная запись тоже может содержать подстановочный знак: ${workspaceFolder}/build/coverage-merged/*.lcov.info загружает каждый такой файл в этой папке, а ** означает любую глубину. Поиск идёт в папках перед первым подстановочным знаком, и за ними ведётся наблюдение, так что отчёт, который сборка туда записывает, загружается сам. Редакция для Visual Studio читает такую запись так же, а начиная с 1.2026.5 относительный подстановочный знак и там ищется на любой глубине, как здесь было всегда.

Если paths не задан, загружается первый из общеизвестных файлов отчётов, найденный в базовой папке. Сначала четыре имени, которые расширение искало всегда, — coverage.json, lcov.info, coverage.out, clover.xml, — затем, начиная с 0.2026.83, jacoco.xml, coverage.xml, cobertura.xml, coverage.cobertura.xml, coverage.opencover.xml и coverage-final.json, а после них — места, куда пишут конфигурации по умолчанию: coverage/ (nyc, Jest, vitest, PHPUnit, SimpleCov), target/site/jacoco/jacoco.xml (Maven), build/reports/jacoco/test/jacocoTestReport.xml (Gradle) и TestResults/*/coverage.cobertura.xml (dotnet test). Поэтому проект на Java или .NET показывает своё покрытие при первом же открытии, как это всегда было с проектами lcov.

Начиная с 1.2026.3 решению .NET нужно ещё меньше: папка TestResults/<run>/ каждого тестового проекта входит в число известных мест, а не только папка самой рабочей области, и <user>_<machine>_<date>.cobertura.xml сборщика Microsoft находится рядом с coverage.cobertura.xml от Coverlet — так что решение, тесты которого были запущены в Test Explorer Visual Studio, показывает своё покрытие при первом открытии, каждая папка запуска загружается, а слияние оставляет за самым новым запуском последнее слово по каждому файлу. А fuciniCoverage.ignore.generated, включённый по умолчанию, исключает файлы дизайнера и сгенерированные файлы — *.Designer.cs, *.g.cs, *.generated.cs, *.razor.g.cs, AssemblyInfo, глобальные using, Migrations/ и obj/ — из каждого итога, страницы и экспорта ещё до применения ignore, так что сгенерированная таблица, которую никто не тестирует, не лишает решение прохождения порога.

В Visual Studio: покрытие после каждого запуска тестов. Запускайте тесты как обычно. Tools → Coverage Studio → Collect Coverage in Test Explorer… кладёт рядом с решением один файл .runsettings со сборщиком Cobertura от Microsoft. Visual Studio подхватывает его, когда включён параметр Tools → Options → Test → Auto detect runsettings files; иначе выберите его один раз в Test → Configure Run Settings. С этого момента каждый запуск записывает отчёт в папку тестового проекта, а слежение за решением загружает его, как только он появляется, объединив со всем, что называют настройки — одинаково из Test Explorer, из терминала или из скрипта. Ничто не встраивается в Test Explorer, поэтому Visual Studio 2026 и Microsoft.Testing.Platform работают как есть. Run Tests with Coverage — это команда VS Code: она распознаёт dotnet test, vitest, jest, pytest, go, cargo, maven, gradle, phpunit и ctest, запускает тесты с включённым покрытием в окне Output и загружает то, что они записали; тестовый проект без coverlet.collector запускается со сборщиком, который поставляется с тестовым SDK, а testRun.skipWhenTestsFail оставляет на экране последнее хорошее покрытие, когда запуск завершается красным.

Артефакт CI за токеном, перепроверяемый каждые четверть часа — токен остаётся в окружении и никогда не попадает в settings.json:

{
  "fuciniCoverage.coverageFile.paths": [
    "https://ci.example.com/api/builds/latest/artifacts/coverage#lcov.info"
  ],
  "fuciniCoverage.remote.headers": ["Authorization: Bearer ${env:CI_TOKEN}"],
  "fuciniCoverage.remote.refreshMinutes": 15
}
A tracefile loaded from a URL: the Coverage view names the address it came fromScreenshot pending — add assets/img/remote-tracefile.png (see SCREENSHOTS.md).

Перезагрузка намеренно молчалива — диалог после каждой сборки это вопрос с одним ответом. Не молчит значок Coverage: пока представление не перед вами, он несёт число заново прочитанных трассировочных файлов, а всплывающая подсказка называет их. Откройте представление — и счётчик исчезает.

A build rewrites two reports while the Explorer is open: the Coverage icon in the activity bar gains a count, its tooltip names what reloaded, and opening the view clears itScreenshot pending — add assets/img/coverage-badge.gif (see SCREENSHOTS.md).

Файлы, которые никогда не должны учитываться, что бы ни говорил о них файл трассировки:

{
  "fuciniCoverage.ignore": ["vendor/**", "**/*.generated.ts", "build/**"]
}

Перезагрузка по завершении тестовой задачи, для сборки, за которой наблюдателю файлов не уследить — прогон, заменяющий выходное дерево по символической ссылке, отчёт на сетевом ресурсе, шаг покрытия, дообрабатывающий свой файл:

{
  "fuciniCoverage.watch.reloadAfterTasks": ["npm: test", "coverage-report"]
}

6. Объединение прогонов

Каждая существующая запись paths загружается и сливается в один запуск. Слияние детерминировано — результат не зависит от порядка загрузки — а файл, измеренный более чем одним файлом трассировки, берёт свои числа из того, у которого самое новое время изменения на диске: собственный повторный запуск модуля заменяет то, что о нём говорил более широкий, старый запуск, а не добавляется к нему. Представление Покрытие показывает, что это сделало: каждая строка файла называет файл трассировки, из которого взяты её числа, значок обведён там, где эти числа перекрыли более старый отчёт, каждая строка файла трассировки над деревом считает, сколько её файлов перекрыто или перекрыло другие, а щелчок по строке файла трассировки подсвечивает её файлы в дереве, пока по ней не щёлкнут снова (coverageView.mergedMarks отключает обводку и подсветку).

Four reports merged onto one source file: llvm-cov brings lines, functions, regions, branches, MC/DC and instantiations, two TRACE32 exports bring function calls and object code, and a mutation report brings the mutation scoreScreenshot pending — add assets/img/four-reports-one-file.svg (see SCREENSHOTS.md).

Объединённый прогон может содержать больше шести классических метрик, причём каждая берётся из того отчёта, который её измеряет: вызовы функций, объектный код и мутационная оценка наряду со строками, функциями, регионами, ветвями, MC/DC и инстанцированиями. Представление Coverage показывает столбец для каждой метрики, которую содержит прогон, — и ни одного для той, которой в нём нет, — упорядоченные по весу каждой метрики в вашей оценке, самая весомая первой, при любой ширине — то, что не помещается, прокручивается вбок, а не исчезает. Перетащите представление в нижнюю панель, и оно превратится в таблицу. Карточки в строке состояния, HTML-отчёт и экспорт в CSV и Markdown следуют тому же правилу: то, что отчёт содержит, показывается, а то, чего в нём нет, опускается, а не выводится как ноль.

The Coverage view in a real window: eight tracefiles from five languages above a tree with MC/DC, Conditions, Branches, Regions and the weighted score in columns beside every file, and a covered C# file open next to itScreenshot pending — add assets/img/coverage-view-editor.png (see SCREENSHOTS.md).
The Coverage view as a wide panel: C, C++, JavaScript, Python and .NET merged into one tree, with MC/DC, branches, regions, functions, lines, instantiations, mutation score, Max CRAP and the weighted score in columns beside every file, and an em dash where a metric does not applyScreenshot pending — add assets/img/coverage-view-columns.png (see SCREENSHOTS.md).
The Coverage view over a workspace of eleven projects: every tracefile listed, the tree collapsed to one row per project with its percentage and bar, scrolled through, expanded, and a file opened from itScreenshot pending — add assets/img/workspace-overview.gif (see SCREENSHOTS.md).
Visual Studio: the Coverage window over a folder of eleven projects — collapsed to one row per project with its bar, then expanded, then a file opened from itScreenshot pending — add assets/img/vs-workspace-overview.gif (see SCREENSHOTS.md).
В Visual Studio: окно Coverage над папкой с одиннадцатью проектами — свёрнутое до одной строки на проект с его полосой, затем развёрнутое, затем файл, открытый из него.

Из какого отчёта берётся файл

Когда несколько загруженных отчётов измеряют один и тот же файл, самый новый забирает его целиком — это верно, когда собственный повторный прогон модуля заменяет то, что говорил более старый и широкий прогон. И неверно, когда отчёты измеряют один и тот же код намеренно: тогда редактор перескакивает между ними с каждой сборкой, и каждый рассказывает лишь часть истории. coverageFile.sources говорит, что делать вместо этого, для каждого файла отдельно. Это нужно в трёх ситуациях:

  • Один отчёт лучше. coverage.json от llvm-cov содержит MC/DC, а gcov.info от GCC для той же сборки — только строки и ветви: выберите coverage.json, а другой оставьте для тех немногих файлов, которые собирает только GCC.
  • Несколько наборов тестов одной сборки. Модульные тесты, интеграционные тесты и hardware-in-the-loop пишут каждый свой отчёт; каждый по отдельности упускает строки, которые выполнили другие. Объединить, union: строка покрыта там, где её выполнил любой отчёт, а счётчики складываются.
  • Одна кодовая база, два компилятора. Одни и те же исходники, собранные clang и GCC, идут разными путями через код, зависящий от инструментария, — здесь intrinsic, там переносимый запасной вариант, — и каждый отчёт говорит, что другая половина ни разу не выполнялась. Объединить, agree: строка покрыта, только если её выполнил каждый отчёт, который её измеряет, а строки, в которых отчёты расходятся, отмечены бирюзовым. Именно этот вопрос задаёт проверка между компиляторами: что на самом деле проверила каждая сборка, которую я поставляю?

Правила редко приходится писать вручную. Щёлкните файл правой кнопкой в представлении Покрытие и выберите Choose Coverage Source... (или выполните Choose Coverage Source for This File... для открытого файла): в списке — каждый отчёт, измеряющий файл, каждый со своими строками, ветвями и MC/DC, а затем Combine them — agree и Combine them — union; выберите вариант и укажите, действует ли он для файла, его папки или шаблона. Clear Coverage Source for This File... снова убирает правило. В настройках каждое правило — это glob-шаблон по файлам, glob-шаблон по отчёту (шаблон без / совпадает с именем файла отчёта, {a,b} называет несколько) и, для объединения, "combine": "agree" или "union". Решает первое правило, чьи файлы совпадают и чей отчёт измеряет файл; файл, который измеряет только один отчёт, никогда у него не отбирается. Ветви, MC/DC и регионы объединённого файла берутся из отчёта, в котором их больше всего. coverageFile.mergeStrategy: "order" решает файлы, для которых нет правила, по порядку в coverageFile.paths, а не по времени.

{
  "fuciniCoverage.coverageFile.paths": ["clang.json", "gcc/bits.gcov.json.gz"],
  "fuciniCoverage.coverageFile.sources": [
    { "files": "src/**", "report": "{clang.json,bits.gcov.json.gz}", "combine": "agree" }
  ]
}

Что вы видите: строка файла в представлении Покрытие называет отчёт, из которого взяты её числа, или, при объединении, каждый отчёт и режим с ≠ n для спорных строк. Поле редактора и Annotated Source отмечают эти строки бирюзовым и сообщают, какой отчёт сколько раз их выполнил. Всплывающая подсказка строки состояния называет источник открытого файла, а для всего прогона — действующие правила и то, как решены остальные файлы. samples/c-cross-check из демо — это случай двух компиляторов, готовый к открытию. Visual Studio тоже всё это умеет: Choose Coverage Source... на строке представления Покрытие, а также Choose и Clear Coverage Source for This File... в Tools > Coverage Studio.

Выбор и объединение источников входят в платные планы. В Community самый новый отчёт побеждает в каждом файле, как и без правила, а о правиле в настройках сообщается, что оно не используется, вместо того чтобы молча его игнорировать.

Choose Coverage Source on bits.c: clang.json with lines, branches and MC/DC, bits.gcov.json.gz with lines and branches, and the two ways to combine them
В VS Code: Choose Coverage Source... для bits.c из представления Покрытие — отчёт clang, отчёт GCC или оба вместе.
Animation in VS Code: bits.c combined by agree with four teal lines, Choose Coverage Source opened from the Coverage view, and the file reloaded combined by union
В VS Code: от agree к union в два щелчка — спорные строки остаются бирюзовыми, числа меняются.
Animation in Visual Studio: bits.c combined by agree, the Coverage window's row menu, Choose Coverage Source and its two dialogs, and the file reloaded combined by union
В Visual Studio: то же из меню строки представления Покрытие — отчёты, затем докуда распространяется правило, затем снова загруженный файл.
bits.c with the clang and GCC reports combined by agree: the Coverage view row names both reports with four disputed lines, and the builtin and the portable loop are marked in teal
В VS Code: bits.c, clang и GCC, объединённые по agree, — строка показывает ≠ 4, а четыре строки, которые выполнила только одна сборка, бирюзовые.
bits.c with the clang and GCC reports combined by union: every line covered, the four disputed lines still teal, and the status bar naming the union
Тот же файл, объединённый по union: покрыта каждая строка, которую выполнила любая из сборок, поэтому bits.c достигает 100% своих строк — а строки, в которых сборки расходятся, остаются бирюзовыми.
The Annotated Source of bits.c, combined by agree: the source line under the title names both reports, and each teal row says how often each report ran it
Annotated Source называет объединение под заголовком, а у каждой бирюзовой строки — сколько раз её выполнил каждый отчёт.
The status bar's hover card for bits.c, ending with the coverage source: both reports, combined by agree, disagreeing on four lines
Всплывающая подсказка файла в строке состояния сообщает, откуда взяты его числа; подсказка прогона подсчитывает файлы, решённые каждым правилом.
Visual Studio: Choose Coverage Source on bits.c from the Coverage window's row menu — the clang report, the GCC report and the two ways to combine them; behind it the row's agree badge with four disputed lines and the teal lines in the margin
В Visual Studio: Choose Coverage Source... из строки представления Покрытие — те же отчёты и те же два способа их объединить; строка состояния называет объединение.

7. Переотображение путей

CI часто записывает абсолютные пути сборочной машины, которых нет на вашей рабочей станции. Перепишите их упорядоченными правилами:

{
  "fuciniCoverage.pathRemap": [
    { "from": "/builds/ci/project", "to": "${workspaceFolder}" },
    { "from": "\\\\", "to": "/" }
  ]
}

Правила применяются по порядку; для каждого префикса побеждает первое совпадение. Используйте это для сглаживания различий разделителей Windows/Unix или сопоставления пути контейнера с вашим чекаутом.

Правило редко приходится писать самому. Самое частое «загрузилось, но ничего не подсвечивается» — файл трассировки, чьи пути пришли из контейнера или чекаута CI (/build/src/a.c, /home/runner/work/repo/repo/src/a.c, C:\agent\_work\1\s\src\a.c), тогда как в рабочей области лежит src/a.c. Когда после загрузки остаются несопоставленные файлы, расширение ищет их имена в рабочей области, выводит правило префикса, объясняющее большинство из них, записывает его в журнал как [FUCINI_REMAP_002], а результат загрузки предлагает Apply Suggested Remap — один щелчок добавляет правило в fuciniCoverage.pathRemap и перезагружает. Suggest Path Remap Rule выполняет тот же анализ по запросу из палитры. Без щелчка ничего не записывается.

В to можно использовать ${workspaceRoot}, обозначающий папку рабочей области, в которой лежит файл трассировки; в многокорневой рабочей области сначала пробуется эта папка, затем каждая другая папка, в которой файл действительно есть. Сами правила читаются для каждой папки отдельно, так что каждый корень многокорневой рабочей области может нести свои.

8. Покрытие в редакторе

  • Индикаторы и линейка — по умолчанию собственное поле редактора показывает только находки: полосы MC/DC, треугольники «с последнего прогона», точки изменённых строк. editorGutter.mode добавляет полосы только на непокрытых строках или тепловую карту попаданий (ниже); цветное представление всего файла — это Annotated Source.
  • CodeLens функции — % покрытия над каждой функцией; нажмите, чтобы перейти к её первой непокрытой строке. Переключается через codeLens.enabled.
  • Наведение на ветвь и MC/DC — наведите на строку решения для количества пройденных/непройденных ветвей и таблицы вектора по условию. Включите цветную полосу поля через mcdcGutter.enabled.
  • Предупреждения о мёртвом коде — функции с измеримыми строками и нулевым покрытием отмечаются на панели Problems (deadCode.enabled).
  • Next / Previous Uncovered — переход между пропусками.
  • Строка состояния — элемент $(dashboard) NN% для всего прогона и элемент для каждого файла, оба окрашены в соответствии с комплексной оценкой; наведите для полной таблицы метрик.
  • Значки в Проводнике — значки покрытия для файлов и папок, с разбивкой по метрикам при наведении. По умолчанию выключены: включите их параметром explorerBadges.enabled.
  • С последнего прогона — красный треугольник на каждой строке, выполненной в предыдущем прогоне и не выполненной в этом, зелёный для обратного случая, с обзорной линейкой и числом попаданий при наведении.
  • Изменённые буферы — каждая отметка, подсказка и CodeLens движутся вместе с текстом при редактировании и снимаются с переписанной строки, где серая точка объясняет почему.
  • Встроенные числа попаданий — hitCounts.inline пишет ×12 после каждой строки, на которой начинается оператор, включая ×0: число, которое всегда было в подсказке при наведении, теперь без наведения. Toggle Inline Hit Counts переключает его; меню строки состояния предлагает его как «Показать» или «Скрыть», смотря что применимо.
  • Записи в Problems на каждую строку — problems.perLine ставит диагностику уровня Information, с волнистой линией в редакторе, на каждый непрерывный блок строк, которые ни разу не выполнялись в открытом документе (мёртвая функция в сорок строк — одна запись, а не сорок), а в режиме uncoveredAndPartial — и на каждую строку, одна из ветвей которой ни разу не была пройдена. Записи следуют за своими строками при правке и исчезают при закрытии документа; запись на файл за problems.enabled не изменилась.
  • Подсветка строки — lineHighlight кладёт полупрозрачный фон за сами строки, для того, что отмечает поле (uncovered или heatmap — и для выполненных строк). Отдельная настройка, чтобы поле оставалось чистым, пока пробелы подкрашены, или наоборот.
  • Обзорная линейка — overviewRuler решает насчёт полосы на полосе прокрутки независимо от поля: follow (вместе со столбиками, как раньше), uncovered (всегда, так что поле может быть выключено, а полоса прокрутки по-прежнему показывает карту пробелов) или off.
  • Какой файл трассировки покрыл строку — когда объединено несколько отчётов, подсказка со счётчиком выполнений заканчивается словом Покрыто: и каждым отчётом, покрывшим эту строку, включая имя набора из TN: в lcov; щелчок по строке файла трассировки в представлении Coverage подсвечивает внесённые им строки во всех открытых редакторах.
Function CodeLens and MC/DC hover tooltipScreenshot pending — add assets/img/codelens-hover.png (see SCREENSHOTS.md).
A function CodeLens on a C# method: 37.14% covered, 13 of 35 lines, branches 10 of 22, complexity 22, CRAP 142.2 — with the uncovered branches of the method marked underneathScreenshot pending — add assets/img/codelens-crap.png (see SCREENSHOTS.md).
Alt+N jumps from one uncovered line to the next, Alt+P goes backScreenshot pending — add assets/img/jump-between-gaps.gif (see SCREENSHOTS.md).
Explorer badges: a coverage percentage on every measured file and folder of all five demo projects, beside a covered C# file with its gutters, hit counts and CodeLensScreenshot pending — add assets/img/explorer-badges.png (see SCREENSHOTS.md).

Режимы поля редактора

editorGutter.mode решает, что собственное поле редактора показывает о покрытии поверх находок, которые оно показывает всегда. off (по умолчанию) — только они. uncovered ставит полосу на каждую строку, которая ни разу не выполнялась, и янтарную — на каждую строку, где одна из ветвей ни разу не была пройдена, и ничего на покрытых строках, так что зелёная стена не прячет три красные. coverage добавляет зелёную полосу на каждую покрытую строку, так что весь файл читается красным, янтарным и зелёным в цветах цветовой шкалы. heatmap вместо этого добавляет тёплый оттенок на каждую выполненную строку — тем горячее, чем чаще она выполнялась; он показывает, как часто строка выполнялась, а не покрыта ли она, поэтому файл со 100% там от жёлтого до коричневого, но никогда не зелёный. Choose Editor Gutter Mode... выбирает режим из палитры или меню строки состояния; кнопка режима справа в строке состояния или Alt+G одним нажатием переходит к следующему (statusBar.showModeToggle скрывает кнопку). Оба записывают настройку. Пример включает полосы на непокрытых строках и встроенные числа вместе:

{
  "fuciniCoverage.editorGutter.mode": "uncovered",
  "fuciniCoverage.hitCounts.inline": true
}
Uncovered mode: a red bar and wash on lines that never ran, amber on a line with a branch arm never taken, and the execution count written after each lineScreenshot pending — add assets/img/gutters.png (see SCREENSHOTS.md).
The coverage mode in VS Code: green bars on lines that ran in full, amber where a branch arm never did, red where nothing ran; the status bar names lcov.info as the report and the mode button reads Coverage
В VS Code: режим coverage — зелёный там, где строка выполнилась полностью, янтарный там, где одна из ветвей ни разу не была пройдена, красный там, где ничего не выполнялось. Элемент файла в строке состояния называет отчёт, из которого взяты его числа; кнопка режима рядом показывает Coverage.
Alt+G pressed four times: heatmap, off, uncovered and back to coverage
В VS Code: Alt+G четыре раза — heatmap, off, uncovered и снова coverage.
Visual Studio: Gutter: Coverage on Quote.cs, green, amber and red bars beside the hit counts
В Visual Studio: Gutter: Coverage (Red, Amber, Green) в Quote.cs.

Тепловая карта строится относительно файла. Оттенок строки сравнивает её счётчик с самой горячей строкой того же файла по логарифмической шкале в пять ступеней — поэтому тело цикла, выполненное тысячу раз, и функция, выполненная дважды, видны оба, и одно не заглушает другое, а файл, в котором всё выполнилось ровно один раз, выглядит равномерно тёплым, а не равномерно бледным. В тёмной теме шкала идёт от тусклого оливкового к яркому оранжевому, в светлой — от бледно-жёлтого к коричневому: в обоих случаях больше цвета означает больше выполнений, а красный остаётся зарезервированным для строк, которые не выполнялись ни разу. lineHighlight: "heatmap" кладёт те же оттенки под код в виде заливки, а hitCounts.inline выводит точное значение счётчика после строки.

{
  "fuciniCoverage.editorGutter.mode": "heatmap",
  "fuciniCoverage.lineHighlight": "heatmap",
  "fuciniCoverage.hitCounts.inline": true
}
The heatmap on a dark theme: functions that ran once in dim olive, the decision that ran five times in vivid orange, lines that never ran in redScreenshot pending — add assets/img/heatmap-dark.png (see SCREENSHOTS.md).
The same file on a light theme: pale yellow for lines that ran once, brown for the hottestScreenshot pending — add assets/img/heatmap-light.png (see SCREENSHOTS.md).
Switching from uncovered mode to the heatmap, on a dark and then on a light themeScreenshot pending — add assets/img/heatmap.gif (see SCREENSHOTS.md).

Цвета — ваши. colorScale.lowColor, midColor и highColor задают красный, янтарный и зелёный в поле, в заливках и в каждом проценте; heatmap.colors и heatmap.colorsDark содержат пять оттенков тепловой карты для светлой и тёмной темы, от строки, выполнявшейся реже всего, до выполнявшейся чаще всего. Страница настроек показывает результат ещё до сохранения: в разделе Colour scale предпросмотр остаётся на виду, пока строки прокручиваются мимо, — градиент с двумя опорными точками, несколько процентов в их цветах и маленькие светлый и тёмный редакторы, окрашенные в режиме coverage и тепловой картой, — и перерисовывается, как только выбран цвет.

{
  "fuciniCoverage.heatmap.colors": ["#e3f2fd", "#90caf9", "#42a5f5", "#1e88e5", "#0d47a1"]
}
The settings page in VS Code: two colours typed in, the pinned colour preview repainting each time
В VS Code: два цвета, введённые на странице настроек, — предпросмотр каждый раз перерисовывается.
Visual Studio: the same settings page and the same colour preview
В Visual Studio: та же страница, тот же предпросмотр.

Часть строки, которая ни разу не выполнялась

Строка либо покрыта, либо нет, а истина часто лежит посередине: return x > hi ? hi : x; выполнилась, а её ветвь hi — ни разу. При включённом lineHighlight ни разу не выполненная часть выполненной строки подкрашивается в редакторе — для каждого формата, который содержит диапазоны столбцов (llvm-cov, Istanbul, Go, dotnet-coverage); это те же участки, которые отмечает Annotated Source. Только в строках, которые выполнялись: строка, которая не выполнялась ни разу, и так отмечена целиком. Шаблон C++ считается выполненным там, где его выполнила хотя бы одна инстанциация; что сделала каждая инстанциация, показывает метрика инстанциаций.

An executed line whose opening brace is tinted: the if ran, the block it opens never didScreenshot pending — add assets/img/partial-line.png (see SCREENSHOTS.md).

Какие тесты выполнили эту строку

Некоторые отчёты сообщают не только о том, что строка выполнялась, но и о том, какие тесты её выполнили: coverage.py, записанный командой coverage json --show-contexts при dynamic_context = test_function в .coveragerc, OpenCover, собранный с -coverbytest, и файл трассировки lcov, записанный по одной секции на тест (TN:), секции которого складываются в одну запись с тестами у каждой строки. Наведите указатель на начало строки, где стоит число попаданий, и тесты будут названы — сокращённо до класса и метода, первые пять по имени, остальные числом; CodeLens функции говорит, сколько тестов её выполнили. Show Tests That Ran This Line — Command Palette, контекстное меню редактора, в Visual Studio Tools → Coverage Studio или Ctrl+Shift+U, T — перечисляет их, самые активные первыми, и открывает выбранный там, где он написан в рабочей области. Прогон dotnet test не может сказать, какой тест что выполнил; задайте testRun.perTest, и Run Tests with Coverage перечислит тестовые классы и запустит их по одному, помечая каждый отчёт его классом, чтобы подсказка, линза и команда могли ответить и для .NET. Эти сведения хранятся по файлам, поэтому переживают слияние с отчётом, который ничего не знает о тестах.

{
  "fuciniCoverage.testRun.perTest": true
}
# .coveragerc
[run]
dynamic_context = test_function

coverage run -m pytest && coverage json --show-contexts
Hovering the start of two lines of a Python file: each names the tests that ran it; an uncovered line names noneScreenshot pending — add assets/img/tests-per-line.gif (see SCREENSHOTS.md).
Show Tests That Ran This Line: the caret on a line, the list of tests that ran it busiest first, and the picked test opened where it is writtenScreenshot pending — add assets/img/tests-for-line.gif (see SCREENSHOTS.md).
Show Tests That Ran This Line: список, самые активные первыми, и тест, открытый там, где он написан.
Visual Studio: the caret on line 62 of Quote.cs, Show Tests That Ran This Line, the dialog listing Tests.QuoteTests, and Go to Test opening QuoteTests.cs
В Visual Studio: Show Tests That Ran This Line на строке 62 файла Quote.cs — тест, который её выполнил, и Go to Test, открывающий его.

Прошёл или упал — на отметках

Покрытая строка выглядит одинаково, прошёл её тест или нет. Там, где отчёт говорит, какие тесты выполнили строку, и файл результатов раннера говорит, чем они закончились, строка, которую выполнил упавший тест, получает красную полосу в начале и полосу на обзорной линейке; подсказка при наведении называет упавшие тесты, а Show Tests That Ran This Line пишет failed рядом с каждым из них. Когда тест красный, смотреть нужно на код, через который он прошёл. Результаты читаются при каждой загрузке из папки каждого отчёта — любой .trx и имена, которые используют средства записи JUnit (TEST-*.xml, junit*.xml, test-results*.xml), — и из testResults.paths, где указываются пути или шаблоны относительно папки. Run Tests with Coverage просит у dotnet test файл test-results.trx рядом с отчётом, так что при включённом testRun.perTest настраивать ничего не нужно. testResults.enabled отключает чтение.

{
  "fuciniCoverage.testResults.paths": ["build/test-results/**/*.xml", "reports/junit.xml"]
  }
A failing test: a red bar on the lines it ran, the hover naming it under Failing tests that ran this line, and Show Tests That Ran This Line writing failed beside itScreenshot pending — add assets/img/pass-fail.gif (see SCREENSHOTS.md).
Visual Studio: a failing test — the red bar on the lines it ran, the hover naming it, and Show Tests That Ran This Line writing failed beside itScreenshot pending — add assets/img/vs-pass-fail.gif (see SCREENSHOTS.md).
В Visual Studio: красный тест — красная полоса на строках, которые он выполнил, подсказка с его именем, и Show Tests That Ran This Line, пишущий рядом с ним failed.

Тесты, которых касается изменение

Полный прогон медленно отвечает на вопрос, на который Run Tests Affected by My Changes… отвечает сразу: какие из всех тестов прошли через код, который я только что правил? У git запрашивается diff рабочего дерева относительно testImpact.baseRef — по умолчанию HEAD, то есть то, что ещё не закоммичено; origin/main — всё, что меняет ветка, — и перечисляются тесты, выполнившие изменённую строку; первым идёт тот, что выполнил наибольшую часть изменения. Выбор теста открывает его. Там, где отчёты получены прогоном с разбивкой по тестам (testRun.perTest), первый пункт запускает только эти тесты, каждый класс — в свой отчёт, так что отчёты всех остальных классов остаются в силе, а итоги по-прежнему относятся ко всему набору. Тест, который выполнил только строки ниже вставки или удаления, тоже попадает в список: иначе его отчёт называл бы не те строки.

An uncommitted edit, Run Tests Affected by My Changes…, the list with the test that ran most of the change first, and the run of only those testsScreenshot pending — add assets/img/test-impact.gif (see SCREENSHOTS.md).
Visual Studio: an edit, Run Tests Affected by My Changes…, the list with the test that ran most of the change first, and the run of only thoseScreenshot pending — add assets/img/vs-test-impact.gif (see SCREENSHOTS.md).
В Visual Studio: правка, Run Tests Affected by My Changes…, список, где первым стоит тест, выполнивший большую часть изменения, и прогон только этих тестов.

Наименьшее число тестов, которые всё ещё выполняют каждую строку

Show Minimal Test Set… отвечает на два вопроса по одним и тем же данным о строках каждого теста: какие немногие тесты по-прежнему выполнили бы каждую строку, которую выполняет набор, — для быстрого прогона перед коммитом, — и какие тесты не выполняют ни одной строки, которую уже не выполняет другой тест. Сначала идут выбранные тесты, каждый со строками, которые он добавляет, и строками, которые выполняет только он; затем остальные, каждый с пометкой, что он ничего не добавляет. Выбор жадный — тест, который выполняет больше всего, затем тот, что добавляет к нему больше всего, — поэтому он близок к наименьшему набору и каждый раз один и тот же. Избыточный по строкам не значит избыточный по тому, что тест проверяет: тест, не добавляющий ни одной строки, может быть единственным, который проверяет значение на этих строках. Список говорит, куда смотреть, когда набор прореживают, но никогда — что удалять.

Show Minimal Test Set…: the chosen tests first, then the tests that add nothingScreenshot pending — add assets/img/minimal-test-set.png (see SCREENSHOTS.md).
Visual Studio: Show Minimal Test Set… — the chosen tests, then the tests that add nothingScreenshot pending — add assets/img/vs-minimal-test-set.png (see SCREENSHOTS.md).
В Visual Studio: Show Minimal Test Set… — выбранные тесты, затем тесты, которые ничего не добавляют.

Запустить этот тест, с покрытием

Поставьте курсор в тест и выполните Run This Test with Coverage — Command Palette, контекстное меню редактора, в Visual Studio Tools → Coverage Studio или Ctrl+Shift+U, R — и только этот тест запустится с включённым покрытием: тестовый метод, в котором стоит курсор, либо тестовый класс или блок describe, когда он между методами, либо файл, когда он выше всех классов. Что под курсором, читается из текста, поэтому это работает для C#, VB и F# ([Fact], [Test], [TestMethod] и подобных), JavaScript и TypeScript (it, test, describe) и Python (def test_…), какая бы языковая служба ни была установлена. Рецепт папки сужается до него — dotnet test --filter "FullyQualifiedName~…", jest или vitest <file> -t "…", идентификатор узла pytest, — а то, что он записал, загружается как любой прогон; раннер, который нельзя сузить, сообщает об этом, и Run Tests with Coverage запускает все.

Run This Test with Coverage: the caret in a Fact, the one dotnet test --filter run in the terminal, the gutter of the class under test filling inScreenshot pending — add assets/img/run-this-test.gif (see SCREENSHOTS.md).
Run This Test with Coverage: один тест, один прогон, подсвечен тестируемый класс.
Visual Studio: the caret in the Fact AGoldCustomerGetsTenPercent, Run This Test with Coverage, the run appearing in the Tracefiles list and the lenses saying run by 1 test(s)
В Visual Studio: Run This Test with Coverage в [Fact] — прогон в списке Tracefiles, строки над методами говорят, что код выполнил один тест.

Go и Rust. В файле Go функция func TestXxx (или Fuzz…, Example…) под курсором запускается одна, в своём пакете и по точному имени — go test ./pkg/calc -run "^TestTotal$", — а вне теста запускается пакет файла. В файле Rust функция с #[test] запускается по своему пути в крейте с --exact; когда курсор стоит в mod, но ни в одном тесте, запускаются тесты этого модуля; интеграционный тест в tests/ запускается в собственном двоичном файле (--test api). Раннеру передаются только файлы его языка: для файла TypeScript в папке Go выводится сообщение, а не пустой прогон.

Покрытие после каждого сохранения

При включённом testRun.onSave сохранение исходника запускает тесты, которые его покрывают, с покрытием, через мгновение после сохранения, и поле редактора следует за ними. Для решения .NET проект сохранённого файла находится через граф .csproj, и запускаются только тестовые проекты, которые на него ссылаются — напрямую или через другие проекты, — по одному dotnet test <project> на каждый; любой другой раннер выполняет весь свой рецепт; файл, которым не владеет ни один проект и который не измеряет ни один отчёт, не запускает ничего. Несколько сохранений подряд — один прогон, а уже идущий прогон не прерывается. Остальное — как это делает Run Tests with Coverage: терминал, панель Output, загруженный отчёт. По умолчанию выключено — это привычка, которую выбирают, а не умолчание, которое обнаруживают.

{
  "fuciniCoverage.testRun.onSave": true
}
Coverage after every save: an edit to a C# file, Ctrl+S, the covering test project runs by itself, and the gutter changesScreenshot pending — add assets/img/coverage-on-save.gif (see SCREENSHOTS.md).
Покрытие после каждого сохранения: правка, сохранение, и поле редактора следует мгновение спустя.
Visual Studio: with testRun.onSave on, a comment line added to Quote.cs and saved, the tests running by themselves and the lens above Price changing
В Visual Studio: testRun.onSave включён — Quote.cs сохранён, тесты запускаются сами, строка над методом меняется.

Само приложение, с покрытием

Тестовый прогон говорит, до чего доходят тесты. То, до чего доходит человек — прощёлкивая экраны перед выпуском, повторяя шаги клиента, — тоже покрытие, и Run App with Coverage… его собирает. Приложение .NET из папки — Exe, WinExe, веб-проект или worker-проект, но никогда не тестовый проект; если их несколько, предлагается выбор — запускается под dotnet-coverage от Microsoft в отдельной консоли, чтобы в консольное приложение можно было вводить текст, и работает, пока вы его не закроете. То, через что вы прошли, затем загружается как любой прогон, а в сессии покрытия добавляется к тому, что выполнили тесты. То, как завершилось приложение, на прогон не влияет. Инструмент нужно установить один раз: dotnet tool install --global dotnet-coverage.

Run App with Coverage…: the console app started under dotnet-coverage, a few inputs, closed, and its coverage loadedScreenshot pending — add assets/img/run-app.gif (see SCREENSHOTS.md).
Visual Studio: Run App with Coverage… — the console app, a few inputs, closed, and its coverage loadedScreenshot pending — add assets/img/vs-run-app.gif (see SCREENSHOTS.md).
В Visual Studio: Run App with Coverage… — консольное приложение, несколько вводов, закрытие и его загруженное покрытие.

Мутанты и где они находятся

Инструмент мутационного тестирования — PIT для Java, Stryker для JavaScript, C# и Scala, Mull для C и C++ — вносит в код небольшие изменения и прогоняет тесты для каждого из них. Изменение, которого не заметил ни один тест, — это выживший мутант: код выполнился, но никто не проверил, что он сделал. Мутационная оценка говорит, сколько мутантов было поймано; начиная с 0.2026.83 редактор показывает, где находятся остальные. После строки с таким мутантом стоит пометка (⚑ 2 mutants survived), изменённый текст подчёркнут, если отчёт указывает, какой именно это был текст (так делают Stryker и Mull), на обзорной линейке появляется полоса, а при наведении видно, что было изменено, сколько тестов выполнили этот код, и для пойманных мутантов — какой тест их поймал.

A surviving mutant in the editor: the note after the line, the underlined text, and the hover naming the change — ConditionalBoundary: value <= loScreenshot pending — add assets/img/mutant-hover.png (see SCREENSHOTS.md).

Выжил означает, что тесты выполнили изменённый код и все прошли. Не проверен (в отчёте — no coverage) означает, что этот код не выполняет ни один тест, так что заметить изменение было некому. Пойманные мутанты подсчитываются в подсказке при наведении и никогда не отмечаются, а мутанты, которые не были честной проверкой, — например, тот, что не скомпилировался бы, — в оценку не входят. В Annotated Source под каждой такой строкой есть отдельная строка и кнопка Next Mutant (U), а в HTML-отчётах — раздел Surviving Mutants со ссылками на строку. Ничего включать не нужно, а проект без мутационного отчёта ничего этого не увидит.

Surviving mutants marked in the editor, the hover naming the change, and the annotated source jumping from one to the next with UScreenshot pending — add assets/img/mutants.gif (see SCREENSHOTS.md).

Заметил бы тест? Мутационный прогон под курсором

Мутационный прогон проекта занимает час, а одной функции — минуту. Run Mutation Tests Here запускает эту минуту: мутационному раннеру папки велят мутировать функцию, в которой стоит курсор, — или выделенные строки, или файл, — а его отчёт загружается рядом с покрытием на экране, где отмечаются выжившие мутанты. Для C# это Stryker.NET (dotnet tool install --global dotnet-stryker), которому сообщают диапазон символов и тестовый проект, зависящий от проекта файла; для JavaScript и TypeScript — StrykerJS, которому сообщают строки; для Java — PIT с Maven, которому сообщают класс. То, как завершился раннер, на прогон не влияет: оценка ниже порога — это код возврата, а отчёт всё равно есть. О раннере, который ничего не записал, сообщается — вместе с тем, что для него нужно установить.

Run Mutation Tests Here on one function: Stryker.NET runs that minute, and the surviving mutants are marked in the editorScreenshot pending — add assets/img/mutation-here.gif (see SCREENSHOTS.md).
Visual Studio: Run Mutation Tests Here on one function, and the surviving mutants markedScreenshot pending — add assets/img/vs-mutation-here.gif (see SCREENSHOTS.md).
В Visual Studio: Run Mutation Tests Here на одной функции и отмеченные выжившие мутанты.

Собственное представление Test Coverage VS Code

При включённом testCoverage.publish (по умолчанию) каждый загруженный прогон попадает и в нативное представление Test Coverage редактора — панель покрытия в Test Explorer, проценты по файлам, которые она ставит в Проводнике, и собственный переключатель редактора Show Inline Coverage — как строки, ветви и функции, с подробностями, подгружаемыми по запросу, из какого бы файла трассировки они ни пришли и без участия какого-либо адаптера тестов. Show in Test Coverage View публикует активный прогон и открывает панель независимо от настройки; меню строки состояния предлагает это за quickActions.showTestCoverage. При выключенной настройке запись Fucini Coverage в Test Explorer не появляется вовсе. Когда файл трассировки называет свои тесты (секции TN: в lcov), каждый тест появляется под Fucini Coverage в Test Explorer, и представление может фильтровать покрытие файла по тесту.

VS Code's own Test Coverage view filled from the loaded reports, one bar each for statements, branches and functionsScreenshot pending — add assets/img/test-coverage-view.png (see SCREENSHOTS.md).

Непокрытые строки на панели Problems

По умолчанию выключено, потому что по своей природе шумно, и только для открытых документов — прогон на две тысячи файлов не затопит панель. Не зависит от problems.enabled, который держится одной записи на файл:

{
  "fuciniCoverage.problems.perLine": "uncoveredAndPartial"
}
The Problems panel: one entry per file below the statement threshold, and a possible-dead-code entry for every function that never ranScreenshot pending — add assets/img/problems-panel.png (see SCREENSHOTS.md).

9. Дифференциальное покрытие

Ограничьте оформление строками, изменёнными относительно базовой ветки git — идеально для проверки того, что именно покрыл или не покрыл PR:

{
  "fuciniCoverage.gitDiff.enabled": true,
  "fuciniCoverage.gitDiff.baseRef": "origin/main"
}
Diff coverage: only the five lines changed against the base ref are marked and judged — 5 of 5 lines, 7 of 8 branchesScreenshot pending — add assets/img/diff-coverage.png (see SCREENSHOTS.md).

В Visual Studio. Те же две настройки, читаемые из тех же файлов: расширение само выполняет git diff <baseRef> для каждого файла запуска и сужает запуск до строк, которые изменила ваша ветка — на полях, в окне Coverage и в порогах. Панель Output сообщает, что ответил git, когда он не отвечает ничего.

Порог для нового кода

threshold.newCode — отдельный порог, рядом с порогами на весь прогон: какой наименьший процент измеримых строк, изменённых относительно gitDiff.baseRef, должен был выполниться. Проект с 62% в целом всё же может потребовать, чтобы написанное сегодня было протестировано. Он считается при каждой загрузке по всему прогону, который остаётся на экране, — gitDiff.enabled — это другое: изменённые строки вместо прогона. Check Coverage Thresholds и записи о соответствии в Problems сообщают о его нарушении отдельной фразой: New code 72.00% < 80% (18 of 25 line(s) changed against origin/main ran). Изменение, в котором нет ни одной измеримой строки — комментарии, README, — ничего не нарушает.

{
  "fuciniCoverage.threshold.newCode": 80,
  "fuciniCoverage.gitDiff.baseRef": "origin/main"
  }
Check Coverage Thresholds failing New code 72.00% < 80% beside a run that passes its own gatesScreenshot pending — add assets/img/new-code-gate.png (see SCREENSHOTS.md).
Visual Studio: Check Coverage Thresholds failing the new-code gate in the Error List, beside a run that passes its own gatesScreenshot pending — add assets/img/vs-new-code-gate.png (see SCREENSHOTS.md).
В Visual Studio: Check Coverage Thresholds с проваленным порогом для нового кода, рядом с прогоном, который свои пороги проходит.

Новый код по тегу, по автору, по дате

Новый код по умолчанию — это строки, которые рабочее дерево меняет относительно gitDiff.baseRef — ветки, коммита или тега: "v1.4.0" — это всё с того выпуска. Две настройки спрашивают о том, чего ссылка выразить не может. gitDiff.author принимает часть имени автора, и тогда новый код — это строки, которые git приписывает этому автору; gitDiff.since принимает день, и тогда это строки, в последний раз изменённые в этот день или позже, через какую бы ветку они ни пришли; обе вместе — строки этого автора с этого дня. Они читаются из git blame, файл за файлом, — что большой прогон почувствует при каждой загрузке, — и занимают место diff для дифференциального покрытия и для порога выше. Ещё не закоммиченная строка считается в любом случае.

{
  "fuciniCoverage.gitDiff.author": "Ada",
  "fuciniCoverage.gitDiff.since": "2026-09-01"
  }
The same gate scoped by author and date: new code by Ada since 2026-09-01Screenshot pending — add assets/img/new-code-author.png (see SCREENSHOTS.md).

Косвенные изменения: что сдвинулось, хотя никто этого не трогал

Diff говорит, какие строки изменились; два прогона говорят, какие строки перестали или начали выполняться. Посмотреть стоит на те строки, где одно расходится с другим: код, который никто не правил и который больше не выполняется, потому что удалили тест, ветвь выше по ходу выполнения теперь делает ранний возврат, изменилась фикстура. Ревью diff их увидеть не может — они по определению вне его. Show Indirect Coverage Changes… перечисляет их по файлам, первым — файл, потерявший больше всего, и открывает выбранный на первой такой строке. Прогон сравнивается с базовой линией CI там, где baseline.report её называет, — по ней и судят pull request, — а иначе с предыдущим прогоном; строки, которые рабочее дерево меняет относительно gitDiff.baseRef, и всё ниже места, где это изменение сдвигает строки файла, не учитываются.

Show Indirect Coverage Changes…: a file that lost lines nobody edited, after a test was removed between two runsScreenshot pending — add assets/img/indirect-changes.png (see SCREENSHOTS.md).
Visual Studio: Show Indirect Coverage Changes… — a file that lost lines nobody editedScreenshot pending — add assets/img/vs-indirect-changes.png (see SCREENSHOTS.md).
В Visual Studio: Show Indirect Coverage Changes… — файл, потерявший строки, которые никто не правил, после того как между двумя прогонами убрали тест.

10. Комплексная оценка

Вместо того чтобы выводить шесть чисел, расширение объединяет их в единое взвешенное среднее гармоническое (WHM). Для метрик с процентами pᵢ и весами wᵢ:

WHM = ( Σ wᵢ ) / ( Σ wᵢ / pᵢ )

В среднем гармоническом доминирует его наименьший член, поэтому слабая метрика не может быть замаскирована идеальной — именно то свойство, которое нужно от предохранительного барьера. Веса по умолчанию:

МетрикаВесОбоснование
MC/DC4IEC 61508 SIL 3–4 / ISO 26262 ASIL C–D / DO-178C DAL A–B.
Ветви2IEC 61508 SIL 2+ / ISO 26262 ASIL B+.
Условия3Между ветвями и MC/DC (DO-178C, ISO 26262).
Регионы1.5Пути внутри инструкции (напр., частичное сокращённое вычисление).
Строки1IEC 61508 SIL 1+ / ISO 26262 ASIL A+.
Функции1Проверка на мёртвый код.
Инстанцирования0.5Покрытие шаблонов C++; самая узкая область.
Вызовы функций1Места вызова, которые были выполнены (gcov, VectorCAST, Testwell CTC++, TRACE32).
Объектный код0Отключено, пока вы не зададите вес: оно включает в себя покрытие операторов.
Мутационная оценка0Отключено, пока вы не зададите вес: это не покрытие.

Установите любой вес в 0, чтобы исключить эту метрику из оценки. Метрики, отсутствующие в данных (например, MC/DC в обычном lcov-прогоне), просто опускаются.

Объектный код и мутационная оценка имеют вес 0, пока вы не решите иначе (whmWeights.objectCode, whmWeights.mutation). Покрытие объектного кода включает в себя покрытие операторов, поэтому, взвешивая оба, вы дважды учитываете одни и те же данные; мутационная оценка вообще не является покрытием — набор тестов может выполнить каждую строку и ничего не обнаружить. У обеих метрик по-прежнему есть собственный столбец и собственный порог.

The default weights of the composite score: MC/DC 4, branches 2, regions 1.5, lines, functions and function calls 1, instantiations 0.5; object code and mutation score 0 until given a weightScreenshot pending — add assets/img/score-weights.svg (see SCREENSHOTS.md).

Покрытие условий

a && b — это одно решение (две ветви) и два условия с четырьмя исходами. Набор тестов может пройти обе ветви и ни разу не увидеть ложное b: покрытие ветвей полное, а покрытие условий — три из четырёх. В DO-178C и ISO 26262 этот уровень находится между покрытием ветвей и MC/DC, и начиная с 0.2026.83 это самостоятельная метрика, а не часть ветвей: строка в карточках строки состояния, столбец в представлении Coverage, в отчётах и экспортах, вес 3 в оценке (whmWeights.conditions) и барьер (threshold.conditions; в профиле standard барьера нет, в safety — 100).

One decision, a && b, and the same two tests: branches 2 of 2, conditions 3 of 4 because b is never seen false, MC/DC 1 of 2 because b is never shown to matterScreenshot pending — add assets/img/branch-condition-mcdc.svg (see SCREENSHOTS.md).

Считывается из проб condition BullseyeCoverage, экспорта cond TRACE32 и GCC 14 при сборке с -fcondition-coverage. В отчёте, который не отличает условия от решений, нет ни такой строки, ни столбца, ни веса. Если вы используете Bullseye или TRACE32: раньше условия считались ветвями, поэтому с этим выпуском ваш процент ветвей изменится, а с новым весом — и составная оценка; чтобы получить оценку в прежнем виде, задайте для whmWeights.conditions значение 0.

GCC 14, построчно. С -fcondition-coverage gcov говорит, какого исхода какого условия не хватает — условие 1 ни разу не было истинным, условие 0 не было ни истинным, ни ложным, — и каждое условие отмечается на своей строке и называется в подсказке при наведении, по JSON gcov и по его текстовому листингу (condition outcomes covered 3/6). Метрика есть там же, где остальные: threshold.conditions задаёт для неё порог и не оценивается там, где у прогона такого показателя нет, у Copy Coverage Summary есть столбец Conditions, badge.metric принимает conditions, а поле поиска — conditions < 80. Показатель ветвей функции не включает исходы её условий, как и показатели файла и прогона.

GCC 14 condition coverage: the mark on the line and the hover naming the outcome never seenScreenshot pending — add assets/img/gcc-conditions.png (see SCREENSHOTS.md).

Сложность и CRAP

Некоторые отчёты содержат цикломатическую сложность каждой функции: OpenCover (именно его записывает Coverlet), JaCoCo, Clover и Cobertura. В таких случаях расширение вычисляет для функции оценку CRAP — Change Risk Anti-Patterns — на основе этой сложности и покрытия, которое оно измерило само:

CRAP = complexity² × (1 − coverage)³ + complexity

Простая, полностью протестированная функция получает оценку, равную её собственной сложности, и не более; сложная и едва протестированная — оценку в несколько сотен. В примере на C# из демонстрационного проекта у Price() сложность 22 и покрыто около 37% строк, что даёт 142.2, а у Shipping() сложность 12 и полное покрытие, что даёт 12. Общепринятая граница — 30: функция с оценкой на этом уровне или выше попадает в список Riskiest Methods в HTML-отчёте, начиная с худшей.

CRAP against line coverage for two methods: Price() with complexity 22 sits at 142.2 and needs about 75% coverage to get under 30; Shipping() with complexity 12 is fully covered and scores 12Screenshot pending — add assets/img/crap-curve.svg (see SCREENSHOTS.md).

Сложность и оценка отображаются в CodeLens функции и рядом с функцией в аннотированном исходном коде. Max CRAP — это наивысшая оценка среди всех функций файла — худшая, а не средняя, — и она является столбцом в представлении Coverage, в HTML-отчёте и в экспорте в CSV и Markdown. Это справочная информация: у неё нет порога и нет веса в комплексной оценке. У отчётов, не содержащих сложности (lcov, gcov, Istanbul, coverage.py), нет CRAP — и пустого столбца тоже нет.

Начиная с 0.2026.83 он есть и у C, и у C++. llvm-cov не сообщает сложность, поэтому она подсчитывается по данным о ветвях, которые уже есть в отчёте: условия, записанные в собственном файле функции, плюс один — это определение Маккейба и то же, что считают инструменты уровня исходного кода для .NET и Java. Условия, пришедшие из макроса из другого файла, — например, каждый assert — не учитываются.

The Riskiest Methods section of the HTML report: Price() with complexity 22, 37.14% of its lines covered, CRAP 142.2Screenshot pending — add assets/img/riskiest-methods.png (see SCREENSHOTS.md).

Risk hotspots и какие файлы показывать

Под деревом представления Coverage раздел Risk hotspots перечисляет функции, где сложный код сочетается с малым покрытием, худшие первыми; каждая открывает свой файл на функции. Функция попадает туда по показателю CRAP — hotspots.crapThreshold, по умолчанию 30, значение, предложенное авторами метрики, — или по одной лишь цикломатической сложности, hotspots.complexityThreshold, по умолчанию выключено; они есть только у отчётов, несущих сложность (OpenCover, Cobertura от Coverlet, JaCoCo, llvm-cov). Community видит количество и худший показатель; ранжированный список — в Pro. Рядом с сортировкой второй выпадающий список говорит, какие файлы показывает дерево — все, файлы с пробелами, непротестированные файлы или файлы, изменившиеся с последнего запуска; папки следуют за своими файлами и сохраняют собственные числа. Обе возможности есть и в Visual Studio, в окне Coverage.

Visual Studio: the Coverage window with the filter beside the sort and the risk hotspots under the treeScreenshot pending — add assets/img/vs-hotspots.png (see SCREENSHOTS.md).
В Visual Studio: окно Coverage с фильтром рядом с сортировкой и разделом Risk hotspots под деревом.

Пространства имён, классы и методы

Для отчёта .NET, называющего свои классы — Cobertura и coverage.cobertura.xml от Coverlet, OpenCover, — третий выпадающий список, Folders / Namespaces, рисует тот же прогон как пространства имён, классы и методы: число пространства имён — сумма его классов, число класса — его методов, точно так же, как число папки — сумма её файлов, с той же сортировкой и тем же фильтром. Строка метода открывает файл на этом методе; класс, объявленный вне пространства имён, стоит под (no namespace). Выбор записывается в coverageView.perspective; прогон, не несущий имён классов, не имеет выпадающего списка и показывает свои папки. В Visual Studio у окна Coverage тот же выпадающий список.

The Coverage view drawn as namespaces, classes and methods, the dropdown on Namespaces, a method row about to open its fileScreenshot pending — add assets/img/namespace-perspective.png (see SCREENSHOTS.md).
Тот же прогон по пространствам имён, классам и методам.
Visual Studio: the Coverage window with the dropdown on Namespaces, the Pricing namespace open on Customer and its methods
В Visual Studio: окно Coverage по пространствам имён, классам и методам.

Под кнопками и выпадающими списками — поле поиска. Слово оставляет строки, чьё имя или путь его содержат — каждое введённое слово должно быть там; сравнение оставляет строки, чья цифра ему удовлетворяет — < 80% по оценке, lines >= 90, branches = 0, functions != 100, mcdc < 50, — а invoice < 80 требует и того и другого. Строки методов из перспективы пространств имён и точки риска ищутся вместе с файлами, так что функция находится там, где она есть в прогоне. Дерево фильтруется по мере ввода, в странице, без перерисовки; папка с совпадением раскрывается для него и снова сворачивается, когда поле пустеет; Esc очищает его. То же поле, та же грамматика — в Visual Studio.

Typing lines < 80 into the Coverage view's search box: the tree keeps only the files under eighty percent, folders opening to show themScreenshot pending — add assets/img/tree-search.gif (see SCREENSHOTS.md).
Поиск по дереву: имя, затем lines < 80.

Дерево следует за редактором

Файл, на который вы смотрите, подсвечивается в представлении Coverage, папки над ним раскрываются, а его строка прокручивается в видимую область — в дереве больше не нужно искать файл, который уже открыт. В остальном дерево сохраняет позицию прокрутки, и ничего не перерисовывается. Настройка coverageView.followActiveEditor, по умолчанию включённая, позволяет отключить это тем, кто предпочитает, чтобы дерево оставалось там, где они его оставили.

Switching editor tabs: the Coverage view's highlighted row follows, the folders above it openedScreenshot pending — add assets/img/follow-editor.gif (see SCREENSHOTS.md).
Visual Studio: switching documents, and the Coverage window's highlighted row followingScreenshot pending — add assets/img/vs-follow-editor.gif (see SCREENSHOTS.md).
В Visual Studio: переключение документов и выделенная строка окна Coverage, следующая за ними.

11. Контроль соответствия

Вердикт COMPLIANT / NON-COMPLIANT (показан в аннотированном виде исходного кода, HTML-отчёте и обеих всплывающих подсказках строки состояния) определяется порогами для каждой метрики и режимом:

The status-bar hover card: NON-COMPLIANT, 3 of 5 gates below threshold — functions and regions pass, lines and branches miss narrowly (amber), MC/DC misses outright (red)Screenshot pending — add assets/img/statusbar.png (see SCREENSHOTS.md).
РежимCOMPLIANT, когда…
strict (default)каждая отдельно проверяемая метрика достигает своего порога — без компромиссов.
whmединая взвешенная сводная оценка достигает threshold.weighted.
bothвыполнены и взвешенная проверка, И строгая проверка по каждой метрике.
eitherвыполнена либо взвешенная проверка, ЛИБО строгая проверка по каждой метрике (самый мягкий режим).

Порог 0 отключает этот барьер, а метрика, которой нет в отчёте, никогда не оценивается. Что означает порог, который вы не задали, определяет профиль, описанный ниже. Запустите Fucini Coverage: Check Coverage Thresholds для оценки по запросу.

В Visual Studio. Tools → Coverage Studio → Check Coverage Thresholds оценивает запуск по тем же ключам threshold.* и сообщает результат в строке состояния и в панели Output; с compliance.diagnostics, problems.enabled, problems.perLine или deadCode.enabled находки попадают в Error List рядом с находками самого компилятора — по одной записи на файл ниже порога, по одной на непокрытый диапазон, по одной на функцию, которая ни разу не выполнялась, — и каждая открывает свой файл на нужной строке.

Visual Studio: Check Coverage Thresholds, with the findings in the Error List beside the compiler's ownScreenshot pending — add assets/img/vs-error-list.png (see SCREENSHOTS.md).
В Visual Studio: Check Coverage Thresholds, с результатами в Error List.

Профили порогов

threshold.profile определяет, что означает порог, который вы не задали. standard, значение по умолчанию начиная с 0.2026.82, — это то, чего можно требовать от обычного проекта, не связанного с функциональной безопасностью. safety — это все барьеры на уровне 100: то, чего требует обоснование безопасности, и в точности то, что поставлялось вплоть до 0.2026.81.

Метрикаstandardsafety
Строки80100
Функции80100
Регионы75100
Ветви70100
Комплексная оценка75100
MC/DC, инстанцирования, вызовы функций, объектный кодбез барьера100
Условиябез барьера100
Мутационная оценкабез барьерабез барьера

Порог, который вы задали сами, имеет приоритет над любым профилем. В примере для ветвей задан барьер 90, а для всего остального — 100; чтобы требовать 100 только от некоторых метрик, оставайтесь на standard и задайте их сами. Одна оговорка вытекает из того, как работают параметры: значение, равное поставляемому значению по умолчанию, неотличимо от отсутствия значения, поэтому при safety порог, записанный в точности как его стандартное значение — например, 80 для строк, — всё равно считается равным 100.

{
  "fuciniCoverage.threshold.profile": "safety",
  "fuciniCoverage.threshold.branch": 90
}
The settings panel: the threshold profile above the grid of thresholds, showing the standard figuresScreenshot pending — add assets/img/threshold-profile.png (see SCREENSHOTS.md).
Switching the threshold profile to safety in the settings panel: the live preview asks for 100% everywhere; back on standard it asks for 80, 70, 80 and 75 and marks the rest as not gatedScreenshot pending — add assets/img/threshold-profiles.gif (see SCREENSHOTS.md).

Если вы полагались на прежние значения по умолчанию, после обновления до 0.2026.82 вас оценивают мягче, пока вы не выберете safety. Установка, которая работала раньше, сообщает об этом один раз — с кнопкой Keep 100% (safety), которая записывает профиль в ваши пользовательские параметры; для общего проекта укажите его также в параметрах рабочей области, чтобы всех оценивали одинаково. В Configure Coverage Compliance & Thresholds... профиль расположен над сеткой порогов, а предварительный просмотр в реальном времени оценивает по действительно действующим барьерам. В каждой карточке и каждом отчёте метрика с отключённым барьером показывает своё значение с нейтральным прочерком вместо галочки или крестика.

The status-bar card under the standard profile: two of four gates below threshold, and a neutral mark on the metrics that are not gatedScreenshot pending — add assets/img/statusbar-metrics.png (see SCREENSHOTS.md).

11a. Покрытие объектного кода

Все остальные метрики здесь измеряются по исходному коду: инструмент инструментирует ваш код, инструментированная сборка выполняется, и счётчики говорят, какие операторы, ветви или условия были задействованы. Покрытие объектного кода измеряет другое — сколько собранного двоичного файла действительно выполнилось, в байтах инструкций, за счёт наблюдения за потоком инструкций, а не инструментирования исходного кода. Это важно, потому что компилятор порождает код, за которым не стоит ни одна строка вашего исходника: проверки границ массивов, таблицы переходов switch, выгрузка регистров, зондирование стека, вызовы вспомогательных подпрограмм среды выполнения для деления или плавающей точки на ядрах без такой инструкции. Это также единственная здесь метрика, отвечающая на вопрос покрывает ли это ассемблер? — написанный вручную ассемблер никогда не инструментируется, и средство уровня исходного кода до него не добирается. DO-178C прямо объясняет почему: на уровне A, где компилятор или компоновщик порождает код, не прослеживаемый напрямую до операторов исходного кода, цель 6.4.4.2b требует дополнительной верификации именно этого порождённого кода.

Оно поглощает покрытие операторов — каждая выполненная инструкция происходит из какого-то оператора, поэтому 100% покрытия объектного кода означает 100% покрытия операторов. Обратное неверно, и именно эта асимметрия и есть смысл измерения. Отсюда следует два вывода. Оно не имеет никакого веса в сводной оценке, поскольку усреднение двух мер одних и тех же выполнений засчитывает одно и то же свидетельство дважды; вместо этого оно контролируется порогом fuciniCoverage.threshold.objectCode — требование, а не вес, как того и требует стандарт. И именно разрыв является результатом: прогон со 100% покрытием строк и покрытием объектного кода менее 100% точно указал на порождённый компилятором код, за которым не стоит ваш исходник, поэтому карточка соответствия говорит 312 байт никогда не выполнялись, при 100% покрытии строк, а не прячет это за «94%». Обратное — весь двоичный файл считается выполненным, а его исходник нет — это противоречие, а не низкая оценка: почти всегда устаревший файл трассировки с одной стороны слияния или имена модулей, так и не совпавшие с путями файлов. Об этом сообщается как о находке.

Покрытию объектного кода нужно, чтобы двоичный файл выполнялся там, где его инструкции можно наблюдать. Путей три, и по стоимости они различаются колоссально. Объектное покрытие даёт только третий; первые два приведены потому, что решают смежную задачу, которую часто и стоит решать на самом деле.

  1. Инструментированная сборка, работающая на целевой системе. Кросс-компиляторы GCC принимают -fprofile-arcs -ftest-coverage, так что инструментация, работающая на рабочей станции, работает и на цели — с той разницей, что у системы без ОС нет файловой системы, куда записать счётчики .gcda, и часто нет exit(), в момент которого их записать. Счётчики всё равно лежат в оперативной памяти, поэтому решение — выгрузить эту память и восстановить файлы вне цели; embedded-gcov от NASA JPL (Apache-2.0) делает именно это, без файловой системы, операционной системы и стандартной библиотеки C. Это даёт покрытие операторов, ветвей и функций сборки, сделанной вашим кросс-компилятором, для вашей системы команд, на вашем оборудовании — но не покрытие объектного кода, а измеряемый двоичный файл инструментирован, то есть больше и медленнее поставляемого. Результат загружайте как LCOV, gcovr JSON, gcov JSON или, на инструментарии старше GCC 9, как текстовый листинг .gcov.
  2. Симулятор системы команд. Запустите настоящий целевой двоичный файл в симуляторе и выведите покрытие из выполненных адресов: без оборудования, без инструментации, без зонда — и, поскольку наблюдаются инструкции, он может дать настоящее покрытие объектного кода. Ограничение — точность: симулятор хорошо моделирует ядро и хуже периферию, поэтому к результатам уровня всей системы относитесь с осторожностью, если он не моделирует ваш кристалл детально.
  3. Трассировка на кристалле и отладчик. Путь сертификационного уровня и единственный по-настоящему невмешивающийся: блок трассировки ядра передаёт поток инструкций отладчику, который превращает его в покрытие. Ничего не инструментируется, размер кода не меняется, тайминги не затронуты — именно это и делает результат доказательством о том двоичном файле, который вы действительно поставляете. Проверьте кристалл, прежде чем закладывать это в бюджет: во многих семействах микроконтроллеров полная трассировка доступна только на эмуляционном или отладочном варианте с большим буфером трассировки и скоростным портом, тогда как серийные кристаллы несут урезанный блок трассировки, а запасной вариант производителя для них — программная трассировка, которая инструментирует код и потребляет процессорное время, то есть уничтожает то самое свойство, ради которого этот путь и выбирали.

Многие встраиваемые проекты измеряют покрытие, компилируя те же исходники для машины разработки и запуская модульные тесты там. Это быстро, не требует оборудования и для большинства команд является правильным выбором по умолчанию — но стоит чётко понимать, что именно описывает это свидетельство: другой компилятор, другой оптимизатор, другую систему команд и другой двоичный файл, нежели поставляемый. Для большей части ПО это приемлемый и хорошо понятный компромисс. Для работ, оцениваемых по IEC 61508, ISO 26262 или DO-178C, это стандартное возражение «хост против цели», и описанные выше пути — стандартные ответы на него: первый закрывает вопрос для покрытия исходников, третий — для объектного кода.

Сегодня для объектного кода читается экспорт COVerage.EXPORT.ListModule Lauterbach TRACE32 с metric="object"; загружайте его как любой другой файл трассировки. Эта метрика оценивается и контролируется только по платной лицензии и в период оценки. Установка Community не оценивает её — но получает уведомление, когда файл трассировки её содержит, потому что вердикт о соответствии, молча опускающий самое сильное доказательство в файле, был бы хуже вердикта, который говорит, что именно он опустил.

12. Исключение строк, не являющихся кодом

При включённом excludeComments (по умолчанию) операторы, полностью находящиеся на строках только с комментариями или пустых строках, исключаются из итогов — это также нейтрализует известную особенность llvm-cov/gcov, когда случайное количество срабатываний попадает на комментарий. Стандартные маркеры также учитываются:

x = risky();          // LCOV_EXCL_LINE
// LCOV_EXCL_START
debug_only_block();
// LCOV_EXCL_STOP

Директивы препроцессора тоже не считаются кодом в C, C++, Objective-C, CUDA и C#: строка, начинающаяся с # — #define, #include, #if, — и строки, на которые её продолжает обратная косая черта в конце. gcov приписывает каждое использование макроса строке, где он определён, поэтому заголовок, единственной строкой которого в отчёте была #define CHANNEL_COUNT (33U), выглядел покрытым на 100%, потому что цикл в файле .c доходил до неё, а тот, чью константу ничто не использовало, — покрытым на 0%. Такому заголовку теперь нечего измерять, и он не попадает в список.

Установите excluded.includeInTotals в true, чтобы всё же учитывать исключённые строки.

Исключение файла или папки, из дерева

Щёлкните правой кнопкой файл или папку в представлении Coverage и выберите Exclude from Coverage — он есть и среди действий с путём строки — или выполните Exclude This File from Coverage для открытого файла: путь файла относительно рабочей области, или /** папки, попадает в fuciniCoverage.ignore в настройках папки, один раз, и перезагрузка убирает его из каждого итога и каждой страницы; шаблон лежит там, чтобы его можно было убрать снова. В Visual Studio у меню строки и Tools → Coverage Studio те же две команды, пишущие ту же настройку в тот же файл.

Right-click a folder row in the Coverage view, Exclude from Coverage, and the tree redraws without it while the pattern appears in settings.jsonScreenshot pending — add assets/img/exclude-from-tree.gif (see SCREENSHOTS.md).
Exclude from Coverage на папке: дерево без неё, шаблон в настройках.
Visual Studio: Exclude This File from Coverage on fees.ts, the tree without fees.ts, then .vscode/settings.json with the pattern src/fees.ts under fuciniCoverage.ignore
В Visual Studio: Exclude This File from Coverage для fees.ts — дерево без него, шаблон в .vscode/settings.json.

Атрибуты и пространства имён, для .NET

Две настройки читают из исходника то, чего отчёт сказать не может. ignore.attributes — по умолчанию ExcludeFromCodeCoverage — исключает класс, метод или свойство под атрибутом, который она называет, как это сделал бы LCOV_EXCL_START/STOP, каким бы инструментом ни был записан отчёт: Coverlet учитывает атрибут, OpenCover, dotCover и сконвертированный lcov — нет, а теперь все они согласны. Имена с суффиксом Attribute или без него, с пространством имён или без; добавьте GeneratedCode, Obsolete или свои. ignore.namespaces исключает каждый файл, чьё объявление пространства имён совпадает с префиксом, * в качестве подстановочного знака — Acme.Generated, Acme.*.Migrations, *Tests, — а панель Output называет файлы, которые она забрала. Обе читаются в том же проходе, что читает маркеры в комментариях, в обеих редакциях.

{
  "fuciniCoverage.ignore.attributes": ["ExcludeFromCodeCoverage", "GeneratedCode"],
  "fuciniCoverage.ignore.namespaces": ["Acme.Generated", "*.Migrations"]
}

Исключение из редактора

Выделите строки и возьмите Exclude Selection from Coverage из лампочки, контекстного меню редактора или Command Palette — в Visual Studio из лампочки (Ctrl+.), меню правой кнопки в окне кода, Tools → Coverage Studio или Ctrl+Shift+U, X, — и исключение записывается там, где код: [ExcludeFromCodeCoverage] над классом, методом или свойством C#, VB или F#, коротким именем, если файл уже импортирует System.Diagnostics.CodeAnalysis, и полностью квалифицированный, если нет, чтобы не пришлось добавлять никакой using; LCOV_EXCL_LINE в конце одной строки кода; LCOV_EXCL_START и LCOV_EXCL_STOP вокруг нескольких, с отступом первой строки — собственным знаком комментария языка, //, #, ', -- или ;. Один шаг отмены. Лампочка предлагает это и на строке, которую записи в Problems отмечают как непокрытую. Уже отмеченная строка или член, уже снабжённый атрибутом, остаются как есть.

A selection in a C# method, the light bulb, Exclude from coverage, and the LCOV_EXCL_START and STOP lines appearing around itScreenshot pending — add assets/img/exclude-selection.gif (see SCREENSHOTS.md).
Exclude Selection from Coverage: маркеры, записанные вокруг выделения, знаком комментария самого файла.
Visual Studio: four lines of an else-if branch in Quote.cs selected, Exclude Selection from Coverage writing LCOV_EXCL_START and LCOV_EXCL_STOP around them, then ExcludeFromCodeCoverage above a method
В Visual Studio: Exclude Selection from Coverage — маркеры LCOV_EXCL вокруг ветви, затем [ExcludeFromCodeCoverage] над методом.

Только ветви, и маркеры gcovr

Защитный else, default:, до которого не доходит ни одно значение, ветвь, которую компилятор добавляет на случай выброса исключения: строка выполняется и должна считаться, и только её ветвь пройти нельзя. LCOV_EXCL_BR_LINE и LCOV_EXCL_BR_START … LCOV_EXCL_BR_STOP убирают из подсчёта ветви на своих строках, а сами строки оставляют. LCOV_EXCL_EXCEPTION_BR_LINE и его область убирают ещё меньше — только ветви, в которые попадают при выбросе исключения, там, где отчёт говорит, какие это ветви, как это делает JSON gcov. Каждый маркер читается и с префиксами gcovr: GCOVR_EXCL_LINE, GCOVR_EXCL_START, GCOVR_EXCL_BR_LINE, GCOV_EXCL_STOP и остальные значат то же, что и маркеры LCOV_EXCL_, так что исходник, написанный для gcovr, читается здесь одинаково, каким бы инструментом ни был создан отчёт. Не читается: GCOVR_EXCL_FUNCTION — ничто на этой строке не говорит, где функция заканчивается.

if (handle == NULL) return E_FAIL;   // LCOV_EXCL_BR_LINE: the allocator never fails on target
  // LCOV_EXCL_BR_START generated state table
  switch (state) { /* … */ }
  // LCOV_EXCL_BR_STOP
LCOV_EXCL_BR_LINE on a line: the line still counted, its branch gone from the countScreenshot pending — add assets/img/branch-markers.png (see SCREENSHOTS.md).

Сказать почему: обоснованные исключения

Убрать код из подсчёта — значит сделать утверждение: «это нельзя протестировать», «это вспомогательное средство разработчика», — а по DO-178C, ISO 26262 или IEC 61508 утверждение, меняющее число, на которое опирается сертификат, должно быть записано и доступно для проверки. Поэтому исключение может сказать, почему оно сделано, — там же, где оно записано: текст после LCOV_EXCL_LINE или LCOV_EXCL_START либо Justification у [ExcludeFromCodeCoverage].

abort();                 // LCOV_EXCL_LINE: defensive, unreachable by construction
  // LCOV_EXCL_START debug dump, never built for the target

  [ExcludeFromCodeCoverage(Justification = "a developer aid")]
  public static void DebugDump(decimal amount) { /* … */ }

Причины читаются при каждой загрузке, и журнал их подсчитывает. Export Coverage Report… записывает список — exclusions and their justifications, в Markdown или CSV: файл, строки, как исключение записано и обоснование либо none given. Файл, в котором не осталось ничего измеримого, всё равно есть в этом списке. При включённом exclusions.requireReason исключение без причины — это находка: предупреждение в Problems на его строке и провал Check Coverage Thresholds. Exclude Selection from Coverage тогда сначала спрашивает причину и записывает её вместе с маркером или как Justification атрибута; если запрос закрыт или оставлен пустым, ничего не исключается.

An exclusion without a reason flagged as a warning at its line, and the exported list with none givenScreenshot pending — add assets/img/justified-exclusions.png (see SCREENSHOTS.md).
Visual Studio: an exclusion without a reason as a warning at its line, and the exported list with none givenScreenshot pending — add assets/img/vs-justified-exclusions.png (see SCREENSHOTS.md).
В Visual Studio: исключение без причины как предупреждение на его строке и экспортированный список с none given.

12a. Описывает ли это покрытие всё ещё этот файл?

Покрытие, прочитанное из файла трассировки, описывает код таким, каким он был при запуске тестов, и большинство форматов не сообщает, какая это была версия. Поэтому каждый файл на панели «Покрытие» находится в одном из трёх состояний:

  • ✓ Проверено — файл трассировки хранит содержимое каждой измеренной строки, и оно по-прежнему совпадает с файлом.
  • Без пометки — не опровергнуто. Ничто не противоречит покрытию и ничто его не подтверждает. Обычное состояние.
  • ⚠ Опровергнуто — сам файл опровергает своё покрытие. Строка приглушена, а подсказка сообщает, что именно найдено.

Заслужить галочку могут только два формата, потому что лишь два сохраняют хоть что-то об измеренном коде: lcov, записанный с --checksum (в geninfo по умолчанию отключено; задокументированная цель — как раз не дать объединить покрытие двух версий файла) и GNATcoverage XML, который встраивает текст каждой измеренной строки. Включить эту опцию lcov стоит.

Предупреждению не нужно ни то, ни другое, и оно работает с любым форматом: покрытие, записанное за концом файла, именованная функция, которой больше нет там, куда её ставит отчёт, или выполненные строки, пустые в файле. Отметок времени самих по себе никогда не достаточно — git checkout меняет каждую, не меняя ни байта —, так что изменённая отметка времени — самое большее повод посмотреть ещё раз. Проверка выполняется при загрузке покрытия и снова всякий раз, когда файл сохраняется или меняется на диске, так что отметка следует за файлом: правка, противоречащая прогону, её ставит, checkout, возвращающий измеренную версию, её снимает. Подсказка говорит точно, что это доказало. Установите staleWarning.enabled в false, чтобы отключить всё это.

12b. Неизмеренное против непокрытого

Файл, который прогон нигде не упоминает, — неизмеренный: его не загружал ни один тест, поэтому ни один инструмент не записал о нём ни строки. Это не то же самое, что непокрытый — измеренный, с нулём попаданий, — и расширение никогда не оценивало отсутствующий файл как 0%. Чего оно не делало до 0.2026.69 — так это вообще не показывало отсутствующие: модуль без единого теста никогда не появлялся ни в дереве, ни в итогах, ни в проверке порогов, и читатель видел «92%» у проекта, половина файлов которого никогда не выполнялась.

Теперь при загруженном прогоне элемент строки состояния для файла говорит не измерено об исходном файле того же вида, что и прогон, которого в прогоне нет, — «того же вида» значит, что у него одно из расширений файлов, которые прогон измерял, так что заголовки и исходники C-проекта — кандидаты, а его скрипты на Python — нет. Всё сверх этого включается по желанию через fuciniCoverage.unmeasured.mode:

  • off (по умолчанию) — больше ничего не меняется.
  • list — представление «Покрытие» получает внизу группу Unmeasured, называющую каждый такой файл, и каждая строка его открывает; результат загрузки их считает; значки помечают их ? в Проводнике; а fuciniCoverage.threshold.unmeasuredFiles может провалить Check Coverage Thresholds, когда их больше заданного вами числа. Ни один процент не сдвигается.
  • count — всё, что делает list, и вдобавок каждый неизмеренный файл засчитывается прогону как непокрытый файл размером в его строки кода, так что строка состояния, пороги, значки, история, каждый экспорт и представление Test Coverage показывают меньшее число. Файл везде отображается как неизмеренный, полосы в его поле говорят «не измерено», а не «ни разу не выполнялось», а проверка несоответствия его не трогает — нет измерения, которому его содержимое могло бы противоречить.
{
  "fuciniCoverage.unmeasured.mode": "list",
  "fuciniCoverage.threshold.unmeasuredFiles": 0
}

Заголовочные файлы — известное ложное срабатывание в C: у них семейство расширений прогона, и ни один файл трассировки никогда не назовёт заголовок, потому что сам по себе он ни во что не компилируется. Поэтому fuciniCoverage.unmeasured.exclude по умолчанию исключает **/*.h, **/*.hpp, **/*.hh, **/*.inl и **/*.d.ts, в том же glob-синтаксисе, что и fuciniCoverage.ignore; добавьте порт под платформу, который здесь не собирается, или сгенерированные заглушки — и они выпадут из списка, из счёта и из состояния строки состояния. Файл, названный в ignore, тоже никогда не называется неизмеренным — исключить его и было целью. threshold.unmeasuredFiles по умолчанию равен -1, то есть не проверяет; 0 проваливает проверку, как только хоть один файл того же вида, что и прогон, не был загружен ни одним тестом, и действует он только пока unmeasured.mode равен list или count.

13. HTML-отчёты

  • Generate Full HTML Report — самостоятельно формируемый, многостраничный отчёт по каждому каталогу. Вывод идёт в htmlReport.outputDir или в глобальное хранилище расширения, если не задано.
  • Open Annotated Source — аннотированный вид для одного файла. Укажите externalHtmlReport.dir на заранее собранный каталог llvm-cov show --format=html, чтобы использовать эти страницы вместо встроенного рендерера.
  • Export Fucini JSON — полная нормализованная модель для ваших собственных инструментов.
  • Export Coverage Report... — запуск как CSV (по файлам или по функциям), сводка в Markdown, файл трассировки LCOV, XML Cobertura, SARIF или автономный значок SVG для вашего README. Доступно и с кнопки рядом с Generate Full HTML Report в представлении Covered Files. Всё, кроме CSV, теряет данные — MC/DC, регионам и инстанцированиям негде разместиться в LCOV, Cobertura или SARIF — поэтому Fucini JSON и CSV остаются форматами, несущими каждую метрику.
  • Copy Uncovered Lines — сводка в формате Markdown по каждому пропуску, готовая для вставки в PR.

В Visual Studio. Группа Reports в Tools → Coverage Studio: Show Annotated Source открывает активный документ как страницу в области документов — каждая строка в своём цвете, находки рядом с ней — с прокруткой, связанной с редактором в обе стороны; Open HTML Report записывает одностраничный отчёт в htmlReport.outputDir (или в %LOCALAPPDATA%\Fucini\CoverageStudio\html-report) и открывает браузер; Generate Full HTML Report записывает туда же индекс на каждую папку и страницу на каждый файл; Export Coverage Report… предлагает те же восемь форматов и спрашивает, куда сохранить; Generate Coverage Badge записывает значок туда, куда указывает badge.outputPath, — после каждой загрузки при badge.autoUpdate. Аннотированной странице нужен WebView2, как и окну Coverage; отчётам на диске — нет.

Visual Studio: Show Annotated Source — the file as a page in the document well, beside the editorScreenshot pending — add assets/img/vs-annotated-source.png (see SCREENSHOTS.md).
В Visual Studio: Show Annotated Source — файл как страница в области документов, рядом с редактором.
Multi-page HTML report directory indexScreenshot pending — add assets/img/report.png (see SCREENSHOTS.md).
The report, Directory Coverage Summary: one row per folder, a column for every metric the run carriesScreenshot pending — add assets/img/report-directory-summary.png (see SCREENSHOTS.md).
The report, File Coverage Summary: one row per file, with Max CRAP where the report carries complexityScreenshot pending — add assets/img/report-file-summary.png (see SCREENSHOTS.md).
The report, Coverage Gaps: every function with uncovered branches or MC/DC conditions, named as writtenScreenshot pending — add assets/img/report-coverage-gaps.png (see SCREENSHOTS.md).
The report, MC/DC Expression Detail: conditions covered per decisionScreenshot pending — add assets/img/report-mcdc-detail.png (see SCREENSHOTS.md).
Export Coverage Report: CSV per file or per function, a Markdown summary, LCOV, Cobertura XML, SARIF, or an SVG badgeScreenshot pending — add assets/img/export-picker.png (see SCREENSHOTS.md).
The report’s annotated page for src/calc.c: per-line hit counts, a fully covered function, an uncovered else branch, a 1/2 partial branch, an MC/DC expansion and a never-called functionScreenshot pending — add assets/img/annotated-source.png (see SCREENSHOTS.md).

Аннотированный исходный код и обычный исходный код

Open Annotated Source заменяет активную вкладку цветной веб-панелью покрытия для этого файла в том же столбце; Open Annotated Source to the Side делает то же самое, но в разделённом виде, чтобы исходный код и аннотированное представление располагались рядом. Чтобы переключаться на том же месте — удобно на маленьком экране, где разделение делает каждую панель слишком узкой — используйте Toggle Annotated Source: она переключает активную вкладку между этими двумя видами, запоминая, какой из них вы сейчас просматриваете. Из самого аннотированного представления Show Source сразу возвращает к обычному редактору для этого файла.

То, какой именно HTML вы увидите, зависит от externalHtmlReport.dir: если он задан и там уже существует страница, соответствующая активному файлу (llvm-cov раскладывает постраничные файлы как <имя-репозитория>/<относительный-путь>.html, поэтому папку с именем репозитория отчёта никогда не нужно настраивать), показывается именно эта сгенерированная набором инструментов страница вместо встроенного рендерера — её локальные ссылки/таблица стилей переписываются для работы внутри веб-панели, и она содержит детали, которые встроенное представление не воспроизводит (представления раскрытия MC/DC, подсветка регионов, сводки по каталогам). Нет соответствующей страницы для этого файла, или настройка оставлена пустой? Тогда используется встроенный рендерер. Расширение только читает этот каталог — оно никогда в него не записывает.

Значок покрытия для README

Generate Coverage Badge записывает в рабочую область SVG в стиле shields для активного прогона — badge.outputPath, по умолчанию coverage-badge.svg, относительно папки рабочей области или абсолютно, с теми же переменными, что и в любой другой настройке путей, — и предлагает его открыть. Это обычный файл в вашем репозитории, так что README встраивает его без какого-либо сервиса за ним, без обращения к shields.io, и он работает и в изолированном от сети репозитории:

![coverage](coverage-badge.svg)

coverage: 83%

badge.metric выбирает, что показывает значок: lines (с подписью coverage), branches, mcdc или weighted — та же комплексная оценка, что и в строке состояния, с подписью score. Прогон без данных для выбранной метрики получает серое n/a, а не красные 0%. Цвет следует вашим собственным уровням colorScale.*: lowColor ниже lowThreshold, midColor до highThreshold, highColor от него и выше. При включённом badge.autoUpdate значок перезаписывается после каждой успешной загрузки, молча, а канал вывода фиксирует каждую запись как [FUCINI_BADGE] — по умолчанию выключено, потому что пишет в рабочую область при каждой загрузке. Закоммитьте SVG вместе с кодом — и картинка на странице репозитория следует за числом в редакторе.

{
  "fuciniCoverage.badge.outputPath": "docs/coverage-badge.svg",
  "fuciniCoverage.badge.metric": "weighted",
  "fuciniCoverage.badge.autoUpdate": true
}

14. История и тренды

Включите history.enabled, чтобы локально сохранять прогоны (сохраняя history.retention из них). Затем вы получите стрелку тренда (▲/▼) в строке состояния, график тренда в представлении Coverage и две команды сравнения: Compare with Last Run и Compare Two History Runs, каждая из которых формирует пофайловое сравнение регрессии/улучшения.

В Visual Studio. Каждая загрузка сравнивается с предыдущей: треугольник на полях у каждой строки, которая начала или перестала выполняться (sinceLastRun.enabled), тренд рядом с оценкой в строке состояния и Compare with Last Run для файлов, чьи числа изменились. При history.enabled запуски хранятся в %LOCALAPPDATA%\Fucini\CoverageStudio\history, в количестве history.retention, так что сравнение переживает перезапуск, графику тренда есть что рисовать, а Compare Two History Runs… выбирает любые два из них.

Compare with Last Run: the run moved from 83.89% to 83.05%, calc.c lost 4.54 points, and the status bar carries the trendScreenshot pending — add assets/img/history-compare.png (see SCREENSHOTS.md).
Since last run: a red mark on the lines that ran in the previous run and not in this one, a green one on the lines that are newly coveredScreenshot pending — add assets/img/since-last-run.png (see SCREENSHOTS.md).

График тренда

Под панелью инструментов представления Coverage, при двух сохранённых прогонах и более, покрытие строк из истории в виде линии, от старейшего к новейшему, последние тридцать: точка на прогон, которая при наведении говорит его дату и цифру, и последняя цифра рядом со стрелкой направления с первого. Вертикальная шкала — собственный диапазон прогонов, так что команда на 92–94% видит движение, а не плоскую линию под 100. Обе редакции рисуют его из одной истории.

The trend chart under the Coverage view's toolbar: a line over a dozen runs, a dot per run, the last figure and an arrow upScreenshot pending — add assets/img/trend-chart.png (see SCREENSHOTS.md).
Тренд: точка на каждый сохранённый прогон, последняя цифра рядом.
Visual Studio: the trend line under the Coverage window toolbar, the last figure 68.25%
В Visual Studio: тренд под панелью инструментов окна Coverage, последнее значение 68.25%.

Сессии покрытия

Один тестовый прогон редко покрывает всё, чего коснулось изменение: модульные тесты прогнали здесь, интеграционный набор там, проход по интерфейсу с включённым покрытием где-то ещё. Start Coverage Session превращает последующие загрузки в одну сумму — каждый прогон добавляется к предыдущему строка за строкой как объединение, строка, выполненная любым из них, покрыта, а счётчики складываются, — так что поле редактора, дерево и оценка показывают, куда они дошли все вместе. Прогон на экране, если он есть, — первый в сессии. Полоса над деревом говорит, сколько прогонов и с какого момента, и завершает сессию; End Coverage Session делает то же самое, и следующая загрузка снова стоит сама по себе. В Visual Studio один пункт в Tools → Coverage Studio начинает сессию, а отмеченный — завершает её.

A coverage session: the unit test run loads, the session starts, the integration run loads on top, and the bar says two runs while the tree's numbers riseScreenshot pending — add assets/img/coverage-session.gif (see SCREENSHOTS.md).
Сессия из двух прогонов: второй добавлен к первому, полоса над деревом считает их.
Visual Studio: the unit run loaded, Start Coverage Session, the integration run added, the bar saying 2 runs and lines at 68.25%, then the session ended
В Visual Studio: Start Coverage Session, и второй прогон добавлен к первому — панель считает два прогона, строки растут с 50.79% до 68.25%.

Базовая линия CI: где я относительно main?

Compare with Last Run отвечает, что сделало ваше последнее изменение; перед pull request вопрос в том, где вы относительно main, и ответ — на сборочном сервере. baseline.report называет отчёт, который публикует сборка ветки main, и каждая загрузка сверяется с ним: карточка в строке состояния несёт однострочную дельту — vs CI baseline: 84.20% → 85.10% (+0.90%), 3 file(s) moved, — а Compare with CI Baseline перечисляет изменившиеся файлы, файл за файлом, теми же словами, что и Compare with Last Run. В Visual Studio команда находится в Tools → Coverage Studio и запрашивает или читает отчёт, когда вы попросите. Три способа назвать его, в .vscode/settings.json:

  1. URL, который отдаёт CI. GitLab держит последние артефакты задания ветки по стабильному адресу; токен остаётся вне файла настроек благодаря ${env:…} в remote.headers. Jenkins: https://jenkins.example.com/job/app/job/main/lastSuccessfulBuild/artifact/coverage/lcov.info. TeamCity: https://tc.example.com/repository/download/App_Main/.lastSuccessful/coverage/lcov.info с Authorization: Bearer ${env:TC_TOKEN}. URL запрашивается и кэшируется точно так же, как URL из coverageFile.paths (ETag, remote.refreshMinutes).
  2. Файл, скопированный конвейером на общий ресурс — абсолютный путь вроде \\build\drops\app\main\coverage.cobertura.xml.
  3. Скачанный артефакт, хранимый в папке, скажем ci/main.lcov.info (внесите папку в .gitignore), — путь относительно папки.
{
  "fuciniCoverage.baseline.report": "https://gitlab.example.com/api/v4/projects/42/jobs/artifacts/main/raw/coverage/lcov.info?job=test",
  "fuciniCoverage.remote.headers": ["PRIVATE-TOKEN: ${env:GITLAB_TOKEN}"]
}

Подойдёт любой формат, который знает читатель. Путь, записанный CI относительно своего checkout — src/Billing/Invoice.cs, как его пишет Coverlet на раннере, — перед сравнением помещается под эту папку, чтобы встретиться с файлом, который разрешил локальный прогон; абсолютный путь с другой машины не совпадает ни с чем, так что пусть конвейер пишет относительные пути. Базовая линия, которую нельзя запросить или прочитать, — это строка в панели Output, но никогда не неудавшаяся загрузка.

Compare with CI Baseline: the status bar's card with the vs CI baseline line, then the command listing the files that moved against main, regressions firstScreenshot pending — add assets/img/ci-baseline.gif (see SCREENSHOTS.md).
Базовая линия CI: дельта на карточке и файлы, изменившиеся относительно main.
Visual Studio: baseline.report set to the unit suite report, then Compare with CI Baseline showing statements 50.79% to 68.25% and branches 30.43% to 50.00%, one file changed
В Visual Studio: Compare with CI Baseline — операторы и ветви против отчёта модульных тестов и файл, который изменился.

Артефакт — это zip, и он тоже читается. GitHub и GitLab отдают артефакт в виде архива, что бы в него ни положили. Назовите отчёт внутри него после #: элемент архива находится по своему пути или по его окончанию; без # берётся единственный файл архива или единственный его файл, похожий на отчёт, а если таких несколько, панель Output их называет. То же относится к скачанному вами zip (ci/artifact.zip#lcov.info) и, в VS Code, к URL в coverageFile.paths. Токен уходит на сервер, для которого он задан, и не дальше: переход по перенаправлению на хранилище, где лежит архив, выполняется без него.

{
  "fuciniCoverage.baseline.report": "https://api.github.com/repos/acme/app/actions/artifacts/1234567/zip#coverage/lcov.info",
  "fuciniCoverage.remote.headers": ["Authorization: Bearer ${env:GITHUB_TOKEN}"]
  }

14a. Для ИИ-агентов: покрытие на экране

Агент, который пишет тесты, может запустить их и прочитать процент. Чего он не может сам — спросить о том, что знает редактор: какие строки этой функции ни разу не выполнялись, какая сторона какой ветви, какие тесты доходят до строки, покрыт ли diff, который он только что написал. Шесть инструментов отвечают именно на это — словами, по которым агент может действовать, по загруженному прогону: те же исключения, то же переотображение, те же числа, что на экране. Поэтому агент, которому велели закрыть пробелы в покрытии функции, и рецензент, глядящий на поле редактора, говорят об одних и тех же строках.

ИнструментНа что отвечает
coverage_summaryИтоги прогона по каждой метрике и файлы, которым дальше всего до полного покрытия. Первое, о чём стоит спросить.
coverage_gapsЧто ни разу не выполнялось в файле, функции или диапазоне строк: непокрытые строки, непройденные ветви, условия MC/DC без своей пары, выжившие мутанты.
tests_for_lineКакие тесты выполнили строку — там, где отчёт это говорит, то есть при прогоне с разбивкой по тестам.
patch_coverageПокрытие diff — рабочего дерева относительно ссылки или переданного патча — и его строки, которые ни разу не выполнялись.
coverage_deltaЧто изменилось с предыдущего прогона: итоги и файлы, которые прибавили или потеряли.
test_run_commandКоманда, которая запускает тесты этой рабочей области с включённым покрытием. Агент запускает её; редактор сам перезагружает отчёт.

У собственного агента VS Code они есть как инструменты, без всякой настройки. Все остальные агенты — любой клиент, говорящий по MCP, Model Context Protocol, — получают их через сервер, который редактор запускает, когда включён mcp.enabled, и в VS Code, и в Visual Studio. Copy MCP Server Configuration помещает в буфер обмена запись для клиента в том виде, в каком VS Code и Visual Studio читают её в mcp.json; клиент, у которого внешний ключ называется mcpServers, принимает ту же запись под этим именем.

Copy MCP Server Configuration and its message, the token painted overScreenshot pending — add assets/img/mcp-config.png (see SCREENSHOTS.md).
Visual Studio: Copy MCP Server Configuration and its message; the token painted overScreenshot pending — add assets/img/vs-mcp-config.png (see SCREENSHOTS.md).
В Visual Studio: Copy MCP Server Configuration и его сообщение; токен закрашен.
{
  "servers": {
    "fucini-coverage": {
      "type": "http",
      "url": "http://127.0.0.1:39217/mcp",
      "headers": { "Authorization": "Bearer …" }
    }
  }
  }
  • Он выключен, пока вы его не включите, и слушает только на 127.0.0.1: ничто за пределами этого компьютера до него не доходит.
  • Вызов должен нести ваш токен, который создаётся один раз и хранится для вас; без него ответ — 401. Скопированная конфигурация содержит токен: не допускайте её в репозиторий.
  • Вызов, отправленный веб-страницей, — он сам говорит об этом в своём Origin — отклоняется, что бы он ни нёс.
  • Он читает загруженный прогон и ничего не записывает. Тестовый прогон называется, но не запускается: команду агент выполняет в собственном терминале.

Порт — mcp.port, 39217, если не задан. Если он занят — вторым окном, другой редакцией рядом с этой, — используется следующий свободный, а скопированная конфигурация называет тот, который работает. Панель Output говорит, где сервер слушает ([FUCINI_MCP]) и почему не смог ([FUCINI_MCP_001]). То, что план не включает, — имена тестов, подробности MC/DC — инструменты считают, не раскрывая, как это делает подсказка при наведении.

15. Справочник команд

В Visual Studio. Все команды — в Tools → Coverage Studio, с теми же названиями, что в таблице, без префикса Fucini Coverage:; панель инструментов окна Coverage и контекстное меню строки содержат те, что там уместны, а контекстное меню окна кода — три, которые действуют на редактор: Run This Test with Coverage, Show Tests That Ran This Line и Exclude Selection from Coverage. Команды с клавишами — двухклавишные аккорды после Ctrl+Shift+U: R перезагружает, N и P переходят к следующей и предыдущей непокрытой строке, B — к следующему частично протестированному решению, T перечисляет тесты, выполнившие строку, X исключает выделение; это не Alt+N из VS Code, потому что в Visual Studio Alt с буквой открывает меню. Измените их в Tools → Options → Keyboard, где они перечислены как Coverage Studio: …. Нет в этой редакции: панель Test Coverage — это API VS Code, вместо неё прогон показан в окне Coverage, а Solution Explorer не может показывать значки. Только в этой редакции: Run Native Project with Coverage, для стартового проекта C++.

В Visual Studio: где что находится. Окно Coverage открывается через View → Other Windows → Coverage, а также первым пунктом в Tools → Coverage Studio, где собраны все команды этой редакции, по группам: загрузка и перезагрузка, слежение за файлом, две команды копирования и карточки; Run Tests with Coverage, Run Native Project with Coverage и Collect Coverage in Test Explorer…; Next и Previous Uncovered и частично проверенные решения; раскраска, счётчики выполнений и режимы полосы; источник покрытия файла, тесты, выполнившие строку, один тест с покрытием, два способа исключения; Compare with Last Run, Compare Two History Runs…, Compare with CI Baseline, сессия покрытия и Check Coverage Thresholds; группа Reports — Show Annotated Source, Open HTML Report, Generate Full HTML Report, Export Coverage Report… и Generate Coverage Badge; затем Getting Started, пороги соответствия и Licence…. Собственная панель инструментов окна содержит пять самых нужных: Load Coverage, Reload, Toggle Coverage Painting, Copy Coverage Summary и пороги соответствия; под ними перспектива, фильтр и сортировка, и поле поиска; под ними тренд. Наведите указатель на строку дерева и нажмите её ⋯, чтобы открыть меню строки — открыть файл, открыть его папку, скопировать путь, абсолютный или относительный, с любым видом косой черты, исключить его из покрытия. Справа в строке состояния — оценка запуска и оценка открытого файла с трендом с последнего запуска; Show Coverage Card и Show Coverage Card for This File открывают карточки за ними — те же, что VS Code показывает, когда указатель останавливается на его элементе строки состояния.

Visual Studio: Tools → Coverage Studio, open, with every command of the edition
В Visual Studio: Tools → Coverage Studio со всеми командами этой редакции.
Visual Studio: View → Other Windows, where the Coverage window is
В Visual Studio: View → Other Windows, где находится окно Coverage.
Visual Studio: the Coverage window's toolbar — Load Coverage, Reload, Toggle Coverage Painting, Copy Coverage Summary, compliance thresholds
В Visual Studio: панель инструментов окна Coverage — Load Coverage, Reload, Toggle Coverage Painting, Copy Coverage Summary и пороги соответствия.
Visual Studio: a row's menu in the Coverage window — Open, Open Containing Folder, and the four Copy Path entries
В Visual Studio: меню ⋯ строки — открыть файл, открыть его папку, скопировать путь в четырёх формах.
Visual Studio: the status bar's two items, the run's score and the open file's
В Visual Studio: два элемента строки состояния — оценка прогона и открытого файла.
Visual Studio: the run's coverage card, opened by Show Coverage Card
В Visual Studio: карточка покрытия прогона, из Show Coverage Card.
Visual Studio: the open file's coverage card, opened by Show Coverage Card for This File
В Visual Studio: карточка открытого файла, из Show Coverage Card for This File.
The status-bar hover cards for the run and the file, then the Coverage view toolbar: Pick Coverage File, Load Coverage, Export Coverage Report and Generate Full HTML Report, ending on the generated reportScreenshot pending — add assets/img/statusbar-and-toolbar.gif (see SCREENSHOTS.md).
В VS Code: всплывающие карточки строки состояния для прогона и файла, затем панель инструментов представления Coverage, в конце — сгенерированный отчёт.
The status-bar menu: select a coverage file, run tests with coverage, reload, open the HTML report, choose the gutter modeScreenshot pending — add assets/img/statusbar-menu.png (see SCREENSHOTS.md).
В VS Code: меню строки состояния — выбрать файл покрытия, запустить тесты с покрытием, перезагрузить, открыть отчёт, выбрать режим поля редактора.
Show Worst-Covered Functions: every function of the run, lowest coverage first, with its CRAP score where the report carries complexityScreenshot pending — add assets/img/worst-functions.png (see SCREENSHOTS.md).
В VS Code: Show Worst-Covered Functions, сначала наименее покрытые, с оценкой CRAP там, где отчёт содержит сложность.
КомандаЧто делает
Load CoverageНайти и загрузить покрытие для рабочего пространства. По умолчанию Alt+Shift+R (Ctrl+Alt+R на macOS).
Pick Coverage FileВыбрать файлы трассировки — один, несколько или ещё один каждый раз; выбранные файлы складываются, сливаются и записываются в coverageFile.paths, так что дальше загружаются сами.
Show Loaded Coverage FilesПеречислить файлы трассировки активного прогона. Представление «Покрытие» тоже показывает их, каждый с кнопкой выгрузки и кнопкой выбора рядом.
Toggle Tracefile WatchСледить за изменениями файлов трассировки и перезагружать автоматически.
Run Tests with CoverageРаспознать тестовый раннер, запустить его с покрытием в задаче терминала, загрузить записанное и предложить добавить путь в настройки.
Run This Test with CoverageТест под курсором — метод, класс или блок describe, либо файл — запускается рецептом, суженным до него, затем загружается как любой прогон. Также в контекстном меню редактора.
Show Tests That Ran This LineТам, где отчёт говорит, какие тесты что выполнили, перечислить тесты, выполнившие строку под курсором, самые активные первыми, и открыть выбранный там, где он написан.
Run Tests Affected by My Changes…Перечислить тесты, выполнившие строку, которую рабочее дерево меняет относительно testImpact.baseRef, открыть выбранный или запустить только их там, где отчёты получены прогоном с разбивкой по тестам.
Show Minimal Test Set…Наименьшее число тестов, которые всё ещё выполняют каждую строку, выполняемую набором, затем тесты, не добавляющие ни одной своей строки.
Run App with Coverage…Запустить приложение .NET из папки под dotnet-coverage и, когда оно закроется, загрузить то, через что вы прошли.
Run Mutation Tests HereМутировать функцию, в которой стоит курсор, — или выделение, или файл — с помощью Stryker.NET, StrykerJS или PIT и загрузить отчёт рядом с покрытием на экране.
Exclude This File from Coverage / Exclude from CoverageПоместить открытый файл — или, на строке представления Coverage, файл либо /** папки — в fuciniCoverage.ignore в настройках папки, один раз.
Exclude Selection from CoverageЗаписать исключение там, где код: [ExcludeFromCodeCoverage] над членом .NET, LCOV_EXCL_LINE в одной строке, LCOV_EXCL_START/STOP вокруг нескольких, знаком комментария самого файла. Также лампочка и контекстное меню редактора.
Start Coverage Session / End Coverage SessionПока идёт сессия, каждая загрузка добавляется к предыдущей как объединение; полоса над деревом считает прогоны и завершает её.
Next / Previous UncoveredПереходить между непокрытыми регионами. По умолчанию Alt+N / Alt+P (Ctrl+Alt+N / Ctrl+Alt+P на macOS).
Choose Editor Gutter Mode...Выключено, полосы только на непокрытых строках, красно-янтарно-зелёные полосы или тепловая карта попаданий в поле редактора.
Cycle Editor Gutter ModeОдин шаг дальше по кругу off, uncovered, coverage и heatmap — Alt+G или кнопка режима в строке состояния.
Choose Coverage Source for This File...Когда открытый файл измеряет больше одного загруженного отчёта, выберите, из какого берутся его числа — для файла, его папки или шаблона; записывает правило coverageFile.sources.
Clear Coverage Source for This File...Убрать правила coverageFile.sources, относящиеся к открытому файлу.
Toggle Inline Hit CountsПоказать или скрыть числа выполнений ×N после каждой строки — переключает hitCounts.inline.
Show in Test Coverage ViewОпубликовать активный прогон в собственную панель Test Coverage VS Code (даже при выключенном testCoverage.publish) и открыть её.
Suggest Path Remap RuleВывести правило pathRemap, которое сопоставило бы несопоставленные файлы активного прогона с рабочей областью, и предложить его добавить.
What Community IncludesКаждое ограничение и каждая невключённая функция бесплатного уровня в одном списке, с тем, сколько каждого ограничения использовано.
Open HTML ReportОткрыть сформированный статический отчёт.
Generate Full HTML ReportСобрать многостраничный отчёт.
Open Annotated Source / …to the SideАннотированный просмотр покрытия для файла.
Toggle Annotated SourceПереключиться между кодом и аннотированным представлением. По умолчанию Alt+A (Ctrl+Alt+A на macOS).
Show Worst-Covered Files / Folders / FunctionsQuick Pick, отсортированный от худшего.
Copy Uncovered LinesСводка в Markdown по каждому непокрытому диапазону.
Compare Two History Runs / with Last RunПофайловая разница регрессии — два прогона из истории или прогон на экране относительно того, который он заменил.
Compare with CI BaselineЗагруженный прогон относительно отчёта, названного в baseline.report — отчёта ветки main из CI, URL или скачанного артефакта — файл за файлом.
Show Indirect Coverage Changes…Строки, покрытие которых изменилось, хотя никто их не правил, — относительно базовой линии CI или предыдущего прогона, по файлам.
Check Coverage ThresholdsОценить проверки соответствия.
Export Fucini JSONЭкспортировать текущий запуск как Fucini JSON.
Export Coverage Report...Выберите формат — CSV, Markdown, LCOV, XML Cobertura, SARIF или значок SVG — и место назначения.
Generate Coverage BadgeЗаписать SVG-значок для активного прогона в badge.outputPath, показывающий badge.metric, и предложить его открыть.
Copy MCP Server ConfigurationПоместить в буфер обмена то, что нужно клиенту MCP, чтобы спрашивать о покрытии на экране, — адрес сервера и ваш токен; при выключенном сервере предложить его включить.
Show Status Bar MenuОткрыть меню быстрых действий.
Browse for External HTML Report / Full Report Output Dir…Задать каталоги отчётов через диалог выбора.

16. Справочник настроек

Полная категоризированная таблица настроек — каждый параметр со значением по умолчанию — находится на странице Возможности и настройки. Все ключи имеют префикс fuciniCoverage..

Стоит отметить: fuciniCoverage.language закрепляет собственный интерфейс расширения во время работы (строка состояния, уведомления, подсказки, веб-представления) за определённым языком, независимо от языка интерфейса VS Code — по умолчанию установлено значение auto, поставляются пакеты для всех 18 поддерживаемых языков. Это не меняет пункты палитры команд или подписи в настройках, которые VS Code по-прежнему определяет по собственному языку интерфейса.

В Visual Studio. Те же ключи fuciniCoverage.*, читаемые максимум из четырёх файлов, поздний побеждает: собственные пользовательские настройки VS Code, если VS Code есть на машине — читаются, никогда не записываются, чтобы ваши цвета и пороги перешли вместе; %APPDATA%\Fucini\CoverageStudio\settings.json, пользовательские настройки этой редакции; .vscode\settings.json в папке или рядом с решением, так что репозиторий, настроенный для VS Code, настроен для Visual Studio; и fucini-coverage.json в корне решения — для команды, которая предпочла бы не держать папку .vscode в репозитории .NET; редакция для VS Code читает его тоже. Шестерёнка на панели инструментов окна Coverage открывает окно настроек, которое записывает пользовательский файл или тот файл папки, что существует, как говорит его переключатель Write changes to, и сохраняет комментарии файла, который редактирует. fuciniCoverage.language там работает так же; меню следуют языку самой Visual Studio.

17. Коды ошибок

Диагностика выводится в канал вывода Fucini Coverage (View → Output → Fucini Coverage), каждая со стабильным кодом FUCINI_*.

В Visual Studio. Те же коды идут в панель Fucini Coverage окна Output (View → Output) и сохраняются в %LOCALAPPDATA%\Fucini\CoverageStudio\coverage-studio.log — для вопроса, который приходит после закрытия IDE. Три, что встречаются: ничего не отмечено, хотя панель говорит, что файлы загружены — режим полей off, выберите Show Coverage in Editor; FUCINI_REMAP_001 и информационная полоса — отчёт называет пути, которых здесь нет, и панель предлагает правило pathRemap; окно Coverage говорит, что ему нужен WebView2 — установите Microsoft Edge WebView2 Runtime, меткам в редакторе он не нужен.

КодЗначение
FUCINI_PARSE_001Неподдерживаемый/нераспознанный формат покрытия — ни один парсер не подошёл к файлу.
FUCINI_PARSE_010В файле lcov нет записей SF.
FUCINI_PARSE_020JSON llvm-cov недействителен, отсутствует data[] или нет файлов.
FUCINI_PARSE_030Корневой элемент Cobertura <coverage> отсутствует или XML недействителен.
FUCINI_PARSE_040Корневой элемент JaCoCo <report> отсутствует или нет исходных файлов.
FUCINI_PARSE_050JSON Istanbul/SimpleCov недействителен или не содержит файлов.
FUCINI_PARSE_060JSON SimpleCov недействителен или отсутствует массив files верхнего уровня.
FUCINI_PARSE_070Профиль Go без заголовка mode: либо без блоков покрытия.
FUCINI_PARSE_080Отсутствует корневой элемент Clover <coverage> или <project>, либо в нём нет файлов.
FUCINI_PARSE_090Отсутствует корневой элемент OpenCover <CoverageSession> либо в нём нет файлов.
FUCINI_PARSE_100JSON coverage.py недействителен либо в нём нет объекта files верхнего уровня.
FUCINI_PARSE_110JSON gcov/gcovr недействителен либо в нём нет массива files верхнего уровня.
FUCINI_PARSE_120GNATcoverage XML без элемента <source> или index.xml, чьи xi:include так и не были раскрыты — загрузите вместо него файлы *.xml для каждого исходника.
FUCINI_PARSE_130Отсутствует корневой элемент <BullseyeCoverage> или в нём нет элементов <src>.
FUCINI_PARSE_140Отсутствует корневой элемент <CoverageDSPriv>, нет таблицы <SourceFileNames> для сопоставления строк или нет данных о строках.
FUCINI_PARSE_150Некорректный JSON покрытия Xcode или отсутствует массив targets верхнего уровня.
FUCINI_PARSE_160Отсутствует корневой элемент <coverage> SonarQube или в нём нет элементов <file>.
FUCINI_PARSE_170Некорректный JSON Coveralls или отсутствует массив source_files верхнего уровня.
FUCINI_LOADЗагрузка отменена из своего уведомления о ходе выполнения; предыдущий прогон, если он был, остаётся загруженным.
FUCINI_REMOTE_001URL из coverageFile.paths не удалось скачать, с указанием причины; вместо него загружается последняя скачанная копия. Обычные строки [FUCINI_REMOTE] фиксируют каждое скачивание и каждую проверку без изменений (304).
FUCINI_REMOTE_002URL из coverageFile.paths не дал полного ответа за 30 секунд — чаще всего это прокси или VPN, который принимает соединение и затем молчит; вместо него загружается последняя скачанная копия.
FUCINI_REMOTE_003URL из coverageFile.paths ответил HTML-страницей вместо файла трассировки — скорее всего, страницей входа. Страница не кэшируется, вместо неё загружается последняя скачанная копия; проверьте токен в remote.headers.
FUCINI_REMAP_002Для путей, оставшихся после загрузки несопоставленными, предложено правило pathRemap, с числом путей, которые оно объясняет, — либо вывести ничего не удалось, либо выведенное правило уже есть в настройке и их не разрешило.
FUCINI_REMAP_003Предложенное правило добавлено в fuciniCoverage.pathRemap командой Apply Suggested Remap, и покрытие перезагружается.
FUCINI_GITDIFF_001Расширение Git недоступно; покрытие diff пропускается и показывается весь прогон.
FUCINI_GITDIFF_002gitDiff.baseRef начинается с -, с чего ни одна ссылка git начинаться не может (git прочитал бы его как опцию); покрытие diff пропускается и показывается весь прогон.
FUCINI_IGNOREСколько файлов fuciniCoverage.ignore исключил из прогона, и какие именно.
FUCINI_UNMEASURED_001Файлы того же вида, что и прогон, которые не загружал ни один тест, по одному на строку.
FUCINI_UNMEASURED_002Более 5 000 файлов рабочей области подошли под одно из расширений прогона; для списка Unmeasured рассмотрены только первые 5 000.
FUCINI_UNMEASURED_003В режиме count некоторые неизмеренные файлы перечислены, но не засчитаны: достигнут предел чтения (2 000 файлов или 50 МБ за загрузку).
FUCINI_UNMEASURED_004Неизмеренный файл не удалось прочитать; он перечислен, но не засчитан.
FUCINI_TESTCOVERAGEСколько файлов опубликовано в представление Test Coverage VS Code.
FUCINI_BADGEЗначок записан, с его путём, подписью и значением.
FUCINI_BADGE_001Значок не удалось записать, с указанием причины.
FUCINI_WORKSPACEПапка добавлена в рабочую область или удалена из неё; покрытие обнаруживается заново.
FUCINI_VARIABLESЧто ответила каждая ${command:…} в настройках путей или почему её оставили как есть: команда не сработала, вернула что-то непригодное или рабочая область не доверенная.
FUCINI_WATCHЧто делает наблюдатель: отслеживаемые пути, появившийся настроенный файл, завершившаяся задача из watch.reloadAfterTasks (с её кодом выхода), вызвавшая перезагрузку, или путь под files.watcherExclude, который вместо этого опрашивается.

18. Конфиденциальность

Телеметрией управляет параметр telemetry.enabled с областью window. По умолчанию он выключен. Для ежедневной сводки и уведомления об удалении также нужно, чтобы глобальный telemetry.telemetryLevel VS Code разрешал телеметрию; сигнал об установке и несколько небольших событий отправляются независимо от него, как и запись в журнале использования для установки в пробном периоде или на бесплатном уровне Community. На Community команда Unlock Full Features обменивает эту телеметрию на все возможности Pro, включая MC/DC, на 30 дней, один раз на машину — выключите его снова, и действует стандартный набор Community; на платную лицензию это не влияет ни в том, ни в другом случае. Что отправляется, см. в политике конфиденциальности.

Знать стоит о двух вещах, потому что с ними вы можете что-то сделать. Первая — ваш идентификатор журнала использования: в пробном периоде и на уровне Community расширение показывает его в Show License / Activation и внизу раздела What Community Includes, где выбор этой строки копирует идентификатор и открывает политику. Журнал знает вашу машину по хешу и больше никак, поэтому этот идентификатор — единственный способ спросить, что журнал хранит о вас, потребовать удаления или заявить возражение: укажите его — и мы найдём вашу строку; без него не найдём. Вторая — запись об установке: одна строка, создаваемая при первом обращении установки и сохраняемая, а не удаляемая через 90 дней; в ней записаны идентификатор установки, день, место, определённое по её адресу, версии и операционная система — но не сам адрес и ничего о вашем коде. Именно она позволяет нам сказать, сколько установок было за все эти годы. И то и другое полностью описано в политике конфиденциальности, и то и другое мы удалим по вашей просьбе.

The bottom of What Community Includes: the ways to lift the caps, and How these are counted — the row that copies your usage ledger ID and opens the privacy policy
Конец раздела What Community Includes: как считаются лимиты и строка, которая выдаёт вам идентификатор журнала использования.

19. Оценка и активация

Fucini Coverage Studio распространяется как ознакомительная предварительная версия. При первом запуске вас просят согласиться её оценивать, и начинается пробный период — все возможности доступны в течение 14-дневного пробного периода, отсчитываемого от первого использования на этой установке. Нет фиксированной даты, после которой что-либо перестаёт работать.

Начиная с версии 2.2026.2 пробный период новой установки начинается, как только расширение один раз свяжется с сервером лицензий; до тех пор оно работает на уровне Community и сообщает об этом. В контейнере разработки или Codespace пробный период не начинается — полный набор там открывает код подписки, ключ или 7-дневный пропуск.

Когда пробный период заканчивается, ничего не блокируется: расширение переходит на бесплатный уровень Community. Чтобы вернуть полный набор, активируйте — активация подключается к серверу лицензий и применяет полученный ключ автоматически, без электронной почты и без ключа, который пришлось бы переносить вручную:

Show License / Activation: the trial state, and the buttons to enter a subscription code, see the plans or request a licenceScreenshot pending — add assets/img/licence-dialog.png (see SCREENSHOTS.md).
  1. Выполните Fucini Coverage: Show License / Activation (также доступно из строки состояния после окончания пробного периода или срока действия ключа) и нажмите Request License… (или Request Renewed Key…, если вы активировали ранее).
  2. Подтвердите запрос. Ключ выдаётся автоматически, а ваши контактные данные из предыдущего запроса используются повторно, а не запрашиваются снова.
  3. Готово. Ключ активации генерируется и автоматически записывается в fuciniCoverage.license.key. Строка состояния через мгновение переключается с пробного периода на активированную лицензию.

Ручной путь на одной картинке: откройте панель лицензии, выберите Enter Key, вставьте ключ из письма — и баннер переключается на «Activated».

Activating by hand: the licence panel, the activation dialog, Enter Key, the pasted key, and the banner turning to ActivatedScreenshot pending — add assets/img/activate-key.gif (see SCREENSHOTS.md).

Если автоматический запрос всё же не сработает (нет соединения, проблема на сервере): выполните Fucini Coverage: Copy Activation Request Code, отправьте код через страницу контактов или на info@fucini.consulting, а полученный в ответ ключ FCK1.… вставьте командой Fucini Coverage: Enter Activation Key (или напрямую в settings.json):

{ "fuciniCoverage.license.key": "FCK1.…вставьте ключ целиком…" }

Название настройки — fuciniCoverage.license.key (область пользователь/глобально). Ключ привязан к вашей машине и пользователю ОС, может иметь срок действия и может включать или ограничивать отдельные функции и ограничивать число загрузок покрытия в день. Если отображается «invalid for this machine», запросите новый ключ из этой установки — ключ, выданный для другой машины, здесь никогда не пройдёт проверку.

В Visual Studio. В этой редакции нет бесплатного уровня: без подписки ничего не читается. С подпиской план решает там то же, что и в VS Code: функция, которую план не включает, недоступна, а её команда сообщает об этом вместо того, чтобы выполняться. Самая старая версия, которую ещё поддерживает сервер лицензий, действует и там: сборка ниже неё получает сообщение раз в день, с датой, когда она перестанет работать, и до тех пор продолжает работать. Отсутствие сети никогда не останавливает сборку.

Бесплатный уровень Community: недельный лимит

Community никогда не перестаёт показывать покрытие. Поля редактора, обзорная линейка, всплывающие подсказки, CodeLens, представление Coverage, режим наблюдения, покрытие изменений (diff) и история не учитываются вообще, а загрузка трасс-файла командой ограничена лишь 25 разами в день. Считается только вывод: по пять раз в неделю — открытие HTML-отчёта, экспорт, сравнение двух прогонов, проверка порогов и просмотр аннотированного исходного кода на месте.

Неделя — ваша, а не календарная: она начинается с первого использования и заканчивается через семь дней. Когда лимит исчерпан, сообщение называет действие и день, когда оно снова станет доступно, а после этого единственного сообщения о нём напоминает только строка состояния. Fucini Coverage: What Community Includes показывает каждый лимит: сколько израсходовано и когда он обновится — ещё до того, как вы его достигнете.

The weekly allowance: five uses of Open HTML Report across three days, a sixth refused with the day the count resets, and the week ending seven days after the first use; gutters, hovers, CodeLens and the Coverage view are never countedScreenshot pending — add assets/img/weekly-allowance.png (see SCREENSHOTS.md).
The message at a Community limit: Checking coverage thresholds, all 5 used, the day it resets, and the buttons Subscribe, 7-Day Pass and What Community IncludesScreenshot pending — add assets/img/community-limit-prompt.png (see SCREENSHOTS.md).
What Community Includes: each allowance with how much is used and the day it resets, then the features that are not includedScreenshot pending — add assets/img/community-includes-weekly.png (see SCREENSHOTS.md).

Три детали относятся к платным планам, а сами находки остаются бесплатными: Community отмечает каждую строку, на которой есть выжившие мутанты, и сообщает их число, но не то, что изменил каждый мутант; сообщает, сколько тестов выполнило строку, но не их имена; и показывает Max CRAP прогона, но не ранжированный список Riskiest Methods.

A mutant hover in Community: 2 mutants survived and 2 more detected on the line; what each mutant changed is part of the paid plansScreenshot pending — add assets/img/mutant-hover-community.png (see SCREENSHOTS.md).

Если вы уже пользовались Community, когда появился недельный лимит (0.2026.83), ваши прежние ограничения сохраняются ещё 30 дней; об этом сообщается один раз, с указанием даты. Счётчики хранятся на вашем компьютере — в собственном хранилище VS Code и в ~/.fucini-coverage/, поэтому новый профиль VS Code продолжает ту же неделю, — и никогда никуда не отправляются.

Пропуск на 7 дней

Не готовы к подписке? Пропуск на 7 дней — это всё, что есть в Pro, на одну неделю с разовой оплатой: ничего не продлевается и нечего отменять. Купите его на странице цен (в сообщении о лимите Community для этого есть кнопка), и код активации придёт по электронной почте.

The 7-day pass on the pricing page: everything in Pro for one week, paid once, with the Get a 7-day pass buttonScreenshot pending — add assets/img/pricing-week-pass.png (see SCREENSHOTS.md).
  1. Выполните Fucini Coverage: Enter Subscription Code — команда есть и в редакции Community, и в Pro.
  2. Выберите Pro и вставьте код FCB1-… из письма.
  3. Готово. Ключ выпускается и применяется за вас и работает офлайн. Неделя отсчитывается от оплаты, поэтому активируйте пропуск сразу после покупки. Когда она закончится, расширение само вернётся к Community и продолжит показывать покрытие.
Enter Subscription Code, step 1: choose Pro from the list of plansScreenshot pending — add assets/img/pass-step-plan.png (see SCREENSHOTS.md).
Enter Subscription Code, step 2: paste the FCB1 code from the purchase email and press EnterScreenshot pending — add assets/img/pass-step-code.png (see SCREENSHOTS.md).

В Visual Studio. Тот же код активирует Fucini Coverage Studio для Visual Studio, 2022 и 2026: откройте Tools → Coverage Studio → Licence…, вставьте код, выберите план, для которого он выдан, и нажмите Activate. Окно Coverage затем показывает Activated над деревом; раскрытая стрелкой строка показывает дату, до которой действителен ключ, и кнопку к вашей учётной записи. Одна машина — одно место: активируйте VS Code или Visual Studio, и другой редактор на той же машине подхватит это сам — Visual Studio читает ключ VS Code, а VS Code читает ключ Visual Studio, — так что ничего не вводится дважды и второе место не занимается. Оба поддерживают ключ одинаково: Pro Plus, Business и Enterprise отмечаются дважды в день, а ключ Pro обновляется за неделю до истечения.

Visual Studio: the Coverage window says no subscription covers this machine, and the Licence dialog takes the subscription code
Visual Studio: the Coverage window after an activation — Activated, valid until a date, and a button to the account

Переход на новый компьютер и управление местами

Ключ привязан к одной машине, поэтому новому компьютеру нужен новый — а подписка оплачивает ровно столько машин, сколько в ней указано. Освободите старую сами: войдите в управление подпиской, найдите место, которым больше не пользуетесь, нажмите Free up и активируйте новую машину тем же кодом. Покупка Pro не присылает вам пароль — учётная запись существует, просто пароля у неё ещё нет, поэтому в первый раз воспользуйтесь ссылкой задать или сбросить пароль с адресом, на который оформлена подписка.

В планах Business и Enterprise вы приглашаете каждого коллегу по электронной почте с той же страницы. Каждый получает код активации, принадлежащий только ему, и как только он им воспользуется, его адрес появляется рядом с его местом — когда кто-то уходит, вы видите, какое место было его, и освобождаете для следующего. Приглашение занимает место с момента отправки и истекает через 14 дней, если остаётся неиспользованным; отзовите его, чтобы вернуть место сразу.

Тестирование предварительных сборок

Новое появляется на fucini.software раньше, чем в Marketplace, и вы можете попросить, чтобы его вам предлагали. Откройте представление Coverage, разверните панель лицензии и нажмите Тестировать предварительные сборки… — или включите fuciniCoverage.update.preRelease. С этого момента расширение проверяет здесь отдельный канал предварительных сборок наряду с обычным и предлагает ту из двух, что новее.

The licence panel in the Coverage view, expanded: a Pro licence valid until 2027-03-19, and the button labelled Test pre-release buildsScreenshot pending — add assets/img/licence-prerelease.png (see SCREENSHOTS.md).

Предварительные сборки протестированы, но это сборки, в которых мы менее всего уверены, — вы добровольно беретесь найти то, что мы упустили. Присоединение сообщает нам, что эта установка в канале, поэтому мы знаем, сколько людей тестирует, и можем рассказать, что изменилось; выход вернет вас на обычный путь со следующим стабильным выпуском. Предварительная сборка есть не всегда, и присоединиться, когда её нет, ничего не стоит: расширение находит пустой канал, ничего об этом не говорит и продолжает предлагать стабильные выпуски, пока какая-нибудь не будет опубликована.

The same panel once the install has joined: an update bar offering 0.2026.84 while 0.2026.83 is running, the button now reading Leave the pre-release channel, and a line saying the install is on the channelScreenshot pending — add assets/img/licence-prerelease-joined.png (see SCREENSHOTS.md).

20. Контакты и лицензия

Вопросы, отчёты об ошибках или альтернативные условия лицензирования — см. страницу контактов или напишите на info@fucini.consulting. Лицензировано по MIT с условием атрибуции в примечаниях к релизу; см. также Notice.

21. В командной строке

Читатель работает и без редактора: npm install -g fucini-coverage, бесплатно, на Windows, Linux, WSL и macOS. fucini-coverage check coverage/lcov.info --lines 80 — это шлюз, на котором падает конвейер; summary, export, report, badge и compare делают то же, что одноимённые команды редакторов, а mcp отдаёт агенту шесть инструментов из раздела выше на сервере. Он читает fucini-coverage.json, файл настроек, который читает редакция для Visual Studio, так что пороги задаются один раз. fucini-coverage help <command> объясняет каждую; полная страница — fucini-coverage на npm.

The command line on the demo: --help, summary, check with two gates passing, export as Markdown, compare against a baseline, badge, report and the MCP server's helpScreenshot pending — add assets/img/cli-demo.gif (see SCREENSHOTS.md).