From 436d6b2cac47106df6a4998343b745f35e7948df Mon Sep 17 00:00:00 2001 From: "Gary W. Smith" Date: Mon, 20 Apr 2015 08:31:03 -0700 Subject: [PATCH] Enable unit tests to run standalone Enable unit tests to be run standalane; that is, without having a local copy of the horizon source. This is primarly intended for use in automated builds. The approach, taken from tuskar-ui, is to add an entry in test-requirements.txt that refers to a source tarball of horizon which is expanded into the virtual environment, permitting references to horizon and openstack_dashboard to resolve correctly. Add additional tooling to support running tests and building virtual environments to mirror what is done in horizon. --- README.rst | 3 +- manage.py | 11 + .../dashboards/project/shares/shares/tests.py | 5 +- .../enabled/_90_manila_admin_shares.py | 0 .../enabled/_90_manila_project_shares.py | 0 manila_ui/enabled/__init__.py | 0 manila_ui/test/settings.py | 5 +- run_tests.sh | 548 ++++++++++++++++++ test-requirements.txt | 5 +- tools/install_venv.py | 154 +++++ tools/with_venv.sh | 7 + tox.ini | 5 +- 12 files changed, 736 insertions(+), 7 deletions(-) create mode 100755 manage.py rename _90_manila_admin_shares.py => manila_ui/enabled/_90_manila_admin_shares.py (100%) rename _90_manila_project_shares.py => manila_ui/enabled/_90_manila_project_shares.py (100%) create mode 100644 manila_ui/enabled/__init__.py create mode 100755 run_tests.sh create mode 100644 tools/install_venv.py create mode 100755 tools/with_venv.sh diff --git a/README.rst b/README.rst index fccf72e2..97521519 100644 --- a/README.rst +++ b/README.rst @@ -58,8 +58,7 @@ Install Manila UI with all dependencies in your virtual environment:: And enable it in Horizon:: - cp ../manila-ui/_90_manila_admin_shares.py openstack_dashboard/local/enabled - cp ../manila-ui/_90_manila_project_shares.py openstack_dashboard/local/enabled + cp ../manila-ui/manila_ui/enabled/_90_manila_*.py openstack_dashboard/local/enabled Starting the app diff --git a/manage.py b/manage.py new file mode 100755 index 00000000..eff45be0 --- /dev/null +++ b/manage.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +import os +import sys + +from django.core.management import execute_from_command_line + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", + "manila_ui.test.settings") + execute_from_command_line(sys.argv) diff --git a/manila_ui/dashboards/project/shares/shares/tests.py b/manila_ui/dashboards/project/shares/shares/tests.py index 20802309..099ce9ef 100644 --- a/manila_ui/dashboards/project/shares/shares/tests.py +++ b/manila_ui/dashboards/project/shares/shares/tests.py @@ -15,12 +15,13 @@ from django.core.urlresolvers import reverse import mock -from openstack_dashboard import api -from openstack_dashboard.usage import quotas from manila_ui.api import manila as api_manila from manila_ui.dashboards.project.shares import test_data from manila_ui.test import helpers as test +from openstack_dashboard import api +from openstack_dashboard.usage import quotas + SHARE_INDEX_URL = reverse('horizon:project:shares:index') diff --git a/_90_manila_admin_shares.py b/manila_ui/enabled/_90_manila_admin_shares.py similarity index 100% rename from _90_manila_admin_shares.py rename to manila_ui/enabled/_90_manila_admin_shares.py diff --git a/_90_manila_project_shares.py b/manila_ui/enabled/_90_manila_project_shares.py similarity index 100% rename from _90_manila_project_shares.py rename to manila_ui/enabled/_90_manila_project_shares.py diff --git a/manila_ui/enabled/__init__.py b/manila_ui/enabled/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/manila_ui/test/settings.py b/manila_ui/test/settings.py index 49a6876d..6f47bc55 100644 --- a/manila_ui/test/settings.py +++ b/manila_ui/test/settings.py @@ -33,7 +33,7 @@ STATIC_URL = '/static/' SECRET_KEY = secret_key.generate_or_read_from_file( os.path.join(TEST_DIR, '.secret_key_store')) -ROOT_URLCONF = 'openstack_dashboard.test.urls' +ROOT_URLCONF = 'manila_ui.test.urls' TEMPLATE_DIRS = ( os.path.join(TEST_DIR, 'templates'), ) @@ -54,6 +54,7 @@ INSTALLED_APPS = ( 'compressor', 'horizon', 'openstack_dashboard', + 'openstack_dashboard.dashboards', 'manila_ui.dashboards', ) @@ -73,6 +74,7 @@ HORIZON_CONFIG = { 'unauthorized': exceptions.UNAUTHORIZED}, 'angular_modules': [], 'js_files': [], + 'customization_module': 'manila_ui.overrides', } # Load the pluggable dashboard settings @@ -80,6 +82,7 @@ from openstack_dashboard.utils import settings dashboard_module_names = [ 'openstack_dashboard.enabled', 'openstack_dashboard.local.enabled', + 'manila_ui.enabled', ] dashboard_modules = [] # All dashboards must be enabled for the namespace to get registered, which is diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 00000000..74fc5d88 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,548 @@ +#!/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 " -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="manila_ui" + +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 +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;; + -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 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=manila_ui.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=manila_ui.test.settings ${command_wrapper} flake8 --diff + exit +} + +function run_sphinx { + echo "Building sphinx..." + DJANGO_SETTINGS_MODULE=manila_ui.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 [ $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 application tests" + export NOSE_XUNIT_FILE=manila_ui/nosetests.xml + if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then + export NOSE_HTML_OUT_FILE='manila_nose_results.html' + fi + if [ $with_coverage -eq 1 ]; then + ${command_wrapper} python -m coverage.__main__ erase + coverage_run="python -m coverage.__main__ run -p" + fi + ${command_wrapper} ${coverage_run} $root/manage.py test manila_ui --settings=manila_ui.test.settings $testopts + # get results of the Horizon tests + MANILA_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 --omit='/usr*,setup.py,*egg*,.venv/*' + ${command_wrapper} python -m coverage.__main__ html -i --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=$(($MANILA_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 run_makemessages { + OPTS="-l en --no-obsolete --settings=openstack_dashboard.test.settings" + DASHBOARD_OPTS="--extension=html,txt,csv --ignore=openstack" + echo -n "horizon: " + cd horizon + ${command_wrapper} $root/manage.py makemessages $OPTS + HORIZON_PY_RESULT=$? + echo -n "horizon javascript: " + ${command_wrapper} $root/manage.py makemessages -d djangojs $OPTS + HORIZON_JS_RESULT=$? + echo -n "openstack_dashboard: " + cd ../openstack_dashboard + ${command_wrapper} $root/manage.py makemessages $DASHBOARD_OPTS $OPTS + DASHBOARD_RESULT=$? + cd .. + if [ $check_only -eq 1 ]; then + git checkout -- horizon/locale/en/LC_MESSAGES/django*.po + git checkout -- openstack_dashboard/locale/en/LC_MESSAGES/django.po + fi + exit $(($HORIZON_PY_RESULT || $HORIZON_JS_RESULT || $DASHBOARD_RESULT)) +} + +function run_compilemessages { + OPTS="--settings=openstack_dashboard.test.settings" + cd horizon + ${command_wrapper} $root/manage.py compilemessages $OPTS + HORIZON_PY_RESULT=$? + cd ../openstack_dashboard + ${command_wrapper} $root/manage.py compilemessages $OPTS + DASHBOARD_RESULT=$? + cd .. + # English is the source language, so compiled catalogs are unnecessary. + rm -vf horizon/locale/en/LC_MESSAGES/django*.mo + rm -vf openstack_dashboard/locale/en/LC_MESSAGES/django.mo + exit $(($HORIZON_PY_RESULT || $DASHBOARD_RESULT)) +} + +function run_pseudo { + for lang in $testargs + # Use English po file as the source file/pot file just like real Horizon translations + do + ${command_wrapper} $root/tools/pseudo.py openstack_dashboard/locale/en/LC_MESSAGES/django.po openstack_dashboard/locale/$lang/LC_MESSAGES/django.po $lang + ${command_wrapper} $root/tools/pseudo.py horizon/locale/en/LC_MESSAGES/django.po horizon/locale/$lang/LC_MESSAGES/django.po $lang + ${command_wrapper} $root/tools/pseudo.py horizon/locale/en/LC_MESSAGES/djangojs.po 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 + +# 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 diff --git a/test-requirements.txt b/test-requirements.txt index 6baf8e50..d634bef2 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,10 +1,13 @@ # 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. - +http://tarballs.openstack.org/horizon/horizon-master.tar.gz#egg=horizon hacking<0.11,>=0.10.0 coverage>=3.6 +django-nose discover +mock>=1.0 +mox>=0.5.3 python-subunit>=0.0.18 sphinx>=1.1.2,!=1.2.0,!=1.3b1,<1.3 oslosphinx>=2.2.0 # Apache-2.0 diff --git a/tools/install_venv.py b/tools/install_venv.py new file mode 100644 index 00000000..8550e2c6 --- /dev/null +++ b/tools/install_venv.py @@ -0,0 +1,154 @@ +# Copyright 2012 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Copyright 2012 OpenStack, LLC +# +# Copyright 2012 Nebula, 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. + +""" +Installation script for the OpenStack Dashboard development virtualenv. +""" + +import os +import subprocess +import sys + + +ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +VENV = os.path.join(ROOT, '.venv') +WITH_VENV = os.path.join(ROOT, 'tools', 'with_venv.sh') +PIP_REQUIRES = os.path.join(ROOT, 'requirements.txt') +TEST_REQUIRES = os.path.join(ROOT, 'test-requirements.txt') + + +def die(message, *args): + print >> sys.stderr, message % args + sys.exit(1) + + +def run_command(cmd, redirect_output=True, check_exit_code=True, cwd=ROOT, + die_message=None): + """ + Runs a command in an out-of-process shell, returning the + output of that command. Working directory is ROOT. + """ + if redirect_output: + stdout = subprocess.PIPE + else: + stdout = None + + proc = subprocess.Popen(cmd, cwd=cwd, stdout=stdout) + output = proc.communicate()[0] + if check_exit_code and proc.returncode != 0: + if die_message is None: + die('Command "%s" failed.\n%s', ' '.join(cmd), output) + else: + die(die_message) + return output + + +HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'], + check_exit_code=False).strip()) +HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'], + check_exit_code=False).strip()) + + +def check_dependencies(): + """Make sure virtualenv is in the path.""" + + print 'Checking dependencies...' + if not HAS_VIRTUALENV: + print 'Virtual environment not found.' + # Try installing it via easy_install... + if HAS_EASY_INSTALL: + print 'Installing virtualenv via easy_install...', + run_command(['easy_install', 'virtualenv'], + die_message='easy_install failed to install virtualenv' + '\ndevelopment requires virtualenv, please' + ' install it using your favorite tool') + if not run_command(['which', 'virtualenv']): + die('ERROR: virtualenv not found in path.\n\ndevelopment ' + ' requires virtualenv, please install it using your' + ' favorite package management tool and ensure' + ' virtualenv is in your path') + print 'virtualenv installation done.' + else: + die('easy_install not found.\n\nInstall easy_install' + ' (python-setuptools in ubuntu) or virtualenv by hand,' + ' then rerun.') + print 'dependency check done.' + + +def create_virtualenv(venv=VENV): + """Creates the virtual environment and installs PIP only into the + virtual environment + """ + print 'Creating venv...', + run_command(['virtualenv', '-q', '--no-site-packages', VENV]) + print 'done.' + print 'Installing pip in virtualenv...', + if not run_command([WITH_VENV, 'easy_install', 'pip']).strip(): + die("Failed to install pip.") + print 'done.' + print 'Installing distribute in virtualenv...' + pip_install('distribute>=0.6.24') + print 'done.' + + +def pip_install(*args): + args = [WITH_VENV, 'pip', 'install', '--upgrade'] + list(args) + run_command(args, redirect_output=False) + + +def install_dependencies(venv=VENV): + print "Installing dependencies..." + print "(This may take several minutes, don't panic)" + pip_install('-r', TEST_REQUIRES) + pip_install('-r', PIP_REQUIRES) + + # Tell the virtual env how to "import dashboard" + py = 'python%d.%d' % (sys.version_info[0], sys.version_info[1]) + pthfile = os.path.join(venv, "lib", py, "site-packages", "dashboard.pth") + f = open(pthfile, 'w') + f.write("%s\n" % ROOT) + + +def install_horizon(): + print 'Installing horizon module in development mode...' + run_command([WITH_VENV, 'python', 'setup.py', 'develop'], cwd=ROOT) + + +def print_summary(): + summary = """ +Horizon development environment setup is complete. + +To activate the virtualenv for the extent of your current shell session you +can run: + +$ source .venv/bin/activate +""" + print summary + + +def main(): + check_dependencies() + create_virtualenv() + install_dependencies() + install_horizon() + print_summary() + +if __name__ == '__main__': + main() diff --git a/tools/with_venv.sh b/tools/with_venv.sh new file mode 100755 index 00000000..7303990b --- /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 && "$@" diff --git a/tox.ini b/tox.ini index 69cef359..3a3f732c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 1.6 -envlist = py33,py34,py27,pep8 +envlist = py27,pep8 skipsdist = True [testenv] @@ -12,6 +12,9 @@ deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python setup.py testr --slowest --testr-args='{posargs}' +[testenv:py27] +setenv = DJANGO_SETTINGS_MODULE=manila_ui.test.settings + [testenv:pep8] commands = flake8