Fixes LP Bug#913608 - tests should be isolated

This reworks the stubs.FAKE_FILESYSTEM_ROOTDIR used in unit
tests by making each test case create and destroy its own little
sandbox in /tmp/test.*/images. Adds a base IsolatedUnitTest that
tests needing to have an isolated filesystem_store_datadir and
configuration can inherit from.

Change-Id: I396f5127c6a687da8dcef3368e7ed0912efc9b3a
This commit is contained in:
Jay Pipes 2012-01-10 17:49:42 -05:00
parent 6fd005465e
commit b4624ec242
9 changed files with 166 additions and 155 deletions

View File

@ -34,11 +34,13 @@ import time
import unittest
import urlparse
from glance.common import utils
from glance.tests.utils import execute, get_unused_port
from sqlalchemy import create_engine
from glance.common import utils
from glance.tests import utils as test_utils
execute, get_unused_port = test_utils.execute, test_utils.get_unused_port
def runs_sql(func):
"""
@ -326,10 +328,7 @@ class FunctionalTest(unittest.TestCase):
disabled = False
def setUp(self):
self.test_id = random.randint(0, 100000)
self.test_dir = os.path.join("/", "tmp", "test.%d" % self.test_id)
utils.safe_mkdirs(self.test_dir)
self.test_id, self.test_dir = test_utils.get_isolated_test_env()
self.api_protocol = 'http'
self.api_port = get_unused_port()

View File

@ -31,44 +31,11 @@ from glance.registry.api import v1 as rserver
from glance.tests import utils
FAKE_FILESYSTEM_ROOTDIR = os.path.join('/tmp', 'glance-tests')
VERBOSE = False
DEBUG = False
def clean_out_fake_filesystem_backend():
"""
Removes any leftover directories used in fake filesystem
backend
"""
if os.path.exists(FAKE_FILESYSTEM_ROOTDIR):
shutil.rmtree(FAKE_FILESYSTEM_ROOTDIR, ignore_errors=True)
def stub_out_filesystem_backend():
"""
Stubs out the Filesystem Glance service to return fake
pped image data from files.
We establish a few fake images in a directory under //tmp/glance-tests
and ensure that this directory contains the following files:
//tmp/glance-tests/2 <-- file containing "chunk00000remainder"
The stubbed service yields the data in the above files.
"""
# Establish a clean faked filesystem with dummy images
if os.path.exists(FAKE_FILESYSTEM_ROOTDIR):
shutil.rmtree(FAKE_FILESYSTEM_ROOTDIR, ignore_errors=True)
os.mkdir(FAKE_FILESYSTEM_ROOTDIR)
f = open(os.path.join(FAKE_FILESYSTEM_ROOTDIR, '2'), "wb")
f.write("chunk00000remainder")
f.close()
def stub_out_registry_and_store_server(stubs):
def stub_out_registry_and_store_server(stubs, images_dir):
"""
Mocks calls to 127.0.0.1 on 9191 and 9292 for testing so
that a real Glance server does not need to be up and
@ -158,7 +125,7 @@ def stub_out_registry_and_store_server(stubs):
'registry_host': '0.0.0.0',
'registry_port': '9191',
'default_store': 'file',
'filesystem_store_datadir': FAKE_FILESYSTEM_ROOTDIR
'filesystem_store_datadir': images_dir
})
api = version_negotiation.VersionNegotiationFilter(
context.ContextMiddleware(router.API(conf), conf),

51
glance/tests/unit/base.py Normal file
View File

@ -0,0 +1,51 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 OpenStack LLC.
# 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.
import os
import shutil
import unittest
import stubout
from glance.common import utils
from glance.tests import stubs
from glance.tests import utils as test_utils
class IsolatedUnitTest(unittest.TestCase):
"""
Unit test case that establishes a mock environment within
a testing directory (in isolation)
"""
def setUp(self):
self.test_id, self.test_dir = test_utils.get_isolated_test_env()
self.stubs = stubout.StubOutForTesting()
stubs.stub_out_registry_and_store_server(self.stubs, self.test_dir)
options = {'sql_connection': 'sqlite://',
'verbose': False,
'debug': False,
'default_store': 'filesystem',
'filesystem_store_datadir': os.path.join(self.test_dir)}
self.conf = test_utils.TestConfigOpts(options)
def tearDown(self):
self.stubs.UnsetAll()
if os.path.exists(self.test_dir):
shutil.rmtree(self.test_dir)

View File

@ -18,7 +18,6 @@
import datetime
import hashlib
import httplib
import os
import json
import unittest
@ -33,8 +32,8 @@ from glance.registry import context as rcontext
from glance.registry.api import v1 as rserver
from glance.registry.db import api as db_api
from glance.registry.db import models as db_models
from glance.tests import stubs
from glance.tests import utils as test_utils
from glance.tests.unit import base
_gen_uuid = utils.generate_uuid
@ -43,15 +42,6 @@ UUID1 = _gen_uuid()
UUID2 = _gen_uuid()
CONF = {'sql_connection': 'sqlite://',
'verbose': False,
'debug': False,
'registry_host': '0.0.0.0',
'registry_port': '9191',
'default_store': 'file',
'filesystem_store_datadir': stubs.FAKE_FILESYSTEM_ROOTDIR}
class TestRegistryDb(unittest.TestCase):
def setUp(self):
@ -97,16 +87,14 @@ class TestRegistryDb(unittest.TestCase):
self.stubs.UnsetAll()
class TestRegistryAPI(unittest.TestCase):
class TestRegistryAPI(base.IsolatedUnitTest):
def setUp(self):
"""Establish a clean test environment"""
self.stubs = stubout.StubOutForTesting()
stubs.stub_out_registry_and_store_server(self.stubs)
stubs.stub_out_filesystem_backend()
conf = test_utils.TestConfigOpts(CONF)
super(TestRegistryAPI, self).setUp()
context_class = 'glance.registry.context.RequestContext'
self.api = context.ContextMiddleware(rserver.API(conf),
conf, context_class=context_class)
self.api = context.ContextMiddleware(rserver.API(self.conf),
self.conf,
context_class=context_class)
self.FIXTURES = [
{'id': UUID1,
'name': 'fake image #1',
@ -122,7 +110,7 @@ class TestRegistryAPI(unittest.TestCase):
'min_disk': 0,
'min_ram': 0,
'size': 13,
'location': "swift://user:passwd@acct/container/obj.tar.0",
'location': "file:///%s/%s" % (self.test_dir, UUID1),
'properties': {'type': 'kernel'}},
{'id': UUID2,
'name': 'fake image #2',
@ -138,22 +126,25 @@ class TestRegistryAPI(unittest.TestCase):
'min_disk': 5,
'min_ram': 256,
'size': 19,
'location': "file:///tmp/glance-tests/2",
'location': "file:///%s/%s" % (self.test_dir, UUID2),
'properties': {}}]
self.context = rcontext.RequestContext(is_admin=True)
db_api.configure_db(conf)
db_api.configure_db(self.conf)
self.destroy_fixtures()
self.create_fixtures()
def tearDown(self):
"""Clear the test environment"""
stubs.clean_out_fake_filesystem_backend()
self.stubs.UnsetAll()
super(TestRegistryAPI, self).tearDown()
self.destroy_fixtures()
def create_fixtures(self):
for fixture in self.FIXTURES:
db_api.image_create(self.context, fixture)
# We write a fake image file to the filesystem
with open("%s/%s" % (self.test_dir, fixture['id']), 'wb') as image:
image.write("chunk00000remainder")
image.flush()
def destroy_fixtures(self):
# Easiest to just drop the models and re-create them...
@ -1932,16 +1923,11 @@ class TestRegistryAPI(unittest.TestCase):
self.assertEquals(res.status_int, webob.exc.HTTPUnauthorized.code)
class TestGlanceAPI(unittest.TestCase):
class TestGlanceAPI(base.IsolatedUnitTest):
def setUp(self):
"""Establish a clean test environment"""
self.stubs = stubout.StubOutForTesting()
stubs.stub_out_registry_and_store_server(self.stubs)
stubs.stub_out_filesystem_backend()
sql_connection = os.environ.get('GLANCE_SQL_CONNECTION', "sqlite://")
conf = test_utils.TestConfigOpts(CONF)
self.api = context.ContextMiddleware(router.API(conf), conf)
super(TestGlanceAPI, self).setUp()
self.api = context.ContextMiddleware(router.API(self.conf), self.conf)
self.FIXTURES = [
{'id': UUID1,
'name': 'fake image #1',
@ -1955,7 +1941,7 @@ class TestGlanceAPI(unittest.TestCase):
'deleted': False,
'checksum': None,
'size': 13,
'location': "swift://user:passwd@acct/container/obj.tar.0",
'location': "file:///%s/%s" % (self.test_dir, UUID1),
'properties': {'type': 'kernel'}},
{'id': UUID2,
'name': 'fake image #2',
@ -1969,22 +1955,25 @@ class TestGlanceAPI(unittest.TestCase):
'deleted': False,
'checksum': None,
'size': 19,
'location': "file:///tmp/glance-tests/2",
'location': "file:///%s/%s" % (self.test_dir, UUID2),
'properties': {}}]
self.context = rcontext.RequestContext(is_admin=True)
db_api.configure_db(conf)
db_api.configure_db(self.conf)
self.destroy_fixtures()
self.create_fixtures()
def tearDown(self):
"""Clear the test environment"""
stubs.clean_out_fake_filesystem_backend()
self.stubs.UnsetAll()
super(TestGlanceAPI, self).tearDown()
self.destroy_fixtures()
def create_fixtures(self):
for fixture in self.FIXTURES:
db_api.image_create(self.context, fixture)
# We write a fake image file to the filesystem
with open("%s/%s" % (self.test_dir, fixture['id']), 'wb') as image:
image.write("chunk00000remainder")
image.flush()
def destroy_fixtures(self):
# Easiest to just drop the models and re-create them...
@ -2691,19 +2680,16 @@ class TestGlanceAPI(unittest.TestCase):
self.assertEquals(res.status_int, webob.exc.HTTPUnauthorized.code)
class TestImageSerializer(unittest.TestCase):
class TestImageSerializer(base.IsolatedUnitTest):
def setUp(self):
"""Establish a clean test environment"""
self.stubs = stubout.StubOutForTesting()
stubs.stub_out_registry_and_store_server(self.stubs)
stubs.stub_out_filesystem_backend()
conf = test_utils.TestConfigOpts(CONF)
super(TestImageSerializer, self).setUp()
self.receiving_user = 'fake_user'
self.receiving_tenant = 2
self.context = rcontext.RequestContext(is_admin=True,
user=self.receiving_user,
tenant=self.receiving_tenant)
self.serializer = images.ImageSerializer(conf)
self.serializer = images.ImageSerializer(self.conf)
def image_iter():
for x in ['chunk', '678911234', '56789']:
@ -2729,11 +2715,6 @@ class TestImageSerializer(unittest.TestCase):
'properties': {}}
}
def tearDown(self):
"""Clear the test environment"""
stubs.clean_out_fake_filesystem_backend()
self.stubs.UnsetAll()
def test_meta(self):
exp_headers = {'x-image-meta-id': UUID2,
'x-image-meta-location': 'file:///tmp/glance-tests/2',

View File

@ -35,6 +35,7 @@ from glance.registry import client as rclient
from glance.registry import context as rcontext
from glance.tests import stubs
from glance.tests import utils as test_utils
from glance.tests.unit import base
CONF = {'sql_connection': 'sqlite://'}
@ -128,7 +129,7 @@ class TestBadClients(unittest.TestCase):
self.fail("Raised ClientConnectionError when it should not")
class TestRegistryClient(unittest.TestCase):
class TestRegistryClient(base.IsolatedUnitTest):
"""
Test proper actions made for both valid and invalid requests
@ -137,10 +138,8 @@ class TestRegistryClient(unittest.TestCase):
def setUp(self):
"""Establish a clean test environment"""
self.stubs = stubout.StubOutForTesting()
stubs.stub_out_registry_and_store_server(self.stubs)
conf = test_utils.TestConfigOpts(CONF)
db_api.configure_db(conf)
super(TestRegistryClient, self).setUp()
db_api.configure_db(self.conf)
self.context = rcontext.RequestContext(is_admin=True)
self.FIXTURES = [
{'id': UUID1,
@ -177,7 +176,7 @@ class TestRegistryClient(unittest.TestCase):
def tearDown(self):
"""Clear the test environment"""
self.stubs.UnsetAll()
super(TestRegistryClient, self).tearDown()
self.destroy_fixtures()
def create_fixtures(self):
@ -1128,7 +1127,7 @@ class TestRegistryClient(unittest.TestCase):
self.client.delete_member, UUID2, 'pattieblack')
class TestClient(unittest.TestCase):
class TestClient(base.IsolatedUnitTest):
"""
Test proper actions made for both valid and invalid requests
@ -1137,11 +1136,8 @@ class TestClient(unittest.TestCase):
def setUp(self):
"""Establish a clean test environment"""
self.stubs = stubout.StubOutForTesting()
stubs.stub_out_registry_and_store_server(self.stubs)
stubs.stub_out_filesystem_backend()
conf = test_utils.TestConfigOpts(CONF)
db_api.configure_db(conf)
super(TestClient, self).setUp()
db_api.configure_db(self.conf)
self.client = client.Client("0.0.0.0")
self.FIXTURES = [
{'id': UUID1,
@ -1156,7 +1152,7 @@ class TestClient(unittest.TestCase):
'deleted': False,
'checksum': None,
'size': 13,
'location': "swift://user:passwd@acct/container/obj.tar.0",
'location': "file:///%s/%s" % (self.test_dir, UUID1),
'properties': {'type': 'kernel'}},
{'id': UUID2,
'name': 'fake image #2',
@ -1170,7 +1166,7 @@ class TestClient(unittest.TestCase):
'deleted': False,
'checksum': None,
'size': 19,
'location': "file:///tmp/glance-tests/2",
'location': "file:///%s/%s" % (self.test_dir, UUID2),
'properties': {}}]
self.context = rcontext.RequestContext(is_admin=True)
self.destroy_fixtures()
@ -1178,13 +1174,16 @@ class TestClient(unittest.TestCase):
def tearDown(self):
"""Clear the test environment"""
stubs.clean_out_fake_filesystem_backend()
self.stubs.UnsetAll()
super(TestClient, self).tearDown()
self.destroy_fixtures()
def create_fixtures(self):
for fixture in self.FIXTURES:
db_api.image_create(self.context, fixture)
# We write a fake image file to the filesystem
with open("%s/%s" % (self.test_dir, fixture['id']), 'wb') as image:
image.write("chunk00000remainder")
image.flush()
def destroy_fixtures(self):
# Easiest to just drop the models and re-create them...

View File

@ -16,9 +16,6 @@
# under the License.
import datetime
import unittest
import stubout
from glance.common import context
from glance.common import exception
@ -26,7 +23,7 @@ from glance.common import utils
from glance.registry import context as rcontext
from glance.registry.db import api as db_api
from glance.registry.db import models as db_models
from glance.tests import stubs
from glance.tests.unit import base
from glance.tests import utils as test_utils
@ -38,11 +35,7 @@ UUID2 = _gen_uuid()
CONF = {'sql_connection': 'sqlite://',
'verbose': False,
'debug': False,
'registry_host': '0.0.0.0',
'registry_port': '9191',
'default_store': 'file',
'filesystem_store_datadir': stubs.FAKE_FILESYSTEM_ROOTDIR}
'debug': False}
FIXTURES = [
{'id': UUID1,
@ -79,13 +72,11 @@ FIXTURES = [
'properties': {}}]
class TestRegistryDb(unittest.TestCase):
class TestRegistryDb(base.IsolatedUnitTest):
def setUp(self):
"""Establish a clean test environment"""
self.stubs = stubout.StubOutForTesting()
stubs.stub_out_registry_and_store_server(self.stubs)
stubs.stub_out_filesystem_backend()
super(TestRegistryDb, self).setUp()
conf = test_utils.TestConfigOpts(CONF)
self.adm_context = rcontext.RequestContext(is_admin=True)
self.context = rcontext.RequestContext(is_admin=False)
@ -93,11 +84,6 @@ class TestRegistryDb(unittest.TestCase):
self.destroy_fixtures()
self.create_fixtures()
def tearDown(self):
"""Clear the test environment"""
stubs.clean_out_fake_filesystem_backend()
self.stubs.UnsetAll()
def create_fixtures(self):
for fixture in FIXTURES:
db_api.image_create(self.adm_context, fixture)

View File

@ -27,34 +27,37 @@ from glance.common import exception
from glance.common import utils
from glance.store.location import get_location_from_uri
from glance.store.filesystem import Store, ChunkedFile
from glance.tests import stubs
from glance.tests import utils as test_utils
FILESYSTEM_CONF = {
'verbose': True,
'debug': True,
'filesystem_store_datadir': stubs.FAKE_FILESYSTEM_ROOTDIR}
from glance.tests.unit import base
class TestStore(unittest.TestCase):
class TestStore(base.IsolatedUnitTest):
def setUp(self):
"""Establish a clean test environment"""
self.stubs = stubout.StubOutForTesting()
stubs.stub_out_filesystem_backend()
super(TestStore, self).setUp()
self.orig_chunksize = ChunkedFile.CHUNKSIZE
ChunkedFile.CHUNKSIZE = 10
self.store = Store(test_utils.TestConfigOpts(FILESYSTEM_CONF))
self.store = Store(self.conf)
def tearDown(self):
"""Clear the test environment"""
stubs.clean_out_fake_filesystem_backend()
self.stubs.UnsetAll()
super(TestStore, self).tearDown()
ChunkedFile.CHUNKSIZE = self.orig_chunksize
def test_get(self):
"""Test a "normal" retrieval of an image in chunks"""
uri = "file:///tmp/glance-tests/2"
# First add an image...
image_id = utils.generate_uuid()
file_contents = "chunk00000remainder"
location = "file://%s/%s" % (self.test_dir, image_id)
image_file = StringIO.StringIO(file_contents)
location, size, checksum = self.store.add(image_id,
image_file,
len(file_contents))
# Now read it back...
uri = "file:///%s/%s" % (self.test_dir, image_id)
loc = get_location_from_uri(uri)
(image_file, image_size) = self.store.get(loc)
@ -74,7 +77,7 @@ class TestStore(unittest.TestCase):
Test that trying to retrieve a file that doesn't exist
raises an error
"""
loc = get_location_from_uri("file:///tmp/glance-tests/non-existing")
loc = get_location_from_uri("file:///%s/non-existing" % self.test_dir)
self.assertRaises(exception.NotFound,
self.store.get,
loc)
@ -86,7 +89,7 @@ class TestStore(unittest.TestCase):
expected_file_size = 1024 * 5 # 5K
expected_file_contents = "*" * expected_file_size
expected_checksum = hashlib.md5(expected_file_contents).hexdigest()
expected_location = "file://%s/%s" % (stubs.FAKE_FILESYSTEM_ROOTDIR,
expected_location = "file://%s/%s" % (self.test_dir,
expected_image_id)
image_file = StringIO.StringIO(expected_file_contents)
@ -98,7 +101,7 @@ class TestStore(unittest.TestCase):
self.assertEquals(expected_file_size, size)
self.assertEquals(expected_checksum, checksum)
uri = "file:///tmp/glance-tests/%s" % expected_image_id
uri = "file:///%s/%s" % (self.test_dir, expected_image_id)
loc = get_location_from_uri(uri)
(new_image_file, new_image_size) = self.store.get(loc)
new_image_contents = ""
@ -116,16 +119,38 @@ class TestStore(unittest.TestCase):
Tests that adding an image with an existing identifier
raises an appropriate exception
"""
ChunkedFile.CHUNKSIZE = 1024
image_id = utils.generate_uuid()
file_size = 1024 * 5 # 5K
file_contents = "*" * file_size
location = "file://%s/%s" % (self.test_dir, image_id)
image_file = StringIO.StringIO(file_contents)
location, size, checksum = self.store.add(image_id,
image_file,
file_size)
image_file = StringIO.StringIO("nevergonnamakeit")
self.assertRaises(exception.Duplicate,
self.store.add,
'2', image_file, 0)
image_id, image_file, 0)
def test_delete(self):
"""
Test we can delete an existing image in the filesystem store
"""
uri = "file:///tmp/glance-tests/2"
# First add an image
image_id = utils.generate_uuid()
file_size = 1024 * 5 # 5K
file_contents = "*" * file_size
location = "file://%s/%s" % (self.test_dir, image_id)
image_file = StringIO.StringIO(file_contents)
location, size, checksum = self.store.add(image_id,
image_file,
file_size)
# Now check that we can delete it
uri = "file:///%s/%s" % (self.test_dir, image_id)
loc = get_location_from_uri(uri)
self.store.delete(loc)

View File

@ -16,35 +16,24 @@
# under the License.
import json
import unittest
import stubout
import webob
from glance import client
from glance.common import config
from glance.common import exception
from glance.api import versions
from glance.tests import stubs
from glance.tests import utils
from glance.tests.unit import base
class VersionsTest(unittest.TestCase):
class VersionsTest(base.IsolatedUnitTest):
"""
Test the version information returned from
the API service
"""
def setUp(self):
"""Establish a clean test environment"""
self.stubs = stubout.StubOutForTesting()
stubs.stub_out_registry_and_store_server(self.stubs)
def tearDown(self):
"""Clear the test environment"""
self.stubs.UnsetAll()
def test_get_version_list(self):
req = webob.Request.blank('/', base_url="http://0.0.0.0:9292/")
req.accept = "application/json"

View File

@ -20,6 +20,7 @@
import errno
import functools
import os
import random
import socket
import subprocess
import tempfile
@ -27,6 +28,19 @@ import tempfile
import nose.plugins.skip
from glance.common import config
from glance.common import utils
def get_isolated_test_env():
"""
Returns a tuple of (test_id, test_dir) that is unique
for an isolated test environment. Also ensure the test_dir
is created.
"""
test_id = random.randint(0, 100000)
test_dir = os.path.join("/", "tmp", "test.%d" % test_id)
utils.safe_mkdirs(test_dir)
return test_id, test_dir
class TestConfigOpts(config.GlanceConfigOpts):