#!/bin/bash # Timestamp: "2025-11-18 11:07:00 (ywatanabe)" # File: ./tests/sync_tests_with_scripts.sh ORIG_DIR="$(pwd)" THIS_DIR="$(cd $(dirname ${BASH_SOURCE[0]}) && pwd)" LOG_PATH="$THIS_DIR/.$(basename $0).log" echo > "$LOG_PATH" GIT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" GRAY='\033[0;90m' GREEN='\033[0;32m' YELLOW='\033[0;33m' RED='\033[0;31m' NC='\033[0m' echo_info() { echo -e "${GRAY}INFO: $1${NC}"; } echo_success() { echo -e "${GREEN}SUCC: $1${NC}"; } echo_warning() { echo -e "${YELLOW}WARN: $1${NC}"; } echo_error() { echo -e "${RED}ERRO: $1${NC}"; } echo_header() { echo_info "=== $1 ==="; } ROOT_DIR="$(realpath $THIS_DIR/..)" DO_MOVE=false SRC_DIR="$(realpath "${ROOT_DIR}/scripts")" TESTS_DIR="$(realpath "${ROOT_DIR}/tests")" usage() { echo "Usage: $0 [options]" echo echo "Synchronizes test files with script files, maintaining test code structure." echo echo "Options:" echo " -m, --move Move stale test files to .old directory instead of just reporting (default: $DO_MOVE)" echo " -s, --source DIR Specify custom source directory (default: $SRC_DIR)" echo " -t, --tests DIR Specify custom tests directory (default: $TESTS_DIR)" echo " -h, --help Display this help message" echo echo "Example:" echo " $0 --move" echo " $0 --source /path/to/scripts --tests /path/to/tests" exit 1 } while [[ $# -gt 0 ]]; do case $1 in -m|--move) DO_MOVE=true shift ;; -s|--source) SRC_DIR="$2" shift 2 ;; -t|--tests) TESTS_DIR="$2" shift 2 ;; -h|--help) usage ;; *) echo "Unknown option: $1" usage ;; esac done prepare_tests_structure_as_source() { [ ! -d "$SRC_DIR" ] && echo_error "Source directory not found: $SRC_DIR" && exit 1 construct_blacklist_patterns find "$SRC_DIR" -type d "${FIND_EXCLUDES[@]}" | while read -r dir; do tests_dir="${dir/$SRC_DIR/$TESTS_DIR}" mkdir -p "$tests_dir" done } extract_test_code() { local test_file=$1 local temp_file=$(mktemp) sed -n '/if __name__ == "__main__":/q;p' "$test_file" > "$temp_file" if [ -s "$temp_file" ]; then sed -i -e :a -e '/^\n*$/{$d;N;ba' -e '}' "$temp_file" cat "$temp_file" fi rm "$temp_file" } get_pytest_guard() { echo '' echo 'if __name__ == "__main__":' echo ' import os' echo '' echo ' import pytest' echo '' echo ' pytest.main([os.path.abspath(__file__)])' } update_test_file() { local test_file=$1 local src_file=$2 if [ ! -f "$test_file" ]; then echo_info "Creating test file: $test_file" mkdir -p "$(dirname "$test_file")" cat > "$test_file" << EOL # Add your tests here $(get_pytest_guard) EOL else local temp_file=$(mktemp) local test_code=$(extract_test_code "$test_file") if [ -n "$test_code" ]; then echo "$test_code" > "$temp_file" [[ "$(tail -c 1 "$temp_file")" != "" ]] && echo "" >> "$temp_file" else echo "# Add your tests here" > "$temp_file" echo "" >> "$temp_file" fi get_pytest_guard >> "$temp_file" mv "$temp_file" "$test_file" fi } construct_blacklist_patterns() { local EXCLUDE_PATHS=( "*/.*" "*/.*/*" "*/deprecated*" "*/archive*" "*/backup*" "*/tmp*" "*/temp*" "*_out" "*_out/*" "*/RUNNING" "*/RUNNING/*" "*/FINISHED" "*/FINISHED/*" "*/FINISHED_SUCCESS" "*/FINISHED_SUCCESS/*" "*/FINISHED_FAILED" "*/FINISHED_FAILED/*" "*/2025Y*" "*/2024Y*" "*/__pycache__" "*/__pycache__/*" "*/template.py" ) FIND_EXCLUDES=() PRUNE_ARGS=() for path in "${EXCLUDE_PATHS[@]}"; do FIND_EXCLUDES+=( -not -path "$path" ) PRUNE_ARGS+=( -path "$path" -o ) done unset 'PRUNE_ARGS[${#PRUNE_ARGS[@]}-1]' } find_files() { local search_path=$1 local type=$2 local name_pattern=$3 construct_blacklist_patterns find "$search_path" \ \( "${PRUNE_ARGS[@]}" \) -prune -o -type "$type" -name "$name_pattern" -print } move_stale_test_files_to_old() { local timestamp="$(date +%Y%m%d_%H%M%S)" find "$TESTS_DIR" -name "test_*.py" -not -path "*.old*" | while read -r test_path; do [[ "$test_path" =~ ^${TESTS_DIR}/custom ]] && continue test_rel_path="${test_path#$TESTS_DIR/}" test_rel_dir="$(dirname $test_rel_path)" test_filename="$(basename $test_rel_path)" src_filename="${test_filename#test_}" src_rel_dir="$test_rel_dir" src_rel_path="$src_rel_dir/$src_filename" src_path="$SRC_DIR/$src_rel_path" if [ ! -f "$src_path" ] && [ -f "$test_path" ]; then stale_test_path=$test_path stale_test_filename="$(basename $stale_test_path)" stale_test_path_dir="$(dirname $stale_test_path)" old_dir_with_timestamp="$stale_test_path_dir/.old-$timestamp" tgt_path="$old_dir_with_timestamp/$stale_test_filename" echo_warning "Stale Test: $stale_test_path" echo_warning "To remove: $0 -m" if [ "$DO_MOVE" = "true" ]; then mkdir -p "$old_dir_with_timestamp" mv "$stale_test_path" "$tgt_path" echo_success "Moved: $stale_test_path -> $tgt_path" fi fi done } remove_hidden_test_files_and_dirs() { find "$TESTS_DIR" -type f -name ".*" -delete 2>/dev/null find "$TESTS_DIR" -type d -name ".*" -not -path "$TESTS_DIR/.old" -not -path "$TESTS_DIR/.old/*" -exec rm -rf {} \; 2>/dev/null } cleanup_unnecessary_test_files() { find "$TESTS_DIR" -type d -name "*RUNNING*" -exec rm -rf {} \; 2>/dev/null find "$TESTS_DIR" -type d -name "*FINISHED*" -exec rm -rf {} \; 2>/dev/null find "$TESTS_DIR" -type d -name "*FINISHED_SUCCESS*" -exec rm -rf {} \; 2>/dev/null find "$TESTS_DIR" -type d -name "*FINISHED_FAILED*" -exec rm -rf {} \; 2>/dev/null find "$TESTS_DIR" -type d -name "*2024Y*" -exec rm -rf {} \; 2>/dev/null find "$TESTS_DIR" -type d -name "*2025Y*" -exec rm -rf {} \; 2>/dev/null find "$TESTS_DIR" -type d -name "*_out" -exec rm -rf {} \; 2>/dev/null find "$TESTS_DIR" -type d -name "*__pycache__*" -exec rm -rf {} \; 2>/dev/null find "$TESTS_DIR" -type f -name "*.pyc" -delete 2>/dev/null } chmod_python_scripts_as_executable() { construct_blacklist_patterns find "$SRC_DIR" -type f -name "*.py" "${FIND_EXCLUDES[@]}" -exec chmod +x {} \; } main() { echo_header "Sync Tests with Source" echo_info "SRC_DIR: $SRC_DIR" echo_info "TESTS_DIR: $TESTS_DIR" echo "" remove_hidden_test_files_and_dirs prepare_tests_structure_as_source chmod_python_scripts_as_executable cleanup_unnecessary_test_files find_files "$SRC_DIR" f "*.py" | while read -r src_file; do rel="${src_file#$SRC_DIR/}" rel_dir=$(dirname "$rel") src_base=$(basename "$rel") tests_dir="$TESTS_DIR/$rel_dir" mkdir -p "$tests_dir" test_file="$tests_dir/test_$src_base" update_test_file "$test_file" "$src_file" done remove_hidden_test_files_and_dirs move_stale_test_files_to_old echo "" echo_header "Test Directory Structure" tree "$TESTS_DIR" 2>&1 | tee -a "$LOG_PATH" echo "" echo_success "Synchronization complete!" } main "$@" cd $ORIG_DIR # EOF