Проблема
Основным средством для командной работы с репозиторием компании, в которой я работаю, является Gitlab. На первый взгляд, он хорош: и бесплатной версии хватает сполна, и CI/CD - пайплайн имеется, и хранить артефакты тоже можно (долой Nexus). Однако, и у GitLab есть свои минусы.
Так, например, разработчики до сих пор не смогли реализовать визуализацию покрытия тестов. Да, она у них есть, но для её реализации нужно сгенерировать и загрузить в GitLab отчет о покрытии в устаревшем формате Cobertura, а не в актуальном Jacoco. Основная же проблема в том, что Cobertura plugin последний раз обновлялся в далеком 2015 году.

То есть этот плагин уже не поддерживается Java 8. Прикрепляю ссылки на официальные источники, чтобы вы могли понять, насколько там все запущено))
И что же нам предлагает сделать GitLab? Он сделал все «по-человечески»? Нет))) он предложил использовать скрипт на Python для перевода отчета в формате Jacoco в отчет в формате Cobertura.
Выглядит это так:
test-jdk11: stage: test image: maven:3.6.3-jdk-11 script: - mvn $MAVEN_CLI_OPTS clean org.jacoco:jacoco-maven-plugin:prepare-agent test jacoco:report artifacts: paths: - target/site/jacoco/jacoco.xml coverage-jdk11: # Must be in a stage later than test-jdk11's stage. # The `visualize` stage does not exist by default. # Please define it first, or choose an existing stage like `deploy`. stage: visualize image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.7 script: # convert report from jacoco to cobertura, using relative project path - python /opt/cover2cover.py target/site/jacoco/jacoco.xml $CI_PROJECT_DIR/src/main/java/ > target/site/cobertura.xml needs: ["test-jdk11"] artifacts: reports: coverage_report: coverage_format: cobertura path: target/site/cobertura.xml
Подробнее об этом решении по ссылке.
Громоздко, правда? Даже приходится подключать дополнительный образ, чтобы скрипт заработал.
Наша компания не единственная, кто заметил эту проблему. Так в 2020 году Markus Schuch создал запрос разработчикам GitLab о добавлении поддержки отчетов о покрытии в формате Jacoco. Но решение данной проблемы так и не сдвинулось с места. Поэтому руководством нашей компании мне было поручено написать плагин, который будет переводить отчет jacoco.xml в cobertura.xml. Таким образом, в компании решили сразу 2 задачи: и получили необходимый функционал, и заняли делом джуна.
Решение
Было решено написать плагин, который упростит создание отчета в формате Cobertura. Принцип работы плагина: перевести отчет формата Jacoco в Cobertura.
Проблемы написания плагина
Подбор библиотек
Сначала для парсинга файла формата XML необходимо было подобрать библиотеку. Так как существует множество разновидностей библиотек со своими плюсами и минусами выбор оказался непростым.
Например, DOM парсер – это древовидный синтаксический анализатор. Таким парсером очень удобно искать элементы в файле, но у него есть недостаток – скорость.
SAX парсер работает иначе, он не создает никакой доменной структуры, а последовательно читает/записывает XML файл. Поэтому SAX парсер работает гораздо быстрее чем DOM, но он не умеет так же хорошо искать элементы файла. Поэтому мной была выбрана библиотека JDOM2 для чтения jacoco.xml и SAX для записи в cobertura.xml.
Написание алгоритма преобразования Jacoco в Cobertura
Основная идея алгоритма была позаимствована из питоновского скрипта cover2cover.py. Однако, немного изменить логику алгоритма всё же пришлось. Например, различная работа XML библиотек в Python и Java, различный результат при математических вычислениях чисел с плавающей запятой.
После того как стало понятно, что алгоритм, написанный на Java, выдает такие же отчеты, что и cover2cover.py необходимо было реализовать удобство применения в сторонних java приложениях. Очевидно, что для этого необходимо написать плагин. О том как написать плагин есть хорошая статья.
Всего в мой плагин можно передать 3 переменные:
source - путь к файлу формата Jacoco (дефолтное значение:
target/site/jacoco/jacoco.xml);result - путь, куда файл cobertura.xml будет размещен после работы плагина (дефолтное значение:
target/site/cobertura/cobertura.xml);pathsToProject - список путей к классам относительно корня проекта (дефолтное значение:
/src/main/java/).
Важно, чтобы по пути, указанному в source, лежал jacoco.xml, иначе перевод не отработает. Также в pathsToProject можно передавать как один путь к пакетам, так и несколько путей, если проект мультимодульный.
Например, для проекта со структурой:
pet-parent/ +- pet/ +- src/ +- main/ +- java/ +- pom.xml ... +- pet-api/ +- src/ +- main/ +- java/ +- pom.xml ... +- pet-test/ +- pom.xml +- pom.xml
Необходимо передать пути /pet-api/src/main/java/ и /pet/src/main/java/. Теперь покрытия тестами обоих модулей будут видны!
Как пользоваться
В pom.xml
Плагин очень просто добавить в pom.xml, и он будет генерировать отчет cobertura.xml туда, куда Вам потребуется. В общем, работает он также, как и Jacoco плагин, а главное условие выполнения - правильно указать параметры и поставить его в pom.xml после Jacoco плагина.
Вот фрагмент pom.xml, в котором наглядно показано, как настраивать плагин:
<project> ... <build> <!-- To use the plugin goals in your POM or parent POM --> <plugins> ... <plugin> <groupId>ru.siblion.lab</groupId> <artifactId>jacocoToCobertura-maven-plugin</artifactId> <version>0.0.2</version> <executions> <execution> <goals> <goal>convert</goal> </goals> <phase>test</phase> </execution> </executions> <configuration> <source>path to jacoco.xml</source> <result>path to cobertura.xml</result> <pathsToProject>path to project files</pathsToProject> </configuration> </plugin> ... </plugins> </build> ... </project>
А для мультимодульного проекта (возьмем пример из прошлой главы) параметр pathsToProject будет выглядеть так:
<pathsToProject>/pet/src/main/java/,/pet-api/src/main/java/</pathsToProject>
То есть просто перечисляем через запятую. Теперь каждый раз после запуска тестов будет появляться отчет в формате Cobertura)))
В .gitlab-ci.yml необходимо будет добавить, чтобы GitLab увидел отчет:
artifacts: reports: coverage_report: coverage_format: cobertura path: target/cobertura/cobertura.xml
В CI/CD пайплайне
Иногда не хочется лишний раз изменять pom.xml, чтобы ничего не поломалось. Поэтому работу jacocoToCobertura плагина можно настроить в файле .gitlab-ci.yml:
mvn-test: stage: test script: - "./mvnw -ntp -s .m2/settings.xml --batch-mode clean test" artifacts: paths: - target/site/jacoco/jacoco.xml only: - merge_requests - main coverage: stage: visualize script: - "./mvnw $MAVEN_CLI_OPTS dependency:get -Dartifact=ru.siblion.lab:jacocoToCobertura-maven-plugin:0.0.2" - "./mvnw $MAVEN_CLI_OPTS ru.siblion.lab:jacocoToCobertura-maven-plugin:0.0.2:convert -Dsource="target/site/jacoco/jacoco.xml" -Dresult="target/site/cobertura/cobertura.xml" -DpathesToProject="${CI_PROJECT_DIR}/src/main/java/"" coverage: "/Total.*?([0-9]{1,3})%/" artifacts: reports: coverage_report: coverage_format: cobertura path: target/site/cobertura/cobertura.xml only: - merge_requests - main
Результат
Теперь после успешного выполнения пайплайна в изменениях в merge request появится красивая подсветка, которая показывает какой код покрыт тестами, а какой нет.

Кроме того, после выполнения пайплайна итоговое покрытие проекта будет отображаться в Jobs пайплайна.

Технические ограничения
Плагин написан на Java 17. Требуется наличие Jacoco plugin, стоящий в файле pom.xml выше, чем jacocoToCobertura-plugin. Визуализация покрытия сохраняется неделю, но затем пропадает.
Ограничения от GitLab: не более 100 источников source в отчете, то есть не более 100 модулей в проекте. Размер одного XML-файла Cobertura не может превышать 10 МБ. Подробнее про ограничения от GitLab тут.
Заключение
Этот плагин выложен на Maven Central, я надеюсь что он сможет сделать ваш merge request чуточку ярче))) Если есть идеи и предложения по улучшению, пишите. Исходники прикреплены.
Авторы
Дмитриев Егор
Митрофанов Сергей
