-
Notifications
You must be signed in to change notification settings - Fork 2
/
run-tests.sh
executable file
·394 lines (355 loc) · 10.5 KB
/
run-tests.sh
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
#!/usr/bin/env bash
# run-tests.sh
#
# Sets up and runs unittests in each python environment it detects in the build files.
# Print if log level is CRITICAL
critical() {
log 0 "(C): " "$@"
}
# Print if log level is ERROR
error() {
log 1 "(E): " "$@"
}
# Print if log level is WARN
warn() {
log 2 "(W): " "$@"
}
# Print if log level is INFO
info() {
log 3 "(I): " "$@"
}
# Print if log level is DEBUG
debug() {
log 4 "(D): " "$@"
}
# Print if log level is >= $1
log() {
__this_level=$1
shift 1
__prefix="$1"
shift 1
if [ $__log_level -ge $__this_level ] ; then
echo -n "$__prefix"
"$@"
else
(2>&1 >/dev/null "$@")
fi
}
# Figure out the full interpreter path for a python version
__get_python_interpreter_path() {
__python=python$1
(> /dev/null which $__python 2>&1)
if [ "$?" != "0" ] ; then
if [ "$1" = "2" ] ; then
__python=python
fi
fi
(> /dev/null which $__python 2>&1)
if [ "$?" = "0" ] ; then
which $__python
else
echo "NONE"
fi
}
# Create a new virtual environment for a particular python version
__create_venv() {
local __python
__python=$(__get_python_interpreter_path $1)
if [ "$__python" = "NONE" ] ; then
info echo " python$1 not found!"
exit 1
else
__virtualenv=$(which virtualenv)
__virtualenv_by_version=$(dirname $__python)/virtualenv
if [ -e $__virtualenv_by_version ] ; then
debug $__virtualenv_by_version -v -p $__python .venv$1
rc=$?
elif [ -e $__virtualenv ] ; then
debug $__virtualenv -v -p $__python .venv$1
rc=$?
else
info echo " virtualenv executable not found for $__python!"
exit 1
fi
info echo " python$1 ($__python) -> .venv$1"
fi
}
# Run PyLint
__do_pylint() {
# Detect the test package
debug echo "Detecting packages..."
__packages="$(python -c 'import setuptools; print(" ".join(setuptools.find_packages()))')"
debug echo $__packages
# Append '/*' to each package name to hand off to pylint
__pylint_directories=${__packages/ //* }/*
__pylint_files="$(find . -name '*.py' ! -path '*.venv*' -printf '%p ')"
debug echo "__pylint_files: $__pylint_files"
__pylint_cmd="pylint $__pylint_files"
debug echo "PyLint Command: '$__pylint_cmd'"
debug echo
info echo "PyLint: start ($PYLINT_LOG)"
echo "Python $1" >> $PYLINT_LOG
echo "" >> $PYLINT_LOG
__pylint_out=$(2>&1 $__pylint_cmd)
__pylint_E=$(echo "$__pylint_out" | grep -c "E:")
__pylint_W=$(echo "$__pylint_out" | grep -c "W:")
__pylint_C=$(echo "$__pylint_out" | grep -c "C:")
__pylint_R=$(echo "$__pylint_out" | grep -c "R:")
__pylint_F=$(echo "$__pylint_out" | grep -c "F:")
echo "$__pylint_out" >> $PYLINT_LOG
__summary="PyLint: done ($__pylint_E errors, $__pylint_W warnings, $__pylint_C conventions, $__pylint_R refactors, $__pylint_F fatals)"
echo "$__summary" >> $PYLINT_LOG
echo "" >> $PYLINT_LOG
echo "" >> $PYLINT_LOG
__rating_line=$(echo "$__pylint_out" | grep "Your code has been rated at ")
__rating=$(echo "$__rating_line" | cut -d ' ' -f7 | cut -d '/' -f1)
info echo " $__rating_line"
info echo "$__summary"
info echo
rm -f $PYLINT_BADGE
if [ $(2>/dev/null which anybadge) ] ; then
anybadge -l pylint -v $__rating -f $PYLINT_BADGE 2=red 4=orange 8=yellow 10=green
else
warn echo "anybdage not found, PyLint badge not created."
fi
}
# Run unit tests
__do_tests() {
info echo "--Running Python$1 Tests--"
# This is always printed... otherwise what is the point?
coverage run $__branch -m unittest discover -s $__test_dir
log 2 "" coverage report
rm -f coverage.svg
coverage-badge -o coverage.svg
if [ "$__html_cov" = "1" ] ; then
info echo "Generating html coverage into '$__html_cov_dir'"
coverage html -d $__html_cov_dir
fi
info echo
info echo "---------Complete---------"
info echo
info echo
}
# Go through a simple yaml file and grab all python versions from it
__parse_yaml() {
# For Windows compatibility, replace any CRLF with LF
local yaml=($(cat $1 | tr '\r\n' '\n'))
debug echo "Parsing '$1'"
local token
local capturing=0
local capture_next=0
__versions=()
for token in "${yaml[@]}" ; do
debug echo "Token: '$token'"
if [ "$token" = "python:" ] ; then
capturing=1
elif [ $capturing -eq 1 ] ; then
if [ $capture_next -eq 1 ] ; then
__versions+=($(echo "$token" | cut -c 2- | rev | cut -c 2- | rev))
capture_next=0
elif [ "$token" = "-" ] ; then
capture_next=1
else
capturing=0
fi
fi
done
}
__parse_rc() {
# For Windows compatibility, replace any CRLF with LF
local rc_file=($(cat $1 | tr '\r\n' '\n'))
local token
for token in "${rc_file[@]}" ; do
args+=("$token")
done
}
#
# Main body
#
__help="\
usage: run-tests.sh [options]
-h --help Display this help message.
-v --verbose Print everything! Equivalent to --log 4
-q --quiet Produce only some output. Equivalent to --log 3
-s --silent Only report errors. Equivalent to --log 1
--log <level> Set logging level.
0 = CRITICAL
1 = ERROR
2 = WARNING
3 = INFO
4 = DEBUG
--recreate-venvs Force creation of Python Virtual Environments.
--no-recreate-venvs Do not force recreation of Python Virtual Environments.
--pylint Run PyLint static analysis.
--no-pylint Do not run PyLint static analysis.
--branch Measure branch coverage in addition to statement coverage.
--no-branch Do not measure branch coverage in addition to statement coverage.
--htmlcov [dir] Generate an HTML coverage report into 'dir' (default=htmlcov/).
--test-dir <dir> Do test discovery from <dir> (default=test/).
--skip-tests Skip unit testing, venv creation and PyLint will still run normally.
--no-skip-tests Do not skip unit testing."
# Initialize variables
__log_level=3
__recreate_venvs=0
__pylint=1
__branch=""
__html_cov=0
__html_cov_dir=""
__test_dir="test"
__skip_tests=0
# Handle .testrc and arguments
if [ -e ".testrc" ] ; then
__parse_rc ".testrc"
fi
# Add command line args after the rc args
args+=("$@")
while [ "${args[0]}" != "" ] ; do
#
# Help
#
if [ "${args[0]}" = "-h" ] || [ "${args[0]}" = "--help" ] ; then
echo "$__help"
exit 0
#
# Logging
#
elif [ "${args[0]}" = "-v" ] || [ "${args[0]}" = "--verbose" ] ; then
__log_level=4
elif [ "${args[0]}" = "-q" ] || [ "${args[0]}" = "--quiet" ] ; then
__log_level=3
elif [ "${args[0]}" = "-s" ] || [ "${args[0]}" = "--silent" ] ; then
__log_level=1
elif [ "${args[0]}" = "--log" ] ; then
# Consume the arg following '--log'
__log_level="${args[1]}"
# Advance args
args=(${args[@]:1})
#
# Virtual Environment Control
#
elif [ "${args[0]}" = "--recreate-venvs" ] ; then
__recreate_venvs=1
elif [ "${args[0]}" = "--no-recreate-venvs" ] ; then
__recreate_venvs=0
#
# PyLint Controls
#
elif [ "${args[0]}" = "--pylint" ] ; then
__pylint=1
elif [ "${args[0]}" = "--no-pylint" ] ; then
__pylint=0
#
# Coverage Controls
#
# Measure branch coverage in addition to statement coverage.
elif [ "${args[0]}" = "--branch" ] ; then
__branch="--branch"
# Do not measure branch coverage in addition to statement coverage.
elif [ "${args[0]}" = "--no-branch" ] ; then
__branch=""
# Produce an HTML coverage report.
elif [ "${args[0]}" = "--htmlcov" ] ; then
__html_cov=1
if [ "${args[1]}" = "" ] || [ "${args[1]:0:1}" = "-" ] ; then
__html_cov_dir="htmlcov/"
else
# Consume the arg following '--htmlcov'
__html_cov_dir="${args[1]}"
# Advance args
args=(${args[@]:1})
fi
#
# Test Discovery Control
#
# Test Discovery Start Directory
elif [ "${args[0]}" = "--test-dir" ] ; then
# Consume the arg following '--test-dir'
__test_dir="${args[1]}"
# Advance args
args=(${args[@]:1})
#
# Skip Tests
#
elif [ "${args[0]}" = "--skip-tests" ] ; then
__skip_tests=1
elif [ "${args[0]}" = "--no-skip-tests" ] ; then
__skip_tests=0
#
# Unrecognized Option/Argument
#
else
echo "Unrecognized option '${args[0]}'"
echo "$__help"
exit 1
fi
# Advance args
args=(${args[@]:1})
done
debug echo "Session Variables:"
debug echo " __recreate_venvs '$__recreate_venvs'"
debug echo " __pylint '$__pylint'"
debug echo " __log_level '$__log_level'"
debug echo " __html_cov '$__html_cov'"
debug echo " __html_cov_dir '$__html_cov_dir'"
debug echo " __test_dir '$__test_dir'"
debug echo " __skip_tests '$__skip_tests'"
# PyLint Variables
LOG_DIR=.
PYLINT_LOG=$LOG_DIR/pylint-report
PYLINT_BADGE=$LOG_DIR/pylint.svg
# Enable dot glob so we will see any "hidden" yml files
shopt -s dotglob
__versions=()
# Capture all the yml files in here
__yml_files=($(ls *.yml))
debug echo "__parse_yml $__yml_files"
# Parse each of them
for __file in "${__yml_files[@]}" ; do
__parse_yaml $__file
done
# Disable dot glob
shopt -u dotglob
# Get the array of version numbers
# Print the versions, replace ' ' with '\n', sort the lines,
__versions=($(echo "${__versions[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ' | uniq))
debug echo "Versions: '$__versions'"
info echo "Setting up Python virtual environments"
declare -a __pids
declare -a __rcs
for __i in "${!__versions[@]}" ; do
__version=${__versions[$__i]}
debug echo "Setting up for Python$__version"
if [ "$__recreate_venvs" = "1" ] || [ ! -e .venv$__version ] ; then
__create_venv $__version &
__pids[$__i]=$!
else
info echo " python$__version (existing virtualenv) -> .venv$__version" &
__pids[$__i]=$!
fi
done
for __i in "${!__pids[@]}" ; do
wait ${__pids[$__i]}
__rcs[$__i]=$?
done
info echo "done."
info echo
# If we're going to run PyLint, reset the log file
if [ "$__pylint" = "1" ] ; then
echo "" > $PYLINT_LOG
fi
for __i in "${!__rcs[@]}" ; do
if [ "${__rcs[$__i]}" = "0" ] ; then
__version="${__versions[$__i]}"
. .venv$__version/bin/activate
if [ "$__pylint" = "1" ] ; then
__do_pylint $__version
fi
if [ "$__skip_tests" = "1" ] ; then
warn echo "Python$__version Unit Testing Skipped (--skip-tests flag set)"
else
# Install the test package, if we're doing the unit tests.
debug pip install -r test_requirements.txt
__do_tests $__version
fi
fi
done