77 description : " Target branch to compare against (e.g., main)."
88 required : true
99 type : string
10+ # Python/pytest options
1011 python-version :
1112 description : " Python version for pytest."
1213 required : false
1314 type : string
1415 default : " 3.10"
16+ # Node.js options for Jest/Mocha
1517 node-version :
1618 description : " Node.js version for Jest/Mocha."
1719 required : false
1820 type : string
1921 default : " 18"
22+ # C++/CMake options
23+ cmake-version :
24+ description : " CMake version for C++ tests."
25+ required : false
26+ type : string
27+ default : " 3.28"
28+ cpp-compiler :
29+ description : " C++ compiler (gcc, clang). Auto-detects if empty."
30+ required : false
31+ type : string
32+ default : " "
33+ cpp-build-type :
34+ description : " CMake build type (Debug, Release, RelWithDebInfo, MinSizeRel)."
35+ required : false
36+ type : string
37+ default : " Release"
38+ cpp-build-dir :
39+ description : " Build directory for C++ projects."
40+ required : false
41+ type : string
42+ default : " build"
43+ cpp-cmake-args :
44+ description : " Additional CMake configuration arguments."
45+ required : false
46+ type : string
47+ default : " "
48+ cpp-test-args :
49+ description : " Additional CTest arguments."
50+ required : false
51+ type : string
52+ default : " "
53+ # Common options
2054 runs_on :
2155 description : " Runner label."
2256 required : false
2761 required : false
2862 type : string
2963 default : " "
64+ use_target_cache :
65+ description : " Whether to use caching for target branch test results. When false, always runs fresh tests."
66+ required : false
67+ type : boolean
68+ default : false
69+ # Jest options
3070 jest-command :
3171 description : " Base command used to invoke Jest."
3272 required : false
3777 required : false
3878 type : string
3979 default : " "
80+ # Mocha options
4081 mocha-command :
4182 description : " Base command used to invoke Mocha."
4283 required : false
64105 regression_count :
65106 description : " Total number of regressions (all frameworks)"
66107 value : ${{ jobs.aggregate-results.outputs.regression_count }}
108+ cpp_has_regressions :
109+ description : " Whether regressions were detected (C++)"
110+ value : ${{ jobs.cpp-compare.outputs.has_regressions }}
111+ cpp_regression_count :
112+ description : " Number of regressions (C++)"
113+ value : ${{ jobs.cpp-compare.outputs.regression_count }}
67114
68115jobs :
69116 # Detect which test frameworks are present
73120 has_pytest : ${{ steps.detect.outputs.has_pytest }}
74121 has_jest : ${{ steps.detect.outputs.has_jest }}
75122 has_mocha : ${{ steps.detect.outputs.has_mocha }}
123+ has_cpp : ${{ steps.detect.outputs.has_cpp }}
76124 steps :
77125 - uses : actions/checkout@v4.2.2
78126 - name : Detect test frameworks
@@ -81,11 +129,30 @@ jobs:
81129 # Detect pytest
82130 if [ -f "pyproject.toml" ] || [ -f "setup.py" ] || [ -f "requirements.txt" ] || find . -name "test_*.py" -o -name "*_test.py" 2>/dev/null | head -1 | grep -q .; then
83131 echo "has_pytest=true" >> $GITHUB_OUTPUT
84- echo "Detected: pytest"
132+ echo "✅ Detected: pytest"
85133 else
86134 echo "has_pytest=false" >> $GITHUB_OUTPUT
87135 fi
88136
137+ # Detect C++ with CMake and tests
138+ HAS_CPP="false"
139+ if [ -f "CMakeLists.txt" ]; then
140+ # Check for test-related CMake content
141+ if grep -rqE "(enable_testing|add_test|gtest|catch|boost.*test)" CMakeLists.txt 2>/dev/null || \
142+ find . -name "CMakeLists.txt" -exec grep -lE "(enable_testing|add_test|gtest|catch)" {} \; 2>/dev/null | head -1 | grep -q .; then
143+ HAS_CPP="true"
144+ echo "✅ Detected: C++ (CMake with tests)"
145+ fi
146+ fi
147+ # Check for test source files
148+ if [ "$HAS_CPP" = "false" ]; then
149+ if find . \( -name "*_test.cpp" -o -name "*_test.cc" -o -name "test_*.cpp" -o -name "test_*.cc" \) 2>/dev/null | head -1 | grep -q .; then
150+ HAS_CPP="true"
151+ echo "✅ Detected: C++ test files"
152+ fi
153+ fi
154+ echo "has_cpp=$HAS_CPP" >> $GITHUB_OUTPUT
155+
89156 # Detect Jest
90157 HAS_JEST="false"
91158 if [ -f "package.json" ]; then
@@ -104,7 +171,7 @@ jobs:
104171 fi
105172 echo "has_jest=$HAS_JEST" >> $GITHUB_OUTPUT
106173 if [ "$HAS_JEST" = "true" ]; then
107- echo "Detected: Jest"
174+ echo "✅ Detected: Jest"
108175 fi
109176
110177 # Detect Mocha
@@ -125,7 +192,7 @@ jobs:
125192 fi
126193 echo "has_mocha=$HAS_MOCHA" >> $GITHUB_OUTPUT
127194 if [ "$HAS_MOCHA" = "true" ]; then
128- echo "Detected: Mocha"
195+ echo "✅ Detected: Mocha"
129196 fi
130197
131198 # ==================== PYTEST ====================
@@ -290,9 +357,69 @@ jobs:
290357 current_no_tests_found : ${{ needs.mocha-source.outputs.no_tests_found }}
291358 artifact_name : regression_mocha_${{ github.event.pull_request.number || github.run_id }}
292359
360+ # ==================== C++ (GTest/CTest) ====================
361+ # Test C++ source branch
362+ cpp-source :
363+ needs : detect-frameworks
364+ if : needs.detect-frameworks.outputs.has_cpp == 'true'
365+ uses : ./.github/workflows/test-cpp-gtest.yml
366+ with :
367+ ref : " " # Default checkout = PR branch
368+ cmake-version : ${{ inputs.cmake-version }}
369+ compiler : ${{ inputs.cpp-compiler }}
370+ build-type : ${{ inputs.cpp-build-type }}
371+ build-dir : ${{ inputs.cpp-build-dir }}
372+ cmake-args : ${{ inputs.cpp-cmake-args }}
373+ test-args : ${{ inputs.cpp-test-args }}
374+ runs_on : ${{ inputs.runs_on }}
375+ artifact_name : cpp_source_${{ github.event.pull_request.number || github.run_id }}
376+ parallel_workers : ${{ inputs.parallel_workers }}
377+
378+ # Test C++ target branch
379+ cpp-target :
380+ needs : detect-frameworks
381+ if : needs.detect-frameworks.outputs.has_cpp == 'true'
382+ uses : ./.github/workflows/test-cpp-gtest.yml
383+ with :
384+ ref : ${{ inputs.target_branch }}
385+ cmake-version : ${{ inputs.cmake-version }}
386+ compiler : ${{ inputs.cpp-compiler }}
387+ build-type : ${{ inputs.cpp-build-type }}
388+ build-dir : ${{ inputs.cpp-build-dir }}
389+ cmake-args : ${{ inputs.cpp-cmake-args }}
390+ test-args : ${{ inputs.cpp-test-args }}
391+ runs_on : ${{ inputs.runs_on }}
392+ artifact_name : cpp_target_${{ github.event.pull_request.number || github.run_id }}
393+ parallel_workers : ${{ inputs.parallel_workers }}
394+
395+ # Compare C++ results
396+ cpp-compare :
397+ needs : [detect-frameworks, cpp-source, cpp-target]
398+ if : always() && needs.detect-frameworks.outputs.has_cpp == 'true' && needs.cpp-source.result == 'success'
399+ uses : ./.github/workflows/regression-test.yml
400+ with :
401+ runs_on : ${{ inputs.runs_on }}
402+ baseline_label : ${{ inputs.target_branch }}
403+ baseline_results_artifact : cpp_target_${{ github.event.pull_request.number || github.run_id }}
404+ baseline_results_filename : test_data.json
405+ current_label : ${{ github.head_ref || github.ref_name }}
406+ current_results_artifact : cpp_source_${{ github.event.pull_request.number || github.run_id }}
407+ current_results_filename : test_data.json
408+ baseline_passed : ${{ needs.cpp-target.outputs.passed }}
409+ baseline_total : ${{ needs.cpp-target.outputs.total }}
410+ baseline_percentage : ${{ needs.cpp-target.outputs.percentage }}
411+ current_passed : ${{ needs.cpp-source.outputs.passed }}
412+ current_total : ${{ needs.cpp-source.outputs.total }}
413+ current_percentage : ${{ needs.cpp-source.outputs.percentage }}
414+ baseline_collection_errors : ${{ needs.cpp-target.outputs.collection_errors }}
415+ baseline_no_tests_found : ${{ needs.cpp-target.outputs.no_tests_found }}
416+ current_collection_errors : ${{ needs.cpp-source.outputs.collection_errors }}
417+ current_no_tests_found : ${{ needs.cpp-source.outputs.no_tests_found }}
418+ artifact_name : regression_cpp_${{ github.event.pull_request.number || github.run_id }}
419+
293420 # ==================== AGGREGATE RESULTS ====================
294421 aggregate-results :
295- needs : [detect-frameworks, pytest-compare, jest-compare, mocha-compare]
422+ needs : [detect-frameworks, pytest-compare, jest-compare, mocha-compare, cpp-compare ]
296423 if : always()
297424 runs-on : ${{ fromJSON(inputs.runs_on) }}
298425 outputs :
@@ -335,13 +462,23 @@ jobs:
335462 fi
336463 fi
337464
465+ # Check C++
466+ if [ "${{ needs.detect-frameworks.outputs.has_cpp }}" == "true" ]; then
467+ CPP_REGRESSIONS="${{ needs.cpp-compare.outputs.regression_count || '0' }}"
468+ if [ "${{ needs.cpp-compare.outputs.has_regressions }}" == "true" ]; then
469+ HAS_REGRESSIONS="true"
470+ TOTAL_REGRESSIONS=$((TOTAL_REGRESSIONS + CPP_REGRESSIONS))
471+ echo "C++ regressions: $CPP_REGRESSIONS"
472+ fi
473+ fi
474+
338475 echo "has_regressions=$HAS_REGRESSIONS" >> $GITHUB_OUTPUT
339476 echo "regression_count=$TOTAL_REGRESSIONS" >> $GITHUB_OUTPUT
340477 echo "Total regressions across all frameworks: $TOTAL_REGRESSIONS"
341478
342479 # ==================== NOTIFICATIONS ====================
343480 notify :
344- needs : [detect-frameworks, pytest-source, pytest-target, pytest-compare, jest-source, jest-target, jest-compare, mocha-source, mocha-target, mocha-compare, aggregate-results]
481+ needs : [detect-frameworks, pytest-source, pytest-target, pytest-compare, jest-source, jest-target, jest-compare, mocha-source, mocha-target, mocha-compare, cpp-source, cpp-target, cpp-compare, aggregate-results]
345482 if : always() && needs.aggregate-results.outputs.has_regressions == 'true'
346483 runs-on : ${{ fromJSON(inputs.runs_on) }}
347484 steps :
@@ -356,7 +493,7 @@ jobs:
356493
357494 MSG="**Test Regression Alert**\n"
358495 MSG+="PR #${{ github.event.pull_request.number }}: ${{ github.event.pull_request.title }}\n"
359- MSG+="\`${{ github.head_ref }}\` -> \`${{ inputs.target_branch }}\`\n\n"
496+ MSG+="\`${{ github.head_ref }}\` → \`${{ inputs.target_branch }}\`\n\n"
360497
361498 # Pytest results
362499 if [ "${{ needs.detect-frameworks.outputs.has_pytest }}" == "true" ]; then
@@ -391,6 +528,17 @@ jobs:
391528 MSG+="\n"
392529 fi
393530
531+ # C++ results
532+ if [ "${{ needs.detect-frameworks.outputs.has_cpp }}" == "true" ]; then
533+ MSG+="**C++:**\n"
534+ MSG+=" Source: ${{ needs.cpp-source.outputs.passed }}/${{ needs.cpp-source.outputs.total }}\n"
535+ MSG+=" Target: ${{ needs.cpp-target.outputs.passed }}/${{ needs.cpp-target.outputs.total }}\n"
536+ if [ "${{ needs.cpp-compare.outputs.has_regressions }}" == "true" ]; then
537+ MSG+=" Regressions: ${{ needs.cpp-compare.outputs.regression_count }}\n"
538+ fi
539+ MSG+="\n"
540+ fi
541+
394542 MSG+="Total Regressions: ${{ needs.aggregate-results.outputs.regression_count }}\n\n"
395543 MSG+="[View Run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
396544
0 commit comments