asteex 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asteex-0.0.1/LICENSE +21 -0
- asteex-0.0.1/PKG-INFO +20 -0
- asteex-0.0.1/README_PYPI.rst +11 -0
- asteex-0.0.1/VERSION +1 -0
- asteex-0.0.1/pyproject.toml +19 -0
- asteex-0.0.1/setup.cfg +4 -0
- asteex-0.0.1/src/asteex/__init__.py +6 -0
- asteex-0.0.1/src/asteex/abstract/__init__.py +1 -0
- asteex-0.0.1/src/asteex/abstract/case.py +27 -0
- asteex-0.0.1/src/asteex/abstract/context.py +21 -0
- asteex-0.0.1/src/asteex/abstract/executor.py +26 -0
- asteex-0.0.1/src/asteex/abstract/generator.py +24 -0
- asteex-0.0.1/src/asteex/abstract/instance.py +39 -0
- asteex-0.0.1/src/asteex/abstract/reporter.py +24 -0
- asteex-0.0.1/src/asteex/abstract/result.py +58 -0
- asteex-0.0.1/src/asteex/core/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/case/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/case/executable/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/case/executable/abstract.py +21 -0
- asteex-0.0.1/src/asteex/core/case/executable/delegate.py +306 -0
- asteex-0.0.1/src/asteex/core/context/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/context/source/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/context/source/fixture.py +19 -0
- asteex-0.0.1/src/asteex/core/context/source/static.py +19 -0
- asteex-0.0.1/src/asteex/core/context/source/strategy.py +9 -0
- asteex-0.0.1/src/asteex/core/decorator/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/decorator/argument.py +311 -0
- asteex-0.0.1/src/asteex/core/executor/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/executor/executable.py +493 -0
- asteex-0.0.1/src/asteex/core/fixture/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/fixture/abstract.py +42 -0
- asteex-0.0.1/src/asteex/core/fixture/producer.py +404 -0
- asteex-0.0.1/src/asteex/core/generator/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/generator/group/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/generator/group/abstract.py +100 -0
- asteex-0.0.1/src/asteex/core/generator/group/module.py +45 -0
- asteex-0.0.1/src/asteex/core/instance/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/instance/static.py +55 -0
- asteex-0.0.1/src/asteex/core/reporter/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/reporter/composite.py +84 -0
- asteex-0.0.1/src/asteex/core/reporter/log.py +61 -0
- asteex-0.0.1/src/asteex/core/result/__init__.py +1 -0
- asteex-0.0.1/src/asteex/core/result/static.py +126 -0
- asteex-0.0.1/src/asteex/core/result/status.py +11 -0
- asteex-0.0.1/src/asteex/core/runner.py +55 -0
- asteex-0.0.1/src/asteex/runpackage/__init__.py +1 -0
- asteex-0.0.1/src/asteex/runpackage/__main__.py +40 -0
- asteex-0.0.1/src/asteex/runpackage/case.py +147 -0
- asteex-0.0.1/src/asteex/runpackage/cli.py +323 -0
- asteex-0.0.1/src/asteex/runpackage/default.py +54 -0
- asteex-0.0.1/src/asteex/runpackage/fixture.py +224 -0
- asteex-0.0.1/src/asteex/runpackage/generator.py +319 -0
- asteex-0.0.1/src/asteex/runpackage/reporter.py +230 -0
- asteex-0.0.1/src/asteex/runpackage/value.py +70 -0
- asteex-0.0.1/src/asteex.egg-info/PKG-INFO +20 -0
- asteex-0.0.1/src/asteex.egg-info/SOURCES.txt +57 -0
- asteex-0.0.1/src/asteex.egg-info/dependency_links.txt +1 -0
- asteex-0.0.1/src/asteex.egg-info/entry_points.txt +2 -0
- asteex-0.0.1/src/asteex.egg-info/top_level.txt +1 -0
asteex-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 KillAChicken
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
asteex-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: asteex
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Test framework for asynchronous test execution
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/x-rst
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Dynamic: license-file
|
|
9
|
+
|
|
10
|
+
asteex
|
|
11
|
+
======
|
|
12
|
+
|
|
13
|
+
asteex is a package for asyncronous test execution.
|
|
14
|
+
|
|
15
|
+
Installation
|
|
16
|
+
============
|
|
17
|
+
|
|
18
|
+
.. code-block:: text
|
|
19
|
+
|
|
20
|
+
$ python -m pip install asteex
|
asteex-0.0.1/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.1
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 82.0.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
dynamic = ["version"]
|
|
7
|
+
name = "asteex"
|
|
8
|
+
requires-python = ">=3.10"
|
|
9
|
+
description = "Test framework for asynchronous test execution"
|
|
10
|
+
readme = {file = "README_PYPI.rst", content-type = "text/x-rst"}
|
|
11
|
+
|
|
12
|
+
[project.entry-points."asteex.reporter_factory"]
|
|
13
|
+
"asteex.log" = "asteex.core.reporter.log:LogReporter"
|
|
14
|
+
|
|
15
|
+
[tool.setuptools.packages.find]
|
|
16
|
+
where = ["src"]
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.dynamic]
|
|
19
|
+
version = {file = "VERSION"}
|
asteex-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package with base and abstract classes of the asteex package."""
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Module for a base asteex test case."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Case(ABC):
|
|
7
|
+
"""Class for a asteex test case."""
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def get_id(self):
|
|
11
|
+
"""Get the unique identifier of the test case.
|
|
12
|
+
|
|
13
|
+
:returns: string id.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def get_dependencies(self):
|
|
18
|
+
"""Get names of the variables that must be present in the execution context.
|
|
19
|
+
|
|
20
|
+
:returns: iterable with string names.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __repr__(self):
|
|
24
|
+
class_name = self.__class__.__name__
|
|
25
|
+
case_id = self.get_id()
|
|
26
|
+
|
|
27
|
+
return f"{class_name}(case_id='{case_id}')"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Module for a base entities of a asteex context."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Source:
|
|
5
|
+
"""Class for a base source to provide a context value."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, strategy):
|
|
8
|
+
self.__strategy = str(strategy)
|
|
9
|
+
|
|
10
|
+
def get_strategy(self):
|
|
11
|
+
"""Get the strategy with which the value should be requested from the source.
|
|
12
|
+
|
|
13
|
+
:returns: string strategy.
|
|
14
|
+
"""
|
|
15
|
+
return self.__strategy
|
|
16
|
+
|
|
17
|
+
def __repr__(self):
|
|
18
|
+
class_name = self.__class__.__name__
|
|
19
|
+
strategy = self.__strategy
|
|
20
|
+
|
|
21
|
+
return f"{class_name}(strategy='{strategy}')"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Module for a base asteex executor."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ExecutionError(Exception):
|
|
7
|
+
"""Exception class for errors raised during the execution."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Executor(ABC):
|
|
11
|
+
"""Class for a base executor."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def execute(self, instances):
|
|
15
|
+
"""Execute test instances.
|
|
16
|
+
|
|
17
|
+
:param instances:
|
|
18
|
+
async iterable with :class:`Instances <asteex.abstract.instance.Instance>`.
|
|
19
|
+
:returns: async iterable with :class:`Results <asteex.abstract.result.Result>`.
|
|
20
|
+
:raises: :class:`ExecutionError`.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __repr__(self):
|
|
24
|
+
class_name = self.__class__.__name__
|
|
25
|
+
|
|
26
|
+
return f"{class_name}()"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Module for a base asteex generator."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class GenerationError(Exception):
|
|
7
|
+
"""Exception class for errors raised during the generation."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Generator(ABC):
|
|
11
|
+
"""Class for a base generator."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def generate(self):
|
|
15
|
+
"""Generate test instances.
|
|
16
|
+
|
|
17
|
+
:returns: async iterable with :class:`Instances <asteex.abstract.instance.Instance>`.
|
|
18
|
+
:raises: :class:`GenerationError`.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __repr__(self):
|
|
22
|
+
class_name = self.__class__.__name__
|
|
23
|
+
|
|
24
|
+
return f"{class_name}()"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Module for a base asteex test instance."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class InstanceError(Exception):
|
|
7
|
+
"""Exception class for errors raised by instances."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Instance(ABC):
|
|
11
|
+
"""Class for a asteex test instance."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def get_id(self):
|
|
15
|
+
"""Get the unique identifier of the instance.
|
|
16
|
+
|
|
17
|
+
:returns: string id.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def get_case(self):
|
|
22
|
+
"""Get test case.
|
|
23
|
+
|
|
24
|
+
:returns: :class:`Case <asteex.abstract.case.Case>`.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def get_context_sources(self):
|
|
29
|
+
"""Get information about origins of the context values.
|
|
30
|
+
|
|
31
|
+
:returns: dictionary with string names as keys and
|
|
32
|
+
:class:`Source <asteex.abstract.context.Source>` as values.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __repr__(self):
|
|
36
|
+
class_name = self.__class__.__name__
|
|
37
|
+
instance_id = self.get_id()
|
|
38
|
+
|
|
39
|
+
return f"{class_name}(instance_id='{instance_id}')"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Module for a base asteex reporter."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ReportError(Exception):
|
|
7
|
+
"""Exception class for errors raised during the reporting."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Reporter(ABC):
|
|
11
|
+
"""Class for a base reporter."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
async def report(self, results):
|
|
15
|
+
"""Report test results.
|
|
16
|
+
|
|
17
|
+
:param results: async iterable with :class:`Results <asteex.abstract.result.Result>`.
|
|
18
|
+
:raises: :class:`ReportError`.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __repr__(self):
|
|
22
|
+
class_name = self.__class__.__name__
|
|
23
|
+
|
|
24
|
+
return f"{class_name}()"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Module for a base asteex test result."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Result(ABC):
|
|
7
|
+
"""Class for a base test result."""
|
|
8
|
+
|
|
9
|
+
@abstractmethod
|
|
10
|
+
def get_instance(self):
|
|
11
|
+
"""Get the executed test instance.
|
|
12
|
+
|
|
13
|
+
:returns: executed :class:`Instance <asteex.abstract.instance.Instance>`.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def get_status(self):
|
|
18
|
+
"""Get the status of the test execution.
|
|
19
|
+
|
|
20
|
+
:returns: string status.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def get_reason(self):
|
|
25
|
+
"""Get the reason for the status.
|
|
26
|
+
|
|
27
|
+
:returns: string reason.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def get_case(self):
|
|
32
|
+
"""Get the executed case.
|
|
33
|
+
|
|
34
|
+
:returns: executed :class:`Case <asteex.abstract.case.Case>` or None,
|
|
35
|
+
if no case was retrieved.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def get_context(self):
|
|
40
|
+
"""Get the execution context.
|
|
41
|
+
|
|
42
|
+
:returns: dictionary with string names and arbitrary values, or None, if no context
|
|
43
|
+
was created.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def get_exception_information(self):
|
|
48
|
+
"""Get information about the exception raised during the execution.
|
|
49
|
+
|
|
50
|
+
:returns: a tuple (type, value, traceback) similar to one returned by :meth:`sys.exc_info`.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __repr__(self):
|
|
54
|
+
class_name = self.__class__.__name__
|
|
55
|
+
instance = self.get_instance()
|
|
56
|
+
status = self.get_status()
|
|
57
|
+
|
|
58
|
+
return f"{class_name}(instance={instance}, status='{status}')"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package with core implementation of the asteex entities."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package for asteex test cases."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package for asteex executable test cases."""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Module for the abstract executable asteex test case."""
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
|
|
5
|
+
from asteex.abstract.case import Case
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ExecutionError(Exception):
|
|
9
|
+
"""Class for errors raised during the execution."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ExecutableCase(Case):
|
|
13
|
+
"""Class for a asteex test case that can be executed."""
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
async def execute(self, context):
|
|
17
|
+
"""Execute the test case.
|
|
18
|
+
|
|
19
|
+
:param context: dictionary with string keys.
|
|
20
|
+
:raises: :class:`ExecutionError`.
|
|
21
|
+
"""
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Module for a test cases that delegates execution to a callable object."""
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from asteex.core.case.executable.abstract import ExecutableCase, ExecutionError
|
|
7
|
+
from asteex.core.decorator.argument import ContextMapper, CopyError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BuildError(Exception):
|
|
11
|
+
"""Exception for errors raised on attempt to build a test case."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DelegateCase(ExecutableCase):
|
|
15
|
+
"""Class for a asteex test case based on a callable."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, delegate, case_id, dependencies=()):
|
|
18
|
+
super().__init__()
|
|
19
|
+
|
|
20
|
+
if not callable(delegate):
|
|
21
|
+
raise ValueError("Can't create a delegate case from a non-callable object")
|
|
22
|
+
self.__delegate = delegate
|
|
23
|
+
|
|
24
|
+
self.__id = str(case_id)
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
logger.debug("Try to refine dependencies for a new delegate case.")
|
|
28
|
+
try:
|
|
29
|
+
dependencies = tuple(str(dependency) for dependency in dependencies)
|
|
30
|
+
except TypeError as error:
|
|
31
|
+
logger.exception("Can't iterate over dependencies of a new delegate case.")
|
|
32
|
+
raise ValueError("Can't iterate over dependencies of a new delegate case") from error
|
|
33
|
+
self.__dependencies = dependencies
|
|
34
|
+
logger.debug("Refined dependencies for a new delegate case.")
|
|
35
|
+
|
|
36
|
+
def get_id(self):
|
|
37
|
+
"""Get the unique identifier of the test case.
|
|
38
|
+
|
|
39
|
+
:returns: string id.
|
|
40
|
+
"""
|
|
41
|
+
return self.__id
|
|
42
|
+
|
|
43
|
+
def get_dependencies(self):
|
|
44
|
+
"""Get names of the required variables.
|
|
45
|
+
|
|
46
|
+
:returns: tuple with string names of the required variables.
|
|
47
|
+
"""
|
|
48
|
+
return self.__dependencies
|
|
49
|
+
|
|
50
|
+
async def execute(self, context):
|
|
51
|
+
"""Execute the test case.
|
|
52
|
+
|
|
53
|
+
:param context: dictionary with string keys.
|
|
54
|
+
:raises: :class:`ExecutionError <asteex.core.case.execution.abstract.ExecutionError>`.
|
|
55
|
+
"""
|
|
56
|
+
logger = logging.getLogger(__name__)
|
|
57
|
+
|
|
58
|
+
logger.debug("Try to execute the delegate of the case %s.", self)
|
|
59
|
+
try:
|
|
60
|
+
execution = self.__delegate(context=context)
|
|
61
|
+
|
|
62
|
+
if inspect.isawaitable(execution):
|
|
63
|
+
await execution
|
|
64
|
+
except Exception as error:
|
|
65
|
+
logger.exception("An error is raised during the execution.")
|
|
66
|
+
raise ExecutionError("An error is raised during the execution") from error
|
|
67
|
+
|
|
68
|
+
logger.info("Executed the delegate of the case %s.", self)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class DelegateCaseBuilder(ContextMapper):
|
|
72
|
+
"""Class to create delegate cases from callables."""
|
|
73
|
+
|
|
74
|
+
def __init__(self):
|
|
75
|
+
super().__init__()
|
|
76
|
+
self.__case_id = None
|
|
77
|
+
self.__additional_dependencies = frozenset()
|
|
78
|
+
self.__enhancers = ()
|
|
79
|
+
|
|
80
|
+
def set_case_id(self, case_id):
|
|
81
|
+
"""Set identifier for new test cases.
|
|
82
|
+
|
|
83
|
+
:param case_id: string identifier or None.
|
|
84
|
+
None value implies that the builder has to deduce case identifiers from callables.
|
|
85
|
+
:returns: self.
|
|
86
|
+
"""
|
|
87
|
+
if case_id is not None:
|
|
88
|
+
case_id = str(case_id)
|
|
89
|
+
self.__case_id = case_id
|
|
90
|
+
|
|
91
|
+
logger = logging.getLogger(__name__)
|
|
92
|
+
logger.info("Set case identifier in %s.", self)
|
|
93
|
+
|
|
94
|
+
return self
|
|
95
|
+
|
|
96
|
+
def get_case_id(self):
|
|
97
|
+
"""Get identifier for new test cases.
|
|
98
|
+
|
|
99
|
+
:returns: string identifier or None.
|
|
100
|
+
"""
|
|
101
|
+
return self.__case_id
|
|
102
|
+
|
|
103
|
+
def add_dependencies(self, dependencies, overwrite=False):
|
|
104
|
+
"""Add dependencies.
|
|
105
|
+
|
|
106
|
+
:param dependencies: iterable with string names of the dependencies.
|
|
107
|
+
:param overwrite: boolean whether the new dependencies should overwrite the existing ones.
|
|
108
|
+
:returns: self.
|
|
109
|
+
:raises: :class:`ValueError` if dependencies is not a valid iterable.
|
|
110
|
+
"""
|
|
111
|
+
logger = logging.getLogger(__name__)
|
|
112
|
+
logger.debug("Try to set additional case dependencies in %s.", self)
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
refined_case_dependencies = frozenset(str(dependency) for dependency in dependencies)
|
|
116
|
+
except TypeError as error:
|
|
117
|
+
logger.exception("Can't iterate over case dependencies.")
|
|
118
|
+
raise ValueError("Can't iterate over case dependencies") from error
|
|
119
|
+
|
|
120
|
+
if overwrite:
|
|
121
|
+
self.__additional_dependencies = refined_case_dependencies
|
|
122
|
+
else:
|
|
123
|
+
self.__additional_dependencies = self.__additional_dependencies.union(
|
|
124
|
+
refined_case_dependencies,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
logger.info("Set additional case dependencies in %s.", self)
|
|
128
|
+
|
|
129
|
+
return self
|
|
130
|
+
|
|
131
|
+
def get_additional_dependencies(self):
|
|
132
|
+
"""Get additional dependencies.
|
|
133
|
+
|
|
134
|
+
:returns: set with string names of the dependencies.
|
|
135
|
+
"""
|
|
136
|
+
return self.__additional_dependencies
|
|
137
|
+
|
|
138
|
+
def add_enhancers(self, enhancers, overwrite=False):
|
|
139
|
+
"""Add enhancers for new test cases.
|
|
140
|
+
|
|
141
|
+
:param enhancers: iterable with callables that accepts two parameters. Each callable will
|
|
142
|
+
receive a :class:`DelegateCase` for the first parameter and a callable for
|
|
143
|
+
the second parameter. The function must return :class:`DelegateCase`.
|
|
144
|
+
:param overwrite: boolean whether the new enhancers should overwrite the existing ones.
|
|
145
|
+
:returns: self.
|
|
146
|
+
:raises: :class:`ValueError` if enhancers is not a valid iterable.
|
|
147
|
+
"""
|
|
148
|
+
logger = logging.getLogger(__name__)
|
|
149
|
+
logger.debug("Try to set case enhancers in %s.", self)
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
refined_case_enhancers = tuple(enhancers)
|
|
153
|
+
except TypeError as error:
|
|
154
|
+
logger.exception("Can't iterate over case enhancers.")
|
|
155
|
+
raise ValueError("Can't iterate over case enhancers") from error
|
|
156
|
+
|
|
157
|
+
if overwrite:
|
|
158
|
+
self.__enhancers = refined_case_enhancers
|
|
159
|
+
else:
|
|
160
|
+
self.__enhancers = self.__enhancers + refined_case_enhancers
|
|
161
|
+
|
|
162
|
+
logger.info("Set case enhancers in %s.", self)
|
|
163
|
+
|
|
164
|
+
return self
|
|
165
|
+
|
|
166
|
+
def get_enhancers(self):
|
|
167
|
+
"""Get enhancers for new test cases.
|
|
168
|
+
|
|
169
|
+
:returns: tuple with callables that accepts two parameters. Each callable accepts
|
|
170
|
+
a :class:`DelegateCase` for the first parameter and a callable for the second parameter.
|
|
171
|
+
Each callable returns :class:`DelegateCase`.
|
|
172
|
+
"""
|
|
173
|
+
return self.__enhancers
|
|
174
|
+
|
|
175
|
+
def build_from_callable(self, callable_):
|
|
176
|
+
"""Create a delegate case from a callable.
|
|
177
|
+
|
|
178
|
+
:param callable_: a callable to use as a base for the delegate case.
|
|
179
|
+
:returns: a new :class`DelegateCase`.
|
|
180
|
+
:raises: :class:`BuildError` if an error occurs on attempt to create or enhance a case.
|
|
181
|
+
"""
|
|
182
|
+
logger = logging.getLogger(__name__)
|
|
183
|
+
logger.debug("Try to create a delegate case from '%s'.", callable_)
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
case = self._create_case(callable_)
|
|
187
|
+
except Exception as error:
|
|
188
|
+
logger.exception("Can't create a delegate case.")
|
|
189
|
+
raise BuildError("Can't create a delegate case") from error
|
|
190
|
+
|
|
191
|
+
for enhancer in self.get_enhancers():
|
|
192
|
+
try:
|
|
193
|
+
case = enhancer(case, callable_)
|
|
194
|
+
except Exception as error:
|
|
195
|
+
logger.exception("Can't enhance a delegate case.")
|
|
196
|
+
raise BuildError("Can't enhance a delegate case") from error
|
|
197
|
+
|
|
198
|
+
logger.info("Created a delegate case from '%s'.", callable_)
|
|
199
|
+
return case
|
|
200
|
+
|
|
201
|
+
def _copy_data(self, other):
|
|
202
|
+
"""Copy data from this instance to another.
|
|
203
|
+
|
|
204
|
+
:param other: instance of the same type.
|
|
205
|
+
:raises: :class:`CopyError <asteex.core.decorator.argument.CopyError>`
|
|
206
|
+
if data can't be copied.
|
|
207
|
+
"""
|
|
208
|
+
super()._copy_data(other)
|
|
209
|
+
|
|
210
|
+
logger = logging.getLogger(__name__)
|
|
211
|
+
|
|
212
|
+
try:
|
|
213
|
+
other.set_case_id(self.get_case_id())
|
|
214
|
+
except Exception as error:
|
|
215
|
+
logger.exception("Can't copy case identifier to another instance.")
|
|
216
|
+
raise CopyError("Can't copy case identifier to another instance") from error
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
other.add_dependencies(self.get_additional_dependencies(), overwrite=True)
|
|
220
|
+
except Exception as error:
|
|
221
|
+
logger.exception("Can't copy case dependencies to another instance.")
|
|
222
|
+
raise CopyError("Can't copy case dependencies to another instance") from error
|
|
223
|
+
|
|
224
|
+
try:
|
|
225
|
+
other.add_enhancers(self.get_enhancers(), overwrite=True)
|
|
226
|
+
except Exception as error:
|
|
227
|
+
logger.exception("Can't copy case enhancers to another instance.")
|
|
228
|
+
raise CopyError("Can't copy case enhancers to another instance") from error
|
|
229
|
+
|
|
230
|
+
def _create_case_id(self, callable_):
|
|
231
|
+
"""Create an identifier for a delegate case from a callable.
|
|
232
|
+
|
|
233
|
+
:param callable_: callable for a delegate for the case.
|
|
234
|
+
:returns: string id.
|
|
235
|
+
"""
|
|
236
|
+
case_id = self.get_case_id()
|
|
237
|
+
if case_id is None:
|
|
238
|
+
refined_case_id = str(callable_)
|
|
239
|
+
else:
|
|
240
|
+
refined_case_id = case_id
|
|
241
|
+
|
|
242
|
+
return refined_case_id
|
|
243
|
+
|
|
244
|
+
def _create_case_dependencies(self, callable_):
|
|
245
|
+
"""Create dependencies for a delegate case from a callable.
|
|
246
|
+
|
|
247
|
+
:param callable_: callable for a delegate for the case.
|
|
248
|
+
:returns: set with string names.
|
|
249
|
+
"""
|
|
250
|
+
case_dependencies = set(self.get_additional_dependencies())
|
|
251
|
+
|
|
252
|
+
positional_arguments, keyword_arguments = self.build_call_arguments(callable_)
|
|
253
|
+
|
|
254
|
+
arguments_with_dependencies = set().union(positional_arguments).union(keyword_arguments)
|
|
255
|
+
arguments_with_dependencies.difference_update(self.get_arguments_mapped_to_context())
|
|
256
|
+
|
|
257
|
+
dependency_map = self.get_arguments_mapped_to_context_names()
|
|
258
|
+
|
|
259
|
+
case_dependencies.update(
|
|
260
|
+
dependency_map.get(argument, argument) for argument in arguments_with_dependencies
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
return case_dependencies
|
|
264
|
+
|
|
265
|
+
def _create_case_delegate(self, callable_):
|
|
266
|
+
"""Create a delegate for a delegate case from a callable.
|
|
267
|
+
|
|
268
|
+
:param callable_: callable for a delegate for the case.
|
|
269
|
+
:returns: callable, suitable for :class:`DelegateCase`.
|
|
270
|
+
"""
|
|
271
|
+
arguments_mapped_to_context_names = self.get_arguments_mapped_to_context_names().copy()
|
|
272
|
+
arguments_mapped_to_context = set(self.get_arguments_mapped_to_context())
|
|
273
|
+
|
|
274
|
+
positional_arguments, keyword_arguments = self.build_call_arguments(callable_)
|
|
275
|
+
|
|
276
|
+
def _delegate(context):
|
|
277
|
+
def _get_delegate_value(argument):
|
|
278
|
+
if argument in arguments_mapped_to_context:
|
|
279
|
+
value = context
|
|
280
|
+
else:
|
|
281
|
+
mapped_argument = arguments_mapped_to_context_names.get(argument, argument)
|
|
282
|
+
value = context[mapped_argument]
|
|
283
|
+
|
|
284
|
+
return value
|
|
285
|
+
|
|
286
|
+
arg_values = [_get_delegate_value(argument) for argument in positional_arguments]
|
|
287
|
+
kwarg_values = {
|
|
288
|
+
argument: _get_delegate_value(argument)
|
|
289
|
+
for argument in keyword_arguments
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return callable_(*arg_values, **kwarg_values)
|
|
293
|
+
|
|
294
|
+
return _delegate
|
|
295
|
+
|
|
296
|
+
def _create_case(self, callable_):
|
|
297
|
+
"""Create a delegate case from a callable.
|
|
298
|
+
|
|
299
|
+
:param callable_: callable for a delegate for the case.
|
|
300
|
+
:returns: :class:`DelegateCase`.
|
|
301
|
+
"""
|
|
302
|
+
return DelegateCase(
|
|
303
|
+
delegate=self._create_case_delegate(callable_),
|
|
304
|
+
case_id=self._create_case_id(callable_),
|
|
305
|
+
dependencies=self._create_case_dependencies(callable_),
|
|
306
|
+
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package for asteex context entities."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Package for sources of context values."""
|