robotframework-robotlog2rqm 1.2.4__tar.gz → 1.4.1__tar.gz
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.
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/PKG-INFO +29 -22
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/README.rst +28 -21
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/CRQM.py +306 -27
- robotframework-robotlog2rqm-1.4.1/RobotLog2RQM/RQM_templates/testsuite.xml +27 -0
- robotframework-robotlog2rqm-1.4.1/RobotLog2RQM/RobotLog2RQM.pdf +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/robotlog2rqm.py +277 -31
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/version.py +2 -2
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/PKG-INFO +29 -22
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/SOURCES.txt +1 -0
- robotframework-robotlog2rqm-1.2.4/RobotLog2RQM/RobotLog2RQM.pdf +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/LICENSE +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/buildrecord.xml +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/configuration.xml +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/executionresult.xml +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/executionworkitem.xml +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/suiteexecutionrecord.xml +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/testcase.xml +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/testsuitelog.xml +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/__init__.py +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/__main__.py +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/dependency_links.txt +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/entry_points.txt +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/requires.txt +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/top_level.txt +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/setup.cfg +0 -0
- {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: robotframework-robotlog2rqm
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.1
|
|
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
|
|
@@ -128,33 +128,40 @@ Use below command to get tools\'s usage:
|
|
|
128
128
|
|
|
129
129
|
The usage should be showed as below:
|
|
130
130
|
|
|
131
|
-
usage: RobotLog2RQM (RobotXMLResult to RQM importer) [-h] [-v] [--recursive]
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
usage: RobotLog2RQM (RobotXMLResult to RQM importer) [-h] [-v] [--testsuite TESTSUITE] [--recursive]
|
|
132
|
+
[--createmissing] [--updatetestcase] [--dryrun] [--stream STREAM] [--baseline BASELINE]
|
|
133
|
+
resultxmlfile host project user password testplan
|
|
134
134
|
|
|
135
|
-
RobotLog2RQM imports XML result files (default: output.xml) generated by the
|
|
135
|
+
RobotLog2RQM imports XML result files (default: output.xml) generated by the
|
|
136
136
|
Robot Framework into an IBM Rational Quality Manager.
|
|
137
137
|
|
|
138
138
|
positional arguments:
|
|
139
|
-
resultxmlfile
|
|
140
|
-
|
|
141
|
-
host
|
|
142
|
-
project
|
|
143
|
-
user
|
|
144
|
-
password
|
|
145
|
-
testplan
|
|
139
|
+
resultxmlfile absolute or relative path to the xml result file
|
|
140
|
+
or directory of result files to be imported.
|
|
141
|
+
host RQM host url.
|
|
142
|
+
project project on RQM.
|
|
143
|
+
user user for RQM login.
|
|
144
|
+
password password for RQM login.
|
|
145
|
+
testplan testplan ID for this execution.
|
|
146
146
|
|
|
147
147
|
optional arguments:
|
|
148
|
-
-h, --help
|
|
149
|
-
-v, --version
|
|
150
|
-
--
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
148
|
+
-h, --help show this help message and exit
|
|
149
|
+
-v, --version Version of the RobotLog2RQM importer.
|
|
150
|
+
--testsuite TESTSUITE
|
|
151
|
+
testsuite ID for this execution. If 'new', then create a new
|
|
152
|
+
testsuite for this execution.
|
|
153
|
+
--recursive if set, then the path is searched recursively for
|
|
154
|
+
log files to be imported.
|
|
155
|
+
--createmissing if set, then all testcases without tcid are created
|
|
156
|
+
when importing.
|
|
157
|
+
--updatetestcase if set, then testcase information on RQM will be updated
|
|
158
|
+
bases on robot testfile.
|
|
159
|
+
--dryrun if set, then verify all input arguments
|
|
160
|
+
(includes RQM authentication) and show what would be done.
|
|
161
|
+
--stream STREAM project stream. Note, requires Configuration Management (CM)
|
|
162
|
+
to be enabled for the project area.
|
|
163
|
+
--baseline BASELINE project baseline. Note, requires Configuration Management (CM),
|
|
164
|
+
or Baselines Only to be enabled for the project area.
|
|
158
165
|
|
|
159
166
|
The below command is simple usage witth all required arguments to import
|
|
160
167
|
Robot Framework results into RQM:
|
|
@@ -112,33 +112,40 @@ The usage should be showed as below:
|
|
|
112
112
|
|
|
113
113
|
::
|
|
114
114
|
|
|
115
|
-
usage: RobotLog2RQM (RobotXMLResult to RQM importer) [-h] [-v] [--recursive]
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
usage: RobotLog2RQM (RobotXMLResult to RQM importer) [-h] [-v] [--testsuite TESTSUITE] [--recursive]
|
|
116
|
+
[--createmissing] [--updatetestcase] [--dryrun] [--stream STREAM] [--baseline BASELINE]
|
|
117
|
+
resultxmlfile host project user password testplan
|
|
118
118
|
|
|
119
|
-
RobotLog2RQM imports XML result files (default: output.xml) generated by the
|
|
119
|
+
RobotLog2RQM imports XML result files (default: output.xml) generated by the
|
|
120
120
|
Robot Framework into an IBM Rational Quality Manager.
|
|
121
121
|
|
|
122
122
|
positional arguments:
|
|
123
|
-
resultxmlfile
|
|
124
|
-
|
|
125
|
-
host
|
|
126
|
-
project
|
|
127
|
-
user
|
|
128
|
-
password
|
|
129
|
-
testplan
|
|
123
|
+
resultxmlfile absolute or relative path to the xml result file
|
|
124
|
+
or directory of result files to be imported.
|
|
125
|
+
host RQM host url.
|
|
126
|
+
project project on RQM.
|
|
127
|
+
user user for RQM login.
|
|
128
|
+
password password for RQM login.
|
|
129
|
+
testplan testplan ID for this execution.
|
|
130
130
|
|
|
131
131
|
optional arguments:
|
|
132
|
-
-h, --help
|
|
133
|
-
-v, --version
|
|
134
|
-
--
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
132
|
+
-h, --help show this help message and exit
|
|
133
|
+
-v, --version Version of the RobotLog2RQM importer.
|
|
134
|
+
--testsuite TESTSUITE
|
|
135
|
+
testsuite ID for this execution. If 'new', then create a new
|
|
136
|
+
testsuite for this execution.
|
|
137
|
+
--recursive if set, then the path is searched recursively for
|
|
138
|
+
log files to be imported.
|
|
139
|
+
--createmissing if set, then all testcases without tcid are created
|
|
140
|
+
when importing.
|
|
141
|
+
--updatetestcase if set, then testcase information on RQM will be updated
|
|
142
|
+
bases on robot testfile.
|
|
143
|
+
--dryrun if set, then verify all input arguments
|
|
144
|
+
(includes RQM authentication) and show what would be done.
|
|
145
|
+
--stream STREAM project stream. Note, requires Configuration Management (CM)
|
|
146
|
+
to be enabled for the project area.
|
|
147
|
+
--baseline BASELINE project baseline. Note, requires Configuration Management (CM),
|
|
148
|
+
or Baselines Only to be enabled for the project area.
|
|
142
149
|
|
|
143
150
|
|
|
144
151
|
The below command is simple usage witth all required arguments to import
|
{robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/CRQM.py
RENAMED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
#
|
|
16
16
|
# File: CRQM.py
|
|
17
17
|
#
|
|
18
|
-
#
|
|
18
|
+
# Initially created by Tran Duy Ngoan(RBVH/ECM11) / January 2021
|
|
19
19
|
#
|
|
20
20
|
# This is CRQMClient class which is used to interact with RQM via RQM REST APIs
|
|
21
21
|
#
|
|
@@ -78,6 +78,14 @@ Parse xml object from file.
|
|
|
78
78
|
exit(1)
|
|
79
79
|
return oTree
|
|
80
80
|
|
|
81
|
+
class Identifier:
|
|
82
|
+
"""
|
|
83
|
+
Identifier class used to identify RQM resource with name and id
|
|
84
|
+
"""
|
|
85
|
+
def __init__(self, name='', id=None):
|
|
86
|
+
self.name = name
|
|
87
|
+
self.id = id
|
|
88
|
+
|
|
81
89
|
#
|
|
82
90
|
# IBM Rational Quality Manager
|
|
83
91
|
#
|
|
@@ -87,7 +95,7 @@ class CRQMClient():
|
|
|
87
95
|
CRQMClient class uses RQM REST APIs to get, create and update resources
|
|
88
96
|
(testplan, testcase, test result, ...) on RQM - Rational Quality Manager
|
|
89
97
|
|
|
90
|
-
|
|
98
|
+
Resource type mapping:
|
|
91
99
|
|
|
92
100
|
* buildrecord: Build Record
|
|
93
101
|
* configuration: Test Environment
|
|
@@ -131,6 +139,17 @@ Resoure type mapping:
|
|
|
131
139
|
'ns21' : "http://www.w3.org/1999/XSL/Transform"
|
|
132
140
|
}
|
|
133
141
|
|
|
142
|
+
# define the convention for naming new RQM resource
|
|
143
|
+
SUPPORTED_PLACEHOLDER = ["testplan", "build", "environment", "testsuite", "testcase"]
|
|
144
|
+
NAMING_CONVENTION = {
|
|
145
|
+
"testcase" : "{testcase}",
|
|
146
|
+
"tcer" : "TCER: {testcase}",
|
|
147
|
+
"testresult" : "Execution result: {testcase}",
|
|
148
|
+
"testsuite" : "{testsuite}",
|
|
149
|
+
"tser" : "TSER: {testsuite}",
|
|
150
|
+
"suiteresult" : "Testsuite result: {testsuite}"
|
|
151
|
+
}
|
|
152
|
+
|
|
134
153
|
def __init__(self, user, password, project, host):
|
|
135
154
|
"""
|
|
136
155
|
Constructor of class ``CRQMClient``.
|
|
@@ -175,7 +194,7 @@ Constructor of class ``CRQMClient``.
|
|
|
175
194
|
'OSLC-Core-Version' : '2.0'
|
|
176
195
|
}
|
|
177
196
|
|
|
178
|
-
# Templates location which is
|
|
197
|
+
# Templates location which is used for importing
|
|
179
198
|
self.templatesDir = os.path.join(os.path.dirname(__file__),'RQM_templates')
|
|
180
199
|
|
|
181
200
|
# Data for mapping and linking
|
|
@@ -190,12 +209,17 @@ Constructor of class ``CRQMClient``.
|
|
|
190
209
|
self.lEndTimes = list()
|
|
191
210
|
|
|
192
211
|
# RQM configuration info
|
|
193
|
-
self.testplan = None
|
|
194
|
-
self.build = None
|
|
195
|
-
self.configuration = None
|
|
196
212
|
self.createmissing = None
|
|
197
213
|
self.updatetestcase= None
|
|
198
|
-
self.
|
|
214
|
+
self.testplan = Identifier()
|
|
215
|
+
self.build = Identifier()
|
|
216
|
+
self.configuration = Identifier()
|
|
217
|
+
self.testsuite = Identifier()
|
|
218
|
+
self.stream = Identifier()
|
|
219
|
+
self.baseline = Identifier()
|
|
220
|
+
|
|
221
|
+
# Naming convention
|
|
222
|
+
self.naming_convention = None
|
|
199
223
|
|
|
200
224
|
def login(self):
|
|
201
225
|
"""
|
|
@@ -285,7 +309,8 @@ Disconnect from RQM.
|
|
|
285
309
|
self.session.close()
|
|
286
310
|
|
|
287
311
|
def config(self, plan_id, build_name=None, config_name=None,
|
|
288
|
-
createmissing=False, updatetestcase=False,suite_id=None
|
|
312
|
+
createmissing=False, updatetestcase=False, suite_id=None,
|
|
313
|
+
stream=None, baseline=None, naming_convention=None):
|
|
289
314
|
"""
|
|
290
315
|
Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
291
316
|
|
|
@@ -338,15 +363,61 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
338
363
|
(*no returns*)
|
|
339
364
|
"""
|
|
340
365
|
try:
|
|
366
|
+
self.naming_convention = self.NAMING_CONVENTION
|
|
367
|
+
if naming_convention:
|
|
368
|
+
self.naming_convention.update(naming_convention)
|
|
369
|
+
|
|
341
370
|
self.createmissing = createmissing
|
|
342
371
|
self.updatetestcase = updatetestcase
|
|
343
|
-
self.
|
|
344
|
-
self.
|
|
372
|
+
self.testplan.id = plan_id
|
|
373
|
+
self.testsuite.id = suite_id
|
|
374
|
+
|
|
375
|
+
# Add Configuration-Context header information due to given stream or baseline
|
|
376
|
+
if stream:
|
|
377
|
+
res = self.getAllByResource('stream')
|
|
378
|
+
if res['success']:
|
|
379
|
+
dStreams = res['data']
|
|
380
|
+
bFoundStream = False
|
|
381
|
+
for stream_id, stream_name in dStreams.items():
|
|
382
|
+
if stream_name == stream:
|
|
383
|
+
self.stream.id = stream_id
|
|
384
|
+
self.stream.name = stream_name
|
|
385
|
+
bFoundStream = True
|
|
386
|
+
self.headers['Configuration-Context'] = stream_id
|
|
387
|
+
self.session.headers = self.headers
|
|
388
|
+
break
|
|
389
|
+
|
|
390
|
+
if not bFoundStream:
|
|
391
|
+
raise Exception(f"Cannot found given stream '{stream}'")
|
|
392
|
+
else:
|
|
393
|
+
raise Exception("Get all streams failed. Reason: %s"%res['message'])
|
|
394
|
+
elif baseline:
|
|
395
|
+
res = self.getAllByResource('baseline')
|
|
396
|
+
if res['success']:
|
|
397
|
+
dBaselines = res['data']
|
|
398
|
+
bFoundBaseline = False
|
|
399
|
+
for baseline_id, baseline_name in dBaselines.items():
|
|
400
|
+
if baseline_name == baseline:
|
|
401
|
+
self.baseline.id = baseline_id
|
|
402
|
+
self.baseline.name = baseline_name
|
|
403
|
+
bFoundBaseline = True
|
|
404
|
+
self.headers['Configuration-Context'] = baseline_id
|
|
405
|
+
self.session.headers = self.headers
|
|
406
|
+
break
|
|
407
|
+
|
|
408
|
+
if not bFoundBaseline:
|
|
409
|
+
raise Exception(f"Cannot found given baseline '{baseline}'")
|
|
410
|
+
else:
|
|
411
|
+
raise Exception("Get all baselines failed. Reason: %s"%res['message'])
|
|
412
|
+
|
|
345
413
|
# Verify testplan ID
|
|
346
414
|
res_plan = self.getResourceByID('testplan', plan_id)
|
|
347
415
|
if res_plan.status_code != 200:
|
|
348
416
|
raise Exception('Testplan with ID %s is not existing!'%str(plan_id))
|
|
349
417
|
|
|
418
|
+
oTestplan = get_xml_tree(BytesIO(str(res_plan.text).encode()), bdtd_validation=False)
|
|
419
|
+
self.testplan.name = oTestplan.find('ns4:title', oTestplan.getroot().nsmap).text
|
|
420
|
+
|
|
350
421
|
# Verify and create build version if required
|
|
351
422
|
if build_name != None:
|
|
352
423
|
if build_name == '':
|
|
@@ -354,7 +425,8 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
354
425
|
self.getAllBuildRecords()
|
|
355
426
|
res_build = self.createBuildRecord(build_name)
|
|
356
427
|
if res_build['success'] or res_build['status_code'] == "303":
|
|
357
|
-
self.build = res_build['id']
|
|
428
|
+
self.build.id = res_build['id']
|
|
429
|
+
self.build.name = build_name
|
|
358
430
|
else:
|
|
359
431
|
raise Exception("Cannot create build '%s': %s"%
|
|
360
432
|
(build_name, res_build['message']))
|
|
@@ -366,13 +438,23 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
366
438
|
self.getAllConfigurations()
|
|
367
439
|
res_conf = self.createConfiguration(config_name)
|
|
368
440
|
if res_conf['success'] or res_conf['status_code'] == "303":
|
|
369
|
-
self.configuration = res_conf['id']
|
|
441
|
+
self.configuration.id = res_conf['id']
|
|
442
|
+
self.configuration.name = config_name
|
|
370
443
|
else:
|
|
371
444
|
raise Exception("Cannot create configuration '%s': %s"%
|
|
372
445
|
(config_name, res_conf['message']))
|
|
373
446
|
|
|
447
|
+
# Verify testsuite if given
|
|
448
|
+
if (suite_id != None) and (suite_id != "new"):
|
|
449
|
+
res_suite = self.getResourceByID('testsuite', suite_id)
|
|
450
|
+
if res_suite.status_code != 200:
|
|
451
|
+
raise Exception('Testsuite with ID %s is not existing!'%str(suite_id))
|
|
452
|
+
oTestsuite = get_xml_tree(BytesIO(str(res_suite.text).encode()), bdtd_validation=False)
|
|
453
|
+
self.testsuite.name = oTestsuite.find('ns4:title', oTestsuite.getroot().nsmap).text
|
|
454
|
+
|
|
374
455
|
# get all team-areas for testcase template
|
|
375
456
|
self.getAllTeamAreas()
|
|
457
|
+
|
|
376
458
|
|
|
377
459
|
except Exception as error:
|
|
378
460
|
raise Exception('Configure RQMClient failed: %s'%error)
|
|
@@ -402,7 +484,7 @@ Return interaction URL of provided userID
|
|
|
402
484
|
|
|
403
485
|
def integrationURL(self, resourceType, id=None, forceinternalID=False):
|
|
404
486
|
"""
|
|
405
|
-
Return interaction URL of provided
|
|
487
|
+
Return interaction URL of provided resource and ID.
|
|
406
488
|
The provided ID can be internalID (contains only digits) or externalID.
|
|
407
489
|
|
|
408
490
|
**Arguments:**
|
|
@@ -434,7 +516,7 @@ The provided ID can be internalID (contains only digits) or externalID.
|
|
|
434
516
|
|
|
435
517
|
/ *Type*: str /
|
|
436
518
|
|
|
437
|
-
The interaction URL of provided
|
|
519
|
+
The interaction URL of provided resource and ID.
|
|
438
520
|
"""
|
|
439
521
|
integrationURL = self.host + "/qm/service/com.ibm.rqm.integration.service.IIntegrationService/resources/" + \
|
|
440
522
|
self.projectID + '/' + resourceType
|
|
@@ -486,7 +568,7 @@ Note:
|
|
|
486
568
|
raise Exception("Cannot get ID from response. Reason: %s"%str(error))
|
|
487
569
|
return resultId
|
|
488
570
|
|
|
489
|
-
def webIDfromGeneratedID(self,
|
|
571
|
+
def webIDfromGeneratedID(self, resourceType, generateID):
|
|
490
572
|
"""
|
|
491
573
|
Return web ID (ns2:webId) from generate ID by get resource data from RQM.
|
|
492
574
|
|
|
@@ -497,7 +579,7 @@ Note:
|
|
|
497
579
|
|
|
498
580
|
**Arguments:**
|
|
499
581
|
|
|
500
|
-
* ``
|
|
582
|
+
* ``resourceType``
|
|
501
583
|
|
|
502
584
|
/ *Condition*: required / *Type*: str /
|
|
503
585
|
|
|
@@ -531,8 +613,8 @@ Note:
|
|
|
531
613
|
'testscript',
|
|
532
614
|
'testsuite',
|
|
533
615
|
'testsuitelog']
|
|
534
|
-
if
|
|
535
|
-
resResource = self.getResourceByID(
|
|
616
|
+
if resourceType in lSupportedResources:
|
|
617
|
+
resResource = self.getResourceByID(resourceType, generateID)
|
|
536
618
|
if resResource.status_code == 200:
|
|
537
619
|
oResource = get_xml_tree(BytesIO(str(resResource.text).encode()),
|
|
538
620
|
bdtd_validation=False)
|
|
@@ -543,6 +625,61 @@ Note:
|
|
|
543
625
|
raise Exception("Cannot get web ID of generated testcase!")
|
|
544
626
|
return webID
|
|
545
627
|
|
|
628
|
+
def __genResourceName(self, resource, name):
|
|
629
|
+
"""
|
|
630
|
+
Return the name for given resource bases on the naming convention
|
|
631
|
+
|
|
632
|
+
**Arguments:**
|
|
633
|
+
|
|
634
|
+
* ``resource``
|
|
635
|
+
|
|
636
|
+
/ *Condition*: required / *Type*: str /
|
|
637
|
+
|
|
638
|
+
The RQM resource type.
|
|
639
|
+
|
|
640
|
+
* ``name``
|
|
641
|
+
|
|
642
|
+
/ *Condition*: required / *Type*: str /
|
|
643
|
+
|
|
644
|
+
Relevant resource name.
|
|
645
|
+
|
|
646
|
+
**Returns:**
|
|
647
|
+
|
|
648
|
+
* ``resourceName``
|
|
649
|
+
|
|
650
|
+
/ *Type*: str /
|
|
651
|
+
|
|
652
|
+
Resource name after replacing bases on naming convention.
|
|
653
|
+
"""
|
|
654
|
+
dPlaceHolders = {
|
|
655
|
+
"{testplan}": self.testplan.name
|
|
656
|
+
}
|
|
657
|
+
testcaseRelevant = ["testcase", "tcer", "testresult"]
|
|
658
|
+
testsuiteRelevant = ["testsuite", "tser", "suiteresult"]
|
|
659
|
+
|
|
660
|
+
# Define scope of place holders
|
|
661
|
+
if resource in testcaseRelevant:
|
|
662
|
+
dPlaceHolders.update({
|
|
663
|
+
"{build}": self.build.name,
|
|
664
|
+
"{environment}": self.configuration.name,
|
|
665
|
+
"{testsuite}": self.testsuite.name,
|
|
666
|
+
"{testcase}": name
|
|
667
|
+
})
|
|
668
|
+
elif resource in testsuiteRelevant:
|
|
669
|
+
dPlaceHolders.update({
|
|
670
|
+
"{build}": self.build.name,
|
|
671
|
+
"{environment}": self.configuration.name,
|
|
672
|
+
"{testsuite}": name,
|
|
673
|
+
})
|
|
674
|
+
|
|
675
|
+
try:
|
|
676
|
+
resourceName = self.naming_convention[resource]
|
|
677
|
+
for placeHolder, val in dPlaceHolders.items():
|
|
678
|
+
resourceName = resourceName.replace(placeHolder, val)
|
|
679
|
+
return resourceName
|
|
680
|
+
except:
|
|
681
|
+
raise Exception(f"Failed to generate name for {resource} '{name}'")
|
|
682
|
+
|
|
546
683
|
#
|
|
547
684
|
# Methods to get resources
|
|
548
685
|
#
|
|
@@ -553,7 +690,7 @@ Return data of provided resource and ID by GET method
|
|
|
553
690
|
|
|
554
691
|
**Arguments:**
|
|
555
692
|
|
|
556
|
-
* ``
|
|
693
|
+
* ``resourceType``
|
|
557
694
|
|
|
558
695
|
/ *Condition*: required / *Type*: str /
|
|
559
696
|
|
|
@@ -583,7 +720,7 @@ Return all entries (in all pages) of provided resource by GET method.
|
|
|
583
720
|
|
|
584
721
|
**Arguments:**
|
|
585
722
|
|
|
586
|
-
* ``
|
|
723
|
+
* ``resourceType``
|
|
587
724
|
|
|
588
725
|
/ *Condition*: required / *Type*: str /
|
|
589
726
|
|
|
@@ -845,7 +982,7 @@ Return testcase template from provided information.
|
|
|
845
982
|
root = oTree.getroot()
|
|
846
983
|
nsmap = root.nsmap
|
|
847
984
|
# prepare required data for template
|
|
848
|
-
testcaseTittle = testcaseName
|
|
985
|
+
testcaseTittle = self.__genResourceName('testcase', testcaseName)
|
|
849
986
|
|
|
850
987
|
# find nodes to change data
|
|
851
988
|
oTittle = oTree.find(f'{{{self.NAMESPACES["ns3"]}}}title')
|
|
@@ -958,7 +1095,8 @@ Return testcase execution record template from provided information.
|
|
|
958
1095
|
root = oTree.getroot()
|
|
959
1096
|
nsmap = root.nsmap
|
|
960
1097
|
# prepare required data for template
|
|
961
|
-
TCERTittle = '
|
|
1098
|
+
TCERTittle = self.__genResourceName('tcer', testcaseName)
|
|
1099
|
+
|
|
962
1100
|
|
|
963
1101
|
# Check tcid is internalid or externalid
|
|
964
1102
|
testcaseURL = self.integrationURL('testcase', testcaseID)
|
|
@@ -1104,7 +1242,7 @@ Return testcase execution result template from provided information.
|
|
|
1104
1242
|
nsmap = root.nsmap
|
|
1105
1243
|
# prepare required data for template
|
|
1106
1244
|
prefixState = 'com.ibm.rqm.execution.common.state.'
|
|
1107
|
-
resultTittle = '
|
|
1245
|
+
resultTittle = self.__genResourceName('testresult', testcaseName)
|
|
1108
1246
|
testcaseURL = self.integrationURL('testcase', testcaseID)
|
|
1109
1247
|
testplanURL = self.integrationURL('testplan', testplanID)
|
|
1110
1248
|
TCERURL = self.integrationURL('executionworkitem', TCERID)
|
|
@@ -1287,7 +1425,7 @@ Return testsuite execution record (TSER) template from provided configuration na
|
|
|
1287
1425
|
oTree = get_xml_tree(sTemplatePath, bdtd_validation=False)
|
|
1288
1426
|
root = oTree.getroot()
|
|
1289
1427
|
# prepare required data for template
|
|
1290
|
-
TSERTittle = '
|
|
1428
|
+
TSERTittle = self.__genResourceName('tser', testsuiteName)
|
|
1291
1429
|
testsuiteURL = self.integrationURL('testsuite', testsuiteID)
|
|
1292
1430
|
testplanURL = self.integrationURL('testplan', testplanID)
|
|
1293
1431
|
testerURL = self.userURL(self.userID)
|
|
@@ -1323,7 +1461,7 @@ Return testsuite execution record (TSER) template from provided configuration na
|
|
|
1323
1461
|
return sTSxml
|
|
1324
1462
|
|
|
1325
1463
|
def createTestsuiteResultTemplate(self, testsuiteID, testsuiteName, TSERID,
|
|
1326
|
-
lTCER, lTCResults, startTime='',
|
|
1464
|
+
lTCER, lTCResults, resultState, startTime='',
|
|
1327
1465
|
endTime='', duration='', sOwnerID=''):
|
|
1328
1466
|
"""
|
|
1329
1467
|
Return testsuite execution result template from provided configuration name.
|
|
@@ -1395,9 +1533,10 @@ Return testsuite execution result template from provided configuration name.
|
|
|
1395
1533
|
sTSResultxml = ''
|
|
1396
1534
|
sTemplatePath = os.path.join(self.templatesDir, 'testsuitelog.xml')
|
|
1397
1535
|
oTree = get_xml_tree(sTemplatePath, bdtd_validation=False)
|
|
1536
|
+
prefixState = 'com.ibm.rqm.execution.common.state.'
|
|
1398
1537
|
|
|
1399
1538
|
# prepare required data for template
|
|
1400
|
-
resultTittle = '
|
|
1539
|
+
resultTittle = self.__genResourceName('suiteresult', testsuiteName)
|
|
1401
1540
|
testsuiteURL = self.integrationURL('testsuite', testsuiteID)
|
|
1402
1541
|
TSERURL = self.integrationURL('suiteexecutionrecord', TSERID)
|
|
1403
1542
|
testerURL = self.userURL(self.userID)
|
|
@@ -1438,6 +1577,11 @@ Return testsuite execution result template from provided configuration name.
|
|
|
1438
1577
|
oStarttime.text = str(startTime).replace(' ', 'T')
|
|
1439
1578
|
oEndtime.text = str(endTime).replace(' ', 'T')
|
|
1440
1579
|
oTotalRunTime.text = str(duration)
|
|
1580
|
+
# set default RQM state as inconclusive
|
|
1581
|
+
oState.text = prefixState + 'inconclusive'
|
|
1582
|
+
if resultState.lower() in self.RESULT_STATES:
|
|
1583
|
+
oState.text = prefixState +resultState.lower()
|
|
1584
|
+
|
|
1441
1585
|
for idx, sTCER in enumerate(lTCER):
|
|
1442
1586
|
sTCERURL = self.integrationURL('executionworkitem', sTCER)
|
|
1443
1587
|
oSuiteElem = etree.Element('{http://jazz.net/xmlns/alm/qm/v0.1/tsl/v0.1/}suiteelement', nsmap=nsmap)
|
|
@@ -1459,6 +1603,79 @@ Return testsuite execution result template from provided configuration name.
|
|
|
1459
1603
|
sTSResultxml = etree.tostring(oTree)
|
|
1460
1604
|
return sTSResultxml
|
|
1461
1605
|
|
|
1606
|
+
def createTestsuiteTemplate(self, testsuiteName, sDescription='', sOwnerID='', sTStemplate=None):
|
|
1607
|
+
"""
|
|
1608
|
+
Return testcase template from provided information.
|
|
1609
|
+
|
|
1610
|
+
**Arguments:**
|
|
1611
|
+
|
|
1612
|
+
* ``testsuiteName``
|
|
1613
|
+
|
|
1614
|
+
/ *Condition*: required / *Type*: str /
|
|
1615
|
+
|
|
1616
|
+
Testsuite name.
|
|
1617
|
+
|
|
1618
|
+
* ``sDescription``
|
|
1619
|
+
|
|
1620
|
+
/ *Condition*: optional / *Type*: str / *Default*: '' /
|
|
1621
|
+
|
|
1622
|
+
Testsuite description.
|
|
1623
|
+
|
|
1624
|
+
* ``sOwnerID``
|
|
1625
|
+
|
|
1626
|
+
/ *Condition*: optional / *Type*: str / *Default*: '' /
|
|
1627
|
+
|
|
1628
|
+
User ID of testsuite owner.
|
|
1629
|
+
|
|
1630
|
+
* ``sTStemplate``
|
|
1631
|
+
|
|
1632
|
+
/ *Condition*: optional / *Type*: str / *Default*: None /
|
|
1633
|
+
|
|
1634
|
+
Existing testsuite template as xml string.
|
|
1635
|
+
|
|
1636
|
+
If not provided, template file under `RQM_templates` is used as default.
|
|
1637
|
+
|
|
1638
|
+
**Returns:**
|
|
1639
|
+
|
|
1640
|
+
* ``sTSxml``
|
|
1641
|
+
|
|
1642
|
+
/ *Type*: str /
|
|
1643
|
+
|
|
1644
|
+
The xml testsuite template as string.
|
|
1645
|
+
"""
|
|
1646
|
+
sTSxml = ''
|
|
1647
|
+
if not sTStemplate:
|
|
1648
|
+
sTemplatePath = os.path.join(self.templatesDir ,'testsuite.xml')
|
|
1649
|
+
oTree = get_xml_tree(sTemplatePath, bdtd_validation=False)
|
|
1650
|
+
else:
|
|
1651
|
+
oTree = get_xml_tree(BytesIO(sTStemplate.encode()),bdtd_validation=False)
|
|
1652
|
+
|
|
1653
|
+
root = oTree.getroot()
|
|
1654
|
+
nsmap = root.nsmap
|
|
1655
|
+
|
|
1656
|
+
# prepare required data for template
|
|
1657
|
+
testerURL = self.userURL(self.userID)
|
|
1658
|
+
|
|
1659
|
+
# find nodes to change data
|
|
1660
|
+
oTittle = oTree.find('ns4:title', nsmap)
|
|
1661
|
+
oDescription = oTree.find('ns4:description', nsmap)
|
|
1662
|
+
oOwner = oTree.find('ns6:owner', nsmap)
|
|
1663
|
+
|
|
1664
|
+
# change nodes's data
|
|
1665
|
+
oTittle.text = self.__genResourceName('testsuite', testsuiteName)
|
|
1666
|
+
oDescription.text = sDescription
|
|
1667
|
+
|
|
1668
|
+
# Incase not specify owner in template or input data, set it as provided user in cli
|
|
1669
|
+
if sOwnerID:
|
|
1670
|
+
oOwner.text = sOwnerID
|
|
1671
|
+
oOwner.attrib['{%s}resource' % nsmap['ns1']] = self.userURL(sOwnerID)
|
|
1672
|
+
elif not oOwner.text:
|
|
1673
|
+
oOwner.text = self.userID
|
|
1674
|
+
oOwner.attrib['{%s}resource' % nsmap['ns1']] = testerURL
|
|
1675
|
+
|
|
1676
|
+
# return xml template as string
|
|
1677
|
+
sTSxml = etree.tostring(oTree)
|
|
1678
|
+
return sTSxml
|
|
1462
1679
|
#
|
|
1463
1680
|
# Methods to create RQM resources
|
|
1464
1681
|
#
|
|
@@ -1643,7 +1860,6 @@ Create new configuration - test environment.
|
|
|
1643
1860
|
"""
|
|
1644
1861
|
returnObj = {'success' : False, 'id': None, 'message': '', 'status_code': ''}
|
|
1645
1862
|
# check existing build record in this executioon
|
|
1646
|
-
sConfID = ''
|
|
1647
1863
|
if (sConfigurationName not in self.dConfiguation.values()) or forceCreate:
|
|
1648
1864
|
sConfTemplate = self.createConfigurationTemplate(sConfigurationName)
|
|
1649
1865
|
returnObj = self.createResource('configuration', sConfTemplate)
|
|
@@ -1827,3 +2043,66 @@ Link list of test cases to provided testsuite ID
|
|
|
1827
2043
|
else:
|
|
1828
2044
|
returnObj['message'] = "No testcase for linking."
|
|
1829
2045
|
return returnObj
|
|
2046
|
+
|
|
2047
|
+
def addTestsuite2Testplan(self, testplanID, testsuiteID=None):
|
|
2048
|
+
"""
|
|
2049
|
+
Add testsuite ID to provided testplan ID
|
|
2050
|
+
|
|
2051
|
+
**Arguments:**
|
|
2052
|
+
|
|
2053
|
+
* ``testplanID``
|
|
2054
|
+
|
|
2055
|
+
/ *Condition*: required / *Type*: str /
|
|
2056
|
+
|
|
2057
|
+
Testplan ID to link given testsuite ID.
|
|
2058
|
+
|
|
2059
|
+
* ``testsuiteID``
|
|
2060
|
+
|
|
2061
|
+
/ *Condition*: optional / *Type*: str / *Default*: None /
|
|
2062
|
+
|
|
2063
|
+
Testsuite to be linked with given testplan.
|
|
2064
|
+
|
|
2065
|
+
If not provide, `testsuite.id` value will be used as id of testsuite.
|
|
2066
|
+
|
|
2067
|
+
**Returns:**
|
|
2068
|
+
|
|
2069
|
+
* ``returnObj``
|
|
2070
|
+
|
|
2071
|
+
/ *Type*: dict /
|
|
2072
|
+
|
|
2073
|
+
Response dictionary which contains status and error message.
|
|
2074
|
+
|
|
2075
|
+
Example:
|
|
2076
|
+
|
|
2077
|
+
.. code:: python
|
|
2078
|
+
|
|
2079
|
+
{
|
|
2080
|
+
'success' : False,
|
|
2081
|
+
'message': ''
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
"""
|
|
2085
|
+
returnObj = {'success' : False, 'message': ''}
|
|
2086
|
+
if testsuiteID == None:
|
|
2087
|
+
testsuiteID = self.testsuite.id
|
|
2088
|
+
if testsuiteID:
|
|
2089
|
+
resTestplanData = self.getResourceByID('testplan', testplanID)
|
|
2090
|
+
oTree = get_xml_tree(BytesIO(str(resTestplanData.text).encode()),bdtd_validation=False)
|
|
2091
|
+
# RQM XML response using namespace for nodes
|
|
2092
|
+
# use namespace mapping from root for access response XML
|
|
2093
|
+
root = oTree.getroot()
|
|
2094
|
+
|
|
2095
|
+
sTestsuiteURL = self.integrationURL('testsuite', testsuiteID)
|
|
2096
|
+
oTS = etree.Element('{http://jazz.net/xmlns/alm/qm/v0.1/}testsuite', nsmap=root.nsmap)
|
|
2097
|
+
oTS.set('href', sTestsuiteURL)
|
|
2098
|
+
root.append(oTS)
|
|
2099
|
+
|
|
2100
|
+
# Update test plan data with linked testsuite and PUT to RQM
|
|
2101
|
+
resUpdateTestplan = self.updateResourceByID('testplan', testplanID, etree.tostring(oTree))
|
|
2102
|
+
if resUpdateTestplan.status_code == 200:
|
|
2103
|
+
returnObj['success'] = True
|
|
2104
|
+
else:
|
|
2105
|
+
returnObj['message'] = str(resUpdateTestplan.reason)
|
|
2106
|
+
else:
|
|
2107
|
+
returnObj['message'] = "No testsuite for adding."
|
|
2108
|
+
return returnObj
|