Skip to content

Commit

Permalink
Rewriting ReportPortal plugin extension [WIP]:
Browse files Browse the repository at this point in the history
* mapping according to options --launch-per-plan and --suite-per-plan
* uploading to existing launch/suite with options --upload-to-launch LAUNCH_ID, --upload-to-suite SUITE_ID
* option --launch-description and preparation for --launch-attributes (for suite-per-plan mapping)
* trial option --launch-rerun
* trial option --defect-type
  • Loading branch information
4N0body5 committed Sep 20, 2023
1 parent 61ab981 commit 7de8f2a
Showing 1 changed file with 119 additions and 61 deletions.
180 changes: 119 additions & 61 deletions tmt/steps/report/reportportal.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,65 @@ class ReportReportPortalData(tmt.steps.report.ReportStepData):
metavar="LAUNCH_NAME",
default=os.getenv('TMT_PLUGIN_REPORT_REPORTPORTAL_LAUNCH'),
help="The launch name (name of plan per launch is used by default).")
launch_description: Optional[str] = field(
option="--launch-description",
metavar="LAUNCH_DESCRIPTION",
default=os.getenv('TMT_PLUGIN_REPORT_REPORTPORTAL_LAUNCH_DESCRIPTION'),
help="Pass the description for ReportPortal launch, especially with '--suite-per-plan' "
"option (Otherwise Summary from plan fmf data per each launch is used by default).")
## launch_attributes: List[str] = field(
## default_factory=list,
## multiple=True,
## normalize=tmt.utils.normalize_string_list,
## option="--launch-attributes",
## metavar="KEY:VALUE",
## help="Additional attributes to be reported to ReportPortal,"
## "especially launch attributes for merge option.")
launch_per_plan: bool = field(
option="--launch-per-plan",
default=False,
is_flag=True,
help="Mapping launch per plan, creating one or more launches with no suite structure.")
suite_per_plan: bool = field(
option="--suite-per-plan",
default=False,
is_flag=True,
help="Mapping suite per plan, creating one launch and continous uploading suites into it. "
"Can be used with '--upload-to-launch' option to avoid creating a new launch.")
upload_to_launch: Optional[str] = field(
option="--upload-to-launch",
metavar="LAUNCH_ID",
default=None,
help="Pass the launch ID for an additional upload to an existing launch. "
"ID can be found in the launch URL.")
upload_to_suite: Optional[str] = field(
option="--upload-to-suite",
metavar="LAUNCH_SUITE",
default=None,
help="Pass the suite ID for an additional upload to an existing launch. "
"ID can be found in the suite URL.")
launch_rerun: bool = field(
option="--launch-rerun",
default=False,
is_flag=True,
help="Rerun the launch and create Retry version per each test. Mapping is based on unique "
"suite/test names and works with '--suite-per-plan' option only.")
defect_type: Optional[str] = field(
option="--defect-type",
metavar="DEFECT_NAME",
default=None,
help="Pass the defect type to be used for failed test "
"('To Investigate' is used by default).")
exclude_variables: str = field(
option="--exclude-variables",
metavar="PATTERN",
default="^TMT_.*",
help="Regular expression for excluding environment variables "
"from reporting to ReportPortal ('^TMT_.*' used by default).")
attributes: List[str] = field(
default_factory=list,
multiple=True,
normalize=tmt.utils.normalize_string_list,
option="--attributes",
metavar="KEY:VALUE",
help="Additional attributes to be reported to ReportPortal,"
"especially launch attributes for merge option.")
merge: bool = field(
option="--merge",
default=False,
is_flag=True,
help="Report suite per plan and merge them into one launch.")
uuid: Optional[str] = field(
option="--uuid",
metavar="LAUNCH_UUID",
default=None,
help="The launch uuid for additional merging to an existing launch.")
launch_url: str = ""
launch_uuid: str = ""
suite_uuid: str = ""
test_uuids: List[str] = []


@tmt.steps.provides_method("reportportal")
Expand Down Expand Up @@ -149,60 +182,74 @@ def go(self) -> None:
"Content-Type": "application/json"}

launch_time = self.step.plan.execute.results()[0].starttime
merge_bool = self.get("merge")

rerun_bool = False
# TODO: implement rerun/retry

create_launch = True
launch_uuid = ""
suite_uuid = ""
# create launch, suites if "suite_per_plan" and tests; or report to
# existing launch/suite if its id is given
launch_uuid = self.get("upload_to_launch") or self.get(
"launch_uuid") or self.step.plan.my_run.rp_uuid
suite_uuid = self.get("upload_to_suite") or self.get("suite_uuid")
suite_per_plan = self.get("suite_per_plan")
launch_per_plan = self.get("launch_per_plan")
if launch_per_plan and suite_per_plan:
# write warning that only one of the options can be used
launch_per_plan = False
elif launch_per_plan and suite_per_plan is False:
launch_per_plan = True # default
create_launch = ((launch_per_plan or (suite_per_plan and not launch_uuid))
and not suite_uuid)

# if suite_per_plan and suite_uuid == True:
# write warning that combinations of these options is not supported and
# test will be uploaded to the given suite without additional suite creation
create_suite = suite_per_plan and not suite_uuid

## create_launch = True
## launch_uuid = ""
## suite_uuid = ""
launch_url = ""
launch_name = ""

launch_rerun = self.get("launch_rerun")
envar_pattern = self.get("exclude-variables") or "$^"
extra_attributes = self.get("attributes")
launch_attributes = [
{'key': attribute.split(':', 2)[0], 'value': attribute.split(':', 2)[1]}
for attribute in extra_attributes] or []
defect_type = self.get("defect_type")

## extra_attributes = self.get("attributes")
## launch_attributes = [
## {'key': attribute.split(':', 2)[0], 'value': attribute.split(':', 2)[1]}
## for attribute in extra_attributes] or []
attributes = [
{'key': key, 'value': value[0]}
for key, value in self.step.plan._fmf_context.items()]
for attr in launch_attributes:
if attr not in attributes:
attributes.append(attr)
## for attr in launch_attributes:
## if attr not in attributes:
## attributes.append(attr)
if suite_per_plan:
launch_attributes = ""
# TODO:
# get common attributes from all plans
else:
launch_attributes = attributes.copy()

launch_description = self.get("launch_description") or self.step.plan.summary

# Communication with RP instance
with tmt.utils.retry_session() as session:

# get defect type locator
response = session.get(
url=f"{url}/settings",
headers=headers)
self.handle_response(response)
defect_types = yaml_to_dict(response.text).get("subTypes")
dt_tmp = [dt['locator']
for dt in defect_types['TO_INVESTIGATE'] if dt['longName'] == 'Idle']
dt_locator = dt_tmp[0] if dt_tmp else None
dt_locator = None
# TODO:
# implement 'idle - update'
# cover cases when there is no Idle defect type defined

stored_launch_uuid = self.get("uuid") or self.step.plan.my_run.rp_uuid
if merge_bool and stored_launch_uuid:
create_launch = False
launch_uuid = stored_launch_uuid
if defect_type:
response = session.get(
url=f"{url}/launch/uuid/{launch_uuid}",
url=f"{url}/settings",
headers=headers)
self.handle_response(response)
launch_name = yaml_to_dict(response.text).get("name")
self.verbose("launch", launch_name, color="yellow")
launch_id = yaml_to_dict(response.text).get("id")
launch_url = f"{endpoint}/ui/#{project}/launches/all/{launch_id}"
else:
defect_types = yaml_to_dict(response.text).get("subTypes")
dt_tmp = [dt['locator']
for dt in defect_types['TO_INVESTIGATE'] if dt['longName'] == defect_type]
dt_locator = dt_tmp[0] if dt_tmp else None
# TODO:
# cover cases when there the given defect type is not defined

if create_launch:
# create_launch = True
launch_name = self.get("launch") or self.step.plan.name

Expand All @@ -213,20 +260,29 @@ def go(self) -> None:
headers=headers,
json={
"name": launch_name,
"description": "" if merge_bool else self.step.plan.summary,
"attributes": launch_attributes if merge_bool else attributes,
"description": launch_description,
"attributes": launch_attributes,
"startTime": launch_time,
"rerun": rerun_bool})
"rerun": launch_rerun})
self.handle_response(response)
launch_uuid = yaml_to_dict(response.text).get("id")
assert launch_uuid is not None
if merge_bool:
if suite_per_plan:
self.step.plan.my_run.rp_uuid = launch_uuid
else:
response = session.get(
url=f"{url}/launch/uuid/{launch_uuid}",
headers=headers)
self.handle_response(response)
launch_name = yaml_to_dict(response.text).get("name")
self.verbose("launch", launch_name, color="yellow")
launch_id = yaml_to_dict(response.text).get("id")
launch_url = f"{endpoint}/ui/#{project}/launches/all/{launch_id}"

self.verbose("uuid", launch_uuid, "yellow", shift=1)
self.data.launch_uuid = launch_uuid

if merge_bool:
if create_suite:
# Create a suite
suite_name = self.step.plan.name
self.info("suite", suite_name, color="cyan")
Expand Down Expand Up @@ -262,7 +318,7 @@ def go(self) -> None:
# Create a test item
self.info("test", result.name, color="cyan")
response = session.post(
url=f"{url}/item{f'/{suite_uuid}' if merge_bool else ''}",
url=f"{url}/item{f'/{suite_uuid}' if suite_uuid else ''}",
headers=headers,
json={
"name": result.name,
Expand Down Expand Up @@ -327,7 +383,7 @@ def go(self) -> None:
self.handle_response(response)
launch_time = result.endtime

if merge_bool:
if create_suite:
# Finish the test suite
response = session.put(
url=f"{url}/item/{suite_uuid}",
Expand All @@ -337,6 +393,8 @@ def go(self) -> None:
"endTime": launch_time})
self.handle_response(response)

# TODO:
# If suite-per-plan, finish only when it is the last plan
if create_launch:
# Finish the launch
response = session.put(
Expand Down

0 comments on commit 7de8f2a

Please sign in to comment.