Skip to content

Commit

Permalink
Change the default name function of parameters testcase.
Browse files Browse the repository at this point in the history
  • Loading branch information
yuxuan-ms committed Dec 5, 2024
1 parent c3e5747 commit 1885c07
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 18 deletions.
1 change: 1 addition & 0 deletions doc/newsfragments/3160_changed.case_name.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Limit the length of parameterization testcase name to 255 characters. If the name length exceeds 255 characters, index-suffixed names (e.g., {func_name[:240]} 1, {func_name[:240]} 2) will be used.
2 changes: 1 addition & 1 deletion testplan/common/utils/reporting.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __new__(cls):
cls.__instance = object.__new__(cls)
return cls.__instance

def __str__(self):
def __repr__(self):
return self.descr


Expand Down
37 changes: 33 additions & 4 deletions testplan/testing/multitest/parametrization.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from testplan.common.utils import callable as callable_utils
from testplan.common.utils import convert, interface
from testplan.testing import tagging
from typing import Callable, Optional

# Although any string will be processed as normal, it's a good
# approach to warn the user if the generated method name is not a
Expand Down Expand Up @@ -206,8 +207,11 @@ def _generated(self, env, result):
)
# Users request the feature that when `name_func` set to `None`,
# then simply append integer suffixes to the names of testcases
_generated.name = (
name_func(name, kwargs) if name_func is not None else f"{name} {idx}"
_generated.name = _parametrization_report_name_func_wrapper(
name_func=name_func,
name=name,
kwargs=kwargs,
index=idx,
)

if hasattr(function, "__xfail__"):
Expand Down Expand Up @@ -267,7 +271,7 @@ def _check_tag_func(tag_func):
)


def _parametrization_name_func_wrapper(func_name, kwargs):
def _parametrization_name_func_wrapper(func_name: str, kwargs: dict):
"""
Make sure that name generation doesn't end up with invalid / unreadable
attribute names/types etc.
Expand All @@ -291,6 +295,31 @@ def _parametrization_name_func_wrapper(func_name, kwargs):
return generated_name


def _parametrization_report_name_func_wrapper(
name_func: Optional[Callable], name: str, kwargs: dict, index: int
):
"""
Make sure that generated name is not too long,
if it is, then use index suffixed names e.g. "{func_name} 1", "{func_name} 2", will be used.
"""
if name_func:
generated_name = name_func(name, kwargs)
if not isinstance(generated_name, str):
raise ValueError(
"The return value of name_func must be a string, "
f"it is of type: {type(generated_name)}, value: {generated_name}"
)
if len(generated_name) <= MAX_METHOD_NAME_LENGTH:
return generated_name
else:
warnings.warn(
f"The name name_func returned ({generated_name}) is too long, using index suffixed names."
)

generated_name = f"{name[:MAX_METHOD_NAME_LENGTH - 10]} {index}"
return generated_name


def parametrization_name_func(func_name, kwargs):
"""
Method name generator for parametrized testcases.
Expand Down Expand Up @@ -321,7 +350,7 @@ def default_name_func(func_name, kwargs):
>>> import collections
>>> default_name_func('Test Method',
collections.OrderedDict(('foo', 5), ('bar', 10)))
'Test Method {foo:5, bar:10}'
'Test Method <foo:5, bar:10>'
:param func_name: Name of the parametrization target function.
:type func_name: ``str``
Expand Down
4 changes: 2 additions & 2 deletions testplan/testing/multitest/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -581,9 +581,9 @@ def _validate_testcase(func):

if len(func.name) > defaults.MAX_TEST_NAME_LENGTH:
warnings.warn(
'Name defined for testcase "{}" is too long,'
f'Name defined for testcase "{func.name}" is too long,'
' consider customizing testcase name with argument "name_func"'
" in @testcase decorator.".format(func.__name__)
" in @testcase decorator."
)


Expand Down
21 changes: 10 additions & 11 deletions tests/functional/testplan/testing/multitest/test_parametrization.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import sys
import logging
from contextlib import contextmanager
from unittest import mock
from imp import reload
from importlib import reload

import pytest

from testplan.defaults import MAX_TEST_NAME_LENGTH
from testplan.testing.multitest import MultiTest, testsuite, testcase
from testplan.testing.multitest.parametrization import (
Expand All @@ -24,15 +22,16 @@
LOGGER = logging.getLogger()


@contextmanager
def module_reloaded(mod):
@pytest.fixture()
def suite_reloaded():
"""
If uncaught exception raised, Testplan process should abort. However,
if the process is managed by PyTest for testing purpose, then the
exception will be caught and execute the next testcase, but Testplan
modules still exist in memory, some global variables need to be reset.
"""
yield
mod = "testplan.testing.multitest.suite"
if mod in sys.modules:
reload(sys.modules[mod])

Expand Down Expand Up @@ -214,7 +213,7 @@ def test_sample(self, env, result, a, b):
),
),
)
def test_invalid_parametrization(val, msg):
def test_invalid_parametrization(suite_reloaded, val, msg):
"""Correct arguments should be passed to parametrized testcases."""
with pytest.raises(ParametrizationError):

Expand All @@ -227,7 +226,7 @@ def sample_test(self, env, result, a, b, c=3):
pytest.fail(msg)


def test_duplicate_parametrization_template_definition():
def test_duplicate_parametrization_template_definition(suite_reloaded):
"""No duplicate name of testcase or parametrization template allowed."""
with pytest.raises(ValueError):

Expand Down Expand Up @@ -297,16 +296,16 @@ def sample(self, env, result, test__val):
(
(
("#@)$*@#%", "a-b"),
["sample_test <val='#@)$*@#%'>", "sample_test <val='a-b'>"],
["sample_test <val=#@)$*@#%>", "sample_test <val=a-b>"],
["sample_test__0", "sample_test__1"],
"Should use original method name + index fallback if"
" generated names are not valid Python attribute names.",
),
(
("a" * MAX_METHOD_NAME_LENGTH, "b" * MAX_METHOD_NAME_LENGTH),
[
"sample_test <val='{}'>".format("a" * MAX_METHOD_NAME_LENGTH),
"sample_test <val='{}'>".format("b" * MAX_METHOD_NAME_LENGTH),
"sample_test 0",
"sample_test 1",
],
["sample_test__0", "sample_test__1"],
"Should use original method name + index fallback if"
Expand Down Expand Up @@ -451,7 +450,7 @@ def sample_test(self, env, result, a, b):
),
),
)
def test_invalid_name_func(name_func, msg, err):
def test_invalid_name_func(suite_reloaded, name_func, msg, err):
"""Custom naming function should be correctly defined."""
with pytest.raises(err):

Expand Down

0 comments on commit 1885c07

Please sign in to comment.