robotframework-robotlog2rqm 1.4.0__py3-none-any.whl → 1.4.1__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 +122 -37
- RobotLog2RQM/RobotLog2RQM.pdf +0 -0
- RobotLog2RQM/robotlog2rqm.py +141 -14
- RobotLog2RQM/version.py +2 -2
- {robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/METADATA +1 -1
- {robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/RECORD +10 -10
- {robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/LICENSE +0 -0
- {robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/WHEEL +0 -0
- {robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/entry_points.txt +0 -0
- {robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/top_level.txt +0 -0
RobotLog2RQM/CRQM.py
CHANGED
|
@@ -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,17 +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.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
self.stream =
|
|
203
|
-
self.baseline =
|
|
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
|
|
204
223
|
|
|
205
224
|
def login(self):
|
|
206
225
|
"""
|
|
@@ -291,7 +310,7 @@ Disconnect from RQM.
|
|
|
291
310
|
|
|
292
311
|
def config(self, plan_id, build_name=None, config_name=None,
|
|
293
312
|
createmissing=False, updatetestcase=False, suite_id=None,
|
|
294
|
-
stream=None, baseline=None):
|
|
313
|
+
stream=None, baseline=None, naming_convention=None):
|
|
295
314
|
"""
|
|
296
315
|
Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
297
316
|
|
|
@@ -344,10 +363,14 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
344
363
|
(*no returns*)
|
|
345
364
|
"""
|
|
346
365
|
try:
|
|
366
|
+
self.naming_convention = self.NAMING_CONVENTION
|
|
367
|
+
if naming_convention:
|
|
368
|
+
self.naming_convention.update(naming_convention)
|
|
369
|
+
|
|
347
370
|
self.createmissing = createmissing
|
|
348
371
|
self.updatetestcase = updatetestcase
|
|
349
|
-
self.testplan = plan_id
|
|
350
|
-
self.testsuite
|
|
372
|
+
self.testplan.id = plan_id
|
|
373
|
+
self.testsuite.id = suite_id
|
|
351
374
|
|
|
352
375
|
# Add Configuration-Context header information due to given stream or baseline
|
|
353
376
|
if stream:
|
|
@@ -357,7 +380,8 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
357
380
|
bFoundStream = False
|
|
358
381
|
for stream_id, stream_name in dStreams.items():
|
|
359
382
|
if stream_name == stream:
|
|
360
|
-
self.stream = stream_id
|
|
383
|
+
self.stream.id = stream_id
|
|
384
|
+
self.stream.name = stream_name
|
|
361
385
|
bFoundStream = True
|
|
362
386
|
self.headers['Configuration-Context'] = stream_id
|
|
363
387
|
self.session.headers = self.headers
|
|
@@ -374,7 +398,8 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
374
398
|
bFoundBaseline = False
|
|
375
399
|
for baseline_id, baseline_name in dBaselines.items():
|
|
376
400
|
if baseline_name == baseline:
|
|
377
|
-
self.baseline = baseline_id
|
|
401
|
+
self.baseline.id = baseline_id
|
|
402
|
+
self.baseline.name = baseline_name
|
|
378
403
|
bFoundBaseline = True
|
|
379
404
|
self.headers['Configuration-Context'] = baseline_id
|
|
380
405
|
self.session.headers = self.headers
|
|
@@ -390,6 +415,9 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
390
415
|
if res_plan.status_code != 200:
|
|
391
416
|
raise Exception('Testplan with ID %s is not existing!'%str(plan_id))
|
|
392
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
|
+
|
|
393
421
|
# Verify and create build version if required
|
|
394
422
|
if build_name != None:
|
|
395
423
|
if build_name == '':
|
|
@@ -397,7 +425,8 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
397
425
|
self.getAllBuildRecords()
|
|
398
426
|
res_build = self.createBuildRecord(build_name)
|
|
399
427
|
if res_build['success'] or res_build['status_code'] == "303":
|
|
400
|
-
self.build = res_build['id']
|
|
428
|
+
self.build.id = res_build['id']
|
|
429
|
+
self.build.name = build_name
|
|
401
430
|
else:
|
|
402
431
|
raise Exception("Cannot create build '%s': %s"%
|
|
403
432
|
(build_name, res_build['message']))
|
|
@@ -409,7 +438,8 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
409
438
|
self.getAllConfigurations()
|
|
410
439
|
res_conf = self.createConfiguration(config_name)
|
|
411
440
|
if res_conf['success'] or res_conf['status_code'] == "303":
|
|
412
|
-
self.configuration = res_conf['id']
|
|
441
|
+
self.configuration.id = res_conf['id']
|
|
442
|
+
self.configuration.name = config_name
|
|
413
443
|
else:
|
|
414
444
|
raise Exception("Cannot create configuration '%s': %s"%
|
|
415
445
|
(config_name, res_conf['message']))
|
|
@@ -420,7 +450,7 @@ Configure RQMClient with testplan ID, build, configuration, createmissing, ...
|
|
|
420
450
|
if res_suite.status_code != 200:
|
|
421
451
|
raise Exception('Testsuite with ID %s is not existing!'%str(suite_id))
|
|
422
452
|
oTestsuite = get_xml_tree(BytesIO(str(res_suite.text).encode()), bdtd_validation=False)
|
|
423
|
-
self.testsuite
|
|
453
|
+
self.testsuite.name = oTestsuite.find('ns4:title', oTestsuite.getroot().nsmap).text
|
|
424
454
|
|
|
425
455
|
# get all team-areas for testcase template
|
|
426
456
|
self.getAllTeamAreas()
|
|
@@ -454,7 +484,7 @@ Return interaction URL of provided userID
|
|
|
454
484
|
|
|
455
485
|
def integrationURL(self, resourceType, id=None, forceinternalID=False):
|
|
456
486
|
"""
|
|
457
|
-
Return interaction URL of provided
|
|
487
|
+
Return interaction URL of provided resource and ID.
|
|
458
488
|
The provided ID can be internalID (contains only digits) or externalID.
|
|
459
489
|
|
|
460
490
|
**Arguments:**
|
|
@@ -486,7 +516,7 @@ The provided ID can be internalID (contains only digits) or externalID.
|
|
|
486
516
|
|
|
487
517
|
/ *Type*: str /
|
|
488
518
|
|
|
489
|
-
The interaction URL of provided
|
|
519
|
+
The interaction URL of provided resource and ID.
|
|
490
520
|
"""
|
|
491
521
|
integrationURL = self.host + "/qm/service/com.ibm.rqm.integration.service.IIntegrationService/resources/" + \
|
|
492
522
|
self.projectID + '/' + resourceType
|
|
@@ -538,7 +568,7 @@ Note:
|
|
|
538
568
|
raise Exception("Cannot get ID from response. Reason: %s"%str(error))
|
|
539
569
|
return resultId
|
|
540
570
|
|
|
541
|
-
def webIDfromGeneratedID(self,
|
|
571
|
+
def webIDfromGeneratedID(self, resourceType, generateID):
|
|
542
572
|
"""
|
|
543
573
|
Return web ID (ns2:webId) from generate ID by get resource data from RQM.
|
|
544
574
|
|
|
@@ -549,7 +579,7 @@ Note:
|
|
|
549
579
|
|
|
550
580
|
**Arguments:**
|
|
551
581
|
|
|
552
|
-
* ``
|
|
582
|
+
* ``resourceType``
|
|
553
583
|
|
|
554
584
|
/ *Condition*: required / *Type*: str /
|
|
555
585
|
|
|
@@ -583,8 +613,8 @@ Note:
|
|
|
583
613
|
'testscript',
|
|
584
614
|
'testsuite',
|
|
585
615
|
'testsuitelog']
|
|
586
|
-
if
|
|
587
|
-
resResource = self.getResourceByID(
|
|
616
|
+
if resourceType in lSupportedResources:
|
|
617
|
+
resResource = self.getResourceByID(resourceType, generateID)
|
|
588
618
|
if resResource.status_code == 200:
|
|
589
619
|
oResource = get_xml_tree(BytesIO(str(resResource.text).encode()),
|
|
590
620
|
bdtd_validation=False)
|
|
@@ -595,6 +625,61 @@ Note:
|
|
|
595
625
|
raise Exception("Cannot get web ID of generated testcase!")
|
|
596
626
|
return webID
|
|
597
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
|
+
|
|
598
683
|
#
|
|
599
684
|
# Methods to get resources
|
|
600
685
|
#
|
|
@@ -605,7 +690,7 @@ Return data of provided resource and ID by GET method
|
|
|
605
690
|
|
|
606
691
|
**Arguments:**
|
|
607
692
|
|
|
608
|
-
* ``
|
|
693
|
+
* ``resourceType``
|
|
609
694
|
|
|
610
695
|
/ *Condition*: required / *Type*: str /
|
|
611
696
|
|
|
@@ -635,7 +720,7 @@ Return all entries (in all pages) of provided resource by GET method.
|
|
|
635
720
|
|
|
636
721
|
**Arguments:**
|
|
637
722
|
|
|
638
|
-
* ``
|
|
723
|
+
* ``resourceType``
|
|
639
724
|
|
|
640
725
|
/ *Condition*: required / *Type*: str /
|
|
641
726
|
|
|
@@ -897,7 +982,7 @@ Return testcase template from provided information.
|
|
|
897
982
|
root = oTree.getroot()
|
|
898
983
|
nsmap = root.nsmap
|
|
899
984
|
# prepare required data for template
|
|
900
|
-
testcaseTittle = testcaseName
|
|
985
|
+
testcaseTittle = self.__genResourceName('testcase', testcaseName)
|
|
901
986
|
|
|
902
987
|
# find nodes to change data
|
|
903
988
|
oTittle = oTree.find(f'{{{self.NAMESPACES["ns3"]}}}title')
|
|
@@ -1010,7 +1095,8 @@ Return testcase execution record template from provided information.
|
|
|
1010
1095
|
root = oTree.getroot()
|
|
1011
1096
|
nsmap = root.nsmap
|
|
1012
1097
|
# prepare required data for template
|
|
1013
|
-
TCERTittle = '
|
|
1098
|
+
TCERTittle = self.__genResourceName('tcer', testcaseName)
|
|
1099
|
+
|
|
1014
1100
|
|
|
1015
1101
|
# Check tcid is internalid or externalid
|
|
1016
1102
|
testcaseURL = self.integrationURL('testcase', testcaseID)
|
|
@@ -1156,7 +1242,7 @@ Return testcase execution result template from provided information.
|
|
|
1156
1242
|
nsmap = root.nsmap
|
|
1157
1243
|
# prepare required data for template
|
|
1158
1244
|
prefixState = 'com.ibm.rqm.execution.common.state.'
|
|
1159
|
-
resultTittle = '
|
|
1245
|
+
resultTittle = self.__genResourceName('testresult', testcaseName)
|
|
1160
1246
|
testcaseURL = self.integrationURL('testcase', testcaseID)
|
|
1161
1247
|
testplanURL = self.integrationURL('testplan', testplanID)
|
|
1162
1248
|
TCERURL = self.integrationURL('executionworkitem', TCERID)
|
|
@@ -1339,7 +1425,7 @@ Return testsuite execution record (TSER) template from provided configuration na
|
|
|
1339
1425
|
oTree = get_xml_tree(sTemplatePath, bdtd_validation=False)
|
|
1340
1426
|
root = oTree.getroot()
|
|
1341
1427
|
# prepare required data for template
|
|
1342
|
-
TSERTittle = '
|
|
1428
|
+
TSERTittle = self.__genResourceName('tser', testsuiteName)
|
|
1343
1429
|
testsuiteURL = self.integrationURL('testsuite', testsuiteID)
|
|
1344
1430
|
testplanURL = self.integrationURL('testplan', testplanID)
|
|
1345
1431
|
testerURL = self.userURL(self.userID)
|
|
@@ -1450,7 +1536,7 @@ Return testsuite execution result template from provided configuration name.
|
|
|
1450
1536
|
prefixState = 'com.ibm.rqm.execution.common.state.'
|
|
1451
1537
|
|
|
1452
1538
|
# prepare required data for template
|
|
1453
|
-
resultTittle = '
|
|
1539
|
+
resultTittle = self.__genResourceName('suiteresult', testsuiteName)
|
|
1454
1540
|
testsuiteURL = self.integrationURL('testsuite', testsuiteID)
|
|
1455
1541
|
TSERURL = self.integrationURL('suiteexecutionrecord', TSERID)
|
|
1456
1542
|
testerURL = self.userURL(self.userID)
|
|
@@ -1576,7 +1662,7 @@ Return testcase template from provided information.
|
|
|
1576
1662
|
oOwner = oTree.find('ns6:owner', nsmap)
|
|
1577
1663
|
|
|
1578
1664
|
# change nodes's data
|
|
1579
|
-
oTittle.text = testsuiteName
|
|
1665
|
+
oTittle.text = self.__genResourceName('testsuite', testsuiteName)
|
|
1580
1666
|
oDescription.text = sDescription
|
|
1581
1667
|
|
|
1582
1668
|
# Incase not specify owner in template or input data, set it as provided user in cli
|
|
@@ -1774,7 +1860,6 @@ Create new configuration - test environment.
|
|
|
1774
1860
|
"""
|
|
1775
1861
|
returnObj = {'success' : False, 'id': None, 'message': '', 'status_code': ''}
|
|
1776
1862
|
# check existing build record in this executioon
|
|
1777
|
-
sConfID = ''
|
|
1778
1863
|
if (sConfigurationName not in self.dConfiguation.values()) or forceCreate:
|
|
1779
1864
|
sConfTemplate = self.createConfigurationTemplate(sConfigurationName)
|
|
1780
1865
|
returnObj = self.createResource('configuration', sConfTemplate)
|
|
@@ -1977,7 +2062,7 @@ Add testsuite ID to provided testplan ID
|
|
|
1977
2062
|
|
|
1978
2063
|
Testsuite to be linked with given testplan.
|
|
1979
2064
|
|
|
1980
|
-
If not provide, `testsuite
|
|
2065
|
+
If not provide, `testsuite.id` value will be used as id of testsuite.
|
|
1981
2066
|
|
|
1982
2067
|
**Returns:**
|
|
1983
2068
|
|
|
@@ -1999,7 +2084,7 @@ Add testsuite ID to provided testplan ID
|
|
|
1999
2084
|
"""
|
|
2000
2085
|
returnObj = {'success' : False, 'message': ''}
|
|
2001
2086
|
if testsuiteID == None:
|
|
2002
|
-
testsuiteID = self.testsuite
|
|
2087
|
+
testsuiteID = self.testsuite.id
|
|
2003
2088
|
if testsuiteID:
|
|
2004
2089
|
resTestplanData = self.getResourceByID('testplan', testplanID)
|
|
2005
2090
|
oTree = get_xml_tree(BytesIO(str(resTestplanData.text).encode()),bdtd_validation=False)
|
RobotLog2RQM/RobotLog2RQM.pdf
CHANGED
|
Binary file
|
RobotLog2RQM/robotlog2rqm.py
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
#
|
|
28
28
|
# ******************************************************************************
|
|
29
29
|
|
|
30
|
+
import json
|
|
30
31
|
import re
|
|
31
32
|
import argparse
|
|
32
33
|
import os
|
|
@@ -64,6 +65,15 @@ DEFAULT_METADATA = {
|
|
|
64
65
|
"team-area" : "",
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
NAMING_CONVENTION_SCHEMA = {
|
|
69
|
+
"testcase" : str,
|
|
70
|
+
"tcer" : str,
|
|
71
|
+
"testresult" : str,
|
|
72
|
+
"testsuite" : str,
|
|
73
|
+
"tser" : str,
|
|
74
|
+
"suiteresult" : str
|
|
75
|
+
}
|
|
76
|
+
|
|
67
77
|
#
|
|
68
78
|
# Logger class
|
|
69
79
|
#
|
|
@@ -281,6 +291,108 @@ Convert time string to datetime.
|
|
|
281
291
|
dt=datetime.datetime(tp[0],tp[1],tp[2],tp[3],tp[4],tp[5],tp[6])
|
|
282
292
|
return dt
|
|
283
293
|
|
|
294
|
+
def process_config_file(path_file):
|
|
295
|
+
"""
|
|
296
|
+
Parse and validate content of configuration file
|
|
297
|
+
|
|
298
|
+
**Arguments:**
|
|
299
|
+
|
|
300
|
+
* ``path_file``
|
|
301
|
+
|
|
302
|
+
/ *Condition*: required / *Type*: str /
|
|
303
|
+
|
|
304
|
+
Path to the configuration json file.
|
|
305
|
+
|
|
306
|
+
**Returns:**
|
|
307
|
+
|
|
308
|
+
* ``dConfig``
|
|
309
|
+
|
|
310
|
+
/ *Type*: dict /
|
|
311
|
+
|
|
312
|
+
Content of json file.
|
|
313
|
+
"""
|
|
314
|
+
with open(path_file, encoding='utf-8') as f:
|
|
315
|
+
try:
|
|
316
|
+
dConfig = json.load(f)
|
|
317
|
+
except Exception as reason:
|
|
318
|
+
Logger.log_error(f"Cannot parse the json file '{path_file}'. Reason: {reason}",
|
|
319
|
+
fatal_error=True)
|
|
320
|
+
|
|
321
|
+
if not is_valid_config(dConfig, bExitOnFail=False):
|
|
322
|
+
Logger.log_error(f"Error in naming configuration file '{path_file}'.",
|
|
323
|
+
fatal_error=True)
|
|
324
|
+
return dConfig
|
|
325
|
+
|
|
326
|
+
def is_valid_config(dConfig, dSchema=NAMING_CONVENTION_SCHEMA, bExitOnFail=True):
|
|
327
|
+
"""
|
|
328
|
+
Validate the json configuration base on given schema.
|
|
329
|
+
|
|
330
|
+
Default schema supports below information:
|
|
331
|
+
|
|
332
|
+
.. code:: python
|
|
333
|
+
|
|
334
|
+
NAMING_CONVENTION_SCHEMA = {
|
|
335
|
+
"testcase" : str,
|
|
336
|
+
"tcer" : str,
|
|
337
|
+
"testresult" : str,
|
|
338
|
+
"testsuite" : str,
|
|
339
|
+
"tser" : str,
|
|
340
|
+
"suiteresult" : str
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
**Arguments:**
|
|
344
|
+
|
|
345
|
+
* ``dConfig``
|
|
346
|
+
|
|
347
|
+
/ *Condition*: required / *Type*: dict /
|
|
348
|
+
|
|
349
|
+
Json configuration object to be verified.
|
|
350
|
+
|
|
351
|
+
* ``dSchema``
|
|
352
|
+
|
|
353
|
+
/ *Condition*: optional / *Type*: dict / *Default*: CONFIG_SCHEMA /
|
|
354
|
+
|
|
355
|
+
Schema for the validation.
|
|
356
|
+
|
|
357
|
+
* ``bExitOnFail``
|
|
358
|
+
|
|
359
|
+
/ *Condition*: optional / *Type*: bool / *Default*: True /
|
|
360
|
+
|
|
361
|
+
If True, exit tool in case the validation is fail.
|
|
362
|
+
|
|
363
|
+
**Returns:**
|
|
364
|
+
|
|
365
|
+
* ``bValid``
|
|
366
|
+
|
|
367
|
+
/ *Type*: bool /
|
|
368
|
+
|
|
369
|
+
True if the given json configuration data is valid.
|
|
370
|
+
"""
|
|
371
|
+
bValid = True
|
|
372
|
+
for key in dConfig:
|
|
373
|
+
if key in dSchema.keys():
|
|
374
|
+
if type(dConfig[key]) != dSchema[key]:
|
|
375
|
+
bValid = False
|
|
376
|
+
Logger.log_error(f"Value of '{key}' has wrong type '{type(dConfig[key])}' in configuration json file.",
|
|
377
|
+
fatal_error=bExitOnFail)
|
|
378
|
+
break
|
|
379
|
+
|
|
380
|
+
# TESTCASE_NAME is not available for non-testcase relevant resources
|
|
381
|
+
# TESTSUITE_NAME is not available for `buildrecord` and `configuration` resources
|
|
382
|
+
# Warning user for using wrong place holders
|
|
383
|
+
oMatch = re.search(".*\{(.*)\}.*", dConfig[key])
|
|
384
|
+
if oMatch:
|
|
385
|
+
if oMatch.group(1) not in CRQMClient.SUPPORTED_PLACEHOLDER:
|
|
386
|
+
Logger.log_warning(f"Place holder '{{{oMatch.group(1)}}}' is not supported, it will not be replaced when generating {key} resource")
|
|
387
|
+
|
|
388
|
+
else:
|
|
389
|
+
bValid = False
|
|
390
|
+
Logger.log_error(f"resource '{key}' is not supported in naming conventions json file.",
|
|
391
|
+
fatal_error=bExitOnFail)
|
|
392
|
+
break
|
|
393
|
+
|
|
394
|
+
return bValid
|
|
395
|
+
|
|
284
396
|
def __process_commandline():
|
|
285
397
|
"""
|
|
286
398
|
Process provided argument(s) from command line.
|
|
@@ -296,6 +408,8 @@ Avalable arguments in command line:
|
|
|
296
408
|
- `--testsuite` : RQM testsuite ID. If value is 'new', then create a new testsuite for this execution.
|
|
297
409
|
- `--recursive` : if True, then the path is searched recursively for log files to be imported.
|
|
298
410
|
- `--createmissing` : if True, then all testcases without tcid are created when importing.
|
|
411
|
+
- `--updatetestcase` : if set, then testcase information on RQM will be updated bases on robot testfile.
|
|
412
|
+
- `--naming_config` : configuration json file for naming conventions when creating RQM resources.
|
|
299
413
|
- `--dryrun` : if True, then verify all input arguments (includes RQM authentication) and show what would be done.
|
|
300
414
|
- `--stream` : project stream. Note, requires Configuration Management (CM) to be enabled for the project area.
|
|
301
415
|
- `--baseline` : project baseline. Note, requires Configuration Management (CM), or Baselines Only to be enabled for the project area.
|
|
@@ -335,6 +449,8 @@ Avalable arguments in command line:
|
|
|
335
449
|
help='if set, then all testcases without tcid are created when importing.')
|
|
336
450
|
cmdParser.add_argument('--updatetestcase', action="store_true",
|
|
337
451
|
help='if set, then testcase information on RQM will be updated bases on robot testfile.')
|
|
452
|
+
cmdParser.add_argument('--naming_config', type=str,
|
|
453
|
+
help='configuration json file for naming conventions when creating RQM resources.')
|
|
338
454
|
cmdParser.add_argument('--dryrun',action="store_true",
|
|
339
455
|
help='if set, then verify all input arguments (includes RQM authentication) and show what would be done.')
|
|
340
456
|
cmdParser.add_argument('--stream', type=str,
|
|
@@ -507,9 +623,9 @@ Process robot test for importing to RQM.
|
|
|
507
623
|
_tc_cmpt = metadata_info['component']
|
|
508
624
|
_tc_team = metadata_info['team-area']
|
|
509
625
|
# from RQMClient
|
|
510
|
-
_tc_testplan_id = RQMClient.testplan
|
|
511
|
-
_tc_config_id = RQMClient.configuration
|
|
512
|
-
_tc_build_id = RQMClient.build
|
|
626
|
+
_tc_testplan_id = RQMClient.testplan.id
|
|
627
|
+
_tc_config_id = RQMClient.configuration.id
|
|
628
|
+
_tc_build_id = RQMClient.build.id
|
|
513
629
|
_tc_createmissing = RQMClient.createmissing
|
|
514
630
|
_tc_update = RQMClient.updatetestcase
|
|
515
631
|
# from robot result object
|
|
@@ -663,6 +779,7 @@ Flow to import Robot results to RQM:
|
|
|
663
779
|
* `recursive` : if True, then the path is searched recursively for log files to be imported.
|
|
664
780
|
* `createmissing` : if True, then all testcases without tcid are created when importing.
|
|
665
781
|
* `updatetestcase` : if True, then testcases information on RQM will be updated bases on robot testfile.
|
|
782
|
+
* `naming_config` : configuration json file for naming conventions when creating RQM resources.
|
|
666
783
|
* `dryrun` : if True, then verify all input arguments (includes RQM authentication) and show what would be done.
|
|
667
784
|
* `stream` : project stream. Note, requires Configuration Management (CM) to be enabled for the project area.
|
|
668
785
|
* `baseline` : project baseline. Note, requires Configuration Management (CM), or Baselines Only to be enabled for the project area.
|
|
@@ -710,6 +827,15 @@ Flow to import Robot results to RQM:
|
|
|
710
827
|
result = ExecutionResult(*sources)
|
|
711
828
|
result.configure()
|
|
712
829
|
|
|
830
|
+
# verify given configuration file
|
|
831
|
+
dNamingConvention = None
|
|
832
|
+
if args.naming_config != None:
|
|
833
|
+
if os.path.isfile(args.naming_config):
|
|
834
|
+
dNamingConvention = process_config_file(args.naming_config)
|
|
835
|
+
else:
|
|
836
|
+
Logger.log_error(f"The given naming configuration file is not existing: '{args.naming_config}'",
|
|
837
|
+
fatal_error=True)
|
|
838
|
+
|
|
713
839
|
# 3. Login Rational Quality Management (RQM)
|
|
714
840
|
RQMClient = CRQMClient(args.user, args.password, args.project, args.host)
|
|
715
841
|
try:
|
|
@@ -735,7 +861,8 @@ Flow to import Robot results to RQM:
|
|
|
735
861
|
metadata_info['project'] = None
|
|
736
862
|
RQMClient.config(args.testplan, metadata_info['version_sw'],
|
|
737
863
|
metadata_info['project'], args.createmissing, args.updatetestcase,
|
|
738
|
-
args.testsuite, stream=args.stream, baseline=args.baseline
|
|
864
|
+
args.testsuite, stream=args.stream, baseline=args.baseline,
|
|
865
|
+
naming_convention=dNamingConvention)
|
|
739
866
|
|
|
740
867
|
if args.testsuite == "new":
|
|
741
868
|
# Create new testsuite
|
|
@@ -749,18 +876,18 @@ Flow to import Robot results to RQM:
|
|
|
749
876
|
if res_testsuite['success']:
|
|
750
877
|
_ts_id = res_testsuite['id']
|
|
751
878
|
Logger.log(f"Create testsuite '{result.suite.name}' with ID '{_ts_id}' successfully!")
|
|
752
|
-
RQMClient.testsuite
|
|
753
|
-
RQMClient.testsuite
|
|
879
|
+
RQMClient.testsuite.id = _ts_id
|
|
880
|
+
RQMClient.testsuite.name = result.suite.name
|
|
754
881
|
else:
|
|
755
882
|
Logger.log_error(f"Create testsuite '{result.suite.name}' failed. Reason: {res_testsuite['message']}", fatal_error=True)
|
|
756
883
|
|
|
757
884
|
# Process suite for importing
|
|
758
885
|
process_suite(RQMClient, result.suite)
|
|
759
886
|
|
|
760
|
-
if RQMClient.testsuite
|
|
887
|
+
if RQMClient.testsuite.id:
|
|
761
888
|
if not args.dryrun:
|
|
762
889
|
# Create testsuite execution record if requires
|
|
763
|
-
testsuite_record_data = RQMClient.createTSERTemplate(RQMClient.testsuite
|
|
890
|
+
testsuite_record_data = RQMClient.createTSERTemplate(RQMClient.testsuite.id, RQMClient.testsuite.name, args.testplan, RQMClient.configuration.id)
|
|
764
891
|
res_TSER = RQMClient.createResource('suiteexecutionrecord', testsuite_record_data)
|
|
765
892
|
sTSERID = res_TSER['id']
|
|
766
893
|
Logger.log()
|
|
@@ -769,13 +896,13 @@ Flow to import Robot results to RQM:
|
|
|
769
896
|
elif (res_TSER['status_code'] == 303 or res_TSER['status_code'] == 200) and res_TSER['id'] != '':
|
|
770
897
|
### incase executionworkitem is existing, cannot create new one
|
|
771
898
|
### Use the existing ID for new result
|
|
772
|
-
Logger.log_warning(f"TSER for testsuite {RQMClient.testsuite
|
|
899
|
+
Logger.log_warning(f"TSER for testsuite {RQMClient.testsuite.id} is existing.\nAdd this execution result to existing TSER id: {sTSERID}")
|
|
773
900
|
else:
|
|
774
901
|
Logger.log_error(f"Create TSER failed, {res_TSER['message']}")
|
|
775
902
|
|
|
776
903
|
# Create new testsuite result and link all TCERs
|
|
777
|
-
testsuite_result_data = RQMClient.createTestsuiteResultTemplate(RQMClient.testsuite
|
|
778
|
-
RQMClient.testsuite
|
|
904
|
+
testsuite_result_data = RQMClient.createTestsuiteResultTemplate(RQMClient.testsuite.id,
|
|
905
|
+
RQMClient.testsuite.name,
|
|
779
906
|
sTSERID,
|
|
780
907
|
RQMClient.lTCERIDs,
|
|
781
908
|
RQMClient.lTCResultIDs,
|
|
@@ -793,15 +920,15 @@ Flow to import Robot results to RQM:
|
|
|
793
920
|
|
|
794
921
|
# Link all imported testcase ID(s) with testsuite
|
|
795
922
|
try:
|
|
796
|
-
RQMClient.linkListTestcase2Testsuite(RQMClient.testsuite
|
|
797
|
-
Logger.log(f"Link all imported test cases with testsuite {RQMClient.testsuite
|
|
923
|
+
RQMClient.linkListTestcase2Testsuite(RQMClient.testsuite.id)
|
|
924
|
+
Logger.log(f"Link all imported test cases with testsuite {RQMClient.testsuite.id} successfully.")
|
|
798
925
|
except Exception as reason:
|
|
799
926
|
Logger.log_error(f"Link all imported test cases with testsuite failed.\nReason: {reason}", fatal_error=True)
|
|
800
927
|
|
|
801
928
|
# Add testsuite to given testplan
|
|
802
929
|
try:
|
|
803
930
|
RQMClient.addTestsuite2Testplan(args.testplan)
|
|
804
|
-
Logger.log(f"Add testsuite {RQMClient.testsuite
|
|
931
|
+
Logger.log(f"Add testsuite {RQMClient.testsuite.id} to testplan {args.testplan} successfully.")
|
|
805
932
|
except Exception as reason:
|
|
806
933
|
Logger.log_error(f"Add testsuite to testplan failed.\nReason: {reason}", fatal_error=True)
|
|
807
934
|
|
RobotLog2RQM/version.py
CHANGED
{robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: robotframework-robotlog2rqm
|
|
3
|
-
Version: 1.4.
|
|
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
|
{robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/RECORD
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
RobotLog2RQM/CRQM.py,sha256=
|
|
2
|
-
RobotLog2RQM/RobotLog2RQM.pdf,sha256=
|
|
1
|
+
RobotLog2RQM/CRQM.py,sha256=fyuU9F2e4RCZzkxOb3bShKrWEjPcXDwzi1HGbUmWjxQ,64123
|
|
2
|
+
RobotLog2RQM/RobotLog2RQM.pdf,sha256=sRhG1vvBX00YwBR2V9pKhMBpJH5AMniLHtPf9rQdK6Q,336238
|
|
3
3
|
RobotLog2RQM/__init__.py,sha256=YKDTJjDsnQkr5X-gjjO8opwKUVKm6kc8sIUpURYMk48,596
|
|
4
4
|
RobotLog2RQM/__main__.py,sha256=JabttEncy80antJWeGVmjoXyiF1DyXxkxdW4xLuHzT0,681
|
|
5
|
-
RobotLog2RQM/robotlog2rqm.py,sha256=
|
|
6
|
-
RobotLog2RQM/version.py,sha256=
|
|
5
|
+
RobotLog2RQM/robotlog2rqm.py,sha256=bhPaj43LazYO58mhchMt7ammaf6Qerxq7z2_aq8qesY,33437
|
|
6
|
+
RobotLog2RQM/version.py,sha256=lTcM8gNMLdiczaZBwoctKVZ7UBQ1h8jjocuoi3Qoc9A,917
|
|
7
7
|
RobotLog2RQM/RQM_templates/buildrecord.xml,sha256=uGot7pNOjPR8do0JsJi0Lz3OCU9NMhODRd428QgvHh4,1498
|
|
8
8
|
RobotLog2RQM/RQM_templates/configuration.xml,sha256=NrFDv51fuGhgeMiZuRhQ5q_UJ0u_pWzdxisIF5AJs74,1378
|
|
9
9
|
RobotLog2RQM/RQM_templates/executionresult.xml,sha256=WTp4qDk29peBc0ll6GHVXX_kF_YBsOVjy9vBzoz7_2k,2160
|
|
@@ -12,9 +12,9 @@ RobotLog2RQM/RQM_templates/suiteexecutionrecord.xml,sha256=9GAs2WqZMkFJSNEZULm9B
|
|
|
12
12
|
RobotLog2RQM/RQM_templates/testcase.xml,sha256=zovFKj-37QHn2S8mMA_9RnAJ3zBmDJkJj31yelsnFFI,2167
|
|
13
13
|
RobotLog2RQM/RQM_templates/testsuite.xml,sha256=r2ijEsyPoE7qzCtUxgIHDOEcqUveDN4SMf9HSE9b0ZU,1326
|
|
14
14
|
RobotLog2RQM/RQM_templates/testsuitelog.xml,sha256=l-NlaCyk6Ben76PElXKOHfMlEvyQ-e9MOZ6-F9HvwDQ,1920
|
|
15
|
-
robotframework_robotlog2rqm-1.4.
|
|
16
|
-
robotframework_robotlog2rqm-1.4.
|
|
17
|
-
robotframework_robotlog2rqm-1.4.
|
|
18
|
-
robotframework_robotlog2rqm-1.4.
|
|
19
|
-
robotframework_robotlog2rqm-1.4.
|
|
20
|
-
robotframework_robotlog2rqm-1.4.
|
|
15
|
+
robotframework_robotlog2rqm-1.4.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
16
|
+
robotframework_robotlog2rqm-1.4.1.dist-info/METADATA,sha256=c_3TNFwH0GFeklIBKGmgJoYhnhXSVL1sNMiaw8IqorA,9951
|
|
17
|
+
robotframework_robotlog2rqm-1.4.1.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
|
18
|
+
robotframework_robotlog2rqm-1.4.1.dist-info/entry_points.txt,sha256=-Xug2kDJW2LtcSADEVPtCwa337twCy2iGh5aK7xApHA,73
|
|
19
|
+
robotframework_robotlog2rqm-1.4.1.dist-info/top_level.txt,sha256=jb_Gt6W44FoOLtGfBe7RzqCLaquhihkEWvSI1zjXDHc,13
|
|
20
|
+
robotframework_robotlog2rqm-1.4.1.dist-info/RECORD,,
|
{robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/LICENSE
RENAMED
|
File without changes
|
{robotframework_robotlog2rqm-1.4.0.dist-info → robotframework_robotlog2rqm-1.4.1.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|