Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
target-branch: dev
22 changes: 13 additions & 9 deletions .github/workflows/regression_test.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
# This is a basic workflow that is manually triggered

name: regression_test

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
workflow_dispatch:
push:
branches: [ master ]
branches: [dev, master]
pull_request:
branches: [ master ]
branches: [dev, master]

concurrency:
group: levelx-regression-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "linux_job"
run_tests:
permissions:
contents: read
issues: read
checks: write
pull-requests: write
# GitHub validates deployment permissions even when deployment is skipped.
# The reusable test job scopes its token separately.
pages: write
id-token: write
uses: eclipse-threadx/threadx/.github/workflows/regression_template.yml@master
uses: eclipse-threadx/threadx/.github/workflows/regression_template.yml@b37cd4a81a1cb8c2ebefc438220ab7f009e13362
with:
coverage_name: merged
coverage_thresholds: '68 100'
skip_deploy: ${{ github.ref != 'refs/heads/master' || github.event_name == 'pull_request' }}
4 changes: 3 additions & 1 deletion scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@
# SPDX-License-Identifier: MIT
##############################################################################

$(dirname `realpath $0`)/../test/cmake/run.sh build all
set -euo pipefail

exec "$(dirname "$(realpath "$0")")/../test/cmake/run.sh" build all
63 changes: 41 additions & 22 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,49 @@
# SPDX-License-Identifier: MIT
##############################################################################

#
set -euo pipefail

# Retry transient package and network failures a bounded number of times.
retry() {
local attempt
for attempt in 1 2 3; do
if "$@"; then
return 0
fi
if [ "$attempt" -lt 3 ]; then
sleep $((attempt * 5))
fi
done
return 1
}

# Remove large folder
rm -rf /opt/hostedtoolcache
apt_options=(-o Acquire::Retries=3 -o DPkg::Lock::Timeout=60)
if ! retry sudo timeout 150 apt-get "${apt_options[@]}" update; then
echo "Package index update failed; package installation will verify availability." >&2
fi
retry sudo timeout 150 apt-get "${apt_options[@]}" install -y \
cmake gcc-14 gcc-14-multilib git ninja-build python3-venv \
unifdef p7zip-full tofrodos gawk

# Install necessary softwares for Ubuntu.
venv_dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/levelx-ci-venv"
python3 -m venv "$venv_dir"
retry timeout 120 "$venv_dir/bin/python" -m pip install \
--retries 3 --timeout 30 gcovr==8.6

sudo apt-get update
sudo apt-get install -y \
gcc-multilib \
git \
g++ \
python3-pip \
ninja-build \
unifdef \
p7zip-full \
tofrodos \
gawk \
software-properties-common
cc="${CC:-gcc-14}"
gcov="${GCOV:-gcov-14}"
cc_version=$("$cc" -dumpfullversion)
gcov_version=$("$gcov" --version | sed -n '1{s/.* \([0-9][0-9]*\.[0-9][0-9]*\(\.[0-9][0-9]*\)\?\).*/\1/p;}')
if [ -z "$gcov_version" ] || [ "$cc_version" != "$gcov_version" ]; then
echo "Compiler $cc and coverage tool $gcov have different versions." >&2
exit 1
fi

wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add -
CODENAME=$(lsb_release -c | cut -f2 -d':' | sed 's/\t//')
apt-add-repository "deb https://apt.kitware.com/ubuntu/ $CODENAME main"
if [ -n "${GITHUB_ENV:-}" ]; then
printf 'CC=%s\nGCOV=%s\n' "$cc" "$gcov" >> "$GITHUB_ENV"
printf '%s\n' "$venv_dir/bin" >> "$GITHUB_PATH"
fi

python3 -m pip install --upgrade pip
pip3 install gcovr==4.1
pip install --upgrade cmake
"$venv_dir/bin/gcovr" --version | head -1
"$cc" --version | head -1
"$gcov" --version | head -1
8 changes: 7 additions & 1 deletion scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@
# SPDX-License-Identifier: MIT
##############################################################################

$(dirname `realpath $0`)/../test/cmake/run.sh test all
set -euo pipefail

test_dir="$(dirname "$(realpath "$0")")/../test/cmake"
"$test_dir/run.sh" test all
if [ "${TX_COVERAGE:-OFF}" = ON ]; then
"$test_dir/check_coverage.sh"
fi
9 changes: 7 additions & 2 deletions test/cmake/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,19 @@ add_subdirectory(regression)
add_subdirectory(samples)

# Coverage
if(CMAKE_BUILD_TYPE MATCHES ".*_coverage")
option(TX_COVERAGE "Instrument every regression configuration" OFF)
if(TX_COVERAGE OR CMAKE_BUILD_TYPE MATCHES ".*_coverage")
target_compile_options(levelx PRIVATE -fprofile-arcs -ftest-coverage)
target_link_options(levelx PRIVATE -fprofile-arcs -ftest-coverage)
endif()


# Build ThreadX library once
execute_process(COMMAND ${CMAKE_CURRENT_LIST_DIR}/run.sh build_libs)
execute_process(COMMAND ${CMAKE_CURRENT_LIST_DIR}/run.sh build_libs
RESULT_VARIABLE dependency_status)
if(NOT dependency_status EQUAL 0)
message(FATAL_ERROR "Dependency build failed: ${dependency_status}")
endif()
add_custom_target(build_libs ALL COMMAND ${CMAKE_CURRENT_LIST_DIR}/run.sh
build_libs)
add_dependencies(levelx build_libs)
Expand Down
62 changes: 62 additions & 0 deletions test/cmake/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Linux regression tests

Use GCC 14, matching gcov 14, CMake, Ninja and gcovr 8.6. The CI installer
supports Ubuntu 24.04. From the repository root:

```sh
export CC=gcc-14 GCOV=gcov-14 TX_COVERAGE=ON
./scripts/build.sh
./scripts/test.sh
```

The runner verifies the dependency commits recorded in `threadx-revision.txt`
and `filex-revision.txt`. The FileX revision is the tested head of
[eclipse-threadx/filex#106](https://github.com/eclipse-threadx/filex/pull/106),
which is still open. Updating a dependency requires changing its pin and
rerunning all ten configurations. An existing checkout with another revision
or modified tracked files is rejected.

All configurations need the FileX source tree. Non-standalone configurations
link the shared ThreadX and FileX libraries; standalone configurations build
FileX with standalone support. LevelX has no Windows regression port.

`TX_COVERAGE=ON` instruments every LevelX configuration. A complete test run
clears old coverage first, runs every configuration, and collects coverage even
when a test fails. Reports under `coverage_report/per_configuration` contain
JSON, XML and HTML for each configuration. The `merged` reports combine all ten
JSON inputs and use repository-relative source names. Missing, empty or
unmeasured inputs fail collection. Both line and branch floors are enforced by
`coverage.sh`; the workflow also enforces its integer line floor.

After a successful full run, `./test/cmake/check_coverage.sh` verifies rejection
of missing, empty and unmeasured coverage inputs, then restores and remerges the
valid reports.

The reusable workflow pin supplies bounded install, build and test steps and
retains test and coverage artifacts on failure. Only master push or manual
runs can deploy coverage. Dependabot updates target dev; GitHub activates this
configuration once it is present on the default branch.

## Coverage target

The measured GCC 14 union is 1,632/2,400 lines (68.00%) and 1,596/2,439
branches (65.44%). The enforced floors are 68.0% lines and 65.4% branches.
The 100% target still needs 768 lines and 843 branches covered. The largest
line gaps are the FileX simulator adapters (164), NOR block reclaim (69), NOR
extended open (65), and NAND block data movement (38). Other gaps include
media-error paths, metadata allocation, sector release and simulator failures.
These sources remain in the denominator.

The ECC regression checks every single-bit position in a 512-byte page,
corrections in both halves, and uncorrectable errors. All four ECC helper files
have full line coverage. The full, driver-interface and combined NOR cache
configurations contribute 97, 51 and 65 source lines absent from the default
configuration respectively. Standalone configurations select a subset of the
same source lines; their reports still participate in the union. Function
merging uses the earliest declaration line because driver-interface macros
place otherwise identical function declarations on different lines.

The ECC test views aligned `USHORT` storage through a character pointer because
the ECC implementation accesses words. This is a deviation from advisory
MISRA C:2004 Rule 11.4; character access preserves alignment and is explicitly
permitted by the character-pointer exception in MISRA C:2012/2023 Rule 11.3.
40 changes: 40 additions & 0 deletions test/cmake/check_coverage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash
# Copyright (c) 2026 Eclipse ThreadX contributors
# SPDX-License-Identifier: MIT

set -euo pipefail
cd "$(dirname "$0")"

# Verify rejection of damaged inputs using a completed coverage run.
base=coverage_report/per_configuration/default_build_coverage
backup=$(mktemp -d)
cp "$base.json" "$backup/input.json"
cp "$base.xml" "$backup/input.xml"
# Restore the original inputs on success or failure.
restore() {
cp "$backup/input.json" "$base.json"
cp "$backup/input.xml" "$base.xml"
rm -r "$backup"
}
trap restore EXIT

# Require the collector to reject each damaged input.
expect_failure() {
if ./coverage.sh --merge > "$backup/output.log" 2>&1; then
echo "Coverage accepted $1." >&2
exit 1
fi
echo "Rejected $1."
}

rm "$base.json"
expect_failure 'a missing JSON input'
: > "$base.json"
expect_failure 'an empty JSON input'
printf '{"files":[]}\n' > "$base.json"
expect_failure 'an unmeasured JSON input'
cp "$backup/input.json" "$base.json"
printf '<coverage lines-valid="0"/>\n' > "$base.xml"
expect_failure 'an unmeasured XML input'
cp "$backup/input.xml" "$base.xml"
./coverage.sh --merge
Loading