diff options
-rw-r--r-- | .coveragerc | 6 | ||||
-rw-r--r-- | .gitignore | 7 | ||||
-rw-r--r-- | contrib/boto_v6/ec2/connection.py | 2 | ||||
-rw-r--r-- | nova/log.py | 4 | ||||
-rwxr-xr-x | nova/network/linux_net.py | 6 | ||||
-rw-r--r-- | nova/testing/runner.py | 368 | ||||
-rw-r--r-- | nova/tests/__init__.py | 8 | ||||
-rw-r--r-- | nova/tests/test_iptables_network.py | 22 | ||||
-rwxr-xr-x | run_tests.sh | 11 | ||||
-rw-r--r-- | setup.cfg | 9 | ||||
-rw-r--r-- | tools/test-requires | 3 | ||||
-rw-r--r-- | tox.ini | 28 |
12 files changed, 65 insertions, 409 deletions
diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000000..82fe477929 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,6 @@ +[run] +branch = True +omit = /usr*,setup.py,*egg*,.venv/*,.tox/*,nova/tests/* + +[report] +ignore-errors = True diff --git a/.gitignore b/.gitignore index eee73ee900..d61dac1f1e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ *.DS_Store local_settings.py ChangeLog +MANIFEST CA/ keeper instances @@ -15,9 +16,13 @@ nova.egg-info *.sqlite *.log *.mo +nosetests.xml +coverage.xml tools/conf/nova.conf* cover/* +nova/tests/cover/* +nova/vcsversion.py +.autogenerated dist/* .coverage covhtml -nova/tests/coverage.xml diff --git a/contrib/boto_v6/ec2/connection.py b/contrib/boto_v6/ec2/connection.py index 868c93c11c..a0cfd7e260 100644 --- a/contrib/boto_v6/ec2/connection.py +++ b/contrib/boto_v6/ec2/connection.py @@ -6,7 +6,7 @@ Created on 2010/12/20 import boto import base64 import boto.ec2 -from boto_v6.ec2.instance import ReservationV6 +from boto_v6.ec2.instance import ReservationV6 from boto.ec2.securitygroup import SecurityGroup diff --git a/nova/log.py b/nova/log.py index 7970802f3a..46a07b26cc 100644 --- a/nova/log.py +++ b/nova/log.py @@ -361,7 +361,9 @@ def _setup_logging_from_flags(): nova_root.addHandler(streamlog) elif not FLAGS.log_file: - streamlog = logging.StreamHandler(stream=sys.stdout) + # pass sys.stdout as a positional argument + # python2.6 calls the argument strm, in 2.7 it's stream + streamlog = logging.StreamHandler(sys.stdout) nova_root.addHandler(streamlog) if FLAGS.publish_errors: diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index b2b68b5b7c..811395f9cf 100755 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -90,7 +90,11 @@ FLAGS.register_opts(linux_net_opts) # add up to 12 characters to binary_name which is used as a prefix, # so we limit it to 16 characters. # (max_chain_name_length - len('-POSTROUTING') == 16) -binary_name = os.path.basename(inspect.stack()[-1][1])[:16] +def get_binary_name(): + """Grab the name of the binary we're running in.""" + return os.path.basename(inspect.stack()[-1][1])[:16] + +binary_name = get_binary_name() class IptablesRule(object): diff --git a/nova/testing/runner.py b/nova/testing/runner.py deleted file mode 100644 index 9abef4feef..0000000000 --- a/nova/testing/runner.py +++ /dev/null @@ -1,368 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# 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 nova/testing/runner.py - -To run a single test module: - python nova/testing/runner.py test_compute - - or - - python nova/testing/runner.py api.test_wsgi - -To run a single test: - python nova/testing/runner.py - test_compute:ComputeTestCase.test_run_terminate - -""" - -import gettext -import heapq -import os -import sys -import time -import unittest - -import eventlet -from nose import config -from nose import core -from nose import result - -gettext.install('nova', unicode=1) -reldir = os.path.join(os.path.dirname(__file__), '..', '..') -absdir = os.path.abspath(reldir) -sys.path.insert(0, absdir) - -from nova import log as logging - - -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 Exception: - 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): - import win32console as win - red, green, blue, bold = (win.FOREGROUND_RED, win.FOREGROUND_GREEN, - win.FOREGROUND_BLUE, win.FOREGROUND_INTENSITY) - self.stream = stream - self.screenBuffer = win.GetStdHandle(win.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_ - - -def run(): - # This is a fix to allow the --hide-elapsed flag while accepting - # arbitrary nosetest flags as well - argv = [x for x in sys.argv if x != '--hide-elapsed'] - hide_elapsed = argv != sys.argv - logging.setup() - - # 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 - for i, arg in enumerate(argv): - if arg.startswith('test_'): - argv[i] = 'nova.tests.%s' % arg - - testdir = os.path.abspath(os.path.join("nova", "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=not hide_elapsed) - sys.exit(not core.run(config=c, testRunner=runner, argv=argv)) - - -if __name__ == '__main__': - eventlet.monkey_patch() - run() diff --git a/nova/tests/__init__.py b/nova/tests/__init__.py index d1fa17185f..32dbca7a12 100644 --- a/nova/tests/__init__.py +++ b/nova/tests/__init__.py @@ -39,8 +39,16 @@ import shutil from nova.db.sqlalchemy.session import get_engine from nova import flags +from nova import log as logging + +import eventlet + +eventlet.monkey_patch() FLAGS = flags.FLAGS +FLAGS.use_stderr = False + +logging.setup() _DB = None diff --git a/nova/tests/test_iptables_network.py b/nova/tests/test_iptables_network.py index b23846bb5e..0d7e54723c 100644 --- a/nova/tests/test_iptables_network.py +++ b/nova/tests/test_iptables_network.py @@ -22,6 +22,9 @@ from nova import test class IptablesManagerTestCase(test.TestCase): + + binary_name = linux_net.get_binary_name() + sample_filter = ['#Generated by iptables-save on Fri Feb 18 15:17:05 2011', '*filter', ':INPUT ACCEPT [2223527:305688874]', @@ -82,13 +85,14 @@ class IptablesManagerTestCase(test.TestCase): table = self.manager.ipv4['filter'] table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP') new_lines = self.manager._modify_rules(current_lines, table) - self.assertTrue('-A runner.py-FORWARD ' - '-s 1.2.3.4/5 -j DROP' in new_lines) + self.assertTrue('-A %s-FORWARD ' + '-s 1.2.3.4/5 -j DROP' % self.binary_name in new_lines) table.remove_rule('FORWARD', '-s 1.2.3.4/5 -j DROP') new_lines = self.manager._modify_rules(current_lines, table) - self.assertTrue('-A runner.py-FORWARD ' - '-s 1.2.3.4/5 -j DROP' not in new_lines) + self.assertTrue('-A %s-FORWARD ' + '-s 1.2.3.4/5 -j DROP' % self.binary_name \ + not in new_lines) def test_nat_rules(self): current_lines = self.sample_nat @@ -121,8 +125,8 @@ class IptablesManagerTestCase(test.TestCase): "nova-postouting-bottom: %s" % last_postrouting_line) for chain in ['POSTROUTING', 'PREROUTING', 'OUTPUT']: - self.assertTrue('-A %s -j runner.py-%s' % - (chain, chain) in new_lines, + self.assertTrue('-A %s -j %s-%s' % + (chain, self.binary_name, chain) in new_lines, "Built-in chain %s not wrapped" % (chain,)) def test_filter_rules(self): @@ -153,10 +157,10 @@ class IptablesManagerTestCase(test.TestCase): break self.assertTrue('-A nova-filter-top ' - '-j runner.py-local' in new_lines, + '-j %s-local' % self.binary_name in new_lines, "nova-filter-top does not jump to wrapped local chain") for chain in ['INPUT', 'OUTPUT', 'FORWARD']: - self.assertTrue('-A %s -j runner.py-%s' % - (chain, chain) in new_lines, + self.assertTrue('-A %s -j %s-%s' % + (chain, self.binary_name, chain) in new_lines, "Built-in chain %s not wrapped" % (chain,)) diff --git a/run_tests.sh b/run_tests.sh index aab28ed3b3..c814b1adf8 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -60,6 +60,13 @@ coverage=0 recreate_db=1 patch_migrate=1 +export NOSE_WITH_OPENSTACK=1 +export NOSE_OPENSTACK_COLOR=1 +export NOSE_OPENSTACK_RED=0.05 +export NOSE_OPENSTACK_YELLOW=0.025 +export NOSE_OPENSTACK_SHOW_ELAPSED=1 +export NOSE_OPENSTACK_STDOUT=1 + for arg in "$@"; do process_option $arg done @@ -77,7 +84,7 @@ function run_tests { # Cleanup *pyc ${wrapper} find . -type f -name "*.pyc" -delete # Just run the test suites in current environment - ${wrapper} $NOSETESTS 2> run_tests.log + ${wrapper} $NOSETESTS # If we get some short import error right away, print the error log directly RESULT=$? if [ "$RESULT" -ne "0" ]; @@ -115,7 +122,7 @@ function run_pep8 { } -NOSETESTS="python nova/testing/runner.py $noseopts $noseargs" +NOSETESTS="nosetests $noseopts $noseargs" if [ $never_venv -eq 0 ] then @@ -25,8 +25,7 @@ output_file = nova/locale/nova.pot [nosetests] verbosity=2 detailed-errors=1 -with-openstack=1 -openstack-red=0.05 -openstack-yellow=0.025 -openstack-show-elapsed=1 -openstack-color=1 +cover-package = nova +cover-html = true +cover-erase = true +where=nova/tests diff --git a/tools/test-requires b/tools/test-requires index 51ef2ff632..3adddefe6f 100644 --- a/tools/test-requires +++ b/tools/test-requires @@ -4,8 +4,7 @@ distribute>=0.6.24 coverage mox==0.5.3 nose -nosexcover -openstack.nose_plugin +openstack.nose_plugin>=0.7 pep8==1.1 sphinx>=1.1.2 MySQL-python @@ -3,9 +3,15 @@ envlist = py26,py27,pep8 [testenv] 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 + NOSE_OPENSTACK_STDOUT=1 deps = -r{toxinidir}/tools/pip-requires -r{toxinidir}/tools/test-requires -commands = /bin/bash run_tests.sh -N -P +commands = nosetests {posargs} [tox:jenkins] sitepackages = True @@ -13,26 +19,10 @@ downloadcache = ~/cache/pip [testenv:pep8] deps = pep8==1.1 -commands = /bin/bash run_tests.sh -N --pep8 +commands = pep8 --repeat --show-source --exclude=.venv,.tox,dist,doc . [testenv:cover] -commands = /bin/bash run_tests.sh -N -P --cover-package=nova --cover-erase --with-xcoverage +setenv = NOSE_WITH_COVERAGE=1 [testenv:venv] commands = {posargs} - -[testenv:jenkins26] -basepython = python2.6 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkins27] -basepython = python2.7 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkinscover] -commands = /bin/bash run_tests.sh -N -P --cover-package=nova --cover-erase --with-xcoverage - -[testenv:jenkinsvenv] -deps = file://{toxinidir}/.cache.bundle -commands = {posargs} - |