Skip to content

Commit

Permalink
OperationAttributes testing expanded & code optimized
Browse files Browse the repository at this point in the history
  • Loading branch information
lane-neuro committed Aug 12, 2024
1 parent 743c4a4 commit a2c90bb
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,36 @@ class OperationAttributes:
def __init__(self, *args, **kwargs):
self.temp_kwargs = {}

if not hasattr(self, '_initialized'):
if args and isinstance(args[0], dict):
self.temp_kwargs.update(args[0])
self.temp_kwargs.update(kwargs)

from research_analytics_suite.utils import CustomLogger
self._logger = CustomLogger()

self.name = self.temp_kwargs.get('name', args[0] if len(args) > 0 else None)
self.version = self.temp_kwargs.get('version', args[1] if len(args) > 1 else None)
self.description = self.temp_kwargs.get('description', args[2] if len(args) > 2 else None)
self.category_id = self.temp_kwargs.get('category_id', args[3] if len(args) > 3 else None)
self.author = self.temp_kwargs.get('author', args[4] if len(args) > 4 else None)
self.github = self.temp_kwargs.get('github', args[5] if len(args) > 5 else None)
self.email = self.temp_kwargs.get('email', args[6] if len(args) > 6 else None)
self.action = self.temp_kwargs.get('action', args[7] if len(args) > 7 else None)
self.required_inputs = self.temp_kwargs.get('required_inputs', args[8] if len(args) > 8 else {})
self.parent_operation = self.temp_kwargs.get('parent_operation', args[9] if len(args) > 9 else None)
self.inheritance = self.temp_kwargs.get('inheritance', args[10] if len(args) > 10 else [])
self.is_loop = self.temp_kwargs.get('is_loop', args[11] if len(args) > 11 else False)
self.is_cpu_bound = self.temp_kwargs.get('is_cpu_bound', args[12] if len(args) > 12 else False)
self.is_gpu_bound = self.temp_kwargs.get('is_gpu_bound', args[13] if len(args) > 13 else False)
self.parallel = self.temp_kwargs.get('parallel', args[14] if len(args) > 14 else False)

self._initialized = False
if args and isinstance(args[0], dict):
self.temp_kwargs.update(args[0])
self.temp_kwargs.update(kwargs)

from research_analytics_suite.utils import CustomLogger
self._logger = CustomLogger()

# Use kwargs to directly initialize attributes, with defaults
self.name = self.temp_kwargs.get('name', '[no-name]')
self.version = self.temp_kwargs.get('version', '0.0.1')
self.description = self.temp_kwargs.get('description', '[no-description]')
self.category_id = self.temp_kwargs.get('category_id', -1)
self.author = self.temp_kwargs.get('author', '[no-author]')
self.github = self.temp_kwargs.get('github', '[no-github]')
self.email = self.temp_kwargs.get('email', '[no-email]')
self.action = self.temp_kwargs.get('action', None)
self.required_inputs = self.temp_kwargs.get('required_inputs', {})
self.parent_operation = self.temp_kwargs.get('parent_operation', None)
self.inheritance = self.temp_kwargs.get('inheritance', [])
self.is_loop = self.temp_kwargs.get('is_loop', False)
self.is_cpu_bound = self.temp_kwargs.get('is_cpu_bound', False)
self.is_gpu_bound = self.temp_kwargs.get('is_gpu_bound', False)
self.parallel = self.temp_kwargs.get('parallel', False)

# Handle any extra attributes not predefined
for key, value in self.temp_kwargs.items():
if not hasattr(self, key):
setattr(self, key, value)

self._initialized = False

async def initialize(self):
if not self._initialized:
Expand Down Expand Up @@ -98,14 +103,10 @@ def _process_required_inputs(self, inputs: dict) -> dict:
self._logger.error(ValueError("Expected a dictionary as input"), self.__class__.__name__)
return {}

processed_dict = {}
for k, v in inputs.items():
if isinstance(v, str):
processed_dict[k] = self._TYPES_DICT.get(v.lower(), getattr(v, '__name__', v))
else:
processed_dict[k] = getattr(v, '__name__', v)

return processed_dict
return {
k: self._TYPES_DICT.get(v.lower(), v) if isinstance(v, str) else v
for k, v in inputs.items()
}

@command
def export_attributes(self) -> dict:
Expand Down
200 changes: 200 additions & 0 deletions tests/operation_manager/operations/core/test_operation_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,36 @@ async def test_initialization_with_defaults(self, mock_logger):
assert attrs.is_gpu_bound is False
assert attrs.parallel is False

@pytest.mark.asyncio
async def test_initialization_mixed_args_kwargs(self, mock_logger):
# Mixed args and kwargs provided, kwargs should take precedence
attrs = OperationAttributes(
{'name': 'arg_name', 'version': '0.0.1', 'description': 'arg_description'},
name='kwarg_name', version='0.0.2', description='kwarg_description',
category_id=2, author='kwarg_author', github='@kwarg_github',
email='kwarg@example.com', action='kwarg_action', required_inputs={'input1': 'str'},
parent_operation=None, inheritance=['kwarg_inheritance'], is_loop=True,
is_cpu_bound=True, is_gpu_bound=False, parallel=False
)
await attrs.initialize()

# Assertions to ensure kwargs take precedence over args
assert attrs.name == 'kwarg_name'
assert attrs.version == '0.0.2'
assert attrs.description == 'kwarg_description'
assert attrs.category_id == 2
assert attrs.author == 'kwarg_author'
assert attrs.github == 'kwarg_github'
assert attrs.email == 'kwarg@example.com'
assert attrs.action == 'kwarg_action'
assert attrs.required_inputs == {'input1': str}
assert attrs.parent_operation is None
assert attrs.inheritance == ['kwarg_inheritance']
assert attrs.is_loop is True
assert attrs.is_cpu_bound is True
assert attrs.is_gpu_bound is False
assert attrs.parallel is False

@pytest.mark.asyncio
async def test_initialization_priority_kwargs_over_args(self, mock_logger):
attrs = OperationAttributes(
Expand All @@ -106,6 +136,97 @@ async def test_initialization_priority_args_over_none(self, mock_logger):

assert attrs.name == 'arg_name'

@pytest.mark.asyncio
async def test_initialization_partial_overlap_args_kwargs(self, mock_logger):
attrs = OperationAttributes(
{'name': 'arg_name', 'version': '0.0.1'},
name='kwarg_name', description='kwarg_description',
category_id=2, author='kwarg_author'
)
await attrs.initialize()

assert attrs.name == 'kwarg_name' # kwarg should override arg
assert attrs.version == '0.0.1' # no kwarg provided, so arg is used
assert attrs.description == 'kwarg_description'
assert attrs.category_id == 2
assert attrs.author == 'kwarg_author'

@pytest.mark.asyncio
async def test_initialization_empty_args_valid_kwargs(self, mock_logger):
attrs = OperationAttributes(
name='kwarg_name', version='0.0.2', description='kwarg_description',
category_id=2, author='kwarg_author', github='@kwarg_github',
email='kwarg@example.com', action='kwarg_action', required_inputs={'input1': 'str'},
parent_operation=None, inheritance=['kwarg_inheritance'], is_loop=True,
is_cpu_bound=True, is_gpu_bound=False, parallel=False
)
await attrs.initialize()

assert attrs.name == 'kwarg_name'
assert attrs.version == '0.0.2'
assert attrs.description == 'kwarg_description'
assert attrs.category_id == 2
assert attrs.author == 'kwarg_author'
assert attrs.github == 'kwarg_github'
assert attrs.email == 'kwarg@example.com'
assert attrs.action == 'kwarg_action'
assert attrs.required_inputs == {'input1': str}
assert attrs.parent_operation is None
assert attrs.inheritance == ['kwarg_inheritance']
assert attrs.is_loop is True
assert attrs.is_cpu_bound is True
assert attrs.is_gpu_bound is False
assert attrs.parallel is False

@pytest.mark.asyncio
async def test_initialization_invalid_args_valid_kwargs(self, mock_logger):
attrs = OperationAttributes(
{'name': 123, 'version': 456}, # Invalid types
name='kwarg_name', version='0.0.2', description='kwarg_description',
category_id=2, author='kwarg_author', github='@kwarg_github',
email='kwarg@example.com', action='kwarg_action', required_inputs={'input1': 'str'},
parent_operation=None, inheritance=['kwarg_inheritance'], is_loop=True,
is_cpu_bound=True, is_gpu_bound=False, parallel=False
)
await attrs.initialize()

assert attrs.name == 'kwarg_name'
assert attrs.version == '0.0.2'
assert attrs.description == 'kwarg_description'
assert attrs.category_id == 2
assert attrs.author == 'kwarg_author'
assert attrs.github == 'kwarg_github'
assert attrs.email == 'kwarg@example.com'
assert attrs.action == 'kwarg_action'
assert attrs.required_inputs == {'input1': str}
assert attrs.parent_operation is None
assert attrs.inheritance == ['kwarg_inheritance']
assert attrs.is_loop is True
assert attrs.is_cpu_bound is True
assert attrs.is_gpu_bound is False
assert attrs.parallel is False

@pytest.mark.asyncio
async def test_initialization_with_no_args_or_kwargs(self, mock_logger):
attrs = OperationAttributes()
await attrs.initialize()

assert attrs.name == '[no-name]'
assert attrs.version == '0.0.1'
assert attrs.description == '[no-description]'
assert attrs.category_id == -1
assert attrs.author == '[no-author]'
assert attrs.github == '[no-github]'
assert attrs.email == '[no-email]'
assert attrs.action is None
assert attrs.required_inputs == {}
assert attrs.parent_operation is None
assert attrs.inheritance == []
assert attrs.is_loop is False
assert attrs.is_cpu_bound is False
assert attrs.is_gpu_bound is False
assert attrs.parallel is False

@pytest.mark.asyncio
async def test_invalid_data_types(self, mock_logger):
attrs = OperationAttributes(
Expand Down Expand Up @@ -269,6 +390,25 @@ async def test_performance_initialization(self, mock_logger):
initialization_time = end_time - start_time
assert initialization_time < 1, f"Initialization took too long: {initialization_time} seconds"

@pytest.mark.asyncio
async def test_performance_mixed_args_kwargs_initialization(self, mock_logger):
start_time = time.time()

attrs = OperationAttributes(
{'name': 'arg_name', 'version': '0.0.1', 'description': 'arg_description'},
name='kwarg_name', version='0.0.2', description='kwarg_description',
category_id=2, author='kwarg_author', github='@kwarg_github',
email='kwarg@example.com', action='kwarg_action',
required_inputs={f'input_{i}': 'str' for i in range(100)},
parent_operation=None, inheritance=['kwarg_inheritance'], is_loop=True,
is_cpu_bound=True, is_gpu_bound=False, parallel=True
)
await attrs.initialize()

end_time = time.time()
initialization_time = end_time - start_time
assert initialization_time < 1, f"Initialization took too long: {initialization_time} seconds"

@pytest.mark.asyncio
async def test_modify_single_attribute_repeatedly(self, mock_logger):
attrs = OperationAttributes()
Expand Down Expand Up @@ -327,3 +467,63 @@ async def test_modify_all_attributes_simultaneously(self, mock_logger):
assert attrs.is_cpu_bound == bool(i % 2)
assert attrs.is_gpu_bound == bool((i + 1) % 2)
assert attrs.parallel == bool((i + 1) % 2)

@pytest.mark.asyncio
async def test_backward_compatibility(self, mock_logger):
# Simulate an older version usage
old_attrs = OperationAttributes(
name='old_name', version='0.0.1', description='old_description',
author='old_author'
)
await old_attrs.initialize()

assert old_attrs.name == 'old_name'
assert old_attrs.version == '0.0.1'
assert old_attrs.description == 'old_description'
assert old_attrs.author == 'old_author'

# Ensure defaults are still applied correctly for missing attributes
assert old_attrs.category_id == -1
assert old_attrs.github == '[no-github]'

@pytest.mark.asyncio
async def test_future_attribute_expansion(self, mock_logger):
# Hypothetical future attribute
future_attrs = OperationAttributes(
name='future_name', version='0.0.2', description='future_description',
category_id=2, author='future_author', github='@future_github',
email='future@example.com', action='future_action', required_inputs={'input1': 'str'},
parent_operation=None, inheritance=['future_inheritance'], is_loop=True,
is_cpu_bound=True, is_gpu_bound=False, parallel=False,
new_attribute='future_value' # Hypothetical future attribute
)
await future_attrs.initialize()

assert hasattr(future_attrs, 'new_attribute')
assert getattr(future_attrs, 'new_attribute', None) == 'future_value'

@pytest.mark.asyncio
async def test_versioning_support(self, mock_logger):
# Version 1.0.0 behavior
v1_attrs = OperationAttributes(version='1.0.0')
await v1_attrs.initialize()
assert v1_attrs.version == '1.0.0'
# Add specific checks for version 1.0.0

# Future version behavior
future_version = '2.0.0'
v2_attrs = OperationAttributes(version=future_version)
await v2_attrs.initialize()
assert v2_attrs.version == future_version
# Add specific checks for future version

@pytest.mark.asyncio
async def test_stress_large_number_of_attributes(self, mock_logger):
large_attrs = OperationAttributes(
**{f'attr_{i}': f'value_{i}' for i in range(1000)}
)
await large_attrs.initialize()

# Ensure all attributes are correctly initialized
for i in range(1000):
assert getattr(large_attrs, f'attr_{i}', None) == f'value_{i}'

0 comments on commit a2c90bb

Please sign in to comment.