Skip to content

GHI #20 Fix workflow files #104

GHI #20 Fix workflow files

GHI #20 Fix workflow files #104

Workflow file for this run

name: Run All Tests
on:
pull_request:
branches:
- '**'
jobs:
test-native:
strategy:
matrix:
# verbose labels make things easier to read in GitHub Actions
# platform gets converted to os, preset, compiler, architecture
platform:
- windows-gcc-x86_64
- windows-msvc-x86_32
- windows-msvc-x86_64
- macos-clang-x86_64
- linux-clang-x86_64
- linux-gcc-x86_64
# convert verbose labels to usable values (which don't appear in GitHub Actions GUI)
include:
# platform -> os, preset, compiler, architecture
- platform: windows-gcc-x86_64
os: windows
preset: patomic-ci-native-unix-gcc
architecture: x86_64
- platform: windows-msvc-x86_32
os: windows
preset: patomic-ci-native-win32-msvc
architecture: x86_32
- platform: windows-msvc-x86_64
os: windows
preset: patomic-ci-native-win64-msvc
architecture: x86_64
- platform: macos-clang-x86_64
os: macos
preset: patomic-ci-native-unix-clang
architecture: x86_64
- platform: linux-clang-x86_64
os: ubuntu
preset: patomic-ci-native-unix-clang
architecture: x86_64
- platform: linux-gcc-x86_64
os: ubuntu
preset: patomic-ci-native-unix-gcc
architecture: x86_64
uses: doodspav/patomic/.github/workflows/reusable-test-native.yml@feature/ghi-20-inner-stuff
with:
os: ${{ matrix.os }}
preset: ${{ matrix.preset }}
architecture: ${{ matrix.architecture }}
# test-qemu:
# strategy:
# matrix:
# # architecture gets converted to triple
# # short form here so that it doesn't take up the whole GitHub Action name
# architecture:
# - aarch64
# # - alpha todo: uncomment
# - arm
# - armhf
# # - hppa todo: uncomment
# # fixme: m68k is supported, but ICEs clang and segfaults qemu when built with gcc (GHI #25)
# # - m68k
# - mips
# - mips64
# - mips64el
# - mipsel
# - ppc
# - ppc64
# - ppc64le
# # - riscv64 todo: uncomment
# - s390x
# # fixme: sh4 is supported (only for gcc), but segfaults qemu when built with gcc (GHI #25)
# # - sh4
# # - sparc64 todo:uncomment
# # - x86_32 todo: uncomment
# # convert architectures to triples
# include:
# - architecture: aarch64
# triple: aarch64-linux-gnu
# # - architecture: alpha
# # triple: alpha-linux-gnu
# - architecture: arm
# triple: arm-linux-gnueabi
# - architecture: armhf
# triple: arm-linux-gnueabihf
# # - architecture: hppa
# # triple: hppa-linux-gnu
# # - architecture: m68k
# # triple: m68k-linux-gnu
# - architecture: mips
# triple: mips-linux-gnu
# - architecture: mips64
# triple: mips64-linux-gnuabi64
# - architecture: mips64el
# triple: mips64el-linux-gnuabi64
# - architecture: mipsel
# triple: mipsel-linux-gnu
# - architecture: ppc
# triple: powerpc-linux-gnu
# - architecture: ppc64
# triple: powerpc64-linux-gnu
# - architecture: ppc64le
# triple: powerpc64le-linux-gnu
# # - architecture: riscv64
# # triple: riscv64-linux-gnu
# - architecture: s390x
# triple: s390x-linux-gnu
# # - architecture: sh4
# # triple: sh4-linux-gnu
# # - architecture: sparc64
# # triple: sparc64-linux-gnu
# # - architecture: x86_32
# # triple: i686-linux-gnu
# uses: doodspav/patomic/.github/workflows/reusable-test-qemu.yml@feature/ghi-20-inner-stuff
# with:
# triple: ${{ matrix.triple }}
# architecture: ${{ matrix.architecture }}
#
# publish-results:
# runs-on: ubuntu-latest
# needs:
# - test-native
# - test-qemu
#
# steps:
# - name: Download Test Result Artifacts
# uses: actions/download-artifact@v3
# with:
# name: test-results
# path: test-results/
#
# - name: Publish Test Results
# uses: EnricoMi/publish-unit-test-result-action@v2
# with:
# action_fail: true
# action_fail_on_inconclusive: true
# check_name: "Test Results: Warning"
# files: test-results/**/*.xml
publish-coverage:
runs-on: ubuntu-latest
needs:
- test-native
# - test-qemu
env:
HI_LIMIT: 100
MED_LIMIT: 90
steps:
- name: Install Dependencies
run: |
sudo apt update
sudo apt install binutils # for c++filt
sudo apt install lcov
- name: Download Test Coverage Artifacts
uses: actions/download-artifact@v3
with:
name: test-coverage-internal
path: test-coverage-internal/
- name: Checkout patomic
uses: actions/checkout@v4
with:
path: patomic
- name: Copy patomic To All Original Mapping Paths
run: |
# go through all patomic.rootpath files
find ./test-coverage-internal -type f -name '*patomic.rootpath' -print0 | while IFS= read -r -d '' rp_file; do
root_path=$(cat "${rp_file}")
# create root_path if it doesn't exist
if [ ! -d "${root_path}" ]; then
sudo mkdir -p "${root_path}"
fi
# ensure full permissions even if it exists
sudo chmod -R a+rwx "${root_path}"
# copy patomic to root_path if not already there
if [ ! -d "${root_path}/patomic" ]; then
cp -R ./patomic/ "${root_path}/patomic"
fi
done
- name: Create Lcov Config File
run: |
touch ./lcovrc
echo "genhtml_hi_limit = ${{ env.HI_LIMIT }}" >> ./lcovrc
echo "genhtml_med_limit = ${{ env.MED_LIMIT }}" >> ./lcovrc
echo "genhtml_function_hi_limit = ${{ env.HI_LIMIT }}" >> ./lcovrc
echo "genhtml_function_med_limit = ${{ env.MED_LIMIT }}" >> ./lcovrc
echo "Contents of lcovrc:"
cat ./lcovrc
- name: Generate HTML Files
run: |
# directory into which to put user artifacts
mkdir test-coverage
# keep only coverage data relating to patomic source files
find ./test-coverage-internal -mindepth 1 -maxdepth 1 -type d -not -path '*/.*' -print0 | while IFS= read -r -d '' dir; do
mv "${dir}/patomic.lcov" "${dir}/patomic.lcov.old"
root_path=$(cat "${dir}/patomic.rootpath")
lcov --output-file "${dir}/patomic.lcov" --extract "${dir}/patomic.lcov.old" "${root_path}/patomic/src/*"
done
# generate html files for each separate compilation
find ./test-coverage-internal -mindepth 1 -maxdepth 1 -type d -not -path '*/.*' -print0 | while IFS= read -r -d '' dir; do
arch=$(basename "${dir}")
mkdir "./test-coverage/${arch}"
genhtml --output-directory "./test-coverage/${arch}" --title "patomic-${arch}" --show-details --num-spaces 4 --legend --demangle-cpp --config-file ./lcovrc --precision 2 "./test-coverage-internal/${arch}/patomic.lcov"
done
# generate html files for combined coverage
mkdir ./test-coverage/universal
lcov_combine_command=("lcov")
while IFS= read -r -d '' lcov_file; do
lcov_combine_command+=("-a" "${lcov_file}")
done < <(find ./test-coverage-internal -type f -name '*patomic.lcov' -print0)
lcov_combine_command+=("-o" "./patomic.lcov")
"${lcov_combine_command[@]}"
genhtml --output-directory "./test-coverage/universal" --title "patomic-universal" --show-details --num-spaces 4 --legend --demangle-cpp --config-file ./lcovrc --precision 2 ./patomic.lcov
- name: Upload Coverage Results
uses: actions/upload-artifact@v3
with:
name: test-coverage
path: test-coverage/
- name: Create Coverage Summary File
id: coverage-summary
shell: python
run: |
import dataclasses
import os
import pathlib
import re
import subprocess
@dataclasses.dataclass(frozen=True, kw_only=True)
class CoverageStats:
percent: float
count: int
total: int
if __name__ == "__main__":
# constants
HI_LEVEL = ${{ env.HI_LIMIT }}
MED_LEVEL = ${{ env.MED_LIMIT }}
BASE_DIR = pathlib.Path("./test-coverage-internal")
OUT_FILE = pathlib.Path("./coverage_summary.md")
# set up table
table_text = "| Target | Lines | Functions | Branches |\n"
table_text += "| :----- | ----: | --------: | -------: |\n"
# get all non-hidden directories in ./test-coverage
archs = [d for d in next(os.walk(BASE_DIR))[1] if d[0] != '.']
archs.sort()
# keep track if any result is below the thresholds
any_warn = False
any_fail = False
# helper lambdas
make_emoji = lambda p: "✅" if p >= HI_LEVEL else ("⚠️" if p >= MED_LEVEL else "🛑")
stat_to_str = lambda s: f"{s.percent:.2f}% ({s.count}/{s.total}) {make_emoji(s.percent)}"
# add all coverage results to table
for arch in archs:
cov_data = {}
# get lcov summary
lcov_path = BASE_DIR / arch / "patomic.lcov"
res = subprocess.run(f'lcov --summary "{lcov_path}', capture_output=True, shell=True, check=True)
if res.returncode != 0:
raise RuntimeError(f"Command '{res.args.join(' ')}' returned non-zero exit status {res.returncode}, with stderr: {res.stderr.decode()}")
lcov_summary = res.stdout.decode()
# go through all lines of lcov summary
for line in lcov_summary.split('\n'):
# skip non-info lines
if not line.lstrip().startswith(("lines", "functions", "branches")):
continue
else:
attr_name = line.lstrip().split('.', maxsplit=1)[0]
# parse coverage stats
if "no data found" in line:
cov = CoverageStats(percent=100.0, count=0, total=0)
else:
pattern = r"(\d+\.\d+)% \((\d+) of (\d+)"
matches = re.findall(pattern, line)[0]
cov = CoverageStats(percent=float(matches[0]), count=int(matches[1]), total=int(matches[2]))
# store for next step
cov_data[attr_name] = cov
# check if we failed
any_warn = any_warn or cov_data[attr_name].percent < HI_LEVEL
any_fail = any_fail or cov_data[attr_name].percent < MED_LEVEL
# fill out table
table_text += f"| `{arch}` | `{stat_to_str(cov_data['lines'])}` | `{stat_to_str(cov_data['functions'])}` | `{stat_to_str(cov_data['branches'])}` |\n"
# write result to file
with open(OUT_FILE, "w") as f:
f.write(table_text)
# set output variables
subprocess.run(f"echo 'any_warn={int(any_warn)}' >> $GITHUB_OUTPUT", shell=True, check=True)
subprocess.run(f"echo 'any_warn={int(any_fail)}' >> $GITHUB_OUTPUT", shell=True, check=True)
- name: Publish Coverage in Action Summary
run: |
# TODO
- name: Publish Coverage in Pull Request Comment
uses: thollander/actions-comment-pull-request@v2
with:
comment_tag: coverage-summary
filePath: coverage_summary.md
- name: Fail If Any Coverage Fails
run: |
if [[ '${{ steps.coverage-summary.outputs.any_fail }}' != '0' ]]; then
>&2 echo "At least one coverage result was below the threshold of ${{ env.MED_LIMIT }}"
exit 1
fi
# keep this last so that we have it for debugging if something fails
- name: Delete Test Coverage Artifacts
uses: geekyeggo/delete-artifact@v2
with:
name: test-coverage-internal