@@ -25,7 +25,9 @@ COVERAGE_REPORT="term"
2525KEEP_RUNNING=false
2626SELECTED_CORES=" "
2727INCLUDE_LOCAL_CORES=false
28- TEST_FILES=" "
28+ TEST_FILES=()
29+ PARALLEL=false
30+ PARALLEL_WORKERS=" auto"
2931
3032# Function to print colored messages
3133print_message () {
@@ -57,6 +59,8 @@ OPTIONS:
5759 -k, --keep-running Keep containers running after tests
5860 -h, --html-coverage Generate HTML coverage report
5961 -f, --files Specify test files to run (can be used multiple times)
62+ -p, --parallel Run tests in parallel using pytest-xdist
63+ -w, --workers Number of parallel workers (default: auto)
6064 --help Show this help message
6165
6266EXAMPLES:
@@ -66,6 +70,8 @@ EXAMPLES:
6670 $0 external -k # Run external backends, keep containers
6771 $0 mongo memory -v # Run MongoDB and memory tests verbosely
6872 $0 all -f tests/test_main.py -f tests/test_redis_core_coverage.py # Run specific test files
73+ $0 memory pickle -p # Run local tests in parallel
74+ $0 all -p -w 4 # Run all tests with 4 parallel workers
6975
7076ENVIRONMENT:
7177 You can also set cores via CACHIER_TEST_CORES environment variable:
@@ -96,13 +102,27 @@ while [[ $# -gt 0 ]]; do
96102 usage
97103 exit 1
98104 fi
99- TEST_FILES= " $TEST_FILES $1 "
105+ TEST_FILES+=( " $1 " )
100106 shift
101107 ;;
102108 --help)
103109 usage
104110 exit 0
105111 ;;
112+ -p|--parallel)
113+ PARALLEL=true
114+ shift
115+ ;;
116+ -w|--workers)
117+ shift
118+ if [[ $# -eq 0 ]] || [[ " $1 " == -* ]]; then
119+ print_message $RED " Error: -w/--workers requires a number argument"
120+ usage
121+ exit 1
122+ fi
123+ PARALLEL_WORKERS=" $1 "
124+ shift
125+ ;;
106126 -* )
107127 print_message $RED " Unknown option: $1 "
108128 usage
@@ -234,6 +254,17 @@ check_dependencies() {
234254 }
235255 fi
236256
257+ # Check for pytest-xdist if parallel testing is requested
258+ if [ " $PARALLEL " = true ]; then
259+ if ! python -c " import xdist" 2> /dev/null; then
260+ print_message $YELLOW " Installing pytest-xdist for parallel testing..."
261+ pip install pytest-xdist || {
262+ print_message $RED " Failed to install pytest-xdist"
263+ exit 1
264+ }
265+ fi
266+ fi
267+
237268 # Check MongoDB dependencies if testing MongoDB
238269 if echo " $SELECTED_CORES " | grep -qw " mongo" ; then
239270 if ! python -c " import pymongo" 2> /dev/null; then
@@ -423,14 +454,20 @@ main() {
423454 # Check and install dependencies
424455 check_dependencies
425456
426- # Check if we need Docker
457+ # Check if we need Docker, and if we should run serial pickle tests
427458 needs_docker=false
459+ run_serial_local_tests=false
428460 for core in $SELECTED_CORES ; do
429461 case $core in
430462 mongo|redis|sql)
431463 needs_docker=true
432464 ;;
433465 esac
466+ case $core in
467+ pickle|all)
468+ run_serial_local_tests=true
469+ ;;
470+ esac
434471 done
435472
436473 if [ " $needs_docker " = true ]; then
@@ -497,26 +534,46 @@ main() {
497534 sql) test_sql ;;
498535 esac
499536 done
537+ if [ -n " $pytest_markers " ]; then
538+ pytest_markers=" ($pytest_markers ) and not seriallocal"
539+ else
540+ pytest_markers=" not seriallocal"
541+ fi
500542
501543 # Run pytest
502544 # Build pytest command
503- PYTEST_CMD=" pytest"
545+ PYTEST_ARGS=(pytest)
546+ # and the specific pytest command for running serial pickle tests
547+ SERIAL_PYTEST_ARGS=(pytest -m seriallocal)
548+ # Only add -n0 if pytest-xdist is available; otherwise, plain pytest is already serial
549+ if python - << 'EOF ' >/dev/null 2>&1
550+ import xdist # noqa: F401
551+ EOF
552+ then
553+ SERIAL_PYTEST_ARGS+=(-n0)
554+ fi
504555
505556 # Add test files if specified
506- if [ -n " $TEST_FILES " ]; then
507- PYTEST_CMD=" $PYTEST_CMD $TEST_FILES "
508- print_message $BLUE " Test files specified: $TEST_FILES "
557+ if [ ${# TEST_FILES[@]} -gt 0 ]; then
558+ PYTEST_ARGS+=(" ${TEST_FILES[@]} " )
559+ print_message $BLUE " Test files specified: ${TEST_FILES[*]} "
560+ # and turn off serial local tests, so we run only selected files
561+ run_serial_local_tests=false
509562 fi
510563
511564 # Add markers if needed (only if no specific test files were given)
512- if [ -z " $ TEST_FILES" ]; then
565+ if [ ${ # TEST_FILES[@]} -eq 0 ]; then
513566 # Check if we selected all cores - if so, run all tests without marker filtering
514567 all_cores=" memory mongo pickle redis s3 sql"
515568 selected_sorted=$( echo " $SELECTED_CORES " | tr ' ' ' \n' | sort | tr ' \n' ' ' | xargs)
516569 all_sorted=$( echo " $all_cores " | tr ' ' ' \n' | sort | tr ' \n' ' ' | xargs)
517570
518571 if [ " $selected_sorted " != " $all_sorted " ]; then
519- PYTEST_CMD=" $PYTEST_CMD -m \" $pytest_markers \" "
572+ PYTEST_ARGS+=(-m " $pytest_markers " )
573+ else
574+ print_message $BLUE " Running all tests without markers since all cores are selected"
575+ PYTEST_ARGS+=(-m " not seriallocal" )
576+ run_serial_local_tests=true
520577 fi
521578 else
522579 # When test files are specified, still apply markers if not running all cores
@@ -525,21 +582,47 @@ main() {
525582 all_sorted=$( echo " $all_cores " | tr ' ' ' \n' | sort | tr ' \n' ' ' | xargs)
526583
527584 if [ " $selected_sorted " != " $all_sorted " ]; then
528- PYTEST_CMD= " $PYTEST_CMD -m \ "$pytest_markers \" "
585+ PYTEST_ARGS+=( -m " $pytest_markers " )
529586 fi
530587 fi
531588
532589 # Add verbose flag if needed
533590 if [ " $VERBOSE " = true ]; then
534- PYTEST_CMD=" $PYTEST_CMD -v"
591+ PYTEST_ARGS+=(-v)
592+ SERIAL_PYTEST_ARGS+=(-v)
593+ fi
594+
595+ # Add parallel testing options if requested
596+ if [ " $PARALLEL " = true ]; then
597+ PYTEST_ARGS+=(-n " $PARALLEL_WORKERS " )
598+
599+ # Show parallel testing info
600+ if [ " $PARALLEL_WORKERS " = " auto" ]; then
601+ print_message $BLUE " Running tests in parallel with automatic worker detection"
602+ else
603+ print_message $BLUE " Running tests in parallel with $PARALLEL_WORKERS workers"
604+ fi
605+
606+ # Special note for pickle tests
607+ if echo " $SELECTED_CORES " | grep -qw " pickle" ; then
608+ print_message $YELLOW " Note: Pickle tests will use isolated cache directories for parallel safety"
609+ fi
535610 fi
536611
537612 # Add coverage options
538- PYTEST_CMD=" $PYTEST_CMD --cov=cachier --cov-report=$COVERAGE_REPORT "
613+ PYTEST_ARGS+=(--cov=cachier --cov-report=" $COVERAGE_REPORT " )
614+ SERIAL_PYTEST_ARGS+=(--cov=cachier --cov-report=" $COVERAGE_REPORT " --cov-append)
539615
540616 # Print and run the command
541- print_message $BLUE " Running: $PYTEST_CMD "
542- eval $PYTEST_CMD
617+ print_message $BLUE " Running: $( printf ' %q ' " ${PYTEST_ARGS[@]} " ) "
618+ " ${PYTEST_ARGS[@]} "
619+
620+ if [ " $run_serial_local_tests " = true ]; then
621+ print_message $BLUE " Running serial local tests (pickle, memory) with: $( printf ' %q ' " ${SERIAL_PYTEST_ARGS[@]} " ) "
622+ " ${SERIAL_PYTEST_ARGS[@]} "
623+ else
624+ print_message $BLUE " Skipping serial local tests (pickle, memory) since not requested"
625+ fi
543626
544627 TEST_EXIT_CODE=$?
545628
0 commit comments