Tox to manage all test environments on its own

Functional tests have been moved to a separate tox
enviroment called functional, so running either py26
or py27 one will only trigger unit tests.

Scripts for prepairing and cleaning up Nailgun environments
have been factored out of run_tests.sh.

run_tests.sh is kept just compatibility reasons and will be
removed once FuelCI is updated.

DocImpact
Partial-bug: #1476808
Closes-bug: #1486030
Change-Id: I492ce5d1ed26a182cc6876594d4508088afd5411
This commit is contained in:
Roman Prykhodchenko 2015-07-31 18:47:27 +02:00
parent 0d5928fd4f
commit 224b490d7d
5 changed files with 252 additions and 459 deletions

2
.gitignore vendored
View File

@ -27,3 +27,5 @@ test_run/*
*.egg-info
fuelclient.xml
.coverage

View File

@ -14,458 +14,5 @@
# License for the specific language governing permissions and limitations
# under the License.
set -e
# settings
ROOT=$(dirname $(readlink -f $0))
FUEL_WEB_REPO=${FUEL_WEB_REPO:-"https://github.com/stackforge/fuel-web.git"}
FUEL_WEB_ROOT=${FUEL_WEB_ROOT:-"/tmp/fuel_web"}
NAILGUN_ROOT=$FUEL_WEB_ROOT/nailgun
FETCH_REPO=${FETCH_REPO:-""}
FETCH_REFSPEC=${FETCH_REFSPEC:-""}
FUEL_COMMIT=${FUEL_COMMIT:-master}
# Nailgun server parameters
NAILGUN_PORT=${NAILGUN_PORT:-8003}
NAILGUN_CHECK_URL=${NAILGUN_CHECK_URL:-"http://0.0.0.0:$NAILGUN_PORT/api/version"}
NAILGUN_START_MAX_WAIT_TIME=${NAILGUN_START_MAX_WAIT_TIME:-10}
TEST_NAILGUN_DB=${TEST_NAILGUN_DB:-nailgun}
# pytest options
ARTIFACTS=${ARTIFACTS:-$(pwd)/test_run}
FUELCLIENT_JUNIT=${FUELCLIENT_JUNIT:-"$ROOT/fuelclient.xml"}
# A POSIX variable
OPTIND=1
# Prints usage and exist
usage() {
msg "Usage: $0 [OPTIONS] [-- TESTR OPTIONS]"
msg "Run python-fuelclient test suite"
msg ""
msg " -6 --py26 Run python-2.6 tests."
msg " -7 --py27 Run python-2.7 tests."
msg " If neither -6 nor -7 is specified, tests for both"
msg " versions will be running."
msg " -c --fuel-commit Checkout fuel-web to a specified commit. If not specified,"
msg " \$FUEL_COMMIT or master will be used."
msg " By default checks out to master."
msg " -f --fetch-repo URI of a remote repo for fetching changes to fuel-web."
msg " If not specified, \$FETCH_REPO or nothing will be used."
msg " -h, --help Print this usage message and exit."
msg " -i --functional-tests Run only functional tests."
msg " -I --no-functional-tests Don't run functional tests."
msg " -n --no-clone Do not clone fuel-web repo and use existing"
msg " one specified in \$FUEL_WEB_ROOT. Make sure it"
msg " does not have pending changes."
msg " -p --pep8 Runs only flake8 tests."
msg " -P --no-pep8 Do not run flake8 tests automatically."
msg " -r --fetch-refspec Refspec to fetch from the remore repo. This option"
msg " requires -r or --fetch-refspec to be specified."
msg " If not specified, \$FETCH_REFSPEC or nothing will be used."
msg " -t --test Test to run. To run many tests, pass this flag multiple times."
msg " Runs all tests, if not specified. "
msg " -u --unit-tests Run only unit tests."
msg " -U --no-unit-tests Don't run unit tests."
msg " -V --client-version Which version of python-fuelclient should be tested, for example:"
msg " ./run_tests.sh -V v1"
msg " To test multiple versions, pass this flag multiple times."
exit
}
err() {
printf "%s\n" "$1" >&2
}
msg() {
printf "%s\n" "$1"
}
# Reads input options and sets appropriate parameters
process_options() {
# Read the options
TEMP=$(getopt \
-o 67huUiInpPc:f:r:t:V: \
--long py26,py27,help,functional-tests,no-functional-tests,no-pep8,pep8,unit-tests,no-unit-tests,no-clone,client-version:,fuel-commit:,fetch-repo:,fetch-refspec:,tests: \
-n 'run_tests.sh' -- "$@")
eval set -- "$TEMP"
while true ; do
case "$1" in
-6|--py26) python_26=1; shift 1;;
-7|--py27) python_27=1; shift 1;;
-h|--help) usage; shift 1;;
-f|--fetch-repo) fetch_repo="$2"; shift 2;;
-r|--fetch-refspec) fetch_refspec="$2"; shift 2;;
-c|--fuel-commit) fuel_commit="$2"; shift 2;;
-n|--no-clone) do_clone=0; shift 1;;
-t|--test) certain_tests+=("$2"); shift 2;;
-p|--pep8) pep8_only=1; shift 1;;
-P|--no-pep8) do_pep8=0; shift 1;;
-i|--functional-tests) functional_tests=1; shift 1;;
-I|--no-functional-tests) no_functional_tests=1; shift 1;;
-u|--unit-tests) unit_tests=1; shift 1;;
-U|--no-unit-tests) no_unit_tests=1; shift 1;;
-V|--client-version) client_version+=("$2"); shift 2;;
# All parameters and alien options will be passed to testr
--) shift 1; testropts="$@";
break;;
*) err "Internal error: got \"$1\" argument.";
usage; exit 1
esac
done
if [[ -z $fetch_repo && -n $fetch_refspec ]]; then
err "--fetch-refspec option requires --fetch-repo to be specified."
exit 1
fi
if [[ $unit_tests -eq 0 && \
$functional_tests -eq 0 && \
${#certain_tests[@]} -eq 0 ]]; then
if [[ $no_unit_tests -ne 1 ]]; then
unit_tests=1
fi
if [[ $no_functional_tests -ne 1 ]]; then
functional_tests=1
fi
else
do_pep8=0
fi
# when no version specified, choose all of the versions available
if [[ ${#client_version[@]} -eq 0 ]]; then
client_version+=("common")
for version in ${ROOT}/fuelclient/tests/v[0-9]; do
client_version+=(`basename $version`)
done
fi
for version in ${client_version[@]}; do
if [[ $unit_tests -eq 1 ]]; then
certain_tests+=("${ROOT}/fuelclient/tests/unit/${version}/")
fi
if [[ $functional_tests -eq 1 ]]; then
certain_tests+=("${ROOT}/fuelclient/tests/functional/${version}/")
fi
done
# Check that specified test file/dir exists. Fail otherwise.
if [[ ${#certain_tests[@]} -ne 0 ]]; then
for test in ${certain_tests[@]}; do
local file_name=${test%:*}
if [[ ! -f $file_name ]] && [[ ! -d $file_name ]]; then
err "Specified tests were not found."
exit 1
fi
done
fi
}
# Arguments:
#
# $1 -- path to the server config
#
# Run python-fuelclient tests.
# It is supposed that nailgun server is up and running.
run_cli_tests() {
local config=$1
local run_single_env=$((python_26^python_27))
local py26_env="py26"
local py27_env="py27"
if [[ $run_single_env -eq 1 ]]; then
if [[ $python_26 -eq 1 ]]; then
env_to_run=$py26_env
else
env_to_run=$py27_env
fi
else
env_to_run=$py26_env,$py27_env
fi
pushd $ROOT/fuelclient > /dev/null
# run tests
NAILGUN_CONFIG=$config LISTEN_PORT=$NAILGUN_PORT \
NAILGUN_ROOT=$NAILGUN_ROOT tox -e$env_to_run -- -vv $testropts \
${certain_tests[@]} --junit-xml $FUELCLIENT_JUNIT || return 1
popd > /dev/null
return 0
}
run_pep8() {
local ret=0
pushd $ROOT/fuelclient > /dev/null
tox -e pep8 || ret=$?
popd > /dev/null
return $ret
}
# Arguments:
#
# $1 -- path to the server config
#
# Remove temporary files and database.
# Does not touch $FUEL_WEB_ROOT if -n or --no-clone is specified.
run_cleanup() {
local config=$1
msg "Doing a clean up."
kill_server
find . -type f -name "*.pyc" -delete
if [[ -f "$config" ]] && [[ -d $NAILGUN_ROOT ]]; then
pushd $NAILGUN_ROOT > /dev/null
NAILGUN_CONFIG=$config tox -evenv -- \
python manage.py dropdb > /dev/null
popd > /dev/null
fi
if [[ -f $ARTIFACTS ]]; then
rm -rf $ARTIFACTS
fi
if [[ -v server_log ]]; then
rm -f $server_log
fi
if [[ "$do_clone" -ne "0" ]] && [[ -d $FUEL_WEB_ROOT ]]; then
rm -rf $FUEL_WEB_ROOT
fi
}
# Arguments:
#
# $1 -- path to the server config
# $2 -- insert default data into database if true
#
# Synchronizes the schema of the database and loads default data.
setup_db() {
local config=$1
local defaults=$2
msg "Setting up database."
pushd $NAILGUN_ROOT > /dev/null
NAILGUN_CONFIG=$config tox -evenv -- python manage.py syncdb > /dev/null
if [[ $# -ne 0 && $defaults = true ]]; then
NAILGUN_CONFIG=$config tox -evenv -- \
python manage.py loaddefault > /dev/null
fi
popd > /dev/null
}
# Sends SIGING to running Nailgun
kill_server() {
# kill old server instance if exists
local pid=$(lsof -ti tcp:$NAILGUN_PORT)
if [[ -n "$pid" ]]; then
kill $pid
sleep $NAILGUN_START_MAX_WAIT_TIME
fi
}
# Arguments:
#
# $1 -- path to log file
# $2 -- path to the server config
#
# Returns: a server pid, that you have to close manually
run_server() {
local server_log=$1
local server_config=$2
local run_server_cmd="\
python manage.py run \
--port=$NAILGUN_PORT \
--config=$server_config \
--fake-tasks \
--fake-tasks-tick-count=80 \
--fake-tasks-tick-interval=1"
kill_server
# run new server instance
pushd $NAILGUN_ROOT > /dev/null
tox -evenv -- $run_server_cmd >> $server_log 2>&1 &
popd > /dev/null
# wait for server availability
which curl > /dev/null
if [[ $? -eq 0 ]]; then
local num_retries=$((NAILGUN_START_MAX_WAIT_TIME * 10))
local i=0
while true; do
# Fail if number of retries exeeded
if [[ $i -gt $((num_retries + 1)) ]]; then return 1; fi
local http_code=$(curl -s -w %{http_code} -o /dev/null $NAILGUN_CHECK_URL)
if [[ "$http_code" = "200" ]]; then return 0; fi
sleep 0.1
i=$((i + 1))
done
else
sleep $NAILGUN_START_MAX_WAIT_TIME
lsof -ti tcp:$NAILGUN_PORT
return $?
fi
}
# Arguments
#
# $1 -- path to the server config
#
# Set up test environment
prepare_env() {
local config=$1
msg "Preparing test environment."
obtain_nailgun || return 1
mkdir -p $ARTIFACTS
create_settings_yaml $config $ARTIFACTS
setup_db $config true
server_log=$(mktemp /tmp/test_nailgun_cli_server.XXXX)
run_server $server_log $config || \
{ err "Failed to start Nailgun"; return 1; }
}
# Arguments:
#
# $1 -- path to the server config
# $2 -- path to folder with testing artifacts
#
# Generates server configuration file
create_settings_yaml() {
local config_path=$1
local artifacts_path=$2
cat > $config_path <<EOL
DEVELOPMENT: 1
STATIC_DIR: ${artifacts_path}/static_compressed
TEMPLATE_DIR: ${artifacts_path}/static_compressed
DATABASE:
name: ${TEST_NAILGUN_DB}
engine: "postgresql"
host: "localhost"
port: "5432"
user: "nailgun"
passwd: "nailgun"
API_LOG: ${artifacts_path}/api.log
APP_LOG: ${artifacts_path}/app.log
EOL
}
# Clones Nailgun from git, pulls specified patch and
# switches to the specified commit
obtain_nailgun() {
err "Obtaining Nailgun with the revision $fuel_commit"
if [[ $do_clone -ne 0 ]]; then
git clone $FUEL_WEB_REPO $FUEL_WEB_ROOT || \
{ err "Failed to clone Nailgun"; return 1; }
fi
if [[ ! -d "$NAILGUN_ROOT" ]]; then
err "Nailgun directory $NAILGUN_ROOT not found."
exit 1
fi
pushd $NAILGUN_ROOT > /dev/null
if [[ -n $fetch_repo ]]; then
err "Fetching changes from $fetch_repo $fetch_refspec"
git fetch $fetch_repo $fetch_refspec || \
{ err "Failed to pull changes"; return 1; }
fi
git checkout $fuel_commit || \
{ err "Failed to checkout to $fuel_commit"; return 1; }
popd > /dev/null
}
# Sets default values for parameters
init_default_params() {
certain_tests=()
client_version=()
do_clone=1
do_pep8=1
fetch_refspec=$FETCH_REFSPEC
fetch_repo=$FETCH_REPO
fuel_commit=$FUEL_COMMIT
functional_tests=0
no_functional_tests=0
no_unit_tests=0
pep8_only=0
python_26=0
python_27=0
testropts="--with-timer --timer-warning=10 --timer-ok=2 --timer-top-n=10"
unit_tests=0
}
# Main function
run() {
local config=$ARTIFACTS/test.yaml
local pep8_ret=0
local cli_ret=0
run_cleanup $config
if [[ $do_pep8 -eq 1 ]]; then
err "Starting PEP8 tests..."
run_pep8 || pep8_ret=$?
[[ $pep8_ret -ne 0 ]] && err "Failed tests: pep8"
[[ $pep8_only -eq 1 ]] && exit $pep8_ret
fi
if [[ "${certain_tests[@]}" == *"functional"* ]]; then
prepare_env $config
fi
err "Starting python-fuelclient tests..."
run_cli_tests $config || cli_ret=$?
[[ $cli_ret -ne 0 ]] && err "Failed tests: cli_tests"
run_cleanup $config
if [[ $pep8_ret -ne 0 || $cli_ret -ne 0 ]]; then
err "Testing python-fuelclient failed."
exit 1
else
err "Testing python-fuelclient succeeded."
exit 0
fi
}
init_default_params
process_options $@
run
# THIS FILE WILL BE REMOVED AS SOON AS FUEL-CI IS UPDATED.
tox

60
tools/cleanup.sh Normal file
View File

@ -0,0 +1,60 @@
#!/bin/bash
# Copyright 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
set -eu
NAILGUN_CONFIG=$ARTIFACTS/test.yaml
# Sends SIGING to the running instance of Nailgun, if it exists
kill_server() {
echo "Stopping Nailgun and waiting $NAILGUN_START_MAX_WAIT_TIME seconds."
local pid=$(lsof -ti tcp:$NAILGUN_PORT)
if [[ -n "$pid" ]]; then
kill $pid
sleep $NAILGUN_START_MAX_WAIT_TIME
fi
}
drop_database () {
echo "Dropping the database."
if [[ -f "$NAILGUN_CONFIG" ]] && [[ -d $NAILGUN_ROOT ]]; then
pushd $NAILGUN_ROOT > /dev/null
tox -e venv -- python manage.py dropdb > /dev/null
popd > /dev/null
fi
}
delete_files() {
echo "Deleting the files."
rm -rf "$ARTIFACTS"
if [[ "$FUEL_WEB_CLONE" == "yes" ]] && [[ -d "$FUEL_WEB_ROOT" ]]; then
rm -rf "$FUEL_WEB_ROOT"
fi
}
echo "Doing a clean up to ensure clean environment."
kill_server
drop_database
delete_files

154
tools/prepare_nailgun.sh Normal file
View File

@ -0,0 +1,154 @@
#!/bin/bash
# Copyright 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
set -eu
NAILGUN_CONFIG=$ARTIFACTS/test.yaml
err() {
printf "%s\n" "$1" >&2
}
msg() {
printf "%s\n" "$1"
}
# Synchronizes the schema of the database and loads default data.
setup_db() {
msg "Setting up database."
pushd $NAILGUN_ROOT > /dev/null
tox -e venv -- python manage.py syncdb > /dev/null
tox -e venv -- python manage.py loaddefault > /dev/null
popd > /dev/null
}
# Returns: a server pid, that you have to close manually
run_server() {
local run_server_cmd="\
python manage.py run \
--port=$NAILGUN_PORT \
--config=$NAILGUN_CONFIG \
--fake-tasks \
--fake-tasks-tick-count=80 \
--fake-tasks-tick-interval=1"
local server_log=$(mktemp $ARTIFACTS/test_nailgun_cli_server.XXXX)
local check_url="http://0.0.0.0:${NAILGUN_PORT}${NAILGUN_CHECK_PATH}"
# run new server instance
pushd $NAILGUN_ROOT > /dev/null
tox -e venv -- $run_server_cmd >> $server_log 2>&1 &
popd > /dev/null
# Wait for server's availability
which curl > /dev/null
if [[ $? -eq 0 ]]; then
local num_retries=$((NAILGUN_START_MAX_WAIT_TIME * 10))
local i=0
while true; do
# Fail if number of retries exeeded
if [[ $i -gt $((num_retries + 1)) ]]; then return 1; fi
local http_code=$(curl -s -w %{http_code} -o /dev/null $check_url)
if [[ "$http_code" = "200" ]]; then return 0; fi
sleep 0.1
i=$((i + 1))
done
else
err "Failed to start Nailgun in ${NAILGUN_START_MAX_WAIT_TIME} seconds."
exit 1
fi
}
# Set up test environment
prepare_env() {
mkdir -p $ARTIFACTS
}
# Generates server configuration file
create_settings_yaml() {
cat > $NAILGUN_CONFIG <<EOL
DEVELOPMENT: 1
STATIC_DIR: ${ARTIFACTS}/static_compressed
TEMPLATE_DIR: ${ARTIFACTS}/static_compressed
DATABASE:
name: ${TEST_NAILGUN_DB}
engine: "postgresql"
host: "localhost"
port: "5432"
user: "nailgun"
passwd: "nailgun"
API_LOG: ${ARTIFACTS}/api.log
APP_LOG: ${ARTIFACTS}/app.log
EOL
# Create appropriate Fuel Client config
cat > $FUELCLIENT_CUSTOM_SETTINGS <<EOL
# Connection settings
SERVER_ADDRESS: "127.0.0.1"
LISTEN_PORT: "${NAILGUN_PORT}"
KEYSTONE_USER: "admin"
KEYSTONE_PASS: "admin"
EOL
}
# Clones Nailgun from git, pulls specified patch and
# switches to the specified commit
obtain_nailgun() {
err "Obtaining Nailgun with the revision $FUEL_COMMIT"
if [[ "$FUEL_WEB_CLONE" == "yes" ]]; then
git clone $FUEL_WEB_REPO $FUEL_WEB_ROOT || \
{ err "Failed to clone Nailgun"; exit 1; }
fi
if [[ ! -d "$NAILGUN_ROOT" ]]; then
err "Nailgun directory $NAILGUN_ROOT not found."
exit 1
fi
pushd "$NAILGUN_ROOT" > /dev/null
echo $FETCH_REPO
if [[ -n $FETCH_REPO ]]; then
err "Fetching changes from $FETCH_REPO $FETCH_REFSPEC"
git fetch "$FETCH_REPO" "$FETCH_REFSPEC" || \
{ err "Failed to pull changes"; exit 1; }
fi
git checkout "$FUEL_COMMIT" || \
{ err "Failed to checkout to $FUEL_COMMIT"; exit 1; }
popd > /dev/null
}
prepare_env
obtain_nailgun
create_settings_yaml
setup_db
run_server

38
tox.ini
View File

@ -1,16 +1,46 @@
[tox]
minversion = 1.6
minversion = 2.1
skipsdist = True
envlist = py26,py27,pep8
envlist = py26,py27,pep8,functional,cleanup
[testenv]
usedevelop = True
install_command = pip install --allow-external -U {opts} {packages}
whitelist_externals = bash
setenv = VIRTUAL_ENV={envdir}
ARTIFACTS={toxinidir}/{env:ARTIFACTS:test_run}
FUELCLIENT_JUNIT={env:FUELCLIENT_JUNIT:fuelclient.xml}
FUELCLIENT_CUSTOM_SETTINGS={toxinidir}/{env:ARTIFACTS:test_run}/fuel_client_config.yaml
# Functional env settings
FUEL_WEB_CLONE={env:FUEL_WEB_CLONE:yes}
FUEL_WEB_REPO={env:FUEL_WEB_REPO:https://github.com/stackforge/fuel-web.git}
FUEL_WEB_ROOT={env:FUEL_WEB_ROOT:/tmp/fuel_web}
FETCH_REPO={env:FETCH_REPO:}
FETCH_REFSPEC={env:FETCH_REFSPEC:}
FUEL_COMMIT={env:FUEL_COMMIT:master}
NAILGUN_ROOT={env:FUEL_WEB_ROOT:/tmp/fuel_web}/nailgun
# Nailgun server parameters
NAILGUN_PORT={env:NAILGUN_PORT:8003}
NAILGUN_CHECK_PATH={env:NAILGUN_CHECK_PATH:/api/version}
NAILGUN_START_MAX_WAIT_TIME={env:NAILGUN_START_MAX_WAIT_TIME:20}
TEST_NAILGUN_DB={env:TEST_NAILGUN_DB:nailgun}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
py.test {posargs:fuelclient}
py.test -vv --junit-xml $FUELCLIENT_JUNIT {posargs:fuelclient/tests/unit}
[testenv:functional]
commands =
bash {toxinidir}/tools/cleanup.sh
bash {toxinidir}/tools/prepare_nailgun.sh
py.test -vv --junit-xml $FUELCLIENT_JUNIT {posargs:fuelclient/tests/functional}
[testenv:cleanup]
commands =
bash {toxinidir}/tools/cleanup.sh
bash -c "find {toxinidir} -name \"*.pyc\" -delete"
[tox:jenkins]
downloadcache = ~/cache/pip
@ -23,7 +53,7 @@ commands =
[testenv:cover]
commands =
py.test --cov={posargs:fuelclient} tests/
py.test -vv --junit-xml $FUELCLIENT_JUNIT --cov=fuelclient {posargs:fuelclient/tests/unit}
[testenv:venv]
commands = {posargs:}