Merge "First steps towards consolidating testing infrastructure"
This commit is contained in:
commit
0b28e574af
@ -1,9 +1,9 @@
|
|||||||
Nova Style Commandments
|
Nova Style Commandments
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
Step 1: Read http://www.python.org/dev/peps/pep-0008/
|
- Step 1: Read http://www.python.org/dev/peps/pep-0008/
|
||||||
Step 2: Read http://www.python.org/dev/peps/pep-0008/ again
|
- Step 2: Read http://www.python.org/dev/peps/pep-0008/ again
|
||||||
Step 3: Read on
|
- Step 3: Read on
|
||||||
|
|
||||||
|
|
||||||
General
|
General
|
||||||
@ -23,7 +23,8 @@ Imports
|
|||||||
- Order your imports by the full module path
|
- Order your imports by the full module path
|
||||||
- Organize your imports according to the following template
|
- Organize your imports according to the following template
|
||||||
|
|
||||||
::
|
Example::
|
||||||
|
|
||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
{{stdlib imports in human alphabetical order}}
|
{{stdlib imports in human alphabetical order}}
|
||||||
\n
|
\n
|
||||||
@ -37,7 +38,8 @@ Imports
|
|||||||
|
|
||||||
Human Alphabetical Order Examples
|
Human Alphabetical Order Examples
|
||||||
---------------------------------
|
---------------------------------
|
||||||
::
|
Example::
|
||||||
|
|
||||||
import httplib
|
import httplib
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
@ -58,6 +60,8 @@ Human Alphabetical Order Examples
|
|||||||
|
|
||||||
Docstrings
|
Docstrings
|
||||||
----------
|
----------
|
||||||
|
Example::
|
||||||
|
|
||||||
"""A one line docstring looks like this and ends in a period."""
|
"""A one line docstring looks like this and ends in a period."""
|
||||||
|
|
||||||
|
|
||||||
@ -87,12 +91,12 @@ Docstrings
|
|||||||
|
|
||||||
Dictionaries/Lists
|
Dictionaries/Lists
|
||||||
------------------
|
------------------
|
||||||
If a dictionary (dict) or list object is longer than 80 characters, its
|
If a dictionary (dict) or list object is longer than 80 characters, its items
|
||||||
items should be split with newlines. Embedded iterables should have their
|
should be split with newlines. Embedded iterables should have their items
|
||||||
items indented. Additionally, the last item in the dictionary should have
|
indented. Additionally, the last item in the dictionary should have a trailing
|
||||||
a trailing comma. This increases readability and simplifies future diffs.
|
comma. This increases readability and simplifies future diffs.
|
||||||
|
|
||||||
Example:
|
Example::
|
||||||
|
|
||||||
my_dictionary = {
|
my_dictionary = {
|
||||||
"image": {
|
"image": {
|
||||||
@ -113,8 +117,8 @@ Dictionaries/Lists
|
|||||||
|
|
||||||
Calling Methods
|
Calling Methods
|
||||||
---------------
|
---------------
|
||||||
Calls to methods 80 characters or longer should format each argument with
|
Calls to methods 80 characters or longer should format each argument with
|
||||||
newlines. This is not a requirement, but a guideline.
|
newlines. This is not a requirement, but a guideline::
|
||||||
|
|
||||||
unnecessarily_long_function_name('string one',
|
unnecessarily_long_function_name('string one',
|
||||||
'string two',
|
'string two',
|
||||||
@ -122,7 +126,7 @@ Calling Methods
|
|||||||
kwarg2=['a', 'b', 'c'])
|
kwarg2=['a', 'b', 'c'])
|
||||||
|
|
||||||
|
|
||||||
Rather than constructing parameters inline, it is better to break things up:
|
Rather than constructing parameters inline, it is better to break things up::
|
||||||
|
|
||||||
list_of_strings = [
|
list_of_strings = [
|
||||||
'what_a_long_string',
|
'what_a_long_string',
|
||||||
@ -143,23 +147,38 @@ Calling Methods
|
|||||||
|
|
||||||
Internationalization (i18n) Strings
|
Internationalization (i18n) Strings
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
In order to support multiple languages, we have a mechanism to support
|
In order to support multiple languages, we have a mechanism to support
|
||||||
automatic translations of exception and log strings.
|
automatic translations of exception and log strings.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
Example:
|
|
||||||
msg = _("An error occurred")
|
msg = _("An error occurred")
|
||||||
raise HTTPBadRequest(explanation=msg)
|
raise HTTPBadRequest(explanation=msg)
|
||||||
|
|
||||||
If you have a variable to place within the string, first internationalize
|
If you have a variable to place within the string, first internationalize the
|
||||||
the template string then do the replacement.
|
template string then do the replacement.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
Example:
|
|
||||||
msg = _("Missing parameter: %s") % ("flavor",)
|
msg = _("Missing parameter: %s") % ("flavor",)
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
|
|
||||||
If you have multiple variables to place in the string, use keyword
|
If you have multiple variables to place in the string, use keyword parameters.
|
||||||
parameters. This helps our translators reorder parameters when needed.
|
This helps our translators reorder parameters when needed.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
Example:
|
|
||||||
msg = _("The server with id %(s_id)s has no key %(m_key)s")
|
msg = _("The server with id %(s_id)s has no key %(m_key)s")
|
||||||
LOG.error(msg % {"s_id": "1234", "m_key": "imageId"})
|
LOG.error(msg % {"s_id": "1234", "m_key": "imageId"})
|
||||||
|
|
||||||
|
|
||||||
|
Creating Unit Tests
|
||||||
|
-------------------
|
||||||
|
For every new feature, unit tests should be created that both test and
|
||||||
|
(implicitly) document the usage of said feature. If submitting a patch for a
|
||||||
|
bug that had no unit test, a new passing unit test should be added. If a
|
||||||
|
submitted bug fix does have a unit test, be sure to add a new one that fails
|
||||||
|
without the patch and passes with the patch.
|
||||||
|
|
||||||
|
For more information on creating unit tests and utilizing the testing
|
||||||
|
infrastructure in OpenStack Nova, please read nova/testing/README.rst.
|
@ -29,7 +29,7 @@ Generating source/api/nova..db.sqlalchemy.api.rst
|
|||||||
Generating source/api/nova..db.sqlalchemy.models.rst
|
Generating source/api/nova..db.sqlalchemy.models.rst
|
||||||
Generating source/api/nova..db.sqlalchemy.session.rst
|
Generating source/api/nova..db.sqlalchemy.session.rst
|
||||||
Generating source/api/nova..exception.rst
|
Generating source/api/nova..exception.rst
|
||||||
Generating source/api/nova..fakerabbit.rst
|
Generating source/api/nova..fake.rabbit.rst
|
||||||
Generating source/api/nova..flags.rst
|
Generating source/api/nova..flags.rst
|
||||||
Generating source/api/nova..image.service.rst
|
Generating source/api/nova..image.service.rst
|
||||||
Generating source/api/nova..manager.rst
|
Generating source/api/nova..manager.rst
|
||||||
|
@ -44,10 +44,10 @@ The :mod:`nova.auth.fakeldap` Module
|
|||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
|
||||||
The :mod:`nova.fakerabbit` Module
|
The :mod:`nova.testing.fake.rabbit` Module
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
.. automodule:: nova.fakerabbit
|
.. automodule:: nova.testing.fake.rabbit
|
||||||
:noindex:
|
:noindex:
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
:undoc-members:
|
||||||
|
@ -120,7 +120,7 @@ class Lockout(wsgi.Middleware):
|
|||||||
if FLAGS.memcached_servers:
|
if FLAGS.memcached_servers:
|
||||||
import memcache
|
import memcache
|
||||||
else:
|
else:
|
||||||
from nova import fakememcache as memcache
|
from nova.testing.fake import memcache
|
||||||
self.mc = memcache.Client(FLAGS.memcached_servers,
|
self.mc = memcache.Client(FLAGS.memcached_servers,
|
||||||
debug=0)
|
debug=0)
|
||||||
super(Lockout, self).__init__(application)
|
super(Lockout, self).__init__(application)
|
||||||
|
@ -72,7 +72,7 @@ LOG = logging.getLogger("nova.ldapdriver")
|
|||||||
if FLAGS.memcached_servers:
|
if FLAGS.memcached_servers:
|
||||||
import memcache
|
import memcache
|
||||||
else:
|
else:
|
||||||
from nova import fakememcache as memcache
|
from nova.testing.fake import memcache
|
||||||
|
|
||||||
|
|
||||||
# TODO(vish): make an abstract base class with the same public methods
|
# TODO(vish): make an abstract base class with the same public methods
|
||||||
|
@ -82,7 +82,7 @@ LOG = logging.getLogger('nova.auth.manager')
|
|||||||
if FLAGS.memcached_servers:
|
if FLAGS.memcached_servers:
|
||||||
import memcache
|
import memcache
|
||||||
else:
|
else:
|
||||||
from nova import fakememcache as memcache
|
from nova.testing.fake import memcache
|
||||||
|
|
||||||
|
|
||||||
class AuthBase(object):
|
class AuthBase(object):
|
||||||
|
@ -41,9 +41,9 @@ import greenlet
|
|||||||
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import fakerabbit
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova.rpc.common import RemoteError, LOG
|
from nova.rpc.common import RemoteError, LOG
|
||||||
|
from nova.testing import fake
|
||||||
|
|
||||||
# Needed for tests
|
# Needed for tests
|
||||||
eventlet.monkey_patch()
|
eventlet.monkey_patch()
|
||||||
@ -71,7 +71,7 @@ class Connection(carrot_connection.BrokerConnection):
|
|||||||
virtual_host=FLAGS.rabbit_virtual_host)
|
virtual_host=FLAGS.rabbit_virtual_host)
|
||||||
|
|
||||||
if FLAGS.fake_rabbit:
|
if FLAGS.fake_rabbit:
|
||||||
params['backend_cls'] = fakerabbit.Backend
|
params['backend_cls'] = fake.rabbit.Backend
|
||||||
|
|
||||||
# NOTE(vish): magic is fun!
|
# NOTE(vish): magic is fun!
|
||||||
# pylint: disable=W0142
|
# pylint: disable=W0142
|
||||||
|
@ -35,12 +35,12 @@ import nova.image.fake
|
|||||||
import shutil
|
import shutil
|
||||||
import stubout
|
import stubout
|
||||||
|
|
||||||
from nova import fakerabbit
|
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import log
|
from nova import log
|
||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova import service
|
from nova import service
|
||||||
|
from nova.testing.fake import rabbit
|
||||||
from nova.virt import fake
|
from nova.virt import fake
|
||||||
|
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ class TestCase(unittest.TestCase):
|
|||||||
finally:
|
finally:
|
||||||
# Clean out fake_rabbit's queue if we used it
|
# Clean out fake_rabbit's queue if we used it
|
||||||
if FLAGS.fake_rabbit:
|
if FLAGS.fake_rabbit:
|
||||||
fakerabbit.reset_all()
|
rabbit.reset_all()
|
||||||
|
|
||||||
if FLAGS.connection_type == 'fake':
|
if FLAGS.connection_type == 'fake':
|
||||||
if hasattr(fake.FakeConnection, '_instance'):
|
if hasattr(fake.FakeConnection, '_instance'):
|
||||||
|
44
nova/testing/README.rst
Normal file
44
nova/testing/README.rst
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
=====================================
|
||||||
|
OpenStack Nova Testing Infrastructure
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
A note of clarification is in order, to help those who are new to testing in
|
||||||
|
OpenStack nova:
|
||||||
|
|
||||||
|
- actual unit tests are created in the "tests" directory;
|
||||||
|
- the "testing" directory is used to house the infrastructure needed to support
|
||||||
|
testing in OpenStack Nova.
|
||||||
|
|
||||||
|
This README file attempts to provide current and prospective contributors with
|
||||||
|
everything they need to know in order to start creating unit tests and
|
||||||
|
utilizing the convenience code provided in nova.testing.
|
||||||
|
|
||||||
|
Note: the content for the rest of this file will be added as the work items in
|
||||||
|
the following blueprint are completed:
|
||||||
|
https://blueprints.launchpad.net/nova/+spec/consolidate-testing-infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
Test Types: Unit vs. Functional vs. Integration
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
TBD
|
||||||
|
|
||||||
|
Writing Unit Tests
|
||||||
|
------------------
|
||||||
|
|
||||||
|
TBD
|
||||||
|
|
||||||
|
Using Fakes
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
TBD
|
||||||
|
|
||||||
|
Writing Functional Tests
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
TBD
|
||||||
|
|
||||||
|
Writing Integration Tests
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
TBD
|
0
nova/testing/__init__.py
Normal file
0
nova/testing/__init__.py
Normal file
2
nova/testing/fake/__init__.py
Normal file
2
nova/testing/fake/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import memcache
|
||||||
|
import rabbit
|
@ -26,7 +26,7 @@ from eventlet import greenthread
|
|||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger("nova.fakerabbit")
|
LOG = logging.getLogger("nova.testing.fake.rabbit")
|
||||||
|
|
||||||
|
|
||||||
EXCHANGES = {}
|
EXCHANGES = {}
|
@ -41,17 +41,18 @@
|
|||||||
"""Unittest runner for Nova.
|
"""Unittest runner for Nova.
|
||||||
|
|
||||||
To run all tests
|
To run all tests
|
||||||
python run_tests.py
|
python nova/testing/runner.py
|
||||||
|
|
||||||
To run a single test:
|
|
||||||
python run_tests.py test_compute:ComputeTestCase.test_run_terminate
|
|
||||||
|
|
||||||
To run a single test module:
|
To run a single test module:
|
||||||
python run_tests.py test_compute
|
python nova/testing/runner.py test_compute
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
python run_tests.py api.test_wsgi
|
python nova/testing/runner.py api.test_wsgi
|
||||||
|
|
||||||
|
To run a single test:
|
||||||
|
python nova/testing/runner.py \
|
||||||
|
test_compute:ComputeTestCase.test_run_terminate
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -336,8 +337,7 @@ class NovaTestRunner(core.TextTestRunner):
|
|||||||
return result_
|
return result_
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def run():
|
||||||
eventlet.monkey_patch()
|
|
||||||
logging.setup()
|
logging.setup()
|
||||||
# If any argument looks like a test name but doesn't have "nova.tests" in
|
# 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
|
# front of it, automatically add that so we don't have to type as much
|
||||||
@ -363,3 +363,8 @@ if __name__ == '__main__':
|
|||||||
config=c,
|
config=c,
|
||||||
show_elapsed=show_elapsed)
|
show_elapsed=show_elapsed)
|
||||||
sys.exit(not core.run(config=c, testRunner=runner, argv=argv))
|
sys.exit(not core.run(config=c, testRunner=runner, argv=argv))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
eventlet.monkey_patch()
|
||||||
|
run()
|
@ -84,12 +84,12 @@ class IptablesManagerTestCase(test.TestCase):
|
|||||||
table = self.manager.ipv4['filter']
|
table = self.manager.ipv4['filter']
|
||||||
table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
||||||
new_lines = self.manager._modify_rules(current_lines, table)
|
new_lines = self.manager._modify_rules(current_lines, table)
|
||||||
self.assertTrue('-A run_tests.py-FORWARD '
|
self.assertTrue('-A runner.py-FORWARD '
|
||||||
'-s 1.2.3.4/5 -j DROP' in new_lines)
|
'-s 1.2.3.4/5 -j DROP' in new_lines)
|
||||||
|
|
||||||
table.remove_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
table.remove_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
||||||
new_lines = self.manager._modify_rules(current_lines, table)
|
new_lines = self.manager._modify_rules(current_lines, table)
|
||||||
self.assertTrue('-A run_tests.py-FORWARD '
|
self.assertTrue('-A runner.py-FORWARD '
|
||||||
'-s 1.2.3.4/5 -j DROP' not in new_lines)
|
'-s 1.2.3.4/5 -j DROP' not in new_lines)
|
||||||
|
|
||||||
def test_nat_rules(self):
|
def test_nat_rules(self):
|
||||||
@ -123,7 +123,7 @@ class IptablesManagerTestCase(test.TestCase):
|
|||||||
"nova-postouting-bottom: %s" % last_postrouting_line)
|
"nova-postouting-bottom: %s" % last_postrouting_line)
|
||||||
|
|
||||||
for chain in ['POSTROUTING', 'PREROUTING', 'OUTPUT']:
|
for chain in ['POSTROUTING', 'PREROUTING', 'OUTPUT']:
|
||||||
self.assertTrue('-A %s -j run_tests.py-%s' \
|
self.assertTrue('-A %s -j runner.py-%s' \
|
||||||
% (chain, chain) in new_lines,
|
% (chain, chain) in new_lines,
|
||||||
"Built-in chain %s not wrapped" % (chain,))
|
"Built-in chain %s not wrapped" % (chain,))
|
||||||
|
|
||||||
@ -155,10 +155,10 @@ class IptablesManagerTestCase(test.TestCase):
|
|||||||
break
|
break
|
||||||
|
|
||||||
self.assertTrue('-A nova-filter-top '
|
self.assertTrue('-A nova-filter-top '
|
||||||
'-j run_tests.py-local' in new_lines,
|
'-j runner.py-local' in new_lines,
|
||||||
"nova-filter-top does not jump to wrapped local chain")
|
"nova-filter-top does not jump to wrapped local chain")
|
||||||
|
|
||||||
for chain in ['INPUT', 'OUTPUT', 'FORWARD']:
|
for chain in ['INPUT', 'OUTPUT', 'FORWARD']:
|
||||||
self.assertTrue('-A %s -j run_tests.py-%s' \
|
self.assertTrue('-A %s -j runner.py-%s' \
|
||||||
% (chain, chain) in new_lines,
|
% (chain, chain) in new_lines,
|
||||||
"Built-in chain %s not wrapped" % (chain,))
|
"Built-in chain %s not wrapped" % (chain,))
|
||||||
|
@ -117,7 +117,7 @@ function run_pep8 {
|
|||||||
${srcfiles}
|
${srcfiles}
|
||||||
}
|
}
|
||||||
|
|
||||||
NOSETESTS="python run_tests.py $noseopts $noseargs"
|
NOSETESTS="python nova/testing/runner.py $noseopts $noseargs"
|
||||||
|
|
||||||
if [ $never_venv -eq 0 ]
|
if [ $never_venv -eq 0 ]
|
||||||
then
|
then
|
||||||
|
Loading…
x
Reference in New Issue
Block a user