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.
Files changed (26) hide show
  1. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/PKG-INFO +29 -22
  2. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/README.rst +28 -21
  3. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/CRQM.py +306 -27
  4. robotframework-robotlog2rqm-1.4.1/RobotLog2RQM/RQM_templates/testsuite.xml +27 -0
  5. robotframework-robotlog2rqm-1.4.1/RobotLog2RQM/RobotLog2RQM.pdf +0 -0
  6. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/robotlog2rqm.py +277 -31
  7. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/version.py +2 -2
  8. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/PKG-INFO +29 -22
  9. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/SOURCES.txt +1 -0
  10. robotframework-robotlog2rqm-1.2.4/RobotLog2RQM/RobotLog2RQM.pdf +0 -0
  11. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/LICENSE +0 -0
  12. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/buildrecord.xml +0 -0
  13. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/configuration.xml +0 -0
  14. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/executionresult.xml +0 -0
  15. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/executionworkitem.xml +0 -0
  16. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/suiteexecutionrecord.xml +0 -0
  17. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/testcase.xml +0 -0
  18. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/RQM_templates/testsuitelog.xml +0 -0
  19. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/__init__.py +0 -0
  20. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/RobotLog2RQM/__main__.py +0 -0
  21. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/dependency_links.txt +0 -0
  22. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/entry_points.txt +0 -0
  23. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/requires.txt +0 -0
  24. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/robotframework_robotlog2rqm.egg-info/top_level.txt +0 -0
  25. {robotframework-robotlog2rqm-1.2.4 → robotframework-robotlog2rqm-1.4.1}/setup.cfg +0 -0
  26. {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.2.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
@@ -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
- [--createmissing] [--updatetestcase] [--dryrun]
133
- resultxmlfile host project user password testplan
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 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.
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 show this help message and exit
149
- -v, --version Version of the RobotLog2RQM importer.
150
- --recursive if set, then the path is searched recursively for
151
- log files to be imported.
152
- --createmissing if set, then all testcases without tcid are created
153
- when importing.
154
- --updatetestcase if set, then testcase information on RQM will be updated
155
- bases on robot testfile.
156
- --dryrun if set, then verify all input arguments
157
- (includes RQM authentication) and show what would be done.
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
- [--createmissing] [--updatetestcase] [--dryrun]
117
- resultxmlfile host project user password testplan
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 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.
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 show this help message and exit
133
- -v, --version Version of the RobotLog2RQM importer.
134
- --recursive if set, then the path is searched recursively for
135
- log files to be imported.
136
- --createmissing if set, then all testcases without tcid are created
137
- when importing.
138
- --updatetestcase if set, then testcase information on RQM will be updated
139
- bases on robot testfile.
140
- --dryrun if set, then verify all input arguments
141
- (includes RQM authentication) and show what would be done.
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
@@ -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,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.testsuite = 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
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.testsuite = suite_id
344
- self.testplan = plan_id
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 reource and ID.
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 reource and ID.
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, resourrceType, generateID):
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
- * ``resourrceType``
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 resourrceType in lSupportedResources:
535
- resResource = self.getResourceByID(resourrceType, generateID)
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
- * ``resourrceType``
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
- * ``resourrceType``
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 = 'TCER: '+testcaseName
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 = 'Execution result: '+testcaseName
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 = 'TSER: ' + testsuiteName
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 = 'Testsuite result: ' + testsuiteName
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