"""
Classes for sorting test context before a test run.
Warning: `sort_instances` functionality is not supported yet, but the
API is available for future compatibility.
"""
import operator
import random
from enum import Enum
from testplan.common.utils.convert import make_tuple
[docs]
class SortType(Enum):
"""Helper enum used by sorter classes."""
ALL = "all"
INSTANCES = "instances"
SUITES = "suites"
TEST_CASES = "testcases"
[docs]
@classmethod
def validate(cls, value, allow_tuple=True):
"""
Valid examples:
all
instances
(suites, instances)
"""
def validate_single(v):
if isinstance(v, str):
return SortType(v.lower()).value
if isinstance(v, SortType):
return v.value
raise ValueError("Invalid shuffle type value: {}".format(v))
if isinstance(value, (tuple, list)) and allow_tuple:
values = [validate_single(v) for v in value]
if SortType.ALL.value in values and len(values) > 1:
raise ValueError(
"Passing extra shuffle types along with"
" `all` is a redundant operation. values = {}".format(
values
)
)
return values
return validate_single(value)
[docs]
class BaseSorter:
"""Base sorter class"""
[docs]
def should_sort_instances(self):
raise NotImplementedError
[docs]
def should_sort_testsuites(self):
raise NotImplementedError
[docs]
def should_sort_testcases(self):
raise NotImplementedError
[docs]
def sort_instances(self, instances):
raise NotImplementedError
[docs]
def sort_testsuites(self, testsuites):
raise NotImplementedError
[docs]
def sort_testcases(self, testcases, param_groups=None):
raise NotImplementedError
[docs]
def sorted_instances(self, instances):
if self.should_sort_instances():
return self.sort_instances(instances)
return instances
[docs]
def sorted_testsuites(self, testsuites):
if self.should_sort_testsuites():
return self.sort_testsuites(testsuites)
return testsuites
[docs]
def sorted_testcases(self, testsuite, testcases):
if self.should_sort_testcases():
test_methods, param_groups = [], {}
for testcase in testcases:
param_template = getattr(
testcase, "_parametrization_template", None
)
if param_template:
if param_template not in param_groups:
test_methods.append(getattr(testsuite, param_template))
param_groups.setdefault(param_template, []).append(
testcase
)
else:
test_methods.append(testcase)
result = self.sort_testcases(test_methods, param_groups)
if isinstance(result, (tuple, list)):
sorted_test_methods, soted_param_groups = result
else:
sorted_test_methods, soted_param_groups = result, {}
testcases = []
for test_method in sorted_test_methods:
if getattr(test_method, "__parametrization_template__", False):
testcases.extend(soted_param_groups[test_method.__name__])
else:
testcases.append(test_method)
return testcases
[docs]
class NoopSorter(BaseSorter):
"""Sorter that returns the original ordering."""
[docs]
def should_sort_instances(self):
return False
[docs]
def should_sort_testsuites(self):
return False
[docs]
def should_sort_testcases(self):
return False
[docs]
class TypedSorter(BaseSorter):
"""
Base sorter that allows configuration of
sort levels via `sort_type` argument.
"""
def __init__(self, sort_type=SortType.ALL):
self.sort_types = set(make_tuple(SortType.validate(sort_type)))
self.sort_all = SortType.ALL.value in self.sort_types
[docs]
def check_sort_type(self, sort_type):
return self.sort_all or sort_type.value in self.sort_types
[docs]
def should_sort_instances(self):
return self.check_sort_type(SortType.INSTANCES)
[docs]
def should_sort_testsuites(self):
return self.check_sort_type(SortType.SUITES)
[docs]
def should_sort_testcases(self):
return self.check_sort_type(SortType.TEST_CASES)
[docs]
class ShuffleSorter(TypedSorter):
"""
Sorter that shuffles the ordering. It is idempotent in a way that,
it will return the same ordering for the same seed for the
same list.
"""
def __init__(self, shuffle_type=SortType.ALL, seed=None):
self.seed = seed or random.randint(0, 1000)
super(ShuffleSorter, self).__init__(sort_type=shuffle_type)
@property
def randomizer(self):
return random.Random(self.seed)
[docs]
def shuffle(self, items):
items_copy = list(items)
self.randomizer.shuffle(items_copy)
return items_copy
[docs]
def sort_instances(self, instances):
return self.shuffle(instances)
[docs]
def sort_testsuites(self, testsuites):
return self.shuffle(testsuites)
[docs]
def sort_testcases(self, testcases, param_groups=None):
param_groups = param_groups or {}
return self.shuffle(testcases), {
param_template: self.shuffle(testcases)
for param_template, testcases in param_groups.items()
}
[docs]
class AlphanumericSorter(TypedSorter):
"""Sorter that uses basic alphanumeric ordering."""
[docs]
def sort_instances(self, instances):
return sorted(instances, key=operator.attrgetter("name"))
[docs]
def sort_testsuites(self, testsuites):
return sorted(testsuites, key=operator.attrgetter("name"))
[docs]
def sort_testcases(self, testcases, param_groups=None):
param_groups = param_groups or {}
return sorted(testcases, key=operator.attrgetter("name")), {
param_template: sorted(testcases, key=operator.attrgetter("name"))
for param_template, testcases in param_groups.items()
}