1#!/usr/bin/env python3 2# 3# Copyright 2018 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16from __future__ import absolute_import 17 18import os 19import sys 20 21# A temporary hack to prevent tests/libs/logging from being selected as the 22# python default logging module. 23sys.path[0] = os.path.join(sys.path[0], '../') 24import mock 25import shutil 26import tempfile 27import unittest 28 29from acts import base_test 30from acts.libs import version_selector 31from acts.test_decorators import test_tracker_info 32 33from mobly.config_parser import TestRunConfig 34 35 36def versioning_decorator(min_sdk, max_sdk): 37 return version_selector.set_version(lambda ret, *_, **__: ret, min_sdk, 38 max_sdk) 39 40 41def versioning_decorator2(min_sdk, max_sdk): 42 return version_selector.set_version(lambda ret, *_, **__: ret, min_sdk, 43 max_sdk) 44 45 46def test_versioning(min_sdk, max_sdk): 47 return version_selector.set_version(lambda *_, **__: 1, min_sdk, max_sdk) 48 49 50@versioning_decorator(1, 10) 51def versioned_func(arg1, arg2): 52 return 'function 1', arg1, arg2 53 54 55@versioning_decorator(11, 11) 56def versioned_func(arg1, arg2): 57 return 'function 2', arg1, arg2 58 59 60@versioning_decorator(12, 20) 61def versioned_func(arg1, arg2): 62 return 'function 3', arg1, arg2 63 64 65@versioning_decorator(1, 20) 66def versioned_func_with_kwargs(_, asdf='jkl'): 67 return asdf 68 69 70def class_versioning_decorator(min_sdk, max_sdk): 71 return version_selector.set_version(lambda _, ret, *__, **___: ret, 72 min_sdk, max_sdk) 73 74 75class VersionedClass(object): 76 @classmethod 77 @class_versioning_decorator(1, 99999999) 78 def class_func(cls, arg1): 79 return cls, arg1 80 81 @staticmethod 82 @versioning_decorator(1, 99999999) 83 def static_func(arg1): 84 return arg1 85 86 @class_versioning_decorator(1, 99999999) 87 def instance_func(self, arg1): 88 return self, arg1 89 90 91class VersionedTestClass(base_test.BaseTestClass): 92 @mock.patch('mobly.utils.create_dir') 93 def __init__(self, configs, _): 94 super().__init__(configs) 95 96 @test_tracker_info('UUID_1') 97 @test_versioning(1, 1) 98 def test_1(self): 99 pass 100 101 @test_versioning(1, 1) 102 @test_tracker_info('UUID_2') 103 def test_2(self): 104 pass 105 106 107class VersionSelectorIntegrationTest(unittest.TestCase): 108 """Tests the acts.libs.version_selector module.""" 109 110 @classmethod 111 def setUpClass(cls): 112 cls.tmp_dir = tempfile.mkdtemp() 113 114 @classmethod 115 def tearDownClass(cls): 116 shutil.rmtree(cls.tmp_dir) 117 118 def test_versioned_test_class_calls_both_functions(self): 119 """Tests that VersionedTestClass (above) can be called with 120 test_tracker_info.""" 121 test_run_config = TestRunConfig() 122 test_run_config.log_path = '' 123 test_run_config.summary_writer = mock.MagicMock() 124 test_run_config.log_path = self.tmp_dir 125 126 test_class = VersionedTestClass(test_run_config) 127 test_class.run(['test_1', 'test_2']) 128 129 self.assertIn('Executed 2', test_class.results.summary_str(), 130 'One or more of the test cases did not execute.') 131 self.assertEqual( 132 'UUID_1', 133 test_class.results.executed[0].extras['test_tracker_uuid'], 134 'The test_tracker_uuid was not found for test_1.') 135 self.assertEqual( 136 'UUID_2', 137 test_class.results.executed[1].extras['test_tracker_uuid'], 138 'The test_tracker_uuid was not found for test_2.') 139 140 def test_raises_syntax_error_if_decorated_with_staticmethod_first(self): 141 try: 142 143 class SomeClass(object): 144 @versioning_decorator(1, 1) 145 @staticmethod 146 def test_1(): 147 pass 148 except SyntaxError: 149 pass 150 else: 151 self.fail('Placing the @staticmethod decorator after the ' 152 'versioning decorator should cause a SyntaxError.') 153 154 def test_raises_syntax_error_if_decorated_with_classmethod_first(self): 155 try: 156 157 class SomeClass(object): 158 @versioning_decorator(1, 1) 159 @classmethod 160 def test_1(cls): 161 pass 162 except SyntaxError: 163 pass 164 else: 165 self.fail('Placing the @classmethod decorator after the ' 166 'versioning decorator should cause a SyntaxError.') 167 168 def test_overriding_an_undecorated_func_raises_a_syntax_error(self): 169 try: 170 171 class SomeClass(object): 172 def test_1(self): 173 pass 174 175 @versioning_decorator(1, 1) 176 def test_1(self): 177 pass 178 except SyntaxError: 179 pass 180 else: 181 self.fail('Overwriting a function that already exists without a ' 182 'versioning decorator should raise a SyntaxError.') 183 184 def test_func_decorated_with_2_different_versioning_decorators_causes_error( 185 self): 186 try: 187 188 class SomeClass(object): 189 @versioning_decorator(1, 1) 190 def test_1(self): 191 pass 192 193 @versioning_decorator2(2, 2) 194 def test_1(self): 195 pass 196 except SyntaxError: 197 pass 198 else: 199 self.fail('Using two different versioning decorators to version a ' 200 'single function should raise a SyntaxError.') 201 202 def test_func_decorated_with_overlapping_ranges_causes_value_error(self): 203 try: 204 205 class SomeClass(object): 206 @versioning_decorator(1, 2) 207 def test_1(self): 208 pass 209 210 @versioning_decorator(2, 2) 211 def test_1(self): 212 pass 213 except ValueError: 214 pass 215 else: 216 self.fail('Decorated functions with overlapping version ranges ' 217 'should raise a ValueError.') 218 219 def test_func_decorated_with_min_gt_max_causes_value_error(self): 220 try: 221 222 class SomeClass(object): 223 @versioning_decorator(2, 1) 224 def test_1(self): 225 pass 226 except ValueError: 227 pass 228 else: 229 self.fail( 230 'If the min_version level is higher than the max_version ' 231 'level, a ValueError should be raised.') 232 233 def test_calling_versioned_func_on_min_version_level_is_inclusive(self): 234 """Tests that calling some versioned function with the minimum version 235 level of the decorated function will call that function.""" 236 ret = versioned_func(1, 'some_value') 237 self.assertEqual( 238 ret, ('function 1', 1, 'some_value'), 239 'Calling versioned_func(1, ...) did not return the ' 240 'versioned function for the correct range.') 241 242 def test_calling_versioned_func_on_middle_level_works(self): 243 """Tests that calling some versioned function a version value within the 244 range of the decorated function will call that function.""" 245 ret = versioned_func(16, 'some_value') 246 self.assertEqual( 247 ret, ('function 3', 16, 'some_value'), 248 'Calling versioned_func(16, ...) did not return the ' 249 'versioned function for the correct range.') 250 251 def test_calling_versioned_func_on_max_version_level_is_inclusive(self): 252 """Tests that calling some versioned function with the maximum version 253 level of the decorated function will call that function.""" 254 ret = versioned_func(10, 'some_value') 255 self.assertEqual( 256 ret, ('function 1', 10, 'some_value'), 257 'Calling versioned_func(10, ...) did not return the ' 258 'versioned function for the correct range.') 259 260 def test_calling_versioned_func_on_min_equals_max_level_works(self): 261 """Tests that calling some versioned function with the maximum version 262 level of the decorated function will call that function.""" 263 ret = versioned_func(11, 'some_value') 264 self.assertEqual( 265 ret, ('function 2', 11, 'some_value'), 266 'Calling versioned_func(10, ...) did not return the ' 267 'versioned function for the correct range.') 268 269 def test_sending_kwargs_through_decorated_functions_works(self): 270 """Tests that calling some versioned function with the maximum version 271 level of the decorated function will call that function.""" 272 ret = versioned_func_with_kwargs(1, asdf='some_value') 273 self.assertEqual( 274 ret, 'some_value', 275 'Calling versioned_func_with_kwargs(1, ...) did not' 276 'return the kwarg value properly.') 277 278 def test_kwargs_can_default_through_decorated_functions(self): 279 """Tests that calling some versioned function with the maximum version 280 level of the decorated function will call that function.""" 281 ret = versioned_func_with_kwargs(1) 282 self.assertEqual( 283 ret, 'jkl', 'Calling versioned_func_with_kwargs(1) did not' 284 'return the default kwarg value properly.') 285 286 def test_staticmethod_can_be_called_properly(self): 287 """Tests that decorating a staticmethod will properly send the arguments 288 in the correct order. 289 290 i.e., we want to make sure self or cls do not get sent as the first 291 argument to the decorated staticmethod. 292 """ 293 versioned_class = VersionedClass() 294 ret = versioned_class.static_func(123456) 295 self.assertEqual( 296 ret, 123456, 'The first argument was not set properly for calling ' 297 'a staticmethod.') 298 299 def test_instance_method_can_be_called_properly(self): 300 """Tests that decorating a method will properly send the arguments 301 in the correct order. 302 303 i.e., we want to make sure self is the first argument returned. 304 """ 305 versioned_class = VersionedClass() 306 ret = versioned_class.instance_func(123456) 307 self.assertEqual( 308 ret, (versioned_class, 123456), 309 'The arguments were not set properly for an instance ' 310 'method.') 311 312 def test_classmethod_can_be_called_properly(self): 313 """Tests that decorating a classmethod will properly send the arguments 314 in the correct order. 315 316 i.e., we want to make sure cls is the first argument returned. 317 """ 318 versioned_class = VersionedClass() 319 ret = versioned_class.class_func(123456) 320 self.assertEqual( 321 ret, (VersionedClass, 123456), 322 'The arguments were not set properly for a ' 323 'classmethod.') 324 325 326if __name__ == '__main__': 327 unittest.main() 328