From 52b36404c2e7e2089109646d0e085f5b369edd91 Mon Sep 17 00:00:00 2001 From: UnDarkle Date: Mon, 12 Aug 2019 11:29:48 +0300 Subject: [PATCH 1/4] Added resource adapter and uts --- setup.py | 2 +- src/rotest/management/base_resource.py | 44 ++++++++++++-- src/rotest/management/client/manager.py | 3 +- tests/core/test_case.py | 81 ++++++++++++++++++++++++- 4 files changed, 121 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index 2f458abe..fd8a4132 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup, find_packages -__version__ = "7.9.2" +__version__ = "7.10.0" result_handlers = [ "db = rotest.core.result.handlers.db_handler:DBHandler", diff --git a/src/rotest/management/base_resource.py b/src/rotest/management/base_resource.py index 0af6c72e..58c80eaa 100644 --- a/src/rotest/management/base_resource.py +++ b/src/rotest/management/base_resource.py @@ -55,12 +55,44 @@ def __eq__(self, oth): def __repr__(self): """Return a string representing the request.""" - return "Request %r of type %r (kwargs=%r)" % (self.name, self.type, - self.kwargs) + return "%s %r of type %r (kwargs=%r)" % (self.__class__.__name__, + self.name, + self.type, + self.kwargs) - def clone(self): - """Create a copy of the request.""" - return ResourceRequest(self.name, self.type, **self.kwargs) + def get_type(self, config): + """Get the requested resource class. + + Args: + config (dict): the configuration file being used. + """ + return self.type + + +class ResourceAdapter(ResourceRequest): + + """Holds the data for a resource request. + + Attributes: + resource_name (str): attribute name to be assigned. + resource_class (type): resource type. + force_initialize (bool): a flag to determine if the resources will be + initialized even if their validation succeeds. + kwargs (dict): requested resource arguments. + """ + + def __init__(self, config_key, resource_classes, **kwargs): + """Initialize the required parameters of resource request.""" + super(ResourceAdapter, self).__init__(None, resource_classes, **kwargs) + self.config_key = config_key + + def get_type(self, config): + """Get the requested resource class. + + Args: + config (dict): the configuration file being used. + """ + return self.type[config.get(self.config_key)] class ExceptionCatchingThread(Thread): @@ -158,7 +190,7 @@ class fields, where the 'data' attribute in the declaration points sub_resources = [] for sub_name, sub_request in get_class_fields(self.__class__, ResourceRequest): - sub_class = sub_request.type + sub_class = sub_request.get_type(self.config) actual_kwargs = sub_request.kwargs.copy() actual_kwargs['config'] = self.config actual_kwargs['base_work_dir'] = self.work_dir diff --git a/src/rotest/management/client/manager.py b/src/rotest/management/client/manager.py index 4de647ec..088f3717 100644 --- a/src/rotest/management/client/manager.py +++ b/src/rotest/management/client/manager.py @@ -384,7 +384,8 @@ def request_resources(self, requests, ServerError. resource manager failed to lock resources. """ requests = list(requests) - descriptors = [ResourceDescriptor(request.type, **request.kwargs) + descriptors = [ResourceDescriptor(request.get_type(config), + **request.kwargs) for request in requests] initialized_resources = AttrDict() diff --git a/tests/core/test_case.py b/tests/core/test_case.py index e31e0ead..f68d2fb9 100644 --- a/tests/core/test_case.py +++ b/tests/core/test_case.py @@ -12,9 +12,11 @@ from rotest.core.case import request from rotest.core.models.case_data import TestOutcome, CaseData +from rotest.management.base_resource import ResourceAdapter from rotest.management.models.ut_models import DemoResourceData from rotest.management.models.ut_resources import (DemoResource, - NonExistingResource) + NonExistingResource, + DemoComplexResource) from tests.core.utils import (ErrorInSetupCase, SuccessCase, FailureCase, ErrorCase, StoreMultipleFailuresCase, @@ -49,6 +51,26 @@ class TempComplexRequestCase(SuccessCase): res2 = DemoResource.request(name='available_resource2') +class TempAdaptiveRequestPositiveCase(SuccessCase): + """Inherit class and override resources requests.""" + __test__ = False + + res = ResourceAdapter(config_key='skip_init', + resource_classes={False: DemoResource, + True: DemoComplexResource}, + name='available_resource1') + + +class TempAdaptiveRequestNegativeCase(SuccessCase): + """Inherit class and override resources requests.""" + __test__ = False + + res = ResourceAdapter(config_key='skip_init', + resource_classes={True: DemoResource, + False: DemoComplexResource}, + name='available_resource1') + + class TempInheritRequestCase(TempComplexRequestCase): """Inherit one resource requests from the parent class.""" __test__ = False @@ -284,6 +306,63 @@ def test_inherit_resource_request(self): "Test doesn't contain field named %s" % request_name) + def test_adaptive_request_positive(self): + """Test a positive TestCase with a resource adapter. + + * Using the kwargs, ask for an existing resource. + """ + case = self._run_case(TempAdaptiveRequestPositiveCase) + + self.assertTrue(self.result.wasSuccessful(), + 'Case failed when it should have succeeded') + + # === Validate case data object === + self.assertTrue(case.data.success) + + test_resources = case.all_resources + locked_names = [] + for resource in six.itervalues(test_resources): + self.assertTrue(isinstance(resource, DemoResource), + "Got wrong resource %r for the request" % resource) + + test_resource = DemoResourceData.objects.get(name=resource.name) + self.validate_resource(test_resource) + locked_names.append(resource.name) + + self.assertEqual(len(test_resources), 1, + "Unexpected number of resources, expected %r got %r" % + (1, len(test_resources))) + + request_name = 'res' + self.assertTrue(request_name in test_resources, + "Test didn't request a resource for %s" % + request_name) + + self.assertTrue(request_name in case.__dict__, + "Test doesn't contain field named %s" % + request_name) + + self.assertIn('available_resource1', locked_names, + "Resource request using 'resources' ignored kwargs") + + def test_adaptive_request_negative(self): + """Test a negative TestCase with a resource adapter. + + * Using the kwargs, ask for a non existing resource. + """ + case = self._run_case(TempAdaptiveRequestNegativeCase) + + self.assertFalse(self.result.wasSuccessful(), + 'Case failed when it should have succeeded') + + # === Validate case data object === + self.assertFalse(case.data.success) + + test_resources = case.all_resources + self.assertEqual(len(test_resources), 0, + "Unexpected number of resources, expected %r got %r" % + (0, len(test_resources))) + def test_dynamic_resources_locking(self): """Test that cases can dynamically lock resources. From 76e942c35ec1531cfeb0815fc1ed523265910290 Mon Sep 17 00:00:00 2001 From: UnDarkle Date: Mon, 12 Aug 2019 11:35:24 +0300 Subject: [PATCH 2/4] fixed by tox --- src/rotest/management/base_resource.py | 2 +- tests/core/test_case.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rotest/management/base_resource.py b/src/rotest/management/base_resource.py index 58c80eaa..e4afc485 100644 --- a/src/rotest/management/base_resource.py +++ b/src/rotest/management/base_resource.py @@ -4,7 +4,7 @@ responsible for the resource static & dynamic information. """ # pylint: disable=too-many-instance-attributes,no-self-use,broad-except -# pylint: disable=protected-access +# pylint: disable=protected-access,unused-argument from __future__ import absolute_import import sys diff --git a/tests/core/test_case.py b/tests/core/test_case.py index f68d2fb9..441839b0 100644 --- a/tests/core/test_case.py +++ b/tests/core/test_case.py @@ -1,6 +1,7 @@ """Test Rotest's TestCase class behavior.""" # pylint: disable=missing-docstring,unused-argument,protected-access # pylint: disable=no-member,no-self-use,too-many-public-methods,invalid-name +# pylint: disable=too-many-lines from __future__ import absolute_import import os @@ -11,8 +12,8 @@ from future.utils import iteritems from rotest.core.case import request -from rotest.core.models.case_data import TestOutcome, CaseData from rotest.management.base_resource import ResourceAdapter +from rotest.core.models.case_data import TestOutcome, CaseData from rotest.management.models.ut_models import DemoResourceData from rotest.management.models.ut_resources import (DemoResource, NonExistingResource, From 997c1edf8310672be5e0c8f2f44f7d3c103f1d71 Mon Sep 17 00:00:00 2001 From: UnDarkle Date: Mon, 12 Aug 2019 11:54:37 +0300 Subject: [PATCH 3/4] fixed uts --- tests/core/test_case.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/core/test_case.py b/tests/core/test_case.py index 441839b0..1dcf647a 100644 --- a/tests/core/test_case.py +++ b/tests/core/test_case.py @@ -56,7 +56,8 @@ class TempAdaptiveRequestPositiveCase(SuccessCase): """Inherit class and override resources requests.""" __test__ = False - res = ResourceAdapter(config_key='skip_init', + resources = () + res = ResourceAdapter(config_key='field1', resource_classes={False: DemoResource, True: DemoComplexResource}, name='available_resource1') @@ -66,7 +67,8 @@ class TempAdaptiveRequestNegativeCase(SuccessCase): """Inherit class and override resources requests.""" __test__ = False - res = ResourceAdapter(config_key='skip_init', + resources = () + res = ResourceAdapter(config_key='field1', resource_classes={True: DemoResource, False: DemoComplexResource}, name='available_resource1') @@ -312,7 +314,8 @@ def test_adaptive_request_positive(self): * Using the kwargs, ask for an existing resource. """ - case = self._run_case(TempAdaptiveRequestPositiveCase) + case = self._run_case(TempAdaptiveRequestPositiveCase, + config={'field1': False}) self.assertTrue(self.result.wasSuccessful(), 'Case failed when it should have succeeded') @@ -351,7 +354,8 @@ def test_adaptive_request_negative(self): * Using the kwargs, ask for a non existing resource. """ - case = self._run_case(TempAdaptiveRequestNegativeCase) + case = self._run_case(TempAdaptiveRequestNegativeCase, + config={'field1': False}) self.assertFalse(self.result.wasSuccessful(), 'Case failed when it should have succeeded') From a687e382d8b74a4febddff50b30713bf408aec28 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 12 Aug 2019 15:55:28 +0300 Subject: [PATCH 4/4] added ut for sub-adaptive-resources --- src/rotest/management/__init__.py | 5 ++- src/rotest/management/models/ut_resources.py | 22 ++++++++++++- tests/core/test_case.py | 34 +++++++++++++++++++- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/rotest/management/__init__.py b/src/rotest/management/__init__.py index d5ce12d6..fb88fc5a 100644 --- a/src/rotest/management/__init__.py +++ b/src/rotest/management/__init__.py @@ -7,12 +7,15 @@ class ManagementConfig(AppConfig): def ready(self): from .models import ResourceData from .client.manager import ClientResourceManager - from .base_resource import BaseResource, ResourceRequest + from .base_resource import (BaseResource, + ResourceAdapter, + ResourceRequest) import rotest rotest.management.ResourceData = ResourceData rotest.management.ClientResourceManager = ClientResourceManager rotest.management.BaseResource = BaseResource + rotest.management.ResourceAdapter = ResourceAdapter rotest.management.ResourceRequest = ResourceRequest diff --git a/src/rotest/management/models/ut_resources.py b/src/rotest/management/models/ut_resources.py index 92647874..f32ce413 100644 --- a/src/rotest/management/models/ut_resources.py +++ b/src/rotest/management/models/ut_resources.py @@ -5,7 +5,7 @@ import os import shutil -from rotest.management.base_resource import BaseResource +from rotest.management.base_resource import BaseResource, ResourceAdapter from .ut_models import ResourceData, DemoResourceData, DemoComplexResourceData @@ -158,3 +158,23 @@ def initialize(self): class DemoService(BaseResource): """Fake service class, used in resource manager tests.""" DATA_CLASS = None + + +class DemoResource2(DemoResource): + """A demo resource class similar to DemoResource.""" + + +class DemoAdaptiveComplexResource(BaseResource): + """Fake complex resource class, used in resource manager tests. + + Attributes: + sub_res1 (DemoResource): sub resource pointer. + sub_res2 (DemoResource / DemoResource2): sub resource pointer. + """ + DATA_CLASS = DemoComplexResourceData + + sub_res1 = DemoResource2.request(data=DATA_CLASS.demo1) + sub_res2 = ResourceAdapter(config_key='field1', + resource_classes={True: DemoResource, + False: DemoResource2}, + data=DATA_CLASS.demo2) diff --git a/tests/core/test_case.py b/tests/core/test_case.py index 1dcf647a..fd1c7af5 100644 --- a/tests/core/test_case.py +++ b/tests/core/test_case.py @@ -16,8 +16,10 @@ from rotest.core.models.case_data import TestOutcome, CaseData from rotest.management.models.ut_models import DemoResourceData from rotest.management.models.ut_resources import (DemoResource, + DemoResource2, NonExistingResource, - DemoComplexResource) + DemoComplexResource, + DemoAdaptiveComplexResource) from tests.core.utils import (ErrorInSetupCase, SuccessCase, FailureCase, ErrorCase, StoreMultipleFailuresCase, @@ -52,6 +54,13 @@ class TempComplexRequestCase(SuccessCase): res2 = DemoResource.request(name='available_resource2') +class TempComplexAdaptiveResourceCase(SuccessCase): + """Inherit class and override resources requests.""" + __test__ = False + + resources = (request('res1', DemoAdaptiveComplexResource),) + + class TempAdaptiveRequestPositiveCase(SuccessCase): """Inherit class and override resources requests.""" __test__ = False @@ -286,6 +295,29 @@ def test_complex_resource_request(self): self.assertIn('available_resource2', locked_names, "Resource request using class field ignored kwargs") + def test_complex_adaptive_resource_request(self): + """Test a TestCase that requests a complex adaptive resources.""" + case = self._run_case(TempComplexAdaptiveResourceCase, + config={'field1': False}) + + self.assertTrue(self.result.wasSuccessful(), + 'Case failed when it should have succeeded') + + # === Validate case data object === + self.assertTrue(case.data.success) + + test_resources = case.all_resources + + self.assertEqual(len(test_resources), 1, + "Unexpected number of resources, expected %r got %r" % + (1, len(test_resources))) + + resource, = list(test_resources.values()) + self.assertTrue(isinstance(resource.sub_res1, DemoResource2), + "Got wrong resource %r for sub_res1" % resource) + self.assertTrue(isinstance(resource.sub_res2, DemoResource2), + "Got wrong resource %r for sub_res2" % resource) + def test_inherit_resource_request(self): """Test a TestCase that inherits its resource request.""" case = self._run_case(TempInheritRequestCase)