summaryrefslogtreecommitdiff
path: root/tools/partest-ack
blob: ab722e3b1c22f97a59f3a0c235dd528c6fd8347c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env bash
#
# wrapper around partest for fine-grained test selection via ack

declare quiet failed update partest_debug
declare cotouched since sortCommand
declare -a ack_args partest_args scalac_args
declare -r standard_ack_args="--noenv -s --java --scala --type-add=scala:ext:flags,check --files-with-matches"

partest_args=( --show-diff )
bindir="$(cd "$(dirname "$0")" && pwd)"
base="$bindir/.."
cd "$base" || { echo "Could not change to base directory $base" && exit 1; }
filesdir="test/files"
sortCommand="sort -u"
partestPaths="$bindir/partest-paths"

[[ -x "$partestPaths" ]] || { echo "Cannot find partest-paths in $partestPaths" && exit 1; }

[[ $# -gt 0 ]] || {
  cat <<EOM
Usage: $0 <regex> [-dfquvp] [ack options]

  -f          pass --failed to partest
  -q          pass --terse to partest
  -u          pass --update-check to partest
  -p <path>   select tests appearing in commits where <path> was also modified
  -s <time>   select tests touched since <time> (git format, e.g. 1.month.ago)
  -r          run tests in random order

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 version 2.12+ installed: http://beyondgrep.com/ack-2.12-single-file

Examples:

  > tools/partest-ack 'case (class|object) Baz'
  % testsWithMatchingPaths         ...  0
  % testsWithMatchingCode          ...  3
  # 3 tests to run.

  > tools/partest-ack -s 12.hours.ago
  % testsTouchedSinceGitTime       ...  33
  # 33 tests to run.

  > tools/partest-ack -p src/library/scala/Enumeration.scala
  % testsModifiedInSameCommit      ...  80
  # 80 tests to run.

  > tools/partest-ack -f
  % tests-which-failed             ...  42
  # 42 tests to run.

  > tools/partest-ack "kinds of the type arguments"
  % testsWithMatchingPaths         ...  0
  % testsWithMatchingCode          ...  6
  # 6 tests to run.
EOM

  exit 0
}

while getopts :fuvdrp:s: opt; do
  case $opt in
    f) failed=true && partest_args+=" --failed" ;;
    p) cotouched="$cotouched $OPTARG" ;;
    r) sortCommand="randomSort" ;;
    s) since="$OPTARG" ;;
    q) partest_args+=" --terse" ;;
    u) partest_args+=" --update-check" ;;
    v) partest_args+=" --verbose" ;;
    :) echo "Option -$OPTARG requires an argument." >&2 ;;
    *) ack_args+="-$OPTARG" ;;  # don't drop unknown args, assume they're for ack
  esac
done

shift $((OPTIND-1))
ack_args=( "${ack_args[@]}" "$@" )

# These methods all just create paths which may or may not be tests
# all are filtered through partest-paths which limits the output to actual tests
regexPathTests () { find "$filesdir" | ack --noenv "$@"; }
failedTests ()    { for p in $(find "$filesdir" -name '*.log'); do p1=${p%.log} && p2=${p1%-*} && echo "$p2"; done; }
sinceTests()      { git log --since="$@" --name-only --pretty="format:" -- "$filesdir"; }
regexCodeTests () { ack $standard_ack_args "$@" -- "$filesdir"; }
sameCommitTests() { for rev in $(git rev-list HEAD -- "$@"); do git --no-pager show --pretty="format:" --name-only "$rev" -- "$filesdir"; done; }

countStdout () {
  local -i count=0
  while read line; do
    printf "$line\n" && count+=1
  done

  printf >&2 " $count\n"
}

randomSort () {
  sort -u | while read line; do echo "$RANDOM $line"; done | sort | sed -E 's/^[0-9]+ //'
}

testRun () {
  local description="$1" && shift
  printf >&2 "%% tests %-25s ... " "$description"
  "$@" | "$partestPaths" | countStdout | egrep -v '^[ ]*$'
}

allMatches() {
  [[ -n $ack_args ]]    && testRun "with matching paths" regexPathTests "${ack_args[@]}"
  [[ -n $ack_args ]]    && testRun "with matching code" regexCodeTests "${ack_args[@]}"
  [[ -n $cotouched ]]   && testRun "modified in same commit" sameCommitTests $cotouched
  [[ -n $since ]]       && testRun "modified since time" sinceTests "$since"
  [[ -n $failed ]]      && testRun "failed on last run" failedTests
}

paths=$(allMatches | $sortCommand)

[[ -z $paths ]] && [[ -z $failed ]] && echo >&2 "No matching tests." && exit 0;

count=$(echo $(echo "$paths" | wc -w))
[[ "$count" -eq 0 ]] && echo >&2 "No tests to run." && exit 0;

# Output a command line which will re-run these same tests.
echo "# $count tests to run."
printf "%-52s %s\n" "$base/test/partest ${partest_args[*]}" "\\"
for path in $paths; do  printf "  %-50s %s\n" "$path" "\\"; done
echo ""

test/partest ${partest_args[*]} $paths