Add @skip_if_disabled decorator to test.utils and integrate
it into the base functional API test case. The S3 functional test case now uses test_api.TestApi as its base class and the setUp() method sets the disabled and disabled_message attributes that the @skip_if_disabled decorator uses. The S3 test case now tests all the methods that are tested for the filesystem store driver in test_api.TestApi Removed the test.functional.test_s3.conf file as it was no longer needed. Change-Id: I308d7a655c8c26339d0e2560634421935bbc8b5d
This commit is contained in:
parent
de78df9020
commit
72e2e31fea
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
*.pyc
|
||||
*.swp
|
||||
*.log
|
||||
.glance-venv
|
||||
tests.sqlite
|
||||
|
@ -252,6 +252,8 @@ class FunctionalTest(unittest.TestCase):
|
||||
servers and clients and not just the stubbed out interfaces
|
||||
"""
|
||||
|
||||
disabled = False
|
||||
|
||||
def setUp(self):
|
||||
|
||||
self.test_id = random.randint(0, 100000)
|
||||
@ -276,11 +278,12 @@ class FunctionalTest(unittest.TestCase):
|
||||
self.files_to_destroy = []
|
||||
|
||||
def tearDown(self):
|
||||
self.cleanup()
|
||||
# We destroy the test data store between each test case,
|
||||
# and recreate it, which ensures that we have no side-effects
|
||||
# from the tests
|
||||
self._reset_database()
|
||||
if not self.disabled:
|
||||
self.cleanup()
|
||||
# We destroy the test data store between each test case,
|
||||
# and recreate it, which ensures that we have no side-effects
|
||||
# from the tests
|
||||
self._reset_database()
|
||||
|
||||
def _reset_database(self):
|
||||
conn_string = self.registry_server.sql_connection
|
||||
|
@ -24,16 +24,17 @@ import os
|
||||
import tempfile
|
||||
|
||||
from glance.tests import functional
|
||||
from glance.tests.utils import execute
|
||||
from glance.tests.utils import execute, skip_if_disabled
|
||||
|
||||
FIVE_KB = 5 * 1024
|
||||
FIVE_GB = 5 * 1024 * 1024 * 1024
|
||||
|
||||
|
||||
class TestApiHttplib2(functional.FunctionalTest):
|
||||
class TestApi(functional.FunctionalTest):
|
||||
|
||||
"""Functional tests using httplib2 against the API server"""
|
||||
|
||||
@skip_if_disabled
|
||||
def test_get_head_simple_post(self):
|
||||
"""
|
||||
We test the following sequential series of actions:
|
||||
@ -273,6 +274,7 @@ class TestApiHttplib2(functional.FunctionalTest):
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
@skip_if_disabled
|
||||
def test_queued_process_flow(self):
|
||||
"""
|
||||
We test the process flow where a user registers an image
|
||||
@ -391,6 +393,7 @@ class TestApiHttplib2(functional.FunctionalTest):
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
@skip_if_disabled
|
||||
def test_version_variations(self):
|
||||
"""
|
||||
We test that various calls to the images and root endpoints are
|
||||
@ -549,6 +552,7 @@ class TestApiHttplib2(functional.FunctionalTest):
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
@skip_if_disabled
|
||||
def test_size_greater_2G_mysql(self):
|
||||
"""
|
||||
A test against the actual datastore backend for the registry
|
||||
@ -592,6 +596,7 @@ class TestApiHttplib2(functional.FunctionalTest):
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
@skip_if_disabled
|
||||
def test_traceback_not_consumed(self):
|
||||
"""
|
||||
A test that errors coming from the POST API do not
|
||||
@ -620,6 +625,7 @@ class TestApiHttplib2(functional.FunctionalTest):
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
@skip_if_disabled
|
||||
def test_filtered_images(self):
|
||||
"""
|
||||
Set up four test images and ensure each query param filter works
|
||||
@ -852,6 +858,7 @@ class TestApiHttplib2(functional.FunctionalTest):
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
@skip_if_disabled
|
||||
def test_limited_images(self):
|
||||
"""
|
||||
Ensure marker and limit query params work
|
||||
@ -940,6 +947,7 @@ class TestApiHttplib2(functional.FunctionalTest):
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
@skip_if_disabled
|
||||
def test_ordered_images(self):
|
||||
"""
|
||||
Set up three test images and ensure each query param filter works
|
||||
@ -1050,6 +1058,7 @@ class TestApiHttplib2(functional.FunctionalTest):
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
@skip_if_disabled
|
||||
def test_duplicate_image_upload(self):
|
||||
"""
|
||||
Upload initial image, then attempt to upload duplicate image
|
||||
|
@ -1,21 +0,0 @@
|
||||
[DEFAULT]
|
||||
|
||||
# Set the following to Amazon S3 credentials that you want
|
||||
# to use while functional testing the S3 backend.
|
||||
|
||||
# Address where the S3 authentication service lives
|
||||
s3_store_host = s3.amazonaws.com
|
||||
|
||||
# User to authenticate against the S3 authentication service
|
||||
s3_store_access_key = <20-char AWS access key>
|
||||
|
||||
# Auth key for the user authenticating against the
|
||||
# S3 authentication service
|
||||
s3_store_secret_key = <40-char AWS secret key>
|
||||
|
||||
# Container within the account that the account should use
|
||||
# for storing images in S3. Note that S3 has a flat namespace,
|
||||
# so you need a unique bucket name for your glance images. An
|
||||
# easy way to do this is append your lower-cased AWS access key
|
||||
# to "glance"
|
||||
s3_store_bucket = <20-char AWS access key - lowercased>glance
|
@ -36,23 +36,21 @@ import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from glance.tests import functional
|
||||
from glance.tests.functional import test_api
|
||||
from glance.tests.utils import execute
|
||||
from glance.tests import utils
|
||||
|
||||
FIVE_KB = 5 * 1024
|
||||
FIVE_GB = 5 * 1024 * 1024 * 1024
|
||||
|
||||
|
||||
class TestS3(functional.FunctionalTest):
|
||||
class TestS3(test_api.TestApi):
|
||||
|
||||
"""Functional tests for the S3 backend"""
|
||||
|
||||
# Test machines can set the GLANCE_TEST_MIGRATIONS_CONF variable
|
||||
# Test machines can set the GLANCE_TEST_S3_CONF variable
|
||||
# to override the location of the config file for migration testing
|
||||
CONFIG_FILE_PATH = os.environ.get('GLANCE_TEST_S3_CONF',
|
||||
os.path.join('glance', 'tests',
|
||||
'functional',
|
||||
'test_s3.conf'))
|
||||
CONFIG_FILE_PATH = os.environ.get('GLANCE_TEST_S3_CONF')
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
@ -61,11 +59,16 @@ class TestS3(functional.FunctionalTest):
|
||||
If the connection fails, mark all tests to skip.
|
||||
"""
|
||||
self.inited = False
|
||||
self.skip_tests = True
|
||||
self.disabled = True
|
||||
|
||||
if self.inited:
|
||||
return
|
||||
|
||||
if not self.CONFIG_FILE_PATH:
|
||||
self.disabled_message = "GLANCE_TEST_S3_CONF environ not set."
|
||||
self.inited = True
|
||||
return
|
||||
|
||||
if os.path.exists(TestS3.CONFIG_FILE_PATH):
|
||||
cp = ConfigParser.RawConfigParser()
|
||||
try:
|
||||
@ -74,8 +77,8 @@ class TestS3(functional.FunctionalTest):
|
||||
for key, value in defaults.items():
|
||||
self.__dict__[key] = value
|
||||
except ConfigParser.ParsingError, e:
|
||||
print ("Failed to read test_s3.conf config file. "
|
||||
"Got error: %s" % e)
|
||||
self.disabled_message = ("Failed to read test_s3.conf config "
|
||||
"file. Got error: %s" % e)
|
||||
super(TestS3, self).setUp()
|
||||
self.inited = True
|
||||
return
|
||||
@ -89,8 +92,8 @@ class TestS3(functional.FunctionalTest):
|
||||
secret_key = self.s3_store_secret_key
|
||||
bucket_name = self.s3_store_bucket
|
||||
except AttributeError, e:
|
||||
print ("Failed to find required configuration options for "
|
||||
"S3 store. Got error: %s" % e)
|
||||
self.disabled_message = ("Failed to find required configuration "
|
||||
"options for S3 store. Got error: %s" % e)
|
||||
self.inited = True
|
||||
super(TestS3, self).setUp()
|
||||
return
|
||||
@ -104,16 +107,17 @@ class TestS3(functional.FunctionalTest):
|
||||
if bucket.name == bucket_name:
|
||||
self.bucket = bucket
|
||||
except S3ResponseError, e:
|
||||
print ("Failed to connect to S3 with credentials,"
|
||||
"to find bucket. Got error: %s" % e)
|
||||
self.disabled_message = ("Failed to connect to S3 with "
|
||||
"credentials, to find bucket. "
|
||||
"Got error: %s" % e)
|
||||
self.inited = True
|
||||
super(TestS3, self).setUp()
|
||||
return
|
||||
except TypeError, e:
|
||||
# This hack is necessary because of a bug in boto 1.9b:
|
||||
# http://code.google.com/p/boto/issues/detail?id=540
|
||||
print ("Failed to connect to S3 with credentials. "
|
||||
"Got error: %s" % e)
|
||||
self.disabled_message = ("Failed to connect to S3 with "
|
||||
"credentials. Got error: %s" % e)
|
||||
self.inited = True
|
||||
super(TestS3, self).setUp()
|
||||
return
|
||||
@ -124,21 +128,22 @@ class TestS3(functional.FunctionalTest):
|
||||
try:
|
||||
self.bucket = s3_conn.create_bucket(bucket_name)
|
||||
except boto.exception.S3ResponseError, e:
|
||||
print ("Failed to create bucket. Got error: %s" % e)
|
||||
self.disabled_message = ("Failed to create bucket. "
|
||||
"Got error: %s" % e)
|
||||
self.inited = True
|
||||
super(TestS3, self).setUp()
|
||||
return
|
||||
else:
|
||||
self.clear_bucket()
|
||||
|
||||
self.skip_tests = False
|
||||
self.disabled = False
|
||||
self.inited = True
|
||||
self.default_store = 's3'
|
||||
|
||||
super(TestS3, self).setUp()
|
||||
|
||||
def tearDown(self):
|
||||
if not self.skip_tests:
|
||||
if not self.disabled:
|
||||
self.clear_bucket()
|
||||
super(TestS3, self).tearDown()
|
||||
|
||||
@ -150,346 +155,7 @@ class TestS3(functional.FunctionalTest):
|
||||
for key in keys:
|
||||
key.delete()
|
||||
|
||||
def test_add_list_delete_list(self):
|
||||
"""
|
||||
We test the following:
|
||||
|
||||
0. GET /images
|
||||
- Verify no public images
|
||||
1. GET /images/detail
|
||||
- Verify no public images
|
||||
2. HEAD /images/1
|
||||
- Verify 404 returned
|
||||
3. POST /images with public image named Image1 with a location
|
||||
attribute and no custom properties
|
||||
- Verify 201 returned
|
||||
4. HEAD /images/1
|
||||
- Verify HTTP headers have correct information we just added
|
||||
5. GET /images/1
|
||||
- Verify all information on image we just added is correct
|
||||
6. GET /images
|
||||
- Verify the image we just added is returned
|
||||
7. GET /images/detail
|
||||
- Verify the image we just added is returned
|
||||
8. PUT /images/1 with custom properties of "distro" and "arch"
|
||||
- Verify 200 returned
|
||||
9. GET /images/1
|
||||
- Verify updated information about image was stored
|
||||
10. PUT /images/1
|
||||
- Remove a previously existing property.
|
||||
11. PUT /images/1
|
||||
- Add a previously deleted property.
|
||||
"""
|
||||
if self.skip_tests:
|
||||
return True
|
||||
|
||||
self.cleanup()
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
|
||||
api_port = self.api_port
|
||||
registry_port = self.registry_port
|
||||
|
||||
# 0. GET /images
|
||||
# Verify no public images
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
self.assertEqual('{"images": []}', out.strip())
|
||||
|
||||
# 1. GET /images/detail
|
||||
# Verify no public images
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
self.assertEqual('{"images": []}', out.strip())
|
||||
|
||||
# 2. HEAD /images/1
|
||||
# Verify 404 returned
|
||||
cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1/images/1" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
status_line = lines[0]
|
||||
|
||||
self.assertEqual("HTTP/1.1 404 Not Found", status_line)
|
||||
|
||||
# 3. POST /images with public image named Image1
|
||||
# attribute and no custom properties. Verify a 200 OK is returned
|
||||
image_data = "*" * FIVE_KB
|
||||
|
||||
cmd = ("curl -i -X POST "
|
||||
"-H 'Expect: ' " # Necessary otherwise sends 100 Continue
|
||||
"-H 'Content-Type: application/octet-stream' "
|
||||
"-H 'X-Image-Meta-Name: Image1' "
|
||||
"-H 'X-Image-Meta-Is-Public: True' "
|
||||
"--data-binary \"%s\" "
|
||||
"http://0.0.0.0:%d/v1/images") % (image_data, api_port)
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
status_line = lines[0]
|
||||
|
||||
self.assertEqual("HTTP/1.1 201 Created", status_line, out)
|
||||
|
||||
# 4. HEAD /images
|
||||
# Verify image found now
|
||||
cmd = "curl -i -X HEAD http://0.0.0.0:%d/v1/images/1" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
status_line = lines[0]
|
||||
|
||||
self.assertEqual("HTTP/1.1 200 OK", status_line)
|
||||
self.assertTrue("X-Image-Meta-Name: Image1" in out)
|
||||
|
||||
# 5. GET /images/1
|
||||
# Verify all information on image we just added is correct
|
||||
|
||||
cmd = "curl -i http://0.0.0.0:%d/v1/images/1" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
|
||||
self.assertEqual("HTTP/1.1 200 OK", lines.pop(0))
|
||||
|
||||
# Handle the headers
|
||||
image_headers = {}
|
||||
std_headers = {}
|
||||
other_lines = []
|
||||
for line in lines:
|
||||
if line.strip() == '':
|
||||
continue
|
||||
if line.startswith("X-Image"):
|
||||
pieces = line.split(":")
|
||||
key = pieces[0].strip()
|
||||
value = ":".join(pieces[1:]).strip()
|
||||
image_headers[key] = value
|
||||
elif ':' in line:
|
||||
pieces = line.split(":")
|
||||
key = pieces[0].strip()
|
||||
value = ":".join(pieces[1:]).strip()
|
||||
std_headers[key] = value
|
||||
else:
|
||||
other_lines.append(line)
|
||||
|
||||
expected_image_headers = {
|
||||
'X-Image-Meta-Id': '1',
|
||||
'X-Image-Meta-Name': 'Image1',
|
||||
'X-Image-Meta-Is_public': 'True',
|
||||
'X-Image-Meta-Status': 'active',
|
||||
'X-Image-Meta-Disk_format': '',
|
||||
'X-Image-Meta-Container_format': '',
|
||||
'X-Image-Meta-Size': str(FIVE_KB),
|
||||
'X-Image-Meta-Location': 's3://%s:%s@%s/%s/1' % (
|
||||
self.s3_store_access_key,
|
||||
self.s3_store_secret_key,
|
||||
self.s3_store_host,
|
||||
self.s3_store_bucket)}
|
||||
|
||||
expected_std_headers = {
|
||||
'Content-Length': str(FIVE_KB),
|
||||
'Content-Type': 'application/octet-stream'}
|
||||
|
||||
for expected_key, expected_value in expected_image_headers.items():
|
||||
self.assertTrue(expected_key in image_headers,
|
||||
"Failed to find key %s in image_headers"
|
||||
% expected_key)
|
||||
self.assertEqual(expected_value, image_headers[expected_key],
|
||||
"For key '%s' expected header value '%s'. Got '%s'"
|
||||
% (expected_key,
|
||||
expected_value,
|
||||
image_headers[expected_key]))
|
||||
|
||||
for expected_key, expected_value in expected_std_headers.items():
|
||||
self.assertTrue(expected_key in std_headers,
|
||||
"Failed to find key %s in std_headers"
|
||||
% expected_key)
|
||||
self.assertEqual(expected_value, std_headers[expected_key],
|
||||
"For key '%s' expected header value '%s'. Got '%s'"
|
||||
% (expected_key,
|
||||
expected_value,
|
||||
std_headers[expected_key]))
|
||||
|
||||
# Now the image data...
|
||||
expected_image_data = "*" * FIVE_KB
|
||||
|
||||
# Should only be a single "line" left, and
|
||||
# that's the image data
|
||||
self.assertEqual(1, len(other_lines))
|
||||
self.assertEqual(expected_image_data, other_lines[0])
|
||||
|
||||
# 6. GET /images
|
||||
# Verify no public images
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
expected_result = {"images": [
|
||||
{"container_format": None,
|
||||
"disk_format": None,
|
||||
"id": 1,
|
||||
"name": "Image1",
|
||||
"checksum": "c2e5db72bd7fd153f53ede5da5a06de3",
|
||||
"size": 5120}]}
|
||||
self.assertEqual(expected_result, json.loads(out.strip()))
|
||||
|
||||
# 7. GET /images/detail
|
||||
# Verify image and all its metadata
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
expected_image = {
|
||||
"status": "active",
|
||||
"name": "Image1",
|
||||
"deleted": False,
|
||||
"container_format": None,
|
||||
"disk_format": None,
|
||||
"id": 1,
|
||||
'location': 's3://%s:%s@%s/%s/1' % (
|
||||
self.s3_store_access_key,
|
||||
self.s3_store_secret_key,
|
||||
self.s3_store_host,
|
||||
self.s3_store_bucket),
|
||||
"is_public": True,
|
||||
"deleted_at": None,
|
||||
"properties": {},
|
||||
"size": 5120}
|
||||
|
||||
image = json.loads(out.strip())['images'][0]
|
||||
|
||||
for expected_key, expected_value in expected_image.items():
|
||||
self.assertTrue(expected_key in image,
|
||||
"Failed to find key %s in image"
|
||||
% expected_key)
|
||||
self.assertEqual(expected_value, expected_image[expected_key],
|
||||
"For key '%s' expected header value '%s'. Got '%s'"
|
||||
% (expected_key,
|
||||
expected_value,
|
||||
image[expected_key]))
|
||||
|
||||
# 8. PUT /images/1 with custom properties of "distro" and "arch"
|
||||
# Verify 200 returned
|
||||
|
||||
cmd = ("curl -i -X PUT "
|
||||
"-H 'X-Image-Meta-Property-Distro: Ubuntu' "
|
||||
"-H 'X-Image-Meta-Property-Arch: x86_64' "
|
||||
"http://0.0.0.0:%d/v1/images/1") % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
status_line = lines[0]
|
||||
|
||||
self.assertEqual("HTTP/1.1 200 OK", status_line)
|
||||
|
||||
# 9. GET /images/detail
|
||||
# Verify image and all its metadata
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
expected_image = {
|
||||
"status": "active",
|
||||
"name": "Image1",
|
||||
"deleted": False,
|
||||
"container_format": None,
|
||||
"disk_format": None,
|
||||
"id": 1,
|
||||
'location': 's3://%s:%s@%s/%s/1' % (
|
||||
self.s3_store_access_key,
|
||||
self.s3_store_secret_key,
|
||||
self.s3_store_host,
|
||||
self.s3_store_bucket),
|
||||
"is_public": True,
|
||||
"deleted_at": None,
|
||||
"properties": {'distro': 'Ubuntu', 'arch': 'x86_64'},
|
||||
"size": 5120}
|
||||
|
||||
image = json.loads(out.strip())['images'][0]
|
||||
|
||||
for expected_key, expected_value in expected_image.items():
|
||||
self.assertTrue(expected_key in image,
|
||||
"Failed to find key %s in image"
|
||||
% expected_key)
|
||||
self.assertEqual(expected_value, image[expected_key],
|
||||
"For key '%s' expected header value '%s'. Got '%s'"
|
||||
% (expected_key,
|
||||
expected_value,
|
||||
image[expected_key]))
|
||||
|
||||
# 10. PUT /images/1 and remove a previously existing property.
|
||||
cmd = ("curl -i -X PUT "
|
||||
"-H 'X-Image-Meta-Property-Arch: x86_64' "
|
||||
"http://0.0.0.0:%d/v1/images/1") % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
status_line = lines[0]
|
||||
|
||||
self.assertEqual("HTTP/1.1 200 OK", status_line)
|
||||
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
image = json.loads(out.strip())['images'][0]
|
||||
self.assertEqual(1, len(image['properties']))
|
||||
self.assertEqual('x86_64', image['properties']['arch'])
|
||||
|
||||
# 11. PUT /images/1 and add a previously deleted property.
|
||||
cmd = ("curl -i -X PUT "
|
||||
"-H 'X-Image-Meta-Property-Distro: Ubuntu' "
|
||||
"-H 'X-Image-Meta-Property-Arch: x86_64' "
|
||||
"http://0.0.0.0:%d/v1/images/1") % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
lines = out.split("\r\n")
|
||||
status_line = lines[0]
|
||||
|
||||
self.assertEqual("HTTP/1.1 200 OK", status_line)
|
||||
|
||||
cmd = "curl http://0.0.0.0:%d/v1/images/detail" % api_port
|
||||
|
||||
exitcode, out, err = execute(cmd)
|
||||
|
||||
self.assertEqual(0, exitcode)
|
||||
|
||||
image = json.loads(out.strip())['images'][0]
|
||||
self.assertEqual(2, len(image['properties']))
|
||||
self.assertEqual('x86_64', image['properties']['arch'])
|
||||
self.assertEqual('Ubuntu', image['properties']['distro'])
|
||||
|
||||
self.stop_servers()
|
||||
|
||||
@utils.skip_if_disabled
|
||||
def test_delete_not_existing(self):
|
||||
"""
|
||||
We test the following:
|
||||
@ -499,9 +165,6 @@ class TestS3(functional.FunctionalTest):
|
||||
1. DELETE /images/1
|
||||
- Verify 404
|
||||
"""
|
||||
if self.skip_tests:
|
||||
return True
|
||||
|
||||
self.cleanup()
|
||||
self.start_servers(**self.__dict__.copy())
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
"""Common utilities used in testing"""
|
||||
|
||||
import functools
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
@ -39,7 +40,7 @@ class skip_test(object):
|
||||
|
||||
|
||||
class skip_if(object):
|
||||
"""Decorator that skips a test if contition is true."""
|
||||
"""Decorator that skips a test if condition is true."""
|
||||
def __init__(self, condition, msg):
|
||||
self.condition = condition
|
||||
self.message = msg
|
||||
@ -72,6 +73,20 @@ class skip_unless(object):
|
||||
return _skipper
|
||||
|
||||
|
||||
def skip_if_disabled(func):
|
||||
"""Decorator that skips a test if test case is disabled."""
|
||||
@functools.wraps(func)
|
||||
def wrapped(*a, **kwargs):
|
||||
func.__test__ = False
|
||||
test_obj = a[0]
|
||||
message = getattr(test_obj, 'disabled_message',
|
||||
'Test disabled')
|
||||
if test_obj.disabled:
|
||||
raise nose.SkipTest(message)
|
||||
func(*a, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
||||
def execute(cmd, raise_error=True):
|
||||
"""
|
||||
Executes a command in a subprocess. Returns a tuple
|
||||
|
Loading…
Reference in New Issue
Block a user