Skip to content

Commit 8bdb705

Browse files
authored
Merge pull request #421 from reportportal/develop
Release
2 parents 89ca336 + 474fb8a commit 8bdb705

8 files changed

Lines changed: 41 additions & 105 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Changelog
22

33
## [Unreleased]
4+
### Added
5+
- Microseconds precision for timestamps, by @HardNorth
6+
### Changed
7+
- Client version updated to [5.7.4](https://github.com/reportportal/client-Python/releases/tag/5.7.4), by @HardNorth
8+
### Removed
9+
- Redundant item name truncation (now handled on the Client level), by @HardNorth
10+
11+
## [5.6.5]
412
### Fixed
513
- Issue [#418](https://github.com/reportportal/agent-python-pytest/issues/418) parametrize marker IDs, by @drcrazy
614

pytest_reportportal/service.py

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import threading
2121
import traceback
2222
from collections import OrderedDict
23+
from datetime import datetime, timezone
2324
from enum import Enum
2425
from functools import wraps
2526
from os import curdir
@@ -28,10 +29,10 @@
2829

2930
from _pytest.doctest import DoctestItem
3031
from py.path import local
31-
from pytest import Class, Function, Item, Module, Package, PytestWarning, Session
32+
from pytest import Class, Function, Item, Module, Package, Session
3233
from reportportal_client.aio import Task
3334
from reportportal_client.core.rp_issues import ExternalIssue, Issue
34-
from reportportal_client.helpers import markdown_helpers, timestamp
35+
from reportportal_client.helpers import markdown_helpers
3536

3637
from .config import AgentConfig
3738

@@ -80,8 +81,6 @@
8081
LOGGER = logging.getLogger(__name__)
8182

8283
KNOWN_LOG_LEVELS = ("TRACE", "DEBUG", "INFO", "WARN", "ERROR")
83-
MAX_ITEM_NAME_LENGTH: int = 1024
84-
TRUNCATION_STR: str = "..."
8584
ROOT_DIR: str = str(os.path.abspath(curdir))
8685
PYTEST_MARKS_IGNORE: set[str] = {"parametrize", "usefixtures", "filterwarnings"}
8786
NOT_ISSUE: Issue = Issue("NOT_ISSUE")
@@ -238,7 +237,7 @@ def _build_start_launch_rq(self) -> dict[str, Any]:
238237
start_rq = {
239238
"attributes": self._get_launch_attributes(attributes),
240239
"name": self._config.rp_launch,
241-
"start_time": timestamp(),
240+
"start_time": datetime.now(tz=timezone.utc),
242241
"description": self._config.rp_launch_description,
243242
"rerun": self._config.rp_rerun,
244243
"rerun_of": self._config.rp_rerun_of,
@@ -497,21 +496,6 @@ def collect_tests(self, session: Session) -> None:
497496
self._merge_code(test_tree)
498497
self._build_item_paths(test_tree, [])
499498

500-
def _truncate_item_name(self, name: str) -> str:
501-
"""Get name of item.
502-
503-
:param name: Test Item name
504-
:return: truncated to maximum length name if needed
505-
"""
506-
if len(name) > MAX_ITEM_NAME_LENGTH:
507-
name = name[: MAX_ITEM_NAME_LENGTH - len(TRUNCATION_STR)] + TRUNCATION_STR
508-
LOGGER.warning(
509-
PytestWarning(
510-
f'Test leaf ID was truncated to "{name}" because of name size constrains on Report Portal'
511-
)
512-
)
513-
return name
514-
515499
def _get_item_description(self, test_item: Any) -> Optional[str]:
516500
"""Get description of item.
517501
@@ -580,9 +564,9 @@ def _build_start_suite_rq(self, leaf: dict[str, Any]) -> dict[str, Any]:
580564
parent_item_id = self._lock(leaf["parent"], lambda p: p.get("item_id")) if "parent" in leaf else None
581565
item = leaf["item"]
582566
payload = {
583-
"name": self._truncate_item_name(leaf["name"]),
567+
"name": leaf["name"],
584568
"description": self._get_item_description(item),
585-
"start_time": timestamp(),
569+
"start_time": datetime.now(tz=timezone.utc),
586570
"item_type": "SUITE",
587571
"code_ref": code_ref,
588572
"parent_item_id": parent_item_id,
@@ -882,9 +866,9 @@ def _process_metadata_item_finish(self, leaf: dict[str, Any]) -> None:
882866
def _build_start_step_rq(self, leaf: dict[str, Any]) -> dict[str, Any]:
883867
payload = {
884868
"attributes": leaf.get("attributes", None),
885-
"name": self._truncate_item_name(leaf["name"]),
869+
"name": leaf["name"],
886870
"description": leaf["description"],
887-
"start_time": timestamp(),
871+
"start_time": datetime.now(tz=timezone.utc),
888872
"item_type": "STEP",
889873
"code_ref": leaf.get("code_ref", None),
890874
"parameters": leaf.get("parameters", None),
@@ -963,7 +947,7 @@ def _build_finish_step_rq(self, leaf: dict[str, Any]) -> dict[str, Any]:
963947
issue = None
964948
payload = {
965949
"attributes": leaf.get("attributes", None),
966-
"end_time": timestamp(),
950+
"end_time": datetime.now(tz=timezone.utc),
967951
"status": status,
968952
"issue": issue,
969953
"item_id": leaf["item_id"],
@@ -979,7 +963,7 @@ def _finish_suite(self, finish_rq: dict[str, Any]) -> None:
979963
self.rp.finish_test_item(**finish_rq)
980964

981965
def _build_finish_suite_rq(self, leaf) -> dict[str, Any]:
982-
payload = {"end_time": timestamp(), "item_id": leaf["item_id"]}
966+
payload = {"end_time": datetime.now(tz=timezone.utc), "item_id": leaf["item_id"]}
983967
return payload
984968

985969
def _proceed_suite_finish(self, leaf) -> None:
@@ -1056,7 +1040,7 @@ def finish_suites(self) -> None:
10561040
self._lock(leaf, lambda p: self._proceed_suite_finish(p))
10571041

10581042
def _build_finish_launch_rq(self) -> dict[str, Any]:
1059-
finish_rq = {"end_time": timestamp()}
1043+
finish_rq = {"end_time": datetime.now(tz=timezone.utc)}
10601044
return finish_rq
10611045

10621046
def _finish_launch(self, finish_rq) -> None:
@@ -1074,7 +1058,7 @@ def _build_log(
10741058
) -> dict[str, Any]:
10751059
sl_rq = {
10761060
"item_id": item_id,
1077-
"time": timestamp(),
1061+
"time": datetime.now(tz=timezone.utc),
10781062
"message": message,
10791063
"level": log_level,
10801064
}
@@ -1123,7 +1107,7 @@ def report_fixture(self, name: str, error_msg: str) -> Generator[None, Any, None
11231107
return
11241108

11251109
reporter = self.rp.step_reporter
1126-
item_id = reporter.start_nested_step(name, timestamp())
1110+
item_id = reporter.start_nested_step(name, datetime.now(tz=timezone.utc))
11271111

11281112
try:
11291113
outcome = yield
@@ -1140,11 +1124,11 @@ def report_fixture(self, name: str, error_msg: str) -> Generator[None, Any, None
11401124
)
11411125
exception_log = self._build_log(item_id, traceback_str, log_level="ERROR")
11421126
self.rp.log(**exception_log)
1143-
reporter.finish_nested_step(item_id, timestamp(), status)
1127+
reporter.finish_nested_step(item_id, datetime.now(tz=timezone.utc), status)
11441128
except Exception as e:
11451129
LOGGER.error("Failed to report fixture: %s", name)
11461130
LOGGER.exception(e)
1147-
reporter.finish_nested_step(item_id, timestamp(), "FAILED")
1131+
reporter.finish_nested_step(item_id, datetime.now(tz=timezone.utc), "FAILED")
11481132

11491133
def _get_python_name(self, scenario: Scenario) -> str:
11501134
python_name = f"test_{make_python_name(self._get_scenario_template(scenario).name)}"
@@ -1301,7 +1285,7 @@ def _finish_bdd_step(self, leaf: dict[str, Any], status: str) -> None:
13011285

13021286
reporter = self.rp.step_reporter
13031287
item_id = leaf["item_id"]
1304-
reporter.finish_nested_step(item_id, timestamp(), status)
1288+
reporter.finish_nested_step(item_id, datetime.now(tz=timezone.utc), status)
13051289
leaf["exec"] = ExecStatus.FINISHED
13061290

13071291
def _is_background_step(self, step: Step, feature: Feature) -> bool:
@@ -1343,15 +1327,15 @@ def start_bdd_step(self, feature: Feature, scenario: Scenario, step: Step) -> No
13431327
background_leaf = scenario_leaf["children"][feature.background]
13441328
background_leaf["children"][step] = step_leaf
13451329
if background_leaf["exec"] != ExecStatus.IN_PROGRESS:
1346-
item_id = reporter.start_nested_step(BACKGROUND_STEP_NAME, timestamp())
1330+
item_id = reporter.start_nested_step(BACKGROUND_STEP_NAME, datetime.now(tz=timezone.utc))
13471331
background_leaf["item_id"] = item_id
13481332
background_leaf["exec"] = ExecStatus.IN_PROGRESS
13491333
else:
13501334
scenario_leaf["children"][step] = step_leaf
13511335
if feature.background:
13521336
background_leaf = scenario_leaf["children"][feature.background]
13531337
self._finish_bdd_step(background_leaf, "PASSED")
1354-
item_id = reporter.start_nested_step(self._truncate_item_name(f"{step.keyword} {step.name}"), timestamp())
1338+
item_id = reporter.start_nested_step(f"{step.keyword} {step.name}", datetime.now(tz=timezone.utc))
13551339
step_leaf["item_id"] = item_id
13561340
step_leaf["exec"] = ExecStatus.IN_PROGRESS
13571341

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
reportportal-client==5.7.2
1+
reportportal-client==5.7.4

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from setuptools import setup
1919

20-
__version__ = "5.6.5"
20+
__version__ = "5.6.6"
2121

2222

2323
def read_file(fname):

tests/integration/test_bdd.py

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,33 +1081,3 @@ def test_rp_tests_attributes_bdd_tags(mock_client_init):
10811081
assert {"key": "test_key", "value": "test_value"} in attributes
10821082
assert {"value": "ok"} in attributes
10831083
assert {"key": "key", "value": "value"} in attributes
1084-
1085-
1086-
@mock.patch(REPORT_PORTAL_SERVICE)
1087-
def test_long_step(mock_client_init):
1088-
mock_client = setup_mock_for_logging(mock_client_init)
1089-
result = utils.run_pytest_tests(tests=["examples/bdd/step_defs/long_step_scenario_steps.py"])
1090-
assert int(result) == 0, "Exit code should be 0 (no errors)"
1091-
1092-
# Verify scenario is correctly identified
1093-
scenario_call = mock_client.start_test_item.call_args_list[0]
1094-
assert scenario_call[1]["name"] == "Feature: Test long step scenario - Scenario: The scenario"
1095-
assert scenario_call[1]["item_type"] == "STEP"
1096-
assert scenario_call[1]["code_ref"] == "features/long_step_scenario.feature/[SCENARIO:The scenario]"
1097-
1098-
# Verify step is truncated but still identifiable
1099-
step_call = mock_client.start_test_item.call_args_list[1]
1100-
step_name = step_call[0][0]
1101-
assert step_name.startswith("Given A very long step.")
1102-
assert len(step_name) <= 1024, "Step name should be truncated to at most 1024 characters"
1103-
assert step_name.endswith("..."), "Truncated step should have ellipsis"
1104-
1105-
# Verify step details
1106-
assert step_call[0][2] == "step"
1107-
assert step_call[1]["parent_item_id"] == scenario_call[1]["name"] + "_1"
1108-
assert step_call[1]["has_stats"] is False
1109-
1110-
# Verify all steps pass
1111-
finish_calls = mock_client.finish_test_item.call_args_list
1112-
for call in finish_calls:
1113-
assert call[1]["status"] == "PASSED"

tests/integration/test_config_handling.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,10 @@ def test_rp_api_retries(mock_client_init):
177177
retries = 5
178178
variables = dict(utils.DEFAULT_VARIABLES)
179179
variables.update({"rp_api_retries": str(retries)}.items())
180+
mock_client = mock_client_init.return_value
180181

181182
with warnings.catch_warnings(record=True) as w:
182-
result = utils.run_pytest_tests(["examples/test_rp_logging.py"], variables=variables)
183+
result = utils.run_tests_with_client(mock_client, ["examples/test_rp_logging.py"], variables=variables)
183184
assert int(result) == 0, "Exit code should be 0 (no errors)"
184185

185186
expect(mock_client_init.call_count == 1)
@@ -210,7 +211,8 @@ def test_launch_uuid_print(mock_client_init):
210211
print_uuid = True
211212
variables = utils.DEFAULT_VARIABLES.copy()
212213
variables.update({"rp_launch_uuid_print": str(print_uuid)}.items())
213-
result = utils.run_pytest_tests(["examples/test_rp_logging.py"], variables=variables)
214+
mock_client = mock_client_init.return_value
215+
result = utils.run_tests_with_client(mock_client, ["examples/test_rp_logging.py"], variables=variables)
214216
assert int(result) == 0, "Exit code should be 0 (no errors)"
215217
expect(mock_client_init.call_count == 1)
216218
expect(mock_client_init.call_args_list[0][1]["launch_uuid_print"] == print_uuid)
@@ -223,7 +225,8 @@ def test_launch_uuid_print_stderr(mock_client_init):
223225
print_uuid = True
224226
variables = utils.DEFAULT_VARIABLES.copy()
225227
variables.update({"rp_launch_uuid_print": str(print_uuid), "rp_launch_uuid_print_output": "stderr"}.items())
226-
result = utils.run_pytest_tests(["examples/test_rp_logging.py"], variables=variables)
228+
mock_client = mock_client_init.return_value
229+
result = utils.run_tests_with_client(mock_client, ["examples/test_rp_logging.py"], variables=variables)
227230
assert int(result) == 0, "Exit code should be 0 (no errors)"
228231
expect(mock_client_init.call_count == 1)
229232
expect(mock_client_init.call_args_list[0][1]["launch_uuid_print"] == print_uuid)
@@ -244,7 +247,8 @@ def test_launch_uuid_print_invalid_output(mock_client_init):
244247
@mock.patch(REPORT_PORTAL_SERVICE)
245248
def test_no_launch_uuid_print(mock_client_init):
246249
variables = utils.DEFAULT_VARIABLES.copy()
247-
result = utils.run_pytest_tests(["examples/test_rp_logging.py"], variables=variables)
250+
mock_client = mock_client_init.return_value
251+
result = utils.run_tests_with_client(mock_client, ["examples/test_rp_logging.py"], variables=variables)
248252
assert int(result) == 0, "Exit code should be 0 (no errors)"
249253
expect(mock_client_init.call_count == 1)
250254
expect(mock_client_init.call_args_list[0][1]["launch_uuid_print"] is False)
@@ -264,7 +268,8 @@ def test_client_timeouts(mock_client_init, connect_value, read_value, expected_r
264268
if read_value:
265269
variables["rp_read_timeout"] = read_value
266270

267-
result = utils.run_pytest_tests(["examples/test_rp_logging.py"], variables=variables)
271+
mock_client = mock_client_init.return_value
272+
result = utils.run_tests_with_client(mock_client, ["examples/test_rp_logging.py"], variables=variables)
268273

269274
assert int(result) == 0, "Exit code should be 0 (no errors)"
270275
assert mock_client_init.call_count == 1

tests/integration/test_empty_run.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
"""This module includes integration tests for the empty run."""
1515

16+
from datetime import datetime, timezone
1617
from unittest import mock
1718

1819
from tests import REPORT_PORTAL_SERVICE
@@ -25,6 +26,7 @@ def test_empty_run(mock_client_init):
2526
2627
:param mock_client_init: Pytest fixture
2728
"""
29+
test_start_time = datetime.now(timezone.utc)
2830
result = utils.run_pytest_tests(tests=["examples/empty/"])
2931

3032
assert int(result) == 5, "Exit code should be 5 (no tests)"
@@ -36,4 +38,4 @@ def test_empty_run(mock_client_init):
3638
finish_args = mock_client.finish_launch.call_args_list
3739
assert "status" not in finish_args[0][1], "Launch status should not be defined"
3840
launch_end_time = finish_args[0][1]["end_time"]
39-
assert launch_end_time is not None and int(launch_end_time) > 0, "Launch end time is empty"
41+
assert launch_end_time is not None and launch_end_time > test_start_time, "Launch end time is empty"

tests/integration/test_max_name_length.py

Lines changed: 0 additions & 33 deletions
This file was deleted.

0 commit comments

Comments
 (0)