robotframework-robotlog2rqm 1.4.0__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.
Files changed (26) hide show
  1. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/PKG-INFO +1 -1
  2. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/CRQM.py +122 -37
  3. robotframework-robotlog2rqm-1.4.1/RobotLog2RQM/RobotLog2RQM.pdf +0 -0
  4. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/robotlog2rqm.py +141 -14
  5. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/version.py +2 -2
  6. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/PKG-INFO +1 -1
  7. robotframework-robotlog2rqm-1.4.0/RobotLog2RQM/RobotLog2RQM.pdf +0 -0
  8. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/LICENSE +0 -0
  9. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/README.rst +0 -0
  10. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/buildrecord.xml +0 -0
  11. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/configuration.xml +0 -0
  12. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/executionresult.xml +0 -0
  13. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/executionworkitem.xml +0 -0
  14. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/suiteexecutionrecord.xml +0 -0
  15. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/testcase.xml +0 -0
  16. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/testsuite.xml +0 -0
  17. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/testsuitelog.xml +0 -0
  18. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/__init__.py +0 -0
  19. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/__main__.py +0 -0
  20. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/SOURCES.txt +0 -0
  21. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/dependency_links.txt +0 -0
  22. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/entry_points.txt +0 -0
  23. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/requires.txt +0 -0
  24. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/top_level.txt +0 -0
  25. {robotframework-robotlog2rqm-1.4.0 → robotframework-robotlog2rqm-1.4.1}/setup.cfg +0 -0
  26. {robotframework-robotlog2rqm-1.4.0 → 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.4.0
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
@@ -15,7 +15,7 @@
15
15
  #
16
16
  # File: CRQM.py
17
17
  #
18
- # Initialy created by Tran Duy Ngoan(RBVH/ECM11) / January 2021
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
- Resoure type mapping:
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 uesd for importing
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.testsuite = {
199
- "id": None,
200
- "name": None
201
- }
202
- self.stream = None
203
- self.baseline = None
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['id'] = suite_id
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['name'] = oTestsuite.find('ns4:title', oTestsuite.getroot().nsmap).text
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 reource and ID.
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 reource and ID.
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, resourrceType, generateID):
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
- * ``resourrceType``
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 resourrceType in lSupportedResources:
587
- resResource = self.getResourceByID(resourrceType, generateID)
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
- * ``resourrceType``
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
- * ``resourrceType``
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 = 'TCER: '+testcaseName
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 = 'Execution result: '+testcaseName
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 = 'TSER: ' + testsuiteName
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 = 'Testsuite result: ' + testsuiteName
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['id']` value will be used as id of 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['id']
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)
@@ -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['id'] = _ts_id
753
- RQMClient.testsuite['name'] = result.suite.name
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['id']:
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['id'], RQMClient.testsuite['name'], args.testplan, RQMClient.configuration)
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['id']} is existing.\nAdd this execution result to existing TSER id: {sTSERID}")
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['id'],
778
- RQMClient.testsuite['name'],
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['id'])
797
- Logger.log(f"Link all imported test cases with testsuite {RQMClient.testsuite['id']} successfully.")
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['id']} to testplan {args.testplan} successfully.")
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
 
@@ -18,6 +18,6 @@
18
18
  #
19
19
  # Version and date of RobotLog2RQM
20
20
  #
21
- VERSION = "1.4.0"
22
- VERSION_DATE = "24.07.2024"
21
+ VERSION = "1.4.1"
22
+ VERSION_DATE = "20.09.2024"
23
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: robotframework-robotlog2rqm
3
- Version: 1.4.0
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