Skip to content

[ci] Testing code coverage workflow #18710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 30 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f052a84
[coverage] Use alma9 to run code coverage
pcanal Apr 22, 2025
151a72a
[coverage] Update yml to match newer build_root.py syntax
pcanal Apr 22, 2025
761ea6b
[coverage] filter out builtins and \*-prefix
pcanal Apr 22, 2025
17d9e18
[coverage] filter out bindings/pyroot/cppyy
pcanal Apr 23, 2025
25abd6a
[coverage] Reduce verbosity of gcovr (more than 77M of debug output)
pcanal Apr 23, 2025
53a6d13
[coverage] Use multiple cores for gcovr
pcanal Apr 23, 2025
7f86f5f
[coverage] Update to v5 of codecov-action
pcanal Apr 23, 2025
48f3d2f
[ci][coverage] Extend env output
pcanal Apr 23, 2025
ea05fff
[coverage] Generate artifact for coverage nigthlies
pcanal Apr 24, 2025
7351d62
[ci][coverage] Add S3 authentication.
pcanal Apr 30, 2025
7843063
REVERTME [coverage] enable for pull request for testing
pcanal Apr 22, 2025
307be2d
REVERTME [coverage] Run also on v6-36-00-patches pull requests
pcanal Apr 23, 2025
a42f413
REVERTME [coverage] Cache the coverage build for testing
pcanal Apr 22, 2025
989020e
REVERT we have an artifact already
pcanal Apr 23, 2025
da9169c
REVERTME: force generation of artifact
pcanal Apr 25, 2025
adbaaf3
Revert "REVERTME: force generation of artifact"
pcanal Apr 30, 2025
0705850
REVERTME: Force reconfiguration in CI
pcanal Apr 29, 2025
d818f93
REVERTME: Skip the build for now
pcanal Apr 30, 2025
081c98a
REVERTME [coverage] disable most builds for testing
pcanal Apr 22, 2025
d3f47a9
[test] Dont rebuild library within test
pcanal May 15, 2025
60c63be
[coverage] Exclude test from coverage run.
pcanal May 15, 2025
ed782b3
[coverage] Exclude test from coverage run.
pcanal May 15, 2025
5e7a9fb
[coverage] Extract part of the (long) gcovr command line
pcanal May 16, 2025
36f31f9
[coverage] Ignore missing source file (eg. ACLiC)
pcanal May 15, 2025
047ab35
[coverage] Extract from gcovr command line dict related opt
pcanal May 16, 2025
55716e5
[coverage] Ignore gcovr parse errors.
pcanal May 19, 2025
b45eddf
[coverage] Remove existing gcda files
pcanal May 19, 2025
941ceb1
[coverage] Increase suspicious hits threshold to 10,000,000,000
pcanal May 20, 2025
36f7de2
[coverage] Increase suspicious hits threshold to 20,000,000,000.
pcanal May 20, 2025
b7457b4
Revert "REVERTME: Skip the build for now"
pcanal May 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions .github/workflows/root-ci-config/build_root.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ def main():
platform_machine = platform.machine()

obj_prefix = f'{args.platform}/{macos_version_prefix}{args.base_ref}/{args.buildtype}_{platform_machine}/{options_hash}'
if args.coverage:
obj_prefix = obj_prefix + "-coverage"

# Make testing of CI in forks not impact artifacts
if 'root-project/root' not in args.repository:
Expand Down Expand Up @@ -126,14 +128,22 @@ def main():
if not WINDOWS:
show_node_state()

if args.coverage and args.incremental:
# Delete all the .gcda files produces by an artefact.
build_utils.remove_file_match_ext(WORKDIR, "gcda")

build(options, args.buildtype)

# Build artifacts should only be uploaded for full builds, and only for
# "official" branches (master, v?-??-??-patches), i.e. not for pull_request
# We also want to upload any successful build, even if it fails testing
# later on.
if not pull_request and not args.incremental and not args.coverage:
if not pull_request and not args.incremental:
archive_and_upload(yyyy_mm_dd, obj_prefix)
#if args.coverage: # for now, force it.
# if we were to actually upload the artefact we probably need to exclude all
# coverage run-time files ('*.gcda' and `*.gcov`)
# archive_and_upload(yyyy_mm_dd, obj_prefix)

if args.binaries:
create_binaries(args.buildtype)
Expand Down Expand Up @@ -394,7 +404,7 @@ def build(options, buildtype):
if result != 0:
die(result, "Failed to create build directory")

if not os.path.exists(os.path.join(WORKDIR, "build", "CMakeCache.txt")):
if True: # or not os.path.exists(os.path.join(WORKDIR, "build", "CMakeCache.txt")):
cmake_configure(options, buildtype)
else:
cmake_dump_config()
Expand Down Expand Up @@ -499,9 +509,15 @@ def get_base_head_sha(directory: str, repository: str, merge_sha: str, head_sha:
@github_log_group("Create Test Coverage in XML")
def create_coverage_xml() -> None:
builddir = os.path.join(WORKDIR, "build")
ignore_directories="runtutorials|interpreter|.*-prefix|bindings/pyroot/cppyy"
ignore_subpattern="runtutorials|externals|ginclude|googletest-prefix|macosx|winnt|geombuilder|cocoa|quartz|win32gdk|x11|x11ttf|eve|fitpanel|ged|gui|guibuilder|guihtml|qtgsi|qtroot|recorder|sessionviewer|tmvagui|treeviewer|geocad|fitsio|gviz|qt|gviz3d|x3d|spectrum|spectrumpainter|dcache|hdfs|foam|genetic|mlp|quadp|splot|memstat|rpdutils|proof|odbc|llvm|test|interpreter"
ignore_errors="--gcov-suspicious-hits-threshold=20000000000 --gcov-ignore-errors=source_not_found --gcov-ignore-errors=no_working_dir_found"
exclude_dictionaries="--exclude='.*/G__.*' --gcov-exclude='.*_ACLiC_dict[.].*' '--exclude=.*_ACLiC_dict[.].*'"
# The output of -v is huge (several 10s of MB at least), we could filter
# the output of -v to keep just the line with ` Processing file:`
result = subprocess_with_log(f"""
cd '{builddir}'
gcovr --output=cobertura-cov.xml --cobertura-pretty --gcov-ignore-errors=no_working_dir_found --merge-mode-functions=merge-use-line-min --exclude-unreachable-branches --exclude-directories="runtutorials|interpreter" --exclude='.*/G__.*' --exclude='.*/(runtutorials|externals|ginclude|googletest-prefix|macosx|winnt|geombuilder|cocoa|quartz|win32gdk|x11|x11ttf|eve|fitpanel|ged|gui|guibuilder|guihtml|qtgsi|qtroot|recorder|sessionviewer|tmvagui|treeviewer|geocad|fitsio|gviz|qt|gviz3d|x3d|spectrum|spectrumpainter|dcache|hdfs|foam|genetic|mlp|quadp|splot|memstat|rpdutils|proof|odbc|llvm|test|interpreter)/.*' --gcov-exclude='.*_ACLiC_dict[.].*' '--exclude=.*_ACLiC_dict[.].*' -v -r ../src ../build
gcovr -j {os.cpu_count()} --output=cobertura-cov.xml --cobertura-pretty {ignore_errors} --merge-mode-functions=merge-use-line-min --exclude-unreachable-branches --exclude-directories="{ignore_directories}" --exclude='.*/({ignore_subpattern})/.*' {exclude_dictionaries} -r ../src ../build
""")

if result != 0:
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/root-ci-config/build_utils.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
#!/usr/bin/env false

import json
import re
import os
import subprocess
import sys
import textwrap
import datetime
import time
import platform
from functools import wraps
from hashlib import sha1
from http import HTTPStatus
from shutil import which
from typing import Callable, Dict
from collections import namedtuple

Check failure on line 17 in .github/workflows/root-ci-config/build_utils.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F401)

.github/workflows/root-ci-config/build_utils.py:17:25: F401 `collections.namedtuple` imported but unused

from openstack.connection import Connection
from requests import get

Check failure on line 20 in .github/workflows/root-ci-config/build_utils.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

.github/workflows/root-ci-config/build_utils.py:3:1: I001 Import block is un-sorted or un-formatted
Expand Down Expand Up @@ -218,7 +219,7 @@
"""
options_and_defines = options
if ('march=native' in options):
print_info(f"A march=native build was detected.")

Check failure on line 222 in .github/workflows/root-ci-config/build_utils.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F541)

.github/workflows/root-ci-config/build_utils.py:222:20: F541 f-string without any placeholders
compiler_name = 'c++' if which('c++') else 'clang++'
command = f'echo | {compiler_name} -dM -E - -march=native'
sp_result = subprocess.run([command], shell=True, capture_output=True, text=True)
Expand Down Expand Up @@ -255,12 +256,12 @@
try:
create_object_local()
success = True
except:

Check failure on line 259 in .github/workflows/root-ci-config/build_utils.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E722)

.github/workflows/root-ci-config/build_utils.py:259:9: E722 Do not use bare `except`
success = False
sleep_time = sleep_time_unit * attempt
print_warning(f"""Attempt {attempt} to upload {src_file} to {dest_object} failed. Retrying in {sleep_time} seconds...""")
time.sleep(sleep_time)
if success: break

Check failure on line 264 in .github/workflows/root-ci-config/build_utils.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (E701)

.github/workflows/root-ci-config/build_utils.py:264:19: E701 Multiple statements on one line (colon)

# We try one last time
create_object_local()
Expand Down Expand Up @@ -302,3 +303,19 @@
log.add(f"\ncurl --output {destination}/artifacts.tar.gz {url}/{latest}\n")

return f"{destination}/artifacts.tar.gz"

def remove_file_match_ext(directory: str, extension: str) -> str:
"""
Deletes all files in a directory and its subdirectory matching an extension

Args:
directory (str): The path to the directory to search in.
extension (str): The regular expression pattern to match filenames against.
"""
log.add(f"Removing gcda files from {directory}")
pattern = f"r.*\.{extension}$"
for filename in os.listdir(directory):
if re.search(pattern, filename):
file_path = os.path.join(directory, filename)
os.remove(file_path)
log.add(f"Deleted: {file_path}\n")
98 changes: 49 additions & 49 deletions .github/workflows/root-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,19 @@ jobs:
# Common configs: {Release,Debug,RelWithDebInfo)
# Build options: https://root.cern/install/build_from_source/#all-build-options
include:
- platform: mac13
arch: ARM64
overrides: ["builtin_zlib=ON"]
- platform: mac14
arch: X64
overrides: ["CMAKE_CXX_STANDARD=20"]
- platform: mac15
arch: ARM64
overrides: ["CMAKE_CXX_STANDARD=23"]
- platform: mac-beta
is_special: true
arch: ARM64
overrides: ["CMAKE_CXX_STANDARD=23"]
# - platform: mac13
# arch: ARM64
# overrides: ["builtin_zlib=ON"]
# - platform: mac14
# arch: X64
# overrides: ["CMAKE_CXX_STANDARD=20"]
# - platform: mac15
# arch: ARM64
# overrides: ["CMAKE_CXX_STANDARD=23"]
# - platform: mac-beta
# is_special: true
# arch: ARM64
# overrides: ["CMAKE_CXX_STANDARD=23"]

runs-on: # Using '[self-hosted, ..., ...]' does not work for some reason :)
- self-hosted
Expand Down Expand Up @@ -240,14 +240,14 @@ jobs:
# for pull requests and on branch pushes. This is further complicated
# by the fact that event_name is a string, but we need an array. So
# we construct a JSON string that we can then convert into an array.
event_name: ${{ fromJSON(format('["{0}"]', github.event_name)) }}
config: ["RelWithDebInfo", "Release"]
target_arch: [x64, x86]
exclude:
- event_name: pull_request
config: RelWithDebInfo
- event_name: push
config: RelWithDebInfo
# event_name: ${{ fromJSON(format('["{0}"]', github.event_name)) }}
# config: ["RelWithDebInfo", "Release"]
# target_arch: [x64, x86]
# exclude:
# - event_name: pull_request
# config: RelWithDebInfo
# - event_name: push
# config: RelWithDebInfo
# This is this platform is subject to timeouts when building from
# scratch.
#- target_arch: x86
Expand Down Expand Up @@ -361,40 +361,40 @@ jobs:
# Common configs: {Release,Debug,RelWithDebInfo)
# Build options: https://root.cern/install/build_from_source/#all-build-options
include:
- image: fedora41
- image: fedora42
overrides: ["CMAKE_CXX_STANDARD=23"]
- image: alma8
- image: alma9
overrides: ["CMAKE_BUILD_TYPE=Debug"]
- image: ubuntu22
overrides: ["imt=Off", "CMAKE_BUILD_TYPE=Debug"]
# - image: fedora41
# - image: fedora42
# overrides: ["CMAKE_CXX_STANDARD=23"]
# - image: alma8
# - image: alma9
# overrides: ["CMAKE_BUILD_TYPE=Debug"]
# - image: ubuntu22
# overrides: ["imt=Off", "CMAKE_BUILD_TYPE=Debug"]
- image: ubuntu2404
overrides: ["CMAKE_BUILD_TYPE=Debug"]
- image: ubuntu2504
overrides: ["CMAKE_CXX_STANDARD=23"]
- image: debian125
overrides: ["CMAKE_CXX_STANDARD=20", "dev=ON"]
# Special builds
- image: alma9
is_special: true
property: modules_off
overrides: ["runtime_cxxmodules=Off"]
# - image: ubuntu2504
# overrides: ["CMAKE_CXX_STANDARD=23"]
# - image: debian125
# overrides: ["CMAKE_CXX_STANDARD=20", "dev=ON"]
# # Special builds
# - image: alma9
# is_special: true
# property: modules_off
# overrides: ["runtime_cxxmodules=Off"]
- image: alma9
is_special: true
property: march_native
overrides: ["CMAKE_BUILD_TYPE=RelWithDebInfo", "CMAKE_CXX_FLAGS=-march=native", "CMAKE_C_FLAGS=-march=native", "fortran=OFF"]
- image: alma9
is_special: true
property: arm64
overrides: ["CMAKE_BUILD_TYPE=RelWithDebInfo"]
architecture: ARM64
- image: alma9-clang
is_special: true
property: clang
overrides: ["CMAKE_C_COMPILER=clang", "CMAKE_CXX_COMPILER=clang++"]
- image: alma10
is_special: true
# - image: alma9
# is_special: true
# property: arm64
# overrides: ["CMAKE_BUILD_TYPE=RelWithDebInfo"]
# architecture: ARM64
# - image: alma9-clang
# is_special: true
# property: clang
# overrides: ["CMAKE_C_COMPILER=clang", "CMAKE_CXX_COMPILER=clang++"]
# - image: alma10
# is_special: true
# Disable GPU builds until the DNS problem is solved
# - image: ubuntu2404-cuda
# is_special: true
Expand Down
75 changes: 55 additions & 20 deletions .github/workflows/test-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ on:

# https://github.com/root-project/root/pull/12112#issuecomment-1411004278
# DISABLED: takes 4 hours! Might clang-coverage be a solution?
#pull_request:
# branches:
# - 'master'
# paths-ignore:
# - 'doc/**'
# - 'documentation/**'
pull_request:
branches:
- 'master'
- 'v6-36-00-patches'
paths-ignore:
- 'doc/**'
- 'documentation/**'

workflow_call:
inputs:
Expand All @@ -24,6 +25,9 @@ on:
type: string
required: true
default: master
ref_name:
type: string
default: master

# Enables manual start of workflow
workflow_dispatch:
Expand Down Expand Up @@ -58,27 +62,53 @@ jobs:

name: Build and test to determine coverage

env:
PYTHONUNBUFFERED: true
OS_APPLICATION_CREDENTIAL_ID: '7f5b64a265244623a3a933308569bdba'
OS_APPLICATION_CREDENTIAL_SECRET: ${{ secrets.OS_APPLICATION_CREDENTIAL_SECRET }}
OS_AUTH_TYPE: 'v3applicationcredential'
OS_AUTH_URL: 'https://keystone.cern.ch/v3'
OS_IDENTITY_API_VERSION: 3
OS_INTERFACE: 'public'
OS_REGION_NAME: 'cern'
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

container:
image: registry.cern.ch/root-ci/fedora38:buildready
image: registry.cern.ch/root-ci/alma9:buildready
options: '--security-opt label=disable --rm'
env:
OS_APPLICATION_CREDENTIAL_ID: '7f5b64a265244623a3a933308569bdba'
PYTHONUNBUFFERED: true

steps:
- name: Set up Python Virtual Env
# if the `if` expr is false, `if` still has exit code 0.
# if the `if` block is entered, the block's exit code becomes the exit
# code of the `if`.
run: 'if [ -d /py-venv/ROOT-CI/bin/ ]; then . /py-venv/ROOT-CI/bin/activate && echo PATH=$PATH >> $GITHUB_ENV; fi'

# This should be part of the container image
- name: Install cov packages
run: |
sudo dnf -y update
sudo dnf -y install lcov
dnf -y update
dnf -y install lcov
pip3 install gcovr

# This checks out the merge commit if this is a PR
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.ref_name }}

- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: echo "$GITHUB_CONTEXT"
JOB_CONTEXT: ${{ toJSON(job) }}
ENV_CONTEXT: ${{ toJSON(env) }}
run: |
echo "$GITHUB_CONTEXT"
echo "--------------------------"
echo "$JOB_CONTEXT"
echo "--------------------------"
echo "$ENV_CONTEXT"

- name: Print debug info
run: 'printf "%s@%s\\n" "$(whoami)" "$(hostname)";
Expand All @@ -87,8 +117,8 @@ jobs:

- name: Apply option override from matrix for this job
env:
OVERRIDE: "coverage=On"
FILE: .github/workflows/root-ci-config/buildconfig/fedora38.txt
OVERRIDE: "coverage=ON"
FILE: .github/workflows/root-ci-config/buildconfig/alma9.txt
shell: bash
run: |
set -x
Expand All @@ -103,40 +133,45 @@ jobs:
GITHUB_PR_ORIGIN: ${{ github.event.pull_request.head.repo.clone_url }}
run: ".github/workflows/root-ci-config/build_root.py
--buildtype Debug
--platform fedora38
--incremental false
--platform alma9
--incremental true
--coverage true
--base_ref ${{ github.base_ref }}
--sha ${{ github.sha }}
--head_ref refs/pull/${{ github.event.pull_request.number }}/head:${{ github.event.pull_request.head.ref }}
--head_sha ${{ github.event.pull_request.head.sha }}
--repository ${{ github.server_url }}/${{ github.repository }}
"

- name: Workflow dispatch
if: github.event_name == 'workflow_dispatch'
run: ".github/workflows/root-ci-config/build_root.py
--buildtype Debug
--platform fedora38
--platform alma9
--incremental false
--coverage true
--base_ref ${{ inputs.base_ref }}
--sha ${{ github.sha }}
--head_ref ${{ inputs.head_ref }}
--head_sha ${{ inputs.head_ref }}
--repository ${{ github.server_url }}/${{ github.repository }}
"

- name: Nightly build
if: github.event_name == 'schedule'
run: ".github/workflows/root-ci-config/build_root.py
--buildtype Debug
--platform fedora38
--platform alma9
--incremental false
--coverage true
--base_ref ${{ github.ref_name }}
--sha ${{ github.sha }}
--repository ${{ github.server_url }}/${{ github.repository }}
"

- name: Upload coverage to Codecov
if: ${{ !cancelled() }}
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v5
with:
env_vars: OS,PYTHON
fail_ci_if_error: true
Expand Down
2 changes: 1 addition & 1 deletion roottest/cling/functionTemplate/testcint.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from __future__ import print_function
import ROOT
from sys import stdout

Check failure on line 3 in roottest/cling/functionTemplate/testcint.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

roottest/cling/functionTemplate/testcint.py:1:1: I001 Import block is un-sorted or un-formatted

def printme(o):
print("t now %g %d %d" % (o.get["double"](), o.get["int"](), o.get["float"]()))
stdout.flush()

ROOT.gROOT.ProcessLine(".L t.h++")
ROOT.gROOT.ProcessLine(".L t.h+")
sortedMethods = [ item for item in ROOT.t.__dict__.keys() if item[0:2] != '__' ]
sortedMethods.sort()
print("# just a comment")
Expand Down
10 changes: 6 additions & 4 deletions roottest/root/aclic/load/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ ROOTTEST_ADD_TEST(autoload
OUTREF execautoload.ref
DEPENDS autoloadtest.C)

ROOTTEST_ADD_TEST(reload
COPY_TO_BUILDDIR Reload.root
MACRO assertReload.C
OUTREF assertReload.ref)
if(NOT coverage)
ROOTTEST_ADD_TEST(reload
COPY_TO_BUILDDIR Reload.root
MACRO assertReload.C
OUTREF assertReload.ref)
endif()

ROOTTEST_ADD_TEST(linktest
MACRO execlinktest.C
Expand Down
Loading
Loading