Skip to content

Commit

Permalink
Merge pull request #197 from reportportal/develop
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
HardNorth authored Sep 17, 2024
2 parents 18de07c + 42daccb commit 992baa9
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 74 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## [Unreleased]
### Added
- Issue [#192](https://github.com/reportportal/agent-Python-RobotFramework/issues/192): Robot link markup to Markdown conversion, by @HardNorth

## [5.5.4]
### Fixed
- Issue [#187](https://github.com/reportportal/agent-Python-RobotFramework/issues/187): Distutils in the agent, by @HardNorth
### Added
Expand Down
18 changes: 18 additions & 0 deletions examples/rkie_keyword.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
*** Settings ***
Documentation Example of 'Run Keyword And Ignore Error' keyword reporting
*** Variables ***
${countval} 0

*** Test Cases ***
Rkie test
Run Keyword And Ignore Error Fail on first try
Fail on first try

*** Keywords ***
Fail on first try
${counter} Evaluate ${countval} + 1
Set Suite Variable ${countval} ${counter}
IF ${countval} < 2
Fail To less executions
END
6 changes: 6 additions & 0 deletions examples/suite_doc_with_urls.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*** Settings ***
Documentation This is a test suite with URLs: [https://www.google.com | Google] and [https://www.google.com]
*** Test Cases ***
Simple test
Log Hello, world!
40 changes: 40 additions & 0 deletions robotframework_reportportal/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2024 EPAM Systems
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""This module contains functions to ease reporting to ReportPortal."""

import re
from typing import Iterable, Tuple


def replace_patterns(text: str, patterns: Iterable[Tuple[re.Pattern, str]]) -> str:
"""Replace given patterns in the text."""
result = text
for p, repl in patterns:
result = p.sub(repl, result)
return result


BARE_LINK_PATTERN = re.compile(r'\[\s*([^]|]+)]')
NAMED_LINK_PATTERN = re.compile(r'\[\s*([^]|]+)\|\s*([^]]+)]')

ROBOT_MARKUP_REPLACEMENT_PATTERS = [
(BARE_LINK_PATTERN, r'<\1>'),
(NAMED_LINK_PATTERN, r'[\2](\1)'),
]


def robot_markup_to_markdown(text: str) -> str:
"""Convert Robot Framework's text markup to Markdown format."""
return replace_patterns(text, ROBOT_MARKUP_REPLACEMENT_PATTERS)
8 changes: 4 additions & 4 deletions robotframework_reportportal/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@

from reportportal_client.helpers import LifoQueue, is_binary, guess_content_type_from_bytes

from .model import Keyword, Launch, Test, LogMessage, Suite
from .service import RobotService
from .static import MAIN_SUITE_ID, PABOT_WITHOUT_LAUNCH_ID_MSG
from .variables import Variables
from robotframework_reportportal.model import Keyword, Launch, Test, LogMessage, Suite
from robotframework_reportportal.service import RobotService
from robotframework_reportportal.static import MAIN_SUITE_ID, PABOT_WITHOUT_LAUNCH_ID_MSG
from robotframework_reportportal.variables import Variables

logger = logging.getLogger(__name__)
VARIABLE_PATTERN = r'^\s*\${[^}]*}\s*=\s*'
Expand Down
2 changes: 1 addition & 1 deletion robotframework_reportportal/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def log_free_memory(self):

from robot.api import logger

from .model import LogMessage
from robotframework_reportportal.model import LogMessage


def write(msg: str, level: str = 'INFO', html: bool = False, attachment: Optional[Dict[str, str]] = None,
Expand Down
7 changes: 4 additions & 3 deletions robotframework_reportportal/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import os
from typing import Any, Dict, List, Optional, Union

from robotframework_reportportal.helpers import robot_markup_to_markdown
from reportportal_client.helpers import gen_attributes


Expand Down Expand Up @@ -48,7 +49,7 @@ def __init__(self, name: str, robot_attributes: Dict[str, Any]):
:param robot_attributes: Suite attributes passed through the listener
"""
self.robot_attributes = robot_attributes
self.doc = robot_attributes['doc']
self.doc = robot_markup_to_markdown(robot_attributes['doc'])
self.end_time = robot_attributes.get('endtime', '')
self.longname = robot_attributes['longname']
self.message = robot_attributes.get('message')
Expand Down Expand Up @@ -145,7 +146,7 @@ def __init__(self, name: str, robot_attributes: Dict[str, Any], test_attributes:
self._tags = robot_attributes['tags']
self.test_attributes = gen_attributes(test_attributes)
self.robot_attributes = robot_attributes
self.doc = robot_attributes['doc']
self.doc = robot_markup_to_markdown(robot_attributes['doc'])
self.end_time = robot_attributes.get('endtime', '')
self.longname = robot_attributes['longname']
self.message = robot_attributes.get('message')
Expand Down Expand Up @@ -242,7 +243,7 @@ def __init__(self, name: str, robot_attributes: Dict[str, Any], parent_type: Opt
self.robot_attributes = robot_attributes
self.args = robot_attributes['args']
self.assign = robot_attributes['assign']
self.doc = robot_attributes['doc']
self.doc = robot_markup_to_markdown(robot_attributes['doc'])
self.end_time = robot_attributes.get('endtime')
self.keyword_name = robot_attributes['kwname']
self.keyword_type = robot_attributes['type']
Expand Down
41 changes: 23 additions & 18 deletions robotframework_reportportal/result_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,37 @@
import re
import string
from datetime import datetime

from robot.api import ResultVisitor
from typing import List, Pattern, Optional
from urllib.parse import unquote

from . import listener
from .time_visitor import corrections
# noinspection PyUnresolvedReferences
from .variables import _variables
from robot.result import ResultVisitor, Result, TestSuite, TestCase, Keyword, Message

from robotframework_reportportal import listener
from robotframework_reportportal.time_visitor import corrections
# noinspection PyUnresolvedReferences
from robotframework_reportportal.variables import _variables

listener = listener.listener()


def to_timestamp(time_str):
def to_timestamp(time_str: str) -> Optional[str]:
if time_str:
dt = datetime.strptime(time_str, '%Y%m%d %H:%M:%S.%f')
return str(int(dt.timestamp() * 1000))
return None


class RobotResultsVisitor(ResultVisitor):
_link_pattern = re.compile("src=[\"\']([^\"\']+)[\"\']")
_link_pattern: Pattern = re.compile("src=[\"\']([^\"\']+)[\"\']")

def start_result(self, result):
def start_result(self, result: Result) -> bool:
if "RP_LAUNCH" not in _variables:
_variables["RP_LAUNCH"] = result.suite.name
if "RP_LAUNCH_DOC" not in _variables:
_variables["RP_LAUNCH_DOC"] = result.suite.doc
return True

def start_suite(self, suite):
def start_suite(self, suite: TestSuite) -> bool:
ts = to_timestamp(suite.starttime if suite.id not in corrections else corrections[suite.id][0])
attrs = {
'id': suite.id,
Expand All @@ -58,8 +59,9 @@ def start_suite(self, suite):
'starttime': ts
}
listener.start_suite(suite.name, attrs, ts)
return True

def end_suite(self, suite):
def end_suite(self, suite: TestSuite) -> None:
ts = to_timestamp(suite.endtime if suite.id not in corrections else corrections[suite.id][1])
attrs = {
'id': suite.id,
Expand All @@ -78,7 +80,7 @@ def end_suite(self, suite):
}
listener.end_suite(None, attrs, ts)

def start_test(self, test):
def start_test(self, test: TestCase) -> bool:
ts = to_timestamp(test.starttime if test.id not in corrections else corrections[test.id][0])
attrs = {
'id': test.id,
Expand All @@ -95,8 +97,9 @@ def start_test(self, test):
'starttime': ts,
}
listener.start_test(test.name, attrs, ts)
return True

def end_test(self, test):
def end_test(self, test: TestCase) -> None:
ts = to_timestamp(test.endtime if test.id not in corrections else corrections[test.id][1])
attrs = {
'id': test.id,
Expand All @@ -117,7 +120,7 @@ def end_test(self, test):
}
listener.end_test(test.name, attrs, ts)

def start_keyword(self, kw):
def start_keyword(self, kw: Keyword) -> bool:
ts = to_timestamp(kw.starttime if kw.id not in corrections else corrections[kw.id][0])
attrs = {
'type': string.capwords(kw.type),
Expand All @@ -130,8 +133,9 @@ def start_keyword(self, kw):
'starttime': ts,
}
listener.start_keyword(kw.name, attrs, ts)
return True

def end_keyword(self, kw):
def end_keyword(self, kw: Keyword) -> None:
ts = to_timestamp(kw.endtime if kw.id not in corrections else corrections[kw.id][1])
attrs = {
'type': string.capwords(kw.type),
Expand All @@ -147,7 +151,7 @@ def end_keyword(self, kw):
}
listener.end_keyword(kw.name, attrs, ts)

def start_message(self, msg):
def start_message(self, msg: Message) -> bool:
if msg.message:
message = {
'message': msg.message,
Expand All @@ -162,8 +166,9 @@ def start_message(self, msg):
try:
listener.log_message(message)
except Exception:
pass
return False
return True

def parse_message(self, msg):
def parse_message(self, msg: str) -> List[str]:
m = self._link_pattern.search(msg)
return [m.group(), unquote(m.group(1))]
41 changes: 0 additions & 41 deletions robotframework_reportportal/result_visitor.pyi

This file was deleted.

11 changes: 5 additions & 6 deletions robotframework_reportportal/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,21 @@

"""This module is a Robot service for reporting results to ReportPortal."""

import logging
from typing import Optional

from dateutil.parser import parse
import logging

from reportportal_client import RP, create_client
from reportportal_client.helpers import (
dict_to_payload,
get_launch_sys_attrs,
get_package_version,
timestamp
)
from reportportal_client import RP, create_client

from .model import Launch, Suite, Test, Keyword, LogMessage
from .variables import Variables
from .static import LOG_LEVEL_MAPPING, STATUS_MAPPING
from robotframework_reportportal.model import Launch, Suite, Test, Keyword, LogMessage
from robotframework_reportportal.static import LOG_LEVEL_MAPPING, STATUS_MAPPING
from robotframework_reportportal.variables import Variables

logger = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from setuptools import setup


__version__ = '5.5.4'
__version__ = '5.5.5'


def read_file(fname):
Expand Down
39 changes: 39 additions & 0 deletions tests/integration/test_rkie_keyword.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2023 EPAM Systems
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from unittest import mock

from tests import REPORT_PORTAL_SERVICE
from tests.helpers import utils


@mock.patch(REPORT_PORTAL_SERVICE)
def test_before_after_suite_with_steps(mock_client_init):
mock_client = mock_client_init.return_value
mock_client.start_test_item.side_effect = utils.item_id_gen

result = utils.run_robot_tests(['examples/rkie_keyword.robot'])
assert result == 0

launch_start = mock_client.start_launch.call_args_list
launch_finish = mock_client.finish_launch.call_args_list
assert len(launch_start) == len(launch_finish) == 1

item_start_calls = mock_client.start_test_item.call_args_list
item_finish_calls = mock_client.finish_test_item.call_args_list
assert len(item_start_calls) == len(item_finish_calls) == 13

statuses = [finish[1]['status'] for finish in item_finish_calls]
assert statuses == ['PASSED'] * 2 + ['FAILED'] * 3 + ['PASSED'] * 3 + [
'SKIPPED'] * 2 + ['PASSED'] * 3
Loading

0 comments on commit 992baa9

Please sign in to comment.