From d91aff5de8e8a7a43a640e2a11953e4bd7aa7551 Mon Sep 17 00:00:00 2001 From: Memo Garcia Date: Mon, 8 Aug 2016 17:16:54 +0100 Subject: [PATCH] Updated tox.ini to support new test environment Closes-bug: 1611040 Change-Id: I83f9d18db23fb4d8282cdce623dafba84cd89a73 --- disaster_recovery/api/api.py | 8 +- disaster_recovery/sessions/tables.py | 2 +- .../sessions/workflows/create.py | 3 +- .../tests}/__init__.py | 0 disaster_recovery/tests/settings.py | 86 +++ .../tests}/test_api.py | 0 manage.py | 23 + run_tests.sh | 583 ++++++++++++++++++ test-requirements.txt | 42 +- tests/api_tests.py | 94 --- tests/rest_api_tests.py | 44 -- tests/settings.py | 20 - tools/install_venv.py | 58 ++ tools/install_venv_common.py | 165 +++++ tools/with_venv.sh | 7 + tox.ini | 45 +- 16 files changed, 982 insertions(+), 198 deletions(-) rename {tests => disaster_recovery/tests}/__init__.py (100%) create mode 100644 disaster_recovery/tests/settings.py rename {tests => disaster_recovery/tests}/test_api.py (100%) create mode 100755 manage.py create mode 100755 run_tests.sh delete mode 100644 tests/api_tests.py delete mode 100644 tests/rest_api_tests.py delete mode 100644 tests/settings.py create mode 100644 tools/install_venv.py create mode 100644 tools/install_venv_common.py create mode 100755 tools/with_venv.sh diff --git a/disaster_recovery/api/api.py b/disaster_recovery/api/api.py index 85d9be6..6204a2b 100644 --- a/disaster_recovery/api/api.py +++ b/disaster_recovery/api/api.py @@ -465,13 +465,13 @@ class Backup(object): self.request = request self.client = client(request) - def list(self, json=False, limit=500, offset=0, search=None): + def list(self, json=False, limit=500, offset=0, search={}): if search: search = {"match": [{"_all": search}, ], } - backups = self.client.backups.list(limit=limit, - offset=offset, - search=search) + backups = self.client.backups.list_all(limit=limit, + offset=offset, + search=search) if json: return backups diff --git a/disaster_recovery/sessions/tables.py b/disaster_recovery/sessions/tables.py index 8ac09d4..06a551e 100644 --- a/disaster_recovery/sessions/tables.py +++ b/disaster_recovery/sessions/tables.py @@ -17,9 +17,9 @@ import logging from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy +from django.core.urlresolvers import reverse from horizon import tables -from django.core.urlresolvers import reverse import disaster_recovery.api.api as freezer_api from disaster_recovery.utils import shield diff --git a/disaster_recovery/sessions/workflows/create.py b/disaster_recovery/sessions/workflows/create.py index 321821f..e02a3c9 100644 --- a/disaster_recovery/sessions/workflows/create.py +++ b/disaster_recovery/sessions/workflows/create.py @@ -15,8 +15,9 @@ import datetime import logging -from django.utils.translation import ugettext_lazy as _ from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ + from horizon import exceptions from horizon import forms diff --git a/tests/__init__.py b/disaster_recovery/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to disaster_recovery/tests/__init__.py diff --git a/disaster_recovery/tests/settings.py b/disaster_recovery/tests/settings.py new file mode 100644 index 0000000..f24d56a --- /dev/null +++ b/disaster_recovery/tests/settings.py @@ -0,0 +1,86 @@ +# (c) Copyright 2014-2016 Hewlett-Packard Development Company, L.P. +# +# 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. + +import socket + +from horizon.test.settings import * # noqa + +SECRET_KEY = 'HELLA_SECRET!' + +DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'test'}} + + +socket.setdefaulttimeout(1) + +DEBUG = False +TEMPLATE_DEBUG = DEBUG + +TESTSERVER = 'http://testserver' + + +MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' + +TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' +NOSE_ARGS = ['--nocapture', + '--nologcapture', + '--cover-package=windc'] + +EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' +SESSION_ENGINE = 'django.contrib.sessions.backends.cache' + +OPENSTACK_ADDRESS = "localhost" +OPENSTACK_ADMIN_TOKEN = "openstack" +OPENSTACK_KEYSTONE_URL = "http://%s:5000/v2.0" % OPENSTACK_ADDRESS +OPENSTACK_KEYSTONE_ADMIN_URL = "http://%s:35357/v2.0" % OPENSTACK_ADDRESS +OPENSTACK_KEYSTONE_DEFAULT_ROLE = "Member" +FREEZER_API_URL = "test" + +# Silence logging output during tests. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'null': { + 'level': 'DEBUG', + 'class': 'logging.NullHandler' + }, + }, + 'loggers': { + 'django.db.backends': { + 'handlers': ['null'], + 'propagate': False, + }, + 'horizon': { + 'handlers': ['null'], + 'propagate': False, + }, + 'novaclient': { + 'handlers': ['null'], + 'propagate': False, + }, + 'keystoneclient': { + 'handlers': ['null'], + 'propagate': False, + }, + 'quantum': { + 'handlers': ['null'], + 'propagate': False, + }, + 'nose.plugins.manager': { + 'handlers': ['null'], + 'propagate': False, + } + } +} diff --git a/tests/test_api.py b/disaster_recovery/tests/test_api.py similarity index 100% rename from tests/test_api.py rename to disaster_recovery/tests/test_api.py diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..6fd19f1 --- /dev/null +++ b/manage.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# Copyright 2014 Hewlett-Packard Development Company, L.P. +# +# 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. +import os +import sys + + +if __name__ == "__main__": + os.environ.setdefault( + "DJANGO_SETTINGS_MODULE", "disaster_recovery.tests.settings") + from django.core.management import execute_from_command_line # noqa + execute_from_command_line(sys.argv) diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..aed8402 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,583 @@ +#!/bin/bash + +set -o errexit + +function usage { + echo "Usage: $0 [OPTION]..." + echo "Run Horizon's test suite(s)" + echo "" + echo " -V, --virtual-env Always use virtualenv. Install automatically" + echo " if not present" + echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local" + echo " environment" + echo " -c, --coverage Generate reports using Coverage" + echo " -f, --force Force a clean re-build of the virtual" + echo " environment. Useful when dependencies have" + echo " been added." + echo " -m, --manage Run a Django management command." + echo " --makemessages Create/Update English translation files." + echo " --compilemessages Compile all translation files." + echo " --check-only Do not update translation files (--makemessages only)." + echo " --pseudo Pseudo translate a language." + echo " -p, --pep8 Just run pep8" + echo " -8, --pep8-changed []" + echo " Just run PEP8 and HACKING compliance check" + echo " on files changed since HEAD~1 (or )" + echo " -P, --no-pep8 Don't run pep8 by default" + echo " -t, --tabs Check for tab characters in files." + echo " -y, --pylint Just run pylint" + echo " -e, --eslint Just run eslint" + echo " -k, --karma Just run karma" + echo " -q, --quiet Run non-interactively. (Relatively) quiet." + echo " Implies -V if -N is not set." + echo " --only-selenium Run only the Selenium unit tests" + echo " --with-selenium Run unit tests including Selenium tests" + echo " --selenium-headless Run Selenium tests headless" + echo " --integration Run the integration tests (requires a running " + echo " OpenStack environment)" + echo " --runserver Run the Django development server for" + echo " openstack_dashboard in the virtual" + echo " environment." + echo " --docs Just build the documentation" + echo " --backup-environment Make a backup of the environment on exit" + echo " --restore-environment Restore the environment before running" + echo " --destroy-environment Destroy the environment and exit" + echo " -h, --help Print this usage message" + echo "" + echo "Note: with no options specified, the script will try to run the tests in" + echo " a virtual environment, If no virtualenv is found, the script will ask" + echo " if you would like to create one. If you prefer to run tests NOT in a" + echo " virtual environment, simply pass the -N option." + exit +} + +# DEFAULTS FOR RUN_TESTS.SH +# +root=`pwd -P` +venv=$root/.venv +venv_env_version=$venv/environments +with_venv=tools/with_venv.sh +included_dirs="disaster_recovery" + +always_venv=0 +backup_env=0 +command_wrapper="" +destroy=0 +force=0 +just_pep8=0 +just_pep8_changed=0 +no_pep8=0 +just_pylint=0 +just_docs=0 +just_tabs=0 +just_eslint=0 +just_karma=0 +never_venv=0 +quiet=0 +restore_env=0 +runserver=0 +only_selenium=0 +with_selenium=0 +selenium_headless=0 +integration=0 +testopts="" +testargs="" +with_coverage=0 +makemessages=0 +compilemessages=0 +check_only=0 +pseudo=0 +manage=0 + +# Jenkins sets a "JOB_NAME" variable, if it's not set, we'll make it "default" +[ "$JOB_NAME" ] || JOB_NAME="default" + +function process_option { + # If running manage command, treat the rest of options as arguments. + if [ $manage -eq 1 ]; then + testargs="$testargs $1" + return 0 + fi + + case "$1" in + -h|--help) usage;; + -V|--virtual-env) always_venv=1; never_venv=0;; + -N|--no-virtual-env) always_venv=0; never_venv=1;; + -p|--pep8) just_pep8=1;; + -8|--pep8-changed) just_pep8_changed=1;; + -P|--no-pep8) no_pep8=1;; + -y|--pylint) just_pylint=1;; + -e|--eslint) just_eslint=1;; + -k|--karma) just_karma=1;; + -f|--force) force=1;; + -t|--tabs) just_tabs=1;; + -q|--quiet) quiet=1;; + -c|--coverage) with_coverage=1;; + -m|--manage) manage=1;; + --makemessages) makemessages=1;; + --compilemessages) compilemessages=1;; + --check-only) check_only=1;; + --pseudo) pseudo=1;; + --only-selenium) only_selenium=1;; + --with-selenium) with_selenium=1;; + --selenium-headless) selenium_headless=1;; + --integration) integration=1;; + --docs) just_docs=1;; + --runserver) runserver=1;; + --backup-environment) backup_env=1;; + --restore-environment) restore_env=1;; + --destroy-environment) destroy=1;; + -*) testopts="$testopts $1";; + *) testargs="$testargs $1" + esac +} + +function run_management_command { + ${command_wrapper} python $root/manage.py $testopts $testargs +} + +function run_server { + echo "Starting Django development server..." + ${command_wrapper} python $root/manage.py runserver $testopts $testargs + echo "Server stopped." +} + +function run_pylint { + echo "Running pylint ..." + PYTHONPATH=$root ${command_wrapper} pylint --rcfile=.pylintrc -f parseable $included_dirs > pylint.txt || true + CODE=$? + grep Global -A2 pylint.txt + if [ $CODE -lt 32 ]; then + echo "Completed successfully." + exit 0 + else + echo "Completed with problems." + exit $CODE + fi +} + +function run_eslint { + echo "Running eslint ..." + if [ "`which npm`" == '' ] ; then + echo "npm is not present; please install, e.g. sudo apt-get install npm" + else + npm install + npm run lint + fi +} + +function run_karma { + echo "Running karma ..." + npm install + npm run test +} + +function warn_on_flake8_without_venv { + set +o errexit + ${command_wrapper} python -c "import hacking" 2>/dev/null + no_hacking=$? + set -o errexit + if [ $never_venv -eq 1 -a $no_hacking -eq 1 ]; then + echo "**WARNING**:" >&2 + echo "OpenStack hacking is not installed on your host. Its detection will be missed." >&2 + echo "Please install or use virtual env if you need OpenStack hacking detection." >&2 + fi +} + +function run_pep8 { + echo "Running flake8 ..." + warn_on_flake8_without_venv + DJANGO_SETTINGS_MODULE=disaster_recovery.test.settings ${command_wrapper} flake8 +} + +function run_pep8_changed { + # NOTE(gilliard) We want use flake8 to check the entirety of every file that has + # a change in it. Unfortunately the --filenames argument to flake8 only accepts + # file *names* and there are no files named (eg) "nova/compute/manager.py". The + # --diff argument behaves surprisingly as well, because although you feed it a + # diff, it actually checks the file on disk anyway. + local base_commit=${testargs:-HEAD~1} + files=$(git diff --name-only $base_commit | tr '\n' ' ') + echo "Running flake8 on ${files}" + warn_on_flake8_without_venv + diff -u --from-file /dev/null ${files} | DJANGO_SETTINGS_MODULE=disaster_recovery.test.settings ${command_wrapper} flake8 --diff + exit +} + +function run_sphinx { + echo "Building sphinx..." + DJANGO_SETTINGS_MODULE=disaster_recovery.test.settings ${command_wrapper} python setup.py build_sphinx + echo "Build complete." +} + +function tab_check { + TAB_VIOLATIONS=`find $included_dirs -type f -regex ".*\.\(css\|js\|py\|html\)" -print0 | xargs -0 awk '/\t/' | wc -l` + if [ $TAB_VIOLATIONS -gt 0 ]; then + echo "TABS! $TAB_VIOLATIONS of them! Oh no!" + HORIZON_FILES=`find $included_dirs -type f -regex ".*\.\(css\|js\|py|\html\)"` + for TABBED_FILE in $HORIZON_FILES + do + TAB_COUNT=`awk '/\t/' $TABBED_FILE | wc -l` + if [ $TAB_COUNT -gt 0 ]; then + echo "$TABBED_FILE: $TAB_COUNT" + fi + done + fi + return $TAB_VIOLATIONS; +} + +function destroy_venv { + echo "Cleaning environment..." + echo "Removing virtualenv..." + rm -rf $venv + echo "Virtualenv removed." +} + +function environment_check { + echo "Checking environment." + if [ -f $venv_env_version ]; then + set +o errexit + cat requirements.txt test-requirements.txt | cmp $venv_env_version - > /dev/null + local env_check_result=$? + set -o errexit + if [ $env_check_result -eq 0 ]; then + # If the environment exists and is up-to-date then set our variables + command_wrapper="${root}/${with_venv}" + echo "Environment is up to date." + return 0 + fi + fi + + if [ $always_venv -eq 1 ]; then + install_venv + else + if [ ! -e ${venv} ]; then + echo -e "Environment not found. Install? (Y/n) \c" + else + echo -e "Your environment appears to be out of date. Update? (Y/n) \c" + fi + read update_env + if [ "x$update_env" = "xY" -o "x$update_env" = "x" -o "x$update_env" = "xy" ]; then + install_venv + else + # Set our command wrapper anyway. + command_wrapper="${root}/${with_venv}" + fi + fi +} + +function sanity_check { + # Anything that should be determined prior to running the tests, server, etc. + # Don't sanity-check anything environment-related in -N flag is set + if [ $never_venv -eq 0 ]; then + if [ ! -e ${venv} ]; then + echo "Virtualenv not found at $venv. Did install_venv.py succeed?" + exit 1 + fi + fi + # Remove .pyc files. This is sanity checking because they can linger + # after old files are deleted. + find . -name "*.pyc" -exec rm -rf {} \; +} + +function backup_environment { + if [ $backup_env -eq 1 ]; then + echo "Backing up environment \"$JOB_NAME\"..." + if [ ! -e ${venv} ]; then + echo "Environment not installed. Cannot back up." + return 0 + fi + if [ -d /tmp/.horizon_environment/$JOB_NAME ]; then + mv /tmp/.horizon_environment/$JOB_NAME /tmp/.horizon_environment/$JOB_NAME.old + rm -rf /tmp/.horizon_environment/$JOB_NAME + fi + mkdir -p /tmp/.horizon_environment/$JOB_NAME + cp -r $venv /tmp/.horizon_environment/$JOB_NAME/ + # Remove the backup now that we've completed successfully + rm -rf /tmp/.horizon_environment/$JOB_NAME.old + echo "Backup completed" + fi +} + +function restore_environment { + if [ $restore_env -eq 1 ]; then + echo "Restoring environment from backup..." + if [ ! -d /tmp/.horizon_environment/$JOB_NAME ]; then + echo "No backup to restore from." + return 0 + fi + + cp -r /tmp/.horizon_environment/$JOB_NAME/.venv ./ || true + echo "Environment restored successfully." + fi +} + +function install_venv { + # Install with install_venv.py + export PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE-/tmp/.pip_download_cache} + export PIP_USE_MIRRORS=true + if [ $quiet -eq 1 ]; then + export PIP_NO_INPUT=true + fi + echo "Fetching new src packages..." + rm -rf $venv/src + python tools/install_venv.py + command_wrapper="$root/${with_venv}" + # Make sure it worked and record the environment version + sanity_check + chmod -R 754 $venv + cat requirements.txt test-requirements.txt > $venv_env_version +} + +function run_tests { + sanity_check + + if [ $with_selenium -eq 1 ]; then + export WITH_SELENIUM=1 + elif [ $only_selenium -eq 1 ]; then + export WITH_SELENIUM=1 + export SKIP_UNITTESTS=1 + fi + + if [ $with_selenium -eq 0 -a $integration -eq 0 ]; then + testopts="$testopts --exclude-dir=disaster_recovery/test/integration_tests" + fi + + if [ $selenium_headless -eq 1 ]; then + export SELENIUM_HEADLESS=1 + fi + + if [ -z "$testargs" ]; then + run_tests_all + else + run_tests_subset + fi +} + +function run_tests_subset { + project=`echo $testargs | awk -F. '{print $1}'` + ${command_wrapper} python $root/manage.py test --settings=$project.test.settings $testopts $testargs +} + +function run_tests_all { + echo "Running Freezer Web UI tests" + export NOSE_XUNIT_FILE=disaster_recovery/nosetests.xml + if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then + export NOSE_HTML_OUT_FILE='dashboard_nose_results.html' + fi + ${command_wrapper} ${coverage_run} $root/manage.py test disaster_recovery --settings=disaster_recovery.test.settings $testopts + # get results of the openstack_dashboard tests + DASHBOARD_RESULT=$? + + if [ $with_coverage -eq 1 ]; then + echo "Generating coverage reports" + ${command_wrapper} python -m coverage.__main__ combine + ${command_wrapper} python -m coverage.__main__ xml -i --include="horizon/*,openstack_dashboard/*" --omit='/usr*,setup.py,*egg*,.venv/*' + ${command_wrapper} python -m coverage.__main__ html -i --include="horizon/*,openstack_dashboard/*" --omit='/usr*,setup.py,*egg*,.venv/*' -d reports + fi + # Remove the leftover coverage files from the -p flag earlier. + rm -f .coverage.* + + PEP8_RESULT=0 + if [ $no_pep8 -eq 0 ] && [ $only_selenium -eq 0 ]; then + run_pep8 + PEP8_RESULT=$? + fi + + TEST_RESULT=$(($DASHBOARD_RESULT || $PEP8_RESULT)) + if [ $TEST_RESULT -eq 0 ]; then + echo "Tests completed successfully." + else + echo "Tests failed." + fi + exit $TEST_RESULT +} + +function run_integration_tests { + export INTEGRATION_TESTS=1 + + if [ $selenium_headless -eq 1 ]; then + export SELENIUM_HEADLESS=1 + fi + + echo "Running Horizon integration tests..." + if [ -z "$testargs" ]; then + ${command_wrapper} nosetests openstack_dashboard/test/integration_tests/tests + else + ${command_wrapper} nosetests $testargs + fi + exit 0 +} + +function babel_extract { + DOMAIN=$1 + KEYWORDS="-k gettext_noop -k gettext_lazy -k ngettext_lazy:1,2" + KEYWORDS+=" -k ugettext_noop -k ugettext_lazy -k ungettext_lazy:1,2" + KEYWORDS+=" -k npgettext:1c,2,3 -k pgettext_lazy:1c,2 -k npgettext_lazy:1c,2,3" + + ${command_wrapper} pybabel extract -F ../babel-${DOMAIN}.cfg -o locale/${DOMAIN}.pot $KEYWORDS . +} + +function run_makemessages { + + echo -n "freezer web ui: " + cd disaster_recovery + babel_extract django + FREEZER_PY_RESULT=$? + + echo -n "freezer web ui javascript: " + babel_extract djangojs + FREEZER_JS_RESULT=$? + + cd .. + if [ $check_only -eq 1 ]; then + rm disaster_recovery/locale/django*.pot + fi + + exit $(($FREEZER_PY_RESULT || $FREEZER_JS_RESULT)) +} + +function run_compilemessages { + cd horizon + ${command_wrapper} $root/manage.py compilemessages + HORIZON_PY_RESULT=$? + cd ../openstack_dashboard + ${command_wrapper} $root/manage.py compilemessages + DASHBOARD_RESULT=$? + exit $(($HORIZON_PY_RESULT || $DASHBOARD_RESULT)) +} + +function run_pseudo { + for lang in $testargs + # Use English pot file as the source file/pot file just like real Horizon translations + do + ${command_wrapper} $root/tools/pseudo.py openstack_dashboard/locale/django.pot openstack_dashboard/locale/$lang/LC_MESSAGES/django.po $lang + ${command_wrapper} $root/tools/pseudo.py openstack_dashboard/locale/djangojs.pot openstack_dashboard/locale/$lang/LC_MESSAGES/djangojs.po $lang + ${command_wrapper} $root/tools/pseudo.py horizon/locale/django.pot horizon/locale/$lang/LC_MESSAGES/django.po $lang + ${command_wrapper} $root/tools/pseudo.py horizon/locale/djangojs.pot horizon/locale/$lang/LC_MESSAGES/djangojs.po $lang + done + exit $? +} + + +# ---------PREPARE THE ENVIRONMENT------------ # + +# PROCESS ARGUMENTS, OVERRIDE DEFAULTS +for arg in "$@"; do + process_option $arg +done + +if [ $quiet -eq 1 ] && [ $never_venv -eq 0 ] && [ $always_venv -eq 0 ] +then + always_venv=1 +fi + +# If destroy is set, just blow it away and exit. +if [ $destroy -eq 1 ]; then + destroy_venv + exit 0 +fi + +# Ignore all of this if the -N flag was set +if [ $never_venv -eq 0 ]; then + + # Restore previous environment if desired + if [ $restore_env -eq 1 ]; then + restore_environment + fi + + # Remove the virtual environment if --force used + if [ $force -eq 1 ]; then + destroy_venv + fi + + # Then check if it's up-to-date + environment_check + + # Create a backup of the up-to-date environment if desired + if [ $backup_env -eq 1 ]; then + backup_environment + fi +fi + +# ---------EXERCISE THE CODE------------ # + +# Run management commands +if [ $manage -eq 1 ]; then + run_management_command + exit $? +fi + +# Build the docs +if [ $just_docs -eq 1 ]; then + run_sphinx + exit $? +fi + +# Update translation files +if [ $makemessages -eq 1 ]; then + run_makemessages + exit $? +fi + +# Compile translation files +if [ $compilemessages -eq 1 ]; then + run_compilemessages + exit $? +fi + +# Generate Pseudo translation +if [ $pseudo -eq 1 ]; then + run_pseudo + exit $? +fi + +# PEP8 +if [ $just_pep8 -eq 1 ]; then + run_pep8 + exit $? +fi + +if [ $just_pep8_changed -eq 1 ]; then + run_pep8_changed + exit $? +fi + +# Pylint +if [ $just_pylint -eq 1 ]; then + run_pylint + exit $? +fi + +# ESLint +if [ $just_eslint -eq 1 ]; then + run_eslint + exit $? +fi + +# Karma +if [ $just_karma -eq 1 ]; then + run_karma + exit $? +fi + +# Tab checker +if [ $just_tabs -eq 1 ]; then + tab_check + exit $? +fi + +# Integration tests +if [ $integration -eq 1 ]; then + run_integration_tests + exit $? +fi + +# Django development server +if [ $runserver -eq 1 ]; then + run_server + exit $? +fi + +# Full test suite +run_tests || exit \ No newline at end of file diff --git a/test-requirements.txt b/test-requirements.txt index c2281ea..e3be0de 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,22 +1,26 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -astroid<1.4.0 # breaks pylint 1.4.4 -hacking>=0.10.2,<0.11 -coverage>=3.6 -discover -python-subunit>=0.0.18 -sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 -oslosphinx>=2.5.0 # Apache-2.0 -oslotest>=1.10.0 # Apache-2.0 -testrepository>=0.0.18 -testscenarios>=0.4 -testtools>=1.4.0 -pbr>=1.6 -flake8>=2.2.4,<=2.4.1 -pytest -pytest-cov -pytest-xdist -pylint==1.4.4 # GNU GPL v2 -testresources>=0.2.4 -mock>=1.2 +hacking>=0.11.0,<0.12 # Apache-2.0 + +coverage>=3.6 # Apache-2.0 +mock>=2.0 # BSD +mox>=0.5.3 # Apache-2.0 +mox3>=0.7.0 # Apache-2.0 +oslo.config>=3.14.0 # Apache-2.0 +pylint==1.4.5 # GPLv2 +testrepository>=0.0.18 # Apache-2.0/BSD +testtools>=1.4.0 # MIT +unittest2 # BSD +sphinx>=1.2.1,!=1.3b1,<1.3 # BSD +oslosphinx>=2.5.0,!=3.4.0 # Apache-2.0 +nose # LGPL +nosehtmloutput>=0.0.3 # Apache-2.0 +openstack.nose_plugin>=0.7 # Apache-2.0 +django-nose>=1.4.4 # BSD +nosexcover # BSD + +# Horizon requirements +Django<1.9,>=1.8 # BSD +django-compressor>=2.0 # MIT +django_openstack_auth>=2.4.0 # Apache-2.0 \ No newline at end of file diff --git a/tests/api_tests.py b/tests/api_tests.py deleted file mode 100644 index f0736a9..0000000 --- a/tests/api_tests.py +++ /dev/null @@ -1,94 +0,0 @@ -# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. -# -# 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. - -from django.conf import settings -from horizon_web_ui.freezer_ui.api import api -from mock import patch -from openstack_auth import utils -import openstack_dashboard.test.helpers as helpers - - -@patch('freezer.apiclient.client') -class TestApi(helpers.TestCase): - CONFIG = {u'user_name': u'admin', - u'config_id': u'053a62e0-66a9-4a1c-ba58-6b7348d22166', - u'config_file': {u'start_datetime': u'1432736797', - u'repeat': u'1440', - u'max_priority': False, - u'encryption_password': u'secret', - u'src_file': u'fdsfsdds', - u'clients': [u'test-client'], - u'levels': 0, - u'proxy': u'', - u'container_name': u'dummy_container', - u'exclude': u'/tmp', - u'compression': u'gzip', - u'log_file': u'', - u'optimize': u'speed', - u'name': u'fdsfs'}, - u'user_id': u'13c2b15308c04cdf86989ee7335eb504'} - - def setUp(self): - super(TestApi, self).setUp() - - # Usually this monkey patching happens in urls.py. This doesn't work - # here because we never invoke urls.py in this test. So we have to do - # it manually. - utils.patch_middleware_get_user() - - def _setup_request(self): - super(TestApi, self)._setup_request() - # For some strange reason, Horizon sets the token to the token id - # rather than the token object. This fixes it. - self.request.session['token'] = self.token - - def assert_client_got_created(self, client_mock): - client_mock.Client.assert_called_with( - token=self.request.session['token'].id, - auth_url=settings.OPENSTACK_KEYSTONE_URL, - endpoint=settings.FREEZER_API_URL) - - def test_configuration_delete(self, client_mock): - api.configuration_delete( - self.request, u'053a62e0-66a9-4a1c-ba58-6b7348d22166') - - self.assert_client_got_created(client_mock) - client_mock.Client().configs.delete.\ - assert_called_once_with(u'053a62e0-66a9-4a1c-ba58-6b7348d22166') - - def test_configuration_clone(self, client_mock): - client_mock.Client().configs.get.return_value = [self.CONFIG] - client_mock.Client().configs.\ - create.return_value = u'28124cf0-6cd3-4b38-a0e9-b6f41568fa37' - - result = api.configuration_clone( - self.request, u'053a62e0-66a9-4a1c-ba58-6b7348d22166') - - self.assertEqual(result, u'28124cf0-6cd3-4b38-a0e9-b6f41568fa37') - self.assert_client_got_created(client_mock) - data = self.CONFIG[u'config_file'] - data['name'] = 'fdsfs_clone' - client_mock.Client().configs.create.assert_called_once_with(data) - - def test_configuration_get(self, client_mock): - client_mock.Client().configs.get.return_value = [self.CONFIG] - - result = api.configuration_get( - self.request, u'053a62e0-66a9-4a1c-ba58-6b7348d22166') - - self.assertEqual(1, len(result)) - # Test if properties are accessible via object properties - self.assertEqual(u'admin', result[0].user_name) - # Test if nested properties are accessible via object properties - self.assertEqual(u'1432736797', result[0].start_datetime) diff --git a/tests/rest_api_tests.py b/tests/rest_api_tests.py deleted file mode 100644 index 7a66b2d..0000000 --- a/tests/rest_api_tests.py +++ /dev/null @@ -1,44 +0,0 @@ -# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. -# -# 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. - -import json -from mock import patch - -from django.core.urlresolvers import reverse -import openstack_dashboard.test.helpers as helpers - - -@patch('freezer.apiclient.client') -class TestRestApi(helpers.TestCase): - CLIENT_1 = {u'client': {u'hostname': u'jonas', - u'description': u'client description', - u'client_id': u'test-client', - u'config_ids': [u'fdaf2fwf2', u'fdsfdsfdsfs']}, - u'user_id': u'13c2b15308c04cdf86989ee7335eb504'} - - JSON_PREFIX = ')]}\',\n' - - def test_clients_get(self, client_mock): - client_mock.Client().registration.list.return_value = [self.CLIENT_1] - url = reverse("horizon:freezer_ui:api_clients") - - res = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') - - self.assertEqual(200, res.status_code) - self.assertEqual('application/json', res['content-type']) - self.assertEqual(self.JSON_PREFIX + json.dumps([self.CLIENT_1]), - res.content) - # there is no get ALL api at the moment, so we just fetch a big number - client_mock.Client().registration.list.assert_called_once_with( - limit=9999) diff --git a/tests/settings.py b/tests/settings.py deleted file mode 100644 index 80c2ef2..0000000 --- a/tests/settings.py +++ /dev/null @@ -1,20 +0,0 @@ -# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. -# -# 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. - -from horizon.test.settings import * # noqa - -INSTALLED_APPS = () -OPENSTACK_KEYSTONE_URL = "http://localhost:5000/v2.0" -OPENSTACK_KEYSTONE_DEFAULT_ROLE = "_member_" -FREEZER_API_URL = "test" diff --git a/tools/install_venv.py b/tools/install_venv.py new file mode 100644 index 0000000..0e955c5 --- /dev/null +++ b/tools/install_venv.py @@ -0,0 +1,58 @@ +# 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. + +import os +import sys + +import install_venv_common as install_venv # noqa + + +def print_help(venv, root): + help = """ + OpenStack development environment setup is complete. + OpenStack development uses virtualenv to track and manage Python + dependencies while in development and testing. + To activate the OpenStack virtualenv for the extent of your current shell + session you can run: + $ source %s/bin/activate + Or, if you prefer, you can run commands in the virtualenv on a case by case + basis by running: + $ %s/tools/with_venv.sh + Also, make test will automatically use the virtualenv. + """ + print(help % (venv, root)) + + +def main(argv): + root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + + if os.environ.get('tools_path'): + root = os.environ['tools_path'] + venv = os.path.join(root, '.venv') + if os.environ.get('venv'): + venv = os.environ['venv'] + + pip_requires = os.path.join(root, 'requirements.txt') + test_requires = os.path.join(root, 'test-requirements.txt') + py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) + project = 'OpenStack' + install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, + py_version, project) + options = install.parse_args(argv) + install.check_python_version() + install.check_dependencies() + install.create_virtualenv(no_site_packages=options.no_site_packages) + install.install_dependencies() + print_help(venv, root) + +if __name__ == '__main__': + main(sys.argv) diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py new file mode 100644 index 0000000..431d206 --- /dev/null +++ b/tools/install_venv_common.py @@ -0,0 +1,165 @@ +# 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. + + +"""Provides methods needed by installation script for OpenStack development +virtual environments. +Since this script is used to bootstrap a virtualenv from the system's Python +environment, it should be kept strictly compatible with Python 2.6. +Synced in from openstack-common +""" + +from __future__ import print_function + +import optparse +import os +import subprocess +import sys + + +class InstallVenv(object): + + def __init__(self, root, venv, requirements, + test_requirements, py_version, + project): + self.root = root + self.venv = venv + self.requirements = requirements + self.test_requirements = test_requirements + self.py_version = py_version + self.project = project + + def die(self, message, *args): + print(message % args, file=sys.stderr) + sys.exit(1) + + def check_python_version(self): + if sys.version_info < (2, 6): + self.die("Need Python Version >= 2.6") + + def run_command_with_code(self, cmd, redirect_output=True, + check_exit_code=True): + """Runs a command in an out-of-process shell. + Returns the output of that command. Working directory is self.root. + """ + if redirect_output: + stdout = subprocess.PIPE + else: + stdout = None + + proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout) + output = proc.communicate()[0] + if check_exit_code and proc.returncode != 0: + self.die('Command "%s" failed.\n%s', ' '.join(cmd), output) + return (output, proc.returncode) + + def run_command(self, cmd, redirect_output=True, check_exit_code=True): + return self.run_command_with_code(cmd, redirect_output, + check_exit_code)[0] + + def get_distro(self): + if (os.path.exists('/etc/fedora-release') or + os.path.exists('/etc/redhat-release')): + return Fedora( + self.root, self.venv, self.requirements, + self.test_requirements, self.py_version, self.project) + else: + return Distro( + self.root, self.venv, self.requirements, + self.test_requirements, self.py_version, self.project) + + def check_dependencies(self): + self.get_distro().install_virtualenv() + + def create_virtualenv(self, no_site_packages=True): + """Creates the virtual environment and installs PIP. + Creates the virtual environment and installs PIP only into the + virtual environment. + """ + if not os.path.isdir(self.venv): + print('Creating venv...', end=' ') + if no_site_packages: + self.run_command(['virtualenv', '-q', '--no-site-packages', + self.venv]) + else: + self.run_command(['virtualenv', '-q', self.venv]) + print('done.') + else: + print("venv already exists...") + pass + + def pip_install(self, *args): + self.run_command(['tools/with_venv.sh', + 'pip', 'install', '--upgrade'] + list(args), + redirect_output=False) + + def install_dependencies(self): + print('Installing dependencies with pip (this can take a while)...') + + # First things first, make sure our venv has the latest pip and + # setuptools and pbr + self.pip_install('pip>=1.4') + self.pip_install('setuptools') + self.pip_install('pbr') + + self.pip_install('-r', self.requirements, '-r', self.test_requirements) + + def parse_args(self, argv): + """Parses command-line arguments.""" + parser = optparse.OptionParser() + parser.add_option('-n', '--no-site-packages', + action='store_true', + help="Do not inherit packages from global Python " + "install.") + return parser.parse_args(argv[1:])[0] + + +class Distro(InstallVenv): + + def check_cmd(self, cmd): + return bool(self.run_command(['which', cmd], + check_exit_code=False).strip()) + + def install_virtualenv(self): + if self.check_cmd('virtualenv'): + return + + if self.check_cmd('easy_install'): + print('Installing virtualenv via easy_install...', end=' ') + if self.run_command(['easy_install', 'virtualenv']): + print('Succeeded') + return + else: + print('Failed') + + self.die('ERROR: virtualenv not found.\n\n%s development' + ' requires virtualenv, please install it using your' + ' favorite package management tool' % self.project) + + +class Fedora(Distro): + """This covers all Fedora-based distributions. + Includes: Fedora, RHEL, CentOS, Scientific Linux + """ + + def check_pkg(self, pkg): + return self.run_command_with_code(['rpm', '-q', pkg], + check_exit_code=False)[1] == 0 + + def install_virtualenv(self): + if self.check_cmd('virtualenv'): + return + + if not self.check_pkg('python-virtualenv'): + self.die("Please install 'python-virtualenv'.") + + super(Fedora, self).install_virtualenv() diff --git a/tools/with_venv.sh b/tools/with_venv.sh new file mode 100755 index 0000000..b15965f --- /dev/null +++ b/tools/with_venv.sh @@ -0,0 +1,7 @@ +#!/bin/bash +TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)} +VENV_PATH=${VENV_PATH:-${TOOLS_PATH}} +VENV_DIR=${VENV_NAME:-/../.venv} +TOOLS=${TOOLS_PATH} +VENV=${VENV:-${VENV_PATH}/${VENV_DIR}} +source ${VENV}/bin/activate && "$@" \ No newline at end of file diff --git a/tox.ini b/tox.ini index 5130477..84288ac 100644 --- a/tox.ini +++ b/tox.ini @@ -1,35 +1,50 @@ [tox] -envlist = py27,pep8,pylint +envlist = py27,py27dj18,pep8,py34,pylint +minversion = 1.6 skipsdist = True [testenv] usedevelop = True +setenv = VIRTUAL_ENV={envdir} + NOSE_WITH_OPENSTACK=1 + NOSE_OPENSTACK_COLOR=1 + NOSE_OPENSTACK_RED=0.05 + NOSE_OPENSTACK_YELLOW=0.025 + NOSE_OPENSTACK_SHOW_ELAPSED=1 install_command = pip install -U {opts} {packages} -setenv = - VIRTUAL_ENV={envdir} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -commands = py.test -v --cov-report term-missing --cov disaster_recovery + http://tarballs.openstack.org/horizon/horizon-master.tar.gz +commands = python manage.py test {posargs} [testenv:pep8] -commands = flake8 +commands = flake8 {posargs} [testenv:venv] commands = {posargs} [testenv:cover] -commands = python setup.py testr --coverage --testr-args='{posargs}' +commands = python setup.py test --coverage --testr-args='{posargs}' -[flake8] -show-source = True -ignore = E123,E125,H405,H238,H306,H701 -builtins = _ -exclude=.venv,.tox,dist,doc,test,*egg,tests,runtests.py +[testenv:py27dj18] +basepython = python2.7 +commands = + python manage.py test {posargs} + + +[testenv:py34dj18] +basepython = python3.4 +commands = + python manage.py test {posargs} + +[testenv:docs] +setenv = DJANGO_SETTINGS_MODULE=disaster_recovery.test.settings +commands = python setup.py build_sphinx [testenv:pylint] commands = pylint --rcfile .pylintrc disaster_recovery -[testenv:docs] -commands = - python setup.py build_sphinx - +[flake8] +exclude = .venv,.git,.tox,dist,*openstack/common*,*lib/python*,*egg,build,panel_template,dash_template,local_settings.py,*/local/*,*/test/test_plugins/*,.ropeproject,tools,doc +max-complexity = 20 +ignore = H405,H404,H403,H401,H238,H306,H701 \ No newline at end of file