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