@@ -30,8 +30,15 @@ envname=${1?First argument required: suite base name}
3030posargs=$2
3131pytest_args=$3
3232
33- # strip leading/trailing quotes from posargs because it can get double quoted as its passed through.
34- posargs=$( sed -e ' s/^"//' -e ' s/"$//' -e " s/'$//" -e " s/^'//" <<< $posargs )
33+ # strip leading/trailing quotes from posargs because it can get double quoted as
34+ # its passed through.
35+ if [[ $posargs == ' "' * ' "' ]]; then
36+ # If wrapped in double quotes, remove them
37+ posargs=" ${posargs: 1: ${# posargs} -2} "
38+ elif [[ $posargs == " '" * " '" ]]; then
39+ # If wrapped in single quotes, remove them.
40+ posargs=" ${posargs: 1: ${# posargs} -2} "
41+ fi
3542echo " pytest_args: $pytest_args "
3643echo " posargs: $posargs "
3744
@@ -41,15 +48,26 @@ marker_regex="-m\s+('[^']+'|\"[^\"]+\"|[^ ]+)"
4148# Initialize the user_marker variable.
4249user_marker=" "
4350
44- # Isolate the user-provided -m argument (matching only).
45- if [[ $pytest_args =~ " -m" ]]; then
46- # Extract the marker value using the defined regex.
47- user_marker=$( echo " $pytest_args " | sed -nE " s/.*$marker_regex .*/\1/p" )
48- fi
51+ # Define regex pattern for quoted strings
52+ quotes_regex=" ^[\" \'](.*)[\" \']$"
4953
50- # Remove the -m argument from pytest_args (substitution only).
51- if [[ -n $user_marker ]]; then
52- pytest_args=$( echo " $pytest_args " | sed -E " s/$marker_regex //" )
54+ # Extract the user markers.
55+ if [[ $posargs =~ $marker_regex ]]; then
56+ # Get the full match including -m and the marker.
57+ full_match=" ${BASH_REMATCH[0]} "
58+
59+ # Get the marker with quotes (this is the first capture group).
60+ quoted_marker=" ${BASH_REMATCH[1]} "
61+
62+ # Remove any quotes around the marker.
63+ if [[ $quoted_marker =~ $quotes_regex ]]; then
64+ user_marker=" ${BASH_REMATCH[1]} "
65+ else
66+ user_marker=" $quoted_marker "
67+ fi
68+
69+ # Remove the entire -m marker portion from posargs.
70+ posargs=" ${posargs/ $full_match / } "
5371fi
5472
5573# Combine user-provided marker with script's internal logic.
@@ -58,20 +76,65 @@ marker_for_sequential_tests="no_xdist"
5876
5977if [[ -n $user_marker ]]; then
6078 # Combine user marker with internal markers.
61- marker_for_parallel_tests=" ($user_marker ) and ($marker_for_parallel_tests )"
62- marker_for_sequential_tests=" ($user_marker ) and ($marker_for_sequential_tests )"
79+ marker_for_parallel_tests=" $user_marker and ($marker_for_parallel_tests )"
80+ marker_for_sequential_tests=" $user_marker and ($marker_for_sequential_tests )"
81+ fi
82+
83+ # Parse posargs to separate pytest options from test paths.
84+ options=" "
85+ test_paths=" "
86+
87+ # Safely split the posargs string into individual arguments.
88+ eval " set -- $posargs "
89+
90+ # Iterate through arguments.
91+ while [[ $# -gt 0 ]]; do
92+ arg=" $1 "
93+ shift
94+
95+ # If argument starts with dash, it's an option.
96+ if [[ " $arg " == -* ]]; then
97+ options+=" $arg "
98+
99+ # Check if there's a next argument and it doesn't start with a dash.
100+ # This assumes it's a value for the current option.
101+ if [[ $# -gt 0 && " $1 " != -* ]]; then
102+ # Get the next argument.
103+ next_arg=" $1 "
104+
105+ # Check if it's quoted and remove quotes if needed.
106+ if [[ $next_arg =~ $quotes_regex ]]; then
107+ # Extract the content inside quotes.
108+ next_arg=" ${BASH_REMATCH[1]} "
109+ fi
110+
111+ # Add the unquoted value to options.
112+ options+=" $next_arg "
113+ shift
114+ fi
115+ else
116+ # Otherwise it's a test path.
117+ test_paths+=" $arg "
118+ fi
119+ done
120+
121+ # Construct the final pytest command arguments.
122+ pyargs_section=" "
123+ if [[ -n " $test_paths " ]]; then
124+ pyargs_section=" --pyargs $test_paths "
63125fi
126+ pytest_command_args=" $options $pyargs_section "
64127
65128# Run tests in parallel.
66- echo " Running parallel tests with: pytest -m \" $marker_for_parallel_tests \" $pytest_args "
129+ echo " Running parallel tests with: pytest -m \" $marker_for_parallel_tests \" $pytest_command_args "
67130pytest -v -rs -o junit_suite_name=${envname} \
68- --junitxml=pytest_${envname} .xml -m " $marker_for_parallel_tests " -n 6 --import-mode=importlib ${pytest_args} --pyargs ${posargs }
131+ --junitxml=pytest_${envname} .xml -m " $marker_for_parallel_tests " -n 6 --import-mode=importlib ${pytest_args} ${pytest_command_args }
69132status1=$?
70133
71134# Run tests sequentially.
72- echo " Running sequential tests with: pytest -m \" $marker_for_sequential_tests \" $pytest_args "
135+ echo " Running sequential tests with: pytest -m \" $marker_for_sequential_tests \" $pytest_command_args "
73136pytest -v -rs -o junit_suite_name=${envname} _no_xdist \
74- --junitxml=pytest_${envname} _no_xdist.xml -m " $marker_for_sequential_tests " --import-mode=importlib ${pytest_args} --pyargs ${posargs }
137+ --junitxml=pytest_${envname} _no_xdist.xml -m " $marker_for_sequential_tests " --import-mode=importlib ${pytest_args} ${pytest_command_args }
75138status2=$?
76139
77140# Exit with error if no tests were run in either suite (status code 5).
0 commit comments