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..8deb0b6 100644 --- a/.github/workflows/regression_test.yml +++ b/.github/workflows/regression_test.yml @@ -1,25 +1,100 @@ -# 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 + # 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 + 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_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; 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_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. */ 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); +} 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. */