3021: Allow '--only X' to override '--skip X'.
[arvados-dev.git] / jenkins / run-tests.sh
1 #!/bin/bash
2
3 read -rd "\000" helpmessage <<EOF
4 $(basename $0): Install and test Arvados components.
5
6 Exit non-zero if any tests fail.
7
8 Syntax:
9         $(basename $0) WORKSPACE=/path/to/arvados [options]
10
11 Options:
12
13 --skip FOO     Do not test the FOO component.
14 --only FOO     Do not test anything except the FOO component.
15 --leave-temp   Do not remove GOPATH, virtualenv, and other temp dirs at exit.
16                Instead, show which directories were used this time so they
17                can be reused in subsequent invocations.
18 --skip-install Do not run any install steps. Just run tests.
19                You should provide GOPATH, GEMHOME, and VENVDIR options
20                from a previous invocation if you use this option.
21 WORKSPACE=path Arvados source tree to test.
22 CONFIGSRC=path Dir with api server config files to copy into source tree.
23                (If none given, leave config files alone in source tree.)
24 services/api_test="TEST=test/functional/arvados/v1/collections_controller_test.rb"
25                Restrict apiserver tests to the given file
26 sdk/python_test="--test-suite test.test_keep_locator"
27                Restrict Python SDK tests to the given class
28 apps/workbench_test="TEST=test/integration/pipeline_instances_test.rb"
29                Restrict Workbench tests to the given file
30 ARVADOS_DEBUG=1
31                Print more debug messages
32 envvar=value   Set \$envvar to value. Primarily useful for WORKSPACE,
33                *_test, and other examples shown above.
34
35 Assuming --skip-install is not given, all components are installed
36 into \$GOPATH, \$VENDIR, and \$GEMHOME before running any tests. Many
37 test suites depend on other components being installed, and installing
38 everything tends to be quicker than debugging dependencies.
39
40 As a special concession to the current CI server config, CONFIGSRC
41 defaults to $HOME/arvados-api-server if that directory exists.
42
43 More information and background:
44
45 https://arvados.org/projects/arvados/wiki/Running_tests
46
47 Available tests:
48
49 apps/workbench
50 apps/workbench_benchmark
51 apps/workbench_profile
52 doc
53 services/api
54 services/crunchstat
55 services/fuse
56 services/keepproxy
57 services/keepstore
58 services/nodemanager
59 sdk/cli
60 sdk/python
61 sdk/ruby
62 sdk/go/arvadosclient
63 sdk/go/keepclient
64 sdk/go/streamer
65
66 EOF
67
68 # First make sure to remove any ARVADOS_ variables from the calling
69 # environment that could interfere with the tests.
70 unset $(env | cut -d= -f1 | grep \^ARVADOS_)
71
72 # Reset other variables that could affect our [tests'] behavior by
73 # accident.
74 GITDIR=
75 GOPATH=
76 VENVDIR=
77 PYTHONPATH=
78 GEMHOME=
79
80 COLUMNS=80
81
82 leave_temp=
83 skip_install=
84
85 declare -A leave_temp
86 clear_temp() {
87     leaving=""
88     for var in VENVDIR GOPATH GITDIR GEMHOME
89     do
90         if [[ -z "${leave_temp[$var]}" ]]
91         then
92             if [[ -n "${!var}" ]]
93             then
94                 rm -rf "${!var}"
95             fi
96         else
97             leaving+=" $var=\"${!var}\""
98         fi
99     done
100     if [[ -n "$leaving" ]]; then
101         echo "Leaving behind temp dirs: $leaving"
102     fi
103 }
104
105 fatal() {
106     clear_temp
107     echo >&2 "Fatal: $* in ${FUNCNAME[1]} at ${BASH_SOURCE[1]} line ${BASH_LINENO[0]}"
108     exit 1
109 }
110
111 report_outcomes() {
112     for x in "${successes[@]}"
113     do
114         echo "Pass: $x"
115     done
116
117     if [[ ${#failures[@]} == 0 ]]
118     then
119         echo "All test suites passed."
120     else
121         echo "Failures (${#failures[@]}):"
122         for x in "${failures[@]}"
123         do
124             echo "Fail: $x"
125         done
126     fi
127 }
128
129 sanity_checks() {
130   # Make sure WORKSPACE is set
131   if ! [[ -n "$WORKSPACE" ]]; then
132     echo >&2 "$helpmessage"
133     echo >&2
134     echo >&2 "Error: WORKSPACE environment variable not set"
135     echo >&2
136     exit 1
137   fi
138
139   # Make sure virtualenv is installed
140   `virtualenv --help >/dev/null 2>&1`
141
142   if [[ "$?" != "0" ]]; then
143     echo >&2
144     echo >&2 "Error: virtualenv could not be found"
145     echo >&2
146     exit 1
147   fi
148
149   # Make sure go is installed
150   `go env >/dev/null 2>&1`
151
152   if [[ "$?" != "0" ]]; then
153     echo >&2
154     echo >&2 "Error: go could not be found"
155     echo >&2
156     exit 1
157   fi
158
159   # Make sure gcc is installed
160   `gcc --help >/dev/null 2>&1`
161
162   if [[ "$?" != "0" ]]; then
163     echo >&2
164     echo >&2 "Error: gcc could not be found"
165     echo >&2
166     exit 1
167   fi
168
169 }
170
171 declare -a failures
172 declare -A skip
173 declare -A testargs
174 skip[apps/workbench_profile]=1
175
176 while [[ -n "$1" ]]
177 do
178     arg="$1"; shift
179     case "$arg" in
180         --help)
181             echo >&2 "$helpmessage"
182             echo >&2
183             exit 1
184             ;;
185         --skip)
186             skipwhat="$1"; shift
187             skip[$skipwhat]=1
188             ;;
189         --only)
190             only="$1"; skip[$1]=""; shift
191             ;;
192         --skip-install)
193             skip_install=1
194             ;;
195         --leave-temp)
196             leave_temp[VENVDIR]=1
197             leave_temp[GOPATH]=1
198             leave_temp[GEMHOME]=1
199             ;;
200         *_test=*)
201             suite="${arg%%_test=*}"
202             args="${arg#*=}"
203             testargs["$suite"]="$args"
204             ;;
205         *=*)
206             eval export $(echo $arg | cut -d= -f1)=\"$(echo $arg | cut -d= -f2-)\"
207             ;;
208         *)
209             echo >&2 "$0: Unrecognized option: '$arg'. Try: $0 --help"
210             exit 1
211             ;;
212     esac
213 done
214
215 sanity_checks
216
217 echo "WORKSPACE=$WORKSPACE"
218
219 if [[ -z "$CONFIGSRC" ]] && [[ -d "$HOME/arvados-api-server" ]]; then
220     # Jenkins expects us to use this by default.
221     CONFIGSRC="$HOME/arvados-api-server"
222 fi
223
224 # Clean up .pyc files that may exist in the workspace
225 cd "$WORKSPACE"
226 find -name '*.pyc' -delete
227
228 # Set up temporary install dirs (unless existing dirs were supplied)
229 for tmpdir in VENVDIR GOPATH GEMHOME
230 do
231     if [[ -n "${!tmpdir}" ]]; then
232         leave_temp[$tmpdir]=1
233     else
234         eval $tmpdir=$(mktemp -d)
235     fi
236 done
237
238 setup_ruby_environment() {
239     if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
240       source "$HOME/.rvm/scripts/rvm"
241       using_rvm=true
242     elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
243       source "/usr/local/rvm/scripts/rvm"
244       using_rvm=true
245     else
246       using_rvm=false
247     fi
248
249     if [[ "$using_rvm" == true ]]; then
250         # If rvm is in use, we can't just put separate "dependencies"
251         # and "gems-under-test" paths to GEM_PATH: passenger resets
252         # the environment to the "current gemset", which would lose
253         # our GEM_PATH and prevent our test suites from running ruby
254         # programs (for example, the Workbench test suite could not
255         # boot an API server or run arv). Instead, we have to make an
256         # rvm gemset and use it for everything.
257
258         [[ `type rvm | head -n1` == "rvm is a function" ]] \
259             || fatal 'rvm check'
260
261         # Put rvm's favorite path back in first place (overriding
262         # virtualenv, which just put itself there). Ignore rvm's
263         # complaint about not being in first place already.
264         rvm use @default 2>/dev/null
265
266         # Create (if needed) and switch to an @arvados-tests
267         # gemset. (Leave the choice of ruby to the caller.)
268         rvm use @arvados-tests --create \
269             || fatal 'rvm gemset setup'
270
271         rvm env
272     else
273         # When our "bundle install"s need to install new gems to
274         # satisfy dependencies, we want them to go where "gem install
275         # --user-install" would put them. (However, if the caller has
276         # already set GEM_HOME, we assume that's where dependencies
277         # should be installed, and we should leave it alone.)
278
279         if [ -z "$GEM_HOME" ]; then
280             user_gempath="$(gem env gempath)"
281             export GEM_HOME="${user_gempath%%:*}"
282         fi
283         PATH="$(gem env gemdir)/bin:$PATH"
284
285         # When we build and install our own gems, we install them in our
286         # $GEMHOME tmpdir, and we want them to be at the front of GEM_PATH and
287         # PATH so integration tests prefer them over other versions that
288         # happen to be installed in $user_gempath, system dirs, etc.
289
290         tmpdir_gem_home="$(env - PATH="$PATH" HOME="$GEMHOME" gem env gempath | cut -f1 -d:)"
291         PATH="$tmpdir_gem_home/bin:$PATH"
292         export GEM_PATH="$tmpdir_gem_home:$(gem env gempath)"
293
294         echo "Will install dependencies to $(gem env gemdir)"
295         echo "Will install arvados gems to $tmpdir_gem_home"
296         echo "Gem search path is GEM_PATH=$GEM_PATH"
297     fi
298 }
299
300 with_test_gemset() {
301     if [[ "$using_rvm" == true ]]; then
302         "$@"
303     else
304         GEM_HOME="$tmpdir_gem_home" "$@"
305     fi
306 }
307
308 export GOPATH
309 mkdir -p "$GOPATH/src/git.curoverse.com"
310 ln -sfn "$WORKSPACE" "$GOPATH/src/git.curoverse.com/arvados.git" \
311     || fatal "symlink failed"
312
313 virtualenv --setuptools "$VENVDIR" || fatal "virtualenv $VENVDIR failed"
314 . "$VENVDIR/bin/activate"
315
316 # Note: this must be the last time we change PATH, otherwise rvm will
317 # whine a lot.
318 setup_ruby_environment
319
320 echo "PATH is $PATH"
321
322 if ! which bundler >/dev/null
323 then
324     gem install --user-install bundler || fatal 'Could not install bundler'
325 fi
326
327 # Needed for run_test_server.py which is used by certain (non-Python) tests.
328 pip install PyYAML
329
330 checkexit() {
331     if [[ "$?" != "0" ]]; then
332         title "!!!!!! $1 FAILED !!!!!!"
333         failures+=("$1 (`timer`)")
334     else
335         successes+=("$1 (`timer`)")
336     fi
337 }
338
339 timer_reset() {
340     t0=$SECONDS
341 }
342
343 timer() {
344     echo -n "$(($SECONDS - $t0))s"
345 }
346
347 do_test() {
348     if [[ -z "${skip[$1]}" ]] && ( [[ -z "$only" ]] || [[ "$only" == "$1" ]] )
349     then
350         title "Running $1 tests"
351         timer_reset
352         if [[ "$2" == "go" ]]
353         then
354             go test ${testargs[$1]} "git.curoverse.com/arvados.git/$1"
355         elif [[ "$2" == "pip" ]]
356         then
357            # Other test suites can depend on tests_require
358            # dependencies of this package. For example, keepproxy runs
359            # run_test_server.py, which depends on the yaml package,
360            # which is in sdk/python's tests_require but not
361            # install_requires, and therefore does not get installed by
362            # setuptools until we run "setup.py test" *and* install the
363            # .egg files that setup.py downloads.
364            cd "$WORKSPACE/$1" \
365                 && python setup.py test ${testargs[$1]} \
366                 && (easy_install *.egg || true)
367         elif [[ "$2" != "" ]]
368         then
369             "test_$2"
370         else
371             "test_$1"
372         fi
373         checkexit "$1 tests"
374         title "End of $1 tests (`timer`)"
375     else
376         title "Skipping $1 tests"
377     fi
378 }
379
380 do_install() {
381     if [[ -z "$skip_install" ]]
382     then
383         title "Running $1 install"
384         timer_reset
385         if [[ "$2" == "go" ]]
386         then
387             go get -t "git.curoverse.com/arvados.git/$1"
388         elif [[ "$2" == "pip" ]]
389         then
390             cd "$WORKSPACE/$1" \
391                 && python setup.py sdist rotate --keep=1 --match .tar.gz \
392                 && pip install --upgrade dist/*.tar.gz
393         elif [[ "$2" != "" ]]
394         then
395             "install_$2"
396         else
397             "install_$1"
398         fi
399         checkexit "$1 install"
400         title "End of $1 install (`timer`)"
401     else
402         title "Skipping $1 install"
403     fi
404 }
405
406 title () {
407     txt="********** $1 **********"
408     printf "\n%*s%s\n\n" $((($COLUMNS-${#txt})/2)) "" "$txt"
409 }
410
411 install_doc() {
412     cd "$WORKSPACE/doc"
413     bundle install --no-deployment
414     rm -rf .site
415 }
416 do_install doc
417
418 install_ruby_sdk() {
419     with_test_gemset gem uninstall --force --all --executables arvados \
420         && cd "$WORKSPACE/sdk/ruby" \
421         && bundle install --no-deployment \
422         && gem build arvados.gemspec \
423         && with_test_gemset gem install --no-ri --no-rdoc `ls -t arvados-*.gem|head -n1`
424 }
425 do_install sdk/ruby ruby_sdk
426
427 install_cli() {
428     with_test_gemset gem uninstall --force --all --executables arvados-cli \
429         && cd "$WORKSPACE/sdk/cli" \
430         && bundle install --no-deployment \
431         && gem build arvados-cli.gemspec \
432         && with_test_gemset gem install --no-ri --no-rdoc `ls -t arvados-cli-*.gem|head -n1`
433 }
434 do_install sdk/cli cli
435
436 # Install the Python SDK early. Various other test suites (like
437 # keepproxy) bring up run_test_server.py, which imports the arvados
438 # module. We can't actually *test* the Python SDK yet though, because
439 # its own test suite brings up some of those other programs (like
440 # keepproxy).
441 declare -a pythonstuff
442 pythonstuff=(
443     sdk/python
444     services/fuse
445     services/nodemanager
446     )
447 for p in "${pythonstuff[@]}"
448 do
449     do_install "$p" pip
450 done
451
452 install_apiserver() {
453     cd "$WORKSPACE/services/api"
454     RAILS_ENV=test bundle install --no-deployment
455
456     rm -f config/environments/test.rb
457     cp config/environments/test.rb.example config/environments/test.rb
458
459     if [ -n "$CONFIGSRC" ]
460     then
461         for f in database.yml application.yml
462         do
463             cp "$CONFIGSRC/$f" config/ || fatal "$f"
464         done
465     fi
466
467     # Fill in a random secret_token and blob_signing_key for testing
468     SECRET_TOKEN=`echo 'puts rand(2**512).to_s(36)' |ruby`
469     BLOB_SIGNING_KEY=`echo 'puts rand(2**512).to_s(36)' |ruby`
470
471     sed -i'' -e "s:SECRET_TOKEN:$SECRET_TOKEN:" config/application.yml
472     sed -i'' -e "s:BLOB_SIGNING_KEY:$BLOB_SIGNING_KEY:" config/application.yml
473
474     # Set up empty git repo (for git tests)
475     GITDIR=$(mktemp -d)
476     sed -i'' -e "s:/var/cache/git:$GITDIR:" config/application.default.yml
477
478     rm -rf $GITDIR
479     mkdir -p $GITDIR/test
480     cd $GITDIR/test \
481         && git init \
482         && git config user.email "jenkins@ci.curoverse.com" \
483         && git config user.name "Jenkins, CI" \
484         && touch tmp \
485         && git add tmp \
486         && git commit -m 'initial commit'
487
488     # Clear out any lingering postgresql connections to arvados_test, so that we can drop it
489     # This assumes the current user is a postgresql superuser
490     psql arvados_test -c "SELECT pg_terminate_backend (pg_stat_activity.procpid::int) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'arvados_test';" 2>/dev/null
491
492     cd "$WORKSPACE/services/api" \
493         && RAILS_ENV=test bundle exec rake db:drop \
494         && RAILS_ENV=test bundle exec rake db:setup \
495         && RAILS_ENV=test bundle exec rake db:fixtures:load
496 }
497 do_install services/api apiserver
498
499 declare -a gostuff
500 gostuff=(
501     services/crunchstat
502     services/keepstore
503     services/keepproxy
504     sdk/go/arvadosclient
505     sdk/go/keepclient
506     sdk/go/streamer
507     )
508 for g in "${gostuff[@]}"
509 do
510     do_install "$g" go
511 done
512
513 install_workbench() {
514     cd "$WORKSPACE/apps/workbench" \
515         && RAILS_ENV=test bundle install --no-deployment
516 }
517 do_install apps/workbench workbench
518
519 test_doclinkchecker() {
520     cd "$WORKSPACE/doc"
521     # Make sure python-epydoc is installed or the next line won't do much good!
522     ARVADOS_API_HOST=qr1hi.arvadosapi.com
523     PYTHONPATH=$WORKSPACE/sdk/python/ bundle exec rake linkchecker baseurl=file://$WORKSPACE/doc/.site/ arvados_workbench_host=workbench.$ARVADOS_API_HOST arvados_api_host=$ARVADOS_API_HOST
524     unset ARVADOS_API_HOST
525 }
526 do_test doc doclinkchecker
527
528 test_ruby_sdk() {
529     cd "$WORKSPACE/sdk/ruby" \
530         && bundle install --no-deployment \
531         && bundle exec rake test ${testargs[sdk/ruby]}
532 }
533 do_test sdk/ruby ruby_sdk
534
535 test_cli() {
536     cd "$WORKSPACE/sdk/cli" \
537         && bundle install --no-deployment \
538         && mkdir -p /tmp/keep \
539         && KEEP_LOCAL_STORE=/tmp/keep bundle exec rake test ${testargs[sdk/cli]}
540 }
541 do_test sdk/cli cli
542
543 test_apiserver() {
544     cd "$WORKSPACE/services/api"
545     RAILS_ENV=test bundle exec rake test ${testargs[services/api]}
546 }
547 do_test services/api apiserver
548
549 # We must test sdk/python before testing services/keepproxy, because
550 # keepproxy depends on sdk/python's test dependencies.
551 for p in "${pythonstuff[@]}"
552 do
553     do_test "$p" pip
554 done
555
556 for g in "${gostuff[@]}"
557 do
558     do_test "$g" go
559 done
560
561 test_workbench() {
562     cd "$WORKSPACE/apps/workbench" \
563         && RAILS_ENV=test bundle exec rake test ${testargs[apps/workbench]}
564 }
565 do_test apps/workbench workbench
566
567 test_workbench_benchmark() {
568     cd "$WORKSPACE/apps/workbench" \
569         && RAILS_ENV=test bundle exec rake test:benchmark ${testargs[apps/workbench_benchmark]}
570 }
571 do_test apps/workbench_benchmark workbench_benchmark
572
573 test_workbench_profile() {
574     cd "$WORKSPACE/apps/workbench" \
575         && RAILS_ENV=test bundle exec rake test:profile ${testargs[apps/workbench_profile]}
576 }
577 do_test apps/workbench_profile workbench_profile
578
579 report_outcomes
580 clear_temp
581
582 exit ${#failures}