def detach_bug(request, case_ids, bug_ids): """Remove one or more bugs to the selected test cases. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param bug_ids: give one or more bug IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a bug ID. :type component_ids: int, str or list :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. Example:: # Remove bug id 1000 from case 1 >>> TestCase.detach_bug(1, 1000) # Remove bug ids list [1000, 1001] from cases list [1, 2] >>> TestCase.detach_bug([1, 2], [1000, 1001]) # Remove bug ids list '1000, 1001' from cases list '1, 2' with String >>> TestCase.detach_bug('1, 2', '1000, 1001') """ case_ids = pre_process_ids(case_ids) bug_ids = pre_process_ids(bug_ids) tcs = TestCase.objects.filter(case_id__in=case_ids).iterator() for tc in tcs: for opk in bug_ids: try: tc.remove_bug(bug_id=opk) except ObjectDoesNotExist: pass return
def remove_cases(request, run_ids, case_ids): """Remove one or more cases from the selected test runs. :param run_ids: give one or more run IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a run ID. :type run_ids: int, str or list :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. :rtype: list Example:: # Remove case 10 from run 1 TestRun.remove_cases(1, 10) # Remove case ids list [10, 20] from run list [1, 2] TestRun.remove_cases([1, 2], [10, 20]) # Remove case ids list '10, 20' from run list '1, 2' with String TestRun.remove_cases('1, 2', '10, 20') """ trs = TestRun.objects.filter(run_id__in=pre_process_ids(run_ids)) for tr in trs.iterator(): crs = TestCaseRun.objects.filter( run=tr, case__in=pre_process_ids(case_ids)) crs.delete()
def add_component(request, plan_ids, component_ids): """ Description: Adds one or more components to the selected test plan. Params: $plan_ids - Integer/Array/String: An integer representing the ID of the plan in the database. $component_ids - Integer/Array/String - The component ID, an array of Component IDs or a comma separated list of component IDs. Returns: Array: empty on success or an array of hashes with failure codes if a failure occured. Example: # Add component id 54321 to plan 1234 >>> TestPlan.add_component(1234, 54321) # Add component ids list [1234, 5678] to plan list [56789, 12345] >>> TestPlan.add_component([56789, 12345], [1234, 5678]) # Add component ids list '1234, 5678' to plan list '56789, 12345' with String >>> TestPlan.add_component('56789, 12345', '1234, 5678') """ # FIXME: optimize this method to reduce possible huge number of SQLs tps = TestPlan.objects.filter( plan_id__in=pre_process_ids(value=plan_ids) ) cs = Component.objects.filter( id__in=pre_process_ids(value=component_ids) ) for tp in tps.iterator(): for c in cs.iterator(): tp.add_component(c) return
def add_cases(request, run_ids, case_ids): """Add one or more cases to the selected test runs. :param run_ids: give one or more run IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a run ID. :type run_ids: int, str or list :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. :rtype: list Example:: # Add case id 10 to run 1 >>> TestRun.add_cases(1, 10) # Add case ids list [10, 20] to run list [1, 2] >>> TestRun.add_cases([1, 2], [10, 20]) # Add case ids list '10, 20' to run list '1, 2' with String >>> TestRun.add_cases('1, 2', '10, 20') """ trs = TestRun.objects.filter(run_id__in=pre_process_ids(run_ids)) tcs = TestCase.objects.filter(case_id__in=pre_process_ids(case_ids)) for tr in trs.iterator(): for tc in tcs.iterator(): tr.add_case_run(case=tc) return
def detach_issue(request, case_run_ids, issue_keys): """Remove one or more issues to the selected test case-runs. :param case_run_ids: give one or more case run IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case run ID. :type run_ids: int, str or list :param issue_keys: give one or more case run IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case run ID. :type issue_keys: int, str or list :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. :rtype: list Example:: # Remove issue 1000 from case run 1 >>> TestCaseRun.detach_issue(1, 1000) # Remove issues [1000, 2000] from case runs list [1, 2] >>> TestCaseRun.detach_issue([1, 2], [1000, 2000]) # Remove issues '1000, 2000' from case runs list '1, 2' with String >>> TestCaseRun.detach_issue('1, 2', '1000, 2000') """ tcrs = TestCaseRun.objects.filter(pk__in=pre_process_ids(case_run_ids)) issue_keys = pre_process_ids(issue_keys) for tcr in tcrs.iterator(): for issue_key in issue_keys: tcr.remove_issue(issue_key=issue_key, case_run=tcr)
def add_component(request, case_ids, component_ids): """Adds one or more components to the selected test cases. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param component_ids: give one or more component IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a component ID. :type component_ids: int, str or list :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. Example:: # Add component id 1 to case 1 >>> TestCase.add_component(1, 1) # Add component ids list [3, 4] to cases list [1, 2] >>> TestCase.add_component([1, 2], [3, 4]) # Add component ids list '3, 4' to cases list '1, 2' with String >>> TestCase.add_component('1, 2', '3, 4') """ from tcms.management.models import Component tcs = TestCase.objects.filter( case_id__in=pre_process_ids(value=case_ids)) cs = Component.objects.filter( id__in=pre_process_ids(value=component_ids)) for tc in tcs.iterator(): for c in cs.iterator(): tc.add_component(component=c)
def add_cases(request, run_ids, case_ids): """ Description: Add one or more cases to the selected test runs. Params: $run_ids - Integer/Array/String: An integer representing the ID in the database an array of IDs, or a comma separated list of IDs. $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, an arry of case_ids or aliases, or a string of comma separated case_ids. Returns: Array: empty on success or an array of hashes with failure codes if a failure occured. Example: # Add case id 54321 to run 1234 >>> TestRun.add_cases(1234, 54321) # Add case ids list [1234, 5678] to run list [56789, 12345] >>> TestRun.add_cases([56789, 12345], [1234, 5678]) # Add case ids list '1234, 5678' to run list '56789, 12345' with String >>> TestRun.add_cases('56789, 12345', '1234, 5678') """ trs = TestRun.objects.filter(run_id__in=pre_process_ids(run_ids)) tcs = TestCase.objects.filter(case_id__in=pre_process_ids(case_ids)) for tr in trs.iterator(): for tc in tcs.iterator(): tr.add_case_run(case=tc) return
def detach_bug(request, case_ids, bug_ids): """ Description: Remove one or more bugs to the selected test cases. Params: $case_ids - Integer/Array/String: An integer representing the ID in the database, an array of case_ids, or a string of comma separated case_ids $bug_ids - Integer/Array/String: An integer representing the ID in the database, an array of bug_ids, or a string of comma separated primary key of bug_ids. Returns: Array: empty on success or an array of hashes with failure codes if a failure occured. Example: # Remove bug id 54321 from case 1234 >>> TestCase.detach_bug(1234, 54321) # Remove bug ids list [1234, 5678] from cases list [56789, 12345] >>> TestCase.detach_bug([56789, 12345], [1234, 5678]) # Remove bug ids list '1234, 5678' from cases list '56789, 12345' with String >>> TestCase.detach_bug('56789, 12345', '1234, 5678') """ case_ids = pre_process_ids(case_ids) bug_ids = pre_process_ids(bug_ids) tcs = TestCase.objects.filter(case_id__in=case_ids).iterator() for tc in tcs: for opk in bug_ids: try: tc.remove_bug(bug_id=opk) except ObjectDoesNotExist: pass return
def link_plan(request, case_ids, plan_ids): """" Description: Link test cases to the given plan. Params: $case_ids - Integer/Array/String: An integer representing the ID in the database, an array of case_ids, or a string of comma separated case_ids. $plan_ids - Integer/Array/String: An integer representing the ID in the database, an array of plan_ids, or a string of comma separated plan_ids. Returns: Array: empty on success or an array of hashes with failure codes if a failure occurs Example: # Add case 1234 to plan id 54321 >>> TestCase.link_plan(1234, 54321) # Add case ids list [56789, 12345] to plan list [1234, 5678] >>> TestCase.link_plan([56789, 12345], [1234, 5678]) # Add case ids list 56789 and 12345 to plan list 1234 and 5678 with String >>> TestCase.link_plan('56789, 12345', '1234, 5678') """ from tcms.apps.testplans.models import TestPlan case_ids = pre_process_ids(value=case_ids) plan_ids = pre_process_ids(value=plan_ids) tcs = TestCase.objects.filter(pk__in=case_ids) tps = TestPlan.objects.filter(pk__in=plan_ids) # Check the non-exist case ids. if tcs.count() < len(case_ids): raise ObjectDoesNotExist( "TestCase", compare_list(case_ids, tcs.values_list('pk', flat=True).iterator()) ) # Check the non-exist plan ids. if tps.count() < len(plan_ids): raise ObjectDoesNotExist( "TestPlan", compare_list(plan_ids, tps.values_list('pk', flat=True).iterator()) ) # Link the plans to cases for tc in tcs.iterator(): for tp in tps.iterator(): tc.add_to_plan(tp) return
def test_pre_process_ids_with_others(self): try: U.pre_process_ids((1,)) except TypeError as e: self.assertEqual(str(e), 'Unrecognizable type of ids') else: self.fail("Missing validations.") try: U.pre_process_ids(dict(a=1)) except TypeError as e: self.assertEqual(str(e), 'Unrecognizable type of ids') else: self.fail("Missing validations.")
def calculate_total_estimated_time(request, case_ids): """ Description: Returns an total estimated time for cases. Params: $case_ids - Integer/String: An integer representing the ID in the database. Returns: String: Time in "HH:MM:SS" format. Example: >>> TestCase.calculate_total_time([609, 610, 611]) """ from django.db.models import Sum tcs = TestCase.objects.filter( pk__in=pre_process_ids(case_ids)).only('estimated_time') if not tcs.exists(): raise ValueError('Please input valid case Id') # aggregate Sum return integer directly rather than timedelta seconds = tcs.aggregate(Sum('estimated_time')).get('estimated_time__sum') m, s = divmod(seconds, 60) h, m = divmod(m, 60) # TODO: return h:m:s or d:h:m return '%02i:%02i:%02i' % (h, m, s)
def add_comment(request, case_ids, comment): """Adds comments to selected test cases. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param str comment: the comment content to add. :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. Example:: # Add comment 'foobar' to case 1 >>> TestCase.add_comment(1, 'foobar') # Add 'foobar' to cases list [1, 2] >>> TestCase.add_comment([1, 2], 'foobar') # Add 'foobar' to cases list '1, 2' with String >>> TestCase.add_comment('1, 2', 'foobar') """ from tcms.xmlrpc.utils import Comment object_pks = pre_process_ids(value=case_ids) c = Comment( request=request, content_type='testcases.testcase', object_pks=object_pks, comment=comment ) return c.add()
def add_tag(request, case_ids, tags): """ Description: Add one or more tags to the selected test cases. Params: $case_ids - Integer/Array/String: An integer representing the ID in the database, an array of case_ids, or a string of comma separated case_ids. $tags - String/Array - A single tag, an array of tags, or a comma separated list of tags. Returns: Array: empty on success or an array of hashes with failure codes if a failure occured. Example: # Add tag 'foobar' to case 1234 >>> TestCase.add_tag(1234, 'foobar') # Add tag list ['foo', 'bar'] to cases list [12345, 67890] >>> TestCase.add_tag([12345, 67890], ['foo', 'bar']) # Add tag list ['foo', 'bar'] to cases list [12345, 67890] with String >>> TestCase.add_tag('12345, 67890', 'foo, bar') """ tcs = TestCase.objects.filter( case_id__in=pre_process_ids(value=case_ids)) tags = TestTag.string_to_list(tags) for tag in tags: t, c = TestTag.objects.get_or_create(name=tag) for tc in tcs.iterator(): tc.add_tag(tag=t) return
def add_tag(request, plan_ids, tags): """ Description: Add one or more tags to the selected test plans. Params: $plan_ids - Integer/Array/String: An integer representing the ID of the plan in the database, an arry of plan_ids, or a string of comma separated plan_ids. $tags - String/Array - A single tag, an array of tags, or a comma separated list of tags. Returns: Array: empty on success or an array of hashes with failure codes if a failure occured. Example: # Add tag 'foobar' to plan 1234 >>> TestPlan.add_tag(1234, 'foobar') # Add tag list ['foo', 'bar'] to plan list [12345, 67890] >>> TestPlan.add_tag([12345, 67890], ['foo', 'bar']) # Add tag list ['foo', 'bar'] to plan list [12345, 67890] with String >>> TestPlan.add_tag('12345, 67890', 'foo, bar') """ # FIXME: this could be optimized to reduce possible huge number of SQLs tps = TestPlan.objects.filter(plan_id__in=pre_process_ids(value=plan_ids)) tags = TestTag.string_to_list(tags) for tag in tags: t, c = TestTag.objects.get_or_create(name=tag) for tp in tps.iterator(): tp.add_tag(tag=t) return
def notification_add_cc(request, case_ids, cc_list): ''' Description: Add email addresses to the notification CC list of specific TestCases Params: $case_ids - Integer/Array: one or more TestCase IDs $cc_list - Array: one or more Email addresses, which will be added to each TestCase indicated by the case_ids. Returns: JSON. When succeed, status is 0, and message maybe empty or anything else that depends on the implementation. If something wrong, status will be 1 and message will be a short description to the error. ''' try: validate_cc_list(cc_list) except (TypeError, ValidationError): raise try: tc_ids = pre_process_ids(case_ids) for tc in TestCase.objects.filter(pk__in=tc_ids).iterator(): # First, find those that do not exist yet. existing_cc = tc.emailing.get_cc_list() adding_cc = list(set(cc_list) - set(existing_cc)) tc.emailing.add_cc(adding_cc) except (TypeError, ValueError, Exception): raise
def add_tag(request, case_ids, tags): """Add one or more tags to the selected test cases. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param tags: tag name or a list of tag names to remove. :type tags: str or list :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. Example:: # Add tag 'foobar' to case 1 >>> TestCase.add_tag(1, 'foobar') # Add tag list ['foo', 'bar'] to cases list [1, 2] >>> TestCase.add_tag([1, 2], ['foo', 'bar']) # Add tag list ['foo', 'bar'] to cases list [1, 2] with String >>> TestCase.add_tag('1, 2', 'foo, bar') """ tcs = TestCase.objects.filter( case_id__in=pre_process_ids(value=case_ids)) tags = TestTag.string_to_list(tags) for tag in tags: t, c = TestTag.objects.get_or_create(name=tag) for tc in tcs.iterator(): tc.add_tag(tag=t) return
def get_bugs(request, run_ids): """Get the list of bugs attached to this run. :param run_ids: give one or more run IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a run ID. :type run_ids: int, str or list :return: a list of mappings of :class:`TestCaseBug`. :rtype: list[dict] Example:: # Get bugs belong to ID 12345 >>> TestRun.get_bugs(1) # Get bug belong to run ids list [1, 2] >>> TestRun.get_bugs([1, 2]) # Get bug belong to run ids list 1 and 2 with string >>> TestRun.get_bugs('1, 2') """ from tcms.testcases.models import TestCaseBug trs = TestRun.objects.filter( run_id__in=pre_process_ids(value=run_ids) ) tcrs = TestCaseRun.objects.filter( run__run_id__in=trs.values_list('run_id', flat=True) ) query = {'case_run__case_run_id__in': tcrs.values_list('case_run_id', flat=True)} return TestCaseBug.to_xmlrpc(query)
def calculate_total_estimated_time(request, case_ids): """Returns an total estimated time for cases. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :return: Time in "HH:MM:SS" format. :rtype: str Example:: >>> TestCase.calculate_total_estimated_time([609, 610, 611]) """ from django.db.models import Sum tcs = TestCase.objects.filter( pk__in=pre_process_ids(case_ids)).only('estimated_time') if not tcs.exists(): raise ValueError('Please input valid case Id') # aggregate Sum return integer directly rather than timedelta seconds = tcs.aggregate(Sum('estimated_time')).get('estimated_time__sum') m, s = divmod(seconds, 60) h, m = divmod(m, 60) # TODO: return h:m:s or d:h:m return '%02i:%02i:%02i' % (h, m, s)
def calculate_average_estimated_time(request, case_ids): """ Description: Returns an average estimated time for cases. Params: $case_ids - Integer/String: An integer representing the ID in the database. Returns: String: Time in "HH:MM:SS" format. Example: >>> TestCase.calculate_average_time([609, 610, 611]) """ from datetime import timedelta from tcms.core.utils.xmlrpc import SECONDS_PER_DAY tcs = TestCase.objects.filter( pk__in=pre_process_ids(case_ids)).only('estimated_time') time = timedelta(0) case_count = 0 for tc in tcs.iterator(): case_count += 1 time += tc.estimated_time seconds = time.seconds + (time.days * SECONDS_PER_DAY) # iterator will not populate cache so here will access db again # seconds = seconds / len(tcs) seconds = seconds / case_count return '%02i:%02i:%02i' % ( seconds / 3600, # Hours seconds / 60, # Minutes seconds % 60 # Seconds )
def calculate_total_estimated_time(request, case_ids): """ Description: Returns an total estimated time for cases. Params: $case_ids - Integer/String: An integer representing the ID in the database. Returns: String: Time in "HH:MM:SS" format. Example: >>> TestCase.calculate_total_time([609, 610, 611]) """ from datetime import timedelta from tcms.core.utils.xmlrpc import SECONDS_PER_DAY tcs = TestCase.objects.filter( pk__in=pre_process_ids(case_ids)).only('estimated_time') time = timedelta(0) for tc in tcs.iterator(): time += tc.estimated_time seconds = time.seconds + (time.days * SECONDS_PER_DAY) return '%02i:%02i:%02i' % ( seconds / 3600, # Hours seconds / 60, # Minutes seconds % 60 # Seconds )
def get_bugs(request, case_ids): """Get the list of bugs that are associated with this test case. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :return: list of mappings of :class:`TestCaseBug`. :rtype: list Example:: # Get bugs belong to ID 1 >>> TestCase.get_bugs(1) # Get bug belong to case ids list [1, 2] >>> TestCase.get_bugs([1, 2]) # Get bug belong to case ids list 1 and 2 with string >>> TestCase.get_bugs('1, 2') """ from tcms.testcases.models import TestCaseBug tcs = TestCase.objects.filter( case_id__in=pre_process_ids(value=case_ids) ) query = {'case__case_id__in': tcs.values_list('case_id', flat=True)} return TestCaseBug.to_xmlrpc(query)
def notification_remove_cc(request, case_ids, cc_list): ''' Description: Remove email addresses from the notification CC list of specific TestCases Params: $case_ids - Integer/Array: one or more TestCase IDs $cc_list - Array: contians the email addresses that will be removed from each TestCase indicated by case_ids. Returns: JSON. When succeed, status is 0, and message maybe empty or anything else that depends on the implementation. If something wrong, status will be 1 and message will be a short description to the error. ''' try: validate_cc_list(cc_list) except (TypeError, ValidationError): raise try: tc_ids = pre_process_ids(case_ids) cursor = connection.writer_cursor ids_values = ",".join(itertools.repeat('%s', len(tc_ids))) email_values = ",".join(itertools.repeat('%s', len(cc_list))) sql = TC_REMOVE_CC % (ids_values, email_values) tc_ids.extend(cc_list) cursor.execute(sql, tc_ids) transaction.commit_unless_managed() except (TypeError, ValueError, Exception): raise
def add_tag(request, plan_ids, tags): """Add one or more tags to the selected test plans. :param plan_ids: give one or more plan IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a plan ID. :type plan_ids: int, str or list :param tags: a tag name or list of tag names to be added. :type tags: str or list[str] :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. Example:: # Add tag 'foobar' to plan 1 >>> TestPlan.add_tag(1, 'foobar') # Add tag list ['foo', 'bar'] to plan list [1, 2] >>> TestPlan.add_tag([1, 2], ['foo', 'bar']) # Add tag list ['foo', 'bar'] to plan list [1, 2] with String >>> TestPlan.add_tag('1, 2', 'foo, bar') """ # FIXME: this could be optimized to reduce possible huge number of SQLs tps = TestPlan.objects.filter(plan_id__in=pre_process_ids(value=plan_ids)) tags = TestTag.string_to_list(tags) for tag in tags: t, c = TestTag.objects.get_or_create(name=tag) for tp in tps.iterator(): tp.add_tag(tag=t) return
def update(request, case_ids, values): """Updates the fields of the selected case or cases. $values - Hash of keys matching TestCase fields and the new values to set each field to. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param dict values: a mapping containing these case data to update. * case_status: (ini) optional * product: (ini) optional (Required if changes category) * category: (ini) optional * priority: (ini) optional * default_tester: (str or int) optional (str - user_name, int - user_id) * estimated_time: (str) optional (2h30m30s(recommend) or HH:MM:SS * is_automated: (ini) optional (0 - Manual, 1 - Auto, 2 - Both) * is_automated_proposed: (bool) optional * script: (str) optional * arguments: (str) optional * summary: (str) optional * requirement: (str) optional * alias: (str) optional * notes: (str) optional * extra_link: (str) optional (reference link) :return: a list of mappings of updated :class:`TestCase`. :rtype: list(dict) Example:: # Update alias to 'tcms' for case 1 and 2 >>> TestCase.update([1, 2], {'alias': 'tcms'}) """ from tcms.core import forms from tcms.xmlrpc.forms import UpdateCaseForm if values.get('estimated_time'): values['estimated_time'] = pre_process_estimated_time(values.get('estimated_time')) form = UpdateCaseForm(values) if values.get('category') and not values.get('product'): raise ValueError('Product ID is required for category') if values.get('product'): form.populate(product_id=values['product']) if form.is_valid(): tcs = TestCase.update( case_ids=pre_process_ids(value=case_ids), values=form.cleaned_data, ) else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': tcs.values_list('pk', flat=True)} return TestCase.to_xmlrpc(query)
def get_bugs(request, run_ids): """ *** FIXME: BUGGY IN SERIALISER - List can not be serialize. *** Description: Get the list of bugs attached to this run. Params: $run_ids - Integer/Array/String: An integer representing the ID in the database an array of integers or a comma separated list of integers. Returns: Array: An array of bug object hashes. Example: # Get bugs belong to ID 12345 >>> TestRun.get_bugs(12345) # Get bug belong to run ids list [12456, 23456] >>> TestRun.get_bugs([12456, 23456]) # Get bug belong to run ids list 12456 and 23456 with string >>> TestRun.get_bugs('12456, 23456') """ from tcms.testcases.models import TestCaseBug trs = TestRun.objects.filter( run_id__in=pre_process_ids(value=run_ids) ) tcrs = TestCaseRun.objects.filter( run__run_id__in=trs.values_list('run_id', flat=True) ) query = {'case_run__case_run_id__in': tcrs.values_list('case_run_id', flat=True)} return TestCaseBug.to_xmlrpc(query)
def add_comment(request, case_ids, comment): """ Description: Adds comments to selected test cases. Params: $case_ids - Integer/Array/String: An integer representing the ID in the database, an array of case_ids, or a string of comma separated case_ids. $comment - String - The comment Returns: Array: empty on success or an array of hashes with failure codes if a failure occured. Example: # Add comment 'foobar' to case 1234 >>> TestCase.add_comment(1234, 'foobar') # Add 'foobar' to cases list [56789, 12345] >>> TestCase.add_comment([56789, 12345], 'foobar') # Add 'foobar' to cases list '56789, 12345' with String >>> TestCase.add_comment('56789, 12345', 'foobar') """ from tcms.xmlrpc.utils import Comment object_pks = pre_process_ids(value=case_ids) c = Comment( request=request, content_type='testcases.testcase', object_pks=object_pks, comment=comment ) return c.add()
def notification_remove_cc(request, case_ids, cc_list): ''' Description: Remove email addresses from the notification CC list of specific TestCases Params: $case_ids - Integer/Array: one or more TestCase IDs $cc_list - Array: contians the email addresses that will be removed from each TestCase indicated by case_ids. Returns: JSON. When succeed, status is 0, and message maybe empty or anything else that depends on the implementation. If something wrong, status will be 1 and message will be a short description to the error. ''' try: validate_cc_list(cc_list) except (TypeError, ValidationError): raise try: tc_ids = pre_process_ids(case_ids) for tc in TestCase.objects.filter(pk__in=tc_ids).iterator(): tc.emailing.cc_list.filter(email__in=cc_list).delete() except (TypeError, ValueError, Exception): raise
def test_pre_process_ids_with_string(self): try: U.pre_process_ids(["a", "b"]) except ValueError as e: pass except Exception: self.fail("Unexcept error occurs.") else: self.fail("Missing validations.") try: U.pre_process_ids("1@2@3@4") except ValueError as e: pass except Exception: self.fail("Unexcept error occurs.") else: self.fail("Missing validations.")
def add_to_run(request, case_ids, run_ids): """Add one or more cases to the selected test runs. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param run_ids: give one or more run IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a run ID. :type run_ids: int, str or list :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. Example:: # Add case 1 to run id 1 >>> TestCase.add_to_run(1, 1) # Add case ids list [1, 2] to run list [3, 4] >>> TestCase.add_to_run([1, 2], [3, 4]) # Add case ids list 1 and 2 to run list 3 and 4 with String >>> TestCase.add_to_run('1, 2', '3, 4') """ from tcms.testruns.models import TestRun case_ids = pre_process_ids(case_ids) run_ids = pre_process_ids(run_ids) trs = TestRun.objects.filter(run_id__in=run_ids) if not trs.exists(): raise ValueError('Invalid run_ids') tcs = TestCase.objects.filter(case_id__in=case_ids) if not tcs.exists(): raise ValueError('Invalid case_ids') for tr in trs.iterator(): for tc in tcs.iterator(): tr.add_case_run(case=tc) return
def remove_component(request, case_ids, component_ids): """Removes selected component from the selected test case. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param component_ids: give one or more component IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a component ID. :type plan_ids: int, str or list :return: a list which is emtpy on success. :rtype: list Example:: # Remove component id 1 from case 1 >>> TestCase.remove_component(1, 1) # Remove component ids list [3, 4] from cases list [1, 2] >>> TestCase.remove_component([1, 2], [3, 4]) # Remove component ids list '3, 4' from cases list '1, 2' with String >>> TestCase.remove_component('1, 2', '3, 4') """ from tcms.management.models import Component tcs = TestCase.objects.filter( case_id__in=pre_process_ids(value=case_ids) ) tccs = Component.objects.filter( id__in=pre_process_ids(value=component_ids) ) for tc in tcs.iterator(): for tcc in tccs.iterator(): try: tc.remove_component(component=tcc) except ObjectDoesNotExist: pass return
def get_bugs(case_ids): """ Description: Get the list of bugs that are associated with this test case. Params: $case_ids - Integer/String: An integer representing the ID in the database Returns: Array: An array of bug object hashes. Example: # Get bugs belong to ID 12345 >>> TestCase.get_bugs(12345) # Get bug belong to case ids list [12456, 23456] >>> TestCase.get_bugs([12456, 23456]) # Get bug belong to case ids list 12456 and 23456 with string >>> TestCase.get_bugs('12456, 23456') """ from tcms.testcases.models import TestCaseBug tcs = TestCase.objects.filter(case_id__in=pre_process_ids(value=case_ids)) query = {'case__case_id__in': tcs.values_list('case_id', flat=True)} return TestCaseBug.to_xmlrpc(query)
def get_issues(request, case_ids): """Get the list of issues that are associated with this test case. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :return: list of mappings of :class:`Issue`. :rtype: list[dict] Example:: # Get issues belonging to case 1 TestCase.get_issues(1) # Get issues belonging to cases [1, 2] TestCase.get_issues([1, 2]) # Get issues belonging to case 1 and 2 with string TestCase.get_issues('1, 2') """ case_ids = pre_process_ids(case_ids) query = {'case__in': case_ids} return Issue.to_xmlrpc(query)
def remove_tag(request, plan_ids, tags): """ Description: Remove a tag from a plan. Params: $plan_ids - Integer/Array/String: An integer or alias representing the ID in the database, an array of plan_ids, or a string of comma separated plan_ids. $tag - String - A single tag to be removed. Returns: Array: Empty on success. Example: # Remove tag 'foo' from plan 1234 >>> TestPlan.remove_tag(1234, 'foo') # Remove tag 'foo' and 'bar' from plan list [56789, 12345] >>> TestPlan.remove_tag([56789, 12345], ['foo', 'bar']) # Remove tag 'foo' and 'bar' from plan list '56789, 12345' with String >>> TestPlan.remove_tag('56789, 12345', 'foo, bar') """ from tcms.management.models import TestTag tps = TestPlan.objects.filter( plan_id__in=pre_process_ids(value=plan_ids) ) tgs = TestTag.objects.filter( name__in=TestTag.string_to_list(tags) ) for tp in tps.iterator(): for tg in tgs.iterator(): try: tp.remove_tag(tag=tg) except ObjectDoesNotExist: pass except: raise return
def remove_tag(plan_ids, tags): """ Description: Remove a tag from a plan. Params: $plan_ids - Integer/Array/String: An integer or alias representing the ID in the database, an array of plan_ids, or a string of comma separated plan_ids. $tag - String - A single tag to be removed. Returns: Array: Empty on success. Example: # Remove tag 'foo' from plan 1234 >>> TestPlan.remove_tag(1234, 'foo') # Remove tag 'foo' and 'bar' from plan list [56789, 12345] >>> TestPlan.remove_tag([56789, 12345], ['foo', 'bar']) # Remove tag 'foo' and 'bar' from plan list '56789, 12345' with String >>> TestPlan.remove_tag('56789, 12345', 'foo, bar') """ from tcms.management.models import TestTag test_plans = TestPlan.objects.filter( plan_id__in=pre_process_ids(value=plan_ids) ) if not isinstance(tags, (str, list)): raise ValueError('Parameter tags must be a string or list(string)') test_tags = TestTag.objects.filter( name__in=string_to_list(tags) ) for test_plan in test_plans.iterator(): for test_tag in test_tags.iterator(): test_plan.remove_tag(tag=test_tag) return
def notification_get_cc_list(request, case_ids): ''' Description: Return whole CC list of each TestCase Params: $case_ids - Integer/Array: one or more TestCase IDs Returns: An dictionary object with case_id as key and a list of CC as the value Each case_id will be converted to a str object in the result. ''' result = {} try: tc_ids = pre_process_ids(case_ids) for tc in TestCase.objects.filter(pk__in=tc_ids).iterator(): cc_list = tc.emailing.get_cc_list() result[str(tc.pk)] = cc_list except (TypeError, ValueError, Exception): raise return result
def add_tag(request, case_ids, tags): """Add one or more tags to the selected test cases. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param tags: tag name or a list of tag names to remove. :type tags: str or list :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. Example:: # Add tag 'foobar' to case 1 TestCase.add_tag(1, 'foobar') # Add tag list ['foo', 'bar'] to cases list [1, 2] TestCase.add_tag([1, 2], ['foo', 'bar']) # Add tag list ['foo', 'bar'] to cases list [1, 2] with String TestCase.add_tag('1, 2', 'foo, bar') """ tcs = TestCase.objects.filter( case_id__in=pre_process_ids(value=case_ids) ).only('pk') if not tcs.exists(): return tags = TestTag.string_to_list(tags) if not tags: return for tag in tags: t, c = TestTag.objects.get_or_create(name=tag) for tc in tcs.iterator(): tc.add_tag(tag=t)
def add_tag(plan_ids, tags): """ Description: Add one or more tags to the selected test plans. Params: $plan_ids - Integer/Array/String: An integer representing the ID of the plan in the database, an arry of plan_ids, or a string of comma separated plan_ids. $tags - String/Array - A single tag, an array of tags, or a comma separated list of tags. Returns: Array: empty on success or an array of hashes with failure codes if a failure occured. Example: # Add tag 'foobar' to plan 1234 >>> TestPlan.add_tag(1234, 'foobar') # Add tag list ['foo', 'bar'] to plan list [12345, 67890] >>> TestPlan.add_tag([12345, 67890], ['foo', 'bar']) # Add tag list ['foo', 'bar'] to plan list [12345, 67890] with String >>> TestPlan.add_tag('12345, 67890', 'foo, bar') """ # FIXME: this could be optimized to reduce possible huge number of SQLs tps = TestPlan.objects.filter(plan_id__in=pre_process_ids(value=plan_ids)) if not isinstance(tags, (str, list)): raise ValueError('Parameter tags must be a string or list(string)') tags = string_to_list(tags) for tag in tags: t, c = TestTag.objects.get_or_create(name=tag) for tp in tps.iterator(): tp.add_tag(tag=t) return
def remove_tag(request, run_ids, tags): """Remove a tag from a run. :param run_ids: give one or more run IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a run ID. :type run_ids: int, str or list :param tags: tag name or a list of tag names to remove. :type tags: str or list :return: a list which is empty on success. :rtype: list Example:: # Remove tag 'foo' from run 1 >>> TestRun.remove_tag(1, 'foo') # Remove tag 'foo' and 'bar' from run list [1, 2] >>> TestRun.remove_tag([1, 2], ['foo', 'bar']) # Remove tag 'foo' and 'bar' from run list '1, 2' with String >>> TestRun.remove_tag('1, 2', 'foo, bar') """ trs = TestRun.objects.filter( run_id__in=pre_process_ids(value=run_ids) ) tgs = TestTag.objects.filter( name__in=TestTag.string_to_list(tags) ) for tr in trs.iterator(): for tg in tgs.iterator(): try: tr.remove_tag(tag=tg) except ObjectDoesNotExist: pass return
def remove_tag(request, case_ids, tags): """ Description: Remove a tag from a case. Params: $case_ids - Integer/Array/String: An integer or alias representing the ID in the database, an array of case_ids, or a string of comma separated case_ids. $tags - String/Array - A single or multiple tag to be removed. Returns: Array: Empty on success. Example: # Remove tag 'foo' from case 1234 >>> TestCase.remove_tag(1234, 'foo') # Remove tag 'foo' and bar from cases list [56789, 12345] >>> TestCase.remove_tag([56789, 12345], ['foo', 'bar']) # Remove tag 'foo' and 'bar' from cases list '56789, 12345' with String >>> TestCase.remove_tag('56789, 12345', 'foo, bar') """ tcs = TestCase.objects.filter( case_id__in=pre_process_ids(value=case_ids) ) tgs = TestTag.objects.filter( name__in=TestTag.string_to_list(tags) ) for tc in tcs.iterator(): for tg in tgs.iterator(): try: tc.remove_tag(tg) except ObjectDoesNotExist: pass except: raise return
def test_pre_process_ids_with_list(self): ids = U.pre_process_ids(["1", "2", "3"]) self.assertEqual(ids, [1, 2, 3])
def test_pre_process_ids_with_str(self): ids = U.pre_process_ids("1") self.assertEqual(ids, [1]) ids = U.pre_process_ids("1,2,3,4") self.assertEqual(ids, [1, 2, 3, 4])
def test_pre_process_ids_with_int(self): ids = U.pre_process_ids(1) self.assertEqual(ids, [1])
def link_plan(case_ids, plan_ids): """ Description: Link test cases to the given plan. Params: $case_ids - Integer/Array/String: An integer representing the ID in the database, an array of case_ids, or a string of comma separated case_ids. $plan_ids - Integer/Array/String: An integer representing the ID in the database, an array of plan_ids, or a string of comma separated plan_ids. Returns: Array: empty on success or an array of hashes with failure codes if a failure occurs Example: # Add case 1234 to plan id 54321 >>> TestCase.link_plan(1234, 54321) # Add case ids list [56789, 12345] to plan list [1234, 5678] >>> TestCase.link_plan([56789, 12345], [1234, 5678]) # Add case ids list 56789 and 12345 to plan list 1234 and 5678 with String >>> TestCase.link_plan('56789, 12345', '1234, 5678') """ case_ids = pre_process_ids(value=case_ids) qs = TestCase.objects.filter(pk__in=case_ids) tcs_ids = qs.values_list('pk', flat=True) # Check the non-exist case ids. ids_diff = set(case_ids) - set(tcs_ids.iterator()) if ids_diff: ids_str = ','.join(map(str, ids_diff)) if len(ids_diff) > 1: err_msg = 'TestCases %s do not exist.' % ids_str else: err_msg = 'TestCase %s does not exist.' % ids_str raise ObjectDoesNotExist(err_msg) plan_ids = pre_process_ids(value=plan_ids) qs = TestPlan.objects.filter(pk__in=plan_ids) tps_ids = qs.values_list('pk', flat=True) # Check the non-exist plan ids. ids_diff = set(plan_ids) - set(tps_ids.iterator()) if ids_diff: ids_str = ','.join(map(str, ids_diff)) if len(ids_diff) > 1: err_msg = 'TestPlans %s do not exist.' % ids_str else: err_msg = 'TestPlan %s does not exist.' % ids_str raise ObjectDoesNotExist(err_msg) # (plan_id, case_id) pair might probably exist in test_case_plans table, so # skip the ones that do exist and create the rest. # note: this query returns a list of tuples! existing = TestCasePlan.objects.filter(plan__in=plan_ids, case__in=case_ids).values_list( 'plan', 'case') # Link the plans to cases def _generate_link_plan_value(): for plan_id in plan_ids: for case_id in case_ids: if (plan_id, case_id) not in existing: yield plan_id, case_id TestCasePlan.objects.bulk_create([ TestCasePlan(plan_id=_plan_id, case_id=_case_id) for _plan_id, _case_id in _generate_link_plan_value() ])
def update(request, case_ids, values): """ Description: Updates the fields of the selected case or cases. Params: $case_ids - Integer/String/Array Integer: A single TestCase ID. String: A comma separates string of TestCase IDs for batch processing. Array: An array of case IDs for batch mode processing $values - Hash of keys matching TestCase fields and the new values to set each field to. Returns: Array: an array of case hashes. If the update on any particular case failed, the has will contain a ERROR key and the message as to why it failed. +-----------------------+----------------+-----------------------------------------+ | Field | Type | Null | +-----------------------+----------------+-----------------------------------------+ | case_status | Integer | Optional | | product | Integer | Optional(Required if changes category) | | category | Integer | Optional | | priority | Integer | Optional | | default_tester | String/Integer | Optional(str - user_name, int - user_id)| | estimated_time | String | Optional(2h30m30s(recommend) or HH:MM:SS| | is_automated | Integer | Optional(0 - Manual, 1 - Auto, 2 - Both)| | is_automated_proposed | Boolean | Optional | | script | String | Optional | | arguments | String | Optional | | summary | String | Optional | | requirement | String | Optional | | alias | String | Optional | | notes | String | Optional | | extra_link | String | Optional(reference link) +-----------------------+----------------+-----------------------------------------+ Example: # Update alias to 'tcms' for case 12345 and 23456 >>> TestCase.update([12345, 23456], {'alias': 'tcms'}) """ from tcms.core import forms from tcms.xmlrpc.forms import UpdateCaseForm if values.get('estimated_time'): values['estimated_time'] = pre_process_estimated_time(values.get('estimated_time')) form = UpdateCaseForm(values) if values.get('category') and not values.get('product'): raise ValueError('Product ID is required for category') if values.get('product'): form.populate(product_id=values['product']) if form.is_valid(): tcs = TestCase.update( case_ids=pre_process_ids(value=case_ids), values=form.cleaned_data, ) else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': tcs.values_list('pk', flat=True)} return TestCase.to_xmlrpc(query)
def create(request, values): """ Description: Creates a new Test Run object and stores it in the database. Params: $values - Hash: A reference to a hash with keys and values matching the fields of the test run to be created. +-------------------+----------------+-----------+---------------------------------------+ | Field | Type | Null | Description | +-------------------+----------------+-----------+---------------------------------------+ | plan | Integer | Required | ID of test plan | | build | Integer/String | Required | ID of Build | | manager | Integer | Required | ID of run manager | | summary | String | Required | | | product | Integer | Required | ID of product | | product_version | Integer | Required | ID of product version | | default_tester | Integer | Optional | ID of run default tester | | plan_text_version | Integer | Optional | | | estimated_time | String | Optional | 2h30m30s(recommend) or HH:MM:SS Format| | notes | String | Optional | | | status | Integer | Optional | 0:RUNNING 1:STOPPED (default 0) | | case | Array/String | Optional | list of case ids to add to the run | | tag | Array/String | Optional | list of tag to add to the run | +-------------------+----------------+-----------+---------------------------------------+ Returns: The newly created object hash. Example: >>> values = {'build': 384, 'manager': 137, 'plan': 137, 'product': 61, 'product_version': 93, 'summary': 'Testing XML-RPC for TCMS', } >>> TestRun.create(values) """ from datetime import datetime from tcms.core import forms from tcms.testruns.forms import XMLRPCNewRunForm if not values.get('product'): raise ValueError('Value of product is required') # TODO: XMLRPC only accept HH:MM:SS rather than DdHhMm if values.get('estimated_time'): values['estimated_time'] = pre_process_estimated_time( values.get('estimated_time')) if values.get('case'): values['case'] = pre_process_ids(value=values['case']) form = XMLRPCNewRunForm(values) form.populate(product_id=values['product']) if form.is_valid(): tr = TestRun.objects.create( product_version=form.cleaned_data['product_version'], plan_text_version=form.cleaned_data['plan_text_version'], stop_date=form.cleaned_data['status'] and datetime.now() or None, summary=form.cleaned_data['summary'], notes=form.cleaned_data['notes'], estimated_time=form.cleaned_data['estimated_time'], plan=form.cleaned_data['plan'], build=form.cleaned_data['build'], manager=form.cleaned_data['manager'], default_tester=form.cleaned_data['default_tester'], ) if form.cleaned_data['case']: for c in form.cleaned_data['case']: tr.add_case_run(case=c) del c if form.cleaned_data['tag']: tags = form.cleaned_data['tag'] if isinstance(tags, str): tags = [c.strip() for c in tags.split(',') if c] for tag in tags: t, c = TestTag.objects.get_or_create(name=tag) tr.add_tag(tag=t) del tag, t, c else: raise ValueError(forms.errors_to_list(form)) return tr.serialize()
def update(request, case_run_ids, values): """Updates the fields of the selected case-runs. :param case_run_ids: give one or more case run IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case run ID. :type run_ids: int, str or list :param dict values: a mapping containing these data to update specified case runs. * build: (int) * assignee: (int) * case_run_status: (int) * notes: (str) * sortkey: (int) :return: In the case of a single object, it is returned. If a list was passed, it returns an array of object hashes. If the update on any particular object failed, the hash will contain a ERROR key and the message as to why it failed. Example:: # Update alias to 'tcms' for case 12345 and 23456 >>> TestCaseRun.update([12345, 23456], {'assignee': 2206}) """ from datetime import datetime from tcms.core import forms from tcms.testruns.forms import XMLRPCUpdateCaseRunForm pks_to_update = pre_process_ids(case_run_ids) tcrs = TestCaseRun.objects.filter(pk__in=pks_to_update) form = XMLRPCUpdateCaseRunForm(values) if form.is_valid(): data = {} if form.cleaned_data['build']: data['build'] = form.cleaned_data['build'] if form.cleaned_data['assignee']: data['assignee'] = form.cleaned_data['assignee'] if form.cleaned_data['case_run_status']: data['case_run_status'] = form.cleaned_data['case_run_status'] data['tested_by'] = request.user data['close_date'] = datetime.now() if 'notes' in values: if values['notes'] in (None, ''): data['notes'] = values['notes'] if form.cleaned_data['notes']: data['notes'] = form.cleaned_data['notes'] if form.cleaned_data['sortkey'] is not None: data['sortkey'] = form.cleaned_data['sortkey'] tcrs.update(**data) else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': pks_to_update} return TestCaseRun.to_xmlrpc(query)
def update(request, plan_ids, values): """ Description: Updates the fields of the selected test plan. Params: $plan_ids - Integer: A single TestPlan ID. $values - Hash of keys matching TestPlan fields and the new values to set each field to. +-------------------------------------------+----------------+--------------------------------------------+ | Field | Type | Description | +-------------------------------------------+----------------+--------------------------------------------+ | product | Integer | ID of product | | name | String | | | type | Integer | ID of plan type | | product_version(default_product_version) | Integer | ID of version, product_version(recommend), | | | | default_product_version will be deprecated | | | | in future release. | | owner | String/Integer | user_name/user_id | | parent | Integer | Parent plan ID | | is_active | Boolean | True/False | | env_group | Integer | | +-------------------------+----------------+--------------------------------------------------------------+ Returns: Hash: The updated test plan object. Example: # Update product to 61 for plan 207 and 208 >>> TestPlan.update([207, 208], {'product': 61}) """ from tcms.core import forms from tcms.xmlrpc.forms import EditPlanForm if values.get('default_product_version'): values['product_version'] = values.pop('default_product_version') form = EditPlanForm(values) if values.get('product_version') and not values.get('product'): raise ValueError('Field "product" is required by product_version') if values.get('product') and not values.get('product_version'): raise ValueError('Field "product_version" is required by product') if values.get('product_version') and values.get('product'): form.populate(product_id=values['product']) plan_ids = pre_process_ids(value=plan_ids) tps = TestPlan.objects.filter(pk__in=plan_ids) if form.is_valid(): _values = dict() if form.cleaned_data['name']: _values['name'] = form.cleaned_data['name'] if form.cleaned_data['type']: _values['type'] = form.cleaned_data['type'] if form.cleaned_data['product']: _values['product'] = form.cleaned_data['product'] if form.cleaned_data['product_version']: _values['product_version'] = form.cleaned_data['product_version'] if form.cleaned_data['owner']: _values['owner'] = form.cleaned_data['owner'] if form.cleaned_data['parent']: _values['parent'] = form.cleaned_data['parent'] if not (values.get('is_active') is None): _values['is_active'] = form.cleaned_data['is_active'] tps.update(**_values) if form.cleaned_data['env_group']: # NOTE: MyISAM does not support transaction, so no need to use # transaction.commit_on_success to control the commit. cursor = connection.writer_cursor in_condition = ','.join(itertools.repeat('%s', len(plan_ids))) del_env_group_sql = TP_CLEAR_ENV_GROUP % in_condition cursor.execute(del_env_group_sql, plan_ids) insert_values = ','.join( itertools.repeat('(%s, %s)', len(plan_ids))) insert_env_group_sql = TP_ADD_ENV_GROUP % insert_values args = list() for arg in itertools.izip( plan_ids, itertools.repeat(form.cleaned_data['env_group'].pk, len(plan_ids))): args.extend(arg) cursor.execute(insert_env_group_sql, args) transaction.commit_unless_managed() else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': tps.values_list('pk', flat=True)} return TestPlan.to_xmlrpc(query)
def update(request, plan_ids, values): """Updates the fields of the selected test plan. :param plan_ids: give one or more plan IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a plan ID. :type plan_ids: int, str or list :param dict values: a mapping containing these plan data to update * product: (int) ID of product * name: (str) * type: (int) ID of plan type * product_version: (int) ID of version * default_product_version: (int) alternative version ID. * owner: (str)/(int) user_name/user_id * parent: (int) Parent plan ID * is_active: bool True/False * env_group: (int) New environment group ID :return: a mapping of updated :class:`TestPlan`. :rtype: dict Example:: # Update product to 7 for plan 1 and 2 TestPlan.update([1, 2], {'product': 7}) .. deprecated:: x.y ``default_product_version`` is deprecated and will be removed. """ from tcms.core import forms from tcms.xmlrpc.forms import EditPlanForm if values.get('default_product_version'): values['product_version'] = values.pop('default_product_version') form = EditPlanForm(values) if values.get('product_version') and not values.get('product'): raise ValueError('Field "product" is required by product_version') if values.get('product') and not values.get('product_version'): raise ValueError('Field "product_version" is required by product') if values.get('product_version') and values.get('product'): form.populate(product_id=values['product']) plan_ids = pre_process_ids(value=plan_ids) tps = TestPlan.objects.filter(pk__in=plan_ids) if form.is_valid(): _values = dict() if form.cleaned_data['name']: _values['name'] = form.cleaned_data['name'] if form.cleaned_data['type']: _values['type'] = form.cleaned_data['type'] if form.cleaned_data['product']: _values['product'] = form.cleaned_data['product'] if form.cleaned_data['product_version']: _values['product_version'] = form.cleaned_data['product_version'] if form.cleaned_data['owner']: _values['owner'] = form.cleaned_data['owner'] if form.cleaned_data['parent']: _values['parent'] = form.cleaned_data['parent'] if not (values.get('is_active') is None): _values['is_active'] = form.cleaned_data['is_active'] tps.update(**_values) # requested to update environment group for selected test plans if form.cleaned_data['env_group']: # prepare the list of new objects to be inserted into DB new_objects = [ TCMSEnvPlanMap(plan_id=plan_pk, group_id=form.cleaned_data['env_group'].pk) for plan_pk in plan_ids ] # first delete the old values (b/c many-to-many I presume ?) TCMSEnvPlanMap.objects.filter(plan__in=plan_ids).delete() # then create all objects with 1 INSERT TCMSEnvPlanMap.objects.bulk_create(new_objects) else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': tps.values_list('pk', flat=True)} return TestPlan.to_xmlrpc(query)
def update(request, run_ids, values): """ Description: Updates the fields of the selected test run. Params: $run_ids - Integer/Array/String: An integer or alias representing the ID in the database, an array of run_ids, or a string of comma separated run_ids. $values - Hash of keys matching TestRun fields and the new values to set each field to. See params of TestRun.create for description +-------------------+----------------+--------------------------------+ | Field | Type | Description | +-------------------+----------------+--------------------------------+ | plan | Integer | TestPlan.plan_id | | product | Integer | Product.id | | build | Integer | Build.id | | manager | Integer | Auth.User.id | | default_tester | Intege | Auth.User.id | | summary | String | | | estimated_time | TimeDelta | 2h30m30s(recommend) or HH:MM:SS| | product_version | Integer | | | plan_text_version | Integer | | | notes | String | | | status | Integer | 0:RUNNING 1:FINISHED | +-------------------+----------------+ -------------------------------+ Returns: Hash: The updated test run object. Example: # Update status to finished for run 1193 and 1194 >>> TestRun.update([1193, 1194], {'status': 1}) """ from datetime import datetime from tcms.core import forms from tcms.testruns.forms import XMLRPCUpdateRunForm if (values.get('product_version') and not values.get('product')): raise ValueError('Field "product" is required by product_version') if values.get('estimated_time'): values['estimated_time'] = pre_process_estimated_time( values.get('estimated_time')) form = XMLRPCUpdateRunForm(values) if values.get('product_version'): form.populate(product_id=values['product']) if form.is_valid(): trs = TestRun.objects.filter(pk__in=pre_process_ids(value=run_ids)) _values = dict() if form.cleaned_data['plan']: _values['plan'] = form.cleaned_data['plan'] if form.cleaned_data['build']: _values['build'] = form.cleaned_data['build'] if form.cleaned_data['manager']: _values['manager'] = form.cleaned_data['manager'] if 'default_tester' in values: if values.get('default_tester') and \ form.cleaned_data['default_tester']: _values['default_tester'] = form.cleaned_data['default_tester'] else: _values['default_tester'] = None if form.cleaned_data['summary']: _values['summary'] = form.cleaned_data['summary'] if values.get('estimated_time') is not None: _values['estimated_time'] = form.cleaned_data['estimated_time'] if form.cleaned_data['product_version']: _values['product_version'] = form.cleaned_data['product_version'] if 'notes' in values: if values['notes'] in (None, ''): _values['notes'] = values['notes'] if form.cleaned_data['notes']: _values['notes'] = form.cleaned_data['notes'] if form.cleaned_data['plan_text_version']: _values['plan_text_version'] = form.cleaned_data[ 'plan_text_version'] if isinstance(form.cleaned_data['status'], int): if form.cleaned_data['status']: _values['stop_date'] = datetime.now() else: _values['stop_date'] = None trs.update(**_values) else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': trs.values_list('pk', flat=True)} return TestRun.to_xmlrpc(query)
def update(request, plan_ids, values): """ Description: Updates the fields of the selected test plan. Params: $plan_ids - Integer: A single TestPlan ID. $values - Hash of keys matching TestPlan fields and the new values to set each field to. +------------------------+----------------+------------------------------------+ | Field | Type | Description | +-------------------------+----------------+------------------------------------+ | product | Integer | ID of product | | name | String | | | type | Integer | ID of plan type | | default_product_version | Integer | | | parent | Integer | Parent plan ID | | is_active | Boolean | True/False | | env_group | Integer | | +-------------------------+----------------+------------------------------------+ Returns: Hash: The updated test plan object. Example: # Update product to 61 for plan 207 and 208 >>> TestPlan.update([207, 208], {'product': 61}) """ from tcms.core import forms from tcms.apps.testplans.forms import XMLRPCEditPlanForm if values.get('is_active') in (False, True): if values.get('is_active') == False: values['is_active'] = 0 else: values['is_active'] = 1 form = XMLRPCEditPlanForm(values) if values.get('default_product_version') and not values.get('product'): raise ValueError( 'Product value is required by default product version') if values.get('default_product_version') and values.get('product'): form.populate(product_id=values['product']) tps = TestPlan.objects.filter(pk__in=pre_process_ids(value=plan_ids)) if form.is_valid(): _values = dict() if form.cleaned_data['name']: _values['name'] = form.cleaned_data['name'] if form.cleaned_data['type']: _values['type'] = form.cleaned_data['type'] if form.cleaned_data['product']: _values['product'] = form.cleaned_data['product'] if form.cleaned_data['default_product_version']: _values['default_product_version'] = form.cleaned_data[ 'default_product_version'] if form.cleaned_data['parent']: _values['parent'] = form.cleaned_data['parent'] if isinstance(form.cleaned_data['is_active'], int): _values['is_active'] = form.cleaned_data['is_active'] tps.update(**_values) if form.cleaned_data['env_group']: for tp in tps.iterator(): tp.clear_env_groups() tp.add_env_group(form.cleaned_data['env_group']) else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': tps.values_list('pk', flat=True)} return TestPlan.to_xmlrpc(query)
def create(request, values): """ Description: Creates a new Test Case object and stores it in the database. Params: $values - Array/Hash: A reference to a hash or array of hashes with keys and values matching the fields of the test case to be created. +----------------------------+----------------+-----------+---------------------------------------+ | Field | Type | Null | Description | +----------------------------+----------------+-----------+---------------------------------------+ | product | Integer | Required | ID of Product | | category | Integer | Required | ID of Category | | priority | Integer | Required | ID of Priority | | summary | String | Required | | | case_status | Integer | Optional | ID of case status | | plan | Array/Str/Int | Optional | ID or List of plan_ids | | component | Integer/String | Optional | ID of Priority | | default_tester | String | Optional | Login of tester | | estimated_time | String | Optional | 2h30m30s(recommend) or HH:MM:SS Format| | is_automated | Integer | Optional | 0: Manual, 1: Auto, 2: Both | | is_automated_proposed | Boolean | Optional | Default 0 | | script | String | Optional | | | arguments | String | Optional | | | requirement | String | Optional | | | alias | String | Optional | Must be unique | | action | String | Optional | | | effect | String | Optional | Expected Result | | setup | String | Optional | | | breakdown | String | Optional | | | tag | Array/String | Optional | String Comma separated | | bug | Array/String | Optional | String Comma separated | | extra_link | String | Optional | reference link | +----------------------------+----------------+-----------+---------------------------------------+ Returns: Array/Hash: The newly created object hash if a single case was created, or an array of objects if more than one was created. If any single case threw an error during creation, a hash with an ERROR key will be set in its place. Example: # Minimal test case parameters >>> values = { 'category': 135, 'product': 61, 'summary': 'Testing XML-RPC', 'priority': 1, } >>> TestCase.create(values) """ from tcms.core import forms from tcms.xmlrpc.forms import NewCaseForm if not (values.get('category') or values.get('summary')): raise ValueError() values['component'] = pre_process_ids(values.get('component', [])) values['plan'] = pre_process_ids(values.get('plan', [])) values['bug'] = pre_process_ids(values.get('bug', [])) if values.get('estimated_time'): values['estimated_time'] = pre_process_estimated_time(values.get('estimated_time')) form = NewCaseForm(values) form.populate(values.get('product')) if form.is_valid(): # Create the case tc = TestCase.create(author=request.user, values=form.cleaned_data) # Add case text to the case tc.add_text( action=form.cleaned_data['action'] or '', effect=form.cleaned_data['effect'] or '', setup=form.cleaned_data['setup'] or '', breakdown=form.cleaned_data['breakdown'] or '', ) # Add the case to specific plans for p in form.cleaned_data['plan']: tc.add_to_plan(plan=p) del p # Add components to the case for c in form.cleaned_data['component']: tc.add_component(component=c) del c # Add tag to the case for tag in TestTag.string_to_list(values.get('tag', [])): t, c = TestTag.objects.get_or_create(name=tag) tc.add_tag(tag=t) else: # Print the errors if the form is not passed validation. raise ValueError(forms.errors_to_list(form)) return get(request, tc.case_id)
def update(request, case_ids, values): """Updates the fields of the selected case or cases. $values - Hash of keys matching TestCase fields and the new values to set each field to. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param dict values: a mapping containing these case data to update. * case_status: (ini) optional * product: (ini) optional (Required if changes category) * category: (ini) optional * priority: (ini) optional * default_tester: (str or int) optional (str - user_name, int - user_id) * estimated_time: (str) optional (2h30m30s(recommend) or HH:MM:SS * is_automated: (ini) optional (0 - Manual, 1 - Auto, 2 - Both) * is_automated_proposed: (bool) optional * script: (str) optional * arguments: (str) optional * summary: (str) optional * requirement: (str) optional * alias: (str) optional * notes: (str) optional * extra_link: (str) optional (reference link) :return: a list of mappings of updated :class:`TestCase`. :rtype: list(dict) Example:: # Update alias to 'tcms' for case 1 and 2 >>> TestCase.update([1, 2], {'alias': 'tcms'}) """ from tcms.core import forms from tcms.xmlrpc.forms import UpdateCaseForm if values.get('estimated_time'): values['estimated_time'] = pre_process_estimated_time( values.get('estimated_time')) form = UpdateCaseForm(values) if values.get('category') and not values.get('product'): raise ValueError('Product ID is required for category') if values.get('product'): form.populate(product_id=values['product']) if form.is_valid(): tcs = TestCase.update( case_ids=pre_process_ids(value=case_ids), values=form.cleaned_data, ) else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': tcs.values_list('pk', flat=True)} return TestCase.to_xmlrpc(query)
def create(request, values): """Creates a new Test Case object and stores it in the database. :param values: a mapping or list of mappings containing these case information for creation. * product: (int) **Required** ID of Product * category: (int) **Required** ID of Category * priority: (int) **Required** ID of Priority * summary: (str) **Required** * case_status: (int) optional ID of case status * plan Array/Str/Int optional ID or List of plan_ids * component: (int)/str optional ID of Priority * default_tester: (str) optional Login of tester * estimated_time: (str) optional 2h30m30s(recommend) or HH:MM:SS Format| * is_automated: (int) optional 0: Manual, 1: Auto, 2: Both * is_automated_proposed: (bool) optional Default 0 * script: (str) optional * arguments: (str) optional * requirement: (str) optional * alias: (str) optional Must be unique * action: (str) optional * effect: (str) optional Expected Result * setup: (str) optional * breakdown: (str) optional * tag Array/str optional String Comma separated * bug Array/str optional String Comma separated * extra_link: (str) optional reference link :return: a mapping of newly created test case if a single case was created, or a list of mappings of created cases if more than one are created. :rtype: dict of list[dict] Example:: # Minimal test case parameters values = { 'category': 1, 'product': 1, 'summary': 'Testing XML-RPC', 'priority': 1, } TestCase.create(values) """ from tcms.core import forms from tcms.xmlrpc.forms import NewCaseForm if not (values.get('category') or values.get('summary')): raise ValueError() values['component'] = pre_process_ids(values.get('component', [])) values['plan'] = pre_process_ids(values.get('plan', [])) values['bug'] = pre_process_ids(values.get('bug', [])) if values.get('estimated_time'): values['estimated_time'] = pre_process_estimated_time(values.get('estimated_time')) form = NewCaseForm(values) form.populate(values.get('product')) if form.is_valid(): # Create the case tc = TestCase.create(author=request.user, values=form.cleaned_data) # Add case text to the case tc.add_text( action=form.cleaned_data['action'] or '', effect=form.cleaned_data['effect'] or '', setup=form.cleaned_data['setup'] or '', breakdown=form.cleaned_data['breakdown'] or '', ) # Add the case to specific plans for p in form.cleaned_data['plan']: tc.add_to_plan(plan=p) del p # Add components to the case for c in form.cleaned_data['component']: tc.add_component(component=c) del c # Add tag to the case for tag in TestTag.string_to_list(values.get('tag', [])): t, c = TestTag.objects.get_or_create(name=tag) tc.add_tag(tag=t) else: # Print the errors if the form is not passed validation. raise ValueError(forms.errors_to_list(form)) return get(request, tc.case_id)
def update(request, case_run_ids, values): """ Description: Updates the fields of the selected case-runs. Params: $caserun_ids - Integer/String/Array Integer: A single TestCaseRun ID. String: A comma separates string of TestCaseRun IDs for batch processing. Array: An array of TestCaseRun IDs for batch mode processing $values - Hash of keys matching TestCaseRun fields and the new values to set each field to. +--------------------+----------------+ | Field | Type | +--------------------+----------------+ | build | Integer | | assignee | Integer | | case_run_status | Integer | | notes | String | | sortkey | Integer | +--------------------+----------------+ Returns: Hash/Array: In the case of a single object, it is returned. If a list was passed, it returns an array of object hashes. If the update on any particular object failed, the hash will contain a ERROR key and the message as to why it failed. Example: # Update alias to 'tcms' for case 12345 and 23456 >>> TestCaseRun.update([12345, 23456], {'assignee': 2206}) """ from datetime import datetime from tcms.core import forms from tcms.testruns.forms import XMLRPCUpdateCaseRunForm pks_to_update = pre_process_ids(case_run_ids) tcrs = TestCaseRun.objects.filter(pk__in=pks_to_update) form = XMLRPCUpdateCaseRunForm(values) if form.is_valid(): data = {} if form.cleaned_data['build']: data['build'] = form.cleaned_data['build'] if form.cleaned_data['assignee']: data['assignee'] = form.cleaned_data['assignee'] if form.cleaned_data['case_run_status']: data['case_run_status'] = form.cleaned_data['case_run_status'] data['tested_by'] = request.user data['close_date'] = datetime.now() if 'notes' in values: if values['notes'] in (None, ''): data['notes'] = values['notes'] if form.cleaned_data['notes']: data['notes'] = form.cleaned_data['notes'] if form.cleaned_data['sortkey'] is not None: data['sortkey'] = form.cleaned_data['sortkey'] tcrs.update(**data) else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': pks_to_update} return TestCaseRun.to_xmlrpc(query)
def link_plan(request, case_ids, plan_ids): """" Description: Link test cases to the given plan. Params: $case_ids - Integer/Array/String: An integer representing the ID in the database, an array of case_ids, or a string of comma separated case_ids. $plan_ids - Integer/Array/String: An integer representing the ID in the database, an array of plan_ids, or a string of comma separated plan_ids. Returns: Array: empty on success or an array of hashes with failure codes if a failure occurs Example: # Add case 1234 to plan id 54321 >>> TestCase.link_plan(1234, 54321) # Add case ids list [56789, 12345] to plan list [1234, 5678] >>> TestCase.link_plan([56789, 12345], [1234, 5678]) # Add case ids list 56789 and 12345 to plan list 1234 and 5678 with String >>> TestCase.link_plan('56789, 12345', '1234, 5678') """ case_ids = pre_process_ids(value=case_ids) qs = TestCase.objects.filter(pk__in=case_ids) tcs_ids = qs.values_list('pk', flat=True) # Check the non-exist case ids. ids_diff = set(case_ids) - set(tcs_ids.iterator()) if ids_diff: ids_str = ','.join(imap(str, ids_diff)) if len(ids_diff) > 1: err_msg = 'TestCases %s do not exist.' % ids_str else: err_msg = 'TestCase %s does not exist.' % ids_str raise ObjectDoesNotExist(err_msg) plan_ids = pre_process_ids(value=plan_ids) qs = TestPlan.objects.filter(pk__in=plan_ids) tps_ids = qs.values_list('pk', flat=True) # Check the non-exist plan ids. ids_diff = set(plan_ids) - set(tps_ids.iterator()) if ids_diff: ids_str = ','.join(imap(str, ids_diff)) if len(ids_diff) > 1: err_msg = 'TestPlans %s do not exist.' % ids_str else: err_msg = 'TestPlan %s does not exist.' % ids_str raise ObjectDoesNotExist(err_msg) # Link the plans to cases def _generate_link_plan_value(): for plan_id in plan_ids: for case_id in case_ids: yield plan_id, case_id # (plan_id, case_id) pair might probably exist in test_case_plans table, so # IGNORE tell MySQL to insert other non-exist ones without error report # that will break whole INSERT INTO operation. sql = 'INSERT IGNORE INTO test_case_plans (plan_id, case_id) VALUES\n%s' sql = sql % ',\n'.join(str(value) for value in _generate_link_plan_value()) cursor = connection.writer_cursor cursor.execute(sql) transaction.commit_unless_managed()
def link_plan(request, case_ids, plan_ids): """"Link test cases to the given plan. :param case_ids: give one or more case IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a case ID. :type case_ids: int, str or list :param plan_ids: give one or more plan IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a plan ID. :type plan_ids: int, str or list :return: a list which is empty on success or a list of mappings with failure codes if a failure occured. :rtype: list or list[dict] Example:: # Add case 1 to plan id 2 TestCase.link_plan(1, 2) # Add case ids list [1, 2] to plan list [3, 4] TestCase.link_plan([1, 2], [3, 4]) # Add case ids list 1 and 2 to plan list 3 and 4 with String TestCase.link_plan('1, 2', '3, 4') """ case_ids = pre_process_ids(value=case_ids) qs = TestCase.objects.filter(pk__in=case_ids) tcs_ids = qs.values_list('pk', flat=True) # Check the non-exist case ids. ids_diff = set(case_ids) - set(tcs_ids.iterator()) if ids_diff: ids_str = ','.join(map(str, ids_diff)) if len(ids_diff) > 1: err_msg = 'TestCases %s do not exist.' % ids_str else: err_msg = 'TestCase %s does not exist.' % ids_str raise ObjectDoesNotExist(err_msg) plan_ids = pre_process_ids(value=plan_ids) qs = TestPlan.objects.filter(pk__in=plan_ids) tps_ids = qs.values_list('pk', flat=True) # Check the non-exist plan ids. ids_diff = set(plan_ids) - set(tps_ids.iterator()) if ids_diff: ids_str = ','.join(map(str, ids_diff)) if len(ids_diff) > 1: err_msg = 'TestPlans %s do not exist.' % ids_str else: err_msg = 'TestPlan %s does not exist.' % ids_str raise ObjectDoesNotExist(err_msg) # (plan_id, case_id) pair might probably exist in test_case_plans table, so # skip the ones that do exist and create the rest. # note: this query returns a list of tuples! existing = TestCasePlan.objects.filter( plan__in=plan_ids, case__in=case_ids ).values_list('plan', 'case') # Link the plans to cases def _generate_link_plan_value(): for plan_id in plan_ids: for case_id in case_ids: if (plan_id, case_id) not in existing: yield plan_id, case_id TestCasePlan.objects.bulk_create([ TestCasePlan(plan_id=_plan_id, case_id=_case_id) for _plan_id, _case_id in _generate_link_plan_value() ])
def create(request, values): """Creates a new Test Run object and stores it in the database. :param dict values: a mapping containing these data to create a test run. * plan: (int) **Required** ID of test plan * build: (int)/(str) **Required** ID of Build * manager: (int) **Required** ID of run manager * summary: (str) **Required** * product: (int) **Required** ID of product * product_version: (int) **Required** ID of product version * default_tester: (int) optional ID of run default tester * plan_text_version: (int) optional * estimated_time: (str) optional, could be in format ``2h30m30s``, which is recommended or ``HH:MM:SS``. * notes: (str) optional * status: (int) optional 0:RUNNING 1:STOPPED (default 0) * case: list or (str) optional list of case ids to add to the run * tag: list or (str) optional list of tag to add to the run :return: a mapping representing newly created :class:`TestRun`. :rtype: dict .. versionchanged:: 4.5 Argument ``errata_id`` is removed. Example:: values = { 'build': 2, 'manager': 1, 'plan': 1, 'product': 1, 'product_version': 2, 'summary': 'Testing XML-RPC for TCMS', } TestRun.create(values) """ from datetime import datetime from tcms.core import forms from tcms.testruns.forms import XMLRPCNewRunForm if not values.get('product'): raise ValueError('Value of product is required') # TODO: XMLRPC only accept HH:MM:SS rather than DdHhMm if values.get('estimated_time'): values['estimated_time'] = pre_process_estimated_time( values.get('estimated_time')) if values.get('case'): values['case'] = pre_process_ids(value=values['case']) form = XMLRPCNewRunForm(values) form.populate(product_id=values['product']) if form.is_valid(): tr = TestRun.objects.create( product_version=form.cleaned_data['product_version'], plan_text_version=form.cleaned_data['plan_text_version'], stop_date=form.cleaned_data['status'] and datetime.now() or None, summary=form.cleaned_data['summary'], notes=form.cleaned_data['notes'], estimated_time=form.cleaned_data['estimated_time'], plan=form.cleaned_data['plan'], build=form.cleaned_data['build'], manager=form.cleaned_data['manager'], default_tester=form.cleaned_data['default_tester'], ) if form.cleaned_data['case']: for c in form.cleaned_data['case']: tr.add_case_run(case=c) del c if form.cleaned_data['tag']: tags = form.cleaned_data['tag'] if isinstance(tags, str): tags = [c.strip() for c in tags.split(',') if c] for tag in tags: t, c = TestTag.objects.get_or_create(name=tag) tr.add_tag(tag=t) del tag, t, c else: raise ValueError(forms.errors_to_list(form)) return tr.serialize()
def update(request, run_ids, values): """Updates the fields of the selected test run. :param run_ids: give one or more run IDs. It could be an integer, a string containing comma separated IDs, or a list of int each of them is a run ID. :type run_ids: int, str or list :param dict values: a mapping containing these data to update specified runs. * plan: (int) TestPlan.plan_id * product: (int) Product.id * build: (int) Build.id * manager: (int) Auth.User.id * default_tester: Intege Auth.User.id * summary: (str) * estimated_time: (TimeDelta) in format ``2h30m30s`` which is recommended or ``HH:MM:SS``. * product_version: (int) * plan_text_version: (int) * notes: (str) * status: (int) 0:RUNNING 1:FINISHED :return: list of mappings of the updated test runs. :rtype: list[dict] .. versionchanged:: 4.5 Argument ``errata_id`` is removed. Example:: # Update status to finished for run 1 and 2 TestRun.update([1, 2], {'status': 1}) """ from datetime import datetime from tcms.core import forms from tcms.testruns.forms import XMLRPCUpdateRunForm if (values.get('product_version') and not values.get('product')): raise ValueError('Field "product" is required by product_version') if values.get('estimated_time'): values['estimated_time'] = pre_process_estimated_time( values.get('estimated_time')) form = XMLRPCUpdateRunForm(values) if values.get('product_version'): form.populate(product_id=values['product']) if form.is_valid(): trs = TestRun.objects.filter(pk__in=pre_process_ids(value=run_ids)) _values = dict() if form.cleaned_data['plan']: _values['plan'] = form.cleaned_data['plan'] if form.cleaned_data['build']: _values['build'] = form.cleaned_data['build'] if form.cleaned_data['manager']: _values['manager'] = form.cleaned_data['manager'] if 'default_tester' in values: default_tester = form.cleaned_data['default_tester'] if values.get('default_tester') and default_tester: _values['default_tester'] = default_tester else: _values['default_tester'] = None if form.cleaned_data['summary']: _values['summary'] = form.cleaned_data['summary'] if values.get('estimated_time') is not None: _values['estimated_time'] = form.cleaned_data['estimated_time'] if form.cleaned_data['product_version']: _values['product_version'] = form.cleaned_data['product_version'] if 'notes' in values: if values['notes'] in (None, ''): _values['notes'] = values['notes'] if form.cleaned_data['notes']: _values['notes'] = form.cleaned_data['notes'] if form.cleaned_data['plan_text_version']: _values['plan_text_version'] = form.cleaned_data[ 'plan_text_version'] if isinstance(form.cleaned_data['status'], int): if form.cleaned_data['status']: _values['stop_date'] = datetime.now() else: _values['stop_date'] = None trs.update(**_values) else: raise ValueError(forms.errors_to_list(form)) query = {'pk__in': trs.values_list('pk', flat=True)} return TestRun.to_xmlrpc(query)
def update(plan_ids, values): """ Description: Updates the fields of the selected test plan. Params: $plan_ids - Integer: A single (or list of) TestPlan ID. $values - Hash of keys matching TestPlan fields and the new values to set each field to. +---------------------------+----------------+--------------------------------------------+ | Field | Type | Description | +---------------------------+----------------+--------------------------------------------+ | product | Integer | ID of product | | name | String | | | type | Integer | ID of plan type | | product_version | Integer | ID of version, product_version(recommend), | | (default_product_version)| | default_product_version will be deprecated | | | | in future release. | | owner | String/Integer | user_name/user_id | | parent | Integer | Parent plan ID | | is_active | Boolean | True/False | | env_group | Integer | New environment group ID | +---------------------------+-------------------------------------------------------------+ Returns: Hash: The updated test plan object. Example: # Update product to 61 for plan 207 and 208 >>> TestPlan.update([207, 208], {'product': 61}) """ from tcms.xmlrpc.forms import EditPlanForm if values.get('default_product_version'): values['product_version'] = values.pop('default_product_version') form = EditPlanForm(values) if values.get('product_version') and not values.get('product'): raise ValueError('Field "product" is required by product_version') if values.get('product') and not values.get('product_version'): raise ValueError('Field "product_version" is required by product') if values.get('product_version') and values.get('product'): form.populate(product_id=values['product']) plan_ids = pre_process_ids(value=plan_ids) tps = TestPlan.objects.filter(pk__in=plan_ids) if form.is_valid(): _values = dict() if form.cleaned_data['name']: _values['name'] = form.cleaned_data['name'] if form.cleaned_data['type']: _values['type'] = form.cleaned_data['type'] if form.cleaned_data['product']: _values['product'] = form.cleaned_data['product'] if form.cleaned_data['product_version']: _values['product_version'] = form.cleaned_data[ 'product_version'] if form.cleaned_data['owner']: _values['owner'] = form.cleaned_data['owner'] if form.cleaned_data['parent']: _values['parent'] = form.cleaned_data['parent'] if not (values.get('is_active') is None): _values['is_active'] = form.cleaned_data['is_active'] tps.update(**_values) # requested to update environment group for selected test plans if form.cleaned_data['env_group']: # prepare the list of new objects to be inserted into DB new_objects = [ TCMSEnvPlanMap( plan_id=plan_pk, group_id=form.cleaned_data['env_group'].pk ) for plan_pk in plan_ids ] # first delete the old values (b/c many-to-many I presume ?) TCMSEnvPlanMap.objects.filter(plan__in=plan_ids).delete() # then create all objects with 1 INSERT TCMSEnvPlanMap.objects.bulk_create(new_objects) else: raise ValueError(form_errors_to_list(form)) query = {'pk__in': tps.values_list('pk', flat=True)} return TestPlan.to_xmlrpc(query)
def test_pre_process_ids_with_others(self): with self.assertRaisesRegex(TypeError, 'Unrecognizable type of ids'): U.pre_process_ids((1,)) with self.assertRaisesRegex(TypeError, 'Unrecognizable type of ids'): U.pre_process_ids({'a': 1})