name: 'Pytest' description: 'Run pytest on pre-built container images' inputs: pytest_marks: description: 'Pytest marks' required: true default: 'e2e and vllm and gpu_1 and not slow' image_tag: description: 'Image Tag to run tests on' required: true cpu_limit: description: 'Maximum number of cores available to docker' required: false default: '10' framework: description: 'Framework name for test metrics' required: false default: 'unknown' test_type: description: 'Test type (unit, e2e, integration)' required: false default: 'e2e' platform_arch: description: 'Platform architecture (amd64, arm64)' required: false default: 'amd64' dry_run: description: 'Run pytest in dry-run mode (collect tests only, do not execute)' required: false default: 'false' runs: using: "composite" steps: - name: Setup Test Environment shell: bash run: | # Setup test directories mkdir -p test-results # Set platform architecture from input PLATFORM_ARCH="${{ inputs.platform_arch }}" if [[ -z "${PLATFORM_ARCH}" ]]; then PLATFORM_ARCH="amd64" fi echo "PLATFORM_ARCH=${PLATFORM_ARCH}" >> $GITHUB_ENV echo "๐Ÿ—๏ธ Platform architecture: ${PLATFORM_ARCH}" - name: Run tests shell: bash env: NUM_CPUS: ${{ inputs.cpu_limit }} CONTAINER_ID: test_${{ github.run_id }}_${{ github.run_attempt }}_${{ github.job }} PYTEST_XML_FILE: pytest_test_report.xml HF_HOME: /runner/_work/_temp run: | # Run pytest with detailed output and JUnit XML set +e # Don't exit on test failures # Determine docker runtime flags and pytest command based on dry_run mode if [[ "${{ inputs.dry_run }}" == "true" ]]; then echo "๐Ÿ” Running pytest in dry-run mode (collect-only, no GPU required)" GPU_FLAGS="" PYTEST_CMD="pytest -v --collect-only -m \"${{ inputs.pytest_marks }}\"" else echo "๐Ÿš€ Running pytest in normal mode" PYTEST_CMD="pytest -v --tb=short --basetemp=/tmp -o cache_dir=/tmp/.pytest_cache --junitxml=/workspace/test-results/${{ env.PYTEST_XML_FILE }} --durations=10 -m \"${{ inputs.pytest_marks }}\"" # Detect GPU availability and conditionally add GPU flags GPU_FLAGS="" if command -v nvidia-smi &> /dev/null && nvidia-smi &> /dev/null; then echo "โœ“ GPU detected, enabling GPU runtime" GPU_FLAGS="--runtime=nvidia --gpus all" else echo "โš ๏ธ No GPU detected, running in CPU-only mode" fi fi # Get absolute path for test-results directory and ensure it has proper permissions TEST_RESULTS_DIR="$(pwd)/test-results" chmod 777 "${TEST_RESULTS_DIR}" echo "๐Ÿ“ Test results will be saved to: ${TEST_RESULTS_DIR}" docker run ${GPU_FLAGS} --rm -w /workspace \ --cpus=${NUM_CPUS} \ --network host \ --name ${{ env.CONTAINER_ID }}_pytest \ -v "${TEST_RESULTS_DIR}:/workspace/test-results" \ ${{ inputs.image_tag }} \ bash -c "${PYTEST_CMD}" TEST_EXIT_CODE=$? echo "TEST_EXIT_CODE=${TEST_EXIT_CODE}" >> $GITHUB_ENV echo "๐Ÿงช Tests completed with exit code: ${TEST_EXIT_CODE}" # Verify test results were written (only in normal mode) if [[ "${{ inputs.dry_run }}" != "true" ]]; then if [[ -f "${TEST_RESULTS_DIR}/${{ env.PYTEST_XML_FILE }}" ]]; then echo "โœ… Test results file found: ${TEST_RESULTS_DIR}/${{ env.PYTEST_XML_FILE }}" else echo "โš ๏ธ Test results file not found: ${TEST_RESULTS_DIR}/${{ env.PYTEST_XML_FILE }}" fi fi # Always continue to results processing exit 0 - name: Process Test Results shell: bash run: | # Sanitize test_type for filenames (always set this for artifact upload) # Remove commas and spaces from test_type for use in filenames STR_TEST_TYPE=$(echo "${{ inputs.test_type }}" | tr ', ' '_') echo "STR_TEST_TYPE=${STR_TEST_TYPE}" >> $GITHUB_ENV # Check for JUnit XML file and determine test status JUNIT_FILE="test-results/pytest_test_report.xml" if [[ -f "$JUNIT_FILE" ]]; then echo "โœ… JUnit XML generated successfully" # Extract basic test counts for status determination TOTAL_TESTS=$(grep -o 'tests="[0-9]*"' "$JUNIT_FILE" | grep -o '[0-9]*' | head -1 || echo "0") FAILED_TESTS=$(grep -o 'failures="[0-9]*"' "$JUNIT_FILE" | grep -o '[0-9]*' | head -1 || echo "0") ERROR_TESTS=$(grep -o 'errors="[0-9]*"' "$JUNIT_FILE" | grep -o '[0-9]*' | head -1 || echo "0") echo "๐Ÿ“Š ${TOTAL_TESTS} tests completed (${FAILED_TESTS} failed, ${ERROR_TESTS} errors)" # Rename XML file to unique name JUNIT_NAME="pytest_test_report_${{ inputs.framework }}_${STR_TEST_TYPE}_${{ inputs.platform_arch }}_${{ github.run_id }}_${{ job.check_run_id }}.xml" mv "$JUNIT_FILE" "test-results/$JUNIT_NAME" echo "๐Ÿ“ Renamed XML file to: $JUNIT_NAME" else echo "โš ๏ธ JUnit XML file not found - test results may not be available for upload" TOTAL_TESTS=0 FAILED_TESTS=1 # Treat missing XML as failure ERROR_TESTS=0 fi # Exit with original test result to maintain workflow behavior exit ${TEST_EXIT_CODE} - name: Upload Test Results uses: actions/upload-artifact@v4 if: always() # Always upload test results, even if tests failed with: name: test-results-${{ inputs.framework }}-${{ env.STR_TEST_TYPE }}-${{ env.PLATFORM_ARCH }}-${{ github.run_id }}-${{ job.check_run_id }} path: test-results/pytest_test_report_${{ inputs.framework }}_${{ env.STR_TEST_TYPE }}_${{ inputs.platform_arch }}_${{ github.run_id }}_${{ job.check_run_id }}.xml retention-days: 7