Проблема

Основным средством для командной работы с репозиторием компании, в которой я работаю, является 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 чуточку ярче))) Если есть идеи и предложения по улучшению, пишите. Исходники прикреплены.

Авторы

  • Дмитриев Егор

  • Митрофанов Сергей

Ссылки