2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-09-02 15:45:25 +00:00

[9.20] fix: ci: Ensure that junit.xml is present and non-empty after each system/unit test job

Previously, JUnit files were not generated or were generated empty for various reasons for some system/unit test runs.

Now, the number of tests collected for a MR is up from about 4k to 5.8k in the "Tests" tab of a pipeline.

Additionally, there is a check that ensures that [a somewhat sane](c5a271eb8b) `junit.xml` file is generated after every system/unit test job and fails the job otherwise.

Closes #5316

Backport of MR !10556

Merge branch 'backport-5316-ensure-junit-xml-9.20' into 'bind-9.20'

See merge request isc-projects/bind9!10649
This commit is contained in:
Štěpán Balážik
2025-06-26 15:37:05 +00:00
2 changed files with 60 additions and 56 deletions

View File

@@ -334,6 +334,21 @@ stages:
.fips-feature-test: &fips_feature_test .fips-feature-test: &fips_feature_test
- if bin/tests/system/feature-test --have-fips-mode; then fips-mode-setup --check; fips-mode-setup --is-enabled; fi - if bin/tests/system/feature-test --have-fips-mode; then fips-mode-setup --check; fips-mode-setup --is-enabled; fi
.check_for_junit_xml: &check_for_junit_xml
# test if junit.xml file exists and is longer 40 bytes
# (i.e., contains more than `<testsuites><testsuite /></testsuites>`)
- if [ -f "$CI_PROJECT_DIR"/junit.xml ]; then
if [ $(wc -c < "$CI_PROJECT_DIR"/junit.xml) -gt 40 ]; then
echo "junit.xml file exists and is longer than 40 bytes.";
else
echo "junit.xml file exists but is too short.";
exit 1;
fi
else
echo "junit.xml file does not exist.";
exit 1;
fi
.build: &build_job .build: &build_job
<<: *default_triggering_rules <<: *default_triggering_rules
stage: build stage: build
@@ -415,28 +430,34 @@ stages:
artifacts: true artifacts: true
timeout: 2h timeout: 2h
.system_test_common: &system_test_common .system_test_common: &system_test_job
<<: *default_triggering_rules <<: *default_triggering_rules
stage: system stage: system
before_script: before_script:
- test -n "${OUT_OF_TREE_WORKSPACE}" && cp -r bin/tests/system/* "${OUT_OF_TREE_WORKSPACE}/bin/tests/system/" && cd "${OUT_OF_TREE_WORKSPACE}" - test -n "${OUT_OF_TREE_WORKSPACE}" && cp -r bin/tests/system/* "${OUT_OF_TREE_WORKSPACE}/bin/tests/system/" && cd "${OUT_OF_TREE_WORKSPACE}"
- *setup_interfaces - *setup_interfaces
# This script needs to: 1) fail if the system tests fail, 2) fail if
# the junit.xml file is broken, 3) produce the junit.xml file even if
# the system tests fail. Therefore, $RET is used to "cache" the
# result of running pytest as interrupting the script immediately when
# system tests fail would make checking the contents of the junit.xml
# file impossible (GitLab Runner uses "set -o pipefail").
script: script:
- *fips_feature_test - *fips_feature_test
- *find_pytest - *find_pytest
- *find_python - *find_python
- ( if [ "${CI_DISPOSABLE_ENVIRONMENT}" = "true" ]; then sleep 3000; "$PYTHON" "${CI_PROJECT_DIR}/util/get-running-system-tests.py"; fi ) & - ( if [ "${CI_DISPOSABLE_ENVIRONMENT}" = "true" ]; then sleep 3000; "$PYTHON" "${CI_PROJECT_DIR}/util/get-running-system-tests.py"; fi ) &
- cd bin/tests/system - cd bin/tests/system
- RET=0
- > - >
"$PYTEST" --junit-xml="$CI_PROJECT_DIR"/junit.xml -n "$TEST_PARALLEL_JOBS" | tee pytest.out.txt ("$PYTEST" --junit-xml="$CI_PROJECT_DIR"/junit.xml -n "$TEST_PARALLEL_JOBS" | tee pytest.out.txt) || RET=1
- *check_for_junit_xml
- (exit $RET)
- '( ! grep -F "grep: warning:" pytest.out.txt )' - '( ! grep -F "grep: warning:" pytest.out.txt )'
- test "$CLEAN_BUILD_ARTIFACTS_ON_SUCCESS" -eq 0 || ( cd ../../.. && make clean >/dev/null 2>&1 ) - test "$CLEAN_BUILD_ARTIFACTS_ON_SUCCESS" -eq 0 || ( cd ../../.. && make clean >/dev/null 2>&1 )
after_script: after_script:
- test -n "${OUT_OF_TREE_WORKSPACE}" && cd "${OUT_OF_TREE_WORKSPACE}" - test -n "${OUT_OF_TREE_WORKSPACE}" && cd "${OUT_OF_TREE_WORKSPACE}"
- *display_pytest_failures - *display_pytest_failures
.system_test: &system_test_job
<<: *system_test_common
artifacts: artifacts:
untracked: true untracked: true
exclude: exclude:
@@ -446,81 +467,65 @@ stages:
junit: junit.xml junit: junit.xml
.system_test_make_check: &system_test_make_check_job .system_test_make_check: &system_test_make_check_job
<<: *system_test_common <<: *system_test_job
# This script needs to: 1) fail if the system tests fail, 2) fail if
# the junit.xml file is broken, 3) produce the junit.xml file even if
# the system tests fail. Therefore, $RET is used to "cache" the
# result of "make check" as interrupting the script immediately when
# system tests fail would prevent the junit.xml file from being
# produced.
script: script:
- cd bin/tests/system - cd bin/tests/system
- make -j${TEST_PARALLEL_JOBS:-1} check - RET=0
- make -j${TEST_PARALLEL_JOBS:-1} check || RET=$?
- cd "$CI_PROJECT_DIR"
- *find_python
- >
"$PYTHON" bin/tests/convert-trs-to-junit.py . > "$CI_PROJECT_DIR"/junit.xml
- *check_for_junit_xml
- (exit $RET)
after_script: after_script:
- cat bin/tests/system/test-suite.log || true - cat bin/tests/system/test-suite.log || true
.system_test_gcov: &system_test_gcov_job
<<: *system_test_common
artifacts:
untracked: true
exclude:
- "**/__pycache__/**/*"
when: always
.system_test_tsan: &system_test_tsan_job .system_test_tsan: &system_test_tsan_job
<<: *system_test_common <<: *system_test_job
after_script: after_script:
- *display_pytest_failures - *display_pytest_failures
- find bin/tests/system -name "*dig.*" | xargs grep "error" || true - find bin/tests/system -name "*dig.*" | xargs grep "error" || true
- *find_python - *find_python
- *parse_tsan - *parse_tsan
- >
"$PYTHON" bin/tests/convert-trs-to-junit.py . > "$CI_PROJECT_DIR"/junit.xml
artifacts:
untracked: true
exclude:
- "**/__pycache__/**/*"
when: always
reports:
junit: junit.xml
.unit_test_common: &unit_test_common .unit_test_common: &unit_test_job
<<: *default_triggering_rules <<: *default_triggering_rules
stage: unit stage: unit
before_script: before_script:
- test -n "${OUT_OF_TREE_WORKSPACE}" && cd "${OUT_OF_TREE_WORKSPACE}" - test -n "${OUT_OF_TREE_WORKSPACE}" && cd "${OUT_OF_TREE_WORKSPACE}"
# This script needs to: 1) fail if the unit tests fail, 2) fail if the
# junit.xml file is broken, 3) produce the junit.xml file even if the
# unit tests fail. Therefore, $RET is used to "cache" the result of
# "make unit" as interrupting the script immediately when unit tests
# fail would prevent the junit.xml file from being produced.
script: script:
- *fips_feature_test - *fips_feature_test
- make -j${TEST_PARALLEL_JOBS:-1} -k unit V=1 - RET=0
- test "$CLEAN_BUILD_ARTIFACTS_ON_SUCCESS" -eq 0 || make clean >/dev/null 2>&1 - make -j${TEST_PARALLEL_JOBS:-1} -k unit V=1 || RET=$?
after_script:
- test -d bind-* && cd bind-*
- REALSOURCEDIR="$PWD"
- test -n "${OUT_OF_TREE_WORKSPACE}" && cd "${OUT_OF_TREE_WORKSPACE}"
- *find_python - *find_python
- > - >
"$PYTHON" "$REALSOURCEDIR"/bin/tests/convert-trs-to-junit.py . > "$CI_PROJECT_DIR"/junit.xml "$PYTHON" "$CI_PROJECT_DIR"/bin/tests/convert-trs-to-junit.py . > "$CI_PROJECT_DIR"/junit.xml
- *check_for_junit_xml
.unit_test: &unit_test_job - (exit $RET)
<<: *unit_test_common - test "$CLEAN_BUILD_ARTIFACTS_ON_SUCCESS" -eq 0 || make clean >/dev/null 2>&1
artifacts: artifacts:
untracked: true untracked: true
when: always when: always
reports: reports:
junit: junit.xml junit: junit.xml
.unit_test_gcov: &unit_test_gcov_job
<<: *unit_test_common
artifacts:
untracked: true
when: always
.unit_test_tsan: &unit_test_tsan_job .unit_test_tsan: &unit_test_tsan_job
<<: *unit_test_common <<: *unit_test_job
after_script: after_script:
- *find_python - *find_python
- *parse_tsan - *parse_tsan
- >
"$PYTHON" bin/tests/convert-trs-to-junit.py . > "$CI_PROJECT_DIR"/junit.xml
artifacts:
untracked: true
when: always
reports:
junit: junit.xml
.docs: &docs_job .docs: &docs_job
variables: variables:
@@ -991,7 +996,7 @@ gcc:bookworm:amd64:
system:gcc:bookworm:amd64: system:gcc:bookworm:amd64:
<<: *debian_bookworm_amd64_image <<: *debian_bookworm_amd64_image
<<: *system_test_gcov_job <<: *system_test_job
variables: variables:
CI_ENABLE_ALL_TESTS: 1 CI_ENABLE_ALL_TESTS: 1
CLEAN_BUILD_ARTIFACTS_ON_SUCCESS: 0 CLEAN_BUILD_ARTIFACTS_ON_SUCCESS: 0
@@ -1002,7 +1007,7 @@ system:gcc:bookworm:amd64:
unit:gcc:bookworm:amd64: unit:gcc:bookworm:amd64:
<<: *debian_bookworm_amd64_image <<: *debian_bookworm_amd64_image
<<: *unit_test_gcov_job <<: *unit_test_job
variables: variables:
CI_ENABLE_ALL_TESTS: 1 CI_ENABLE_ALL_TESTS: 1
CLEAN_BUILD_ARTIFACTS_ON_SUCCESS: 0 CLEAN_BUILD_ARTIFACTS_ON_SUCCESS: 0
@@ -1224,7 +1229,6 @@ gcc:tumbleweed:amd64:
system:gcc:tumbleweed:amd64: system:gcc:tumbleweed:amd64:
<<: *tumbleweed_latest_amd64_image <<: *tumbleweed_latest_amd64_image
<<: *system_test_job
<<: *system_test_make_check_job <<: *system_test_make_check_job
needs: needs:
- job: gcc:tumbleweed:amd64 - job: gcc:tumbleweed:amd64

View File

@@ -28,14 +28,14 @@ def read_trs_result(filename):
items = line.split() items = line.split()
if len(items) < 2: if len(items) < 2:
raise ValueError("unsupported line in trs file", filename, line) raise ValueError("unsupported line in trs file", filename, line)
if items[0] != (":test-result:"): if items[0] != (":global-test-result:"):
continue continue
if result is not None: if result is not None:
raise NotImplementedError("double :test-result:", filename) raise NotImplementedError("double :global-test-result:", filename)
result = items[1].upper() result = items[1].upper()
if result is None: if result is None:
raise ValueError(":test-result: not found", filename) raise ValueError(":global-test-result: not found", filename)
return result return result