def export_to_nitrate(test, create, general): """ Export fmf metadata to nitrate test cases """ import_nitrate() new_test_created = False # Check nitrate test case try: nitrate_id = test.node.get('extra-nitrate')[3:] nitrate_case = nitrate.TestCase(int(nitrate_id)) echo(style(f"Test case '{nitrate_case.identifier}' found.", fg='blue')) except TypeError: # Create a new nitrate test case if create: nitrate_case = create_nitrate_case(test) new_test_created = True else: raise ConvertError("Nitrate test case id not found.") # Summary summary = test.node.get('extra-summary', test.node.get('extra-task', test.summary)) if summary: nitrate_case.summary = summary echo(style('summary: ', fg='green') + summary) else: raise ConvertError("Nitrate case summary could not be determined.") # Script if test.node.get('extra-task'): nitrate_case.script = test.node.get('extra-task') echo(style('script: ', fg='green') + test.node.get('extra-task')) # Components # First remove any components that are already there nitrate_case.components.clear() # Then add fmf ones if test.component: echo(style('components: ', fg='green') + ' '.join(test.component)) for component in test.component: try: nitrate_case.components.add( nitrate.Component(name=component, product=DEFAULT_PRODUCT.id)) except nitrate.xmlrpc_driver.NitrateError as error: log.debug(error) echo(style(f"Failed to add component '{component}'.", fg='red')) if general: try: general_plan = find_general_plan(component) nitrate_case.testplans.add(general_plan) except nitrate.NitrateError as error: log.debug(error) echo( style( f"Failed to link general test plan for '{component}'.", fg='red')) # Tags nitrate_case.tags.clear() # Convert 'tier' attribute into a Tier tag if test.tier is not None: test.tag.append(f"Tier{test.tier}") # Add special fmf-export tag test.tag.append('fmf-export') nitrate_case.tags.add([nitrate.Tag(tag) for tag in test.tag]) echo(style('tags: ', fg='green') + ' '.join(set(test.tag))) # Default tester if test.contact: # Need to pick one value, so picking the first contact email_address = email.utils.parseaddr(test.contact[0])[1] # TODO handle nitrate user not existing and other possible exceptions nitrate_case.tester = nitrate.User(email_address) echo(style('default tester: ', fg='green') + email_address) # Duration nitrate_case.time = test.duration echo(style('estimated time: ', fg='green') + test.duration) # Status current_status = nitrate_case.status # Enable enabled tests if test.enabled: nitrate_case.status = nitrate.CaseStatus('CONFIRMED') echo(style('status: ', fg='green') + 'CONFIRMED') # Disable disabled tests which are CONFIRMED elif current_status == nitrate.CaseStatus('CONFIRMED'): nitrate_case.status = nitrate.CaseStatus('DISABLED') echo(style('status: ', fg='green') + 'DISABLED') # Keep disabled tests in their states else: echo(style('status: ', fg='green') + str(current_status)) # Environment if test.environment: environment = ' '.join(tmt.utils.shell_variables(test.environment)) nitrate_case.arguments = environment echo(style('arguments: ', fg='green') + environment) else: # FIXME unable clear to set empty arguments # (possibly error in xmlrpc, BZ#1805687) nitrate_case.arguments = ' ' echo(style('arguments: ', fg='green') + "' '") # Structured Field struct_field = tmt.utils.StructuredField(nitrate_case.notes) echo(style('Structured Field: ', fg='green')) # Mapping of structured field sections to fmf case attributes section_to_attr = { 'relevancy': test.relevancy, 'description': test.summary, 'purpose-file': test.description, 'hardware': test.node.get('extra-hardware'), 'pepa': test.node.get('extra-pepa'), } for section, attribute in section_to_attr.items(): if attribute is None: try: struct_field.remove(section) except tmt.utils.StructuredFieldError: pass else: struct_field.set(section, attribute) echo(style(section + ': ', fg='green') + attribute.strip()) # fmf identifer fmf_id = test.fmf_id struct_field.set('fmf', yaml.dump(fmf_id)) echo(style('fmf id:\n', fg='green') + yaml.dump(fmf_id).strip()) # Warning if WARNING not in struct_field.header(): struct_field.header(WARNING + struct_field.header()) echo(style('Added warning about porting to case notes.', fg='green')) # Saving case.notes with edited StructField nitrate_case.notes = struct_field.save() # Update nitrate test case nitrate_case.update() echo( style("Test case '{0}' successfully exported to nitrate.".format( nitrate_case.identifier), fg='magenta')) # Write id of newly created nitrate case to its file if new_test_created: fmf_file_path = test.node.sources[-1] try: with open(fmf_file_path, encoding='utf-8') as fmf_file: content = yaml.safe_load(fmf_file) except IOError: raise ConvertError("Unable to open '{0}'.".format(fmf_file_path)) content['extra-nitrate'] = nitrate_case.identifier tmt.convert.write(fmf_file_path, content)
def export_to_nitrate(test): """ Export fmf metadata to nitrate test cases """ import_nitrate() new_test_created = False # Check command line options create = test.opt('create') general = test.opt('general') duplicate = test.opt('duplicate') link_bugzilla = test.opt('bugzilla') if link_bugzilla: import_bugzilla() try: bz_instance = bugzilla.Bugzilla(url=BUGZILLA_XMLRPC_URL) except Exception as exc: log.debug(traceback.format_exc()) raise ConvertError("Couldn't initialize the Bugzilla client.", original=exc) if not bz_instance.logged_in: raise ConvertError( "Not logged to Bugzilla, check `man bugzilla` section " "'AUTHENTICATION CACHE AND API KEYS'.") # Check nitrate test case try: nitrate_id = test.node.get('extra-nitrate')[3:] nitrate_case = nitrate.TestCase(int(nitrate_id)) nitrate_case.summary # Make sure we connect to the server now echo(style(f"Test case '{nitrate_case.identifier}' found.", fg='blue')) except TypeError: # Create a new nitrate test case if create: nitrate_case = None # Check for existing Nitrate tests with the same fmf id if not duplicate: testcases = _nitrate_find_fmf_testcases(test) try: # Select the first found testcase if any nitrate_case = next(testcases) except StopIteration: pass if not nitrate_case: nitrate_case = create_nitrate_case(test) new_test_created = True # Newly created tmt tests have special format summary test._metadata['extra-summary'] = nitrate_case.summary else: raise ConvertError(f"Nitrate test case id not found for {test}" " (You can use --create option to enforce" " creating testcases)") except (nitrate.NitrateError, gssapi.raw.misc.GSSError) as error: raise ConvertError(error) # Summary summary = (test._metadata.get('extra-summary') or test._metadata.get('extra-task') or test.summary or test.name) if summary: nitrate_case.summary = summary echo(style('summary: ', fg='green') + summary) else: raise ConvertError("Nitrate case summary could not be determined.") # Script if test.node.get('extra-task'): nitrate_case.script = test.node.get('extra-task') echo(style('script: ', fg='green') + test.node.get('extra-task')) # Components # First remove any components that are already there nitrate_case.components.clear() if general: # Remove also all general plans linked to testcase for nitrate_plan in [plan for plan in nitrate_case.testplans]: if nitrate_plan.type.name == "General": nitrate_case.testplans.remove(nitrate_plan) # Then add fmf ones if test.component: echo(style('components: ', fg='green') + ' '.join(test.component)) for component in test.component: try: nitrate_case.components.add( nitrate.Component(name=component, product=DEFAULT_PRODUCT.id)) except nitrate.xmlrpc_driver.NitrateError as error: log.debug(error) echo(style(f"Failed to add component '{component}'.", fg='red')) if general: try: general_plan = find_general_plan(component) nitrate_case.testplans.add(general_plan) except nitrate.NitrateError as error: log.debug(error) echo( style( f"Failed to link general test plan for '{component}'.", fg='red')) # Tags nitrate_case.tags.clear() # Convert 'tier' attribute into a Tier tag if test.tier is not None: test.tag.append(f"Tier{test.tier}") # Add special fmf-export tag test.tag.append('fmf-export') nitrate_case.tags.add([nitrate.Tag(tag) for tag in test.tag]) echo(style('tags: ', fg='green') + ' '.join(set(test.tag))) # Default tester if test.contact: # Need to pick one value, so picking the first contact email_address = email.utils.parseaddr(test.contact[0])[1] # TODO handle nitrate user not existing and other possible exceptions nitrate_case.tester = nitrate.User(email_address) echo(style('default tester: ', fg='green') + email_address) # Duration nitrate_case.time = test.duration echo(style('estimated time: ', fg='green') + test.duration) # Manual nitrate_case.automated = not test.manual echo(style('automated: ', fg='green') + ['auto', 'manual'][test.manual]) # Status current_status = nitrate_case.status # Enable enabled tests if test.enabled: nitrate_case.status = nitrate.CaseStatus('CONFIRMED') echo(style('status: ', fg='green') + 'CONFIRMED') # Disable disabled tests which are CONFIRMED elif current_status == nitrate.CaseStatus('CONFIRMED'): nitrate_case.status = nitrate.CaseStatus('DISABLED') echo(style('status: ', fg='green') + 'DISABLED') # Keep disabled tests in their states else: echo(style('status: ', fg='green') + str(current_status)) # Environment if test.environment: environment = ' '.join(tmt.utils.shell_variables(test.environment)) nitrate_case.arguments = environment echo(style('arguments: ', fg='green') + environment) else: # FIXME unable clear to set empty arguments # (possibly error in xmlrpc, BZ#1805687) nitrate_case.arguments = ' ' echo(style('arguments: ', fg='green') + "' '") # Structured Field struct_field = tmt.utils.StructuredField(nitrate_case.notes) echo(style('Structured Field: ', fg='green')) # Mapping of structured field sections to fmf case attributes section_to_attr = { 'description': test.summary, 'purpose-file': test.description, 'hardware': test.node.get('extra-hardware'), 'pepa': test.node.get('extra-pepa'), } for section, attribute in section_to_attr.items(): if attribute is None: try: struct_field.remove(section) except tmt.utils.StructuredFieldError: pass else: struct_field.set(section, attribute) echo(style(section + ': ', fg='green') + attribute.strip()) # fmf identifer fmf_id = tmt.utils.dict_to_yaml(test.fmf_id) struct_field.set('fmf', fmf_id) echo(style('fmf id:\n', fg='green') + fmf_id.strip()) # Warning if WARNING not in struct_field.header(): struct_field.header(WARNING + struct_field.header()) echo(style('Add migration warning to the test case notes.', fg='green')) # Saving case.notes with edited StructField nitrate_case.notes = struct_field.save() # Export manual test instructions from *.md file to nitrate as html md_path = return_markdown_file() if os.path.exists(md_path): step, expect, setup, cleanup = convert_manual_to_nitrate(md_path) nitrate.User()._server.TestCase.store_text(nitrate_case.id, step, expect, setup, cleanup) echo(style(f"manual steps:", fg='green') + f" found in {md_path}") # Append id of newly created nitrate case to its file if new_test_created: echo(style(f"Append the nitrate test case id.", fg='green')) try: with test.node as data: data["extra-nitrate"] = nitrate_case.identifier except AttributeError: # FIXME: Remove this deprecated code after fmf support # for storing modified data is released long enough file_path = test.node.sources[-1] try: with open(file_path, encoding='utf-8', mode='a+') as file: file.write(f"extra-nitrate: {nitrate_case.identifier}\n") except IOError: raise ConvertError("Unable to open '{0}'.".format(file_path)) # List of bugs test verifies verifies_bug_ids = [] for link in test.link: try: verifies_bug_ids.append( int(re.search(RE_BUGZILLA_URL, link['verifies']).group(1))) except Exception as err: log.debug(err) # Add bugs to the Nitrate case for bug_id in verifies_bug_ids: nitrate_case.bugs.add(nitrate.Bug(bug=int(bug_id))) # Update nitrate test case nitrate_case.update() echo( style("Test case '{0}' successfully exported to nitrate.".format( nitrate_case.identifier), fg='magenta')) # Optionally link Bugzilla to Nitrate case if link_bugzilla and verifies_bug_ids: try: bz_set_coverage(bz_instance, verifies_bug_ids, int(nitrate_case.id)) echo( style("Linked to Bugzilla: {}.".format(" ".join( [f"BZ#{bz_id}" for bz_id in verifies_bug_ids])), fg='magenta')) except Exception as err: raise ConvertError("Couldn't update bugs", original=err)