#!/usr/bin/env bash # # wrapper around partest for fine-grained test selection via ack declare quiet failed update partest_debug file_regex partest_args ack_args cotouched [[ $# -gt 0 ]] || { cat < [-dfquvp] [ack options] -d pass --debug to partest -f pass --failed to partest -q DON'T pass --show-log and --show-diff to partest -u pass --update-check to partest -v pass --verbose to partest -p select tests appearing in commits where was also modified Given a regular expression (and optionally, any arguments accepted by ack) runs all the tests for which any associated file matches the regex. Associated files include .check and .flags files. Tests in directories will match if any file matches. A file can match the regex by its contents or by its name. You must have ack installed: http://betterthangrep.com/ack-standalone Examples: > tools/partest-ack monad % tests-with-matching-paths ... 2 % tests-with-matching-code ... 2 # 4 tests to run. > tools/partest-ack -p src/library/scala/Enumeration.scala % tests-modified-in-same-commit ... 84 # 84 tests to run. > tools/partest-ack -f % tests-which-failed ... 42 # 42 tests to run. EOM exit 0 } # The leading : in :achs suppresses some errors. Each letter is a valid # option. If an option takes an argument, a colon follows it, e.g. # it would be :ach:s if -h took an argument. while getopts :fuvdp: opt; do case $opt in d) partest_debug=true && partest_args="$partest_args --debug" ;; f) failed=true && partest_args="$partest_args --failed" ;; p) cotouched="$cotouched $OPTARG" ;; q) quiet=true ;; u) partest_args="$partest_args --update-check" ;; v) partest_args="$partest_args --verbose" ;; :) echo "Option -$OPTARG requires an argument." >&2 ;; # this case is called for a missing option argument *) echo "Unrecognized argument $OPTARG" ;; # this is the catch-all implying an unknown option esac done shift $((OPTIND-1)) file_regex="$1" ack_args="$*" tests () { find test/files -mindepth 2 -maxdepth 2 -name '*.scala' -o -type d } pathsToTests () { for path in $(perl -pe 's#^(test/files/[^/]+/[^/.]+).*$#$1#'); do if [[ -d "$path" ]]; then echo "$path" elif [[ -f "$path.scala" ]]; then echo "$path.scala" fi done | sort -u } tests-with-matching-paths() { local re="$1" for p in $(find test/files -type f); do [[ $p =~ $re ]] && echo "$p" done } tests-which-failed () { for f in $(find test/files -name '*.log'); do echo ${f%-*} done } tests-modified-in-same-commit() { [[ $# -gt 0 ]] && \ for rev in $(git rev-list HEAD -- "$@"); do git --no-pager show --pretty="format:" --name-only "$rev" -- test/files done } tests-with-matching-code() { ack --noenv --text --files-with-matches "$@" -- test/files } countStdout () { local -i count=0 while read line; do printf "$line\n" count+=1 done printf >&2 " $count\n" } testRun () { printf >&2 "%% %-30s ... " "$1" "$@" | pathsToTests | countStdout } allMatches() { [[ -n $file_regex ]] && testRun tests-with-matching-paths $file_regex [[ -n $cotouched ]] && testRun tests-modified-in-same-commit $cotouched [[ -n $ack_args ]] && testRun tests-with-matching-code $ack_args [[ -n $failed ]] && testRun tests-which-failed } paths=$(allMatches | sort -u) [[ -n $quiet ]] || partest_args="--show-diff --show-log $partest_args" if [[ -z $paths ]] && [[ -z $failed ]]; then echo >&2 "No matching tests." else count=$(echo $(echo "$paths" | wc -w)) # Output a command line which will re-run these same tests. echo "# $count tests to run." printf "%-52s %s\n" "test/partest $partest_args" "\\" for path in $paths; do printf " %-50s %s\n" "$path" "\\" done echo ' ""' test/partest $partest_args $paths fi