diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e862867b3d..95deb813f0 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -68,6 +68,10 @@ jobs: with: fetch-depth: 0 + - name: Fix workspace ownership + run: | + chown -R $(id -u):$(id -g) "$GITHUB_WORKSPACE" + - name: Install dependencies uses: ./.github/actions/install_unix_deps continue-on-error: false @@ -342,46 +346,25 @@ jobs: cd "${{ steps.install-root.outputs.INSTALL_ROOT }}" "$GITHUB_WORKSPACE/.venv/Scripts/pytest" -v --cov=./cuda --cov-append --cov-context=test --cov-config="$GITHUB_WORKSPACE/.coveragerc" "$GITHUB_WORKSPACE/cuda_pathfinder/tests" + # Cython linetrace under coverage on Windows needs more stack than the + # default 1 MB thread size. The helper runs pytest on an 8 MB thread. - name: Run cuda.bindings tests (with 8MB stack) continue-on-error: true run: | - cd "${{ steps.install-root.outputs.INSTALL_ROOT }}" - # Run pytest in 8MB stack thread (Cython linetrace requirement) - "$GITHUB_WORKSPACE/.venv/Scripts/python" << PYTEST_EOF - import os - import sys - import threading - import pytest - - os.chdir(r'${{ steps.install-root.outputs.INSTALL_ROOT }}') - threading.stack_size(8 * 1024 * 1024) - result = {'code': 1} - - def _run(): - workspace = os.environ['GITHUB_WORKSPACE'] - result['code'] = pytest.main([ - '-v', - '--cov=./cuda', - '--cov-append', - '--cov-context=test', - f'--cov-config={workspace}/.coveragerc', - f'{workspace}/cuda_bindings/tests' - ]) - - t = threading.Thread(target=_run) - t.start() - t.join() - - print(f'Bindings tests exit code: {result["code"]}') - # Exit with actual code (continue-on-error handles it) - sys.exit(result['code']) - PYTEST_EOF + "$GITHUB_WORKSPACE/.venv/Scripts/python" "$GITHUB_WORKSPACE/ci/tools/run_pytest_with_stack.py" \ + --cwd "${{ steps.install-root.outputs.INSTALL_ROOT }}" \ + -v --cov=./cuda --cov-append --cov-context=test \ + --cov-config="$GITHUB_WORKSPACE/.coveragerc" \ + "$GITHUB_WORKSPACE/cuda_bindings/tests" - - name: Run cuda.core tests + - name: Run cuda.core tests (with 8MB stack) continue-on-error: true run: | - cd "${{ steps.install-root.outputs.INSTALL_ROOT }}" - "$GITHUB_WORKSPACE/.venv/Scripts/pytest" -v --cov=./cuda --cov-append --cov-context=test --cov-config="$GITHUB_WORKSPACE/.coveragerc" "$GITHUB_WORKSPACE/cuda_core/tests" + "$GITHUB_WORKSPACE/.venv/Scripts/python" "$GITHUB_WORKSPACE/ci/tools/run_pytest_with_stack.py" \ + --cwd "${{ steps.install-root.outputs.INSTALL_ROOT }}" \ + -v --cov=./cuda --cov-append --cov-context=test \ + --cov-config="$GITHUB_WORKSPACE/.coveragerc" \ + "$GITHUB_WORKSPACE/cuda_core/tests" - name: Copy Windows coverage file to workspace run: | diff --git a/ci/tools/run_pytest_with_stack.py b/ci/tools/run_pytest_with_stack.py new file mode 100644 index 0000000000..e1e1f782ba --- /dev/null +++ b/ci/tools/run_pytest_with_stack.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +"""Run pytest on a thread with a larger stack size. + +Cython linetrace instrumentation under coverage on Windows can exceed the +default 1 MB thread stack. This helper spawns a single worker thread with +a configurable stack (default 8 MB) so the rest of the CI workflow stays +readable. + +Usage: + python run_pytest_with_stack.py [--stack-mb N] [--cwd DIR] [pytest args ...] +""" + +import argparse +import concurrent.futures +import os +import sys +import threading + +import pytest + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--stack-mb", + type=int, + default=8, + help="Thread stack size in megabytes (default: 8)", + ) + parser.add_argument( + "--cwd", + default=None, + help="Working directory for the test run", + ) + args, pytest_args = parser.parse_known_args() + + if args.cwd: + os.chdir(args.cwd) + + threading.stack_size(args.stack_mb * 1024 * 1024) + + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + code = pool.submit(pytest.main, pytest_args).result() + + sys.exit(code) + + +if __name__ == "__main__": + main()