From 0a6ec877b989ce78198e81750c444c4197a00374 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sat, 10 Jan 2026 19:00:57 -0500 Subject: [PATCH 01/12] Added initial update_tests.sh script --- scripts/update_tests.sh | 73 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100755 scripts/update_tests.sh diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh new file mode 100755 index 0000000000..428ebb2581 --- /dev/null +++ b/scripts/update_tests.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +usage() { echo "Usage: $0 [-c ] [-r ] [-u ]" 1>&2; exit 1; } + + +cpython_path="" +rpython_path="" +copy_untracked=false +libraries=() + +while [[ $# -gt 0 ]]; do + case "$1" in + -c|--cpython-path) + cpython_path="$2" + shift 2 + ;; + -r|--rpython-path) + rpython_path="$2" + shift 2 + ;; + -u|--copy-untracked) + copy_untracked=true + shift + ;; + *) + libraries+=("$1") + shift + ;; + esac +done + +cpython_path="$cpython_path/Lib/test" +rpython_path="$rpython_path/Lib/test" + + +if [[ ${#libraries[@]} -eq 0 ]]; then + libraries=$(find ${cpython_path} -type f -printf "%P\n") +fi +echo "libraries is ${libraries}" + +for lib in "${arr[@]}" +do + cpython_file="$cpython_path/$lib" + rpython_file="$rpython_path/$lib" + + + if [[ $files_equal $cpython_file $rpython_file -eq 0 ]]; then + continue + fi + + if [[ ! -f $cpython_file ]]; then + if $copy_untracked then + echo "Test file $lib missing. Copying..." + cp "$cpython_file" "$rpython_file" + fi + else + echo "Updating $lib..." + ./scripts/lib_updater.py --from ${rpython_path}/Lib/test/$lib --to ${rpython_path}/Lib/test/$lib + fi + + + cargo run +done + + + +files_equal() { + file1=$1 + file2=$2 + cmp --silent $file1 $file2 && files_equal=0 || files_equal=1 + return $files_equal +} + From 0fe8ac2e9b60d7987a8de39e04e5f0278f13a898 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sat, 10 Jan 2026 20:56:12 -0500 Subject: [PATCH 02/12] Updated update_tests.sh * Added usage instructions * Added basic usability (debugging + automatic adding expectedFailure) --- scripts/update_tests.sh | 95 ++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 24 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index 428ebb2581..709370f74b 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -1,11 +1,34 @@ #!/bin/bash -usage() { echo "Usage: $0 [-c ] [-r ] [-u ]" 1>&2; exit 1; } +usage() { + cat >&2 < Path to the CPython source tree (older version) + -r/--rpython-path Path to the RustPython source tree (newer version) + -u/--copy-untracked Copy untracked tests only + -a/--annotate While copying tests, run them and annotate failures dynamically + -h/--help Show this help message and exit + +Example Usage: $0 -c ~/cpython -r . +EOF + exit 1 +} + +if [[ $# -eq 0 ]]; then + usage + exit 1 +fi cpython_path="" rpython_path="" copy_untracked=false +annotate=false libraries=() while [[ $# -gt 0 ]]; do @@ -22,6 +45,14 @@ while [[ $# -gt 0 ]]; do copy_untracked=true shift ;; + -h|--help) + usage + return + ;; + -a|--annotate) + annotate=true + shift + ;; *) libraries+=("$1") shift @@ -32,35 +63,49 @@ done cpython_path="$cpython_path/Lib/test" rpython_path="$rpython_path/Lib/test" +update_libraries() { + if [[ ${#libraries[@]} -eq 0 ]]; then + libraries=$(find ${cpython_path} -type f -printf "%P\n") + fi -if [[ ${#libraries[@]} -eq 0 ]]; then - libraries=$(find ${cpython_path} -type f -printf "%P\n") -fi -echo "libraries is ${libraries}" - -for lib in "${arr[@]}" -do - cpython_file="$cpython_path/$lib" - rpython_file="$rpython_path/$lib" + for lib in "${libraries[@]}" + do + cpython_file="$cpython_path/$lib" + rpython_file="$rpython_path/$lib" + filename=${lib##*/} + basename=${filename%.py} - if [[ $files_equal $cpython_file $rpython_file -eq 0 ]]; then - continue - fi + if files_equal "$cpython_file" "$rpython_file"; then + echo "No changes in $lib. Skipping..." + continue + fi - if [[ ! -f $cpython_file ]]; then - if $copy_untracked then - echo "Test file $lib missing. Copying..." - cp "$cpython_file" "$rpython_file" + if [[ ! -f "$rpython_file" ]]; then + echo "Test file $lib missing." + if $copy_untracked; then + echo "Copying..." + cp "$cpython_file" "$rpython_file" + fi + else + ./scripts/lib_updater.py --from $cpython_file --to $rpython_file -o $rpython_file fi - else - echo "Updating $lib..." - ./scripts/lib_updater.py --from ${rpython_path}/Lib/test/$lib --to ${rpython_path}/Lib/test/$lib - fi - - cargo run -done + if $annotate; then + output=$(cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --slowest --fail-env-changed -v $lib 2>&1) + failed_tests=$(echo "$output" | grep '^FAIL: ' | awk '{print $2}') + errored_tests=$(echo "$output" | grep '^ERROR ' | awk '{print $2}') + failed_tests=$(echo "$failed_tests" | sort -u) + + for test in "${failed_tests[@]}" + do + sed -i "s/^\([[:space:]]*\)def $test(/\1@unittest.expectedFailure # TODO: RUSTPYTHON\n\1def $test(/" "$rpython_file" + done + output=$(cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --slowest --fail-env-changed -v $lib 2>&1) + echo "$failed_tests" + fi + done +} @@ -71,3 +116,5 @@ files_equal() { return $files_equal } + +update_libraries \ No newline at end of file From 0a2ea1b12ac79b745b169c98d52ae409a7691f1c Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sat, 10 Jan 2026 21:33:20 -0500 Subject: [PATCH 03/12] Added conditioning for erroring tests + moved some logic into separate functions --- scripts/update_tests.sh | 55 +++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index 709370f74b..18134863ca 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -70,29 +70,37 @@ update_libraries() { for lib in "${libraries[@]}" do - cpython_file="$cpython_path/$lib" - rpython_file="$rpython_path/$lib" + update_library "$lib" + done +} - filename=${lib##*/} - basename=${filename%.py} +update_library() { + lib=$1 + cpython_file="$cpython_path/$lib" + rpython_file="$rpython_path/$lib" - if files_equal "$cpython_file" "$rpython_file"; then - echo "No changes in $lib. Skipping..." - continue - fi + filename=${lib##*/} + basename=${filename%.py} - if [[ ! -f "$rpython_file" ]]; then - echo "Test file $lib missing." - if $copy_untracked; then - echo "Copying..." - cp "$cpython_file" "$rpython_file" - fi - else - ./scripts/lib_updater.py --from $cpython_file --to $rpython_file -o $rpython_file + if files_equal "$cpython_file" "$rpython_file"; then + echo "No changes in $lib. Skipping..." + continue + fi + + if [[ ! -f "$rpython_file" ]]; then + echo "Test file $lib missing." + if $copy_untracked; then + echo "Copying..." + cp "$cpython_file" "$rpython_file" fi + else + ./scripts/lib_updater.py --from $cpython_file --to $rpython_file -o $rpython_file + fi - if $annotate; then - output=$(cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --slowest --fail-env-changed -v $lib 2>&1) + + output=$(cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --slowest --fail-env-changed -v $lib 2>&1) + if $annotate; then + while ! grep -q "Tests result: SUCCESS" <<< "$output"; do failed_tests=$(echo "$output" | grep '^FAIL: ' | awk '{print $2}') errored_tests=$(echo "$output" | grep '^ERROR ' | awk '{print $2}') failed_tests=$(echo "$failed_tests" | sort -u) @@ -101,10 +109,15 @@ update_libraries() { do sed -i "s/^\([[:space:]]*\)def $test(/\1@unittest.expectedFailure # TODO: RUSTPYTHON\n\1def $test(/" "$rpython_file" done + + if grep -q "\.\.\.$" <<< "$output"; then + hanging_test=$(echo "$output" | grep '\.\.\.$' | head -n 1 | awk '{print $1}') + sed -i "s/^\([[:space:]]*\)def $hanging_test(/\1@unittest.skip('TODO: RUSTPYTHON')\n\1def $hanging_test(/" "$rpython_file" + fi + output=$(cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --slowest --fail-env-changed -v $lib 2>&1) - echo "$failed_tests" - fi - done + done + fi } From f8ff03e2ea9b8b75043661aab5fe8173ba71d4cd Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sat, 10 Jan 2026 22:08:47 -0500 Subject: [PATCH 04/12] Added logic for hanging tests --- scripts/update_tests.sh | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index 18134863ca..055efe2b98 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -54,7 +54,7 @@ while [[ $# -gt 0 ]]; do shift ;; *) - libraries+=("$1") + libraries+=("$1.py") shift ;; esac @@ -63,18 +63,18 @@ done cpython_path="$cpython_path/Lib/test" rpython_path="$rpython_path/Lib/test" -update_libraries() { +update_tests() { if [[ ${#libraries[@]} -eq 0 ]]; then libraries=$(find ${cpython_path} -type f -printf "%P\n") fi for lib in "${libraries[@]}" do - update_library "$lib" + update_test "$lib" done } -update_library() { +update_test() { lib=$1 cpython_file="$cpython_path/$lib" rpython_file="$rpython_path/$lib" @@ -88,34 +88,39 @@ update_library() { fi if [[ ! -f "$rpython_file" ]]; then - echo "Test file $lib missing." + echo "Test file $lib missing" if $copy_untracked; then - echo "Copying..." + echo "Copying $lib ..." cp "$cpython_file" "$rpython_file" fi else + echo "Using lib_updater to update $lib" ./scripts/lib_updater.py --from $cpython_file --to $rpython_file -o $rpython_file fi - output=$(cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --slowest --fail-env-changed -v $lib 2>&1) + output=$(rustpython $lib 2>&1) if $annotate; then while ! grep -q "Tests result: SUCCESS" <<< "$output"; do - failed_tests=$(echo "$output" | grep '^FAIL: ' | awk '{print $2}') - errored_tests=$(echo "$output" | grep '^ERROR ' | awk '{print $2}') - failed_tests=$(echo "$failed_tests" | sort -u) + echo "$lib failing, annotating..." + failed_tests=$(echo "$output" | grep '^FAIL: ' | awk '{print $2}' | sort -u) + # If the test fails/errors, then expectedFailure it for test in "${failed_tests[@]}" do sed -i "s/^\([[:space:]]*\)def $test(/\1@unittest.expectedFailure # TODO: RUSTPYTHON\n\1def $test(/" "$rpython_file" done + # If the test crashes/hangs, then skip it if grep -q "\.\.\.$" <<< "$output"; then - hanging_test=$(echo "$output" | grep '\.\.\.$' | head -n 1 | awk '{print $1}') - sed -i "s/^\([[:space:]]*\)def $hanging_test(/\1@unittest.skip('TODO: RUSTPYTHON')\n\1def $hanging_test(/" "$rpython_file" + crashing_test=$(echo "$output" | grep '\.\.\.$' | head -n 1 | awk '{print $1}') + if grep -q "Timeout" <<< "$output"; then + message="; hanging" + fi + sed -i "s/^\([[:space:]]*\)def $crashing_test(/\1@unittest.skip('TODO: RUSTPYTHON$message')\n\1def $crashing_test(/" "$rpython_file" fi - output=$(cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --slowest --fail-env-changed -v $lib 2>&1) + output=$(rustpython $lib 2>&1) done fi } @@ -129,5 +134,8 @@ files_equal() { return $files_equal } +rustpython() { + cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --fail-env-changed --timeout 300 -v "$@" +} -update_libraries \ No newline at end of file +update_tests \ No newline at end of file From 965c27a85421fce70d9bfbf3ac27d81c6fc864bf Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sat, 10 Jan 2026 23:06:16 -0500 Subject: [PATCH 05/12] Added checking existing skipped tests + setting custom timeout + checking erroring but not crashing tests --- scripts/update_tests.sh | 99 ++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 22 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index 055efe2b98..42fda883a6 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -11,10 +11,14 @@ Options: -c/--cpython-path Path to the CPython source tree (older version) -r/--rpython-path Path to the RustPython source tree (newer version) -u/--copy-untracked Copy untracked tests only + -s/--check-skipped Check existing skipped tests (must be run separate from updating the tests) + -t/--timeout Set a timeout for a test -a/--annotate While copying tests, run them and annotate failures dynamically -h/--help Show this help message and exit -Example Usage: $0 -c ~/cpython -r . +Example Usage: +$0 -c ~/cpython -r . +$0 -r . --check-skipped EOF exit 1 } @@ -29,6 +33,8 @@ cpython_path="" rpython_path="" copy_untracked=false annotate=false +timeout=300 +check_skip_flag=false libraries=() while [[ $# -gt 0 ]]; do @@ -49,6 +55,14 @@ while [[ $# -gt 0 ]]; do usage return ;; + -s|--check-skipped) + check_skip_flag=true + shift + ;; + -t|--timeout) + timout="$2" + shift 2 + ;; -a|--annotate) annotate=true shift @@ -63,11 +77,13 @@ done cpython_path="$cpython_path/Lib/test" rpython_path="$rpython_path/Lib/test" -update_tests() { - if [[ ${#libraries[@]} -eq 0 ]]; then - libraries=$(find ${cpython_path} -type f -printf "%P\n") - fi +if [[ ${#libraries[@]} -eq 0 ]]; then + libraries=$(find ${cpython_path} -type f -printf "%P\n") +fi + +update_tests() { + libraries=$1 for lib in "${libraries[@]}" do update_test "$lib" @@ -76,39 +92,63 @@ update_tests() { update_test() { lib=$1 - cpython_file="$cpython_path/$lib" - rpython_file="$rpython_path/$lib" - - filename=${lib##*/} - basename=${filename%.py} + clib_path="$cpython_path/$lib" + rlib_path="$rpython_path/$lib" - if files_equal "$cpython_file" "$rpython_file"; then + if files_equal "$clib_path" "$rlib_path"; then echo "No changes in $lib. Skipping..." continue fi - if [[ ! -f "$rpython_file" ]]; then + if [[ ! -f "$rlib_path" ]]; then echo "Test file $lib missing" if $copy_untracked; then echo "Copying $lib ..." - cp "$cpython_file" "$rpython_file" + cp "$clib_path" "$rlib_path" fi else echo "Using lib_updater to update $lib" - ./scripts/lib_updater.py --from $cpython_file --to $rpython_file -o $rpython_file + ./scripts/lib_updater.py --from $clib_path --to $rlib_path -o $rlib_path fi - output=$(rustpython $lib 2>&1) if $annotate; then + annotate_lib $lib $rlib_path + fi +} + +check_skips() { + libraries=$1 + for lib in "${libraries[@]}" + do + check_skip "$lib" + done +} + +check_skip() { + lib=$1 + rlib_path="$rpython_path/$lib" + + remove_skips $rlib_path + + annotate_lib $lib $rlib_path +} + +annotate_lib() { + lib=$1 + rlib_path=$2 + output=$(rustpython $lib 2>&1) + + echo "Annotating $lib" + while ! grep -q "Tests result: SUCCESS" <<< "$output"; do echo "$lib failing, annotating..." - failed_tests=$(echo "$output" | grep '^FAIL: ' | awk '{print $2}' | sort -u) + readarray -t failed_tests <<< $(echo "$output" | awk '/^(FAIL:|ERROR:)/ {print $2}' | sort -u) # If the test fails/errors, then expectedFailure it for test in "${failed_tests[@]}" do - sed -i "s/^\([[:space:]]*\)def $test(/\1@unittest.expectedFailure # TODO: RUSTPYTHON\n\1def $test(/" "$rpython_file" + add_above_test $rlib_path $test "@unittest.expectedFailure # TODO: RUSTPYTHON" done # If the test crashes/hangs, then skip it @@ -117,16 +157,13 @@ update_test() { if grep -q "Timeout" <<< "$output"; then message="; hanging" fi - sed -i "s/^\([[:space:]]*\)def $crashing_test(/\1@unittest.skip('TODO: RUSTPYTHON$message')\n\1def $crashing_test(/" "$rpython_file" + add_above_test $rlib_path $crashing_test "@unittest.skip('TODO: RUSTPYTHON$message')" fi output=$(rustpython $lib 2>&1) done - fi } - - files_equal() { file1=$1 file2=$2 @@ -138,4 +175,22 @@ rustpython() { cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --fail-env-changed --timeout 300 -v "$@" } -update_tests \ No newline at end of file +add_above_test() { + file=$1 + test=$2 + line=$3 + sed -i "s/^\([[:space:]]*\)def $test(/\1$line\n\1def $test(/" "$file" +} + +remove_skips() { + rlib_path=$1 + sed -i -E '/^[[:space:]]*@unittest\.skip.*\(["'\'']TODO\s?:\s?RUSTPYTHON.*["'\'']\)/Id' $rlib_path +} + +if ! $check_skip_flag; then + echo "Updating Tests" + update_tests $libraries +else + echo "Checking Skips" + check_skips $libraries +fi From f6b69b769108c9fc890137e096bdb6019ced3d85 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sat, 10 Jan 2026 23:58:37 -0500 Subject: [PATCH 06/12] Some quality of life changes + bug fixes --- scripts/update_tests.sh | 72 +++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index 42fda883a6..e2bc154589 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -68,7 +68,7 @@ while [[ $# -gt 0 ]]; do shift ;; *) - libraries+=("$1.py") + libraries+=("$1") shift ;; esac @@ -77,10 +77,6 @@ done cpython_path="$cpython_path/Lib/test" rpython_path="$rpython_path/Lib/test" -if [[ ${#libraries[@]} -eq 0 ]]; then - libraries=$(find ${cpython_path} -type f -printf "%P\n") -fi - update_tests() { libraries=$1 @@ -97,7 +93,7 @@ update_test() { if files_equal "$clib_path" "$rlib_path"; then echo "No changes in $lib. Skipping..." - continue + return fi if [[ ! -f "$rlib_path" ]]; then @@ -112,7 +108,7 @@ update_test() { fi - if $annotate; then + if [[ $annotate && -f "$rlib_path" && $(basename -- "$rlib_path") == test_*.py ]]; then annotate_lib $lib $rlib_path fi } @@ -135,33 +131,38 @@ check_skip() { } annotate_lib() { - lib=$1 - rlib_path=$2 - output=$(rustpython $lib 2>&1) + lib=$(echo "$1" | sed 's/\//./g') + rlib_path=$2 + output=$(rustpython $lib 2>&1) + + if grep -q "NO TESTS RAN" <<< "$output"; then + echo "No tests ran in $lib. skipping annotation" + return + fi + + echo "Annotating $lib" + + while ! grep -q "Tests result: SUCCESS" <<< "$output"; do + echo "$lib failing, annotating..." + readarray -t failed_tests <<< $(echo "$output" | awk '/^(FAIL:|ERROR:)/ {print $2}' | sort -u) + + # If the test fails/errors, then expectedFailure it + for test in "${failed_tests[@]}" + do + add_above_test $rlib_path $test "@unittest.expectedFailure # TODO: RUSTPYTHON" + done - echo "Annotating $lib" - - while ! grep -q "Tests result: SUCCESS" <<< "$output"; do - echo "$lib failing, annotating..." - readarray -t failed_tests <<< $(echo "$output" | awk '/^(FAIL:|ERROR:)/ {print $2}' | sort -u) - - # If the test fails/errors, then expectedFailure it - for test in "${failed_tests[@]}" - do - add_above_test $rlib_path $test "@unittest.expectedFailure # TODO: RUSTPYTHON" - done - - # If the test crashes/hangs, then skip it - if grep -q "\.\.\.$" <<< "$output"; then - crashing_test=$(echo "$output" | grep '\.\.\.$' | head -n 1 | awk '{print $1}') - if grep -q "Timeout" <<< "$output"; then - message="; hanging" - fi - add_above_test $rlib_path $crashing_test "@unittest.skip('TODO: RUSTPYTHON$message')" + # If the test crashes/hangs, then skip it + if grep -q "\.\.\.$" <<< "$output"; then + crashing_test=$(echo "$output" | grep '\.\.\.$' | head -n 1 | awk '{print $1}') + if grep -q "Timeout" <<< "$output"; then + message="; hanging" fi + add_above_test $rlib_path $crashing_test "@unittest.skip('TODO: RUSTPYTHON$message')" + fi - output=$(rustpython $lib 2>&1) - done + output=$(rustpython $lib 2>&1) + done } files_equal() { @@ -184,13 +185,22 @@ add_above_test() { remove_skips() { rlib_path=$1 + + echo "Removing all skips from $rlib_path" + sed -i -E '/^[[:space:]]*@unittest\.skip.*\(["'\'']TODO\s?:\s?RUSTPYTHON.*["'\'']\)/Id' $rlib_path } if ! $check_skip_flag; then echo "Updating Tests" + + if [[ ${#libraries[@]} -eq 0 ]]; then + readarray -t libraries <<< $(find ${cpython_path} -type f -printf "%P\n") + fi update_tests $libraries else echo "Checking Skips" + + readarray -t libraries <<< $(find ${rpython_path} -iname "test_*.py" -type f -printf "%P\n") check_skips $libraries fi From e9265bd5c1ec10bb11bf91a84e2e2eca56d77d55 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sun, 11 Jan 2026 00:11:49 -0500 Subject: [PATCH 07/12] Added local annotations + added initial concurrency + added a limit on amount of times the annotation loop can run + fixed bugs --- scripts/update_tests.sh | 57 ++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index e2bc154589..b0f0c81884 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -25,7 +25,6 @@ EOF if [[ $# -eq 0 ]]; then usage - exit 1 fi @@ -53,14 +52,14 @@ while [[ $# -gt 0 ]]; do ;; -h|--help) usage - return + exit 1 ;; -s|--check-skipped) check_skip_flag=true shift ;; -t|--timeout) - timout="$2" + timeout="$2" shift 2 ;; -a|--annotate) @@ -79,17 +78,18 @@ rpython_path="$rpython_path/Lib/test" update_tests() { - libraries=$1 + local libraries=("$@") for lib in "${libraries[@]}" do - update_test "$lib" + update_test "$lib" & done + wait } update_test() { - lib=$1 - clib_path="$cpython_path/$lib" - rlib_path="$rpython_path/$lib" + local lib=$1 + local clib_path="$cpython_path/$lib" + local rlib_path="$rpython_path/$lib" if files_equal "$clib_path" "$rlib_path"; then echo "No changes in $lib. Skipping..." @@ -114,16 +114,17 @@ update_test() { } check_skips() { - libraries=$1 + local libraries=("$@") for lib in "${libraries[@]}" do - check_skip "$lib" + check_skip "$lib" & done + wait } check_skip() { - lib=$1 - rlib_path="$rpython_path/$lib" + local lib=$1 + local rlib_path="$rpython_path/$lib" remove_skips $rlib_path @@ -131,9 +132,9 @@ check_skip() { } annotate_lib() { - lib=$(echo "$1" | sed 's/\//./g') - rlib_path=$2 - output=$(rustpython $lib 2>&1) + local lib=$(echo "$1" | sed 's/\//./g') + local rlib_path=$2 + local output=$(rustpython $lib 2>&1) if grep -q "NO TESTS RAN" <<< "$output"; then echo "No tests ran in $lib. skipping annotation" @@ -142,7 +143,9 @@ annotate_lib() { echo "Annotating $lib" + local attempts=0 while ! grep -q "Tests result: SUCCESS" <<< "$output"; do + ((attempts++)) echo "$lib failing, annotating..." readarray -t failed_tests <<< $(echo "$output" | awk '/^(FAIL:|ERROR:)/ {print $2}' | sort -u) @@ -162,29 +165,31 @@ annotate_lib() { fi output=$(rustpython $lib 2>&1) + + if [[ attempts -gt 10 ]]; then + echo "Issue annotating $lib" + break; + fi done } files_equal() { - file1=$1 - file2=$2 - cmp --silent $file1 $file2 && files_equal=0 || files_equal=1 - return $files_equal + [[ -f "$1" && -f "$2" ]] && cmp --silent "$1" "$2" } rustpython() { - cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --fail-env-changed --timeout 300 -v "$@" + cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --fail-env-changed --timeout "$timeout" -v "$@" } add_above_test() { - file=$1 - test=$2 - line=$3 + local file=$1 + local test=$2 + local line=$3 sed -i "s/^\([[:space:]]*\)def $test(/\1$line\n\1def $test(/" "$file" } remove_skips() { - rlib_path=$1 + local rlib_path=$1 echo "Removing all skips from $rlib_path" @@ -197,10 +202,10 @@ if ! $check_skip_flag; then if [[ ${#libraries[@]} -eq 0 ]]; then readarray -t libraries <<< $(find ${cpython_path} -type f -printf "%P\n") fi - update_tests $libraries + update_tests "${libraries[@]}" else echo "Checking Skips" readarray -t libraries <<< $(find ${rpython_path} -iname "test_*.py" -type f -printf "%P\n") - check_skips $libraries + check_skips "${libraries[@]}" fi From 28ef3b46b94c22ef6b60fede8ca77c270ed197f6 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sun, 11 Jan 2026 00:36:18 -0500 Subject: [PATCH 08/12] Added a semaphore + limited number of jobs at the same time --- scripts/update_tests.sh | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index b0f0c81884..60de4d7ac1 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -14,6 +14,7 @@ Options: -s/--check-skipped Check existing skipped tests (must be run separate from updating the tests) -t/--timeout Set a timeout for a test -a/--annotate While copying tests, run them and annotate failures dynamically + -j/--jobs How many libraries can be processed at a time -h/--help Show this help message and exit Example Usage: @@ -34,6 +35,7 @@ copy_untracked=false annotate=false timeout=300 check_skip_flag=false +num_jobs=5 libraries=() while [[ $# -gt 0 ]]; do @@ -66,6 +68,10 @@ while [[ $# -gt 0 ]]; do annotate=true shift ;; + -j|--jobs) + num_jobs="$2" + shift 2 + ;; *) libraries+=("$1") shift @@ -81,6 +87,7 @@ update_tests() { local libraries=("$@") for lib in "${libraries[@]}" do + sem update_test "$lib" & done wait @@ -91,7 +98,7 @@ update_test() { local clib_path="$cpython_path/$lib" local rlib_path="$rpython_path/$lib" - if files_equal "$clib_path" "$rlib_path"; then + if [[ -f "$clib_path" && -f "$rlib_path" ]] && files_equal "$clib_path" "$rlib_path"; then echo "No changes in $lib. Skipping..." return fi @@ -117,6 +124,7 @@ check_skips() { local libraries=("$@") for lib in "${libraries[@]}" do + sem check_skip "$lib" & done wait @@ -132,7 +140,7 @@ check_skip() { } annotate_lib() { - local lib=$(echo "$1" | sed 's/\//./g') + local lib=${1//\//.} local rlib_path=$2 local output=$(rustpython $lib 2>&1) @@ -147,7 +155,7 @@ annotate_lib() { while ! grep -q "Tests result: SUCCESS" <<< "$output"; do ((attempts++)) echo "$lib failing, annotating..." - readarray -t failed_tests <<< $(echo "$output" | awk '/^(FAIL:|ERROR:)/ {print $2}' | sort -u) + readarray -t failed_tests <<< "$(echo "$output" | awk '/^(FAIL:|ERROR:)/ {print $2}' | sort -u)" # If the test fails/errors, then expectedFailure it for test in "${failed_tests[@]}" @@ -159,28 +167,34 @@ annotate_lib() { if grep -q "\.\.\.$" <<< "$output"; then crashing_test=$(echo "$output" | grep '\.\.\.$' | head -n 1 | awk '{print $1}') if grep -q "Timeout" <<< "$output"; then - message="; hanging" + message=" hanging" fi - add_above_test $rlib_path $crashing_test "@unittest.skip('TODO: RUSTPYTHON$message')" + add_above_test $rlib_path $crashing_test "@unittest.skip('TODO: RUSTPYTHON;$message')" fi output=$(rustpython $lib 2>&1) if [[ attempts -gt 10 ]]; then - echo "Issue annotating $lib" + echo "Issue annotating $lib" >&2 break; fi done } files_equal() { - [[ -f "$1" && -f "$2" ]] && cmp --silent "$1" "$2" + cmp --silent "$1" "$2" } rustpython() { cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --fail-env-changed --timeout "$timeout" -v "$@" } +sem() { + while (( $(jobs -rp | wc -l) >= $num_jobs )); do + sleep 0.1 # brief pause before checking again + done +} + add_above_test() { local file=$1 local test=$2 From 61e485bdccbc11cfe26243d66a935b6d98f3f628 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sun, 11 Jan 2026 02:35:39 -0500 Subject: [PATCH 09/12] Added backing up and reapplying skips --- scripts/update_tests.sh | 55 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index 60de4d7ac1..4cdf947de0 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -20,12 +20,17 @@ Options: Example Usage: $0 -c ~/cpython -r . $0 -r . --check-skipped + +** Notes: + * When using the update skip functionality + * Updating only looks for files with the format "test_*.py". Everything else (including __init__.py and __main__.py files are ignored) + * Care needs to be taken when updating. All tests with the same name will be updated at the same time EOF exit 1 } if [[ $# -eq 0 ]]; then - usage + usage fi @@ -167,9 +172,11 @@ annotate_lib() { if grep -q "\.\.\.$" <<< "$output"; then crashing_test=$(echo "$output" | grep '\.\.\.$' | head -n 1 | awk '{print $1}') if grep -q "Timeout" <<< "$output"; then - message=" hanging" + hanging=true + else + hanging=false fi - add_above_test $rlib_path $crashing_test "@unittest.skip('TODO: RUSTPYTHON;$message')" + apply_skip "$rlib_path" "$crashing_test" $hanging fi output=$(rustpython $lib 2>&1) @@ -179,6 +186,8 @@ annotate_lib() { break; fi done + + unset SKIP_BACKUP } files_equal() { @@ -207,9 +216,45 @@ remove_skips() { echo "Removing all skips from $rlib_path" + backup_skips "$rlib_path" + sed -i -E '/^[[:space:]]*@unittest\.skip.*\(["'\'']TODO\s?:\s?RUSTPYTHON.*["'\'']\)/Id' $rlib_path } +apply_skip() { + local rlib_path=$1 + local test_name=$2 + local hanging=$3 + message="unknown" + + # Check if the test has a backup skip + if [[ -n "${SKIP_BACKUP[$test_name]}" ]]; then + message="${SKIP_BACKUP[$test_name]//\'/\"}" + echo "Message is $message" + elif $hanging; then + message="hanging" + fi + + add_above_test "$rlib_path" "$test_name" "@unittest.skip('TODO: RUSTPYTHON; $message')" +} + +backup_skips() { + local rlib_path=$1 + declare -gA SKIP_BACKUP=() # global associative array + readarray -t skips < <(grep -E -n "^[[:space:]]*@unittest\.skip.*TODO\s?:\s?RUSTPYTHON" "$rlib_path" | sort -u) + + for line in "${skips[@]}"; do + line_num="${line%%:*}" + line_text=$(echo "$line" | grep -oPi "(?<=RUSTPYTHON)\s*[;:]\s*\K(.*)?(?=[\"'])") + next_line=$(sed -n "$((line_num + 1))p" "$rlib_path") + + if [[ "$next_line" =~ def[[:space:]]+([a-zA-Z0-9_]+)\( ]]; then + test_name="${BASH_REMATCH[1]}" + SKIP_BACKUP[$test_name]="$line_text" + fi + done +} + if ! $check_skip_flag; then echo "Updating Tests" @@ -220,6 +265,8 @@ if ! $check_skip_flag; then else echo "Checking Skips" - readarray -t libraries <<< $(find ${rpython_path} -iname "test_*.py" -type f -printf "%P\n") + if [[ ${#libraries[@]} -eq 0 ]]; then + readarray -t libraries <<< $(find ${rpython_path} -iname "test_*.py" -type f -printf "%P\n") + fi check_skips "${libraries[@]}" fi From fdb476d657a4b8ca0f101a56e727d4ef14e82f14 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sun, 11 Jan 2026 21:01:11 -0500 Subject: [PATCH 10/12] Added ignored_libraries for multiprocessing and concurrent libraries Added known limitations --- scripts/update_tests.sh | 47 +++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index 4cdf947de0..fa7afd0730 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -6,12 +6,13 @@ Usage: $0 [OPTIONS] Copy tests from CPython to RustPython. Optionally copy untracked tests, and dynamic annotation of failures. +This updater is not meant to be used as a standalone updater. Care needs to be taken to review everything done Options: -c/--cpython-path Path to the CPython source tree (older version) -r/--rpython-path Path to the RustPython source tree (newer version) -u/--copy-untracked Copy untracked tests only - -s/--check-skipped Check existing skipped tests (must be run separate from updating the tests) + -s/--update-skipped Update existing skipped tests (must be run separate from updating the tests) -t/--timeout Set a timeout for a test -a/--annotate While copying tests, run them and annotate failures dynamically -j/--jobs How many libraries can be processed at a time @@ -21,10 +22,17 @@ Example Usage: $0 -c ~/cpython -r . $0 -r . --check-skipped -** Notes: +*Notes: * When using the update skip functionality * Updating only looks for files with the format "test_*.py". Everything else (including __init__.py and __main__.py files are ignored) - * Care needs to be taken when updating. All tests with the same name will be updated at the same time +**Known limitations: + * In multithreaded tests, if the tests are orphaned, then the updater can deadlock, as threads can accumulate and block the semaphore + * In multithreaded tests, when annotating, multiple decorators can accumulate on one test + * The updater does not add skips to classes, only on tests + * If there are multiple tests with the same name, a decorator will be added to all of them + * The updater does not retain anything more specific than a general skip (skipIf/Unless will be replaced by a general skip) + * Currently, the updater does not take unexpected successes into account + EOF exit 1 } @@ -40,8 +48,9 @@ copy_untracked=false annotate=false timeout=300 check_skip_flag=false -num_jobs=5 +num_jobs=20 libraries=() +ignored_libraries=("multiprocessing" "concurrent") while [[ $# -gt 0 ]]; do case "$1" in @@ -61,7 +70,7 @@ while [[ $# -gt 0 ]]; do usage exit 1 ;; - -s|--check-skipped) + -s|--update-skipped) check_skip_flag=true shift ;; @@ -159,13 +168,17 @@ annotate_lib() { local attempts=0 while ! grep -q "Tests result: SUCCESS" <<< "$output"; do ((attempts++)) - echo "$lib failing, annotating..." + # echo "$lib failing, annotating..." readarray -t failed_tests <<< "$(echo "$output" | awk '/^(FAIL:|ERROR:)/ {print $2}' | sort -u)" # If the test fails/errors, then expectedFailure it for test in "${failed_tests[@]}" do - add_above_test $rlib_path $test "@unittest.expectedFailure # TODO: RUSTPYTHON" + if already_failed $rlib_path $test; then + replace_expected_with_skip $rlib_path $test + else + add_above_test $rlib_path $test "@unittest.expectedFailure # TODO: RUSTPYTHON" + fi done # If the test crashes/hangs, then skip it @@ -183,13 +196,26 @@ annotate_lib() { if [[ attempts -gt 10 ]]; then echo "Issue annotating $lib" >&2 - break; + return; fi done + echo "Successfully updated $lib" unset SKIP_BACKUP } +replace_expected_with_skip() { + file=$1 + test_name=$2 + sed -E "/^\s*@unittest\.expectedFailure\s+# TODO: RUSTPYTHON/ { N; /\n\s*def $test_name/ { s/^(\s*)@unittest\.expectedFailure\s+# TODO: RUSTPYTHON/\1@unittest.skip\('TODO: RUSTPYTHON'\)/ } }" -i $file +} + +already_failed() { + file=$1 + test_name=$2 + grep -Pz "\s*@unittest\.expectedFailure # TODO: RUSTPYTHON\n\s*def\s+${test_name}\(" $file +} + files_equal() { cmp --silent "$1" "$2" } @@ -230,7 +256,6 @@ apply_skip() { # Check if the test has a backup skip if [[ -n "${SKIP_BACKUP[$test_name]}" ]]; then message="${SKIP_BACKUP[$test_name]//\'/\"}" - echo "Message is $message" elif $hanging; then message="hanging" fi @@ -259,14 +284,14 @@ if ! $check_skip_flag; then echo "Updating Tests" if [[ ${#libraries[@]} -eq 0 ]]; then - readarray -t libraries <<< $(find ${cpython_path} -type f -printf "%P\n") + readarray -t libraries <<< $(find ${cpython_path} -type f -printf "%P\n" | grep -vE "$(IFS=\|; echo "${ignored_libraries[*]}")") fi update_tests "${libraries[@]}" else echo "Checking Skips" if [[ ${#libraries[@]} -eq 0 ]]; then - readarray -t libraries <<< $(find ${rpython_path} -iname "test_*.py" -type f -printf "%P\n") + readarray -t libraries <<< $(find ${rpython_path} -iname "test_*.py" -type f -printf "%P\n" | grep -vE "$(IFS=\|; echo "${ignored_libraries[*]}")") fi check_skips "${libraries[@]}" fi From 5b9ae5c9e21e6e68d77ef9fb85b679ea220b597c Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sun, 11 Jan 2026 22:31:28 -0500 Subject: [PATCH 11/12] When checking skips skipping tests with no skip in them + updated documentation + quieted grep --- scripts/update_tests.sh | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index fa7afd0730..8fc67a4804 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -11,23 +11,24 @@ This updater is not meant to be used as a standalone updater. Care needs to be t Options: -c/--cpython-path Path to the CPython source tree (older version) -r/--rpython-path Path to the RustPython source tree (newer version) - -u/--copy-untracked Copy untracked tests only -s/--update-skipped Update existing skipped tests (must be run separate from updating the tests) - -t/--timeout Set a timeout for a test -a/--annotate While copying tests, run them and annotate failures dynamically + -u/--copy-untracked Copy untracked tests + -t/--timeout Set a timeout for a test -j/--jobs How many libraries can be processed at a time -h/--help Show this help message and exit Example Usage: -$0 -c ~/cpython -r . -$0 -r . --check-skipped +$0 -c ~/cpython -r - . -t 300 # Updates all non-updated tests with a timeout value of 300 seconds +$0 -c ~/cpython -r . -u -j 5 # Updates all non-updated tests + copies files not in cpython into rpython, with maximum 5 processes active at a time +$0 -c ~/cpython -r . -a # Updates all non-updated tests + annotates with @unittest.expectedFailure/@unittest.skip +$0 -r . -s # For all current tests, check if @unittest.skip can be downgraded to @unittest.expectedFailure *Notes: * When using the update skip functionality * Updating only looks for files with the format "test_*.py". Everything else (including __init__.py and __main__.py files are ignored) **Known limitations: * In multithreaded tests, if the tests are orphaned, then the updater can deadlock, as threads can accumulate and block the semaphore - * In multithreaded tests, when annotating, multiple decorators can accumulate on one test * The updater does not add skips to classes, only on tests * If there are multiple tests with the same name, a decorator will be added to all of them * The updater does not retain anything more specific than a general skip (skipIf/Unless will be replaced by a general skip) @@ -138,8 +139,12 @@ check_skips() { local libraries=("$@") for lib in "${libraries[@]}" do - sem - check_skip "$lib" & + if grep -qiE "@unittest.skip.*\('TODO:\s*RUSTPYTHON.*'\)" "$rpython_path/$lib"; then + sem + check_skip "$lib" & + else + echo "Skipping $lib" >&2 + fi done wait } @@ -213,7 +218,7 @@ replace_expected_with_skip() { already_failed() { file=$1 test_name=$2 - grep -Pz "\s*@unittest\.expectedFailure # TODO: RUSTPYTHON\n\s*def\s+${test_name}\(" $file + grep -qPz "\s*@unittest\.expectedFailure # TODO: RUSTPYTHON\n\s*def\s+${test_name}\(" $file } files_equal() { From db005d465c44ca18b37fd0223bd6fa6d38ec0202 Mon Sep 17 00:00:00 2001 From: Terry Luan Date: Sun, 11 Jan 2026 23:36:04 -0500 Subject: [PATCH 12/12] Rearranged the update_tests.sh to be easier to read + fixed minor bugs/formatting issues --- scripts/update_tests.sh | 155 ++++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 69 deletions(-) diff --git a/scripts/update_tests.sh b/scripts/update_tests.sh index 8fc67a4804..ff65c7216b 100755 --- a/scripts/update_tests.sh +++ b/scripts/update_tests.sh @@ -19,7 +19,7 @@ Options: -h/--help Show this help message and exit Example Usage: -$0 -c ~/cpython -r - . -t 300 # Updates all non-updated tests with a timeout value of 300 seconds +$0 -c ~/cpython -r . -t 300 # Updates all non-updated tests with a timeout value of 300 seconds $0 -c ~/cpython -r . -u -j 5 # Updates all non-updated tests + copies files not in cpython into rpython, with maximum 5 processes active at a time $0 -c ~/cpython -r . -a # Updates all non-updated tests + annotates with @unittest.expectedFailure/@unittest.skip $0 -r . -s # For all current tests, check if @unittest.skip can be downgraded to @unittest.expectedFailure @@ -56,11 +56,11 @@ ignored_libraries=("multiprocessing" "concurrent") while [[ $# -gt 0 ]]; do case "$1" in -c|--cpython-path) - cpython_path="$2" + cpython_path="$2/Lib/test" shift 2 ;; -r|--rpython-path) - rpython_path="$2" + rpython_path="$2/Lib/test" shift 2 ;; -u|--copy-untracked) @@ -93,10 +93,13 @@ while [[ $# -gt 0 ]]; do ;; esac done +# -------------------------------------- Constants ------------------------------------- # +RUSTPYTHON_POSSIBLE_SKIP_RE="@unittest.skip.*\([\"']TODO:\s*RUSTPYTHON.*[\"']\)" +RUSTPYTHON_CANONICAL_SKIP_RE="@unittest.skip\('TODO: RUSTPYTHON; .*'\)" +RUSTPYTHON_CANONICAL_EX_FAILURE="@unittest.expectedFailure # TODO: RUSTPYTHON" +RUSTPYTHON_CANONICAL_EX_FAILURE_RE="\s@unittest\.expectedFailure # TODO: RUSTPYTHON.*" -cpython_path="$cpython_path/Lib/test" -rpython_path="$rpython_path/Lib/test" - +# --------------------------------- Updating functions --------------------------------- # update_tests() { local libraries=("$@") @@ -135,11 +138,13 @@ update_test() { fi } +# --------------------------------- Downgrade Skips functions --------------------------------- # + check_skips() { local libraries=("$@") for lib in "${libraries[@]}" do - if grep -qiE "@unittest.skip.*\('TODO:\s*RUSTPYTHON.*'\)" "$rpython_path/$lib"; then + if grep -qiE "$RUSTPYTHON_POSSIBLE_SKIP_RE" "$rpython_path/$lib"; then sem check_skip "$lib" & else @@ -158,6 +163,41 @@ check_skip() { annotate_lib $lib $rlib_path } +apply_skip() { + local rlib_path=$1 + local test_name=$2 + local hanging=$3 + message="unknown" + + # Check if the test has a backup skip + if [[ -n "${SKIP_BACKUP[$test_name]}" ]]; then + message="${SKIP_BACKUP[$test_name]//\'/\"}" + elif $hanging; then + message="hanging" + fi + + add_above_test "$rlib_path" "$test_name" "@unittest.skip('TODO: RUSTPYTHON; $message')" +} + +backup_skips() { + local rlib_path=$1 + declare -gA SKIP_BACKUP=() # global associative array + readarray -t skips < <(grep -E -n "^[[:space:]]*@unittest\.skip.*TODO\s?:\s?RUSTPYTHON" "$rlib_path" | sort -u) + + for line in "${skips[@]}"; do + line_num="${line%%:*}" + line_text=$(echo "$line" | grep -oPi "(?<=RUSTPYTHON)\s*[;:]\s*\K(.*)?(?=[\"'])") + next_line=$(sed -n "$((line_num + 1))p" "$rlib_path") + + if [[ "$next_line" =~ def[[:space:]]+([a-zA-Z0-9_]+)\( ]]; then + test_name="${BASH_REMATCH[1]}" + SKIP_BACKUP[$test_name]="$line_text" + fi + done +} + +# --------------------------------- General functions --------------------------------- # + annotate_lib() { local lib=${1//\//.} local rlib_path=$2 @@ -182,7 +222,7 @@ annotate_lib() { if already_failed $rlib_path $test; then replace_expected_with_skip $rlib_path $test else - add_above_test $rlib_path $test "@unittest.expectedFailure # TODO: RUSTPYTHON" + add_above_test $rlib_path $test "$RUSTPYTHON_CANONICAL_EX_FAILURE" fi done @@ -199,7 +239,7 @@ annotate_lib() { output=$(rustpython $lib 2>&1) - if [[ attempts -gt 10 ]]; then + if [[ $attempts -gt 15 ]]; then echo "Issue annotating $lib" >&2 return; fi @@ -209,38 +249,41 @@ annotate_lib() { unset SKIP_BACKUP } +sem() { + while (( $(jobs -rp | wc -l) >= $num_jobs )); do + sleep 0.1 # brief pause before checking again + done +} + +add_above_test() { + local file=$1 + local test=$2 + local line=$3 + sed -i "s/^\([[:space:]]*\)def $test(/\1$line\n\1def $test(/" "$file" +} + +# --------------------------------- Utility functions --------------------------------- # + +rustpython() { + cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --fail-env-changed --timeout "$timeout" -v "$@" +} + replace_expected_with_skip() { file=$1 test_name=$2 - sed -E "/^\s*@unittest\.expectedFailure\s+# TODO: RUSTPYTHON/ { N; /\n\s*def $test_name/ { s/^(\s*)@unittest\.expectedFailure\s+# TODO: RUSTPYTHON/\1@unittest.skip\('TODO: RUSTPYTHON'\)/ } }" -i $file + sed -E "/$RUSTPYTHON_CANONICAL_EX_FAILURE_RE/ { N; /\n\s*def $test_name/ { s/^(\s*)@unittest\.expectedFailure\s+# TODO: RUSTPYTHON/\1@unittest.skip\('TODO: RUSTPYTHON'\)/ } }" -i $file } already_failed() { file=$1 test_name=$2 - grep -qPz "\s*@unittest\.expectedFailure # TODO: RUSTPYTHON\n\s*def\s+${test_name}\(" $file + grep -qPz "$RUSTPYTHON_CANONICAL_EX_FAILURE_RE\n\s*def\s+${test_name}\(" $file } files_equal() { cmp --silent "$1" "$2" } -rustpython() { - cargo run --release --features encodings,sqlite -- -m test -j 1 -u all --fail-env-changed --timeout "$timeout" -v "$@" -} - -sem() { - while (( $(jobs -rp | wc -l) >= $num_jobs )); do - sleep 0.1 # brief pause before checking again - done -} - -add_above_test() { - local file=$1 - local test=$2 - local line=$3 - sed -i "s/^\([[:space:]]*\)def $test(/\1$line\n\1def $test(/" "$file" -} remove_skips() { local rlib_path=$1 @@ -252,51 +295,25 @@ remove_skips() { sed -i -E '/^[[:space:]]*@unittest\.skip.*\(["'\'']TODO\s?:\s?RUSTPYTHON.*["'\'']\)/Id' $rlib_path } -apply_skip() { - local rlib_path=$1 - local test_name=$2 - local hanging=$3 - message="unknown" - - # Check if the test has a backup skip - if [[ -n "${SKIP_BACKUP[$test_name]}" ]]; then - message="${SKIP_BACKUP[$test_name]//\'/\"}" - elif $hanging; then - message="hanging" - fi - - add_above_test "$rlib_path" "$test_name" "@unittest.skip('TODO: RUSTPYTHON; $message')" -} - -backup_skips() { - local rlib_path=$1 - declare -gA SKIP_BACKUP=() # global associative array - readarray -t skips < <(grep -E -n "^[[:space:]]*@unittest\.skip.*TODO\s?:\s?RUSTPYTHON" "$rlib_path" | sort -u) +main() { + if ! $check_skip_flag; then + echo "Updating Tests" - for line in "${skips[@]}"; do - line_num="${line%%:*}" - line_text=$(echo "$line" | grep -oPi "(?<=RUSTPYTHON)\s*[;:]\s*\K(.*)?(?=[\"'])") - next_line=$(sed -n "$((line_num + 1))p" "$rlib_path") + # If libraries are not specified, then update all tests + if [[ "${#libraries[@]}" -eq 0 ]]; then + readarray -t libraries <<< $(find ${cpython_path} -type f -printf "%P\n" | grep -vE "$(IFS=\|; echo "${ignored_libraries[*]}")") + fi + update_tests "${libraries[@]}" + else + echo "Checking Skips" - if [[ "$next_line" =~ def[[:space:]]+([a-zA-Z0-9_]+)\( ]]; then - test_name="${BASH_REMATCH[1]}" - SKIP_BACKUP[$test_name]="$line_text" + # If libraries are not specified, then check all tests + if [[ ${#libraries[@]} -eq 0 ]]; then + readarray -t libraries <<< $(find ${rpython_path} -iname "test_*.py" -type f -printf "%P\n" | grep -vE "$(IFS=\|; echo "${ignored_libraries[*]}")") fi - done + check_skips "${libraries[@]}" + fi } -if ! $check_skip_flag; then - echo "Updating Tests" - - if [[ ${#libraries[@]} -eq 0 ]]; then - readarray -t libraries <<< $(find ${cpython_path} -type f -printf "%P\n" | grep -vE "$(IFS=\|; echo "${ignored_libraries[*]}")") - fi - update_tests "${libraries[@]}" -else - echo "Checking Skips" - if [[ ${#libraries[@]} -eq 0 ]]; then - readarray -t libraries <<< $(find ${rpython_path} -iname "test_*.py" -type f -printf "%P\n" | grep -vE "$(IFS=\|; echo "${ignored_libraries[*]}")") - fi - check_skips "${libraries[@]}" -fi +main \ No newline at end of file