tests: register all objects before validating their hash versions

The test validates that all object types registered in the object
registry have expected object hashes. If they are not, it means that
object API is changed, and it is used as an indicator to reviewers that
object version conversion rules should be added to allow upgrading and
downgrading objects between older and the new versions.

The test was not importing all modules that contain objects though, so
if no other code executed by the current testr process before the hasher
validation test imported all objects to validate, the test would
misbehave, claiming some expected object types not registered at all.

To make sure all object types available in the tree are imported (and
registered) before the test case is executed, we need to import all
modules under neutron.objects. Instead of maintaining the list of
modules with objects to import somewhere, inspect the list of those
modules dynamically, assuming they are all located under
neutron/objects/ subtree.

Without the fix, the test may randomly fail depending on test case
order for the current process.

Note it's not an issue for QoS objects since they are implicitly
imported by rpc callbacks resource manager that is initialized in the
base test class. This becomes a problem when you start to introduce
objects that are not part of rpc callbacks list of supported resources.

Change-Id: Ice408faf10b75c508b9c5f5b7ab23b2fc3289eaa
This commit is contained in:
Ihar Hrachyshka 2016-01-20 15:42:04 +01:00 committed by Assaf Muller
parent 487ad1dea4
commit 58ba6586b7
7 changed files with 76 additions and 0 deletions

View File

@ -13,10 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
import importlib
import os
import platform
import random
import string
import sys
import time
import warnings
@ -24,6 +26,7 @@ import fixtures
import mock
import six
import neutron
from neutron.api.v2 import attributes
@ -185,6 +188,36 @@ class UnorderedList(list):
return not self == other
def import_modules_recursively(topdir):
'''Import and return all modules below the topdir directory.'''
modules = []
for root, dirs, files in os.walk(topdir):
for file_ in files:
if file_[-3:] != '.py':
continue
module = file_[:-3]
if module == '__init__':
continue
import_base = root.replace('/', '.')
# NOTE(ihrachys): in Python3, or when we are not located in the
# directory containing neutron code, __file__ is absolute, so we
# should truncate it to exclude PYTHONPATH prefix
prefixlen = len(os.path.dirname(neutron.__file__))
import_base = 'neutron' + import_base[prefixlen:]
module = '.'.join([import_base, module])
if module not in sys.modules:
importlib.import_module(module)
modules.append(module)
for dir_ in dirs:
modules.extend(import_modules_recursively(dir_))
return modules
def get_random_string(n=10):
return ''.join(random.choice(string.ascii_lowercase) for _ in range(n))

View File

@ -18,7 +18,9 @@ import pprint
from oslo_versionedobjects import base as obj_base
from oslo_versionedobjects import fixture
from neutron import objects
from neutron.tests import base as test_base
from neutron.tests import tools
# NOTE: The hashes in this list should only be changed if they come with a
@ -32,6 +34,12 @@ object_data = {
class TestObjectVersions(test_base.BaseTestCase):
def setUp(self):
super(TestObjectVersions, self).setUp()
# NOTE(ihrachys): seed registry with all objects under neutron.objects
# before validating the hashes
tools.import_modules_recursively(os.path.dirname(objects.__file__))
def test_versions(self):
checker = fixture.ObjectVersionChecker(
obj_base.VersionedObjectRegistry.obj_classes())

View File

@ -0,0 +1,2 @@
This directory is used by:
neutron.tests.unit.tests.test_tools.ImportModulesRecursivelyTestCase

View File

@ -0,0 +1,33 @@
# 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
from neutron.tests import base
from neutron.tests import tools
from neutron.tests.unit import tests # noqa
EXAMPLE_MODULE = 'neutron.tests.unit.tests.example.dir.example_module'
class ImportModulesRecursivelyTestCase(base.BaseTestCase):
def test_object_modules(self):
sys.modules.pop(EXAMPLE_MODULE, None)
modules = tools.import_modules_recursively(
os.path.dirname(tests.__file__))
self.assertIn(
'neutron.tests.unit.tests.example.dir.example_module',
modules)
self.assertIn(EXAMPLE_MODULE, sys.modules)