1# Copyright (c) 2013-2014 Rackspace, Inc. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12# implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15import datetime 16import functools 17from os import path 18import time 19import types 20 21import oslotest.base as oslotest 22import six 23import six.moves.urllib.parse as urlparse 24 25 26class BaseTestCase(oslotest.BaseTestCase): 27 def setUp(self): 28 super(BaseTestCase, self).setUp() 29 self.order_id = 'order1234' 30 self.external_project_id = 'keystone1234' 31 32 def tearDown(self): 33 super(BaseTestCase, self).tearDown() 34 35 36def construct_new_test_function(original_func, name, build_params): 37 """Builds a new test function based on parameterized data. 38 39 :param original_func: The original test function that is used as a template 40 :param name: The fullname of the new test function 41 :param build_params: A dictionary or list containing args or kwargs 42 for the new test 43 :return: A new function object 44 """ 45 new_func = types.FunctionType( 46 six.get_function_code(original_func), 47 six.get_function_globals(original_func), 48 name=name, 49 argdefs=six.get_function_defaults(original_func) 50 ) 51 52 # Support either an arg list or kwarg dict for our data 53 build_args = build_params if isinstance(build_params, list) else [] 54 build_kwargs = build_params if isinstance(build_params, dict) else {} 55 56 # Build a test wrapper to execute with our kwargs 57 def test_wrapper(func, test_args, test_kwargs): 58 @functools.wraps(func) 59 def wrapper(self): 60 return func(self, *test_args, **test_kwargs) 61 return wrapper 62 63 return test_wrapper(new_func, build_args, build_kwargs) 64 65 66def process_parameterized_function(name, func_obj, build_data): 67 """Build lists of functions to add and remove to a test case.""" 68 to_remove = [] 69 to_add = [] 70 71 for subtest_name, params in build_data.items(): 72 # Build new test function 73 func_name = '{0}_{1}'.format(name, subtest_name) 74 new_func = construct_new_test_function(func_obj, func_name, params) 75 76 # Mark the new function as needed to be added to the class 77 to_add.append((func_name, new_func)) 78 79 # Mark key for removal 80 to_remove.append(name) 81 82 return to_remove, to_add 83 84 85def parameterized_test_case(cls): 86 """Class decorator to process parameterized tests 87 88 This allows for parameterization to be used for potentially any 89 unittest compatible runner; including testr and py.test. 90 """ 91 tests_to_remove = [] 92 tests_to_add = [] 93 for key, val in vars(cls).items(): 94 # Only process tests with build data on them 95 if key.startswith('test_') and val.__dict__.get('build_data'): 96 to_remove, to_add = process_parameterized_function( 97 name=key, 98 func_obj=val, 99 build_data=val.__dict__.get('build_data') 100 ) 101 tests_to_remove.extend(to_remove) 102 tests_to_add.extend(to_add) 103 104 # Add all new test functions 105 [setattr(cls, name, func) for name, func in tests_to_add] 106 107 # Remove all old test function templates (if they still exist) 108 [delattr(cls, key) for key in tests_to_remove if hasattr(cls, key)] 109 return cls 110 111 112def parameterized_dataset(build_data): 113 """Simple decorator to mark a test method for processing.""" 114 def decorator(func): 115 func.__dict__['build_data'] = build_data 116 return func 117 return decorator 118 119 120def create_timestamp_w_tz_and_offset(timezone=None, days=0, hours=0, minutes=0, 121 seconds=0): 122 """Creates a timestamp with a timezone and offset in days 123 124 :param timezone: Timezone used in creation of timestamp 125 :param days: The offset in days 126 :param hours: The offset in hours 127 :param minutes: The offset in minutes 128 129 :return: a timestamp 130 """ 131 if timezone is None: 132 timezone = time.strftime("%z") 133 134 timestamp = '{time}{timezone}'.format( 135 time=(datetime.datetime.today() + datetime.timedelta(days=days, 136 hours=hours, 137 minutes=minutes, 138 seconds=seconds)), 139 timezone=timezone) 140 141 return timestamp 142 143 144def get_limit_and_offset_from_ref(ref): 145 matches = dict(urlparse.parse_qsl(urlparse.urlparse(ref).query)) 146 ref_limit = matches['limit'] 147 ref_offset = matches['offset'] 148 149 return ref_limit, ref_offset 150 151 152def get_tomorrow_timestamp(): 153 tomorrow = (datetime.today() + datetime.timedelta(days=1)) 154 return tomorrow.isoformat() 155 156 157def string_to_datetime(datetimestring, date_formats=None): 158 date_formats = date_formats or [ 159 '%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S.%f', '%Y-%m-%dT%H:%M:%S.%fZ', 160 '%Y-%m-%dT%H:%M:%S.%f', "%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%dT%H:%M:%S"] 161 162 for dateformat in date_formats: 163 try: 164 return datetime.datetime.strptime(datetimestring, dateformat) 165 except ValueError: 166 continue 167 else: 168 raise 169 170 171def get_id_from_ref(ref): 172 """Returns id from reference.""" 173 ref_id = None 174 if ref is not None and len(ref) > 0: 175 ref_id = path.split(ref)[1] 176 return ref_id 177