robotframework-robotlog2rqm 1.4.1__py3-none-any.whl → 1.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- RobotLog2RQM/CRQM.py +92 -9
- RobotLog2RQM/RobotLog2RQM.pdf +0 -0
- RobotLog2RQM/logger.py +180 -0
- RobotLog2RQM/robotlog2rqm.py +11 -165
- RobotLog2RQM/rqmtool.py +316 -0
- RobotLog2RQM/version.py +2 -3
- {robotframework_robotlog2rqm-1.4.1.dist-info → robotframework_robotlog2rqm-1.5.0.dist-info}/METADATA +76 -31
- {robotframework_robotlog2rqm-1.4.1.dist-info → robotframework_robotlog2rqm-1.5.0.dist-info}/RECORD +12 -10
- {robotframework_robotlog2rqm-1.4.1.dist-info → robotframework_robotlog2rqm-1.5.0.dist-info}/WHEEL +1 -1
- {robotframework_robotlog2rqm-1.4.1.dist-info → robotframework_robotlog2rqm-1.5.0.dist-info}/entry_points.txt +1 -0
- {robotframework_robotlog2rqm-1.4.1.dist-info → robotframework_robotlog2rqm-1.5.0.dist-info}/LICENSE +0 -0
- {robotframework_robotlog2rqm-1.4.1.dist-info → robotframework_robotlog2rqm-1.5.0.dist-info}/top_level.txt +0 -0
RobotLog2RQM/CRQM.py
CHANGED
|
@@ -147,7 +147,7 @@ Resource type mapping:
|
|
|
147
147
|
"testresult" : "Execution result: {testcase}",
|
|
148
148
|
"testsuite" : "{testsuite}",
|
|
149
149
|
"tser" : "TSER: {testsuite}",
|
|
150
|
-
"suiteresult" : "Testsuite result: {testsuite}"
|
|
150
|
+
"suiteresult" : "Testsuite result: {testsuite}"
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
def __init__(self, user, password, project, host):
|
|
@@ -309,7 +309,7 @@ Disconnect from RQM.
|
|
|
309
309
|
self.session.close()
|
|
310
310
|
|
|
311
311
|
def config(self, plan_id, build_name=None, config_name=None,
|
|
312
|
-
createmissing=False, updatetestcase=False, suite_id=None,
|
|
312
|
+
createmissing=False, updatetestcase=False, suite_id=None,
|
|
313
313
|
stream=None, baseline=None, naming_convention=None):
|
|
314
314
|
"""
|
|
315
315
|
Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
@@ -371,7 +371,7 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
371
371
|
self.updatetestcase = updatetestcase
|
|
372
372
|
self.testplan.id = plan_id
|
|
373
373
|
self.testsuite.id = suite_id
|
|
374
|
-
|
|
374
|
+
|
|
375
375
|
# Add Configuration-Context header information due to given stream or baseline
|
|
376
376
|
if stream:
|
|
377
377
|
res = self.getAllByResource('stream')
|
|
@@ -386,7 +386,7 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
386
386
|
self.headers['Configuration-Context'] = stream_id
|
|
387
387
|
self.session.headers = self.headers
|
|
388
388
|
break
|
|
389
|
-
|
|
389
|
+
|
|
390
390
|
if not bFoundStream:
|
|
391
391
|
raise Exception(f"Cannot found given stream '{stream}'")
|
|
392
392
|
else:
|
|
@@ -404,7 +404,7 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
404
404
|
self.headers['Configuration-Context'] = baseline_id
|
|
405
405
|
self.session.headers = self.headers
|
|
406
406
|
break
|
|
407
|
-
|
|
407
|
+
|
|
408
408
|
if not bFoundBaseline:
|
|
409
409
|
raise Exception(f"Cannot found given baseline '{baseline}'")
|
|
410
410
|
else:
|
|
@@ -454,7 +454,7 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
454
454
|
|
|
455
455
|
# get all team-areas for testcase template
|
|
456
456
|
self.getAllTeamAreas()
|
|
457
|
-
|
|
457
|
+
|
|
458
458
|
|
|
459
459
|
except Exception as error:
|
|
460
460
|
raise Exception('Configure RQMClient failed: %s'%error)
|
|
@@ -849,6 +849,83 @@ Example:
|
|
|
849
849
|
else:
|
|
850
850
|
raise Exception(f"Could not get 'team-areas' of project '{self.projectname}'.")
|
|
851
851
|
|
|
852
|
+
def getTestsFromTestplan(self, testplan_id, artifact_types):
|
|
853
|
+
"""
|
|
854
|
+
Get all test cases and test suites associated with a given test plan.
|
|
855
|
+
|
|
856
|
+
**Arguments:**
|
|
857
|
+
|
|
858
|
+
* ``testplan_id``
|
|
859
|
+
|
|
860
|
+
/ *Condition*: required / *Type*: str /
|
|
861
|
+
|
|
862
|
+
The RQM test plan to get test artifact(s).
|
|
863
|
+
|
|
864
|
+
* ``artifact_types``
|
|
865
|
+
|
|
866
|
+
/ *Condition*: required / *Type*: list /
|
|
867
|
+
|
|
868
|
+
List of artifact types (`testcase`, `testsuite`) for fetching.
|
|
869
|
+
|
|
870
|
+
**Returns:**
|
|
871
|
+
|
|
872
|
+
* / *Type*: dict /
|
|
873
|
+
|
|
874
|
+
A dictionary containing fetched artifacts:
|
|
875
|
+
|
|
876
|
+
.. code:: python
|
|
877
|
+
|
|
878
|
+
{
|
|
879
|
+
'testcase': [{'id': ..., 'name': ..., 'url': ...}, ...],
|
|
880
|
+
'testsuite': [{'id': ..., 'name': ..., 'url': ...}, ...]
|
|
881
|
+
}
|
|
882
|
+
"""
|
|
883
|
+
ALLOW_ARTIFACT_TYPES = ['testcase', 'testsuite']
|
|
884
|
+
result = {}
|
|
885
|
+
|
|
886
|
+
if isinstance(artifact_types, str):
|
|
887
|
+
artifact_types = [artifact_types]
|
|
888
|
+
elif not isinstance(artifact_types, (list, tuple)):
|
|
889
|
+
raise TypeError(
|
|
890
|
+
f"Invalid type for 'artifact_types': expected str or list, got {type(artifact_types).__name__}."
|
|
891
|
+
)
|
|
892
|
+
|
|
893
|
+
for item in artifact_types:
|
|
894
|
+
artifact_type = item.lower()
|
|
895
|
+
if artifact_type not in ALLOW_ARTIFACT_TYPES:
|
|
896
|
+
raise ValueError(
|
|
897
|
+
f"Unsupported artifact type '{artifact_type}'. "
|
|
898
|
+
f"Please use one of the following: {', '.join(ALLOW_ARTIFACT_TYPES)}."
|
|
899
|
+
)
|
|
900
|
+
result[artifact_type] = []
|
|
901
|
+
|
|
902
|
+
res = self.getResourceByID('testplan', testplan_id)
|
|
903
|
+
if res.status_code != 200:
|
|
904
|
+
raise Exception(f"Failed to get testplan {testplan_id}: {res.reason}")
|
|
905
|
+
|
|
906
|
+
oTree = get_xml_tree(BytesIO(str(res.text).encode()), bdtd_validation=False)
|
|
907
|
+
root = oTree.getroot()
|
|
908
|
+
nsmap = root.nsmap
|
|
909
|
+
|
|
910
|
+
for item in artifact_types:
|
|
911
|
+
artifact_type = item.lower()
|
|
912
|
+
# Find all linked test artifact
|
|
913
|
+
for oTest in root.findall(f'.//ns2:{artifact_type}', nsmap):
|
|
914
|
+
href = oTest.attrib.get('href')
|
|
915
|
+
if href:
|
|
916
|
+
test_id = href.split('/')[-1]
|
|
917
|
+
# Get testcase name
|
|
918
|
+
test_res = self.getResourceByID(artifact_type, test_id)
|
|
919
|
+
if test_res.status_code == 200:
|
|
920
|
+
test_tree = get_xml_tree(BytesIO(str(test_res.text).encode()), bdtd_validation=False)
|
|
921
|
+
test_name = test_tree.find('ns4:title', test_tree.getroot().nsmap)
|
|
922
|
+
test_web_id = test_tree.find('ns2:webId', test_tree.getroot().nsmap)
|
|
923
|
+
test_url = test_tree.find('ns4:identifier', test_tree.getroot().nsmap)
|
|
924
|
+
result[artifact_type].append({'id': test_web_id.text,
|
|
925
|
+
'name': test_name.text if test_name is not None else '',
|
|
926
|
+
'url': test_url.text})
|
|
927
|
+
|
|
928
|
+
return result
|
|
852
929
|
|
|
853
930
|
#
|
|
854
931
|
# Methods to create XML template for resources
|
|
@@ -1096,7 +1173,7 @@ Return testcase execution record template from provided information.
|
|
|
1096
1173
|
nsmap = root.nsmap
|
|
1097
1174
|
# prepare required data for template
|
|
1098
1175
|
TCERTittle = self.__genResourceName('tcer', testcaseName)
|
|
1099
|
-
|
|
1176
|
+
|
|
1100
1177
|
|
|
1101
1178
|
# Check tcid is internalid or externalid
|
|
1102
1179
|
testcaseURL = self.integrationURL('testcase', testcaseID)
|
|
@@ -1462,7 +1539,7 @@ Return testsuite execution record (TSER) template from provided configuration na
|
|
|
1462
1539
|
|
|
1463
1540
|
def createTestsuiteResultTemplate(self, testsuiteID, testsuiteName, TSERID,
|
|
1464
1541
|
lTCER, lTCResults, resultState, startTime='',
|
|
1465
|
-
endTime='', duration='', sOwnerID=''):
|
|
1542
|
+
endTime='', duration='', sOwnerID='', buildrecordID=''):
|
|
1466
1543
|
"""
|
|
1467
1544
|
Return testsuite execution result template from provided configuration name.
|
|
1468
1545
|
|
|
@@ -1581,7 +1658,7 @@ Return testsuite execution result template from provided configuration name.
|
|
|
1581
1658
|
oState.text = prefixState + 'inconclusive'
|
|
1582
1659
|
if resultState.lower() in self.RESULT_STATES:
|
|
1583
1660
|
oState.text = prefixState +resultState.lower()
|
|
1584
|
-
|
|
1661
|
+
|
|
1585
1662
|
for idx, sTCER in enumerate(lTCER):
|
|
1586
1663
|
sTCERURL = self.integrationURL('executionworkitem', sTCER)
|
|
1587
1664
|
oSuiteElem = etree.Element('{http://jazz.net/xmlns/alm/qm/v0.1/tsl/v0.1/}suiteelement', nsmap=nsmap)
|
|
@@ -1599,6 +1676,12 @@ Return testsuite execution result template from provided configuration name.
|
|
|
1599
1676
|
oExecutionResult.set('href', sTCResultURL)
|
|
1600
1677
|
root.append(oExecutionResult)
|
|
1601
1678
|
|
|
1679
|
+
if buildrecordID:
|
|
1680
|
+
oBuildRecord = etree.Element('{http://jazz.net/xmlns/alm/qm/v0.1/}buildrecord', nsmap=nsmap)
|
|
1681
|
+
buildrecordURL = self.integrationURL('buildrecord', buildrecordID)
|
|
1682
|
+
oBuildRecord.set('href', buildrecordURL)
|
|
1683
|
+
root.append(oBuildRecord)
|
|
1684
|
+
|
|
1602
1685
|
# return xml template as string
|
|
1603
1686
|
sTSResultxml = etree.tostring(oTree)
|
|
1604
1687
|
return sTSResultxml
|
RobotLog2RQM/RobotLog2RQM.pdf
CHANGED
|
Binary file
|
RobotLog2RQM/logger.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Copyright 2020-2023 Robert Bosch GmbH
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ******************************************************************************
|
|
15
|
+
#
|
|
16
|
+
# File: logging.py
|
|
17
|
+
#
|
|
18
|
+
# Initially created by Tran Duy Ngoan(RBVH/EMC51) / October 2025
|
|
19
|
+
#
|
|
20
|
+
# Logging class
|
|
21
|
+
#
|
|
22
|
+
# History:
|
|
23
|
+
#
|
|
24
|
+
# 2025-10-20:
|
|
25
|
+
# - initial version
|
|
26
|
+
#
|
|
27
|
+
# ******************************************************************************
|
|
28
|
+
import sys
|
|
29
|
+
import colorama as col
|
|
30
|
+
import os
|
|
31
|
+
|
|
32
|
+
class Logger():
|
|
33
|
+
"""
|
|
34
|
+
Logger class for logging message.
|
|
35
|
+
"""
|
|
36
|
+
output_logfile = None
|
|
37
|
+
output_console = True
|
|
38
|
+
color_normal = col.Fore.WHITE + col.Style.NORMAL
|
|
39
|
+
color_error = col.Fore.RED + col.Style.BRIGHT
|
|
40
|
+
color_warn = col.Fore.YELLOW + col.Style.BRIGHT
|
|
41
|
+
color_reset = col.Style.RESET_ALL + col.Fore.RESET + col.Back.RESET
|
|
42
|
+
prefix_warn = "WARN: "
|
|
43
|
+
prefix_error = "ERROR: "
|
|
44
|
+
prefix_fatalerror = "FATAL ERROR: "
|
|
45
|
+
prefix_all = ""
|
|
46
|
+
dryrun = False
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def config(cls, output_console=True, output_logfile=None, dryrun=False):
|
|
50
|
+
"""
|
|
51
|
+
Configure Logger class.
|
|
52
|
+
|
|
53
|
+
**Arguments:**
|
|
54
|
+
|
|
55
|
+
* ``output_console``
|
|
56
|
+
|
|
57
|
+
/ *Condition*: optional / *Type*: bool / *Default*: True /
|
|
58
|
+
|
|
59
|
+
Write message to console output.
|
|
60
|
+
|
|
61
|
+
* ``output_logfile``
|
|
62
|
+
|
|
63
|
+
/ *Condition*: optional / *Type*: str / *Default*: None /
|
|
64
|
+
|
|
65
|
+
Path to log file output.
|
|
66
|
+
|
|
67
|
+
* ``dryrun``
|
|
68
|
+
|
|
69
|
+
/ *Condition*: optional / *Type*: bool / *Default*: True /
|
|
70
|
+
|
|
71
|
+
If set, a prefix as 'dryrun' is added for all messages.
|
|
72
|
+
|
|
73
|
+
**Returns:**
|
|
74
|
+
|
|
75
|
+
(*no returns*)
|
|
76
|
+
"""
|
|
77
|
+
cls.output_console = output_console
|
|
78
|
+
cls.output_logfile = output_logfile
|
|
79
|
+
cls.dryrun = dryrun
|
|
80
|
+
if cls.dryrun:
|
|
81
|
+
cls.prefix_all = cls.color_warn + "DRYRUN " + cls.color_reset
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def log(cls, msg='', color=None, indent=0):
|
|
85
|
+
"""
|
|
86
|
+
Write log message to console/file output.
|
|
87
|
+
|
|
88
|
+
**Arguments:**
|
|
89
|
+
|
|
90
|
+
* ``msg``
|
|
91
|
+
|
|
92
|
+
/ *Condition*: optional / *Type*: str / *Default*: '' /
|
|
93
|
+
|
|
94
|
+
Message which is written to output.
|
|
95
|
+
|
|
96
|
+
* ``color``
|
|
97
|
+
|
|
98
|
+
/ *Condition*: optional / *Type*: str / *Default*: None /
|
|
99
|
+
|
|
100
|
+
Color style for the message.
|
|
101
|
+
|
|
102
|
+
* ``indent``
|
|
103
|
+
|
|
104
|
+
/ *Condition*: optional / *Type*: int / *Default*: 0 /
|
|
105
|
+
|
|
106
|
+
Offset indent.
|
|
107
|
+
|
|
108
|
+
**Returns:**
|
|
109
|
+
|
|
110
|
+
(*no returns*)
|
|
111
|
+
"""
|
|
112
|
+
if color is None:
|
|
113
|
+
color = cls.color_normal
|
|
114
|
+
if cls.output_console:
|
|
115
|
+
print(cls.prefix_all + cls.color_reset + color + " "*indent + msg + cls.color_reset)
|
|
116
|
+
if cls.output_logfile is not None and os.path.isfile(cls.output_logfile):
|
|
117
|
+
with open(cls.output_logfile, 'a') as f:
|
|
118
|
+
f.write(" "*indent + msg)
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
@classmethod
|
|
122
|
+
def log_warning(cls, msg, indent=0):
|
|
123
|
+
"""
|
|
124
|
+
Write warning message to console/file output.
|
|
125
|
+
|
|
126
|
+
**Arguments:**
|
|
127
|
+
|
|
128
|
+
* ``msg``
|
|
129
|
+
|
|
130
|
+
/ *Condition*: required / *Type*: str /
|
|
131
|
+
|
|
132
|
+
Warning message which is written to output.
|
|
133
|
+
|
|
134
|
+
* ``indent``
|
|
135
|
+
|
|
136
|
+
/ *Condition*: optional / *Type*: int / *Default*: 0 /
|
|
137
|
+
|
|
138
|
+
Offset indent.
|
|
139
|
+
|
|
140
|
+
**Returns:**
|
|
141
|
+
|
|
142
|
+
(*no returns*)
|
|
143
|
+
"""
|
|
144
|
+
cls.log(cls.prefix_warn+str(msg), cls.color_warn, indent)
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def log_error(cls, msg, fatal_error=False, indent=0):
|
|
148
|
+
"""
|
|
149
|
+
Write error message to console/file output.
|
|
150
|
+
|
|
151
|
+
* ``msg``
|
|
152
|
+
|
|
153
|
+
/ *Condition*: required / *Type*: str /
|
|
154
|
+
|
|
155
|
+
Error message which is written to output.
|
|
156
|
+
|
|
157
|
+
* ``fatal_error``
|
|
158
|
+
|
|
159
|
+
/ *Condition*: optional / *Type*: bool / *Default*: False /
|
|
160
|
+
|
|
161
|
+
If set, tool will terminate after logging error message.
|
|
162
|
+
|
|
163
|
+
* ``indent``
|
|
164
|
+
|
|
165
|
+
/ *Condition*: optional / *Type*: int / *Default*: 0 /
|
|
166
|
+
|
|
167
|
+
Offset indent.
|
|
168
|
+
|
|
169
|
+
**Returns:**
|
|
170
|
+
|
|
171
|
+
(*no returns*)
|
|
172
|
+
"""
|
|
173
|
+
prefix = cls.prefix_error
|
|
174
|
+
if fatal_error:
|
|
175
|
+
prefix = cls.prefix_fatalerror
|
|
176
|
+
|
|
177
|
+
cls.log(prefix+str(msg), cls.color_error, indent)
|
|
178
|
+
if fatal_error:
|
|
179
|
+
cls.log(f"{sys.argv[0]} has been stopped!", cls.color_error)
|
|
180
|
+
exit(1)
|
RobotLog2RQM/robotlog2rqm.py
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
#
|
|
16
16
|
# File: robotlog2rqm.py
|
|
17
17
|
#
|
|
18
|
-
#
|
|
18
|
+
# Initially created by Tran Duy Ngoan(RBVH/ECM11) / January 2021
|
|
19
19
|
#
|
|
20
20
|
# This tool is used to parse the robot framework results output.xml
|
|
21
21
|
# then import them into RQM - IBM Rational Quality Manager
|
|
@@ -31,13 +31,12 @@ import json
|
|
|
31
31
|
import re
|
|
32
32
|
import argparse
|
|
33
33
|
import os
|
|
34
|
-
import sys
|
|
35
34
|
import datetime
|
|
36
|
-
import colorama as col
|
|
37
35
|
|
|
38
36
|
from robot.api import ExecutionResult
|
|
39
37
|
from RobotLog2RQM.CRQM import CRQMClient
|
|
40
38
|
from RobotLog2RQM.version import VERSION, VERSION_DATE
|
|
39
|
+
from RobotLog2RQM.logger import Logger
|
|
41
40
|
|
|
42
41
|
DRESULT_MAPPING = {
|
|
43
42
|
"PASS": "Passed",
|
|
@@ -74,160 +73,6 @@ NAMING_CONVENTION_SCHEMA = {
|
|
|
74
73
|
"suiteresult" : str
|
|
75
74
|
}
|
|
76
75
|
|
|
77
|
-
#
|
|
78
|
-
# Logger class
|
|
79
|
-
#
|
|
80
|
-
########################################################################
|
|
81
|
-
class Logger():
|
|
82
|
-
"""
|
|
83
|
-
Logger class for logging message.
|
|
84
|
-
"""
|
|
85
|
-
output_logfile = None
|
|
86
|
-
output_console = True
|
|
87
|
-
color_normal = col.Fore.WHITE + col.Style.NORMAL
|
|
88
|
-
color_error = col.Fore.RED + col.Style.BRIGHT
|
|
89
|
-
color_warn = col.Fore.YELLOW + col.Style.BRIGHT
|
|
90
|
-
color_reset = col.Style.RESET_ALL + col.Fore.RESET + col.Back.RESET
|
|
91
|
-
prefix_warn = "WARN: "
|
|
92
|
-
prefix_error = "ERROR: "
|
|
93
|
-
prefix_fatalerror = "FATAL ERROR: "
|
|
94
|
-
prefix_all = ""
|
|
95
|
-
dryrun = False
|
|
96
|
-
|
|
97
|
-
@classmethod
|
|
98
|
-
def config(cls, output_console=True, output_logfile=None, dryrun=False):
|
|
99
|
-
"""
|
|
100
|
-
Configure Logger class.
|
|
101
|
-
|
|
102
|
-
**Arguments:**
|
|
103
|
-
|
|
104
|
-
* ``output_console``
|
|
105
|
-
|
|
106
|
-
/ *Condition*: optional / *Type*: bool / *Default*: True /
|
|
107
|
-
|
|
108
|
-
Write message to console output.
|
|
109
|
-
|
|
110
|
-
* ``output_logfile``
|
|
111
|
-
|
|
112
|
-
/ *Condition*: optional / *Type*: str / *Default*: None /
|
|
113
|
-
|
|
114
|
-
Path to log file output.
|
|
115
|
-
|
|
116
|
-
* ``dryrun``
|
|
117
|
-
|
|
118
|
-
/ *Condition*: optional / *Type*: bool / *Default*: True /
|
|
119
|
-
|
|
120
|
-
If set, a prefix as 'dryrun' is added for all messages.
|
|
121
|
-
|
|
122
|
-
**Returns:**
|
|
123
|
-
|
|
124
|
-
(*no returns*)
|
|
125
|
-
"""
|
|
126
|
-
cls.output_console = output_console
|
|
127
|
-
cls.output_logfile = output_logfile
|
|
128
|
-
cls.dryrun = dryrun
|
|
129
|
-
if cls.dryrun:
|
|
130
|
-
cls.prefix_all = cls.color_warn + "DRYRUN " + cls.color_reset
|
|
131
|
-
|
|
132
|
-
@classmethod
|
|
133
|
-
def log(cls, msg='', color=None, indent=0):
|
|
134
|
-
"""
|
|
135
|
-
Write log message to console/file output.
|
|
136
|
-
|
|
137
|
-
**Arguments:**
|
|
138
|
-
|
|
139
|
-
* ``msg``
|
|
140
|
-
|
|
141
|
-
/ *Condition*: optional / *Type*: str / *Default*: '' /
|
|
142
|
-
|
|
143
|
-
Message which is written to output.
|
|
144
|
-
|
|
145
|
-
* ``color``
|
|
146
|
-
|
|
147
|
-
/ *Condition*: optional / *Type*: str / *Default*: None /
|
|
148
|
-
|
|
149
|
-
Color style for the message.
|
|
150
|
-
|
|
151
|
-
* ``indent``
|
|
152
|
-
|
|
153
|
-
/ *Condition*: optional / *Type*: int / *Default*: 0 /
|
|
154
|
-
|
|
155
|
-
Offset indent.
|
|
156
|
-
|
|
157
|
-
**Returns:**
|
|
158
|
-
|
|
159
|
-
(*no returns*)
|
|
160
|
-
"""
|
|
161
|
-
if color==None:
|
|
162
|
-
color = cls.color_normal
|
|
163
|
-
if cls.output_console:
|
|
164
|
-
print(cls.prefix_all + cls.color_reset + color + " "*indent + msg + cls.color_reset)
|
|
165
|
-
if cls.output_logfile!=None and os.path.isfile(cls.output_logfile):
|
|
166
|
-
with open(cls.output_logfile, 'a') as f:
|
|
167
|
-
f.write(" "*indent + msg)
|
|
168
|
-
return
|
|
169
|
-
|
|
170
|
-
@classmethod
|
|
171
|
-
def log_warning(cls, msg, indent=0):
|
|
172
|
-
"""
|
|
173
|
-
Write warning message to console/file output.
|
|
174
|
-
|
|
175
|
-
**Arguments:**
|
|
176
|
-
|
|
177
|
-
* ``msg``
|
|
178
|
-
|
|
179
|
-
/ *Condition*: required / *Type*: str /
|
|
180
|
-
|
|
181
|
-
Warning message which is written to output.
|
|
182
|
-
|
|
183
|
-
* ``indent``
|
|
184
|
-
|
|
185
|
-
/ *Condition*: optional / *Type*: int / *Default*: 0 /
|
|
186
|
-
|
|
187
|
-
Offset indent.
|
|
188
|
-
|
|
189
|
-
**Returns:**
|
|
190
|
-
|
|
191
|
-
(*no returns*)
|
|
192
|
-
"""
|
|
193
|
-
cls.log(cls.prefix_warn+str(msg), cls.color_warn, indent)
|
|
194
|
-
|
|
195
|
-
@classmethod
|
|
196
|
-
def log_error(cls, msg, fatal_error=False, indent=0):
|
|
197
|
-
"""
|
|
198
|
-
Write error message to console/file output.
|
|
199
|
-
|
|
200
|
-
* ``msg``
|
|
201
|
-
|
|
202
|
-
/ *Condition*: required / *Type*: str /
|
|
203
|
-
|
|
204
|
-
Error message which is written to output.
|
|
205
|
-
|
|
206
|
-
* ``fatal_error``
|
|
207
|
-
|
|
208
|
-
/ *Condition*: optional / *Type*: bool / *Default*: False /
|
|
209
|
-
|
|
210
|
-
If set, tool will terminate after logging error message.
|
|
211
|
-
|
|
212
|
-
* ``indent``
|
|
213
|
-
|
|
214
|
-
/ *Condition*: optional / *Type*: int / *Default*: 0 /
|
|
215
|
-
|
|
216
|
-
Offset indent.
|
|
217
|
-
|
|
218
|
-
**Returns:**
|
|
219
|
-
|
|
220
|
-
(*no returns*)
|
|
221
|
-
"""
|
|
222
|
-
prefix = cls.prefix_error
|
|
223
|
-
if fatal_error:
|
|
224
|
-
prefix = cls.prefix_fatalerror
|
|
225
|
-
|
|
226
|
-
cls.log(prefix+str(msg), cls.color_error, indent)
|
|
227
|
-
if fatal_error:
|
|
228
|
-
cls.log(f"{sys.argv[0]} has been stopped!", cls.color_error)
|
|
229
|
-
exit(1)
|
|
230
|
-
|
|
231
76
|
|
|
232
77
|
def get_from_tags(lTags, reInfo):
|
|
233
78
|
"""
|
|
@@ -319,7 +164,7 @@ Parse and validate content of configuration file
|
|
|
319
164
|
fatal_error=True)
|
|
320
165
|
|
|
321
166
|
if not is_valid_config(dConfig, bExitOnFail=False):
|
|
322
|
-
Logger.log_error(f"Error in naming configuration file '{path_file}'.",
|
|
167
|
+
Logger.log_error(f"Error in naming configuration file '{path_file}'.",
|
|
323
168
|
fatal_error=True)
|
|
324
169
|
return dConfig
|
|
325
170
|
|
|
@@ -441,7 +286,7 @@ Avalable arguments in command line:
|
|
|
441
286
|
cmdParser.add_argument('password', type=str, help='password for RQM login.')
|
|
442
287
|
cmdParser.add_argument('testplan', type=str,
|
|
443
288
|
help='testplan ID for this execution.')
|
|
444
|
-
cmdParser.add_argument('--testsuite', type=str,
|
|
289
|
+
cmdParser.add_argument('--testsuite', type=str,
|
|
445
290
|
help="testsuite ID for this execution. If 'new', then create a new testsuite for this execution.")
|
|
446
291
|
cmdParser.add_argument('--recursive',action="store_true",
|
|
447
292
|
help='if set, then the path is searched recursively for log files to be imported.')
|
|
@@ -744,7 +589,7 @@ Process robot test for importing to RQM.
|
|
|
744
589
|
# Append lTestcaseIDs (for linking testplan/testsuite)
|
|
745
590
|
if _tc_id not in RQMClient.lTestcaseIDs:
|
|
746
591
|
RQMClient.lTestcaseIDs.append(_tc_id)
|
|
747
|
-
|
|
592
|
+
|
|
748
593
|
# Collect starttime and endtime for testsuite log creation
|
|
749
594
|
RQMClient.lStartTimes.append(_tc_start_time)
|
|
750
595
|
RQMClient.lEndTimes.append(_tc_end_time)
|
|
@@ -844,7 +689,7 @@ Flow to import Robot results to RQM:
|
|
|
844
689
|
Logger.log()
|
|
845
690
|
Logger.log(f"Login RQM as user '{args.user}' successfully!")
|
|
846
691
|
else:
|
|
847
|
-
Logger.log_error("Could not login to RQM: '
|
|
692
|
+
Logger.log_error("Could not login to RQM: 'Unknown reason'.")
|
|
848
693
|
except Exception as reason:
|
|
849
694
|
Logger.log_error(f"Could not login to RQM: '{str(reason)}'.")
|
|
850
695
|
|
|
@@ -860,7 +705,7 @@ Flow to import Robot results to RQM:
|
|
|
860
705
|
metadata_info['version_sw'] = None
|
|
861
706
|
metadata_info['project'] = None
|
|
862
707
|
RQMClient.config(args.testplan, metadata_info['version_sw'],
|
|
863
|
-
metadata_info['project'], args.createmissing, args.updatetestcase,
|
|
708
|
+
metadata_info['project'], args.createmissing, args.updatetestcase,
|
|
864
709
|
args.testsuite, stream=args.stream, baseline=args.baseline,
|
|
865
710
|
naming_convention=dNamingConvention)
|
|
866
711
|
|
|
@@ -906,10 +751,11 @@ Flow to import Robot results to RQM:
|
|
|
906
751
|
sTSERID,
|
|
907
752
|
RQMClient.lTCERIDs,
|
|
908
753
|
RQMClient.lTCResultIDs,
|
|
909
|
-
DRESULT_MAPPING[result.suite.status]
|
|
754
|
+
DRESULT_MAPPING[result.suite.status],
|
|
755
|
+
buildrecordID=RQMClient.build.id
|
|
910
756
|
)
|
|
911
757
|
res_TSLog = RQMClient.createResource('testsuitelog', testsuite_result_data)
|
|
912
|
-
sSuiteResultID = res_TSLog['id']
|
|
758
|
+
sSuiteResultID = res_TSLog['id']
|
|
913
759
|
if res_TSLog['success']:
|
|
914
760
|
Logger.log(f"Created testsuite result with id {sSuiteResultID} successfully.", indent=2)
|
|
915
761
|
else:
|
|
@@ -939,7 +785,7 @@ Flow to import Robot results to RQM:
|
|
|
939
785
|
Logger.log(f"Link all imported test cases with testplan {args.testplan} successfully.")
|
|
940
786
|
except Exception as reason:
|
|
941
787
|
Logger.log_error(f"Link all imported test cases with testplan failed.\nReason: {reason}", fatal_error=True)
|
|
942
|
-
|
|
788
|
+
|
|
943
789
|
# Update testcase(s) with generated ID(s)
|
|
944
790
|
# Under developing
|
|
945
791
|
|
RobotLog2RQM/rqmtool.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Copyright 2020-2023 Robert Bosch GmbH
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ******************************************************************************
|
|
15
|
+
#
|
|
16
|
+
# File: rqmtool.py
|
|
17
|
+
#
|
|
18
|
+
# Initially created by Tran Duy Ngoan(RBVH/EMC51) / October 2025
|
|
19
|
+
#
|
|
20
|
+
# This tool is used to fetch RQM resources
|
|
21
|
+
#
|
|
22
|
+
# History:
|
|
23
|
+
#
|
|
24
|
+
# 2025-10-20:
|
|
25
|
+
# - initial version
|
|
26
|
+
#
|
|
27
|
+
# ******************************************************************************
|
|
28
|
+
|
|
29
|
+
import argparse
|
|
30
|
+
import os
|
|
31
|
+
import csv
|
|
32
|
+
import json
|
|
33
|
+
|
|
34
|
+
from RobotLog2RQM.CRQM import CRQMClient
|
|
35
|
+
from RobotLog2RQM.logger import Logger
|
|
36
|
+
from RobotLog2RQM.version import VERSION, VERSION_DATE
|
|
37
|
+
|
|
38
|
+
OUTPUT_FORMATS = ['json', 'csv']
|
|
39
|
+
ARTIFACT_TYPES = ['testcase', 'testsuite']
|
|
40
|
+
|
|
41
|
+
def __process_commandline():
|
|
42
|
+
"""
|
|
43
|
+
Process provided argument(s) from command line.
|
|
44
|
+
|
|
45
|
+
**Arguments:**
|
|
46
|
+
|
|
47
|
+
(*no arguments*)
|
|
48
|
+
|
|
49
|
+
**Returns:**
|
|
50
|
+
|
|
51
|
+
/ *Type*: `ArgumentParser` object /
|
|
52
|
+
|
|
53
|
+
ArgumentParser object.
|
|
54
|
+
"""
|
|
55
|
+
parser = argparse.ArgumentParser(
|
|
56
|
+
prog="RQMTool",
|
|
57
|
+
description="Fetch RQM resources."
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
'-v', '--version',
|
|
62
|
+
action='version',
|
|
63
|
+
version=f'v{VERSION} ({VERSION_DATE})',
|
|
64
|
+
help='Version of the RQMTool.'
|
|
65
|
+
)
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
"--host",
|
|
68
|
+
required=True,
|
|
69
|
+
help="RQM server URL."
|
|
70
|
+
)
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"--project",
|
|
73
|
+
required=True,
|
|
74
|
+
help="RQM project."
|
|
75
|
+
)
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"--user",
|
|
78
|
+
required=True,
|
|
79
|
+
help="RQM username."
|
|
80
|
+
)
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"--password",
|
|
83
|
+
required=True,
|
|
84
|
+
help="RQM password."
|
|
85
|
+
)
|
|
86
|
+
parser.add_argument(
|
|
87
|
+
"--testplan",
|
|
88
|
+
required=True,
|
|
89
|
+
help="RQM testplan ID."
|
|
90
|
+
)
|
|
91
|
+
parser.add_argument(
|
|
92
|
+
"--dryrun",
|
|
93
|
+
action="store_true",
|
|
94
|
+
help='if set, then verify all input arguments (includes RQM authentication) and show what would be done.')
|
|
95
|
+
parser.add_argument(
|
|
96
|
+
"--format",
|
|
97
|
+
default="csv",
|
|
98
|
+
choices=OUTPUT_FORMATS,
|
|
99
|
+
help="Output format (csv or json). Default is csv.")
|
|
100
|
+
parser.add_argument(
|
|
101
|
+
"--types",
|
|
102
|
+
default="testcase,testsuite",
|
|
103
|
+
help="Comma-separated list of artifact types to fetch. Allowed: testcase, testsuite.")
|
|
104
|
+
parser.add_argument(
|
|
105
|
+
"--output-dir",
|
|
106
|
+
default=".",
|
|
107
|
+
help="Directory to save output files.")
|
|
108
|
+
parser.add_argument(
|
|
109
|
+
"--basename",
|
|
110
|
+
default="testplan_export",
|
|
111
|
+
help="Base name for output files.")
|
|
112
|
+
return parser.parse_args()
|
|
113
|
+
|
|
114
|
+
def __validate_arguments(arguments):
|
|
115
|
+
"""
|
|
116
|
+
Validate and normalize command line arguments.
|
|
117
|
+
|
|
118
|
+
**Arguments:**
|
|
119
|
+
|
|
120
|
+
* ``arguments``
|
|
121
|
+
|
|
122
|
+
/ *Condition*: required / *Type*: `ArgumentParser` object /
|
|
123
|
+
|
|
124
|
+
Parsed arguments from __process_commandline().
|
|
125
|
+
|
|
126
|
+
**Returns:**
|
|
127
|
+
|
|
128
|
+
* ``arguments``
|
|
129
|
+
|
|
130
|
+
/ *Type*: `ArgumentParser` object /
|
|
131
|
+
|
|
132
|
+
ArgumentParser object.
|
|
133
|
+
"""
|
|
134
|
+
artifact_types = arguments.types
|
|
135
|
+
if isinstance(artifact_types, str):
|
|
136
|
+
artifact_types = [x.strip().lower() for x in artifact_types.split(',')]
|
|
137
|
+
elif isinstance(artifact_types, (list, tuple)):
|
|
138
|
+
artifact_types = [x.lower() for x in artifact_types]
|
|
139
|
+
else:
|
|
140
|
+
raise TypeError(f"Invalid type for '--types': {type(artifact_types).__name__}, expected str or list.")
|
|
141
|
+
|
|
142
|
+
for t in artifact_types:
|
|
143
|
+
if t not in ARTIFACT_TYPES:
|
|
144
|
+
raise ValueError(
|
|
145
|
+
f"Invalid artifact type '{t}'. Allowed: {', '.join(ARTIFACT_TYPES)}."
|
|
146
|
+
)
|
|
147
|
+
arguments.types = artifact_types
|
|
148
|
+
|
|
149
|
+
return arguments
|
|
150
|
+
|
|
151
|
+
def write_json_file(file_name, data):
|
|
152
|
+
"""
|
|
153
|
+
Write data to a JSON file.
|
|
154
|
+
|
|
155
|
+
**Arguments:**
|
|
156
|
+
|
|
157
|
+
* ``file_name``
|
|
158
|
+
|
|
159
|
+
/ *Condition*: required / *Type*: str /
|
|
160
|
+
|
|
161
|
+
Path of the JSON file to write.
|
|
162
|
+
|
|
163
|
+
* ``data``
|
|
164
|
+
|
|
165
|
+
/ *Condition*: required / *Type*: dict /
|
|
166
|
+
|
|
167
|
+
Data to export.
|
|
168
|
+
|
|
169
|
+
**Returns:**
|
|
170
|
+
|
|
171
|
+
(*no returns*)
|
|
172
|
+
"""
|
|
173
|
+
with open(file_name, 'w', encoding='utf-8') as f:
|
|
174
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
175
|
+
Logger.log(f"Exported data to: {file_name}")
|
|
176
|
+
|
|
177
|
+
def write_csv_file(file_name, data, artifact_type):
|
|
178
|
+
"""
|
|
179
|
+
Write data to a CSV file for a specific artifact type.
|
|
180
|
+
|
|
181
|
+
**Arguments:**
|
|
182
|
+
|
|
183
|
+
* ``file_name``
|
|
184
|
+
|
|
185
|
+
/ *Condition*: required / *Type*: str /
|
|
186
|
+
|
|
187
|
+
Path of the CSV file to write.
|
|
188
|
+
|
|
189
|
+
* ``data``
|
|
190
|
+
|
|
191
|
+
/ *Condition*: required / *Type*: dict /
|
|
192
|
+
|
|
193
|
+
Data dictionary containing artifacts.
|
|
194
|
+
|
|
195
|
+
* ``artifact_type``
|
|
196
|
+
|
|
197
|
+
/ *Condition*: required / *Type*: str /
|
|
198
|
+
|
|
199
|
+
Artifact type (`testcase` or `testsuite`) to export.
|
|
200
|
+
|
|
201
|
+
**Returns:**
|
|
202
|
+
|
|
203
|
+
(*no returns*)
|
|
204
|
+
"""
|
|
205
|
+
artifact_data = data.get(artifact_type, [])
|
|
206
|
+
if not artifact_data:
|
|
207
|
+
Logger.log_warning(f"No data for '{artifact_type}', skipping CSV export.")
|
|
208
|
+
return
|
|
209
|
+
|
|
210
|
+
fieldnames = list(data[artifact_type][0].keys()) if data[artifact_type] else ["id", "name"]
|
|
211
|
+
with open(file_name, mode="w", newline='', encoding="utf-8") as f:
|
|
212
|
+
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
|
213
|
+
writer.writeheader()
|
|
214
|
+
for row in data[artifact_type]:
|
|
215
|
+
writer.writerow(row)
|
|
216
|
+
Logger.log(f"Exported {artifact_type} to: {file_name}")
|
|
217
|
+
|
|
218
|
+
def write_output_file(data, output_dir=".", basename="testplan_export", extension="csv", artifact_types=None):
|
|
219
|
+
"""
|
|
220
|
+
Write data to output files (JSON or CSV) according to specified options.
|
|
221
|
+
|
|
222
|
+
**Arguments:**
|
|
223
|
+
|
|
224
|
+
* ``data``
|
|
225
|
+
|
|
226
|
+
/ *Condition*: required / *Type*: dict /
|
|
227
|
+
|
|
228
|
+
Data dictionary containing artifacts.
|
|
229
|
+
|
|
230
|
+
* ``output_dir``
|
|
231
|
+
|
|
232
|
+
/ *Condition*: optional / *Type*: str /
|
|
233
|
+
|
|
234
|
+
Directory to save output files. Default is current directory.
|
|
235
|
+
|
|
236
|
+
* ``basename``
|
|
237
|
+
|
|
238
|
+
/ *Condition*: optional / *Type*: str /
|
|
239
|
+
|
|
240
|
+
Base name for output files. Default is "testplan_export".
|
|
241
|
+
|
|
242
|
+
* ``extension``
|
|
243
|
+
|
|
244
|
+
/ *Condition*: optional / *Type*: str /
|
|
245
|
+
|
|
246
|
+
Output format: `json` or `csv`. Default is `csv`.
|
|
247
|
+
|
|
248
|
+
* ``artifact_types``
|
|
249
|
+
|
|
250
|
+
/ *Condition*: optional / *Type*: list /
|
|
251
|
+
|
|
252
|
+
Artifact types to export. Default is all supported types.
|
|
253
|
+
|
|
254
|
+
**Returns:**
|
|
255
|
+
|
|
256
|
+
(*no returns*)
|
|
257
|
+
"""
|
|
258
|
+
if extension == 'json':
|
|
259
|
+
file_name = os.path.join(output_dir, f"{basename}.json")
|
|
260
|
+
write_json_file(file_name, data)
|
|
261
|
+
else:
|
|
262
|
+
# CSV export: one file per artifact_type
|
|
263
|
+
for artifact_type in (artifact_types or ARTIFACT_TYPES):
|
|
264
|
+
file_name = os.path.join(output_dir, f"{basename}_{artifact_type}s.csv")
|
|
265
|
+
write_csv_file(file_name, data, artifact_type)
|
|
266
|
+
|
|
267
|
+
def RQMTool():
|
|
268
|
+
"""
|
|
269
|
+
Main entry point for RQMTool CLI.
|
|
270
|
+
|
|
271
|
+
**Arguments:**
|
|
272
|
+
|
|
273
|
+
(*no arguments*)
|
|
274
|
+
|
|
275
|
+
**Returns:**
|
|
276
|
+
|
|
277
|
+
(*no returns*)
|
|
278
|
+
"""
|
|
279
|
+
args = __process_commandline()
|
|
280
|
+
__validate_arguments(args)
|
|
281
|
+
Logger.config(dryrun=args.dryrun)
|
|
282
|
+
|
|
283
|
+
RQMClient = CRQMClient(args.user, args.password, args.project, args.host)
|
|
284
|
+
try:
|
|
285
|
+
bSuccess = RQMClient.login()
|
|
286
|
+
if bSuccess:
|
|
287
|
+
Logger.log()
|
|
288
|
+
Logger.log(f"Login RQM as user '{args.user}' successfully!")
|
|
289
|
+
else:
|
|
290
|
+
Logger.log_error("Could not login to RQM: 'Unknown reason'.")
|
|
291
|
+
except Exception as reason:
|
|
292
|
+
Logger.log_error(f"Could not login to RQM: '{str(reason)}'.")
|
|
293
|
+
|
|
294
|
+
if not args.dryrun:
|
|
295
|
+
testplan_data = RQMClient.getTestsFromTestplan(args.testplan, args.types)
|
|
296
|
+
|
|
297
|
+
basename_with_id = f"{args.basename}_{args.testplan}"
|
|
298
|
+
|
|
299
|
+
write_output_file(
|
|
300
|
+
testplan_data,
|
|
301
|
+
output_dir=args.output_dir,
|
|
302
|
+
basename=basename_with_id,
|
|
303
|
+
extension=args.format,
|
|
304
|
+
artifact_types=args.types
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
for artifact_type in args.types:
|
|
308
|
+
items = testplan_data.get(artifact_type, [])
|
|
309
|
+
Logger.log(f"Found {len(items)} {artifact_type}(s)")
|
|
310
|
+
cnt = 1
|
|
311
|
+
for item in items:
|
|
312
|
+
Logger.log(f"{cnt:>3}. {item['id']} - {item['name']}", indent=2)
|
|
313
|
+
cnt += 1
|
|
314
|
+
|
|
315
|
+
if __name__ == "__main__":
|
|
316
|
+
RQMTool()
|
RobotLog2RQM/version.py
CHANGED
{robotframework_robotlog2rqm-1.4.1.dist-info → robotframework_robotlog2rqm-1.5.0.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: robotframework-robotlog2rqm
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.5.0
|
|
4
4
|
Summary: Imports robot result(s) to IBM Rational Quality Manager (RQM)
|
|
5
5
|
Home-page: https://github.com/test-fullautomation/robotframework-robotlog2rqm
|
|
6
6
|
Author: Tran Duy Ngoan
|
|
@@ -21,8 +21,7 @@ Requires-Dist: requests
|
|
|
21
21
|
Requires-Dist: colorama
|
|
22
22
|
Requires-Dist: robotframework
|
|
23
23
|
|
|
24
|
-
RobotLog2RQM Description
|
|
25
|
-
========================
|
|
24
|
+
# RobotLog2RQM Description
|
|
26
25
|
|
|
27
26
|
The Python package **RobotLog2RQM** provides ability to import [Robot
|
|
28
27
|
Framework test
|
|
@@ -42,14 +41,13 @@ Manager](https://www.ibm.com/support/knowledgecenter/SSYMRC_6.0.2/com.ibm.ration
|
|
|
42
41
|
**RobotLog2RQM** tool is operating system independent and only works
|
|
43
42
|
with Python 3.
|
|
44
43
|
|
|
45
|
-
How to install
|
|
46
|
-
--------------
|
|
44
|
+
## How to install
|
|
47
45
|
|
|
48
46
|
**RobotLog2RQM** can be installed in two different ways.
|
|
49
47
|
|
|
50
48
|
1. Installation via PyPi (recommended for users)
|
|
51
49
|
|
|
52
|
-
```
|
|
50
|
+
```
|
|
53
51
|
pip install RobotLog2RQM
|
|
54
52
|
```
|
|
55
53
|
|
|
@@ -61,7 +59,7 @@ How to install
|
|
|
61
59
|
- Clone the **robotframework-robotlog2rqm** repository to your
|
|
62
60
|
machine.
|
|
63
61
|
|
|
64
|
-
```
|
|
62
|
+
```
|
|
65
63
|
git clone https://github.com/test-fullautomation/robotframework-robotlog2rqm.git
|
|
66
64
|
```
|
|
67
65
|
|
|
@@ -76,7 +74,7 @@ How to install
|
|
|
76
74
|
packages you can find in the file `requirements.txt` in the
|
|
77
75
|
repository root folder. Use pip to install them:
|
|
78
76
|
|
|
79
|
-
```
|
|
77
|
+
```
|
|
80
78
|
pip install -r ./requirements.txt
|
|
81
79
|
```
|
|
82
80
|
|
|
@@ -95,7 +93,7 @@ How to install
|
|
|
95
93
|
to find **LaTeX**. This is defined in the **GenPackageDoc**
|
|
96
94
|
configuration file
|
|
97
95
|
|
|
98
|
-
```
|
|
96
|
+
```
|
|
99
97
|
packagedoc\packagedoc_config.json
|
|
100
98
|
```
|
|
101
99
|
|
|
@@ -107,7 +105,7 @@ How to install
|
|
|
107
105
|
|
|
108
106
|
- Use the following command to install **RobotLog2RQM**:
|
|
109
107
|
|
|
110
|
-
```
|
|
108
|
+
```
|
|
111
109
|
python setup.py install
|
|
112
110
|
```
|
|
113
111
|
|
|
@@ -118,8 +116,7 @@ be available (under *Scripts* folder of Python on Windows and
|
|
|
118
116
|
In case above location is added to **PATH** environment variable then
|
|
119
117
|
you can run it directly as operation system\'s command.
|
|
120
118
|
|
|
121
|
-
How to use
|
|
122
|
-
----------
|
|
119
|
+
## How to use
|
|
123
120
|
|
|
124
121
|
**RobotLog2RQM** tool requires the Robot Framework `output.xml` result
|
|
125
122
|
file(s) which will be imported, RQM information(e.g. host url, project,
|
|
@@ -132,15 +129,15 @@ Use below command to get tools\'s usage:
|
|
|
132
129
|
|
|
133
130
|
The usage should be showed as below:
|
|
134
131
|
|
|
135
|
-
usage: RobotLog2RQM (RobotXMLResult to RQM importer) [-h] [-v] [--testsuite TESTSUITE] [--recursive]
|
|
136
|
-
[--createmissing] [--updatetestcase] [--dryrun] [--stream STREAM] [--baseline BASELINE]
|
|
132
|
+
usage: RobotLog2RQM (RobotXMLResult to RQM importer) [-h] [-v] [--testsuite TESTSUITE] [--recursive]
|
|
133
|
+
[--createmissing] [--updatetestcase] [--dryrun] [--stream STREAM] [--baseline BASELINE]
|
|
137
134
|
resultxmlfile host project user password testplan
|
|
138
135
|
|
|
139
|
-
RobotLog2RQM imports XML result files (default: output.xml) generated by the
|
|
136
|
+
RobotLog2RQM imports XML result files (default: output.xml) generated by the
|
|
140
137
|
Robot Framework into an IBM Rational Quality Manager.
|
|
141
138
|
|
|
142
139
|
positional arguments:
|
|
143
|
-
resultxmlfile absolute or relative path to the xml result file
|
|
140
|
+
resultxmlfile absolute or relative path to the xml result file
|
|
144
141
|
or directory of result files to be imported.
|
|
145
142
|
host RQM host url.
|
|
146
143
|
project project on RQM.
|
|
@@ -152,19 +149,19 @@ The usage should be showed as below:
|
|
|
152
149
|
-h, --help show this help message and exit
|
|
153
150
|
-v, --version Version of the RobotLog2RQM importer.
|
|
154
151
|
--testsuite TESTSUITE
|
|
155
|
-
testsuite ID for this execution. If 'new', then create a new
|
|
152
|
+
testsuite ID for this execution. If 'new', then create a new
|
|
156
153
|
testsuite for this execution.
|
|
157
|
-
--recursive if set, then the path is searched recursively for
|
|
154
|
+
--recursive if set, then the path is searched recursively for
|
|
158
155
|
log files to be imported.
|
|
159
|
-
--createmissing if set, then all testcases without tcid are created
|
|
156
|
+
--createmissing if set, then all testcases without tcid are created
|
|
160
157
|
when importing.
|
|
161
|
-
--updatetestcase if set, then testcase information on RQM will be updated
|
|
158
|
+
--updatetestcase if set, then testcase information on RQM will be updated
|
|
162
159
|
bases on robot testfile.
|
|
163
|
-
--dryrun if set, then verify all input arguments
|
|
160
|
+
--dryrun if set, then verify all input arguments
|
|
164
161
|
(includes RQM authentication) and show what would be done.
|
|
165
|
-
--stream STREAM project stream. Note, requires Configuration Management (CM)
|
|
162
|
+
--stream STREAM project stream. Note, requires Configuration Management (CM)
|
|
166
163
|
to be enabled for the project area.
|
|
167
|
-
--baseline BASELINE project baseline. Note, requires Configuration Management (CM),
|
|
164
|
+
--baseline BASELINE project baseline. Note, requires Configuration Management (CM),
|
|
168
165
|
or Baselines Only to be enabled for the project area.
|
|
169
166
|
|
|
170
167
|
The below command is simple usage witth all required arguments to import
|
|
@@ -216,6 +213,58 @@ Then, open RQM with your favourite browser and you will see that the
|
|
|
216
213
|
test case execution records and their results are imported in the given
|
|
217
214
|
testplan ID.
|
|
218
215
|
|
|
216
|
+
# RQMTool Submodule
|
|
217
|
+
|
|
218
|
+
The package now includes a submodule **RQMTool** which provides a
|
|
219
|
+
standalone CLI to fetch test cases and test suites from IBM RQM test
|
|
220
|
+
plans.
|
|
221
|
+
|
|
222
|
+
RQMTool is accessible as a Python module:
|
|
223
|
+
|
|
224
|
+
python -m RobotLog2RQM.rqmtool --host <RQM_SERVER_URL> --project <PROJECT_AREA> \
|
|
225
|
+
--user <USERNAME> --password <PASSWORD> \
|
|
226
|
+
--testplan <TESTPLAN_ID> [--types <artifact_types>] \
|
|
227
|
+
[--format <csv|json>] [--output-dir <DIR>] \
|
|
228
|
+
[--basename <BASENAME>] [--dryrun]
|
|
229
|
+
|
|
230
|
+
Main features:
|
|
231
|
+
|
|
232
|
+
- Fetches selected artifact types ([testcase]{.title-ref} or
|
|
233
|
+
[testsuite]{.title-ref}) from a given test plan.
|
|
234
|
+
- Supports CSV or JSON export, with automatic filenames including the
|
|
235
|
+
test plan ID.
|
|
236
|
+
|
|
237
|
+
## How to use RQMTool
|
|
238
|
+
|
|
239
|
+
RQMTool requires RQM information (host URL, project, credentials) and a
|
|
240
|
+
test plan ID. Use the [-h]{.title-ref} argument to see full usage:
|
|
241
|
+
|
|
242
|
+
python -m RobotLog2RQM.rqmtool -h
|
|
243
|
+
|
|
244
|
+
This will display all available command line options for RQMTool, such
|
|
245
|
+
as:
|
|
246
|
+
|
|
247
|
+
- [\--types]{.title-ref} : artifact types to fetch
|
|
248
|
+
([testcase]{.title-ref}, [testsuite]{.title-ref}). Default: both.
|
|
249
|
+
- \`\--format\`: output format, either [csv]{.title-ref} or
|
|
250
|
+
[json]{.title-ref}.
|
|
251
|
+
- \`\--output-dir\`: directory to save exported files.
|
|
252
|
+
- \`\--basename\`: base name for output files.
|
|
253
|
+
- \`\--dryrun\`: validate inputs and RQM login without fetching data.
|
|
254
|
+
|
|
255
|
+
## Example
|
|
256
|
+
|
|
257
|
+
Fetch all test cases and test suites from test plan ID 720 and export to
|
|
258
|
+
CSV:
|
|
259
|
+
|
|
260
|
+
python -m RobotLog2RQM.rqmtool --host https://sample-rqm-host.com \
|
|
261
|
+
--project ROBFW-AIO \
|
|
262
|
+
--user test_user \
|
|
263
|
+
--password test_pw \
|
|
264
|
+
--testplan 720 \
|
|
265
|
+
--types testcase,testsuite \
|
|
266
|
+
--format csv
|
|
267
|
+
|
|
219
268
|
### Sourcecode Documentation
|
|
220
269
|
|
|
221
270
|
To understand more detail about the tool\'s features and how Robot test
|
|
@@ -223,8 +272,7 @@ cases and their results are reflected on RQM, please refer to
|
|
|
223
272
|
[RobotLog2RQM tool's
|
|
224
273
|
Documentation](https://github.com/test-fullautomation/robotframework-robotlog2rqm/blob/develop/RobotLog2RQM/RobotLog2RQM.pdf).
|
|
225
274
|
|
|
226
|
-
Feedback
|
|
227
|
-
--------
|
|
275
|
+
## Feedback
|
|
228
276
|
|
|
229
277
|
To give us a feedback, you can send an email to [Thomas
|
|
230
278
|
Pollerspöck](mailto:Thomas.Pollerspoeck@de.bosch.com).
|
|
@@ -232,15 +280,13 @@ Pollerspöck](mailto:Thomas.Pollerspoeck@de.bosch.com).
|
|
|
232
280
|
In case you want to report a bug or request any interesting feature,
|
|
233
281
|
please don\'t hesitate to raise a ticket.
|
|
234
282
|
|
|
235
|
-
Maintainers
|
|
236
|
-
-----------
|
|
283
|
+
## Maintainers
|
|
237
284
|
|
|
238
285
|
[Thomas Pollerspöck](mailto:Thomas.Pollerspoeck@de.bosch.com)
|
|
239
286
|
|
|
240
287
|
[Tran Duy Ngoan](mailto:Ngoan.TranDuy@vn.bosch.com)
|
|
241
288
|
|
|
242
|
-
Contributors
|
|
243
|
-
------------
|
|
289
|
+
## Contributors
|
|
244
290
|
|
|
245
291
|
[Nguyen Huynh Tri Cuong](mailto:Cuong.NguyenHuynhTri@vn.bosch.com)
|
|
246
292
|
|
|
@@ -250,8 +296,7 @@ Contributors
|
|
|
250
296
|
|
|
251
297
|
[Holger Queckenstedt](mailto:Holger.Queckenstedt@de.bosch.com)
|
|
252
298
|
|
|
253
|
-
License
|
|
254
|
-
-------
|
|
299
|
+
## License
|
|
255
300
|
|
|
256
301
|
Copyright 2020-2024 Robert Bosch GmbH
|
|
257
302
|
|
{robotframework_robotlog2rqm-1.4.1.dist-info → robotframework_robotlog2rqm-1.5.0.dist-info}/RECORD
RENAMED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
RobotLog2RQM/CRQM.py,sha256=
|
|
2
|
-
RobotLog2RQM/RobotLog2RQM.pdf,sha256=
|
|
1
|
+
RobotLog2RQM/CRQM.py,sha256=TsgEXYu608vb-rjQ9qTbxzql_EPTgVHqnrdkDs3rAus,67139
|
|
2
|
+
RobotLog2RQM/RobotLog2RQM.pdf,sha256=Ujot_99PiYu2s9hS_sQu3fP1occXcbs2WO9qiHSVx6M,349449
|
|
3
3
|
RobotLog2RQM/__init__.py,sha256=YKDTJjDsnQkr5X-gjjO8opwKUVKm6kc8sIUpURYMk48,596
|
|
4
4
|
RobotLog2RQM/__main__.py,sha256=JabttEncy80antJWeGVmjoXyiF1DyXxkxdW4xLuHzT0,681
|
|
5
|
-
RobotLog2RQM/
|
|
6
|
-
RobotLog2RQM/
|
|
5
|
+
RobotLog2RQM/logger.py,sha256=FnZZqssOcrF5yK6wxkgifScoQcFGrqrvzLvXWY3V-9A,4252
|
|
6
|
+
RobotLog2RQM/robotlog2rqm.py,sha256=FeMJsBuAu1CLBjDJsQQs7CjQifYgqyhaPMNabnEeSc0,30160
|
|
7
|
+
RobotLog2RQM/rqmtool.py,sha256=wDagUGptbPwRYyn5jo1RA8HJmwHoDX_iBew40WFtWJ0,7957
|
|
8
|
+
RobotLog2RQM/version.py,sha256=4VudYtnU16wL-FZAKungXo19Q5QacjNSAHMEWv-53JE,916
|
|
7
9
|
RobotLog2RQM/RQM_templates/buildrecord.xml,sha256=uGot7pNOjPR8do0JsJi0Lz3OCU9NMhODRd428QgvHh4,1498
|
|
8
10
|
RobotLog2RQM/RQM_templates/configuration.xml,sha256=NrFDv51fuGhgeMiZuRhQ5q_UJ0u_pWzdxisIF5AJs74,1378
|
|
9
11
|
RobotLog2RQM/RQM_templates/executionresult.xml,sha256=WTp4qDk29peBc0ll6GHVXX_kF_YBsOVjy9vBzoz7_2k,2160
|
|
@@ -12,9 +14,9 @@ RobotLog2RQM/RQM_templates/suiteexecutionrecord.xml,sha256=9GAs2WqZMkFJSNEZULm9B
|
|
|
12
14
|
RobotLog2RQM/RQM_templates/testcase.xml,sha256=zovFKj-37QHn2S8mMA_9RnAJ3zBmDJkJj31yelsnFFI,2167
|
|
13
15
|
RobotLog2RQM/RQM_templates/testsuite.xml,sha256=r2ijEsyPoE7qzCtUxgIHDOEcqUveDN4SMf9HSE9b0ZU,1326
|
|
14
16
|
RobotLog2RQM/RQM_templates/testsuitelog.xml,sha256=l-NlaCyk6Ben76PElXKOHfMlEvyQ-e9MOZ6-F9HvwDQ,1920
|
|
15
|
-
robotframework_robotlog2rqm-1.
|
|
16
|
-
robotframework_robotlog2rqm-1.
|
|
17
|
-
robotframework_robotlog2rqm-1.
|
|
18
|
-
robotframework_robotlog2rqm-1.
|
|
19
|
-
robotframework_robotlog2rqm-1.
|
|
20
|
-
robotframework_robotlog2rqm-1.
|
|
17
|
+
robotframework_robotlog2rqm-1.5.0.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
18
|
+
robotframework_robotlog2rqm-1.5.0.dist-info/METADATA,sha256=mBHsyTik_HMPC-SsoTjqph_S_3MQc_CLj3VViXfgXuI,11846
|
|
19
|
+
robotframework_robotlog2rqm-1.5.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
20
|
+
robotframework_robotlog2rqm-1.5.0.dist-info/entry_points.txt,sha256=94N661T4lHzLSey2WQ18OVduw9-Mf6Kh8HK7cBL2YPY,112
|
|
21
|
+
robotframework_robotlog2rqm-1.5.0.dist-info/top_level.txt,sha256=jb_Gt6W44FoOLtGfBe7RzqCLaquhihkEWvSI1zjXDHc,13
|
|
22
|
+
robotframework_robotlog2rqm-1.5.0.dist-info/RECORD,,
|
{robotframework_robotlog2rqm-1.4.1.dist-info → robotframework_robotlog2rqm-1.5.0.dist-info}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|