Fix a newly introduced bug in run_tests.sh.
[python_utils.git] / tests / run_tests.sh
1 #!/bin/bash
2
3 source /home/scott/bin/color_vars.sh
4
5 ROOT=/home/scott/lib/python_modules
6 DOCTEST=0
7 UNITTEST=0
8 INTEGRATION=0
9 FAILURES=0
10 TESTS_RUN=0
11 COVERAGE=0
12
13 dup() {
14     if [ $# -ne 2 ]; then
15         echo "Usage: dup <string> <count>"
16         return
17     fi
18     local times=$(seq 1 $2)
19     for x in ${times}; do
20         echo -n "$1"
21     done
22 }
23
24 make_header() {
25     if [ $# -ne 2 ]; then
26         echo "Usage: make_header <required title> <color>"
27         return
28     fi
29     local title="$1"
30     local title_len=${#title}
31     title_len=$((title_len + 4))
32     local width=70
33     local left=4
34     local right=$(($width-($title_len+$left)))
35     local color="$2"
36     dup '-' $left
37     echo -ne "[ ${color}${title}${NC} ]"
38     dup '-' $right
39     echo
40 }
41
42 function usage() {
43     echo "Usage: $0 [-a]|[-i][-u][-d] [--coverage]"
44     echo
45     echo "Runs tests under ${ROOT}.  Options control which test types:"
46     echo
47     echo "    -a | --all . . . . . . . . . . . . Run all types of tests"
48     echo "    -d | --doctests  . . . . . . . . . Run doctests"
49     echo "    -u | --unittests . . . . . . . . . Run unittests"
50     echo "    -i | --integration . . . . . . . . Run integration tests"
51     echo
52     exit 1
53 }
54
55 while [[ $# -gt 0 ]]; do
56     key="$1"
57     case $key in
58         -a|--all)
59             DOCTEST=1
60             UNITTEST=1
61             INTEGRATION=1
62             ;;
63         -d|--doctests)
64             DOCTEST=1
65             ;;
66         -u|--unittests)
67             UNITTEST=1
68             ;;
69         -i|--integration)
70             INTEGRATION=1
71             ;;
72         --coverage)
73             COVERAGE=1
74             ;;
75         *)    # unknown option
76             echo "Argument $key was not recognized."
77             echo
78             usage
79             exit 1
80             ;;
81     esac
82     shift
83 done
84
85 if [ $(expr ${DOCTEST} + ${UNITTEST} + ${INTEGRATION}) -eq 0 ]; then
86     usage
87     exit 2
88 fi
89
90 if [ ${COVERAGE} -eq 1 ]; then
91     coverage erase
92 fi
93
94 FAILED_TESTS=""
95 if [ ${DOCTEST} -eq 1 ]; then
96     for doctest in $(grep -lR doctest ${ROOT}/*); do
97         if [[ ${doctest} == *.py ]]; then
98             BASE=$(basename ${doctest})
99             BASE="${BASE} (doctest)"
100             make_header "${BASE}" "${CYAN}"
101             if [ ${COVERAGE} -eq 1 ]; then
102                 OUT=$( coverage run --source ${HOME}/lib --append ${doctest} 2>&1 )
103             else
104                 OUT=$( python3 ${doctest} 2>&1 )
105             fi
106             TESTS_RUN=$((TESTS_RUN+1))
107             FAILED=$( echo "${OUT}" | grep '\*\*\*Test Failed\*\*\*' | wc -l )
108             if [ $FAILED == 0 ]; then
109                 echo "OK"
110             else
111                 echo -e "${FAILED}"
112                 FAILURES=$((FAILURES+1))
113                 FAILED_TESTS="${FAILED_TESTS},${BASE} (python3 ${doctest})"
114             fi
115         fi
116     done
117 fi
118
119 if [ ${UNITTEST} -eq 1 ]; then
120     for test in $(find ${ROOT} -name "*_test.py" -print); do
121         BASE=$(basename ${test})
122         BASE="${BASE} (unittest)"
123         make_header "${BASE}" "${GREEN}"
124         if [ ${COVERAGE} -eq 1 ]; then
125             coverage run --source ${HOME}/lib --append ${test} --unittests_ignore_perf
126         else
127             ${test}
128         fi
129         if [ $? -ne 0 ]; then
130             FAILURES=$((FAILURES+1))
131             FAILED_TESTS="${FAILED_TESTS},${BASE} (python3 ${test})"
132         fi
133         TESTS_RUN=$((TESTS_RUN+1))
134     done
135 fi
136
137 if [ ${INTEGRATION} -eq 1 ]; then
138     for test in $(find ${ROOT} -name "*_itest.py" -print); do
139         BASE=$(basename ${test})
140         BASE="${BASE} (integration test)"
141         make_header "${BASE}" "${ORANGE}"
142         if [ ${COVERAGE} -eq 1 ]; then
143             coverage run --source ${HOME}/lib --append ${test}
144         else
145             ${test}
146         fi
147         if [ $? -ne 0 ]; then
148             FAILURES=$((FAILURES+1))
149             FAILED_TESTS="${FAILED_TESTS},${BASE} (python3 ${test})"
150         fi
151         TESTS_RUN=$((TESTS_RUN+1))
152     done
153 fi
154
155 if [ ${COVERAGE} -eq 1 ]; then
156     make_header "Code Coverage Report" "${GREEN}"
157     coverage report --omit=config-3.8.py,*_test.py --sort=-cover
158     echo
159     echo "To recall this report w/o run-running the tests:"
160     echo
161     echo "    $ coverage report --omit=config-3.8.py,*_test.py --sort=-cover"
162     echo
163     echo "...from the 'tests' directory.  Note that subsequent calls to "
164     echo "run_tests.sh with --coverage will klobber previous results.  See:"
165     echo
166     echo "    https://coverage.readthedocs.io/en/6.2/"
167     echo
168 fi
169
170 if [ ${FAILURES} -ne 0 ]; then
171     FAILED_TESTS=$(echo ${FAILED_TESTS} | sed 's/^,/__/g')
172     FAILED_TESTS=$(echo ${FAILED_TESTS} | sed 's/,/\n__/g')
173     if [ ${FAILURES} -eq 1 ]; then
174         echo -e "${RED}There was ${FAILURES}/${TESTS_RUN} failure:"
175     else
176         echo -e "${RED}There were ${FAILURES}/${TESTS_RUN} failures:"
177     fi
178     echo "${FAILED_TESTS}"
179     echo -e "${NC}"
180     exit ${FAILURES}
181 else
182     echo -e "${BLACK}${ON_GREEN}All (${TESTS_RUN}) test(s) passed.${NC}"
183     exit 0
184 fi