Skip to content

Commit

Permalink
Merge 0cb19e8 into 5c01976
Browse files Browse the repository at this point in the history
  • Loading branch information
UnDarkle committed Oct 16, 2018
2 parents 5c01976 + 0cb19e8 commit c46fe49
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 52 deletions.
2 changes: 1 addition & 1 deletion setup.py
@@ -1,7 +1,7 @@
"""Setup file for handling packaging and distribution."""
from setuptools import setup, find_packages

__version__ = "4.2.2"
__version__ = "4.3.0"

result_handlers = [
"db = rotest.core.result.handlers.db_handler:DBHandler",
Expand Down
28 changes: 28 additions & 0 deletions src/rotest/common/utils.py
Expand Up @@ -72,3 +72,31 @@ def get_work_dir(base_dir, test_name, test_item):

os.makedirs(work_dir)
return work_dir


def get_class_fields(cls, field_type):
"""Get all fields of the class that inherit from the given type.
* This method searches also in the parent classes.
* Fields of sons override fields of parents.
* Ignores fields starting with '_'.
Args:
cls (type): class to search.
field_type (type): field type to find.
Yields:
tuple. all found (field name, field value).
"""
if cls is object:
return

for parent_class in cls.__bases__:
for (field_name, field) in get_class_fields(parent_class, field_type):
yield (field_name, field)

for field_name in cls.__dict__:
if not field_name.startswith("_"):
field = getattr(cls, field_name)
if isinstance(field, field_type):
yield (field_name, field)
20 changes: 2 additions & 18 deletions src/rotest/core/abstract_test.py
Expand Up @@ -14,6 +14,7 @@
from ipdbugger import debug
from attrdict import AttrDict

from rotest.common.utils import get_class_fields
from rotest.core.models.case_data import TestOutcome
from rotest.management.base_resource import BaseResource
from rotest.management.client.manager import ResourceRequest
Expand Down Expand Up @@ -102,23 +103,6 @@ def release_resource_loggers(self):
for resource in self.all_resources.itervalues():
resource.release_logger(self.logger)

@classmethod
def get_resource_requests_fields(cls):
"""Yield tuples of all the resource request fields of this test.
Yields:
tuple. (requests name, request field) tuples of the test class.
"""
checked_class = cls
while checked_class is not AbstractTest:
for field_name in checked_class.__dict__:
if not field_name.startswith("_"):
field = getattr(checked_class, field_name)
if isinstance(field, BaseResource):
yield (field_name, field)

checked_class = checked_class.__bases__[0]

@classmethod
def get_resource_requests(cls):
"""Return a list of all the resource requests this test makes.
Expand All @@ -131,7 +115,7 @@ def get_resource_requests(cls):
list. resource requests of the test class.
"""
all_requests = list(cls.resources)
for (field_name, field) in cls.get_resource_requests_fields():
for (field_name, field) in get_class_fields(cls, BaseResource):
new_request = request(field_name,
field.__class__,
**field.kwargs)
Expand Down
28 changes: 5 additions & 23 deletions src/rotest/core/block.py
Expand Up @@ -2,6 +2,7 @@
# pylint: disable=dangerous-default-value,too-many-arguments
from itertools import count

from rotest.common.utils import get_class_fields
from rotest.common.config import ROTEST_WORK_DIR
from rotest.core.flow_component import (AbstractFlowComponent, MODE_OPTIONAL,
MODE_FINALLY, MODE_CRITICAL,
Expand Down Expand Up @@ -105,40 +106,21 @@ def get_name(cls):

@classmethod
def get_inputs(cls):
"""Return a list of all the input instances of this block.
"""Return a dict of all the input instances of this block.
Returns:
dict. block's inputs (name: input placeholder instance).
"""
all_inputs = {}
checked_class = cls
while checked_class is not TestBlock:
all_inputs.update({key: value for (key, value) in
checked_class.__dict__.iteritems()
if (isinstance(value, BlockInput) and
key not in all_inputs)})

checked_class = checked_class.__bases__[0]

return all_inputs
return dict(get_class_fields(cls, BlockInput))

@classmethod
def get_outputs(cls):
"""Return a list of all the input instances of this block.
"""Return a dict of all the input instances of this block.
Returns:
dict. block's inputs (name: input placeholder instance).
"""
all_outputs = {}
checked_class = cls
while checked_class is not TestBlock:
all_outputs.update({key: value for (key, value) in
checked_class.__dict__.iteritems()
if isinstance(value, BlockOutput)})

checked_class = checked_class.__bases__[0]

return all_outputs
return dict(get_class_fields(cls, BlockOutput))

def _share_outputs(self):
"""Share all the declared outputs of the block."""
Expand Down
6 changes: 3 additions & 3 deletions src/rotest/management/__init__.py
@@ -1,3 +1,3 @@
from models import ResourceData
from base_resource import BaseResource
from client.manager import ClientResourceManager
from models import ResourceData
from base_resource import BaseResource
from client.manager import ClientResourceManager
29 changes: 25 additions & 4 deletions src/rotest/management/base_resource.py
Expand Up @@ -10,7 +10,8 @@
from attrdict import AttrDict

from rotest.common import core_log
from rotest.common.utils import get_work_dir
from rotest.common.utils import get_work_dir, get_class_fields
from rotest.management.models.resource_data import ResourceData, DataPointer


class ConvertToKwargsMeta(type):
Expand Down Expand Up @@ -77,8 +78,9 @@ def __init__(self, data=None, **kwargs):

if data is not None:
self.data = data
for field_name, field_value in self.data.get_fields().iteritems():
setattr(self, field_name, field_value)
if isinstance(data, ResourceData):
for field_name, field_value in data.get_fields().iteritems():
setattr(self, field_name, field_value)

else:
self.data = AttrDict()
Expand All @@ -95,13 +97,32 @@ def __init__(self, data=None, **kwargs):
def create_sub_resources(self):
"""Create and return the sub resources if needed.
By default, this method searches for sub-resources declared as
class fields, where the 'data' attribute in the declaration points
to the name of the sub-resource's data field under the current's data.
Override and assign sub-resources to fields in the current resource,
using the 'data' object.
Returns:
iterable. sub-resources created.
"""
return ()
sub_resources = []
for sub_name, sub_placeholder in get_class_fields(self.__class__,
BaseResource):

sub_class = sub_placeholder.__class__
actual_kwargs = sub_placeholder.kwargs.copy()
for key, value in sub_placeholder.kwargs.iteritems():
if isinstance(value, DataPointer):
actual_kwargs[key] = getattr(self.data, value.field_name)

sub_resource = sub_class(**actual_kwargs)

setattr(self, sub_name, sub_resource)
sub_resources.append(sub_resource)

return sub_resources

def is_available(self, user_name=""):
"""Return whether resource is available for the given user.
Expand Down
19 changes: 18 additions & 1 deletion src/rotest/management/models/resource_data.py
Expand Up @@ -9,6 +9,8 @@
from datetime import datetime

from django.db import models
from django.utils import six
from django.db.models.base import ModelBase
from django.contrib.auth import models as auth_models
from django.core.exceptions import ObjectDoesNotExist, ValidationError

Expand All @@ -17,7 +19,22 @@
from rotest.common.django_utils import get_sub_model, linked_unicode


class ResourceData(models.Model):
class DataPointer(object):
"""Pointer to a field in the resource's data."""
def __init__(self, field_name):
self.field_name = field_name


class DataBase(ModelBase):
"""Metaclass that creates data pointers for django fields."""
def __getattr__(cls, key):
if hasattr(cls, '_meta') and key in cls._meta.get_all_field_names():
return DataPointer(key)

raise AttributeError(key)


class ResourceData(six.with_metaclass(DataBase, models.Model)):
"""Represent a container for a resource's global data.
Inheriting resource datas may add more fields, specific to the resource.
Expand Down

0 comments on commit c46fe49

Please sign in to comment.