Use /opt/test as workspace and fix lint issues

Change-Id: I47045225bfad92bb8d660e6755d3f817eea5f953
diff --git a/test/library/test-lib.sh b/test/library/test-lib.sh
index 2b41fa5..a7340c9 100644
--- a/test/library/test-lib.sh
+++ b/test/library/test-lib.sh
@@ -105,10 +105,6 @@
     export TEST_SUITE=${TEST_SUITE}
   fi
 
-  # set ENGINE_CACHE
-  ENGINE_CACHE=/opt/engine/.cache
-  export ENGINE_CACHE
-
   log_summary
 }
 
@@ -144,6 +140,7 @@
   #-------------------------------------------------------------------------------
   # Ensure inventory exists
   #-------------------------------------------------------------------------------
+  export ENGINE_CACHE=/opt/engine/.cache
   if [[ ! -f "$ENGINE_CACHE/config/inventory.ini" ]]; then
     echo "ERROR : Unable to find $ENGINE_CACHE/config/inventory.ini"
     echo "        Please ensure you have the inventory.ini file in place before running this script."
@@ -166,10 +163,15 @@
   echo "Info  : Prepare environment to run Cloud Infra tests"
 
   # source engine-vars
-  source $TEST_PATH/test/config/test-vars
+  # shellcheck source=test/library/test-vars.sh
+  source "$TEST_PATH/test/library/test-vars.sh"
 
   # Make sure we pass TEST_PATH everywhere
-  export TEST_ANSIBLE_PARAMS+=" -e test_path=${TEST_PATH}"
+  TEST_ANSIBLE_PARAMS+=" -e test_path=${TEST_PATH}"
+
+  # Convert to array to allow quoting of TEST_ANSIBLE_PARAMS
+  read -r -a TEST_ANSIBLE_PARAMS <<< "$(echo -e "${TEST_ANSIBLE_PARAMS}")"
+  export TEST_ANSIBLE_PARAMS
 
   # Make sure everybody knows where our global roles are
   export ANSIBLE_ROLES_PATH="$HOME/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:${TEST_PATH}/test/playbooks/roles"
@@ -184,9 +186,14 @@
     export SUT="kubernetes"
   else
     export SUT="openstack"
-    source $HOME/admin-openrc.sh
+    # shellcheck disable=SC1090
+    source "$HOME/admin-openrc.sh"
   fi
 
+  # Create engine workspace directory
+  sudo mkdir -p "${TEST_WORKSPACE}"
+  sudo chown "${USER}:${USER}" "${TEST_WORKSPACE}"
+
 }
 
 #-------------------------------------------------------------------------------
@@ -197,7 +204,7 @@
 function cleanup() {
 
   # remove engine venv, cache and .ansible
-  sudo /bin/rm -rf $TEST_VENV $TEST_CACHE $HOME/.ansible
+  sudo /bin/rm -rf "$TEST_VENV" "$TEST_CACHE" "$HOME/.ansible"
 
 }
 
@@ -239,7 +246,7 @@
         [python]=python3-minimal
         [venv]=virtualenv
       )
-      sudo apt update -q=3 > /dev/null 2>&1
+      redirect_cmd sudo apt update
 
       ;;
 
@@ -247,21 +254,19 @@
   esac
 
   # Build installation map
-  for pkgmap in ${CHECK_CMD_PKGS[@]}; do
-    install_map+=(${PKG_MAP[$pkgmap]} )
+  for pkgmap in "${CHECK_CMD_PKGS[@]}"; do
+    install_map+=( "${PKG_MAP[$pkgmap]}" )
   done
 
-  install_map+=(${EXTRA_PKG_DEPS[@]} )
-
   echo "Info  : Install ${install_map[*]} using $PKG_MGR on jumphost"
-  # shellcheck disable=SC2068
-  ${INSTALLER_CMD} ${install_map[@]} > /dev/null 2>&1
+  # shellcheck disable=SC2068 disable=SC2086
+  redirect_cmd ${INSTALLER_CMD} ${install_map[@]}
 
   # We need to prepare our virtualenv now
-  virtualenv -p python3 --quiet --no-site-packages ${TEST_VENV} > /dev/null 2>&1
+  redirect_cmd virtualenv -p python3 --no-site-packages "${TEST_VENV}"
   set +u
   # shellcheck disable=SC1090
-  source ${TEST_VENV}/bin/activate
+  source "${TEST_VENV}/bin/activate"
   set -u
 
   # since we use bindep.txt to control distro packages to install, we need to install bindep first using pip
@@ -272,8 +277,8 @@
   cd "$TEST_PATH"
   # bindep -b exits with non-zero if it identifies a missing package so we disable pipefail
   set +o pipefail
-  # shellcheck disable=SC2046
-  bindep -b &> /dev/null || ${INSTALLER_CMD} $(bindep -b) > /dev/null 2>&1
+  # shellcheck disable=SC2046 disable=SC2086
+  bindep -b &> /dev/null || redirect_cmd ${INSTALLER_CMD} $(bindep -b)
   set -o pipefail
 
   echo "Info  : Install python packages listed in requirements.txt using pip"
@@ -290,7 +295,7 @@
     venv_site_packages_dir="${TEST_VENV}"/lib/python3*/site-packages
     cd /tmp
     echo "Info  : Download and install python3-apt using apt"
-    apt download -q=3 python3-apt > /dev/null 2>&1
+    redirect_cmd apt download python3-apt
 
     dpkg -x python3-apt_*.deb python3-apt
     chown -R "$USER:$USER" /tmp/python3-apt/usr/lib/python3*/dist-packages
@@ -354,4 +359,19 @@
 
 }
 
+#-------------------------------------------------------------------------------
+# In some cases, it is useful to see all the output generated by commands so
+# this function makes it possible for users to achieve that by not redirecting
+# output to /dev/null when verbosity is enabled
+#-------------------------------------------------------------------------------
+redirect_cmd() {
+
+  if [[ "$VERBOSITY" == "false" ]]; then
+    "$@" > /dev/null 2>&1
+  else
+    "$@"
+  fi
+
+}
+
 # vim: set ts=2 sw=2 expandtab:
diff --git a/test/config/test-vars b/test/library/test-vars.sh
similarity index 89%
rename from test/config/test-vars
rename to test/library/test-vars.sh
index 9ed3605..15fc53d 100644
--- a/test/config/test-vars
+++ b/test/library/test-vars.sh
@@ -1,3 +1,4 @@
+#!/bin/bash
 # ============LICENSE_START=======================================================
 #  Copyright (C) 2019 The Nordix Foundation. All rights reserved.
 # ================================================================================
@@ -27,11 +28,16 @@
 #-------------------------------------------------------------------------------
 # Set paths of key stuff
 #-------------------------------------------------------------------------------
+# location of nordix workspace
+export TEST_WORKSPACE="${ENGINE_WORKSPACE:-/opt/test}"
+export ENGINE_WORKSPACE="${ENGINE_WORKSPACE:-/opt/engine}"
+export ENGINE_CACHE=/opt/engine/.cache
+
 # location of TEST_VENV
-export TEST_VENV=${TEST_PATH}/.venv/
+export TEST_VENV="${TEST_WORKSPACE}/.venv"
 
 # location of the cache the repositories will be cloned
-export TEST_CACHE=${TEST_PATH}/.cache
+export TEST_CACHE=${TEST_WORKSPACE}/.cache
 
 #-------------------------------------------------------------------------------
 # Set versions of the engine, pip and ansible here as we use bash
@@ -39,12 +45,6 @@
 # the Engine version to use
 export TEST_VERSION=${TEST_VERSION:-"master"}
 
-# PIP version to use
-export TEST_PIP_VERSION="19.0.3"
-
-# Ansible version to use
-export TEST_ANSIBLE_VERSION="2.7.8"
-
 #-------------------------------------------------------------------------------
 # Other variables
 #-------------------------------------------------------------------------------
diff --git a/test/playbooks/roles/configure-jumphost/tasks/install-packages-Debian.yml b/test/playbooks/roles/configure-jumphost/tasks/install-packages-Debian.yml
index 717cae8..5407e1c 100644
--- a/test/playbooks/roles/configure-jumphost/tasks/install-packages-Debian.yml
+++ b/test/playbooks/roles/configure-jumphost/tasks/install-packages-Debian.yml
@@ -24,7 +24,7 @@
 
 - name: Add docker apt repository
   apt_repository:
-    repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename}} stable"
+    repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} stable"
     state: present
 
 - name: Install docker
diff --git a/test/playbooks/roles/configure-jumphost/tasks/main.yml b/test/playbooks/roles/configure-jumphost/tasks/main.yml
index 34edc1a..c4ef86d 100644
--- a/test/playbooks/roles/configure-jumphost/tasks/main.yml
+++ b/test/playbooks/roles/configure-jumphost/tasks/main.yml
@@ -38,7 +38,8 @@
   when: not st.stat.exists
 
 - name: Check if docker daemon is configured with iptables
-  shell: grep '\-\-iptables\=True' /etc/systemd/system/docker.service.d/docker-options.conf
+  command: grep '\-\-iptables\=True' /etc/systemd/system/docker.service.d/docker-options.conf
+  changed_when: false
   register: docker_check
   ignore_errors: true
 
@@ -51,7 +52,7 @@
     backrefs: true
     regexp: '^(Environment="DOCKER_OPTS.*)"$'
     line: '\1 --iptables=True"'
-  when: docker_check.stdout == ""
+  when: not docker_check.stdout
 
 - name: Enable and start docker service
   service:
diff --git a/test/playbooks/roles/prepare-testframework/tasks/main.yml b/test/playbooks/roles/prepare-testframework/tasks/main.yml
index 98f4aca..0c08b8f 100644
--- a/test/playbooks/roles/prepare-testframework/tasks/main.yml
+++ b/test/playbooks/roles/prepare-testframework/tasks/main.yml
@@ -22,7 +22,7 @@
     testfw_image: "{{ framework[test_fw][sut][test_suite].image }}"
     testfw_version: "{{ framework[test_fw][sut][test_suite].version }}"
     testfw_git_version: "{{ framework[test_fw][sut][test_suite].git_version }}"
-    test_filter: "{{ framework[test_fw][sut][test_suite].filter | default(['all'], true)}}"
+    test_filter: "{{ framework[test_fw][sut][test_suite].filter | default(['all'], true) }}"
 
 # Prepare tests
 - include: prepare-{{ test_fw }}-{{ sut }}.yml
diff --git a/test/playbooks/roles/prepare-testframework/tasks/prepare-functest-kubernetes.yml b/test/playbooks/roles/prepare-testframework/tasks/prepare-functest-kubernetes.yml
index 1eb12fc..eecdb0c 100644
--- a/test/playbooks/roles/prepare-testframework/tasks/prepare-functest-kubernetes.yml
+++ b/test/playbooks/roles/prepare-testframework/tasks/prepare-functest-kubernetes.yml
@@ -17,7 +17,8 @@
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=========================================================
 
-- set_fact:
+- name: Set test work and result directories
+  set_fact:
     testfw_workdir: "{{ test_cache }}/{{ test_fw }}"
     testfw_resultdir: "{{ test_cache }}/{{ test_fw }}/results"
 
@@ -26,19 +27,28 @@
     path: "{{ testfw_workdir }}"
     state: "{{ item }}"
     mode: 0755
-    owner: "{{ lookup('env','USER') }}"
   with_items:
     - absent
     - directory
   become: true
 
 - name: Get kubernetes master URL
-  shell: "kubectl config view  | grep server | cut -d' ' -f6"
+  shell: |
+    set -o pipefail
+    kubectl config view | grep server | cut -d' ' -f6
+  args:
+    executable: /bin/bash
   register: master_url
+  changed_when: false
 
 - name: Get kubernetes master IP
-  shell: "kubectl config view  | grep server | cut -d'/' -f3 | sed 's/:.*//g'"
+  shell: |
+    set -o pipefail
+    kubectl config view  | grep server | cut -d'/' -f3 | sed 's/:.*//g'
+  args:
+    executable: /bin/bash
   register: master_ip
+  changed_when: false
 
 - name: Set kubernetes master URL and IP
   set_fact:
@@ -51,16 +61,16 @@
     dest: "{{ item.dest }}"
     mode: "{{ item.mode }}"
   with_items:
-    - { src: "functest-kubernetes.env.j2", dest: "{{ testfw_workdir }}/functest.env", mode: "0644" }
-    - { src: "functest-kubernetes.creds.j2", dest: "{{ testfw_workdir }}/functest.creds", mode: "0644" }
-    - { src: "functest-kubernetes.sh.j2", dest: "{{ testfw_workdir }}/run-tests.sh", mode: "0755" }
+    - {src: "functest-kubernetes.env.j2", dest: "{{ testfw_workdir }}/functest.env", mode: "0644"}
+    - {src: "functest-kubernetes.creds.j2", dest: "{{ testfw_workdir }}/functest.creds", mode: "0644"}
+    - {src: "functest-kubernetes.sh.j2", dest: "{{ testfw_workdir }}/run-tests.sh", mode: "0755"}
+  become: true
 
 - name: Ensure {{ testfw_resultdir }} exists and empty
   file:
     path: "{{ testfw_resultdir }}"
     state: "{{ item }}"
     mode: 0755
-    owner: "{{ lookup('env','USER') }}"
   with_items:
     - absent
     - directory
diff --git a/test/playbooks/roles/prepare-testframework/tasks/prepare-functest-openstack.yml b/test/playbooks/roles/prepare-testframework/tasks/prepare-functest-openstack.yml
index fbb2b7f..80f6183 100644
--- a/test/playbooks/roles/prepare-testframework/tasks/prepare-functest-openstack.yml
+++ b/test/playbooks/roles/prepare-testframework/tasks/prepare-functest-openstack.yml
@@ -17,7 +17,8 @@
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=========================================================
 
-- set_fact:
+- name: Set test work, images, and result directories
+  set_fact:
     testfw_workdir: "{{ test_cache }}/{{ test_fw }}"
     testfw_imagesdir: "{{ test_cache }}/{{ test_fw }}/images"
     testfw_resultdir: "{{ test_cache }}/{{ test_fw }}/results"
@@ -27,7 +28,6 @@
     path: "{{ testfw_workdir }}"
     state: "{{ item }}"
     mode: 0755
-    owner: "{{ lookup('env','USER') }}"
   with_items:
     - absent
     - directory
@@ -41,6 +41,7 @@
 
 - name: Gather facts about a previously created network with filter
   command: "openstack network list --external -c 'Name' -f value"
+  changed_when: false
   register: external_network
 
 - name: Prepare files necessary to run {{ test_fw }}
@@ -49,8 +50,9 @@
     dest: "{{ item.dest }}"
     mode: "{{ item.mode }}"
   with_items:
-    - { src: "functest-openstack.env.j2", dest: "{{ testfw_workdir }}/functest.env", mode: "0644" }
-    - { src: "functest-openstack.sh.j2", dest: "{{ testfw_workdir }}/run-tests.sh", mode: "0755" }
+    - {src: "functest-openstack.env.j2", dest: "{{ testfw_workdir }}/functest.env", mode: "0644"}
+    - {src: "functest-openstack.sh.j2", dest: "{{ testfw_workdir }}/run-tests.sh", mode: "0755"}
+  become: true
 
 - name: Ensure {{ testfw_imagesdir }} exists and empty
   file:
@@ -75,6 +77,7 @@
     url: "{{ arm_url }}/{{ cirros_image }}"
     dest: "{{ testfw_imagesdir }}/"
     checksum: sha256:a8dd75ecffd4cdd96072d60c2237b448e0c8b2bc94d57f10fdbc8c481d9005b8
+  become: true
 
 - name: Download shaker and ubuntu images for smoke test
   get_url:
@@ -86,6 +89,7 @@
   loop_control:
     loop_var: image
   when: test_suite == 'smoke'
+  become: true
 
 - name: Ensure {{ testfw_resultdir }} exists and empty
   file:
diff --git a/test/playbooks/roles/prepare-testframework/tasks/prepare-yardstick-kubernetes.yml b/test/playbooks/roles/prepare-testframework/tasks/prepare-yardstick-kubernetes.yml
index 56524c9..a5cf228 100644
--- a/test/playbooks/roles/prepare-testframework/tasks/prepare-yardstick-kubernetes.yml
+++ b/test/playbooks/roles/prepare-testframework/tasks/prepare-yardstick-kubernetes.yml
@@ -17,7 +17,8 @@
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=========================================================
 
-- set_fact:
+- name: Set test work and result directories
+  set_fact:
     testfw_workdir: "{{ test_cache }}/{{ test_fw }}"
     testfw_resultdir: "{{ test_cache }}/{{ test_fw }}/results"
 
@@ -26,18 +27,27 @@
     path: "{{ testfw_workdir }}"
     state: "{{ item }}"
     mode: 0755
-    owner: "{{ lookup('env','USER') }}"
   with_items:
     - absent
     - directory
   become: true
 
 - name: Get kubernetes master URL
-  shell: "kubectl config view  | grep server | cut -d' ' -f6"
+  shell: |
+    set -o pipefail
+    kubectl config view  | grep server | cut -d' ' -f6
+  args:
+    executable: /bin/bash
+  changed_when: false
   register: master_url
 
 - name: Get kubernetes master IP
-  shell: "kubectl config view  | grep server | cut -d'/' -f3 | sed 's/:.*//g'"
+  shell: |
+    set -o pipefail
+    kubectl config view  | grep server | cut -d'/' -f3 | sed 's/:.*//g'
+  args:
+    executable: /bin/bash
+  changed_when: false
   register: master_ip
 
 - name: Set kubernetes master URL and IP
@@ -51,8 +61,9 @@
     dest: "{{ item.dest }}"
     mode: "{{ item.mode }}"
   with_items:
-    - { src: "yardstick-kubernetes.env.j2", dest: "{{ testfw_workdir }}/yardstick.env", mode: "0644" }
-    - { src: "yardstick-kubernetes.sh.j2", dest: "{{ testfw_workdir }}/run-tests.sh", mode: "0755" }
+    - {src: "yardstick-kubernetes.env.j2", dest: "{{ testfw_workdir }}/yardstick.env", mode: "0644"}
+    - {src: "yardstick-kubernetes.sh.j2", dest: "{{ testfw_workdir }}/run-tests.sh", mode: "0755"}
+  become: true
 
 - name: Ensure {{ testfw_resultdir }} exists and empty
   file:
@@ -75,4 +86,6 @@
     http_proxy: "{{ lookup('env','http_proxy') }}"
     https_proxy: "{{ lookup('env','https_proxy') }}"
     no_proxy: "{{ lookup('env','no_proxy') }}"
+  become: true
+
 # vim: set ts=2 sw=2 expandtab:
diff --git a/test/playbooks/roles/prepare-testframework/tasks/prepare-yardstick-openstack.yml b/test/playbooks/roles/prepare-testframework/tasks/prepare-yardstick-openstack.yml
index 50826dd..c330bea 100644
--- a/test/playbooks/roles/prepare-testframework/tasks/prepare-yardstick-openstack.yml
+++ b/test/playbooks/roles/prepare-testframework/tasks/prepare-yardstick-openstack.yml
@@ -17,7 +17,8 @@
 # SPDX-License-Identifier: Apache-2.0
 # ============LICENSE_END=========================================================
 
-- set_fact:
+- name: Set test work and result directories
+  set_fact:
     testfw_workdir: "{{ test_cache }}/{{ test_fw }}"
     testfw_resultdir: "{{ test_cache }}/{{ test_fw }}/results"
 
@@ -26,7 +27,6 @@
     path: "{{ testfw_workdir }}"
     state: "{{ item }}"
     mode: 0755
-    owner: "{{ lookup('env','USER') }}"
   with_items:
     - absent
     - directory
@@ -40,6 +40,7 @@
 
 - name: Gather facts about a previously created network with filter
   command: "openstack network list --external -c 'Name' -f value"
+  changed_when: false
   register: external_network
 
 - name: Prepare files necessary to run {{ test_fw }}
@@ -48,7 +49,8 @@
     dest: "{{ item.dest }}"
     mode: "{{ item.mode }}"
   with_items:
-    - { src: "yardstick-openstack.sh.j2", dest: "{{ testfw_workdir }}/run-tests.sh", mode: "0755" }
+    - {src: "yardstick-openstack.sh.j2", dest: "{{ testfw_workdir }}/run-tests.sh", mode: "0755"}
+  become: true
 
 - name: Ensure {{ testfw_resultdir }} exists and empty
   file:
@@ -71,4 +73,6 @@
     http_proxy: "{{ lookup('env','http_proxy') }}"
     https_proxy: "{{ lookup('env','https_proxy') }}"
     no_proxy: "{{ lookup('env','no_proxy') }}"
+  become: true
+
 # vim: set ts=2 sw=2 expandtab:
diff --git a/test/test.sh b/test/test.sh
index 595f411..78a1229 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -25,15 +25,17 @@
 # Getting ready to run
 #-------------------------------------------------------------------------------
 # Find/set where we are
-export TEST_PATH="$(git rev-parse --show-toplevel)"
+TEST_PATH="$(git rev-parse --show-toplevel)"
+export TEST_PATH
 
 # source helpers library
-source ${TEST_PATH}/test/library/test-lib.sh
+# shellcheck source=test/library/test-lib.sh
+source "${TEST_PATH}/test/library/test-lib.sh"
 
 #-------------------------------------------------------------------------------
 # Parse command line options
 #-------------------------------------------------------------------------------
-parse_cmdline_opts $*
+parse_cmdline_opts "$@"
 
 #-------------------------------------------------------------------------------
 # Check prerequisites before doing anything else to see if we should continue
@@ -60,8 +62,8 @@
 #-------------------------------------------------------------------------------
 echo "Info  : Prepare jumphost for test execution"
 echo "-------------------------------------------------------------------------"
-cd ${TEST_PATH}
-ansible-playbook ${TEST_ANSIBLE_PARAMS} \
+cd "${TEST_PATH}"
+ansible-playbook "${TEST_ANSIBLE_PARAMS[@]}" \
   -i "${TEST_PATH}/test/inventory/inventory.ini" \
   test/playbooks/configure-jumphost.yml
 echo "-------------------------------------------------------------------------"
@@ -71,8 +73,8 @@
 #-------------------------------------------------------------------------------
 echo "Info  : Preparing test framework"
 echo "-------------------------------------------------------------------------"
-cd ${TEST_PATH}
-ansible-playbook ${TEST_ANSIBLE_PARAMS} \
+cd "${TEST_PATH}"
+ansible-playbook "${TEST_ANSIBLE_PARAMS[@]}" \
   -i "${TEST_PATH}/test/inventory/inventory.ini" \
   test/playbooks/prepare-testframework.yml
 echo "-------------------------------------------------------------------------"
@@ -82,8 +84,8 @@
 #-------------------------------------------------------------------------------
 echo "Info  : Running tests"
 echo "-------------------------------------------------------------------------"
-cd ${TEST_PATH}
-ansible-playbook ${TEST_ANSIBLE_PARAMS} \
+cd "${TEST_PATH}"
+ansible-playbook "${TEST_ANSIBLE_PARAMS[@]}" \
   -i "${TEST_PATH}/test/inventory/inventory.ini" \
   test/playbooks/run-tests.yaml
 echo "-------------------------------------------------------------------------"
diff --git a/tox.ini b/tox.ini
index 48f0bcf..3fbf0f4 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,28 +3,28 @@
 minversion = 2.5
 skipsdist = true
 envlist = ansible-lint, shellcheck, yamllint
-install_command = pip install {opts} {packages}
+install_command = pip install --force-reinstall {opts} {packages}
 deps = -r{toxinidir}/test-requirements.txt
 
 [testenv:ansible-lint]
-# TODO: set ignore_outcome to false once issues are fixed
-ignore_outcome = true
+description = invoke ansible-lint to analyse Ansible playbooks and roles
+deps = -r{toxinidir}/test-requirements.txt
 whitelist_externals = bash
 commands =
-  bash -c "find {toxinidir}/test -type f -regex '.*.ya?ml' ! -regex '.*heat-template.*' -print0 | \
+  bash -c "find {toxinidir}/test -type f -regex '.*.ya?ml' -print0 | \
     xargs -t -n1 -0 ansible-lint --nocolor"
 
 [testenv:yamllint]
-# TODO: set ignore_outcome to false once issues are fixed
-ignore_outcome = true
+description = invoke yamllint to analyse YAML files
+deps = -r{toxinidir}/test-requirements.txt
 whitelist_externals = bash
 commands =
   bash -c "find {toxinidir}/test -type f -regex '.*.ya?ml' -print0 | \
     xargs -t -n1 -0 yamllint --format standard --strict"
 
 [testenv:shellcheck]
-# TODO: set ignore_outcome to false once issues are fixed
-ignore_outcome = true
+description = invoke shellcheck to analyse bash shell scripts
+deps = -r{toxinidir}/test-requirements.txt
 whitelist_externals = bash
 commands =
   bash -c "find {toxinidir}/test -type f -name '*.sh' -print0 | \