diff options
-rw-r--r-- | .coveragerc | 6 | ||||
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | keystone/common/sql/util.py | 5 | ||||
-rw-r--r-- | keystone/test.py | 7 | ||||
-rw-r--r-- | run_tests.py | 367 | ||||
-rwxr-xr-x | run_tests.sh | 6 | ||||
-rw-r--r-- | setup.cfg | 1 | ||||
-rw-r--r-- | tests/test_backend_sql.py | 12 | ||||
-rw-r--r-- | tests/test_import_legacy.py | 4 | ||||
-rw-r--r-- | tests/test_keystoneclient_sql.py | 4 | ||||
-rw-r--r-- | tests/test_migrate_nova_auth.py | 4 | ||||
-rw-r--r-- | tox.ini | 4 |
12 files changed, 51 insertions, 371 deletions
diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..41845590 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,6 @@ +[run] +branch = True +omit = /usr*,setup.py,*egg*,.venv/*,.tox/*,tests/* + +[report] +ignore-errors = True @@ -7,6 +7,8 @@ vendor keystone.egg-info/ *.log .coverage +coverage.xml +cover/* covhtml pep8.txt nosetests.xml diff --git a/keystone/common/sql/util.py b/keystone/common/sql/util.py index 195adc83..65e87bec 100644 --- a/keystone/common/sql/util.py +++ b/keystone/common/sql/util.py @@ -35,3 +35,8 @@ def setup_test_database(): shutil.copyfile('test.db.pristine', 'test.db') except Exception: pass + + +def teardown_test_database(): + if os.path.exists('test.db.pristine'): + os.unlink('test.db.pristine') diff --git a/keystone/test.py b/keystone/test.py index 44857d7e..519008dc 100644 --- a/keystone/test.py +++ b/keystone/test.py @@ -19,6 +19,7 @@ import subprocess import sys import time +import eventlet import mox from paste import deploy import stubout @@ -32,8 +33,12 @@ from keystone import config from keystone.openstack.common import importutils +do_monkeypatch = not os.getenv('STANDARD_THREADS') +eventlet.patcher.monkey_patch(all=False, socket=True, time=True, + thread=do_monkeypatch) + LOG = logging.getLogger(__name__) -ROOTDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +ROOTDIR = os.path.dirname(os.path.abspath(os.curdir)) VENDOR = os.path.join(ROOTDIR, 'vendor') TESTSDIR = os.path.join(ROOTDIR, 'tests') ETCDIR = os.path.join(ROOTDIR, 'etc') diff --git a/run_tests.py b/run_tests.py deleted file mode 100644 index 429eb30e..00000000 --- a/run_tests.py +++ /dev/null @@ -1,367 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2012 OpenStack LLC -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# 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. - -# Colorizer Code is borrowed from Twisted: -# Copyright (c) 2001-2010 Twisted Matrix Laboratories. -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -"""Unittest runner for Nova. - -To run all tests - python run_tests.py - -To run a single test: - python run_tests.py test_compute:ComputeTestCase.test_run_terminate - -To run a single test module: - python run_tests.py test_compute - - or - - python run_tests.py api.test_wsgi - -""" -import eventlet -import heapq -import os -import unittest -import sys -import time - -from nose import config -from nose import core -from nose import result - - -class _AnsiColorizer(object): - """ - A colorizer is an object that loosely wraps around a stream, allowing - callers to write text to the stream in a particular color. - - Colorizer classes must implement C{supported()} and C{write(text, color)}. - """ - _colors = dict(black=30, red=31, green=32, yellow=33, - blue=34, magenta=35, cyan=36, white=37) - - def __init__(self, stream): - self.stream = stream - - def supported(cls, stream=sys.stdout): - """ - A class method that returns True if the current platform supports - coloring terminal output using this method. Returns False otherwise. - """ - if not stream.isatty(): - return False # auto color only on TTYs - try: - import curses - except ImportError: - return False - else: - try: - try: - return curses.tigetnum("colors") > 2 - except curses.error: - curses.setupterm() - return curses.tigetnum("colors") > 2 - except: - raise - # guess false in case of error - return False - supported = classmethod(supported) - - def write(self, text, color): - """ - Write the given text to the stream in the given color. - - @param text: Text to be written to the stream. - - @param color: A string label for a color. e.g. 'red', 'white'. - """ - color = self._colors[color] - self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text)) - - -class _Win32Colorizer(object): - """ - See _AnsiColorizer docstring. - """ - def __init__(self, stream): - from win32console import (GetStdHandle, STD_OUT_HANDLE, - FOREGROUND_RED, FOREGROUND_GREEN, - FOREGROUND_BLUE, FOREGROUND_INTENSITY) - red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, - FOREGROUND_BLUE, FOREGROUND_INTENSITY) - self.stream = stream - self.screenBuffer = GetStdHandle(STD_OUT_HANDLE) - self._colors = { - 'normal': red | green | blue, - 'red': red | bold, - 'green': green | bold, - 'blue': blue | bold, - 'yellow': red | green | bold, - 'magenta': red | blue | bold, - 'cyan': green | blue | bold, - 'white': red | green | blue | bold - } - - def supported(cls, stream=sys.stdout): - try: - import win32console - screenBuffer = win32console.GetStdHandle( - win32console.STD_OUT_HANDLE) - except ImportError: - return False - import pywintypes - try: - screenBuffer.SetConsoleTextAttribute( - win32console.FOREGROUND_RED | - win32console.FOREGROUND_GREEN | - win32console.FOREGROUND_BLUE) - except pywintypes.error: - return False - else: - return True - supported = classmethod(supported) - - def write(self, text, color): - color = self._colors[color] - self.screenBuffer.SetConsoleTextAttribute(color) - self.stream.write(text) - self.screenBuffer.SetConsoleTextAttribute(self._colors['normal']) - - -class _NullColorizer(object): - """ - See _AnsiColorizer docstring. - """ - def __init__(self, stream): - self.stream = stream - - def supported(cls, stream=sys.stdout): - return True - supported = classmethod(supported) - - def write(self, text, color): - self.stream.write(text) - - -def get_elapsed_time_color(elapsed_time): - if elapsed_time > 1.0: - return 'red' - elif elapsed_time > 0.25: - return 'yellow' - else: - return 'green' - - -class NovaTestResult(result.TextTestResult): - def __init__(self, *args, **kw): - self.show_elapsed = kw.pop('show_elapsed') - result.TextTestResult.__init__(self, *args, **kw) - self.num_slow_tests = 5 - self.slow_tests = [] # this is a fixed-sized heap - self._last_case = None - self.colorizer = None - # NOTE(vish): reset stdout for the terminal check - stdout = sys.stdout - sys.stdout = sys.__stdout__ - for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]: - if colorizer.supported(): - self.colorizer = colorizer(self.stream) - break - sys.stdout = stdout - - # NOTE(lorinh): Initialize start_time in case a sqlalchemy-migrate - # error results in it failing to be initialized later. Otherwise, - # _handleElapsedTime will fail, causing the wrong error message to - # be outputted. - self.start_time = time.time() - - def getDescription(self, test): - return str(test) - - def _handleElapsedTime(self, test): - self.elapsed_time = time.time() - self.start_time - item = (self.elapsed_time, test) - # Record only the n-slowest tests using heap - if len(self.slow_tests) >= self.num_slow_tests: - heapq.heappushpop(self.slow_tests, item) - else: - heapq.heappush(self.slow_tests, item) - - def _writeElapsedTime(self, test): - color = get_elapsed_time_color(self.elapsed_time) - self.colorizer.write(" %.2f" % self.elapsed_time, color) - - def _writeResult(self, test, long_result, color, short_result, success): - if self.showAll: - self.colorizer.write(long_result, color) - if self.show_elapsed and success: - self._writeElapsedTime(test) - self.stream.writeln() - elif self.dots: - self.stream.write(short_result) - self.stream.flush() - - # NOTE(vish): copied from unittest with edit to add color - def addSuccess(self, test): - unittest.TestResult.addSuccess(self, test) - self._handleElapsedTime(test) - self._writeResult(test, 'OK', 'green', '.', True) - - # NOTE(vish): copied from unittest with edit to add color - def addFailure(self, test, err): - unittest.TestResult.addFailure(self, test, err) - self._handleElapsedTime(test) - self._writeResult(test, 'FAIL', 'red', 'F', False) - - # NOTE(vish): copied from nose with edit to add color - def addError(self, test, err): - """Overrides normal addError to add support for - errorClasses. If the exception is a registered class, the - error will be added to the list for that class, not errors. - """ - self._handleElapsedTime(test) - stream = getattr(self, 'stream', None) - ec, ev, tb = err - try: - exc_info = self._exc_info_to_string(err, test) - except TypeError: - # 2.3 compat - exc_info = self._exc_info_to_string(err) - for cls, (storage, label, isfail) in self.errorClasses.items(): - if result.isclass(ec) and issubclass(ec, cls): - if isfail: - test.passed = False - storage.append((test, exc_info)) - # Might get patched into a streamless result - if stream is not None: - if self.showAll: - message = [label] - detail = result._exception_detail(err[1]) - if detail: - message.append(detail) - stream.writeln(": ".join(message)) - elif self.dots: - stream.write(label[:1]) - return - self.errors.append((test, exc_info)) - test.passed = False - if stream is not None: - self._writeResult(test, 'ERROR', 'red', 'E', False) - - def startTest(self, test): - unittest.TestResult.startTest(self, test) - self.start_time = time.time() - current_case = test.test.__class__.__name__ - - if self.showAll: - if current_case != self._last_case: - self.stream.writeln(current_case) - self._last_case = current_case - - self.stream.write( - ' %s' % str(test.test._testMethodName).ljust(60)) - self.stream.flush() - - -class NovaTestRunner(core.TextTestRunner): - def __init__(self, *args, **kwargs): - self.show_elapsed = kwargs.pop('show_elapsed') - core.TextTestRunner.__init__(self, *args, **kwargs) - - def _makeResult(self): - return NovaTestResult(self.stream, - self.descriptions, - self.verbosity, - self.config, - show_elapsed=self.show_elapsed) - - def _writeSlowTests(self, result_): - # Pare out 'fast' tests - slow_tests = [item for item in result_.slow_tests - if get_elapsed_time_color(item[0]) != 'green'] - if slow_tests: - slow_total_time = sum(item[0] for item in slow_tests) - self.stream.writeln("Slowest %i tests took %.2f secs:" - % (len(slow_tests), slow_total_time)) - for elapsed_time, test in sorted(slow_tests, reverse=True): - time_str = "%.2f" % elapsed_time - self.stream.writeln(" %s %s" % (time_str.ljust(10), test)) - - def run(self, test): - result_ = core.TextTestRunner.run(self, test) - if self.show_elapsed: - self._writeSlowTests(result_) - return result_ - - -if __name__ == '__main__': - # If any argument looks like a test name but doesn't have "nova.tests" in - # front of it, automatically add that so we don't have to type as much - show_elapsed = True - do_monkeypatch = True - argv = [] - for x in sys.argv: - if x.startswith('test_'): - pass - #argv.append('tests.%s' % x) - argv.append(x) - elif x.startswith('--hide-elapsed'): - show_elapsed = False - elif x.startswith('--no-monkeypatch'): - do_monkeypatch = False - else: - argv.append(x) - eventlet.patcher.monkey_patch(all=False, socket=True, time=True, - thread=do_monkeypatch) - testdir = os.path.abspath(os.path.join("tests")) - c = config.Config(stream=sys.stdout, - env=os.environ, - verbosity=3, - workingDir=testdir, - plugins=core.DefaultPluginManager()) - - runner = NovaTestRunner(stream=c.stream, - verbosity=c.verbosity, - config=c, - show_elapsed=show_elapsed) - if os.path.exists('tests/test.db.pristine'): - os.unlink('tests/test.db.pristine') - - sys.exit(not core.run(config=c, testRunner=runner, argv=argv)) diff --git a/run_tests.sh b/run_tests.sh index a225d78d..2e21e24f 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -32,6 +32,7 @@ function usage { echo " -h, --help Print this usage message" echo " -xintegration Ignore all keystoneclient test cases (integration tests)" echo " --hide-elapsed Don't print the elapsed time for each test along with slow test list" + echo " --standard-threads Don't do the eventlet threading monkeypatch." echo "" echo "Note: with no options specified, the script will try to run the tests in a virtual environment," echo " If no virtualenv is found, the script will ask if you would like to create one. If you " @@ -51,6 +52,9 @@ function process_option { -P|--no-pep8) no_pep8=1;; -c|--coverage) coverage=1;; -xintegration) nokeystoneclient=1;; + --standard-threads) + export STANDARD_THREADS=1 + ;; -*) noseopts="$noseopts $1";; *) noseargs="$noseargs $1" esac @@ -114,7 +118,7 @@ function run_pep8 { --exclude=vcsversion.py ${srcfiles} | tee pep8.txt } -NOSETESTS="python run_tests.py $noseopts $noseargs" +NOSETESTS="nosetests $noseopts $noseargs" if [ $never_venv -eq 0 ] then @@ -32,3 +32,4 @@ detailed-errors=1 cover-package = keystone cover-html = true cover-erase = true +where=tests diff --git a/tests/test_backend_sql.py b/tests/test_backend_sql.py index ee181dee..e8c345a7 100644 --- a/tests/test_backend_sql.py +++ b/tests/test_backend_sql.py @@ -42,6 +42,10 @@ class SqlIdentity(test.TestCase, test_backend.IdentityTests): self.identity_api = identity_sql.Identity() self.load_fixtures(default_fixtures) + def tearDown(self): + sql_util.teardown_test_database() + super(SqlIdentity, self).tearDown() + def test_delete_user_with_tenant_association(self): user = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex, @@ -143,6 +147,10 @@ class SqlToken(test.TestCase, test_backend.TokenTests): sql_util.setup_test_database() self.token_api = token_sql.Token() + def tearDown(self): + sql_util.teardown_test_database() + super(SqlToken, self).tearDown() + class SqlCatalog(test.TestCase, test_backend.CatalogTests): def setUp(self): @@ -154,3 +162,7 @@ class SqlCatalog(test.TestCase, test_backend.CatalogTests): self.catalog_api = catalog_sql.Catalog() self.catalog_man = catalog.Manager() self.load_fixtures(default_fixtures) + + def tearDown(self): + sql_util.teardown_test_database() + super(SqlCatalog, self).tearDown() diff --git a/tests/test_import_legacy.py b/tests/test_import_legacy.py index 84c4bb6b..c5cd481f 100644 --- a/tests/test_import_legacy.py +++ b/tests/test_import_legacy.py @@ -39,6 +39,10 @@ class ImportLegacy(test.TestCase): sql_util.setup_test_database() self.identity_api = identity_sql.Identity() + def tearDown(self): + sql_util.teardown_test_database() + super(ImportLegacy, self).tearDown() + def setup_old_database(self, sql_dump): sql_path = test.testsdir(sql_dump) db_path = test.testsdir('%s.db' % sql_dump) diff --git a/tests/test_keystoneclient_sql.py b/tests/test_keystoneclient_sql.py index e4178dc8..541ca670 100644 --- a/tests/test_keystoneclient_sql.py +++ b/tests/test_keystoneclient_sql.py @@ -34,6 +34,10 @@ class KcMasterSqlTestCase(test_keystoneclient.KcMasterTestCase): test.testsdir('backend_sql.conf')]) sql_util.setup_test_database() + def tearDown(self): + sql_util.teardown_test_database() + super(KcMasterSqlTestCase, self).tearDown() + def test_endpoint_crud(self): from keystoneclient import exceptions as client_exceptions diff --git a/tests/test_migrate_nova_auth.py b/tests/test_migrate_nova_auth.py index 34375d12..d699d4b8 100644 --- a/tests/test_migrate_nova_auth.py +++ b/tests/test_migrate_nova_auth.py @@ -75,6 +75,10 @@ class MigrateNovaAuth(test.TestCase): self.identity_api = identity_sql.Identity() self.ec2_api = ec2_sql.Ec2() + def tearDown(self): + sql_util.teardown_test_database() + super(MigrateNovaAuth, self).tearDown() + def _create_role(self, role_name): role_id = uuid.uuid4().hex role_dict = {'id': role_id, 'name': role_name} @@ -11,7 +11,7 @@ setenv = VIRTUAL_ENV={envdir} NOSE_OPENSTACK_STDOUT=1 deps = -r{toxinidir}/tools/pip-requires -r{toxinidir}/tools/test-requires -commands = bash run_tests.sh -N --no-pep8 +commands = nosetests {posargs} [testenv:pep8] deps = pep8==1.3.3 @@ -21,7 +21,7 @@ commands = pep8 --exclude=vcsversion.py,*.pyc,openstack --repeat --show-source b downloadcache = ~/cache/pip [testenv:cover] -commands = /bin/bash run_tests.sh -N -P --with-xcoverage --cover-erase --cover-package=keystone +setenv = NOSE_WITH_COVERAGE=1 [testenv:venv] commands = {posargs} |