]> OCCT Git - occt.git/commitdiff
Testing - Reorganize GitHub actions by actions #480
authorPasukhin Dmitry <dpasukhi@opencascade.com>
Sun, 6 Apr 2025 14:14:23 +0000 (15:14 +0100)
committerGitHub <noreply@github.com>
Sun, 6 Apr 2025 14:14:23 +0000 (15:14 +0100)
Refactor test workflow to be based on reusable actions.

.github/actions/build-occt/action.yml [new file with mode: 0644]
.github/actions/retest-failures/action.yml [new file with mode: 0644]
.github/actions/run-tests/action.yml [new file with mode: 0644]
.github/actions/test-summary/action.yml [new file with mode: 0644]
.github/workflows/build-and-test-multiplatform.yml

diff --git a/.github/actions/build-occt/action.yml b/.github/actions/build-occt/action.yml
new file mode 100644 (file)
index 0000000..2f30680
--- /dev/null
@@ -0,0 +1,221 @@
+name: 'Build OCCT'
+description: 'Prepare and build OCCT on a specific platform'
+
+inputs:
+  platform:
+    description: 'Platform (windows, macos, linux)'
+    required: true
+  compiler:
+    description: 'Compiler (msvc, clang, gcc)'
+    required: true
+  artifact-name:
+    description: 'Name of the artifact to store build results'
+    required: true
+  additional-cmake-flags:
+    description: 'Additional CMake flags'
+    required: false
+    default: ''
+  use-vtk:
+    description: 'Enable VTK'
+    required: false
+    default: 'true'
+
+runs:
+  using: "composite"
+  steps:
+    - name: Download and extract 3rdparty dependencies (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+          Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/3rdparty-vc14-64.zip -OutFile 3rdparty-vc14-64.zip
+          Expand-Archive -Path 3rdparty-vc14-64.zip -DestinationPath .
+          Remove-Item 3rdparty-vc14-64.zip
+      shell: pwsh
+
+    - name: Download and extract Mesa3D (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+        curl -L -o mesa3d.7z https://github.com/pal1000/mesa-dist-win/releases/download/24.3.2/mesa3d-24.3.2-release-mingw.7z
+        7z x mesa3d.7z -omesa3d
+      shell: pwsh
+
+    - name: Run system-wide deployment (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+        cd mesa3d
+        .\systemwidedeploy.cmd 1
+        .\systemwidedeploy.cmd 5
+      shell: cmd
+
+    - name: Install Ninja (Windows Clang)
+      if: ${{ inputs.platform == 'windows' && inputs.compiler == 'clang' }}
+      run: |
+        choco install ninja -y
+        ninja --version
+      shell: pwsh
+
+    - name: Install dependencies (macOS)
+      if: ${{ inputs.platform == 'macos' }}
+      run: |
+        brew update
+        brew install tcl-tk tbb gl2ps xerces-c \
+                     libxmu libxi libxft libxpm \
+                     glew freeimage draco glfw
+      shell: bash
+
+    - name: Install dependencies (Linux)
+      if: ${{ inputs.platform == 'linux' }}
+      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake ${{ inputs.compiler == 'clang' && 'clang' || 'gcc g++' }} make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev
+      shell: bash
+
+    - name: Install rapidjson (macOS/Linux)
+      if: ${{ inputs.platform == 'macos' || inputs.platform == 'linux' }}
+      run: |
+        wget https://github.com/Tencent/rapidjson/archive/858451e5b7d1c56cf8f6d58f88cf958351837e53.zip -O rapidjson.zip
+        unzip rapidjson.zip
+      shell: bash
+
+    - name: Configure OCCT (Windows MSVC)
+      if: ${{ inputs.platform == 'windows' && inputs.compiler == 'msvc' }}
+      run: |
+          mkdir build
+          cd build
+          cmake -T host=x64 `
+                -D USE_FREETYPE=ON `
+                -D USE_TK=OFF `
+                -D BUILD_USE_PCH=ON `
+                -D BUILD_OPT_PROFILE=Production `
+                -D BUILD_INCLUDE_SYMLINK=ON `
+                -D CMAKE_BUILD_TYPE=Release `
+                -D 3RDPARTY_DIR=${{ github.workspace }}/3rdparty-vc14-64 `
+                -D INSTALL_DIR=${{ github.workspace }}/install `
+                -D USE_D3D=ON `
+                -D USE_DRACO=ON `
+                -D USE_FFMPEG=ON `
+                -D USE_FREEIMAGE=ON `
+                -D USE_GLES2=ON `
+                -D USE_OPENVR=ON `
+                -D USE_VTK=${{ inputs.use-vtk }} `
+                -D USE_TBB=ON `
+                -D USE_RAPIDJSON=ON `
+                -D USE_OPENGL=ON `
+                -D BUILD_GTEST=ON `
+                -D BUILD_CPP_STANDARD=C++14 `
+                -D INSTALL_GTEST=ON ${{ inputs.additional-cmake-flags }} ..
+      shell: pwsh
+
+    - name: Configure OCCT (Windows Clang)
+      if: ${{ inputs.platform == 'windows' && inputs.compiler == 'clang' }}
+      run: |
+          mkdir build
+          cd build
+          cmake -G "Ninja" `
+                -D CMAKE_C_COMPILER=clang `
+                -D CMAKE_CXX_COMPILER=clang++ `
+                -D USE_FREETYPE=ON `
+                -D USE_TK=OFF `
+                -D BUILD_USE_PCH=ON `
+                -D BUILD_OPT_PROFILE=Production `
+                -D BUILD_INCLUDE_SYMLINK=ON `
+                -D CMAKE_BUILD_TYPE=Release `
+                -D 3RDPARTY_DIR=${{ github.workspace }}/3rdparty-vc14-64 `
+                -D INSTALL_DIR=${{ github.workspace }}/install `
+                -D USE_D3D=ON `
+                -D USE_DRACO=ON `
+                -D USE_FFMPEG=ON `
+                -D USE_FREEIMAGE=ON `
+                -D USE_GLES2=ON `
+                -D USE_OPENVR=ON `
+                -D USE_VTK=${{ inputs.use-vtk }} `
+                -D USE_TBB=ON `
+                -D USE_RAPIDJSON=ON `
+                -D USE_OPENGL=ON `
+                -D BUILD_GTEST=ON `
+                -D BUILD_CPP_STANDARD=C++14 `
+                -D INSTALL_GTEST=ON `
+                -D CMAKE_CXX_FLAGS="-Werror -Wall -Wextra -Wno-unknown-warning-option" `
+                -D CMAKE_C_FLAGS="-Werror -Wall -Wextra -Wno-unknown-warning-option" ${{ inputs.additional-cmake-flags }} ..
+      shell: pwsh
+
+    - name: Configure OCCT (macOS)
+      if: ${{ inputs.platform == 'macos' }}
+      run: |
+        mkdir -p build
+        cd build
+        cmake -G "Unix Makefiles" \
+              -D CMAKE_C_COMPILER=${{ inputs.compiler == 'clang' && 'clang' || 'gcc' }} \
+              -D CMAKE_CXX_COMPILER=${{ inputs.compiler == 'clang' && 'clang++' || 'g++' }} \
+              -D BUILD_USE_PCH=ON \
+              -D BUILD_INCLUDE_SYMLINK=ON \
+              -D CMAKE_BUILD_TYPE=Release \
+              -D INSTALL_DIR=${{ github.workspace }}/install \
+              -D 3RDPARTY_RAPIDJSON_DIR=${{ github.workspace }}/rapidjson-858451e5b7d1c56cf8f6d58f88cf958351837e53 \
+              -D USE_RAPIDJSON=ON \
+              -D USE_DRACO=ON \
+              -D USE_FREETYPE=ON \
+              -D USE_OPENGL=ON \
+              -D USE_FREEIMAGE=ON \
+              -D BUILD_GTEST=ON \
+              -D BUILD_CPP_STANDARD=C++14 \
+              -D INSTALL_GTEST=ON \
+              -D CMAKE_CXX_FLAGS="-Werror -Wall -Wextra" \
+              -D CMAKE_C_FLAGS="-Werror -Wall -Wextra" ${{ inputs.additional-cmake-flags }} ..
+      shell: bash
+
+    - name: Configure OCCT (Linux)
+      if: ${{ inputs.platform == 'linux' }}
+      run: |
+        mkdir -p build
+        cd build
+        cmake -G "Unix Makefiles" \
+              -D CMAKE_C_COMPILER=${{ inputs.compiler == 'clang' && 'clang' || 'gcc' }} \
+              -D CMAKE_CXX_COMPILER=${{ inputs.compiler == 'clang' && 'clang++' || 'g++' }} \
+              -D BUILD_USE_PCH=ON \
+              -D BUILD_INCLUDE_SYMLINK=ON \
+              -D BUILD_OPT_PROFILE=Production \
+              -D USE_TK=OFF \
+              -D CMAKE_BUILD_TYPE=Release \
+              -D INSTALL_DIR=${{ github.workspace }}/install \
+              -D 3RDPARTY_RAPIDJSON_DIR=${{ github.workspace }}/rapidjson-858451e5b7d1c56cf8f6d58f88cf958351837e53 \
+              -D USE_FREETYPE=ON \
+              -D USE_DRACO=ON \
+              -D USE_FFMPEG=OFF \
+              -D USE_FREEIMAGE=ON \
+              -D USE_GLES2=ON \
+              -D USE_OPENVR=ON \
+              -D USE_VTK=${{ inputs.use-vtk }} \
+              -D USE_TBB=OFF \
+              -D USE_RAPIDJSON=ON \
+              -D USE_OPENGL=ON \
+              -D BUILD_GTEST=ON \
+              -D BUILD_CPP_STANDARD=C++14 \
+              -D INSTALL_GTEST=ON \
+              ${{ inputs.compiler == 'clang' && '-D CMAKE_CXX_FLAGS="-Werror -Wall -Wextra" -D CMAKE_C_FLAGS="-Werror -Wall -Wextra"' || '' }} ${{ inputs.additional-cmake-flags }} ..
+      shell: bash
+
+    - name: Build OCCT (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+          cd build
+          cmake --build . --target install --config Release
+      shell: pwsh
+
+    - name: Build OCCT (macOS)
+      if: ${{ inputs.platform == 'macos' }}
+      run: |
+        cd build
+        make install -j$(sysctl -n hw.logicalcpu)
+      shell: bash
+
+    - name: Build OCCT (Linux)
+      if: ${{ inputs.platform == 'linux' }}
+      run: |
+        cd build
+        cmake --build . --target install --config Release -- -j
+      shell: bash
+
+    - name: Upload install directory
+      uses: actions/upload-artifact@v4.4.3
+      with:
+        name: ${{ inputs.artifact-name }}
+        path: install
+        retention-days: 7
diff --git a/.github/actions/retest-failures/action.yml b/.github/actions/retest-failures/action.yml
new file mode 100644 (file)
index 0000000..8a710f8
--- /dev/null
@@ -0,0 +1,311 @@
+name: 'Retest Failures'
+description: 'Rerun failed tests and update test results'
+
+inputs:
+  platform:
+    description: 'Platform (windows, macos, linux)'
+    required: true
+  compiler:
+    description: 'Compiler (msvc, clang, gcc)'
+    required: true
+  install-artifact-name:
+    description: 'Name of the artifact containing the install directory'
+    required: true
+  results-artifact-name:
+    description: 'Name of the artifact containing the test results'
+    required: true
+  test-directory-name:
+    description: 'Name of the directory containing test results'
+    required: true
+
+runs:
+  using: "composite"
+  steps:
+    - name: Download previous test results (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      uses: actions/download-artifact@v4.1.7
+      with:
+        name: ${{ inputs.results-artifact-name }}
+        path: install/results
+      
+    - name: Download previous test results (macOS/Linux)
+      if: ${{ inputs.platform != 'windows' }}
+      uses: actions/download-artifact@v4.1.7
+      with:
+        name: ${{ inputs.results-artifact-name }}
+        path: install/bin/results
+
+    - name: Check for test failures (Windows)
+      id: check_failures_windows
+      if: ${{ inputs.platform == 'windows' }}
+      shell: pwsh
+      run: |
+        $failedCount = 0
+        if (Test-Path "install/results/${{ inputs.test-directory-name }}/tests.log") {
+          $content = Get-Content "install/results/${{ inputs.test-directory-name }}/tests.log"
+          $totalLine = $content | Select-String "Total cases:"
+          if ($totalLine) {
+            if ($totalLine -match "FAILED") {
+              $failedCount = ($totalLine | ForEach-Object { $_.Line -replace '.*?(\d+) FAILED.*','$1' }) -as [int]
+            }
+          }
+          echo "failed_count=$failedCount" >> $env:GITHUB_OUTPUT
+          if ($failedCount -gt 0) {
+            echo "Tests failed count: $failedCount"
+          }
+        }
+
+    - name: Check for test failures (macOS/Linux)
+      id: check_failures_unix
+      if: ${{ inputs.platform != 'windows' }}
+      shell: bash
+      run: |
+        failed_count=0
+        if [ -f "install/bin/results/${{ inputs.test-directory-name }}/tests.log" ]; then
+          total_line=$(grep "Total cases:" install/bin/results/${{ inputs.test-directory-name }}/tests.log)
+          if [ ! -z "$total_line" ]; then
+            if [[ $total_line =~ "FAILED" ]]; then
+              failed_count=$(echo "$total_line" | grep -o "[0-9]* FAILED" | awk '{print $1}')
+            fi
+          fi
+          echo "failed_count=$failed_count" >> $GITHUB_OUTPUT
+          if [ "$failed_count" -gt 0 ]; then
+            echo "Tests failed count: $failed_count"
+          fi
+        fi
+
+    - name: Set failed count
+      id: check_failures
+      shell: ${{ inputs.platform == 'windows' && 'pwsh' || 'bash' }}
+      run: |
+        ${{ inputs.platform == 'windows' && format('
+        echo "failed_count={0}" >> $env:GITHUB_OUTPUT
+        ', steps.check_failures_windows.outputs.failed_count) || format('
+        echo "failed_count={0}" >> $GITHUB_OUTPUT
+        ', steps.check_failures_unix.outputs.failed_count) }}
+
+    - name: Download and extract 3rdparty dependencies (Windows)
+      if: ${{ inputs.platform == 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: pwsh
+      run: |
+          Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/3rdparty-vc14-64.zip -OutFile 3rdparty-vc14-64.zip
+          Expand-Archive -Path 3rdparty-vc14-64.zip -DestinationPath .
+          Remove-Item 3rdparty-vc14-64.zip
+
+    - name: Install dependencies (macOS)
+      if: ${{ inputs.platform == 'macos' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: bash
+      run: |
+        brew update
+        brew install tcl-tk tbb gl2ps xerces-c \
+                     libxmu libxi libxft libxpm \
+                     glew freeimage draco glfw
+
+    - name: Install dependencies (Linux)
+      if: ${{ inputs.platform == 'linux' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: bash
+      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake ${{ inputs.compiler == 'clang' && 'clang' || 'gcc g++' }} make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev fonts-noto-cjk fonts-liberation fonts-ubuntu fonts-liberation fonts-ubuntu fonts-noto-cjk fonts-ipafont-gothic fonts-ipafont-mincho fonts-unfonts-core
+
+    - name: Setup Xvfb and Mesa (Linux)
+      if: ${{ inputs.platform == 'linux' && steps.check_failures.outputs.failed_count > 0 }}
+      uses: ./.github/actions/setup-xvfb-mesa
+
+    - name: Download test data (Windows)
+      if: ${{ inputs.platform == 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: pwsh
+      run: |
+        cd data
+        Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.zip -OutFile opencascade-dataset-7.9.0.zip
+        Expand-Archive -Path opencascade-dataset-7.9.0.zip -DestinationPath .
+        Remove-Item opencascade-dataset-7.9.0.zip
+
+    - name: Download test data (macOS/Linux)
+      if: ${{ (inputs.platform == 'macos' || inputs.platform == 'linux') && steps.check_failures.outputs.failed_count > 0 }}
+      shell: bash
+      run: |
+        cd data
+        curl -L -O https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.${{ inputs.platform == 'macos' && 'tar.xz' || 'tar.xz' }}
+        tar -xf opencascade-dataset-7.9.0.tar.xz
+
+    - name: Download and extract install directory
+      if: steps.check_failures.outputs.failed_count > 0
+      uses: actions/download-artifact@v4.1.7
+      with:
+        name: ${{ inputs.install-artifact-name }}
+        path: install
+
+    - name: Download and extract Mesa3D (Windows)
+      if: ${{ inputs.platform == 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: pwsh
+      run: |
+        curl -L -o mesa3d.7z https://github.com/pal1000/mesa-dist-win/releases/download/24.3.2/mesa3d-24.3.2-release-mingw.7z
+        7z x mesa3d.7z -omesa3d
+
+    - name: Run system-wide deployment (Windows)
+      if: ${{ inputs.platform == 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: cmd
+      run: |
+        cd mesa3d
+        .\systemwidedeploy.cmd 1
+        .\systemwidedeploy.cmd 5
+
+    - name: Install CJK Fonts (Windows)
+      if: ${{ inputs.platform == 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: pwsh
+      run: |
+        Invoke-WebRequest -Uri https://noto-website-2.storage.googleapis.com/pkgs/Noto-hinted.zip -OutFile Noto-hinted.zip
+        Expand-Archive -Path Noto-hinted.zip -DestinationPath $env:windir\Fonts
+        Remove-Item Noto-hinted.zip
+
+    - name: Set execute permissions on DRAWEXE (macOS/Linux)
+      if: ${{ (inputs.platform == 'macos' || inputs.platform == 'linux') && steps.check_failures.outputs.failed_count > 0 }}
+      shell: bash
+      run: chmod +x install/${{ inputs.platform == 'macos' && 'bin' || 'bin' }}/DRAWEXE
+
+    - name: Run regression tests (Windows)
+      if: ${{ inputs.platform == 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: cmd
+      run: |
+        cd install
+        call env.bat ${{ inputs.compiler == 'clang' && 'clang' || 'vc14' }} win64 release
+        DRAWEXE.exe -v -c testgrid -regress results/${{ inputs.test-directory-name }} -outdir results/${{ inputs.test-directory-name }}-retest -parallel 0
+      env:
+        LIBGL_ALWAYS_SOFTWARE: 1
+        CSF_TestScriptsPath: ${{ github.workspace }}/tests
+        CSF_TestDataPath: ${{ github.workspace }}/data
+
+    - name: Run regression tests (macOS/Linux)
+      if: ${{ (inputs.platform == 'macos' || inputs.platform == 'linux') && steps.check_failures.outputs.failed_count > 0 }}
+      shell: bash
+      run: |
+         cd install
+         cd bin
+         source env.sh
+         ./DRAWEXE -v -c testgrid -regress results/${{ inputs.test-directory-name }} -outdir results/${{ inputs.test-directory-name }}-retest -parallel 0
+      env:
+        DISPLAY: ${{ inputs.platform == 'linux' && ':99' || '' }}
+        LIBGL_ALWAYS_SOFTWARE: 1
+        CSF_TestScriptsPath: ${{ github.workspace }}/tests
+        CSF_TestDataPath: ${{ github.workspace }}/data
+
+    - name: Repeating failed tests (Windows)
+      if: ${{ inputs.platform == 'windows' && steps.check_failures.outputs.failed_count > 0 && steps.check_failures.outputs.failed_count < 20 }}
+      shell: cmd
+      run: |
+        cd install
+        call env.bat ${{ inputs.compiler == 'clang' && 'clang' || 'vc14' }} win64 release
+        # Repeat failed tests for 10 times
+        for /l %%i in (1,1,10) do (
+          DRAWEXE.exe -v -c testgrid -regress results/${{ inputs.test-directory-name }}-retest -outdir results/${{ inputs.test-directory-name }}-retest -parallel 0 -overwrite
+          DRAWEXE.exe -v -c "testsummarize results/${{ inputs.test-directory-name }}-retest"
+        )
+      env:
+        LIBGL_ALWAYS_SOFTWARE: 1
+        CSF_TestScriptsPath: ${{ github.workspace }}/tests
+        CSF_TestDataPath: ${{ github.workspace }}/data
+
+    - name: Repeating failed tests (macOS/Linux)
+      if: ${{ inputs.platform != 'windows' && steps.check_failures.outputs.failed_count > 0 && steps.check_failures.outputs.failed_count < 20 }}
+      shell: bash
+      run: |
+        cd install
+        cd bin
+        source env.sh
+        # Repeat failed tests for 10 times
+        for i in {1..10}; do
+          ./DRAWEXE -v -c testgrid -regress results/${{ inputs.test-directory-name }}-retest -outdir results/${{ inputs.test-directory-name }}-retest -parallel 0 -overwrite
+          ./DRAWEXE -v -c "testsummarize results/${{ inputs.test-directory-name }}-retest"
+        done
+      env:
+        DISPLAY: ${{ inputs.platform == 'linux' && ':99' || '' }}
+        LIBGL_ALWAYS_SOFTWARE: 1
+        CSF_TestScriptsPath: ${{ github.workspace }}/tests
+        CSF_TestDataPath: ${{ github.workspace }}/data
+
+    - name: Upload regression test results
+      if: steps.check_failures.outputs.failed_count > 0
+      uses: actions/upload-artifact@v4.4.3
+      with:
+        name: ${{ inputs.results-artifact-name }}-retest
+        path: install/${{ (inputs.platform == 'windows') && '' || 'bin/' }}results/${{ inputs.test-directory-name }}-retest
+        retention-days: 15
+        overwrite: true
+
+    - name: Copy retest results back to original location (Windows)
+      if: ${{ inputs.platform == 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: cmd
+      run: |
+        cd install\results\${{ inputs.test-directory-name }}-retest
+        if exist "*" (
+          xcopy /s /y /i . "..\${{ inputs.test-directory-name }}"
+          cd ..\..
+          call env.bat ${{ inputs.compiler == 'clang' && 'clang' || 'vc14' }} win64 release
+          DRAWEXE.exe -v -c "testsummarize results/${{ inputs.test-directory-name }}"
+        ) else (
+          echo No retest results to copy - directory is empty
+        )
+
+    - name: Copy retest results back to original location (macOS/Linux)
+      if: ${{ inputs.platform != 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: bash
+      run: |
+        cd install/bin/results/${{ inputs.test-directory-name }}-retest
+        if [ "$(ls -A)" ]; then
+          cp -rf * ../${{ inputs.test-directory-name }}/
+
+          cd ../../
+          source env.sh
+          ./DRAWEXE -v -c testsummarize results/${{ inputs.test-directory-name }}
+        else
+          echo "No retest results to copy - directory is empty"
+        fi
+
+    - name: Upload updated test results
+      uses: actions/upload-artifact@v4.4.3
+      with:
+        name: ${{ inputs.results-artifact-name }}
+        path: install/${{ inputs.platform == 'windows' && 'results' || 'bin/results' }}/${{ inputs.test-directory-name }}*
+        retention-days: 15
+        overwrite: true
+
+    - name: Check test failures (Windows)
+      if: ${{ inputs.platform == 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: pwsh
+      run: |
+        cd install/results/${{ inputs.test-directory-name }}-retest
+        $failedCount = 0
+        if (Test-Path tests.log) {
+          $content = Get-Content tests.log
+          $totalLine = $content | Select-String "Total cases:"
+          if ($totalLine) {
+            if ($totalLine -match "FAILED") {
+              $failedCount = ($totalLine | ForEach-Object { $_.Line -replace '.*?(\d+) FAILED.*','$1' }) -as [int]
+            }
+          }
+          if ($failedCount -gt 0) {
+            Write-Error "Number of FAILED tests ($failedCount) exceeds threshold of 0"
+            echo "FAILED_COUNT=$failedCount" >> $env:GITHUB_ENV
+            exit 1
+          }
+          Write-Output "Found $failedCount FAILED tests"
+        }
+
+    - name: Check test failures (macOS/Linux)
+      if: ${{ inputs.platform != 'windows' && steps.check_failures.outputs.failed_count > 0 }}
+      shell: bash
+      run: |
+        cd install/bin/results/${{ inputs.test-directory-name }}-retest
+        if [ -f tests.log ]; then
+          TOTAL_LINE=$(grep "Total cases:" tests.log | tail -n1)
+          echo "Total line: $TOTAL_LINE"
+          if [[ $TOTAL_LINE =~ ([0-9]+)[[:space:]]FAILED ]]; then
+            FAILED_COUNT="${BASH_REMATCH[1]}"
+            if [ $FAILED_COUNT -gt 0 ]; then
+              echo "Number of FAILED tests ($FAILED_COUNT) exceeds threshold of 0"
+              echo "::error::Number of FAILED tests ($FAILED_COUNT) exceeds threshold of 0"
+              echo "FAILED_COUNT=$FAILED_COUNT" >> $GITHUB_ENV
+              exit 1
+            fi
+          fi
+          echo "Found 0 FAILED tests"
+        fi
diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml
new file mode 100644 (file)
index 0000000..3099b25
--- /dev/null
@@ -0,0 +1,178 @@
+name: 'Run Tests'
+description: 'Run OCCT tests on a specific platform'
+
+inputs:
+  platform:
+    description: 'Platform (windows, macos, linux)'
+    required: true
+  compiler:
+    description: 'Compiler (msvc, clang, gcc)'
+    required: true
+  install-artifact-name:
+    description: 'Name of the artifact containing the install directory'
+    required: true
+  test-directory-name:
+    description: 'Name of the directory to store test results'
+    required: true
+  test-script:
+    description: 'The test script to run'
+    required: true
+
+runs:
+  using: "composite"
+  steps:
+    - name: Download and extract 3rdparty dependencies (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+          Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/3rdparty-vc14-64.zip -OutFile 3rdparty-vc14-64.zip
+          Expand-Archive -Path 3rdparty-vc14-64.zip -DestinationPath .
+          Remove-Item 3rdparty-vc14-64.zip
+      shell: pwsh
+
+    - name: Install dependencies (macOS)
+      if: ${{ inputs.platform == 'macos' }}
+      run: |
+        brew update
+        brew install tcl-tk tbb gl2ps xerces-c \
+                     libxmu libxi libxft libxpm \
+                     glew freeimage draco glfw
+      shell: bash
+
+    - name: Install dependencies (Linux)
+      if: ${{ inputs.platform == 'linux' }}
+      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake ${{ inputs.compiler == 'gcc' && 'gcc g++' || 'clang' }} make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev fonts-noto-cjk fonts-liberation fonts-ubuntu fonts-liberation fonts-ubuntu fonts-noto-cjk fonts-ipafont-gothic fonts-ipafont-mincho fonts-unfonts-core
+      shell: bash
+
+    - name: Setup Xvfb and Mesa (Linux)
+      if: ${{ inputs.platform == 'linux' }}
+      uses: ./.github/actions/setup-xvfb-mesa
+
+    - name: Download and extract test data (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+        cd data
+        Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.zip -OutFile opencascade-dataset-7.9.0.zip
+        Expand-Archive -Path opencascade-dataset-7.9.0.zip -DestinationPath .
+        Remove-Item opencascade-dataset-7.9.0.zip
+      shell: pwsh
+
+    - name: Download test data (macOS/Linux)
+      if: ${{ (inputs.platform == 'macos' || inputs.platform == 'linux') }}
+      run: |
+        cd data
+        wget -q https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.tar.xz
+        tar -xf opencascade-dataset-7.9.0.tar.xz
+      shell: bash
+
+    - name: Download and extract install directory
+      uses: actions/download-artifact@v4.1.7
+      with:
+        name: ${{ inputs.install-artifact-name }}
+        path: install
+
+    - name: Download and extract Mesa3D (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+        curl -L -o mesa3d.7z https://github.com/pal1000/mesa-dist-win/releases/download/24.3.2/mesa3d-24.3.2-release-mingw.7z
+        7z x mesa3d.7z -omesa3d
+      shell: pwsh
+
+    - name: Run system-wide deployment (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+        cd mesa3d
+        .\systemwidedeploy.cmd 1
+        .\systemwidedeploy.cmd 5
+      shell: cmd
+
+    - name: Install CJK Fonts (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+        Invoke-WebRequest -Uri https://noto-website-2.storage.googleapis.com/pkgs/Noto-hinted.zip -OutFile Noto-hinted.zip
+        Expand-Archive -Path Noto-hinted.zip -DestinationPath $env:windir\Fonts
+        Remove-Item Noto-hinted.zip
+      shell: pwsh
+
+    - name: Set LIBGL_ALWAYS_SOFTWARE environment variable (macOS)
+      if: ${{ inputs.platform == 'macos' }}
+      run: echo "LIBGL_ALWAYS_SOFTWARE=1" >> $GITHUB_ENV
+      shell: bash
+
+    - name: Set execute permissions on DRAWEXE (macOS/Linux)
+      if: ${{ inputs.platform == 'macos' || inputs.platform == 'linux' }}
+      run: chmod +x install/bin/DRAWEXE
+      shell: bash
+
+    - name: Run tests (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+        cd install
+        call env.bat ${{ inputs.compiler == 'msvc' && 'vc14' || 'clang' }} win64 release
+        DRAWEXE.exe -v -f ${{ github.workspace }}/${{ inputs.test-script }}
+      shell: cmd
+      env:
+        LIBGL_ALWAYS_SOFTWARE: 1
+        CSF_TestScriptsPath: ${{ github.workspace }}/tests
+        CSF_TestDataPath: ${{ github.workspace }}/data
+
+    - name: Run tests (macOS/Linux)
+      if: ${{ inputs.platform == 'macos' || inputs.platform == 'linux' }}
+      run: |
+         cd install
+         cd bin
+         source env.sh
+         ./DRAWEXE -v -f ${{ github.workspace }}/${{ inputs.test-script }}
+      shell: bash
+      env:
+        DISPLAY: ${{ inputs.platform == 'linux' && ':99' || '' }}
+        LIBGL_ALWAYS_SOFTWARE: 1
+        CSF_TestScriptsPath: ${{ github.workspace }}/tests
+        CSF_TestDataPath: ${{ github.workspace }}/data
+
+    - name: Clean up test results (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      run: |
+        cd install
+        call env.bat ${{ inputs.compiler == 'msvc' && 'vc14' || 'clang' }} win64 release
+        DRAWEXE.exe -v -c cleanuptest results/${{ inputs.test-directory-name }}
+      shell: cmd
+      env:
+        LIBGL_ALWAYS_SOFTWARE: 1
+        CSF_TestScriptsPath: ${{ github.workspace }}/tests
+        CSF_TestDataPath: ${{ github.workspace }}/data
+
+    - name: Clean up test results (macOS/Linux)
+      if: ${{ inputs.platform == 'macos' || inputs.platform == 'linux' }}
+      run: |
+         cd install
+         cd bin
+         source env.sh
+         ./DRAWEXE -v -c cleanuptest results/${{ inputs.test-directory-name }}
+      shell: bash
+      env:
+        DISPLAY: ${{ inputs.platform == 'linux' && ':99' || '' }}
+        LIBGL_ALWAYS_SOFTWARE: 1
+        CSF_TestScriptsPath: ${{ github.workspace }}/tests
+        CSF_TestDataPath: ${{ github.workspace }}/data
+
+    - name: Upload test results (Windows)
+      if: ${{ inputs.platform == 'windows' }}
+      uses: actions/upload-artifact@v4.4.3
+      with:
+        name: results-${{ inputs.test-directory-name }}
+        path: |
+          install/results/**/*.log
+          install/results/**/*.png
+          install/results/**/*.html
+        retention-days: 15
+
+    - name: Upload test results (macOS/Linux)
+      if: ${{ inputs.platform == 'macos' || inputs.platform == 'linux' }}
+      uses: actions/upload-artifact@v4.4.3
+      with:
+        name: results-${{ inputs.test-directory-name }}
+        path: |
+          install/bin/results/**/*.log
+          install/bin/results/**/*.png
+          install/bin/results/**/*.html
+        retention-days: 15
diff --git a/.github/actions/test-summary/action.yml b/.github/actions/test-summary/action.yml
new file mode 100644 (file)
index 0000000..a582a47
--- /dev/null
@@ -0,0 +1,94 @@
+name: 'Test Summary'
+description: 'Compare test results between current branch and master'
+
+runs:
+  using: "composite"
+  steps:
+    - name: Install dependencies
+      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake gcc g++ make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev
+      shell: bash
+
+    - name: Setup Xvfb and Mesa
+      uses: ./.github/actions/setup-xvfb-mesa
+
+    - name: Set environment variables
+      run: |
+        echo "DISPLAY=:99" >> $GITHUB_ENV
+        echo "LIBGL_ALWAYS_SOFTWARE=1" >> $GITHUB_ENV
+      shell: bash
+
+    - name: Download and extract install directory
+      uses: actions/download-artifact@v4.1.7
+      with:
+        name: install-linux-gcc-x64
+        path: install
+
+    - name: Set execute permissions on DRAWEXE
+      run: chmod +x install/bin/DRAWEXE
+      shell: bash
+
+    - name: Get latest workflow run ID from target branch
+      id: get_run_id
+      run: |
+        response=$(curl -s \
+          -H "Accept: application/vnd.github.v3+json" \
+          "https://api.github.com/repos/${{ github.repository }}/actions/runs?branch=${{ github.event.pull_request.base.ref }}&status=success")
+        latest_run_id=$(echo "$response" | jq -r '.workflow_runs[] | select(.name=="Build and Test OCCT on Multiple Platforms") | .id' | head -n 1)
+        echo "latest_run_id=$latest_run_id" >> $GITHUB_ENV
+      shell: bash
+
+    - name: Download master branch test results
+      env:
+        GH_TOKEN: ${{ github.token }}
+      run: |
+        for platform in windows-x64 windows-clang-x64 macos-x64 macos-gcc-x64 linux-clang-x64 linux-gcc-x64; do
+          echo "Downloading results for $platform"
+          gh run download ${{ env.latest_run_id }} -n "results-$platform" -D "install/bin/results/master/"
+        done
+      shell: bash
+
+    - name: Download current branch test results
+      env:
+        GH_TOKEN: ${{ github.token }}
+      run: |
+        for platform in windows-x64 windows-clang-x64 macos-x64 macos-gcc-x64 linux-clang-x64 linux-gcc-x64; do
+          echo "Downloading results for $platform"
+          gh run download -n "results-$platform" -D "install/bin/results/current/"
+        done
+      shell: bash
+
+    - name: Compare test results
+      run: |
+        echo "Comparing test results..."
+        cd install/bin
+        source env.sh
+        for platform in windows-x64 windows-clang-x64 macos-x64 macos-gcc-x64 linux-clang-x64 linux-gcc-x64; do
+          ./DRAWEXE -v -c testdiff "results/current/$platform" "results/master/$platform" &
+        done
+        wait
+      shell: bash
+
+    - name: Install BeautifulSoup
+      run: pip install beautifulsoup4
+      shell: bash
+
+    - name: Clean unused test images
+      run: |
+        # copy to the install/bin/results directory
+        cp ${{ github.workspace }}/.github/actions/scripts/cleanup_test_images.py install/bin/results
+        cd install/bin/results
+        python cleanup_test_images.py
+      shell: bash
+
+    - name: Upload comparison results
+      uses: actions/upload-artifact@v4.4.3
+      with:
+        name: test-compare-results
+        retention-days: 15
+        overwrite: true
+        path: |
+          install/bin/results/**/diff-*.html
+          install/bin/results/**/diff-*.log
+          install/bin/results/**/summary.html
+          install/bin/results/**/tests.log
+          install/bin/results/**/*.png
index 4274d5a05035260563e9d555622c71792b521ab0..cfa28c7bddd1b748f2a385fbc48d03164e0b90de 100644 (file)
@@ -49,374 +49,91 @@ jobs:
     runs-on: windows-2022
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Download and extract 3rdparty dependencies
-      run: |
-          Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/3rdparty-vc14-64.zip -OutFile 3rdparty-vc14-64.zip
-          Expand-Archive -Path 3rdparty-vc14-64.zip -DestinationPath .
-          Remove-Item 3rdparty-vc14-64.zip
-      shell: pwsh
-
-    - name: Download and extract Mesa3D
-      run: |
-        curl -L -o mesa3d.7z https://github.com/pal1000/mesa-dist-win/releases/download/24.3.2/mesa3d-24.3.2-release-mingw.7z
-        7z x mesa3d.7z -omesa3d
-
-    - name: Run system-wide deployment
-      run: |
-        cd mesa3d
-        .\systemwidedeploy.cmd 1
-        .\systemwidedeploy.cmd 5
-      shell: cmd
-
-    - name: Configure OCCT
-      run: |
-          mkdir build
-          cd build
-          cmake -T host=x64 `
-                -D USE_FREETYPE=ON `
-                -D USE_TK=OFF `
-                -D BUILD_USE_PCH=ON `
-                -D BUILD_OPT_PROFILE=Production `
-                -D BUILD_INCLUDE_SYMLINK=ON `
-                -D CMAKE_BUILD_TYPE=Release `
-                -D 3RDPARTY_DIR=${{ github.workspace }}/3rdparty-vc14-64 `
-                -D INSTALL_DIR=${{ github.workspace }}/install `
-                -D USE_D3D=ON `
-                -D USE_DRACO=ON `
-                -D USE_FFMPEG=ON `
-                -D USE_FREEIMAGE=ON `
-                -D USE_GLES2=ON `
-                -D USE_OPENVR=ON `
-                -D USE_VTK=ON `
-                -D USE_TBB=ON `
-                -D USE_RAPIDJSON=ON `
-                -D USE_OPENGL=ON `
-                -D BUILD_GTEST=ON `
-                -D BUILD_CPP_STANDARD=C++14 `
-                -D INSTALL_GTEST=ON ..
-      shell: pwsh
-
     - name: Build OCCT
-      run: |
-          cd build
-          cmake --build . --target install --config Release
-
-    - name: Upload install directory
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/build-occt
       with:
-        name: install-windows-x64
-        path: install
-        retention-days: 7
+        platform: windows
+        compiler: msvc
+        artifact-name: install-windows-x64
 
   prepare-and-build-windows-clang-x64:
     name: Prepare and Build on Windows with Clang (x64)
     runs-on: windows-2022
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Download and extract 3rdparty dependencies
-      run: |
-          Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/3rdparty-vc14-64.zip -OutFile 3rdparty-vc14-64.zip
-          Expand-Archive -Path 3rdparty-vc14-64.zip -DestinationPath .
-          Remove-Item 3rdparty-vc14-64.zip
-      shell: pwsh
-
-    - name: Download and extract Mesa3D
-      run: |
-        curl -L -o mesa3d.7z https://github.com/pal1000/mesa-dist-win/releases/download/24.3.2/mesa3d-24.3.2-release-mingw.7z
-        7z x mesa3d.7z -omesa3d
-
-    - name: Run system-wide deployment
-      run: |
-        cd mesa3d
-        .\systemwidedeploy.cmd 1
-        .\systemwidedeploy.cmd 5
-      shell: cmd
-
-    - name: Install Ninja
-      run: |
-        choco install ninja -y
-        ninja --version
-      shell: pwsh
-
-    - name: Configure OCCT
-      run: |
-          mkdir build
-          cd build
-          cmake -G "Ninja" `
-                -D CMAKE_C_COMPILER=clang `
-                -D CMAKE_CXX_COMPILER=clang++ `
-                -D USE_FREETYPE=ON `
-                -D USE_TK=OFF `
-                -D BUILD_USE_PCH=ON `
-                -D BUILD_OPT_PROFILE=Production `
-                -D BUILD_INCLUDE_SYMLINK=ON `
-                -D CMAKE_BUILD_TYPE=Release `
-                -D 3RDPARTY_DIR=${{ github.workspace }}/3rdparty-vc14-64 `
-                -D INSTALL_DIR=${{ github.workspace }}/install `
-                -D USE_D3D=ON `
-                -D USE_DRACO=ON `
-                -D USE_FFMPEG=ON `
-                -D USE_FREEIMAGE=ON `
-                -D USE_GLES2=ON `
-                -D USE_OPENVR=ON `
-                -D USE_VTK=OFF `
-                -D USE_TBB=ON `
-                -D USE_RAPIDJSON=ON `
-                -D USE_OPENGL=ON `
-                -D BUILD_GTEST=ON `
-                -D BUILD_CPP_STANDARD=C++14 `
-                -D INSTALL_GTEST=ON `
-                -D CMAKE_CXX_FLAGS="-Werror -Wall -Wextra -Wno-unknown-warning-option" `
-                -D CMAKE_C_FLAGS="-Werror -Wall -Wextra -Wno-unknown-warning-option" ..
-      shell: pwsh
-
     - name: Build OCCT
-      run: |
-          cd build
-          cmake --build . --target install --config Release
-
-    - name: Upload install directory
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/build-occt
       with:
-        name: install-windows-clang-x64
-        path: install
-        retention-days: 7
+        platform: windows
+        compiler: clang
+        artifact-name: install-windows-clang-x64
+        use-vtk: 'false'
 
   prepare-and-build-macos-x64:
     name: Prepare and Build on macOS with Clang (x64)
     runs-on: macos-15
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Install dependencies
-      run: |
-        brew update
-        brew install tcl-tk tbb gl2ps xerces-c \
-                     libxmu libxi libxft libxpm \
-                     glew freeimage draco glfw
-
-    - name: Install rapidjson
-      run: |
-        wget https://github.com/Tencent/rapidjson/archive/858451e5b7d1c56cf8f6d58f88cf958351837e53.zip -O rapidjson.zip
-        unzip rapidjson.zip
-
-    - name: Configure OCCT
-      run: |
-        mkdir -p build
-        cd build
-        cmake -G "Unix Makefiles" \
-              -D CMAKE_C_COMPILER=clang \
-              -D CMAKE_CXX_COMPILER=clang++ \
-              -D BUILD_USE_PCH=ON \
-              -D BUILD_INCLUDE_SYMLINK=ON \
-              -D CMAKE_BUILD_TYPE=Release \
-              -D INSTALL_DIR=${{ github.workspace }}/install \
-              -D 3RDPARTY_RAPIDJSON_DIR=${{ github.workspace }}/rapidjson-858451e5b7d1c56cf8f6d58f88cf958351837e53 \
-              -D USE_RAPIDJSON=ON \
-              -D USE_DRACO=ON \
-              -D USE_FREETYPE=ON \
-              -D USE_OPENGL=ON \
-              -D USE_FREEIMAGE=ON \
-              -D BUILD_GTEST=ON \
-              -D BUILD_CPP_STANDARD=C++14 \
-              -D INSTALL_GTEST=ON \
-              -D CMAKE_CXX_FLAGS="-Werror -Wall -Wextra" \
-              -D CMAKE_C_FLAGS="-Werror -Wall -Wextra" ..
-
     - name: Build OCCT
-      run: |
-        cd build
-        make install -j$(sysctl -n hw.logicalcpu)
-
-    - name: Upload install directory
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/build-occt
       with:
-        name: install-macos-x64
-        path: install
-        retention-days: 7
+        platform: macos
+        compiler: clang
+        artifact-name: install-macos-x64
 
   prepare-and-build-macos-gcc-x64:
     name: Prepare and Build on macOS with GCC (x64)
     runs-on: macos-15
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Install dependencies
-      run: |
-        brew update
-        brew install tcl-tk tbb gl2ps xerces-c \
-                     libxmu libxi libxft libxpm \
-                     glew freeimage draco glfw
-
-    - name: Install rapidjson
-      run: |
-        wget https://github.com/Tencent/rapidjson/archive/858451e5b7d1c56cf8f6d58f88cf958351837e53.zip -O rapidjson.zip
-        unzip rapidjson.zip
-
-    - name: Configure OCCT
-      run: |
-        mkdir -p build
-        cd build
-        cmake -G "Unix Makefiles" \
-              -D CMAKE_C_COMPILER=gcc \
-              -D CMAKE_CXX_COMPILER=g++ \
-              -D BUILD_USE_PCH=ON \
-              -D BUILD_INCLUDE_SYMLINK=ON \
-              -D CMAKE_BUILD_TYPE=Release \
-              -D INSTALL_DIR=${{ github.workspace }}/install \
-              -D 3RDPARTY_RAPIDJSON_DIR=${{ github.workspace }}/rapidjson-858451e5b7d1c56cf8f6d58f88cf958351837e53 \
-              -D USE_RAPIDJSON=ON \
-              -D USE_DRACO=ON \
-              -D USE_FREETYPE=ON \
-              -D USE_OPENGL=ON \
-              -D USE_FREEIMAGE=ON \
-              -D BUILD_GTEST=ON \
-              -D BUILD_CPP_STANDARD=C++14 \
-              -D INSTALL_GTEST=ON \
-              -D CMAKE_CXX_FLAGS="-Werror -Wall -Wextra" \
-              -D CMAKE_C_FLAGS="-Werror -Wall -Wextra" ..
-
     - name: Build OCCT
-      run: |
-        cd build
-        make install -j$(sysctl -n hw.logicalcpu)
-
-    - name: Upload install directory
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/build-occt
       with:
-        name: install-macos-gcc-x64
-        path: install
-        retention-days: 7
+        platform: macos
+        compiler: gcc
+        artifact-name: install-macos-gcc-x64
 
   prepare-and-build-linux-clang-x64:
     name: Prepare and Build on Ubuntu with Clang (x64)
     runs-on: ubuntu-24.04
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Install dependencies
-      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake clang make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev
-
-    - name: Install rapidjson
-      run: |
-        wget https://github.com/Tencent/rapidjson/archive/858451e5b7d1c56cf8f6d58f88cf958351837e53.zip -O rapidjson.zip
-        unzip rapidjson.zip
-
-    - name: Configure OCCT
-      run: |
-        mkdir -p build
-        cd build
-        cmake -G "Unix Makefiles" \
-              -D CMAKE_C_COMPILER=clang \
-              -D CMAKE_CXX_COMPILER=clang++ \
-              -D BUILD_USE_PCH=ON \
-              -D BUILD_INCLUDE_SYMLINK=ON \
-              -D BUILD_OPT_PROFILE=Production \
-              -D USE_TK=OFF \
-              -D CMAKE_BUILD_TYPE=Release \
-              -D INSTALL_DIR=${{ github.workspace }}/install \
-              -D 3RDPARTY_RAPIDJSON_DIR=${{ github.workspace }}/rapidjson-858451e5b7d1c56cf8f6d58f88cf958351837e53 \
-              -D USE_FREETYPE=ON \
-              -D USE_DRACO=ON \
-              -D USE_FFMPEG=OFF \
-              -D USE_FREEIMAGE=ON \
-              -D USE_GLES2=ON \
-              -D USE_OPENVR=ON \
-              -D USE_VTK=ON \
-              -D USE_TBB=OFF \
-              -D USE_RAPIDJSON=ON \
-              -D USE_OPENGL=ON \
-              -D BUILD_GTEST=ON \
-              -D BUILD_CPP_STANDARD=C++14 \
-              -D INSTALL_GTEST=ON \
-              -D CMAKE_CXX_FLAGS="-Werror -Wall -Wextra" \
-              -D CMAKE_C_FLAGS="-Werror -Wall -Wextra" ..
-
     - name: Build OCCT
-      run: |
-        cd build
-        cmake --build . --target install --config Release -- -j
-
-    - name: Upload install directory
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/build-occt
       with:
-        name: install-linux-clang-x64
-        path: install
-        retention-days: 7
+        platform: linux
+        compiler: clang
+        artifact-name: install-linux-clang-x64
 
   prepare-and-build-linux-gcc-x64:
     name: Prepare and Build on Ubuntu with GCC (x64)
     runs-on: ubuntu-24.04
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Install dependencies
-      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake gcc g++ make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev
-
-    - name: Install rapidjson
-      run: |
-        wget https://github.com/Tencent/rapidjson/archive/858451e5b7d1c56cf8f6d58f88cf958351837e53.zip -O rapidjson.zip
-        unzip rapidjson.zip
-
-    - name: Configure OCCT
-      run: |
-        mkdir -p build
-        cd build
-        cmake -G "Unix Makefiles" \
-              -D CMAKE_C_COMPILER=gcc \
-              -D CMAKE_CXX_COMPILER=g++ \
-              -D BUILD_USE_PCH=ON \
-              -D BUILD_INCLUDE_SYMLINK=ON \
-              -D BUILD_OPT_PROFILE=Production \
-              -D USE_TK=OFF \
-              -D CMAKE_BUILD_TYPE=Release \
-              -D INSTALL_DIR=${{ github.workspace }}/install \
-              -D 3RDPARTY_RAPIDJSON_DIR=${{ github.workspace }}/rapidjson-858451e5b7d1c56cf8f6d58f88cf958351837e53 \
-              -D USE_FREETYPE=ON \
-              -D USE_DRACO=ON \
-              -D USE_FFMPEG=OFF \
-              -D USE_FREEIMAGE=ON \
-              -D USE_GLES2=ON \
-              -D USE_OPENVR=ON \
-              -D USE_VTK=ON \
-              -D USE_TBB=OFF \
-              -D USE_RAPIDJSON=ON \
-              -D USE_OPENGL=ON \
-              -D BUILD_GTEST=ON \
-              -D BUILD_CPP_STANDARD=C++14 \
-              -D INSTALL_GTEST=ON ..
-
     - name: Build OCCT
-      run: |
-        cd build
-        cmake --build . --target install --config Release -- -j
-
-    - name: Upload install directory
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/build-occt
       with:
-        name: install-linux-gcc-x64
-        path: install
-        retention-days: 7
+        platform: linux
+        compiler: gcc
+        artifact-name: install-linux-gcc-x64
 
   build-inspector-windows:
     name: Build TInspector on Windows
@@ -517,77 +234,14 @@ jobs:
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Download and extract 3rdparty dependencies
-      run: |
-          Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/3rdparty-vc14-64.zip -OutFile 3rdparty-vc14-64.zip
-          Expand-Archive -Path 3rdparty-vc14-64.zip -DestinationPath .
-          Remove-Item 3rdparty-vc14-64.zip
-      shell: pwsh
-
-    - name: Download and extract test data
-      run: |
-        cd data
-        Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.zip -OutFile opencascade-dataset-7.9.0.zip
-        Expand-Archive -Path opencascade-dataset-7.9.0.zip -DestinationPath .
-        Remove-Item opencascade-dataset-7.9.0.zip
-      shell: pwsh
-
-    - name: Download and extract install directory
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-windows-x64
-        path: install
-
-    - name: Download and extract Mesa3D
-      run: |
-        curl -L -o mesa3d.7z https://github.com/pal1000/mesa-dist-win/releases/download/24.3.2/mesa3d-24.3.2-release-mingw.7z
-        7z x mesa3d.7z -omesa3d
-
-    - name: Run system-wide deployment
-      run: |
-        cd mesa3d
-        .\systemwidedeploy.cmd 1
-        .\systemwidedeploy.cmd 5
-      shell: cmd
-
-    - name: Install CJK Fonts
-      run: |
-        Invoke-WebRequest -Uri https://noto-website-2.storage.googleapis.com/pkgs/Noto-hinted.zip -OutFile Noto-hinted.zip
-        Expand-Archive -Path Noto-hinted.zip -DestinationPath $env:windir\Fonts
-        Remove-Item Noto-hinted.zip
-      shell: pwsh
-
     - name: Run tests
-      run: |
-        cd install
-        call env.bat vc14 win64 release
-        DRAWEXE.exe -v -f ${{ github.workspace }}/.github/actions/testgrid/testwindows.tcl
-      shell: cmd
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Clean up test results
-      run: |
-        cd install
-        call env.bat vc14 win64 release
-        DRAWEXE.exe -v -c cleanuptest results/windows-x64
-      shell: cmd
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload test results
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/run-tests
       with:
-        name: results-windows-x64
-        path: |
-          install/results/**/*.log
-          install/results/**/*.png
-          install/results/**/*.html
-        retention-days: 15
+        platform: windows
+        compiler: msvc
+        install-artifact-name: install-windows-x64
+        test-directory-name: windows-x64
+        test-script: .github/actions/testgrid/testwindows.tcl
 
   retest-windows-x64:
     name: Regression Test on Windows (x64)
@@ -597,159 +251,15 @@ jobs:
     steps:
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
-
-    - name: Download previous test results
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: results-windows-x64
-        path: install/results
-
-    - name: Check for test failures
-      id: check_failures
-      run: |
-        $failedCount = 0
-        if (Test-Path "install/results/windows-x64/tests.log") {
-          $content = Get-Content "install/results/windows-x64/tests.log"
-          $totalLine = $content | Select-String "Total cases:"
-          if ($totalLine) {
-            if ($totalLine -match "FAILED") {
-              $failedCount = ($totalLine | ForEach-Object { $_.Line -replace '.*?(\d+) FAILED.*','$1' }) -as [int]
-            }
-          }
-          echo "failed_count=$failedCount" >> $env:GITHUB_OUTPUT
-          if ($failedCount -gt 0) {
-            echo "Tests failed count: $failedCount"
-          }
-        }
-      shell: pwsh
-
-    - name: Download and extract 3rdparty dependencies
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-          Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/3rdparty-vc14-64.zip -OutFile 3rdparty-vc14-64.zip
-          Expand-Archive -Path 3rdparty-vc14-64.zip -DestinationPath .
-          Remove-Item 3rdparty-vc14-64.zip
-      shell: pwsh
-
-    - name: Download test data
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd data
-        Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.zip -OutFile opencascade-dataset-7.9.0.zip
-        Expand-Archive -Path opencascade-dataset-7.9.0.zip -DestinationPath .
-        Remove-Item opencascade-dataset-7.9.0.zip
-      shell: pwsh
-
-    - name: Download and extract install directory
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-windows-x64
-        path: install
-
-    - name: Download and extract Mesa3D
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        curl -L -o mesa3d.7z https://github.com/pal1000/mesa-dist-win/releases/download/24.3.2/mesa3d-24.3.2-release-mingw.7z
-        7z x mesa3d.7z -omesa3d
-
-    - name: Run system-wide deployment
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd mesa3d
-        .\systemwidedeploy.cmd 1
-        .\systemwidedeploy.cmd 5
-      shell: cmd
-
-    - name: Install CJK Fonts
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        Invoke-WebRequest -Uri https://noto-website-2.storage.googleapis.com/pkgs/Noto-hinted.zip -OutFile Noto-hinted.zip
-        Expand-Archive -Path Noto-hinted.zip -DestinationPath $env:windir\Fonts
-        Remove-Item Noto-hinted.zip
-      shell: pwsh
-
-    - name: Run regression tests
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install
-        call env.bat vc14 win64 release
-        DRAWEXE.exe -v -c testgrid -regress results/windows-x64 -outdir results/windows-x64-retest -parallel 0
-      shell: cmd
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Repeating failed tests
-      if: steps.check_failures.outputs.failed_count > 0 && steps.check_failures.outputs.failed_count < 20
-      run: |
-        cd install
-        call env.bat vc14 win64 release
-        # Repeat failed tests for 10 times
-        for /l %%i in (1,1,10) do (
-          DRAWEXE.exe -v -c testgrid -regress results/windows-x64-retest -outdir results/windows-x64-retest -parallel 0 -overwrite
-          DRAWEXE.exe -v -c "testsummarize results/windows-x64-retest"
-        )
-      shell: cmd
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload regression test results
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/upload-artifact@v4.4.3
+      
+    - name: Run retest
+      uses: ./.github/actions/retest-failures
       with:
-        name: results-windows-x64-retest
-        path: install/results/windows-x64-retest
-        retention-days: 15
-        overwrite: true
-
-    - name: Copy retest results back to original location
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/results/windows-x64-retest
-        if exist "*" (
-          xcopy /s /y /i . ..\windows-x64\
-          cd ..\..\
-          call env.bat vc14 win64 release
-          DRAWEXE.exe -v -c "testsummarize results/windows-x64"
-        ) else (
-          echo No retest results to copy - directory is empty
-        )
-      shell: cmd
-
-    - name: Upload updated test results
-      if: ${{ hashFiles('install/results/windows-x64-retest/*') != '' }}
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-windows-x64
-        path: install/results/windows*-x64/
-        retention-days: 15
-        overwrite: true
-
-    - name: Check test failures
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/results/windows-x64-retest
-        $failedCount = 0
-        if (Test-Path tests.log) {
-          $content = Get-Content tests.log
-          $totalLine = $content | Select-String "Total cases:"
-          if ($totalLine) {
-            if ($totalLine -match "FAILED") {
-              $failedCount = ($totalLine | ForEach-Object { $_.Line -replace '.*?(\d+) FAILED.*','$1' }) -as [int]
-            }
-          }
-          if ($failedCount -gt 0) {
-            Write-Error "Number of FAILED tests ($failedCount) exceeds threshold of 0"
-            echo "FAILED_COUNT=$failedCount" >> $env:GITHUB_ENV
-            exit 1
-          }
-          Write-Output "Found $failedCount FAILED tests"
-        }
-      shell: pwsh
+        platform: windows
+        compiler: msvc
+        install-artifact-name: install-windows-x64
+        results-artifact-name: results-windows-x64
+        test-directory-name: windows-x64
 
   test-windows-clang-x64:
     name: Test on Windows with Clang (x64)
@@ -757,81 +267,17 @@ jobs:
     needs: prepare-and-build-windows-clang-x64
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Download and extract 3rdparty dependencies
-      run: |
-          Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/3rdparty-vc14-64.zip -OutFile 3rdparty-vc14-64.zip
-          Expand-Archive -Path 3rdparty-vc14-64.zip -DestinationPath .
-          Remove-Item 3rdparty-vc14-64.zip
-      shell: pwsh
-
-    - name: Download and extract test data
-      run: |
-        cd data
-        Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.zip -OutFile opencascade-dataset-7.9.0.zip
-        Expand-Archive -Path opencascade-dataset-7.9.0.zip -DestinationPath .
-        Remove-Item opencascade-dataset-7.9.0.zip
-      shell: pwsh
-
-    - name: Download and extract install directory
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-windows-clang-x64
-        path: install
-
-    - name: Download and extract Mesa3D
-      run: |
-        curl -L -o mesa3d.7z https://github.com/pal1000/mesa-dist-win/releases/download/24.3.2/mesa3d-24.3.2-release-mingw.7z
-        7z x mesa3d.7z -omesa3d
-
-    - name: Run system-wide deployment
-      run: |
-        cd mesa3d
-        .\systemwidedeploy.cmd 1
-        .\systemwidedeploy.cmd 5
-      shell: cmd
-
-    - name: Install CJK Fonts
-      run: |
-        Invoke-WebRequest -Uri https://noto-website-2.storage.googleapis.com/pkgs/Noto-hinted.zip -OutFile Noto-hinted.zip
-        Expand-Archive -Path Noto-hinted.zip -DestinationPath $env:windir\Fonts
-        Remove-Item Noto-hinted.zip
-      shell: pwsh
-
     - name: Run tests
-      run: |
-        cd install
-        call env.bat clang win64 release
-        DRAWEXE.exe -v -f ${{ github.workspace }}/.github/actions/testgrid/testwindowsclang.tcl
-      shell: cmd
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Clean up test results
-      run: |
-        cd install
-        call env.bat clang win64 release
-        DRAWEXE.exe -v -c cleanuptest results/windows-clang-x64
-      shell: cmd
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload test results
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/run-tests
       with:
-        name: results-windows-clang-x64
-        path: |
-          install/results/**/*.log
-          install/results/**/*.png
-          install/results/**/*.html
-        retention-days: 15
+        platform: windows
+        compiler: clang
+        install-artifact-name: install-windows-clang-x64
+        test-directory-name: windows-clang-x64
+        test-script: .github/actions/testgrid/testwindowsclang.tcl
 
   retest-windows-clang-x64:
     name: Regression Test on Windows with Clang (x64)
@@ -841,159 +287,15 @@ jobs:
     steps:
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
-
-    - name: Download previous test results
-      uses: actions/download-artifact@v4.1.7
+      
+    - name: Run retest
+      uses: ./.github/actions/retest-failures
       with:
-        name: results-windows-clang-x64
-        path: install/results
-
-    - name: Check for test failures
-      id: check_failures
-      run: |
-        $failedCount = 0
-        if (Test-Path "install/results/windows-clang-x64/tests.log") {
-          $content = Get-Content "install/results/windows-clang-x64/tests.log"
-          $totalLine = $content | Select-String "Total cases:"
-          if ($totalLine) {
-            if ($totalLine -match "FAILED") {
-              $failedCount = ($totalLine | ForEach-Object { $_.Line -replace '.*?(\d+) FAILED.*','$1' }) -as [int]
-            }
-          }
-          echo "failed_count=$failedCount" >> $env:GITHUB_OUTPUT
-          if ($failedCount -gt 0) {
-            echo "Tests failed count: $failedCount"
-          }
-        }
-      shell: pwsh
-
-    - name: Download and extract 3rdparty dependencies
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-          Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/3rdparty-vc14-64.zip -OutFile 3rdparty-vc14-64.zip
-          Expand-Archive -Path 3rdparty-vc14-64.zip -DestinationPath .
-          Remove-Item 3rdparty-vc14-64.zip
-      shell: pwsh
-
-    - name: Download test data
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd data
-        Invoke-WebRequest -Uri https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.zip -OutFile opencascade-dataset-7.9.0.zip
-        Expand-Archive -Path opencascade-dataset-7.9.0.zip -DestinationPath .
-        Remove-Item opencascade-dataset-7.9.0.zip
-      shell: pwsh
-
-    - name: Download and extract install directory
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-windows-clang-x64
-        path: install
-
-    - name: Download and extract Mesa3D
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        curl -L -o mesa3d.7z https://github.com/pal1000/mesa-dist-win/releases/download/24.3.2/mesa3d-24.3.2-release-mingw.7z
-        7z x mesa3d.7z -omesa3d
-
-    - name: Run system-wide deployment
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd mesa3d
-        .\systemwidedeploy.cmd 1
-        .\systemwidedeploy.cmd 5
-      shell: cmd
-
-    - name: Install CJK Fonts
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        Invoke-WebRequest -Uri https://noto-website-2.storage.googleapis.com/pkgs/Noto-hinted.zip -OutFile Noto-hinted.zip
-        Expand-Archive -Path Noto-hinted.zip -DestinationPath $env:windir\Fonts
-        Remove-Item Noto-hinted.zip
-      shell: pwsh
-
-    - name: Run regression tests
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install
-        call env.bat clang win64 release
-        DRAWEXE.exe -v -c testgrid -regress results/windows-clang-x64 -outdir results/windows-clang-x64-retest -parallel 0
-      shell: cmd
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Repeating failed tests
-      if: steps.check_failures.outputs.failed_count > 0 && steps.check_failures.outputs.failed_count < 20
-      run: |
-        cd install
-        call env.bat clang win64 release
-        # Repeat failed tests for 10 times
-        for /l %%i in (1,1,10) do (
-          DRAWEXE.exe -v -c testgrid -regress results/windows-clang-x64-retest -outdir results/windows-clang-x64-retest -parallel 0 -overwrite
-          DRAWEXE.exe -v -c "testsummarize results/windows-clang-x64-retest"
-        )
-      shell: cmd
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload regression test results
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-windows-clang-x64-retest
-        path: install/results/windows-clang-x64-retest
-        retention-days: 15
-        overwrite: true
-
-    - name: Copy retest results back to original location
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/results/windows-clang-x64-retest
-        if exist "*" (
-          xcopy /s /y /i . ..\windows-clang-x64\
-          cd ..\..\
-          call env.bat clang win64 release
-          DRAWEXE.exe -v -c "testsummarize results/windows-clang-x64"
-        ) else (
-          echo No retest results to copy - directory is empty
-        )
-      shell: cmd
-
-    - name: Upload updated test results
-      if: ${{ hashFiles('install/results/windows-clang-x64-retest/*') != '' }}
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-windows-clang-x64
-        path: install/results/windows*-x64/
-        retention-days: 15
-        overwrite: true
-
-    - name: Check test failures
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/results/windows-clang-x64-retest
-        $failedCount = 0
-        if (Test-Path tests.log) {
-          $content = Get-Content tests.log
-          $totalLine = $content | Select-String "Total cases:"
-          if ($totalLine) {
-            if ($totalLine -match "FAILED") {
-              $failedCount = ($totalLine | ForEach-Object { $_.Line -replace '.*?(\d+) FAILED.*','$1' }) -as [int]
-            }
-          }
-          if ($failedCount -gt 0) {
-            Write-Error "Number of FAILED tests ($failedCount) exceeds threshold of 0"
-            echo "FAILED_COUNT=$failedCount" >> $env:GITHUB_ENV
-            exit 1
-          }
-          Write-Output "Found $failedCount FAILED tests"
-        }
-      shell: pwsh
+        platform: windows
+        compiler: clang
+        install-artifact-name: install-windows-clang-x64
+        results-artifact-name: results-windows-clang-x64
+        test-directory-name: windows-clang-x64
 
   test-macos-x64:
     name: Test on macOS (x64)
@@ -1004,64 +306,14 @@ jobs:
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Install dependencies
-      run: |
-        brew update
-        brew install tcl-tk tbb gl2ps xerces-c \
-                     libxmu libxi libxft libxpm \
-                     glew freeimage draco glfw
-
-    - name: Download test data
-      run: |
-        cd data
-        curl -L -O https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.tar.xz
-        tar -xf opencascade-dataset-7.9.0.tar.xz
-
-    - name: Download and extract install directory
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-macos-x64
-        path: install
-
-    - name: Set LIBGL_ALWAYS_SOFTWARE environment variable
-      run: echo "LIBGL_ALWAYS_SOFTWARE=1" >> $GITHUB_ENV
-
-    - name: Set execute permissions on DRAWEXE
-      run: chmod +x install/bin/DRAWEXE
-
     - name: Run tests
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -f ${{ github.workspace }}/.github/actions/testgrid/testmacos.tcl
-      shell: bash
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Clean up test results
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -c cleanuptest results/macos-x64
-      shell: bash
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload test results
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/run-tests
       with:
-        name: results-macos-x64
-        path: |
-          install/bin/results/**/*.log
-          install/bin/results/**/*.png
-          install/bin/results/**/*.html
-        retention-days: 15
+        platform: macos
+        compiler: clang
+        install-artifact-name: install-macos-x64
+        test-directory-name: macos-x64
+        test-script: .github/actions/testgrid/testmacos.tcl
 
   retest-macos-x64:
     name: Regression Test on macOS (x64)
@@ -1071,136 +323,15 @@ jobs:
     steps:
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
-
-    - name: Download previous test results
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: results-macos-x64
-        path: install/bin/results
-
-    - name: Check for test failures
-      id: check_failures
-      run: |
-        failed_count=0
-        if [ -f "install/bin/results/macos-x64/tests.log" ]; then
-          total_line=$(grep "Total cases:" install/bin/results/macos-x64/tests.log)
-          if [ ! -z "$total_line" ]; then
-            if [[ $total_line =~ "FAILED" ]]; then
-              failed_count=$(echo "$total_line" | grep -o "[0-9]* FAILED" | awk '{print $1}')
-            fi
-          fi
-          echo "failed_count=$failed_count" >> $GITHUB_OUTPUT
-          if [ "$failed_count" -gt 0 ]; then
-            echo "Tests failed count: $failed_count"
-          fi
-        fi
-      shell: bash
-
-    - name: Install dependencies
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        brew update
-        brew install tcl-tk tbb gl2ps xerces-c \
-                     libxmu libxi libxft libxpm \
-                     glew freeimage draco glfw
-
-    - name: Download test data
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd data
-        curl -L -O https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.tar.xz
-        tar -xf opencascade-dataset-7.9.0.tar.xz
-
-    - name: Download and extract install directory
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/download-artifact@v4.1.7
+      
+    - name: Run retest
+      uses: ./.github/actions/retest-failures
       with:
-        name: install-macos-x64
-        path: install
-
-    - name: Set LIBGL_ALWAYS_SOFTWARE environment variable
-      if: steps.check_failures.outputs.failed_count > 0
-      run: echo "LIBGL_ALWAYS_SOFTWARE=1" >> $GITHUB_ENV
-
-    - name: Set execute permissions on DRAWEXE
-      if: steps.check_failures.outputs.failed_count > 0
-      run: chmod +x install/bin/DRAWEXE
-
-    - name: Run regression tests
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -c testgrid -regress results/macos-x64 -outdir results/macos-x64-retest -parallel 0
-      shell: bash
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Repeating failed tests
-      if: steps.check_failures.outputs.failed_count > 0 && steps.check_failures.outputs.failed_count < 20
-      run: |
-        cd install
-        cd bin
-        source env.sh
-        # Repeat failed tests for 10 times
-        for i in {1..10}; do
-          ./DRAWEXE -v -c testgrid -regress results/macos-x64-retest -outdir results/macos-x64-retest -parallel 0 -overwrite
-          ./DRAWEXE -v -c "testsummarize results/macos-x64-retest"
-        done
-      shell: bash
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Copy retest results back to original location
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/bin/results/macos-x64-retest
-        if [ "$(ls -A)" ]; then
-          cp -rf * ../macos-x64/
-
-          cd ../../
-          source env.sh
-          ./DRAWEXE -v -c testsummarize results/macos-x64
-        else
-          echo "No retest results to copy - directory is empty"
-        fi
-
-    - name: Upload regression test results
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-macos-x64-retest
-        path: install/bin/results/macos-x64-retest
-        retention-days: 15
-        overwrite: true
-
-    - name: Upload updated test results
-      if: ${{ hashFiles('install/bin/results/macos-x64-retest/*') != '' }}
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-macos-x64
-        path: install/bin/results/macos*-x64/
-        retention-days: 15
-        overwrite: true
-
-    - name: Check test failures
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/bin/results/macos-x64-retest
-        if [ -f tests.log ]; then
-          FAILED_COUNT=$(grep "Total cases:" tests.log | grep -o "[0-9]* FAILED" | awk '{print $1}')
-          if [ ! -z "$FAILED_COUNT" ] && [ $FAILED_COUNT -gt 0 ]; then
-            echo "::error::Number of FAILED tests ($FAILED_COUNT) exceeds threshold of 0"
-            echo "FAILED_COUNT=$FAILED_COUNT" >> $GITHUB_ENV
-            exit 1
-          fi
-          echo "Found $FAILED_COUNT FAILED tests"
-        fi
+        platform: macos
+        compiler: clang
+        install-artifact-name: install-macos-x64
+        results-artifact-name: results-macos-x64
+        test-directory-name: macos-x64
 
   test-macos-gcc-x64:
     name: Test on macOS with GCC (x64)
@@ -1208,68 +339,17 @@ jobs:
     needs: prepare-and-build-macos-gcc-x64
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Install dependencies
-      run: |
-        brew update
-        brew install tcl-tk tbb gl2ps xerces-c \
-                     libxmu libxi libxft libxpm \
-                     glew freeimage draco glfw
-
-    - name: Download test data
-      run: |
-        cd data
-        curl -L -O https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.tar.xz
-        tar -xf opencascade-dataset-7.9.0.tar.xz
-
-    - name: Download and extract install directory
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-macos-gcc-x64
-        path: install
-
-    - name: Set LIBGL_ALWAYS_SOFTWARE environment variable
-      run: echo "LIBGL_ALWAYS_SOFTWARE=1" >> $GITHUB_ENV
-
-    - name: Set execute permissions on DRAWEXE
-      run: chmod +x install/bin/DRAWEXE
-
     - name: Run tests
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -f ${{ github.workspace }}/.github/actions/testgrid/testmacosgcc.tcl
-      shell: bash
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Clean up test results
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -c cleanuptest results/macos-gcc-x64
-      shell: bash
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload test results
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/run-tests
       with:
-        name: results-macos-gcc-x64
-        path: |
-          install/bin/results/**/*.log
-          install/bin/results/**/*.png
-          install/bin/results/**/*.html
-        retention-days: 15
+        platform: macos
+        compiler: gcc
+        install-artifact-name: install-macos-gcc-x64
+        test-directory-name: macos-gcc-x64
+        test-script: .github/actions/testgrid/testmacosgcc.tcl
 
   retest-macos-gcc-x64:
     name: Regression Test on macOS with GCC (x64)
@@ -1279,136 +359,15 @@ jobs:
     steps:
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
-
-    - name: Download previous test results
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: results-macos-gcc-x64
-        path: install/bin/results
-
-    - name: Check for test failures
-      id: check_failures
-      run: |
-        failed_count=0
-        if [ -f "install/bin/results/macos-gcc-x64/tests.log" ]; then
-          total_line=$(grep "Total cases:" install/bin/results/macos-gcc-x64/tests.log)
-          if [ ! -z "$total_line" ]; then
-            if [[ $total_line =~ "FAILED" ]]; then
-              failed_count=$(echo "$total_line" | grep -o "[0-9]* FAILED" | awk '{print $1}')
-            fi
-          fi
-          echo "failed_count=$failed_count" >> $GITHUB_OUTPUT
-          if [ "$failed_count" -gt 0 ]; then
-            echo "Tests failed count: $failed_count"
-          fi
-        fi
-      shell: bash
-
-    - name: Install dependencies
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        brew update
-        brew install tcl-tk tbb gl2ps xerces-c \
-                     libxmu libxi libxft libxpm \
-                     glew draco glfw
-
-    - name: Download test data
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd data
-        curl -L -O https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.tar.xz
-        tar -xf opencascade-dataset-7.9.0.tar.xz
-
-    - name: Download and extract install directory
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/download-artifact@v4.1.7
+      
+    - name: Run retest
+      uses: ./.github/actions/retest-failures
       with:
-        name: install-macos-gcc-x64
-        path: install
-
-    - name: Set LIBGL_ALWAYS_SOFTWARE environment variable
-      if: steps.check_failures.outputs.failed_count > 0
-      run: echo "LIBGL_ALWAYS_SOFTWARE=1" >> $GITHUB_ENV
-
-    - name: Set execute permissions on DRAWEXE
-      if: steps.check_failures.outputs.failed_count > 0
-      run: chmod +x install/bin/DRAWEXE
-
-    - name: Run regression tests
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -c testgrid -regress results/macos-gcc-x64 -outdir results/macos-gcc-x64-retest -parallel 0
-      shell: bash
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Repeating failed tests
-      if: steps.check_failures.outputs.failed_count > 0 && steps.check_failures.outputs.failed_count < 20
-      run: |
-        cd install
-        cd bin
-        source env.sh
-        # Repeat failed tests for 10 times
-        for i in {1..10}; do
-          ./DRAWEXE -v -c testgrid -regress results/macos-gcc-x64-retest -outdir results/macos-gcc-x64-retest -parallel 0 -overwrite
-          ./DRAWEXE -v -c "testsummarize results/macos-gcc-x64-retest"
-        done
-      shell: bash
-      env:
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload regression test results
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-macos-gcc-x64-retest
-        path: install/bin/results/macos-gcc-x64-retest
-        retention-days: 15
-        overwrite: true
-
-    - name: Copy retest results back to original location
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/bin/results/macos-gcc-x64-retest
-        if [ "$(ls -A)" ]; then
-          cp -rf * ../macos-gcc-x64/
-
-          cd ../../
-          source env.sh
-          ./DRAWEXE -v -c testsummarize results/macos-gcc-x64
-        else
-          echo "No retest results to copy - directory is empty"
-        fi
-
-    - name: Upload updated test results
-      if: ${{ hashFiles('install/bin/results/macos-gcc-x64-retest/*') != '' }}
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-macos-gcc-x64
-        path: install/bin/results/macos*-x64/
-        retention-days: 15
-        overwrite: true
-
-    - name: Check test failures
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/bin/results/macos-gcc-x64-retest
-        if [ -f tests.log ]; then
-          FAILED_COUNT=$(grep "Total cases:" tests.log | grep -o "[0-9]* FAILED" | awk '{print $1}')
-          if [ ! -z "$FAILED_COUNT" ] && [ $FAILED_COUNT -gt 0 ]; then
-            echo "::error::Number of FAILED tests ($FAILED_COUNT) exceeds threshold of 0"
-            echo "FAILED_COUNT=$FAILED_COUNT" >> $GITHUB_ENV
-            exit 1
-          fi
-          echo "Found $FAILED_COUNT FAILED tests"
-        fi
+        platform: macos
+        compiler: gcc
+        install-artifact-name: install-macos-gcc-x64
+        results-artifact-name: results-macos-gcc-x64
+        test-directory-name: macos-gcc-x64
 
   test-linux-clang-x64:
     name: Test on Linux with Clang (x64)
@@ -1416,66 +375,17 @@ jobs:
     needs: prepare-and-build-linux-clang-x64
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Install dependencies
-      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake clang make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev fonts-noto-cjk fonts-liberation fonts-ubuntu fonts-liberation fonts-ubuntu fonts-noto-cjk fonts-ipafont-gothic fonts-ipafont-mincho fonts-unfonts-core
-
-    - name: Setup Xvfb and Mesa
-      uses: ./.github/actions/setup-xvfb-mesa
-
-    - name: Download test data
-      run: |
-        cd data
-        wget https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.tar.xz
-        tar -xf opencascade-dataset-7.9.0.tar.xz
-
-    - name: Download and extract install directory
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-linux-clang-x64
-        path: install
-
-    - name: Set execute permissions on DRAWEXE
-      run: chmod +x install/bin/DRAWEXE
-
     - name: Run tests
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -f ${{ github.workspace }}/.github/actions/testgrid/testlinuxclang.tcl
-      shell: bash
-      env:
-        DISPLAY: :99
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Clean up test results
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -c cleanuptest results/linux-clang-x64
-      shell: bash
-      env:
-        DISPLAY: :99
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload test results
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/run-tests
       with:
-        name: results-linux-clang-x64
-        path: |
-          install/bin/results/**/*.log
-          install/bin/results/**/*.png
-          install/bin/results/**/*.html
-        retention-days: 15
+        platform: linux
+        compiler: clang
+        install-artifact-name: install-linux-clang-x64
+        test-directory-name: linux-clang-x64
+        test-script: .github/actions/testgrid/testlinuxclang.tcl
 
   retest-linux-clang-x64:
     name: Regression Test on Linux with Clang (x64)
@@ -1485,134 +395,15 @@ jobs:
     steps:
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
-
-    - name: Download previous test results
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: results-linux-clang-x64
-        path: install/bin/results
-
-    - name: Check for test failures
-      id: check_failures
-      run: |
-        failed_count=0
-        if [ -f "install/bin/results/linux-clang-x64/tests.log" ]; then
-          total_line=$(grep "Total cases:" install/bin/results/linux-clang-x64/tests.log)
-          if [ ! -z "$total_line" ]; then
-            if [[ $total_line =~ "FAILED" ]]; then
-              failed_count=$(echo "$total_line" | grep -o "[0-9]* FAILED" | awk '{print $1}')
-            fi
-          fi
-          echo "failed_count=$failed_count" >> $GITHUB_OUTPUT
-          if [ "$failed_count" -gt 0 ]; then
-            echo "Tests failed count: $failed_count"
-          fi
-        fi
-      shell: bash
-
-    - name: Install dependencies
-      if: steps.check_failures.outputs.failed_count > 0
-      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake clang make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev fonts-noto-cjk fonts-liberation fonts-ubuntu fonts-liberation fonts-ubuntu fonts-noto-cjk fonts-ipafont-gothic fonts-ipafont-mincho fonts-unfonts-core
-
-    - name: Setup Xvfb and Mesa
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: ./.github/actions/setup-xvfb-mesa
-
-    - name: Download test data
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd data
-        wget https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.tar.xz
-        tar -xf opencascade-dataset-7.9.0.tar.xz
-
-    - name: Download and extract install directory
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-linux-clang-x64
-        path: install
-
-    - name: Set execute permissions on DRAWEXE
-      if: steps.check_failures.outputs.failed_count > 0
-      run: chmod +x install/bin/DRAWEXE
-
-    - name: Run regression tests
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -c testgrid -regress results/linux-clang-x64 -outdir results/linux-clang-x64-retest -parallel 0
-      shell: bash
-      env:
-        DISPLAY: :99
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Repeating failed tests
-      if: steps.check_failures.outputs.failed_count > 0 && steps.check_failures.outputs.failed_count < 20
-      run: |
-        cd install
-        cd bin
-        source env.sh
-        # Repeat failed tests for 10 times
-        for i in {1..10}; do
-          ./DRAWEXE -v -c testgrid -regress results/linux-clang-x64-retest -outdir results/linux-clang-x64-retest -parallel 0 -overwrite
-          ./DRAWEXE -v -c "testsummarize results/linux-clang-x64-retest"
-        done
-      shell: bash
-      env:
-        DISPLAY: :99
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload regression test results
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-linux-clang-x64-retest
-        path: install/bin/results/linux-clang-x64-retest
-        retention-days: 15
-        overwrite: true
-
-    - name: Copy retest results back to original location
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/bin/results/linux-clang-x64-retest
-        if [ "$(ls -A)" ]; then
-          cp -rf * ../linux-clang-x64/
-
-          cd ../../
-          source env.sh
-          ./DRAWEXE -v -c testsummarize results/linux-clang-x64
-        else
-          echo "No retest results to copy - directory is empty"
-        fi
-
-    - name: Upload updated test results
-      if: ${{ hashFiles('install/bin/results/linux-clang-x64-retest/*') != '' }}
-      uses: actions/upload-artifact@v4.4.3
+      
+    - name: Run retest
+      uses: ./.github/actions/retest-failures
       with:
-        name: results-linux-clang-x64
-        path: install/bin/results/linux*-x64/
-        retention-days: 15
-        overwrite: true
-
-    - name: Check test failures
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/bin/results/linux-clang-x64-retest
-        if [ -f tests.log ]; then
-          FAILED_COUNT=$(grep "Total cases:" tests.log | grep -o "[0-9]* FAILED" | awk '{print $1}')
-          if [ ! -z "$FAILED_COUNT" ] && [ $FAILED_COUNT -gt 0 ]; then
-            echo "::error::Number of FAILED tests ($FAILED_COUNT) exceeds threshold of 0"
-            echo "FAILED_COUNT=$FAILED_COUNT" >> $GITHUB_ENV
-            exit 1
-          fi
-          echo "Found $FAILED_COUNT FAILED tests"
-        fi
+        platform: linux
+        compiler: clang
+        install-artifact-name: install-linux-clang-x64
+        results-artifact-name: results-linux-clang-x64
+        test-directory-name: linux-clang-x64
 
   test-linux-gcc-x64:
     name: Test on Linux with GCC (x64)
@@ -1620,66 +411,17 @@ jobs:
     needs: prepare-and-build-linux-gcc-x64
 
     steps:
-
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
 
-    - name: Install dependencies
-      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake gcc g++ make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev fonts-noto-cjk fonts-liberation fonts-ubuntu fonts-liberation fonts-ubuntu fonts-noto-cjk fonts-ipafont-gothic fonts-ipafont-mincho fonts-unfonts-core
-
-    - name: Setup Xvfb and Mesa
-      uses: ./.github/actions/setup-xvfb-mesa
-
-    - name: Download test data
-      run: |
-        cd data
-        wget https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.tar.xz
-        tar -xf opencascade-dataset-7.9.0.tar.xz
-
-    - name: Download and extract install directory
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-linux-gcc-x64
-        path: install
-
-    - name: Set execute permissions on DRAWEXE
-      run: chmod +x install/bin/DRAWEXE
-
     - name: Run tests
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -f ${{ github.workspace }}/.github/actions/testgrid/testlinuxgcc.tcl
-      shell: bash
-      env:
-        DISPLAY: :99
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Clean up test results
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -c cleanuptest results/linux-gcc-x64
-      shell: bash
-      env:
-        DISPLAY: :99
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload test results
-      uses: actions/upload-artifact@v4.4.3
+      uses: ./.github/actions/run-tests
       with:
-        name: results-linux-gcc-x64
-        path: |
-          install/bin/results/**/*.log
-          install/bin/results/**/*.png
-          install/bin/results/**/*.html
-        retention-days: 15
+        platform: linux
+        compiler: gcc
+        install-artifact-name: install-linux-gcc-x64
+        test-directory-name: linux-gcc-x64
+        test-script: .github/actions/testgrid/testlinuxgcc.tcl
 
   retest-linux-gcc-x64:
     name: Regression Test on Linux with GCC (x64)
@@ -1689,134 +431,15 @@ jobs:
     steps:
     - name: Checkout repository
       uses: actions/checkout@v4.1.7
-
-    - name: Download previous test results
-      uses: actions/download-artifact@v4.1.7
+      
+    - name: Run retest
+      uses: ./.github/actions/retest-failures
       with:
-        name: results-linux-gcc-x64
-        path: install/bin/results
-
-    - name: Check for test failures
-      id: check_failures
-      run: |
-        failed_count=0
-        if [ -f "install/bin/results/linux-gcc-x64/tests.log" ]; then
-          total_line=$(grep "Total cases:" install/bin/results/linux-gcc-x64/tests.log)
-          if [ ! -z "$total_line" ]; then
-            if [[ $total_line =~ "FAILED" ]]; then
-              failed_count=$(echo "$total_line" | grep -o "[0-9]* FAILED" | awk '{print $1}')
-            fi
-          fi
-          echo "failed_count=$failed_count" >> $GITHUB_OUTPUT
-          if [ "$failed_count" -gt 0 ]; then
-            echo "Tests failed count: $failed_count"
-          fi
-        fi
-      shell: bash
-
-    - name: Install dependencies
-      if: steps.check_failures.outputs.failed_count > 0
-      run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake gcc g++ make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev fonts-noto-cjk fonts-liberation fonts-ubuntu fonts-liberation fonts-ubuntu fonts-noto-cjk fonts-ipafont-gothic fonts-ipafont-mincho fonts-unfonts-core
-
-    - name: Setup Xvfb and Mesa
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: ./.github/actions/setup-xvfb-mesa
-
-    - name: Download test data
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd data
-        wget https://github.com/Open-Cascade-SAS/OCCT/releases/download/V7_9_0_beta1/opencascade-dataset-7.9.0.tar.xz
-        tar -xf opencascade-dataset-7.9.0.tar.xz
-
-    - name: Download and extract install directory
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/download-artifact@v4.1.7
-      with:
-        name: install-linux-gcc-x64
-        path: install
-
-    - name: Set execute permissions on DRAWEXE
-      if: steps.check_failures.outputs.failed_count > 0
-      run: chmod +x install/bin/DRAWEXE
-
-    - name: Run regression tests
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-         cd install
-         cd bin
-         source env.sh
-         ./DRAWEXE -v -c testgrid -regress results/linux-gcc-x64 -outdir results/linux-gcc-x64-retest -parallel 0
-      shell: bash
-      env:
-        DISPLAY: :99
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Repeating failed tests
-      if: steps.check_failures.outputs.failed_count > 0 && steps.check_failures.outputs.failed_count < 20
-      run: |
-        cd install
-        cd bin
-        source env.sh
-        # Repeat failed tests for 10 times
-        for i in {1..10}; do
-          ./DRAWEXE -v -c testgrid -regress results/linux-gcc-x64-retest -outdir results/linux-gcc-x64-retest -parallel 0 -overwrite
-          ./DRAWEXE -v -c "testsummarize results/linux-gcc-x64-retest"
-        done
-      shell: bash
-      env:
-        DISPLAY: :99
-        LIBGL_ALWAYS_SOFTWARE: 1
-        CSF_TestScriptsPath: ${{ github.workspace }}/tests
-        CSF_TestDataPath: ${{ github.workspace }}/data
-
-    - name: Upload regression test results
-      if: steps.check_failures.outputs.failed_count > 0
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-linux-gcc-x64-retest
-        path: install/bin/results/linux-gcc-x64-retest
-        retention-days: 15
-        overwrite: true
-
-    - name: Copy retest results back to original location
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/bin/results/linux-gcc-x64-retest
-        if [ "$(ls -A)" ]; then
-          cp -rf * ../linux-gcc-x64/
-
-          cd ../../
-          source env.sh
-          ./DRAWEXE -v -c testsummarize results/linux-gcc-x64
-        else
-          echo "No retest results to copy - directory is empty"
-        fi
-
-    - name: Upload updated test results
-      if: ${{ hashFiles('install/bin/results/linux-gcc-x64-retest/*') != '' }}
-      uses: actions/upload-artifact@v4.4.3
-      with:
-        name: results-linux-gcc-x64
-        path: install/bin/results/linux*-x64/
-        retention-days: 15
-        overwrite: true
-
-    - name: Check test failures
-      if: steps.check_failures.outputs.failed_count > 0
-      run: |
-        cd install/bin/results/linux-gcc-x64-retest
-        if [ -f tests.log ]; then
-          FAILED_COUNT=$(grep "Total cases:" tests.log | grep -o "[0-9]* FAILED" | awk '{print $1}')
-          if [ ! -z "$FAILED_COUNT" ] && [ $FAILED_COUNT -gt 0 ]; then
-            echo "::error::Number of FAILED tests ($FAILED_COUNT) exceeds threshold of 0"
-            echo "FAILED_COUNT=$FAILED_COUNT" >> $GITHUB_ENV
-            exit 1
-          fi
-          echo "Found $FAILED_COUNT FAILED tests"
-        fi
+        platform: linux
+        compiler: gcc
+        install-artifact-name: install-linux-gcc-x64
+        results-artifact-name: results-linux-gcc-x64
+        test-directory-name: linux-gcc-x64
 
   run-gtest-windows-x64:
     name: Run GTest on Windows with MSVC (x64)
@@ -1929,83 +552,6 @@ jobs:
     steps:
       - name: Checkout repository
         uses: actions/checkout@v4.1.7
-
-      - name: Install dependencies
-        run: sudo apt-get update && sudo apt-get install -y tcl-dev tk-dev cmake gcc g++ make libbtbb-dev libx11-dev libglu1-mesa-dev tcllib tcl-thread tcl libvtk9-dev libopenvr-dev libdraco-dev libfreeimage-dev libegl1-mesa-dev libgles2-mesa-dev libfreetype-dev
-
-      - name: Setup Xvfb and Mesa
-        uses: ./.github/actions/setup-xvfb-mesa
-
-      - name: Set environment variables
-        run: |
-          echo "DISPLAY=:99" >> $GITHUB_ENV
-          echo "LIBGL_ALWAYS_SOFTWARE=1" >> $GITHUB_ENV
-
-      - name: Download and extract install directory
-        uses: actions/download-artifact@v4.1.7
-        with:
-          name: install-linux-gcc-x64
-          path: install
-
-      - name: Set execute permissions on DRAWEXE
-        run: chmod +x install/bin/DRAWEXE
-
-      - name: Get latest workflow run ID from target branch
-        id: get_run_id
-        run: |
-          response=$(curl -s \
-            -H "Accept: application/vnd.github.v3+json" \
-            "https://api.github.com/repos/${{ github.repository }}/actions/runs?branch=${{ github.event.pull_request.base.ref }}&status=success")
-          latest_run_id=$(echo "$response" | jq -r '.workflow_runs[] | select(.name=="Build and Test OCCT on Multiple Platforms") | .id' | head -n 1)
-          echo "latest_run_id=$latest_run_id" >> $GITHUB_ENV
-
-      - name: Download master branch test results
-        env:
-          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        run: |
-          for platform in windows-x64 windows-clang-x64 macos-x64 macos-gcc-x64 linux-clang-x64 linux-gcc-x64; do
-            echo "Downloading results for $platform"
-            gh run download ${{ env.latest_run_id }} -n "results-$platform" -D "install/bin/results/master/"
-          done
-
-      - name: Download current branch test results
-        env:
-          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-        run: |
-          for platform in windows-x64 windows-clang-x64 macos-x64 macos-gcc-x64 linux-clang-x64 linux-gcc-x64; do
-            echo "Downloading results for $platform"
-            gh run download -n "results-$platform" -D "install/bin/results/current/"
-          done
-
-      - name: Compare test results
-        run: |
-          echo "Comparing test results..."
-          cd install/bin
-          source env.sh
-          for platform in windows-x64 windows-clang-x64 macos-x64 macos-gcc-x64 linux-clang-x64 linux-gcc-x64; do
-            ./DRAWEXE -v -c testdiff "results/current/$platform" "results/master/$platform" &
-          done
-          wait
-
-      - name: Install BeautifulSoup
-        run: pip install beautifulsoup4
-
-      - name: Clean unused test images
-        run: |
-          # copy to the install/bin/results directory
-          cp ${{ github.workspace }}/.github/actions/scripts/cleanup_test_images.py install/bin/results
-          cd install/bin/results
-          python cleanup_test_images.py
-
-      - name: Upload comparison results
-        uses: actions/upload-artifact@v4.4.3
-        with:
-          name: test-compare-results
-          retention-days: 15
-          overwrite: true
-          path: |
-            install/bin/results/**/diff-*.html
-            install/bin/results/**/diff-*.log
-            install/bin/results/**/summary.html
-            install/bin/results/**/tests.log
-            install/bin/results/**/*.png
+      
+      - name: Generate Test Summary
+        uses: ./.github/actions/test-summary