Compare commits

...

15 Commits

Author SHA1 Message Date
01c3205f5d revert: remove cmocka 2.0 compatibility workaround
Some checks failed
CI Code / Code Coverage (pull_request) Has been cancelled
CI Code / Linux (arch) (pull_request) Has been cancelled
CI Code / Linux (ubuntu) (pull_request) Has been cancelled
CI Code / Check coding style (pull_request) Has been cancelled
CI Code / Check spelling (pull_request) Has been cancelled
CI Code / Linux (debian) (pull_request) Has been cancelled
Restore original prof_cmocka.h - the CMOCKA_DISABLE_DEPRECATION_WARNINGS
workaround is not needed for this project.
2026-01-19 16:33:22 +03:00
4789ada834 ci: fix deprecated lcov option
All checks were successful
CI Code / Check spelling (pull_request) Successful in 21s
CI Code / Check coding style (pull_request) Successful in 35s
CI Code / Code Coverage (pull_request) Successful in 15m29s
CI Code / Linux (ubuntu) (pull_request) Successful in 18m0s
CI Code / Linux (debian) (pull_request) Successful in 18m19s
CI Code / Linux (arch) (pull_request) Successful in 20m21s
Change lcov_branch_coverage to branch_coverage (new syntax)
2026-01-19 16:29:38 +03:00
f6b621ad40 ci: add branch coverage to lcov
All checks were successful
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Check coding style (pull_request) Successful in 37s
CI Code / Code Coverage (pull_request) Successful in 15m56s
CI Code / Linux (ubuntu) (pull_request) Successful in 18m20s
CI Code / Linux (debian) (pull_request) Successful in 18m42s
CI Code / Linux (arch) (pull_request) Successful in 20m9s
Enable branch coverage with --rc lcov_branch_coverage=1.
This provides more detailed coverage metrics:
- Line coverage: % of code lines executed
- Function coverage: % of functions called
- Branch coverage: % of if/else/switch branches taken

Branch coverage is a step towards MC/DC (Modified Condition/Decision Coverage)
used in safety-critical systems.

Also add coverage/ to .gitignore.
2026-01-19 15:53:52 +03:00
d80092821b ci: add functional tests to coverage + fix lcov errors
All checks were successful
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Check coding style (pull_request) Successful in 35s
CI Code / Code Coverage (pull_request) Successful in 15m28s
CI Code / Linux (ubuntu) (pull_request) Successful in 18m1s
CI Code / Linux (debian) (pull_request) Successful in 18m16s
CI Code / Linux (arch) (pull_request) Successful in 20m20s
1. Add 'make check-functional-parallel' to coverage job
   - Unit tests alone cover ~27% (mostly utilities)
   - Functional tests exercise UI, XMPP, and full application flow
   - Should significantly increase coverage

2. Fix lcov errors:
   - Remove /usr/lib/* pattern (unused, causes error)
   - Add 'unused' to --ignore-errors
2026-01-17 19:10:24 +03:00
ca5835c58e ci: fix coverage lcov exclude pattern
Some checks failed
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Check coding style (pull_request) Successful in 35s
CI Code / Code Coverage (pull_request) Failing after 14m46s
CI Code / Linux (ubuntu) (pull_request) Successful in 18m1s
CI Code / Linux (debian) (pull_request) Successful in 18m16s
CI Code / Linux (arch) (pull_request) Successful in 20m5s
The code is built in /usr/src/profanity/ inside Docker, so
excluding '/usr/*' removes all source files!

Fix: exclude only system headers/libs (/usr/include/*, /usr/lib/*),
not the entire /usr/ tree.

Also add --ignore-errors empty to handle edge cases.
2026-01-17 18:37:01 +03:00
1d113c4d64 ci: run coverage on PRs too, not just master push
Some checks failed
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Check coding style (pull_request) Successful in 30s
CI Code / Linux (ubuntu) (pull_request) Successful in 18m7s
CI Code / Linux (debian) (pull_request) Successful in 18m25s
CI Code / Linux (arch) (pull_request) Successful in 20m32s
CI Code / Code Coverage (pull_request) Failing after 12m34s
2026-01-17 18:13:47 +03:00
e40b89ad92 ci: improve test failure detection in parallel tests
All checks were successful
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Code Coverage (pull_request) Has been skipped
CI Code / Check coding style (pull_request) Successful in 30s
CI Code / Linux (ubuntu) (pull_request) Successful in 18m10s
CI Code / Linux (debian) (pull_request) Successful in 18m23s
CI Code / Linux (arch) (pull_request) Successful in 20m2s
1. ci-build.sh: Add verify_test_failure_detection() function
   - Tests that single test failures are properly detected
   - Tests that parallel test failures are properly detected
   - Runs at CI start to catch framework issues early

2. Makefile.am: Fix check-functional-parallel exit code handling
   - OLD: used 'wait' without checking individual exit codes
   - NEW: capture each PID and check exit code with 'wait $pid'
   - On failure: print the log file and set failed flag
   - Exit with error if any group failed

This ensures CI correctly fails when:
- Any unit test fails
- Any of the 4 functional test groups fails
- Any parallel build configuration fails
2026-01-17 18:03:07 +03:00
f41888b7a5 feat: add code coverage support
All checks were successful
CI Code / Code Coverage (pull_request) Has been skipped
CI Code / Check spelling (pull_request) Successful in 20s
CI Code / Check coding style (pull_request) Successful in 33s
CI Code / Linux (ubuntu) (pull_request) Successful in 18m16s
CI Code / Linux (debian) (pull_request) Successful in 18m27s
CI Code / Linux (arch) (pull_request) Successful in 20m58s
- Add --enable-coverage option to configure.ac
- Add coverage and coverage-html targets to Makefile.am
- Add coverage CI job with Codecov upload
- Add lcov to all Dockerfiles (arch, debian, fedora, tumbleweed, ubuntu)

Usage:
  ./configure --enable-coverage
  make check
  make coverage-html
2026-01-17 17:45:34 +03:00
c125746f6e perf: optimize build with configure cache and parallel make
All checks were successful
CI Code / Check spelling (pull_request) Successful in 18s
CI Code / Check coding style (pull_request) Successful in 36s
CI Code / Linux (ubuntu) (pull_request) Successful in 17m55s
CI Code / Linux (debian) (pull_request) Successful in 18m8s
CI Code / Linux (arch) (pull_request) Successful in 20m12s
- Add -C flag to ./configure for caching results
- Use --depth 1 for git clone (faster)
- Use make -j$(nproc) for stabber/libstrophe builds

Borrowed optimizations from build/multicore branch.
2026-01-16 19:33:56 +03:00
16d4efbccc ci: disable Docker cache to test cmocka 2.0 breakage
Add --no-cache to docker build to force fresh image build with
latest cmocka 2.0.1 from debian:testing.
2026-01-16 18:15:45 +03:00
e1a9f55a32 test: remove cmocka 2.0 fix to verify CI failure
All checks were successful
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Linux (debian) (pull_request) Successful in 15m51s
CI Code / Linux (ubuntu) (pull_request) Successful in 15m58s
CI Code / Linux (arch) (pull_request) Successful in 16m8s
Temporarily removing CMOCKA_DISABLE_DEPRECATION_WARNINGS to demonstrate
that tests fail on debian:testing (cmocka 2.0.1) without this fix.
2026-01-16 17:38:21 +03:00
7765f65be1 fix(build): use $(srcdir)/tests for VPATH builds
All checks were successful
CI Code / Linux (arch) (pull_request) Successful in 16m40s
CI Code / Check coding style (pull_request) Successful in 36s
CI Code / Check spelling (pull_request) Successful in 21s
CI Code / Linux (ubuntu) (pull_request) Successful in 18m20s
CI Code / Linux (debian) (pull_request) Successful in 18m41s
Fix include paths for out-of-tree builds (e.g., build-1/, build-2/)
to correctly find tests/prof_cmocka.h header file.
2026-01-16 17:11:51 +03:00
5153e04f96 feat: update compatibility header for cmocka 2.0 deprecation handling
All checks were successful
CI Code / Linux (debian) (pull_request) Successful in 12m52s
CI Code / Check coding style (pull_request) Successful in 32s
CI Code / Check spelling (pull_request) Successful in 17s
CI Code / Linux (ubuntu) (pull_request) Successful in 15m26s
CI Code / Linux (arch) (pull_request) Successful in 18m7s
2026-01-16 16:47:48 +03:00
bed046e7ed feat(tests): update Dockerfile and CI script for parallel builds and improved caching
All checks were successful
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Check coding style (pull_request) Successful in 33s
CI Code / Linux (ubuntu) (pull_request) Successful in 15m23s
CI Code / Linux (debian) (pull_request) Successful in 15m35s
CI Code / Linux (arch) (pull_request) Successful in 18m11s
2026-01-15 17:20:19 +03:00
fa17173e5c feat(tests): enable parallel execution of functional tests
All checks were successful
CI Code / Check spelling (pull_request) Successful in 19s
CI Code / Check coding style (pull_request) Successful in 34s
CI Code / Linux (ubuntu) (pull_request) Successful in 30m9s
CI Code / Linux (arch) (pull_request) Successful in 33m47s
CI Code / Linux (debian) (pull_request) Successful in 33m9s
2026-01-13 17:46:03 +03:00
13 changed files with 392 additions and 117 deletions

View File

@@ -31,7 +31,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Run tests - name: Run tests
run: | run: |
docker build -f Dockerfile.${{ matrix.flavor }} -t profanity . docker build --no-cache -f Dockerfile.${{ matrix.flavor }} -t profanity .
docker run profanity ./ci-build.sh docker run profanity ./ci-build.sh
code-style: code-style:
@@ -98,3 +98,33 @@ jobs:
- name: Check spelling - name: Check spelling
run: | run: |
codespell codespell
coverage:
runs-on: ubuntu-latest
name: Code Coverage
steps:
- uses: actions/checkout@v4
- name: Build and run coverage
run: |
docker build -f Dockerfile.debian -t profanity-cov .
docker run -v ${{ github.workspace }}/coverage:/coverage profanity-cov bash -c '
./bootstrap.sh
./configure --enable-coverage --enable-otr --enable-pgp --enable-omemo --enable-plugins
make -j$(nproc)
make check || true
make check-functional-parallel || true
lcov --capture --directory . --output-file /coverage/coverage.info \
--rc branch_coverage=1 \
--ignore-errors inconsistent
lcov --remove /coverage/coverage.info \
"/usr/include/*" "*/tests/*" \
--output-file /coverage/coverage.info \
--rc branch_coverage=1 \
--ignore-errors inconsistent,empty,unused
'
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: ./coverage/coverage.info
fail_ci_if_error: false
verbose: true

1
.gitignore vendored
View File

@@ -107,3 +107,4 @@ breaks
*.tar.* *.tar.*
*.zip *.zip
*.log* *.log*
coverage/

View File

@@ -1,6 +1,7 @@
FROM archlinux FROM archlinux
ENV TERM=xterm ENV TERM=xterm
ENV CC="ccache gcc"
RUN pacman -Syu --noconfirm RUN pacman -Syu --noconfirm
# reflector is optional - if it fails due to network issues, continue with default mirrorlist # reflector is optional - if it fails due to network issues, continue with default mirrorlist
@@ -12,9 +13,11 @@ RUN pacman -S --needed --noconfirm \
autoconf-archive \ autoconf-archive \
automake \ automake \
base-devel \ base-devel \
ccache \
check \ check \
cmake \ cmake \
cmocka \ cmocka \
lcov \
curl \ curl \
debuginfod \ debuginfod \
doxygen \ doxygen \
@@ -61,12 +64,12 @@ USER root
RUN pacman -U --noconfirm libstrophe-git/libstrophe-git-*.pkg.tar.zst RUN pacman -U --noconfirm libstrophe-git/libstrophe-git-*.pkg.tar.zst
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber RUN git clone --depth 1 -c http.sslverify=false https://git.jabber.space/devs/stabber
WORKDIR /usr/src/stabber WORKDIR /usr/src/stabber
RUN ./bootstrap.sh RUN ./bootstrap.sh
RUN ./configure --prefix=/usr --disable-dependency-tracking RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make RUN make -j$(nproc)
RUN make install RUN make install
WORKDIR /usr/src/profanity WORKDIR /usr/src/profanity

View File

@@ -3,13 +3,16 @@ FROM debian:testing
ENV DEBIAN_FRONTEND="noninteractive" ENV DEBIAN_FRONTEND="noninteractive"
ENV TERM=xterm ENV TERM=xterm
ENV CC="ccache gcc"
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
autoconf \ autoconf \
autoconf-archive \ autoconf-archive \
automake \ automake \
ccache \
gcc \ gcc \
git \ git \
lcov \
libcmocka-dev \ libcmocka-dev \
libcurl3-dev \ libcurl3-dev \
libgcrypt-dev \ libgcrypt-dev \
@@ -37,19 +40,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN mkdir -p /usr/src/{stabber,libstrophe,profanity} RUN mkdir -p /usr/src/{stabber,libstrophe,profanity}
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber RUN git clone --depth 1 -c http.sslverify=false https://git.jabber.space/devs/stabber
RUN git clone -c http.sslverify=false https://github.com/strophe/libstrophe RUN git clone --depth 1 -c http.sslverify=false https://github.com/strophe/libstrophe
WORKDIR /usr/src/stabber WORKDIR /usr/src/stabber
RUN ./bootstrap.sh RUN ./bootstrap.sh
RUN ./configure --prefix=/usr --disable-dependency-tracking RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make RUN make -j$(nproc)
RUN make install RUN make install
WORKDIR /usr/src/libstrophe WORKDIR /usr/src/libstrophe
RUN ./bootstrap.sh RUN ./bootstrap.sh
RUN ./configure --prefix=/usr RUN ./configure --prefix=/usr
RUN make RUN make -j$(nproc)
RUN make install RUN make install
WORKDIR /usr/src/profanity WORKDIR /usr/src/profanity

View File

@@ -1,6 +1,9 @@
# Build the latest Fedora image # Build the latest Fedora image
FROM fedora:latest FROM fedora:latest
ENV TERM=xterm
ENV CC="ccache gcc"
# libmicrohttpd - for stabber # libmicrohttpd - for stabber
# glibc-locale - to have en_US locale # glibc-locale - to have en_US locale
RUN dnf install -y \ RUN dnf install -y \
@@ -8,8 +11,10 @@ RUN dnf install -y \
autoconf-archive \ autoconf-archive \
automake \ automake \
awk \ awk \
ccache \
gcc \ gcc \
git \ git \
lcov \
glib2-devel \ glib2-devel \
glibc-all-langpacks \ glibc-all-langpacks \
gtk2-devel \ gtk2-devel \
@@ -45,20 +50,20 @@ ENV TERM=xterm
RUN mkdir -p /usr/src RUN mkdir -p /usr/src
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber RUN git clone --depth 1 -c http.sslverify=false https://git.jabber.space/devs/stabber
WORKDIR /usr/src/stabber WORKDIR /usr/src/stabber
RUN ./bootstrap.sh RUN ./bootstrap.sh
RUN ./configure --prefix=/usr --disable-dependency-tracking RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make RUN make -j$(nproc)
RUN make install RUN make install
WORKDIR /usr/src WORKDIR /usr/src
RUN mkdir -p /usr/src/libstrophe RUN mkdir -p /usr/src/libstrophe
RUN git clone -c http.sslverify=false https://github.com/strophe/libstrophe RUN git clone --depth 1 -c http.sslverify=false https://github.com/strophe/libstrophe
WORKDIR /usr/src/libstrophe WORKDIR /usr/src/libstrophe
RUN ./bootstrap.sh RUN ./bootstrap.sh
RUN ./configure --prefix=/usr RUN ./configure --prefix=/usr
RUN make RUN make -j$(nproc)
RUN make install RUN make install
RUN mkdir -p /usr/src/profanity RUN mkdir -p /usr/src/profanity

View File

@@ -1,14 +1,19 @@
# Build the latest openSUSE Tumbleweed image # Build the latest openSUSE Tumbleweed image
FROM opensuse/tumbleweed FROM opensuse/tumbleweed
ENV TERM=xterm
ENV CC="ccache gcc"
# libmicrohttpd - for stabber # libmicrohttpd - for stabber
# glibc-locale - to have en_US locale # glibc-locale - to have en_US locale
RUN zypper --non-interactive in --no-recommends \ RUN zypper --non-interactive in --no-recommends \
autoconf \ autoconf \
autoconf-archive \ autoconf-archive \
automake \ automake \
ccache \
gcc \ gcc \
git \ git \
lcov \
glib2-devel \ glib2-devel \
glibc-locale \ glibc-locale \
gtk2-devel \ gtk2-devel \
@@ -44,11 +49,11 @@ ENV TERM=xterm
RUN mkdir -p /usr/src RUN mkdir -p /usr/src
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber RUN git clone --depth 1 -c http.sslverify=false https://git.jabber.space/devs/stabber
WORKDIR /usr/src/stabber WORKDIR /usr/src/stabber
RUN ./bootstrap.sh RUN ./bootstrap.sh
RUN ./configure --prefix=/usr --disable-dependency-tracking RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make RUN make -j$(nproc)
RUN make install RUN make install
RUN mkdir -p /usr/src/profanity RUN mkdir -p /usr/src/profanity

View File

@@ -2,13 +2,16 @@ FROM ubuntu:latest
ENV DEBIAN_FRONTEND="noninteractive" ENV DEBIAN_FRONTEND="noninteractive"
ENV TERM=xterm ENV TERM=xterm
ENV CC="ccache gcc"
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
autoconf \ autoconf \
autoconf-archive \ autoconf-archive \
automake \ automake \
ccache \
gcc \ gcc \
git \ git \
lcov \
libcmocka-dev \ libcmocka-dev \
libcurl3-dev \ libcurl3-dev \
libgcrypt-dev \ libgcrypt-dev \
@@ -36,19 +39,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN mkdir -p /usr/src/{stabber,libstrophe,profanity} RUN mkdir -p /usr/src/{stabber,libstrophe,profanity}
WORKDIR /usr/src WORKDIR /usr/src
RUN git clone -c http.sslverify=false https://git.jabber.space/devs/stabber RUN git clone --depth 1 -c http.sslverify=false https://git.jabber.space/devs/stabber
RUN git clone -c http.sslverify=false https://github.com/strophe/libstrophe RUN git clone --depth 1 -c http.sslverify=false https://github.com/strophe/libstrophe
WORKDIR /usr/src/stabber WORKDIR /usr/src/stabber
RUN ./bootstrap.sh RUN ./bootstrap.sh
RUN ./configure --prefix=/usr --disable-dependency-tracking RUN ./configure --prefix=/usr --disable-dependency-tracking
RUN make RUN make -j$(nproc)
RUN make install RUN make install
WORKDIR /usr/src/libstrophe WORKDIR /usr/src/libstrophe
RUN ./bootstrap.sh RUN ./bootstrap.sh
RUN ./configure --prefix=/usr RUN ./configure --prefix=/usr
RUN make RUN make -j$(nproc)
RUN make install RUN make install
WORKDIR /usr/src/profanity WORKDIR /usr/src/profanity

View File

@@ -286,7 +286,7 @@ endif
TESTS = tests/unittests/unittests TESTS = tests/unittests/unittests
check_PROGRAMS = tests/unittests/unittests check_PROGRAMS = tests/unittests/unittests
tests_unittests_unittests_CPPFLAGS = -Itests/ tests_unittests_unittests_CPPFLAGS = -I$(srcdir)/tests
tests_unittests_unittests_SOURCES = $(unittest_sources) tests_unittests_unittests_SOURCES = $(unittest_sources)
tests_unittests_unittests_LDADD = -lcmocka tests_unittests_unittests_LDADD = -lcmocka
@@ -302,9 +302,28 @@ if HAVE_FORKPTY
TESTS += tests/functionaltests/functionaltests TESTS += tests/functionaltests/functionaltests
check_PROGRAMS += tests/functionaltests/functionaltests check_PROGRAMS += tests/functionaltests/functionaltests
tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources) tests_functionaltests_functionaltests_SOURCES = $(functionaltest_sources)
tests_functionaltests_functionaltests_CPPFLAGS = -Itests/ tests_functionaltests_functionaltests_CPPFLAGS = -I$(srcdir)/tests
tests_functionaltests_functionaltests_CFLAGS = $(AM_CFLAGS) tests_functionaltests_functionaltests_CFLAGS = $(AM_CFLAGS)
tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber @FORKPTY_LIB@ tests_functionaltests_functionaltests_LDADD = -lcmocka -lstabber @FORKPTY_LIB@
# Parallel functional tests target (~3x faster than sequential)
# Usage: make check-functional-parallel
check-functional-parallel: tests/functionaltests/functionaltests
@echo "Running functional tests in parallel (4 groups)..."
@mkdir -p $(builddir)/test-logs
@failed=0; \
./tests/functionaltests/functionaltests 1 > $(builddir)/test-logs/group1.log 2>&1 & pid1=$$!; \
./tests/functionaltests/functionaltests 2 > $(builddir)/test-logs/group2.log 2>&1 & pid2=$$!; \
./tests/functionaltests/functionaltests 3 > $(builddir)/test-logs/group3.log 2>&1 & pid3=$$!; \
./tests/functionaltests/functionaltests 4 > $(builddir)/test-logs/group4.log 2>&1 & pid4=$$!; \
wait $$pid1 || { echo "Group 1 FAILED (exit $$?)"; cat $(builddir)/test-logs/group1.log; failed=1; }; \
wait $$pid2 || { echo "Group 2 FAILED (exit $$?)"; cat $(builddir)/test-logs/group2.log; failed=1; }; \
wait $$pid3 || { echo "Group 3 FAILED (exit $$?)"; cat $(builddir)/test-logs/group3.log; failed=1; }; \
wait $$pid4 || { echo "Group 4 FAILED (exit $$?)"; cat $(builddir)/test-logs/group4.log; failed=1; }; \
echo "=== Test Results Summary ==="; \
grep -E 'PASSED|FAILED|Running' $(builddir)/test-logs/group*.log || true; \
if [ $$failed -ne 0 ]; then echo "FUNCTIONAL TESTS FAILED"; exit 1; fi; \
echo "All functional test groups passed!"
endif endif
endif endif
@@ -367,6 +386,20 @@ check-unit: tests/unittests/unittests
@VALGRIND_CHECK_RULES@ @VALGRIND_CHECK_RULES@
VALGRIND_SUPPRESSIONS_FILES=prof.supp VALGRIND_SUPPRESSIONS_FILES=prof.supp
# Code coverage targets (requires --enable-coverage)
coverage-clean:
find . -name '*.gcda' -delete
find . -name '*.gcno' -delete
rm -rf coverage-html coverage.info
coverage-report: check
lcov --capture --directory . --output-file coverage.info --ignore-errors inconsistent
lcov --remove coverage.info '/usr/*' '*/tests/*' --output-file coverage.info --ignore-errors inconsistent
genhtml coverage.info --output-directory coverage-html
@echo "Coverage report generated in coverage-html/index.html"
.PHONY: coverage-clean coverage-report
format: $(all_c_sources) format: $(all_c_sources)
clang-format -i $(all_c_sources) clang-format -i $(all_c_sources)

View File

@@ -23,6 +23,79 @@ error_handler()
trap error_handler ERR trap error_handler ERR
# Verify that test failures are properly detected
# This is a meta-test: it runs a deliberately failing test
# and checks that the test framework reports the failure correctly
verify_test_failure_detection()
{
echo
echo "==> Verifying test failure detection..."
# Create a simple failing test
cat > /tmp/test_must_fail.c << 'EOF'
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
static void test_that_must_fail(void **state) {
(void)state;
assert_true(0); // This MUST fail
}
int main(void) {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_that_must_fail),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}
EOF
# Compile the failing test
if ! gcc -o /tmp/test_must_fail /tmp/test_must_fail.c -lcmocka 2>/dev/null; then
echo "Warning: Could not compile test failure verification (cmocka not available?)"
echo "Skipping test failure detection verification"
return 0
fi
# Test 1: Single failing test detection
echo " Testing single test failure detection..."
if /tmp/test_must_fail > /tmp/test_must_fail.log 2>&1; then
echo "ERROR: Test that should fail returned success (exit code 0)"
echo "This means the test framework is NOT detecting failures correctly!"
echo "--- Test output ---"
cat /tmp/test_must_fail.log
echo "--- End output ---"
rm -f /tmp/test_must_fail /tmp/test_must_fail.c /tmp/test_must_fail.log
exit 1
fi
echo " ✓ Single test failure correctly detected"
# Test 2: Parallel failure detection (simulates check-functional-parallel)
echo " Testing parallel test failure detection..."
failed=0
/tmp/test_must_fail > /tmp/p1.log 2>&1 & pid1=$!
true > /tmp/p2.log 2>&1 & pid2=$! # This passes
/tmp/test_must_fail > /tmp/p3.log 2>&1 & pid3=$!
true > /tmp/p4.log 2>&1 & pid4=$! # This passes
wait $pid1 || failed=$((failed + 1))
wait $pid2 || failed=$((failed + 1))
wait $pid3 || failed=$((failed + 1))
wait $pid4 || failed=$((failed + 1))
if [ $failed -ne 2 ]; then
echo "ERROR: Expected 2 failures in parallel tests, got $failed"
echo "Parallel failure detection is broken!"
rm -f /tmp/test_must_fail /tmp/test_must_fail.c /tmp/test_must_fail.log /tmp/p?.log
exit 1
fi
echo " ✓ Parallel test failures correctly detected (2 of 4 failed as expected)"
rm -f /tmp/test_must_fail /tmp/test_must_fail.c /tmp/test_must_fail.log /tmp/p?.log
echo "✓ Test failure detection verified"
}
num_cores() num_cores()
{ {
# Check for cores, for systems with: # Check for cores, for systems with:
@@ -34,6 +107,9 @@ num_cores()
|| getconf _NPROCESSORS_ONLN 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null
} }
# Run test failure detection verification first
verify_test_failure_detection
./bootstrap.sh ./bootstrap.sh
tests=() tests=()
@@ -44,7 +120,7 @@ ARCH="$(uname | tr '[:upper:]' '[:lower:]')"
case "$ARCH" in case "$ARCH" in
linux*) linux*)
# Reduced set of configurations for faster CI # 4 configurations for parallel CI
tests=( tests=(
# 1. Full build (all features enabled) # 1. Full build (all features enabled)
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp "--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
@@ -56,15 +132,13 @@ case "$ARCH" in
--disable-python-plugins --without-xscreensaver --disable-omemo-qrcode --disable-gdk-pixbuf" --disable-python-plugins --without-xscreensaver --disable-omemo-qrcode --disable-gdk-pixbuf"
# 3. No encryption (disable otr, pgp, omemo) # 3. No encryption (disable otr, pgp, omemo)
"--disable-pgp --disable-otr --disable-omemo --disable-omemo-qrcode" "--disable-pgp --disable-otr --disable-omemo --disable-omemo-qrcode"
# 4. No plugins # 4. Default configuration
"--disable-plugins --disable-c-plugins --disable-python-plugins"
# 5. Default configuration
"" ""
) )
source /etc/profile.d/debuginfod.sh 2>/dev/null || true source /etc/profile.d/debuginfod.sh 2>/dev/null || true
;; ;;
darwin*) darwin*)
# Reduced set of configurations for faster CI # 4 configurations for parallel CI
tests=( tests=(
# 1. Full build (all features enabled) # 1. Full build (all features enabled)
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp "--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
@@ -76,9 +150,7 @@ case "$ARCH" in
--disable-python-plugins" --disable-python-plugins"
# 3. No encryption (disable otr, pgp, omemo) # 3. No encryption (disable otr, pgp, omemo)
"--disable-pgp --disable-otr --disable-omemo" "--disable-pgp --disable-otr --disable-omemo"
# 4. No plugins # 4. Default configuration
"--disable-plugins --disable-c-plugins --disable-python-plugins"
# 5. Default configuration
"" ""
) )
;; ;;
@@ -90,7 +162,7 @@ case "$ARCH" in
# src/event/server_events.c:1454:19: error: universal character names are only valid in C++ and C99 # src/event/server_events.c:1454:19: error: universal character names are only valid in C++ and C99
CC="egcc -std=gnu99 -fexec-charset=UTF-8" CC="egcc -std=gnu99 -fexec-charset=UTF-8"
# Reduced set of configurations for faster CI # 4 configurations for parallel CI
tests=( tests=(
# 1. Full build (all features enabled) # 1. Full build (all features enabled)
"--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp "--enable-notifications --enable-icons-and-clipboard --enable-otr --enable-pgp
@@ -102,9 +174,7 @@ case "$ARCH" in
--disable-python-plugins" --disable-python-plugins"
# 3. No encryption (disable otr, pgp, omemo) # 3. No encryption (disable otr, pgp, omemo)
"--disable-pgp --disable-otr --disable-omemo" "--disable-pgp --disable-otr --disable-omemo"
# 4. No plugins # 4. Default configuration
"--disable-plugins --disable-c-plugins --disable-python-plugins"
# 5. Default configuration
"" ""
) )
;; ;;
@@ -113,11 +183,11 @@ esac
case "$ARCH" in case "$ARCH" in
linux*) linux*)
echo echo
echo "--> Building with ./configure ${tests[0]} --enable-valgrind $*" echo "--> Building with ./configure -C ${tests[0]} --enable-valgrind $*"
echo echo
# shellcheck disable=SC2086 # shellcheck disable=SC2086
./configure ${tests[0]} --enable-valgrind $* ./configure -C ${tests[0]} --enable-valgrind $*
$MAKE CC="${CC}" $MAKE CC="${CC}"
if grep '^ID=' /etc/os-release | grep -q -e debian; then if grep '^ID=' /etc/os-release | grep -q -e debian; then
@@ -129,18 +199,73 @@ case "$ARCH" in
;; ;;
esac esac
for features in "${tests[@]}" # Function to build and test a single configuration
do build_and_test() {
echo local features="$1"
echo "--> Building with ./configure ${features} $*" local extra_args="$2"
echo local idx="$3"
local build_dir="build-$idx"
local log_file="build-$idx.log"
# shellcheck disable=SC2086 {
./configure $features $* echo "=== Build $idx started at $(date) ==="
echo "--> Building in $build_dir with ./configure -C $features $extra_args"
$MAKE CC="${CC}" mkdir -p "$build_dir"
$MAKE check cd "$build_dir"
./profanity -v # shellcheck disable=SC2086
$MAKE clean ../configure -C $features $extra_args
$MAKE CC="${CC}"
$MAKE check-functional-parallel
./profanity -v
$MAKE clean
cd ..
rm -rf "$build_dir"
echo "=== Build $idx completed at $(date) ==="
} > "$log_file" 2>&1
}
# Run all 4 configurations in parallel
echo "Starting parallel builds..."
pids=()
for idx in 1 2 3 4; do
if [ $idx -le ${#tests[@]} ]; then
build_and_test "${tests[$((idx-1))]}" "$*" "$idx" &
pids+=("$!")
echo "Started build $idx (PID: $!)"
fi
done done
# Wait for all builds and check exit codes
failed=0
for i in "${!pids[@]}"; do
idx=$((i + 1))
if wait "${pids[$i]}"; then
echo "✓ Build $idx passed"
else
echo "✗ Build $idx failed"
echo "--- Log for build $idx ---"
cat "build-$idx.log"
echo "--- End log ---"
failed=1
fi
done
# Show all logs on success too
if [ $failed -eq 0 ]; then
echo
echo "All builds passed!"
for idx in 1 2 3 4; do
if [ -f "build-$idx.log" ]; then
echo "--- Log for build $idx ---"
cat "build-$idx.log"
fi
done
else
exit 1
fi

View File

@@ -69,6 +69,8 @@ AC_ARG_ENABLE([gdk-pixbuf],
[AS_HELP_STRING([--enable-gdk-pixbuf], [enable GDK Pixbuf support to scale avatars before uploading])]) [AS_HELP_STRING([--enable-gdk-pixbuf], [enable GDK Pixbuf support to scale avatars before uploading])])
AC_ARG_ENABLE([omemo-qrcode], AC_ARG_ENABLE([omemo-qrcode],
[AS_HELP_STRING([--enable-omemo-qrcode], [enable ability to display omemo qr code])]) [AS_HELP_STRING([--enable-omemo-qrcode], [enable ability to display omemo qr code])])
AC_ARG_ENABLE([coverage],
[AS_HELP_STRING([--enable-coverage], [enable code coverage analysis])])
m4_include([m4/ax_valgrind_check.m4]) m4_include([m4/ax_valgrind_check.m4])
AX_VALGRIND_DFLT([drd], [off]) AX_VALGRIND_DFLT([drd], [off])
@@ -386,6 +388,11 @@ AC_SUBST([FORKPTY_LIB])
AM_CFLAGS="$AM_CFLAGS -Wall -Wno-deprecated-declarations -std=gnu99 -ggdb3" AM_CFLAGS="$AM_CFLAGS -Wall -Wno-deprecated-declarations -std=gnu99 -ggdb3"
AM_LDFLAGS="$AM_LDFLAGS -export-dynamic" AM_LDFLAGS="$AM_LDFLAGS -export-dynamic"
AS_IF([test "x$enable_coverage" = xyes],
[AM_CFLAGS="$AM_CFLAGS --coverage -O0"
AM_LDFLAGS="$AM_LDFLAGS --coverage"
AC_MSG_NOTICE([Code coverage analysis enabled])])
AS_IF([test "x$PACKAGE_STATUS" = xdevelopment], AS_IF([test "x$PACKAGE_STATUS" = xdevelopment],
[AM_CFLAGS="$AM_CFLAGS -Wunused -Werror"]) [AM_CFLAGS="$AM_CFLAGS -Wunused -Werror"])
AS_IF([test "x$PLATFORM" = xosx], AS_IF([test "x$PLATFORM" = xosx],

View File

@@ -14,16 +14,28 @@
* flaky tests caused by leftover state. The overhead is acceptable since * flaky tests caused by leftover state. The overhead is acceptable since
* functional tests run less frequently than unit tests. * functional tests run less frequently than unit tests.
* *
* Tests are organized into groups for better maintainability: * Tests are organized into groups for better maintainability and parallel execution:
* Group 1: Connection, Ping, Rooms, Presence * Group 1: Connect, Ping, Rooms, Software (17 tests)
* Group 2: Messages, Receipts, Roster management * Group 2: Message, Receipts, Roster, Chat Session (17 tests)
* Group 3: MUC (Multi-User Chat) functionality * Group 3: Presence (16 tests)
* Group 4: Carbons, Chat sessions, Software version, Disconnect * Group 4: MUC, Carbons (19 tests)
*
* Parallel execution:
* ./functionaltests - run all tests sequentially
* ./functionaltests 1 - run group 1 only
* ./functionaltests 2 - run group 2 only
* ./functionaltests 3 - run group 3 only
* ./functionaltests 4 - run group 4 only
*
* For parallel execution, run multiple groups simultaneously:
* ./functionaltests 1 & ./functionaltests 2 & ./functionaltests 3 & ./functionaltests 4 & wait
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h>
#include "prof_cmocka.h" #include "prof_cmocka.h"
#include <sys/stat.h> #include <sys/stat.h>
@@ -49,14 +61,21 @@
int int
main(int argc, char* argv[]) main(int argc, char* argv[])
{ {
const struct CMUnitTest all_tests[] = { int group = 0; /* 0 = all groups */
if (argc > 1) {
group = atoi(argv[1]);
if (group < 1 || group > 4) {
fprintf(stderr, "Usage: %s [group]\n", argv[0]);
fprintf(stderr, " group: 1-4 to run specific group, or omit for all\n");
return 1;
}
}
/* ============================================================ /* GROUP 1: Connect, Ping, Rooms, Software (17 tests)
* GROUP 1: Connect, Ping, Rooms, Presence * Basic XMPP session establishment and server queries */
* Basic XMPP session establishment and presence management
* ============================================================ */
/* Connection tests - verify login, roster, bookmarks */ /* Connection tests - verify login, roster, bookmarks */
const struct CMUnitTest group1_tests[] = {
PROF_FUNC_TEST(connect_jid_requests_roster), PROF_FUNC_TEST(connect_jid_requests_roster),
PROF_FUNC_TEST(connect_jid_sends_presence_after_receiving_roster), PROF_FUNC_TEST(connect_jid_sends_presence_after_receiving_roster),
PROF_FUNC_TEST(connect_jid_requests_bookmarks), PROF_FUNC_TEST(connect_jid_requests_bookmarks),
@@ -73,27 +92,18 @@ main(int argc, char* argv[])
/* Room discovery - XEP-0045 */ /* Room discovery - XEP-0045 */
PROF_FUNC_TEST(rooms_query), PROF_FUNC_TEST(rooms_query),
/* Presence tests - online/away/xa/dnd/chat status */ /* Software Version - XEP-0092 */
PROF_FUNC_TEST(presence_online), PROF_FUNC_TEST(send_software_version_request),
PROF_FUNC_TEST(presence_online_with_message), PROF_FUNC_TEST(display_software_version_result),
PROF_FUNC_TEST(presence_away), PROF_FUNC_TEST(shows_message_when_software_version_error),
PROF_FUNC_TEST(presence_away_with_message), PROF_FUNC_TEST(display_software_version_result_when_from_domainpart),
PROF_FUNC_TEST(presence_xa), PROF_FUNC_TEST(show_message_in_chat_window_when_no_resource),
PROF_FUNC_TEST(presence_xa_with_message), PROF_FUNC_TEST(display_software_version_result_in_chat),
PROF_FUNC_TEST(presence_dnd), };
PROF_FUNC_TEST(presence_dnd_with_message),
PROF_FUNC_TEST(presence_chat),
PROF_FUNC_TEST(presence_chat_with_message),
PROF_FUNC_TEST(presence_set_priority),
PROF_FUNC_TEST(presence_includes_priority),
PROF_FUNC_TEST(presence_keeps_status),
PROF_FUNC_TEST(presence_received),
PROF_FUNC_TEST(presence_missing_resource_defaults),
/* ============================================================ /* GROUP 2: Message, Receipts, Roster, Chat Session (17 tests)
* GROUP 2: Message, Receipts, Roster * Core messaging and contact management */
* Core messaging and contact management const struct CMUnitTest group2_tests[] = {
* ============================================================ */
/* Basic message send/receive */ /* Basic message send/receive */
PROF_FUNC_TEST(message_send), PROF_FUNC_TEST(message_send),
@@ -112,21 +122,51 @@ main(int argc, char* argv[])
PROF_FUNC_TEST(sends_remove_item_nick), PROF_FUNC_TEST(sends_remove_item_nick),
PROF_FUNC_TEST(sends_nick_change), PROF_FUNC_TEST(sends_nick_change),
/* ============================================================ /* Chat session management - bare/full JID routing */
* GROUP 3: MUC (Multi-User Chat) PROF_FUNC_TEST(sends_message_to_barejid_when_contact_offline),
* XEP-0045 conference room functionality PROF_FUNC_TEST(sends_message_to_barejid_when_contact_online),
* ============================================================ */ PROF_FUNC_TEST(sends_message_to_fulljid_when_received_from_fulljid),
PROF_FUNC_TEST(sends_subsequent_messages_to_fulljid),
PROF_FUNC_TEST(resets_to_barejid_after_presence_received),
PROF_FUNC_TEST(new_session_when_message_received_from_different_fulljid),
};
/* Room join with various options */ /* GROUP 3: Presence (16 tests)
* Online/away/xa/dnd/chat status management */
const struct CMUnitTest group3_tests[] = {
PROF_FUNC_TEST(presence_online),
PROF_FUNC_TEST(presence_online_with_message),
PROF_FUNC_TEST(presence_away),
PROF_FUNC_TEST(presence_away_with_message),
PROF_FUNC_TEST(presence_xa),
PROF_FUNC_TEST(presence_xa_with_message),
PROF_FUNC_TEST(presence_dnd),
PROF_FUNC_TEST(presence_dnd_with_message),
PROF_FUNC_TEST(presence_chat),
PROF_FUNC_TEST(presence_chat_with_message),
PROF_FUNC_TEST(presence_set_priority),
PROF_FUNC_TEST(presence_includes_priority),
PROF_FUNC_TEST(presence_keeps_status),
PROF_FUNC_TEST(presence_received),
PROF_FUNC_TEST(presence_missing_resource_defaults),
/* Disconnect - clean session termination */
PROF_FUNC_TEST(disconnect_ends_session),
};
/* GROUP 4: MUC, Carbons (19 tests)
* Multi-user chat and message synchronization */
const struct CMUnitTest group4_tests[] = {
/* MUC room join with various options - XEP-0045 */
PROF_FUNC_TEST(sends_room_join), PROF_FUNC_TEST(sends_room_join),
PROF_FUNC_TEST(sends_room_join_with_nick), PROF_FUNC_TEST(sends_room_join_with_nick),
PROF_FUNC_TEST(sends_room_join_with_password), PROF_FUNC_TEST(sends_room_join_with_password),
PROF_FUNC_TEST(sends_room_join_with_nick_and_password), PROF_FUNC_TEST(sends_room_join_with_nick_and_password),
/* Room information display */ /* MUC room information display */
PROF_FUNC_TEST(shows_role_and_affiliation_on_join), PROF_FUNC_TEST(shows_role_and_affiliation_on_join),
PROF_FUNC_TEST(shows_subject_on_join), PROF_FUNC_TEST(shows_subject_on_join),
// PROF_FUNC_TEST(shows_history_message), // temporarily disabled due to timing issues in CI
PROF_FUNC_TEST(shows_occupant_join), PROF_FUNC_TEST(shows_occupant_join),
/* MUC messaging */ /* MUC messaging */
@@ -134,16 +174,11 @@ main(int argc, char* argv[])
PROF_FUNC_TEST(shows_me_message_from_occupant), PROF_FUNC_TEST(shows_me_message_from_occupant),
PROF_FUNC_TEST(shows_me_message_from_self), PROF_FUNC_TEST(shows_me_message_from_self),
/* Console notification settings for MUC */ /* MUC console notification settings */
PROF_FUNC_TEST(shows_all_messages_in_console_when_window_not_focussed), PROF_FUNC_TEST(shows_all_messages_in_console_when_window_not_focussed),
PROF_FUNC_TEST(shows_first_message_in_console_when_window_not_focussed), PROF_FUNC_TEST(shows_first_message_in_console_when_window_not_focussed),
PROF_FUNC_TEST(shows_no_message_in_console_when_window_not_focussed), PROF_FUNC_TEST(shows_no_message_in_console_when_window_not_focussed),
/* ============================================================
* GROUP 4: Carbons, Chat Session, Software, Disconnect
* Message synchronization and session management
* ============================================================ */
/* Message Carbons - XEP-0280 (message sync across devices) */ /* Message Carbons - XEP-0280 (message sync across devices) */
PROF_FUNC_TEST(send_enable_carbons), PROF_FUNC_TEST(send_enable_carbons),
PROF_FUNC_TEST(connect_with_carbons_enabled), PROF_FUNC_TEST(connect_with_carbons_enabled),
@@ -151,26 +186,31 @@ main(int argc, char* argv[])
PROF_FUNC_TEST(receive_carbon), PROF_FUNC_TEST(receive_carbon),
PROF_FUNC_TEST(receive_self_carbon), PROF_FUNC_TEST(receive_self_carbon),
PROF_FUNC_TEST(receive_private_carbon), PROF_FUNC_TEST(receive_private_carbon),
/* Chat session management - bare/full JID routing */
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_offline),
PROF_FUNC_TEST(sends_message_to_barejid_when_contact_online),
PROF_FUNC_TEST(sends_message_to_fulljid_when_received_from_fulljid),
PROF_FUNC_TEST(sends_subsequent_messages_to_fulljid),
PROF_FUNC_TEST(resets_to_barejid_after_presence_received),
PROF_FUNC_TEST(new_session_when_message_received_from_different_fulljid),
/* Software Version - XEP-0092 */
PROF_FUNC_TEST(send_software_version_request),
PROF_FUNC_TEST(display_software_version_result),
PROF_FUNC_TEST(shows_message_when_software_version_error),
PROF_FUNC_TEST(display_software_version_result_when_from_domainpart),
PROF_FUNC_TEST(show_message_in_chat_window_when_no_resource),
PROF_FUNC_TEST(display_software_version_result_in_chat),
/* Disconnect - clean session termination */
PROF_FUNC_TEST(disconnect_ends_session),
}; };
return cmocka_run_group_tests(all_tests, NULL, NULL); int result = 0;
switch (group) {
case 1:
result = cmocka_run_group_tests_name("Group 1: Connect/Ping/Rooms/Software", group1_tests, NULL, NULL);
break;
case 2:
result = cmocka_run_group_tests_name("Group 2: Message/Receipts/Roster/Session", group2_tests, NULL, NULL);
break;
case 3:
result = cmocka_run_group_tests_name("Group 3: Presence", group3_tests, NULL, NULL);
break;
case 4:
result = cmocka_run_group_tests_name("Group 4: MUC/Carbons", group4_tests, NULL, NULL);
break;
default:
/* Run all groups sequentially */
result |= cmocka_run_group_tests_name("Group 1: Connect/Ping/Rooms/Software", group1_tests, NULL, NULL);
result |= cmocka_run_group_tests_name("Group 2: Message/Receipts/Roster/Session", group2_tests, NULL, NULL);
result |= cmocka_run_group_tests_name("Group 3: Presence", group3_tests, NULL, NULL);
result |= cmocka_run_group_tests_name("Group 4: MUC/Carbons", group4_tests, NULL, NULL);
break;
}
return result;
} }

View File

@@ -24,6 +24,13 @@ int fd = 0;
int stub_port = 5230; int stub_port = 5230;
pid_t child_pid = 0; pid_t child_pid = 0;
/*
* Dynamic XDG paths based on stub_port for parallel test execution.
* Each test instance gets unique directories to avoid file conflicts.
*/
char xdg_config_home[256];
char xdg_data_home[256];
/* /*
* Buffer for accumulating output from profanity. * Buffer for accumulating output from profanity.
* 64KB is sufficient for typical test output while keeping memory usage * 64KB is sufficient for typical test output while keeping memory usage
@@ -77,7 +84,7 @@ _mkdir_recursive(const char *dir)
void void
_create_config_dir(void) _create_config_dir(void)
{ {
GString *profanity_dir = g_string_new(XDG_CONFIG_HOME); GString *profanity_dir = g_string_new(xdg_config_home);
g_string_append(profanity_dir, "/profanity"); g_string_append(profanity_dir, "/profanity");
if (!_mkdir_recursive(profanity_dir->str)) { if (!_mkdir_recursive(profanity_dir->str)) {
@@ -90,7 +97,7 @@ _create_config_dir(void)
void void
_create_data_dir(void) _create_data_dir(void)
{ {
GString *profanity_dir = g_string_new(XDG_DATA_HOME); GString *profanity_dir = g_string_new(xdg_data_home);
g_string_append(profanity_dir, "/profanity"); g_string_append(profanity_dir, "/profanity");
if (!_mkdir_recursive(profanity_dir->str)) { if (!_mkdir_recursive(profanity_dir->str)) {
@@ -103,7 +110,7 @@ _create_data_dir(void)
void void
_create_chatlogs_dir(void) _create_chatlogs_dir(void)
{ {
GString *chatlogs_dir = g_string_new(XDG_DATA_HOME); GString *chatlogs_dir = g_string_new(xdg_data_home);
g_string_append(chatlogs_dir, "/profanity/chatlogs"); g_string_append(chatlogs_dir, "/profanity/chatlogs");
if (!_mkdir_recursive(chatlogs_dir->str)) { if (!_mkdir_recursive(chatlogs_dir->str)) {
@@ -116,7 +123,7 @@ _create_chatlogs_dir(void)
void void
_create_logs_dir(void) _create_logs_dir(void)
{ {
GString *logs_dir = g_string_new(XDG_DATA_HOME); GString *logs_dir = g_string_new(xdg_data_home);
g_string_append(logs_dir, "/profanity/logs"); g_string_append(logs_dir, "/profanity/logs");
if (!_mkdir_recursive(logs_dir->str)) { if (!_mkdir_recursive(logs_dir->str)) {
@@ -129,7 +136,9 @@ _create_logs_dir(void)
void void
_cleanup_dirs(void) _cleanup_dirs(void)
{ {
int res = system("rm -rf ./tests/functionaltests/files"); char cmd[512];
snprintf(cmd, sizeof(cmd), "rm -rf ./tests/functionaltests/files/%d", stub_port);
int res = system(cmd);
if (res == -1) { if (res == -1) {
assert_true(FALSE); assert_true(FALSE);
} }
@@ -231,14 +240,20 @@ init_prof_test(void **state)
return -1; return -1;
} }
// Generate unique XDG paths based on stub_port for parallel execution
snprintf(xdg_config_home, sizeof(xdg_config_home),
"./tests/functionaltests/files/%d/xdg_config_home", stub_port);
snprintf(xdg_data_home, sizeof(xdg_data_home),
"./tests/functionaltests/files/%d/xdg_data_home", stub_port);
// Give stabber server thread time to start listening // Give stabber server thread time to start listening
usleep(100000); // 100ms usleep(100000); // 100ms
config_orig = getenv("XDG_CONFIG_HOME"); config_orig = getenv("XDG_CONFIG_HOME");
data_orig = getenv("XDG_DATA_HOME"); data_orig = getenv("XDG_DATA_HOME");
setenv("XDG_CONFIG_HOME", XDG_CONFIG_HOME, 1); setenv("XDG_CONFIG_HOME", xdg_config_home, 1);
setenv("XDG_DATA_HOME", XDG_DATA_HOME, 1); setenv("XDG_DATA_HOME", xdg_data_home, 1);
_cleanup_dirs(); _cleanup_dirs();

View File

@@ -1,8 +1,13 @@
#ifndef __H_PROFTEST #ifndef __H_PROFTEST
#define __H_PROFTEST #define __H_PROFTEST
#define XDG_CONFIG_HOME "./tests/functionaltests/files/xdg_config_home" /*
#define XDG_DATA_HOME "./tests/functionaltests/files/xdg_data_home" * XDG paths are now dynamic, generated per-test based on stub_port.
* This allows parallel test execution without file conflicts.
* Use xdg_config_home and xdg_data_home variables instead of macros.
*/
extern char xdg_config_home[256];
extern char xdg_data_home[256];
extern int stub_port; extern int stub_port;