3021: Install docutils (unlisted dependency of python-daemon).
[arvados.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 || fatal "pip install PyYAML failed"
329
330 # Needed for python-daemon 2.0.2, which breaks otherwise with
331 # "ImportError: No module named docutils.core"
332 pip install docutils || fatal "pip install docutils failed"
333
334 checkexit() {
335     if [[ "$?" != "0" ]]; then
336         title "!!!!!! $1 FAILED !!!!!!"
337         failures+=("$1 (`timer`)")
338     else
339         successes+=("$1 (`timer`)")
340     fi
341 }
342
343 timer_reset() {
344     t0=$SECONDS
345 }
346
347 timer() {
348     echo -n "$(($SECONDS - $t0))s"
349 }
350
351 do_test() {
352     if [[ -z "${skip[$1]}" ]] && ( [[ -z "$only" ]] || [[ "$only" == "$1" ]] )
353     then
354         title "Running $1 tests"
355         timer_reset
356         if [[ "$2" == "go" ]]
357         then
358             go test ${testargs[$1]} "git.curoverse.com/arvados.git/$1"
359         elif [[ "$2" == "pip" ]]
360         then
361            cd "$WORKSPACE/$1" \
362                 && python setup.py test ${testargs[$1]}
363         elif [[ "$2" != "" ]]
364         then
365             "test_$2"
366         else
367             "test_$1"
368         fi
369         checkexit "$1 tests"
370         title "End of $1 tests (`timer`)"
371     else
372         title "Skipping $1 tests"
373     fi
374 }
375
376 do_install() {
377     if [[ -z "$skip_install" ]]
378     then
379         title "Running $1 install"
380         timer_reset
381         if [[ "$2" == "go" ]]
382         then
383             go get -t "git.curoverse.com/arvados.git/$1"
384         elif [[ "$2" == "pip" ]]
385         then
386             cd "$WORKSPACE/$1" \
387                 && python setup.py sdist rotate --keep=1 --match .tar.gz \
388                 && pip install --upgrade dist/*.tar.gz
389         elif [[ "$2" != "" ]]
390         then
391             "install_$2"
392         else
393             "install_$1"
394         fi
395         checkexit "$1 install"
396         title "End of $1 install (`timer`)"
397     else
398         title "Skipping $1 install"
399     fi
400 }
401
402 title () {
403     txt="********** $1 **********"
404     printf "\n%*s%s\n\n" $((($COLUMNS-${#txt})/2)) "" "$txt"
405 }
406
407 install_doc() {
408     cd "$WORKSPACE/doc"
409     bundle install --no-deployment
410     rm -rf .site
411 }
412 do_install doc
413
414 install_ruby_sdk() {
415     with_test_gemset gem uninstall --force --all --executables arvados \
416         && cd "$WORKSPACE/sdk/ruby" \
417         && bundle install --no-deployment \
418         && gem build arvados.gemspec \
419         && with_test_gemset gem install --no-ri --no-rdoc `ls -t arvados-*.gem|head -n1`
420 }
421 do_install sdk/ruby ruby_sdk
422
423 install_cli() {
424     with_test_gemset gem uninstall --force --all --executables arvados-cli \
425         && cd "$WORKSPACE/sdk/cli" \
426         && bundle install --no-deployment \
427         && gem build arvados-cli.gemspec \
428         && with_test_gemset gem install --no-ri --no-rdoc `ls -t arvados-cli-*.gem|head -n1`
429 }
430 do_install sdk/cli cli
431
432 # Install the Python SDK early. Various other test suites (like
433 # keepproxy) bring up run_test_server.py, which imports the arvados
434 # module. We can't actually *test* the Python SDK yet though, because
435 # its own test suite brings up some of those other programs (like
436 # keepproxy).
437 declare -a pythonstuff
438 pythonstuff=(
439     sdk/python
440     services/fuse
441     services/nodemanager
442     )
443 for p in "${pythonstuff[@]}"
444 do
445     do_install "$p" pip
446 done
447
448 install_apiserver() {
449     cd "$WORKSPACE/services/api"
450     RAILS_ENV=test bundle install --no-deployment
451
452     rm -f config/environments/test.rb
453     cp config/environments/test.rb.example config/environments/test.rb
454
455     if [ -n "$CONFIGSRC" ]
456     then
457         for f in database.yml application.yml
458         do
459             cp "$CONFIGSRC/$f" config/ || fatal "$f"
460         done
461     fi
462
463     # Fill in a random secret_token and blob_signing_key for testing
464     SECRET_TOKEN=`echo 'puts rand(2**512).to_s(36)' |ruby`
465     BLOB_SIGNING_KEY=`echo 'puts rand(2**512).to_s(36)' |ruby`
466
467     sed -i'' -e "s:SECRET_TOKEN:$SECRET_TOKEN:" config/application.yml
468     sed -i'' -e "s:BLOB_SIGNING_KEY:$BLOB_SIGNING_KEY:" config/application.yml
469
470     # Set up empty git repo (for git tests)
471     GITDIR=$(mktemp -d)
472     sed -i'' -e "s:/var/cache/git:$GITDIR:" config/application.default.yml
473
474     rm -rf $GITDIR
475     mkdir -p $GITDIR/test
476     cd $GITDIR/test \
477         && git init \
478         && git config user.email "jenkins@ci.curoverse.com" \
479         && git config user.name "Jenkins, CI" \
480         && touch tmp \
481         && git add tmp \
482         && git commit -m 'initial commit'
483
484     # Clear out any lingering postgresql connections to arvados_test, so that we can drop it
485     # This assumes the current user is a postgresql superuser
486     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
487
488     cd "$WORKSPACE/services/api" \
489         && RAILS_ENV=test bundle exec rake db:drop \
490         && RAILS_ENV=test bundle exec rake db:setup \
491         && RAILS_ENV=test bundle exec rake db:fixtures:load
492 }
493 do_install services/api apiserver
494
495 declare -a gostuff
496 gostuff=(
497     services/crunchstat
498     services/keepstore
499     services/keepproxy
500     sdk/go/arvadosclient
501     sdk/go/keepclient
502     sdk/go/streamer
503     )
504 for g in "${gostuff[@]}"
505 do
506     do_install "$g" go
507 done
508
509 install_workbench() {
510     cd "$WORKSPACE/apps/workbench" \
511         && RAILS_ENV=test bundle install --no-deployment
512 }
513 do_install apps/workbench workbench
514
515 test_doclinkchecker() {
516     cd "$WORKSPACE/doc"
517     # Make sure python-epydoc is installed or the next line won't do much good!
518     ARVADOS_API_HOST=qr1hi.arvadosapi.com
519     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
520     unset ARVADOS_API_HOST
521 }
522 do_test doc doclinkchecker
523
524 test_ruby_sdk() {
525     cd "$WORKSPACE/sdk/ruby" \
526         && bundle install --no-deployment \
527         && bundle exec rake test ${testargs[sdk/ruby]}
528 }
529 do_test sdk/ruby ruby_sdk
530
531 test_cli() {
532     cd "$WORKSPACE/sdk/cli" \
533         && bundle install --no-deployment \
534         && mkdir -p /tmp/keep \
535         && KEEP_LOCAL_STORE=/tmp/keep bundle exec rake test ${testargs[sdk/cli]}
536 }
537 do_test sdk/cli cli
538
539 test_apiserver() {
540     cd "$WORKSPACE/services/api"
541     RAILS_ENV=test bundle exec rake test ${testargs[services/api]}
542 }
543 do_test services/api apiserver
544
545 for p in "${pythonstuff[@]}"
546 do
547     do_test "$p" pip
548 done
549
550 for g in "${gostuff[@]}"
551 do
552     do_test "$g" go
553 done
554
555 test_workbench() {
556     cd "$WORKSPACE/apps/workbench" \
557         && RAILS_ENV=test bundle exec rake test ${testargs[apps/workbench]}
558 }
559 do_test apps/workbench workbench
560
561 test_workbench_benchmark() {
562     cd "$WORKSPACE/apps/workbench" \
563         && RAILS_ENV=test bundle exec rake test:benchmark ${testargs[apps/workbench_benchmark]}
564 }
565 do_test apps/workbench_benchmark workbench_benchmark
566
567 test_workbench_profile() {
568     cd "$WORKSPACE/apps/workbench" \
569         && RAILS_ENV=test bundle exec rake test:profile ${testargs[apps/workbench_profile]}
570 }
571 do_test apps/workbench_profile workbench_profile
572
573 report_outcomes
574 clear_temp
575
576 exit ${#failures}