From 92655a0a948ec084897e9443f6849f7c5bdd0147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Thu, 24 Sep 2026 09:44:42 -0400 Subject: [PATCH 1/5] Added Linux coverage and Win64 regression gates FileX's regression workflow ran only for master, measured four of nine Linux configurations, and did not run the Win64 simulator suite. Its floating ThreadX checkout and legacy installer could drift or conceal failures. The workflow now gates dev and master with pinned dependencies, all nine Linux coverage configurations, and a separate Win64 job; only a successful master run deploys Pages. Coverage requires at least 99.9% lines and 99.4% branches. The new partition test uses early exits for distinct failure codes (MISRA C:2012 Rule 15.5 exception) and a local declaration of an internal function to test it directly (Rule 8.5 exception). GCC 14.3.0 passed 137/137 tests in each Linux configuration. The merged report has 7,642/7,647 lines and 8,203/8,245 branches. A forced failing test returned status 8; empty or missing reports and a failed dependency fetch failed as expected. Installer retries stayed bounded. Actionlint, ShellCheck, and diff whitespace checks passed. Win64 awaits CI. Assisted-by: Codex (GPT-6) --- .github/dependabot.yml | 7 + .github/workflows/regression_test.yml | 89 ++++++- scripts/build.sh | 2 +- scripts/install.sh | 62 +++-- scripts/test.sh | 4 +- test/cmake/CMakeLists.txt | 13 +- test/cmake/coverage.sh | 139 ++++++++++- test/cmake/regression/CMakeLists.txt | 1 + test/cmake/run.sh | 33 ++- test/cmake/threadx-revision.txt | 1 + .../filex_directory_local_path_test.c | 32 ++- .../filex_partition_offset_extended_test.c | 224 ++++++++++++++++++ 12 files changed, 557 insertions(+), 50 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 test/cmake/threadx-revision.txt create mode 100644 test/regression_test/filex_partition_offset_extended_test.c diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..ff41be5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + target-branch: dev diff --git a/.github/workflows/regression_test.yml b/.github/workflows/regression_test.yml index 5aab97d..ec6ee07 100644 --- a/.github/workflows/regression_test.yml +++ b/.github/workflows/regression_test.yml @@ -1,25 +1,96 @@ -# 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: filex-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 + uses: eclipse-threadx/threadx/.github/workflows/regression_template.yml@b37cd4a81a1cb8c2ebefc438220ab7f009e13362 + with: + coverage_name: merged + coverage_thresholds: '99 100' + skip_deploy: true + + win64: + runs-on: windows-2022 + timeout-minutes: 90 + permissions: + contents: read + steps: + - name: Check out FileX + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Check out ThreadX + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: eclipse-threadx/threadx + ref: b37cd4a81a1cb8c2ebefc438220ab7f009e13362 + path: threadx + - name: Build Win64 configurations + timeout-minutes: 35 + shell: pwsh + run: | + Start-Transcript -Path win64-build.log + try { + ./scripts/build_fx.ps1 -Arch win64 -Configuration all -ThreadXDir "$env:GITHUB_WORKSPACE/threadx" -BuildTimeoutSeconds 300 + } finally { + Stop-Transcript + } + - name: Test Win64 configurations + timeout-minutes: 50 + shell: pwsh + run: | + Start-Transcript -Path win64-test.log + try { + ./scripts/test_fx.ps1 -Arch win64 -Configuration all -Parallel 1 -TestTimeoutSeconds 600 + } finally { + Stop-Transcript + } + - name: Upload failure diagnostics + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: win64-diagnostics + path: | + win64-build.log + win64-test.log + build/tests/win64/**/Testing/Temporary/* + build/tests/win64/**/CMakeFiles/CMakeConfigureLog.yaml + if-no-files-found: warn + + deploy: + if: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/master' }} + needs: [run_tests, win64] + runs-on: ubuntu-24.04 + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + permissions: pages: write id-token: write - uses: eclipse-threadx/threadx/.github/workflows/regression_template.yml@master + steps: + - name: Download merged coverage + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: coverage_report-* + merge-multiple: true + path: coverage_report + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + with: + path: coverage_report/merged + - name: Deploy coverage + id: deployment + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 diff --git a/scripts/build.sh b/scripts/build.sh index 736b73f..a7f75e3 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -10,4 +10,4 @@ # SPDX-License-Identifier: MIT ############################################################################## -$(dirname `realpath $0`)/../test/cmake/run.sh build all +"$(dirname "$(realpath "$0")")/../test/cmake/run.sh" build all diff --git a/scripts/install.sh b/scripts/install.sh index bcf4a0c..a5ddcb9 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -10,30 +10,48 @@ # SPDX-License-Identifier: MIT ############################################################################## -# +set -euo pipefail + +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}}/filex-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 \ No newline at end of file +"$venv_dir/bin/gcovr" --version | head -1 +"$cc" --version | head -1 +"$gcov" --version | head -1 diff --git a/scripts/test.sh b/scripts/test.sh index 49fe8a3..cfe112d 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -10,4 +10,6 @@ # SPDX-License-Identifier: MIT ############################################################################## -$(dirname `realpath $0`)/../test/cmake/run.sh test all +export CTEST_PARALLEL_LEVEL="${CTEST_PARALLEL_LEVEL:-1}" +export CTEST_REPEAT_FAIL="${CTEST_REPEAT_FAIL:-1}" +"$(dirname "$(realpath "$0")")/../test/cmake/run.sh" test all diff --git a/test/cmake/CMakeLists.txt b/test/cmake/CMakeLists.txt index d6ccd66..569b9b0 100644 --- a/test/cmake/CMakeLists.txt +++ b/test/cmake/CMakeLists.txt @@ -10,6 +10,8 @@ endif() project(filex_test LANGUAGES C) +option(FX_COVERAGE "Instrument FileX in every Linux test configuration" OFF) + set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) @@ -92,15 +94,19 @@ add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../.. filex) add_subdirectory(regression) add_subdirectory(samples) -# Coverage instrumentation (GCC/Clang only — not supported by MSVC) -if(NOT MSVC AND CMAKE_BUILD_TYPE MATCHES ".*_coverage") +# TX_COVERAGE is supplied by the ThreadX regression runner. +if(NOT MSVC AND (FX_COVERAGE OR TX_COVERAGE OR CMAKE_BUILD_TYPE MATCHES ".*_coverage")) target_compile_options(filex PRIVATE -fprofile-arcs -ftest-coverage) target_link_options(filex PRIVATE -fprofile-arcs -ftest-coverage) endif() # Build ThreadX shared library for Linux if(NOT MSVC AND NOT FX_STANDALONE_ENABLE) - execute_process(COMMAND ${CMAKE_CURRENT_LIST_DIR}/run.sh build_libs) + execute_process(COMMAND ${CMAKE_CURRENT_LIST_DIR}/run.sh build_libs + RESULT_VARIABLE threadx_build_result) + if(NOT threadx_build_result STREQUAL "0") + message(FATAL_ERROR "ThreadX dependency build failed: ${threadx_build_result}") + endif() add_custom_target(build_libs ALL COMMAND ${CMAKE_CURRENT_LIST_DIR}/run.sh build_libs) add_dependencies(filex build_libs) @@ -135,4 +141,3 @@ else() -Waggregate-return -Wfloat-equal) endif() - diff --git a/test/cmake/coverage.sh b/test/cmake/coverage.sh index ce81e77..5f22b29 100755 --- a/test/cmake/coverage.sh +++ b/test/cmake/coverage.sh @@ -10,10 +10,139 @@ # SPDX-License-Identifier: MIT ############################################################################## +set -euo pipefail -set -e +cd "$(dirname "$0")" +repo_root=$(cd ../.. && pwd) +report_dir=coverage_report +configurations=( + default_build_coverage no_cache_build no_cache_standalone_build + fault_tolerant_build_coverage no_check_build no_cache_fault_tolerant_build + standalone_build_coverage standalone_fault_tolerant_build_coverage + standalone_no_cache_fault_tolerant_build +) -cd $(dirname $0) -mkdir -p coverage_report/$1 -gcovr --object-directory=build/$1/filex/CMakeFiles/filex.dir/common/src -r ../../common/src -e ".*driver.*" --xml-pretty --output coverage_report/$1.xml -gcovr --object-directory=build/$1/filex/CMakeFiles/filex.dir/common/src -r ../../common/src -e ".*driver.*" --html --html-details --output coverage_report/$1/index.html +check_report() { + python3 - "$@" <<'PY' +import json +import pathlib +import sys +import xml.etree.ElementTree as ET + +report = pathlib.Path(sys.argv[1]) +root = ET.parse(report).getroot() +classes = root.findall('.//class') +if int(root.get('lines-valid', '0')) == 0 or not classes: + raise SystemExit(f'{report}: report contains no measured FileX files') +if any(not item.get('filename', '').startswith('common/src/') for item in classes): + raise SystemExit(f'{report}: report contains a source outside common/src') +if len(sys.argv) > 2: + with open(sys.argv[2], encoding='utf-8') as stream: + data = json.load(stream) + if not data.get('files'): + raise SystemExit(f'{sys.argv[2]}: tracefile contains no measured files') + if any(not item.get('file', '').startswith('common/src/') for item in data['files']): + raise SystemExit(f'{sys.argv[2]}: tracefile contains a source outside common/src') +print(f"{report}: lines {root.get('lines-covered')}/{root.get('lines-valid')} " + f"({float(root.get('line-rate', '0')) * 100:.2f}%), branches " + f"{root.get('branches-covered')}/{root.get('branches-valid')} " + f"({float(root.get('branch-rate', '0')) * 100:.2f}%)") +PY +} + +check_merged_floor() { + python3 - "$1" <<'PY' +import sys +import xml.etree.ElementTree as ET + +root = ET.parse(sys.argv[1]).getroot() +for label, attribute, minimum in ( + ('line', 'lines', 999), + ('branch', 'branches', 994), +): + covered = int(root.get(f'{attribute}-covered', '0')) + valid = int(root.get(f'{attribute}-valid', '0')) + if valid == 0 or covered * 1000 < valid * minimum: + raise SystemExit( + f'{sys.argv[1]}: {label} coverage {covered}/{valid} ' + f'is below {minimum / 10:.1f}%' + ) +PY +} + +if [ "${1:-}" = --clean ]; then + if [ -d "$report_dir" ]; then + rm -r -- "$report_dir" + fi + if [ -d build ]; then + find build -type f -name '*.gcda' -delete + fi + exit 0 +fi + +if [ "${1:-}" = --merge ]; then + trace_args=() + for configuration in "${configurations[@]}"; do + base="$report_dir/per_configuration/$configuration" + for path in "$base.json" "$base.xml" "$base/index.html"; do + if [ ! -s "$path" ]; then + echo "Missing or empty coverage report: $path" >&2 + exit 1 + fi + done + check_report "$base.xml" "$base.json" + trace_args+=(--add-tracefile "$base.json") + done + + mkdir -p "$report_dir/merged" + gcovr -r "$repo_root" "${trace_args[@]}" --xml-pretty \ + --output "$report_dir/merged.xml" + gcovr -r "$repo_root" "${trace_args[@]}" --html --html-details \ + --output "$report_dir/merged/index.html" + check_report "$report_dir/merged.xml" + check_merged_floor "$report_dir/merged.xml" + exit 0 +fi + +configuration="${1:-}" +valid=0 +for item in "${configurations[@]}"; do + if [ "$configuration" = "$item" ]; then + valid=1 + break + fi +done +if [ "$valid" -ne 1 ]; then + echo "Unknown coverage configuration: $configuration" >&2 + exit 1 +fi + +cc_name=$(basename "${CC:-gcc}") +if [ -n "${GCOV:-}" ]; then + gcov="$GCOV" +elif [[ "$cc_name" = gcc* ]]; then + gcov="gcov${cc_name#gcc}" +else + gcov=gcov +fi +if ! command -v "$gcov" >/dev/null 2>&1; then + echo "Coverage tool $gcov is unavailable." >&2 + exit 1 +fi + +objects="$PWD/build/$configuration/filex/CMakeFiles/filex.dir/common/src" +if [ ! -d "$objects" ] || [ -z "$(find "$objects" -name '*.gcda' -print -quit)" ]; then + echo "No FileX coverage data for $configuration." >&2 + exit 1 +fi + +base="$report_dir/per_configuration/$configuration" +mkdir -p "$base" +# Regression tests use a separate driver with fault injection. +gcovr --gcov-executable "$gcov" -r "$repo_root" -f "$repo_root/common/src" \ + -e "$repo_root/common/src/fx_ram_driver.c" \ + "$objects" --json "$base.json" --xml-pretty --output "$base.xml" +gcovr --gcov-executable "$gcov" -r "$repo_root" -f "$repo_root/common/src" \ + -e "$repo_root/common/src/fx_ram_driver.c" \ + "$objects" --html --html-details --output "$base/index.html" +check_report "$base.xml" "$base.json" diff --git a/test/cmake/regression/CMakeLists.txt b/test/cmake/regression/CMakeLists.txt index 613f601..4f58a67 100644 --- a/test/cmake/regression/CMakeLists.txt +++ b/test/cmake/regression/CMakeLists.txt @@ -141,6 +141,7 @@ set(regression_test_cases ${SOURCE_DIR}/filex_file_write_notify_test.c ${SOURCE_DIR}/filex_file_write_available_cluster_test.c ${SOURCE_DIR}/filex_utility_test.c + ${SOURCE_DIR}/filex_partition_offset_extended_test.c ${SOURCE_DIR}/filex_utility_fat_flush_test.c) add_library(test_utility ${SOURCE_DIR}/fx_ram_driver_test.c diff --git a/test/cmake/run.sh b/test/cmake/run.sh index 4d7950a..e32ab61 100755 --- a/test/cmake/run.sh +++ b/test/cmake/run.sh @@ -10,10 +10,33 @@ # SPDX-License-Identifier: MIT ############################################################################## +set -euo pipefail -cd $(dirname $0) +cd "$(dirname "$0")" +revision=$(cat threadx-revision.txt) -# if threadx repo does not exist, clone it -[ -d threadx ] || git clone https://github.com/eclipse-threadx/threadx.git --depth 1 -[ -f .run.sh ] || ln -sf threadx/scripts/cmake_bootstrap.sh .run.sh -./.run.sh $* \ No newline at end of file +if [ ! -e threadx ]; then + git init -q threadx + git -C threadx remote add origin https://github.com/eclipse-threadx/threadx.git + timeout 180 git -C threadx fetch --depth 1 origin "$revision" + git -C threadx checkout -q --detach FETCH_HEAD +fi + +actual_revision=$(git -C threadx rev-parse HEAD) +if [ "$actual_revision" != "$revision" ]; then + echo "ThreadX checkout does not match threadx-revision.txt." >&2 + exit 1 +fi + +bootstrap=threadx/scripts/cmake_bootstrap.sh +if [ ! -f "$bootstrap" ]; then + echo "ThreadX bootstrap script is missing." >&2 + exit 1 +fi + +if [ "${1:-}" = test ] && [ "${2:-}" = all ] && [ "${TX_COVERAGE:-OFF}" = ON ]; then + ./coverage.sh --clean +fi + +ln -sfn "$bootstrap" .run.sh +exec ./.run.sh "$@" diff --git a/test/cmake/threadx-revision.txt b/test/cmake/threadx-revision.txt new file mode 100644 index 0000000..b01234d --- /dev/null +++ b/test/cmake/threadx-revision.txt @@ -0,0 +1 @@ +b37cd4a81a1cb8c2ebefc438220ab7f009e13362 diff --git a/test/regression_test/filex_directory_local_path_test.c b/test/regression_test/filex_directory_local_path_test.c index 71ea3db..99ba41a 100644 --- a/test/regression_test/filex_directory_local_path_test.c +++ b/test/regression_test/filex_directory_local_path_test.c @@ -9,6 +9,8 @@ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ +/* Portions of this file were generated with AI assistance. */ + /* This FileX test concentrates on the local path operations. */ #ifndef FX_STANDALONE_ENABLE @@ -803,12 +805,36 @@ void filex_directory_local_path_application_define(void *first_unused_memory) #endif { +FX_MEDIA media = {0}; +FX_LOCAL_PATH local_path = {0}; +CHAR *path_name = FX_NULL; +UINT status; + FX_PARAMETER_NOT_USED(first_unused_memory); - /* Print out some test information banners. */ - printf("FileX Test: Directory local path test..............................N/A\n"); + /* Local paths are unavailable in standalone mode. */ + media.fx_media_id = FX_MEDIA_ID; + status = fx_directory_local_path_clear(&media); + if (status != FX_NOT_IMPLEMENTED) + { + test_control_return(1); + return; + } + status = fx_directory_local_path_get(&media, &path_name); + if (status != FX_NOT_IMPLEMENTED) + { + test_control_return(2); + return; + } + status = fx_directory_local_path_restore(&media, &local_path); + if (status != FX_NOT_IMPLEMENTED) + { + test_control_return(3); + return; + } - test_control_return(255); + printf("FileX Test: Directory local path test..............................SUCCESS\n"); + test_control_return(0); } #endif diff --git a/test/regression_test/filex_partition_offset_extended_test.c b/test/regression_test/filex_partition_offset_extended_test.c new file mode 100644 index 0000000..8087e75 --- /dev/null +++ b/test/regression_test/filex_partition_offset_extended_test.c @@ -0,0 +1,224 @@ +/*************************************************************************** + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Codex (GPT-6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include "fx_api.h" +#include + +#define TEST_SECTOR_SIZE 512u +#define TEST_PARTITION_OFFSET 446u +#define TEST_PARTITION_TYPE_OFFSET 4u +#define TEST_PARTITION_START_OFFSET 8u +#define TEST_PARTITION_SIZE_OFFSET 12u + +extern UINT _fx_partition_offset_calculate_extended(FX_MEDIA *media_ptr, void *partition_sector, + UINT partition, ULONG *partition_start, + ULONG *partition_size); +void test_control_return(UINT status); +void test_application_define(void *first_unused_memory); + +typedef enum TEST_DRIVER_MODE_ENUM +{ + TEST_DRIVER_SUCCESS, + TEST_DRIVER_READ_ERROR, + TEST_DRIVER_BAD_SIGNATURE, + TEST_DRIVER_EMPTY_PARTITION, + TEST_DRIVER_CHAIN +} TEST_DRIVER_MODE; + +static TEST_DRIVER_MODE test_driver_mode; + +/* Supply an extended boot record or a controlled read error. */ +static void test_partition_driver(FX_MEDIA *media_ptr) +{ +UCHAR *sector; + + if (test_driver_mode == TEST_DRIVER_READ_ERROR) + { + media_ptr->fx_media_driver_status = FX_IO_ERROR; + return; + } + + sector = (UCHAR *)media_ptr->fx_media_driver_buffer; + (void)memset(sector, 0, TEST_SECTOR_SIZE); + sector[510] = (UCHAR)0x55; + sector[511] = (UCHAR)0xAA; + if (test_driver_mode == TEST_DRIVER_BAD_SIGNATURE) + { + sector[510] = (UCHAR)0; + } + if (test_driver_mode != TEST_DRIVER_EMPTY_PARTITION) + { + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0x0B; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_START_OFFSET] = (UCHAR)3; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_SIZE_OFFSET] = (UCHAR)9; + } + if (test_driver_mode == TEST_DRIVER_CHAIN) + { + if (media_ptr->fx_media_driver_logical_sector == (ULONG)7) + { + sector[TEST_PARTITION_OFFSET + 16u + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0x05; + sector[TEST_PARTITION_OFFSET + 16u + TEST_PARTITION_START_OFFSET] = (UCHAR)20; + } + else + { + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_START_OFFSET] = (UCHAR)4; + } + } + media_ptr->fx_media_driver_status = FX_SUCCESS; +} + +/* Verify boot records, primary partitions, and extended partition reads. */ +void test_application_define(void *first_unused_memory) +{ +FX_MEDIA media = {0}; +UCHAR sector[TEST_SECTOR_SIZE] = {0}; +ULONG start = 0; +ULONG size = 0; +UINT status; + + FX_PARAMETER_NOT_USED(first_unused_memory); + + sector[0] = (UCHAR)0xEB; + sector[2] = (UCHAR)0x90; + sector[0x16] = (UCHAR)1; + sector[0x13] = (UCHAR)0x34; + sector[0x14] = (UCHAR)0x12; + status = _fx_partition_offset_calculate_extended(&media, sector, 0, &start, &size); + if ((status != FX_SUCCESS) || (start != (ULONG)0) || (size != (ULONG)0x1234)) + { + test_control_return((UINT)1); + return; + } + + sector[0x13] = (UCHAR)0; + sector[0x14] = (UCHAR)0; + sector[0x20] = (UCHAR)0x78; + sector[0x21] = (UCHAR)0x56; + status = _fx_partition_offset_calculate_extended(&media, sector, 0, FX_NULL, &size); + if ((status != FX_SUCCESS) || (size != (ULONG)0x5678)) + { + test_control_return((UINT)2); + return; + } + + (void)memset(sector, 0, sizeof(sector)); + status = _fx_partition_offset_calculate_extended(&media, sector, 0, &start, &size); + if (status != FX_NOT_FOUND) + { + test_control_return((UINT)12); + return; + } + sector[510] = (UCHAR)0x55; + sector[511] = (UCHAR)0xAA; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0x0B; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_START_OFFSET] = (UCHAR)7; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_SIZE_OFFSET] = (UCHAR)9; + status = _fx_partition_offset_calculate_extended(&media, sector, 0, &start, &size); + if ((status != FX_SUCCESS) || (start != (ULONG)7) || (size != (ULONG)9)) + { + test_control_return((UINT)3); + return; + } + status = _fx_partition_offset_calculate_extended(&media, sector, 0, FX_NULL, FX_NULL); + if (status != FX_SUCCESS) + { + test_control_return((UINT)4); + return; + } + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0; + status = _fx_partition_offset_calculate_extended(&media, sector, 0, &start, &size); + if (status != FX_NOT_FOUND) + { + test_control_return((UINT)13); + return; + } + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0x0B; + status = _fx_partition_offset_calculate_extended(&media, sector, 17, &start, &size); + if (status != FX_NOT_FOUND) + { + test_control_return((UINT)5); + return; + } + status = _fx_partition_offset_calculate_extended(&media, sector, 4, &start, &size); + if (status != FX_NOT_FOUND) + { + test_control_return((UINT)6); + return; + } + + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0x05; + media.fx_media_driver_entry = test_partition_driver; + test_driver_mode = TEST_DRIVER_SUCCESS; + status = _fx_partition_offset_calculate_extended(&media, sector, 5, &start, &size); + if (status != FX_NOT_FOUND) + { + test_control_return((UINT)14); + return; + } + (void)memset(sector, 0, sizeof(sector)); + sector[510] = (UCHAR)0x55; + sector[511] = (UCHAR)0xAA; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0x05; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_START_OFFSET] = (UCHAR)7; + status = _fx_partition_offset_calculate_extended(&media, sector, 4, &start, &size); + if ((status != FX_SUCCESS) || (start != (ULONG)10) || (size != (ULONG)9)) + { + test_control_return((UINT)7); + return; + } + + test_driver_mode = TEST_DRIVER_READ_ERROR; + (void)memset(sector, 0, sizeof(sector)); + sector[510] = (UCHAR)0x55; + sector[511] = (UCHAR)0xAA; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0x05; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_START_OFFSET] = (UCHAR)7; + status = _fx_partition_offset_calculate_extended(&media, sector, 4, &start, &size); + if (status != FX_IO_ERROR) + { + test_control_return((UINT)8); + return; + } + test_driver_mode = TEST_DRIVER_BAD_SIGNATURE; + status = _fx_partition_offset_calculate_extended(&media, sector, 4, &start, &size); + if (status != FX_NOT_FOUND) + { + test_control_return((UINT)9); + return; + } + test_driver_mode = TEST_DRIVER_EMPTY_PARTITION; + sector[510] = (UCHAR)0x55; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0x05; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_START_OFFSET] = (UCHAR)7; + status = _fx_partition_offset_calculate_extended(&media, sector, 4, &start, &size); + if (status != FX_NOT_FOUND) + { + test_control_return((UINT)10); + return; + } + + test_driver_mode = TEST_DRIVER_CHAIN; + sector[510] = (UCHAR)0x55; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_TYPE_OFFSET] = (UCHAR)0x05; + sector[TEST_PARTITION_OFFSET + TEST_PARTITION_START_OFFSET] = (UCHAR)7; + status = _fx_partition_offset_calculate_extended(&media, sector, 5, &start, &size); + if ((status != FX_SUCCESS) || (start != (ULONG)31) || (size != (ULONG)9)) + { + test_control_return((UINT)11); + return; + } + + test_control_return((UINT)0); +} From 919b8758509c27d17ec0d195ea14dcca048a8152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Thu, 24 Sep 2026 10:06:53 -0400 Subject: [PATCH 2/5] Allowed the reusable workflow to start with scoped test permissions GitHub rejected the regression workflow before starting any job because it validates permissions requested by every job in a called workflow, including a deployment job disabled for this caller. Grant the caller the Pages and OIDC permission ceiling required for validation. The called test job still limits its own token, and the called deployment job remains disabled; FileX deploys only after a successful master run. The PR run exposed the permission error before jobs were scheduled. Actionlint and diff whitespace checks passed; CI awaits this correction. Assisted-by: Codex (GPT-6) --- .github/workflows/regression_test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/regression_test.yml b/.github/workflows/regression_test.yml index ec6ee07..8deb0b6 100644 --- a/.github/workflows/regression_test.yml +++ b/.github/workflows/regression_test.yml @@ -18,6 +18,10 @@ jobs: issues: read checks: write pull-requests: write + # GitHub validates the called workflow's skipped deploy job permissions. + # Its test job narrows its own token permissions. + pages: write + id-token: write uses: eclipse-threadx/threadx/.github/workflows/regression_template.yml@b37cd4a81a1cb8c2ebefc438220ab7f009e13362 with: coverage_name: merged From fe73ad562e0b07be0f106fc2909ac981ef82917e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Thu, 24 Sep 2026 10:20:24 -0400 Subject: [PATCH 3/5] Matched Win64 test buffer declarations to their allocations The Win64 regression build failed because the test RAM driver's header declared four buffers as arrays while its MSVC implementation allocates pointers. Declare pointer types for MSVC and retain the array types used by other compilers. The declarations now match the storage in each configuration. The GitHub Win64 build exposed the four conflicting declarations. Header type checks passed in normal, standalone, MSVC, and MSVC standalone modes; all nine GCC 14 Linux configurations rebuilt successfully. Win64 CI awaits this fix. Assisted-by: Codex (GPT-6) --- test/regression_test/fx_ram_driver_test.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/regression_test/fx_ram_driver_test.h b/test/regression_test/fx_ram_driver_test.h index df9e610..2d5e5ef 100644 --- a/test/regression_test/fx_ram_driver_test.h +++ b/test/regression_test/fx_ram_driver_test.h @@ -24,15 +24,25 @@ /* Define the memory buffer to format the media if enable fault tolerant feature.*/ +#ifdef _MSC_VER +extern UCHAR *ram_disk_memory_large; +extern UCHAR *large_data_buffer; +#else extern UCHAR ram_disk_memory_large[900000000]; extern UCHAR large_data_buffer[900000000]; +#endif #define large_data_buffer_size 900000000 /* Define memory for tests to be run in standalone mode (without Azure RTOS: ThreadX) */ #ifdef FX_STANDALONE_ENABLE +#ifdef _MSC_VER +extern UCHAR *ram_disk_memory; +extern UCHAR *ram_disk_memory1; +#else extern UCHAR ram_disk_memory[300000000]; extern UCHAR ram_disk_memory1[30000000]; #endif +#endif /* Define a macro for test. */ /* We do not need error code anymore but still define return_value_if_fail for compatibility backward. */ From f35691356b5256cd641550e8d7ba03ef4e2b3952 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Thu, 24 Sep 2026 10:32:52 -0400 Subject: [PATCH 4/5] Removed zero-length Win64 test buffers MSVC stopped the standalone regression build because two tests declared fault-tolerant buffers with a size of zero when that feature was disabled. Declare those buffers only for configurations that enable fault tolerance. Their uses are already limited to those configurations. The GitHub Win64 build identified both compile errors. All nine GCC 14 Linux configurations rebuilt, and both affected tests passed in every configuration (18/18 targeted tests). Win64 CI awaits this fix. Assisted-by: Codex (GPT-6) --- test/regression_test/filex_directory_create_delete_test.c | 5 ++++- .../regression_test/filex_directory_duplicate_entries_test.c | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/test/regression_test/filex_directory_create_delete_test.c b/test/regression_test/filex_directory_create_delete_test.c index d092469..b339431 100644 --- a/test/regression_test/filex_directory_create_delete_test.c +++ b/test/regression_test/filex_directory_create_delete_test.c @@ -9,6 +9,8 @@ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ +/* Portions of this file were generated with AI assistance. */ + /* This FileX test concentrates on the basic directory create/delete operations. */ #ifndef FX_STANDALONE_ENABLE @@ -111,8 +113,10 @@ static UCHAR *cache_buffer; static UCHAR *fault_tolerant_buffer; #else static UCHAR cache_buffer[CACHE_SIZE]; +#ifdef FX_ENABLE_FAULT_TOLERANT static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; #endif +#endif extern ULONG _fx_ram_driver_copy_default_format; @@ -3064,4 +3068,3 @@ FX_DIR_ENTRY search_directory; test_control_return(0); } } - diff --git a/test/regression_test/filex_directory_duplicate_entries_test.c b/test/regression_test/filex_directory_duplicate_entries_test.c index 5266d66..145648b 100644 --- a/test/regression_test/filex_directory_duplicate_entries_test.c +++ b/test/regression_test/filex_directory_duplicate_entries_test.c @@ -9,6 +9,8 @@ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ +/* Portions of this file were generated with AI assistance. */ + /* This FileX test concentrates on recovery operation when there are duplicate entries. */ #ifndef FX_STANDALONE_ENABLE @@ -49,8 +51,10 @@ static UCHAR *cache_buffer; static UCHAR *fault_tolerant_buffer; #else static UCHAR cache_buffer[CACHE_SIZE]; +#ifdef FX_ENABLE_FAULT_TOLERANT static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; #endif +#endif extern ULONG _fx_ram_driver_copy_default_format; From 79c703d917e648c615ae6e0c253e63a73bb134a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Thu, 24 Sep 2026 10:42:21 -0400 Subject: [PATCH 5/5] Removed remaining zero-length Win64 test buffers MSVC stopped at another standalone test that declared a fault-tolerant buffer with a size of zero. A scan found three remaining declarations with this shape. Declare those buffers only when fault tolerance is enabled, matching the code that uses them. All nine GCC 14 Linux configurations rebuilt, and the three affected tests passed in every configuration (27/27 targeted tests). Win64 CI awaits this fix. Assisted-by: Codex (GPT-6) --- test/regression_test/filex_directory_rename_test.c | 4 ++++ test/regression_test/filex_file_create_delete_test.c | 4 ++++ test/regression_test/filex_file_rename_test.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/test/regression_test/filex_directory_rename_test.c b/test/regression_test/filex_directory_rename_test.c index 36fd181..cf69238 100644 --- a/test/regression_test/filex_directory_rename_test.c +++ b/test/regression_test/filex_directory_rename_test.c @@ -9,6 +9,8 @@ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ +/* Portions of this file were generated with AI assistance. */ + /* This FileX test concentrates on the directory rename operations. */ #ifndef FX_STANDALONE_ENABLE @@ -47,8 +49,10 @@ static UCHAR *cache_buffer; static UCHAR *fault_tolerant_buffer; #else static UCHAR cache_buffer[CACHE_SIZE]; +#ifdef FX_ENABLE_FAULT_TOLERANT static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; #endif +#endif /* Define thread prototypes. */ diff --git a/test/regression_test/filex_file_create_delete_test.c b/test/regression_test/filex_file_create_delete_test.c index d296ba4..19b6161 100644 --- a/test/regression_test/filex_file_create_delete_test.c +++ b/test/regression_test/filex_file_create_delete_test.c @@ -9,6 +9,8 @@ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ +/* Portions of this file were generated with AI assistance. */ + /* This FileX test concentrates on the file create/delete operations. */ #ifndef FX_STANDALONE_ENABLE @@ -54,8 +56,10 @@ static UCHAR *cache_buffer; static UCHAR *fault_tolerant_buffer; #else static UCHAR cache_buffer[CACHE_SIZE]; +#ifdef FX_ENABLE_FAULT_TOLERANT static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; #endif +#endif static UCHAR fat_buffer[128]; static UCHAR name_buffer[FX_MAX_LONG_NAME_LEN+1]; diff --git a/test/regression_test/filex_file_rename_test.c b/test/regression_test/filex_file_rename_test.c index c6c104e..ffa0fc3 100644 --- a/test/regression_test/filex_file_rename_test.c +++ b/test/regression_test/filex_file_rename_test.c @@ -9,6 +9,8 @@ /* SPDX-License-Identifier: MIT */ /***************************************************************************/ +/* Portions of this file were generated with AI assistance. */ + /* This FileX test concentrates on the file rename operations. */ #ifndef FX_STANDALONE_ENABLE @@ -46,8 +48,10 @@ static UCHAR *cache_buffer; static UCHAR *fault_tolerant_buffer; #else static UCHAR cache_buffer[CACHE_SIZE]; +#ifdef FX_ENABLE_FAULT_TOLERANT static UCHAR fault_tolerant_buffer[FAULT_TOLERANT_SIZE]; #endif +#endif /* Define thread prototypes. */