Skip to content

Update run_tests.sh #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/all-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,24 @@ jobs:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
- name: Checkout repository (full history)
uses: actions/checkout@v2
with:
fetch-depth: 0 # ← fetch all branches and commits

- name: Ensure master/main exist locally
run: |
# Try to fetch both master and main so that one of them is guaranteed to exist
git fetch origin master:master || true
git fetch origin main:main || true

- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.x'

- name: Install CMake and Make
run: sudo apt-get install -y cmake make
run: sudo apt-get update && sudo apt-get install -y cmake make

- name: Run tests
run: |
Expand Down
238 changes: 149 additions & 89 deletions run_tests.sh
Original file line number Diff line number Diff line change
@@ -1,150 +1,210 @@
#!/bin/bash

set -e

# Determine base branch by looking at the upstream. If no upstream is set, fall back to "master".
detect_base_branch() {
# Try to get the upstream name, e.g. "origin/master" or "origin/main"
if base="$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null)"; then
echo "$base"
else
echo "master"
fi
}

# Find all directories under start_dir that contain a CMakeLists.txt
find_cmake_subdirs() {
local start_dir="$1"
local cmakedirs=$(find "$start_dir" -type d -exec test -e '{}/CMakeLists.txt' \; -print -prune)
for cmakedir in $cmakedirs; do
echo "$cmakedir"
done
find "$start_dir" -type f -name 'CMakeLists.txt' -printf '%h\n' | sort -u
}

test_cpp_projects() {
local subdirs=$(find_cmake_subdirs .)
local current_dir=$(pwd)

local cpp_test_log="$current_dir/cpp_test.log"
: > "$cpp_test_log" # truncate the log file

local total_passed_tests=0
local total_failed_tests=0

cleanup() {
echo "Cleaning up..."
cd ..
rm -rf build
# Find all directories under start_dir that contain a __init__.py
find_python_subdirs() {
local start_dir="$1"
find "$start_dir" -type f -name '__init__.py' -printf '%h\n' | sort -u
}

for subdir in $subdirs; do
: > "$cpp_test_log" # truncate the log file
echo -e "\nRunning tests for C++ project at: $subdir"
cd "$subdir"
mkdir -p build && cd build
trap cleanup EXIT
# Get the list of files changed since the last commit on base branch
get_modified_files() {
local base_branch
base_branch="$(detect_base_branch)"
git diff --name-only "$base_branch"...HEAD
}

cmake .. 1>/dev/null 2>&1 && make 1>/dev/null 2>&1
ctest --verbose 2>&1 | tee -a "$cpp_test_log"
test_cpp_projects() {
local current_dir
current_dir=$(pwd)

# Find every directory that has a CMakeLists.txt
local all_subdirs
all_subdirs=$(find_cmake_subdirs .)

# Which files changed in this PR (relative to base)
local modified_files
modified_files=$(get_modified_files)

# Filter to only those C++ subdirs where at least one file was modified
local modified_subdirs=""
for subdir in $all_subdirs; do
# strip leading "./" for comparison against git output
local sub="${subdir#./}"
if echo "$modified_files" | grep -q "^$sub/"; then
modified_subdirs="$modified_subdirs $subdir"
fi
done

trap - EXIT
cleanup
cd "$current_dir"
if [ -z "$modified_subdirs" ]; then
echo "No modified C++ projects to test."
return
fi

# count the number of passed and total tests
cpp_total_tests=$(grep -oP '\d+ tests from' "$cpp_test_log" | tail -1 | awk '{print $1}')
cpp_passed_tests=$(grep -oP '\[\s*PASSED\s*\] \d+' "$cpp_test_log" | tail -1 | awk '{print $4}')
local total_passed_tests=0
local total_failed_tests=0
local cpp_test_log

cleanup() {
cd "$current_dir"
rm -rf build
}

echo "cpp_total_tests: $cpp_total_tests"
echo "cpp_passed_tests: $cpp_passed_tests"
echo "Running tests for modified C++ projects:"
for subdir in $modified_subdirs; do
echo -e "\nRunning tests for C++ project at: $subdir"
cd "$subdir"

local cpp_failed_tests=$((cpp_total_tests - cpp_passed_tests))
mkdir -p build && cd build
trap cleanup EXIT

total_passed_tests=$((total_passed_tests + cpp_passed_tests))
total_failed_tests=$((total_failed_tests + cpp_failed_tests))
# Hide cmake/make output
cmake .. 1>/dev/null 2>&1 && make 1>/dev/null 2>&1

echo "C++ Tests summary for $subdir:"
echo -e "Passed: \e[32m$cpp_passed_tests\e[0m, Failed: \e[31m$cpp_failed_tests\e[0m"
done
cpp_test_log="$current_dir/cpp_test_$(echo "$subdir" | tr '/' '_').log"
: > "$cpp_test_log"
ctest --verbose 2>&1 | tee -a "$cpp_test_log"

echo "Total C++ Tests summary:"
echo -e "Total Passed: \e[32m$total_passed_tests\e[0m, Total Failed: \e[31m$total_failed_tests\e[0m"
trap - EXIT
cleanup
cd "$current_dir"

}
local cpp_total_tests
cpp_total_tests=$(grep -oP '\d+(?= tests from)' "$cpp_test_log" | tail -1 || echo 0)
local cpp_passed_tests
cpp_passed_tests=$(grep -oP '(?<=\[ *PASSED *\] )\d+' "$cpp_test_log" | tail -1 || echo 0)
local cpp_failed_tests=$((cpp_total_tests - cpp_passed_tests))

total_passed_tests=$((total_passed_tests + cpp_passed_tests))
total_failed_tests=$((total_failed_tests + cpp_failed_tests))

find_python_subdirs() {
local start_dir="$1"
local pydirs=$(find "$start_dir" -type d -exec test -e '{}/__init__.py' \; -print -prune)
for pydir in $pydirs; do
echo "$pydir"
echo "C++ Tests summary for $subdir:"
echo -e " Passed: \e[32m$cpp_passed_tests\e[0m, Failed: \e[31m$cpp_failed_tests\e[0m"
done

echo -e "\nTotal C++ Tests summary for modified projects:"
echo -e " Total Passed: \e[32m$total_passed_tests\e[0m, Total Failed: \e[31m$total_failed_tests\e[0m"
}

test_python_projects() {
local subdirs=$(find_python_subdirs .)
local current_dir=$(pwd)
local current_dir
current_dir=$(pwd)

# Find every directory that has an __init__.py
local all_subdirs
all_subdirs=$(find_python_subdirs .)

# Which files changed in this PR (relative to base)
local modified_files
modified_files=$(get_modified_files)

# Filter to only those Python subdirs where at least one file was modified
local modified_subdirs=""
for subdir in $all_subdirs; do
local sub="${subdir#./}"
if echo "$modified_files" | grep -q "^$sub/"; then
modified_subdirs="$modified_subdirs $subdir"
fi
done

local python_test_log="$current_dir/python_test_log"
: > "$python_test_log" # truncate the log file
if [ -z "$modified_subdirs" ]; then
echo "No modified Python projects to test."
return
fi

local total_passed_tests=0
local total_failed_tests=0
local python_test_log

for subdir in $subdirs; do
echo "Running tests for modified Python projects:"
for subdir in $modified_subdirs; do
echo -e "\nRunning tests for Python project at: $subdir"
cd "$subdir"
: > "$python_test_log" # truncate the log file

python_test_log="$current_dir/python_test_$(echo "$subdir" | tr '/' '_').log"
: > "$python_test_log"

python3 -m unittest discover -v 2>&1 | tee -a "$python_test_log"
cd "$current_dir"

# count the number of passed and total tests
local python_total_tests=$(grep -oP 'Ran \K\d+' "$python_test_log")
local python_passed_tests=$(grep -o '.*... ok' "$python_test_log" | wc -l)
local python_total_tests
python_total_tests=$(grep -oP '(?<=Ran )\d+' "$python_test_log" | head -1 || echo 0)
local python_passed_tests
python_passed_tests=$(grep -c '\.\.\. ok' "$python_test_log" || echo 0)
local python_failed_tests=$((python_total_tests - python_passed_tests))

# add the passed and failed tests to the total
total_passed_tests=$((total_passed_tests + python_passed_tests))
total_failed_tests=$((total_failed_tests + python_failed_tests))

echo "Python Tests summary for $subdir:"
echo -e "Passed: \e[32m$python_passed_tests\e[0m, Failed: \e[31m$python_failed_tests\e[0m"
echo -e " Passed: \e[32m$python_passed_tests\e[0m, Failed: \e[31m$python_failed_tests\e[0m"
done

echo -e "\nTotal Python Tests summary:"
echo -e "Total Passed: \e[32m$total_passed_tests\e[0m, Total Failed: \e[31m$total_failed_tests\e[0m"

echo -e "\nTotal Python Tests summary for modified projects:"
echo -e " Total Passed: \e[32m$total_passed_tests\e[0m, Total Failed: \e[31m$total_failed_tests\e[0m"
}


main() {
if [ "$#" -eq 0 ]; then
echo "Running tests for all projects"
echo "Running tests for Python projects"
echo "Running tests only for projects modified since last commit on base branch."
echo "---- Python Projects ----"
test_python_projects
echo "Running tests for C++ projects"
echo "---- C++ Projects ----"
test_cpp_projects
exit 0
fi

if [ "$#" -eq 1 ]; then
if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
echo "Usage: run_tests.sh [OPTION]"
echo "Run tests for all projects."
echo ""
echo "Options:"
echo " -h, --help Show this help message and exit"
echo " -p, --python Run tests for Python projects"
echo " -c, --cpp Run tests for C++ projects"
exit 0
fi
if [ "$1" == "-p" ] || [ "$1" == "--python" ]; then
echo "Running tests for Python projects"
test_python_projects
exit 0
fi
if [ "$1" == "-c" ] || [ "$1" == "--cpp" ]; then
echo "Running tests for C++ projects"
test_cpp_projects
exit 0
fi
case "$1" in
-h|--help)
echo "Usage: run_tests.sh [OPTION]"
echo "Run tests for projects modified since base branch."
echo ""
echo "Options:"
echo " -h, --help Show this help message and exit"
echo " -p, --python Run tests for modified Python projects only"
echo " -c, --cpp Run tests for modified C++ projects only"
exit 0
;;
-p|--python)
echo "Running tests for modified Python projects:"
test_python_projects
exit 0
;;
-c|--cpp)
echo "Running tests for modified C++ projects:"
test_cpp_projects
exit 0
;;
*)
echo "Unknown option: $1"
echo "Usage: run_tests.sh [OPTION]"
exit 1
;;
esac
fi

if [ "$#" -gt 1 ]; then
echo "Too many arguments"
echo "Usage: run_tests.sh [OPTION]"
exit 1
fi

}

main "$@"