Esempio n. 1
0
class TestPlan(TreeNode, UrlMixin):
    """A plan within the TCMS"""

    history = KiwiHistoricalRecords()

    name = models.CharField(max_length=255, db_index=True)
    text = models.TextField(blank=True)
    create_date = models.DateTimeField(auto_now_add=True)
    is_active = models.BooleanField(default=True, db_index=True)
    extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True)

    product_version = models.ForeignKey(
        Version, related_name="plans", on_delete=models.CASCADE
    )
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    product = models.ForeignKey(
        "management.Product", related_name="plan", on_delete=models.CASCADE
    )
    type = models.ForeignKey(PlanType, on_delete=models.CASCADE)
    tag = models.ManyToManyField(
        "management.Tag", through="testplans.TestPlanTag", related_name="plan"
    )

    def __str__(self):
        return self.name

    def add_case(self, case, sortkey=None):
Esempio n. 2
0
class TestPlan(TCMSActionModel):
    """A plan within the TCMS"""
    history = KiwiHistoricalRecords()

    name = models.CharField(max_length=255, db_index=True)
    text = models.TextField(blank=True)
    create_date = models.DateTimeField(auto_now_add=True)
    is_active = models.BooleanField(default=True, db_index=True)
    extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True)

    product_version = models.ForeignKey(Version, related_name='plans', on_delete=models.CASCADE)
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    product = models.ForeignKey('management.Product', related_name='plan',
                                on_delete=models.CASCADE)
    type = models.ForeignKey(PlanType, on_delete=models.CASCADE)
    parent = models.ForeignKey('TestPlan', blank=True, null=True, related_name='child_set',
                               on_delete=models.CASCADE)

    tag = models.ManyToManyField('management.Tag',
                                 through='testplans.TestPlanTag',
                                 related_name='plan')

    def __str__(self):
        return self.name

    @classmethod
    def to_xmlrpc(cls, query=None):

        _query = query or {}
        qs = distinct_filter(TestPlan, _query).order_by('pk')
        serializer = TestPlanRPCSerializer(model_class=cls, queryset=qs)
        return serializer.serialize_queryset()

    def add_case(self, case, sortkey=None):
Esempio n. 3
0
class TestPlan(TCMSActionModel):
    """A plan within the TCMS"""
    history = KiwiHistoricalRecords()

    plan_id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255, db_index=True)
    text = models.TextField(blank=True)
    create_date = models.DateTimeField(db_column='creation_date', auto_now_add=True)
    is_active = models.BooleanField(db_column='isactive', default=True, db_index=True)
    extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True)

    product_version = models.ForeignKey(Version, related_name='plans', on_delete=models.CASCADE)
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    product = models.ForeignKey('management.Product', related_name='plan',
                                on_delete=models.CASCADE)
    type = models.ForeignKey(PlanType, on_delete=models.CASCADE)
    parent = models.ForeignKey('TestPlan', blank=True, null=True, related_name='child_set',
                               on_delete=models.CASCADE)

    tag = models.ManyToManyField('management.Tag',
                                 through='testplans.TestPlanTag',
                                 related_name='plan')

    class Meta:
        index_together = [['product', 'plan_id']]

    def __str__(self):
        return self.name

    @classmethod
    def to_xmlrpc(cls, query=None):

        _query = query or {}
        qs = distinct_filter(TestPlan, _query).order_by('pk')
        serializer = TestPlanXMLRPCSerializer(model_class=cls, queryset=qs)
        return serializer.serialize_queryset()

    @classmethod
    def list(cls, query=None):
        """docstring for list_plans"""

        new_query = {}

        for key, value in query.items():
            if value and key not in ['action', 't', 'f', 'a']:
                if key == 'version':  # comes from case/clone.html
                    key = 'product_version'
                new_query[key] = value.strip() if hasattr(value, 'strip') else value

        query_set = cls.objects

        if new_query.get('search'):
            query_set = query_set.filter(Q(plan_id__icontains=new_query['search']) |
                                         Q(name__icontains=new_query['search']))
            del new_query['search']

        return query_set.filter(**new_query).order_by('pk').distinct()

    def add_case(self, case, sortkey=None):
Esempio n. 4
0
class TestExecution(TCMSActionModel):
    history = KiwiHistoricalRecords()

    case_run_id = models.AutoField(primary_key=True)
    assignee = models.ForeignKey(settings.AUTH_USER_MODEL,
                                 blank=True,
                                 null=True,
                                 related_name='case_run_assignee',
                                 on_delete=models.CASCADE)
    tested_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                  blank=True,
                                  null=True,
                                  related_name='case_run_tester',
                                  on_delete=models.CASCADE)
    case_text_version = models.IntegerField()
    close_date = models.DateTimeField(null=True, blank=True)
    sortkey = models.IntegerField(null=True, blank=True)

    run = models.ForeignKey(TestRun,
                            related_name='case_run',
                            on_delete=models.CASCADE)
    case = models.ForeignKey('testcases.TestCase',
                             related_name='case_run',
                             on_delete=models.CASCADE)
    status = models.ForeignKey(TestExecutionStatus, on_delete=models.CASCADE)
    build = models.ForeignKey('management.Build', on_delete=models.CASCADE)

    class Meta:
        unique_together = ('case', 'run', 'case_text_version')

    def links(self):
        """
            Returns all links attached to this object!
        """
        return LinkReference.objects.filter(execution=self.pk)

    def __str__(self):
        return '%s: %s' % (self.pk, self.case_id)

    @classmethod
    def to_xmlrpc(cls, query: dict = None):
        if query is None:
            query = {}
        query_set = distinct_filter(TestExecution, query).order_by('pk')
        serializer = TestExecutionXMLRPCSerializer(model_class=cls,
                                                   queryset=query_set)
        return serializer.serialize_queryset()

    def get_bugs(self):
        return LinkReference.objects.filter(execution=self.pk, is_defect=True)

    def get_bugs_count(self):
        return self.get_bugs().count()

    def _get_absolute_url(self):
        # NOTE: this returns the URL to the TestRun containing this TestExecution!
        return reverse('testruns-get', args=[self.run_id])
Esempio n. 5
0
class TestExecution(TCMSActionModel):
    history = KiwiHistoricalRecords()

    assignee = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        related_name="case_run_assignee",
        on_delete=models.CASCADE,
    )
    tested_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        related_name="case_run_tester",
        on_delete=models.CASCADE,
    )
    case_text_version = models.IntegerField()
    close_date = models.DateTimeField(null=True, blank=True)
    sortkey = models.IntegerField(null=True, blank=True)

    run = models.ForeignKey(TestRun,
                            related_name="case_run",
                            on_delete=models.CASCADE)
    case = models.ForeignKey("testcases.TestCase",
                             related_name="case_run",
                             on_delete=models.CASCADE)
    status = models.ForeignKey(TestExecutionStatus, on_delete=models.CASCADE)
    build = models.ForeignKey("management.Build", on_delete=models.CASCADE)

    class Meta:
        unique_together = ("case", "run", "case_text_version")

    def links(self):
        # used in tests
        return LinkReference.objects.filter(execution=self.pk)

    def __str__(self):
        return "%s: %s" % (self.pk, self.case_id)

    @classmethod
    def to_xmlrpc(cls, query: dict = None):
        if query is None:
            query = {}
        query_set = distinct_filter(TestExecution, query).order_by("pk")
        serializer = TestExecutionRPCSerializer(model_class=cls,
                                                queryset=query_set)
        return serializer.serialize_queryset()

    def get_bugs(self):
        return LinkReference.objects.filter(execution=self.pk, is_defect=True)

    def _get_absolute_url(self):
        # NOTE: this returns the URL to the TestRun containing this TestExecution!
        return reverse("testruns-get", args=[self.run_id])
Esempio n. 6
0
class Template(models.Model):
    history = KiwiHistoricalRecords()

    name = models.CharField(max_length=255)
    text = models.TextField(blank=False)

    class Meta:
        verbose_name = _("Template")
        verbose_name_plural = _("Templates")

    def __str__(self):
        return self.name
Esempio n. 7
0
class TestExecution(models.Model, UrlMixin):
    history = KiwiHistoricalRecords()

    assignee = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        related_name="execution_assignee",
        on_delete=models.CASCADE,
    )
    tested_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        blank=True,
        null=True,
        related_name="execution_tester",
        on_delete=models.CASCADE,
    )
    case_text_version = models.IntegerField()
    start_date = models.DateTimeField(null=True, blank=True, db_index=True)
    stop_date = models.DateTimeField(null=True, blank=True, db_index=True)
    sortkey = models.IntegerField(null=True, blank=True)

    run = models.ForeignKey(TestRun,
                            related_name="executions",
                            on_delete=models.CASCADE)
    case = models.ForeignKey("testcases.TestCase",
                             related_name="executions",
                             on_delete=models.CASCADE)
    status = models.ForeignKey(TestExecutionStatus, on_delete=models.CASCADE)
    build = models.ForeignKey("management.Build", on_delete=models.CASCADE)

    class Meta:
        unique_together = ("case", "run", "case_text_version")

    def __str__(self):
        return "%s: %s" % (self.pk, self.case_id)

    def links(self):
        return LinkReference.objects.filter(execution=self.pk)

    def get_bugs(self):
        return self.links().filter(is_defect=True)

    def _get_absolute_url(self):
        # NOTE: this returns the URL to the TestRun containing this TestExecution!
        return reverse("testruns-get", args=[self.run_id])

    @property
    def actual_duration(self):
        if self.stop_date is None or self.start_date is None:
            return None
        return self.stop_date - self.start_date
Esempio n. 8
0
class TestRun(TCMSActionModel):
    history = KiwiHistoricalRecords()

    run_id = models.AutoField(primary_key=True)

    # todo: this field should be removed in favor of plan.product_version
    # no longer shown in edit forms
    product_version = models.ForeignKey('management.Version',
                                        related_name='version_run',
                                        on_delete=models.CASCADE)

    start_date = models.DateTimeField(auto_now_add=True, db_index=True)
    stop_date = models.DateTimeField(null=True, blank=True, db_index=True)
    summary = models.TextField()
    notes = models.TextField(blank=True)

    plan = models.ForeignKey('testplans.TestPlan',
                             related_name='run',
                             on_delete=models.CASCADE)
    build = models.ForeignKey('management.Build',
                              related_name='build_run',
                              on_delete=models.CASCADE)
    manager = models.ForeignKey(settings.AUTH_USER_MODEL,
                                related_name='manager',
                                on_delete=models.CASCADE)
    default_tester = models.ForeignKey(settings.AUTH_USER_MODEL,
                                       null=True,
                                       blank=True,
                                       related_name='default_tester',
                                       on_delete=models.CASCADE)

    tag = models.ManyToManyField('management.Tag',
                                 through='testruns.TestRunTag',
                                 related_name='run')

    cc = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                through='testruns.TestRunCC')

    class Meta:
        unique_together = ('run_id', 'product_version')

    def __str__(self):
        return self.summary

    @classmethod
    def to_xmlrpc(cls, query=None):
        _query = query or {}
        qs = distinct_filter(TestRun, _query).order_by('pk')
        serializer = TestRunXMLRPCSerializer(model_class=cls, queryset=qs)
        return serializer.serialize_queryset()

    def _get_absolute_url(self):
        return reverse('testruns-get', args=[
            self.pk,
        ])

    def get_notify_addrs(self):
        """
        Get the all related mails from the run
        """
        send_to = [self.manager.email]
        send_to.extend(self.cc.values_list('email', flat=True))
        if self.default_tester_id:
            send_to.append(self.default_tester.email)

        for tcr in self.case_run.select_related('assignee').all():
            if tcr.assignee_id:
                send_to.append(tcr.assignee.email)

        send_to = set(send_to)
        # don't email author of last change
        send_to.discard(
            getattr(
                self.history.latest().history_user,  # pylint: disable=no-member
                'email',
                ''))
        return list(send_to)

    # FIXME: rewrite to use multiple values INSERT statement
    def add_case_run(self,
                     case,
                     status=1,
                     assignee=None,
                     case_text_version=None,
                     build=None,
                     sortkey=0):
Esempio n. 9
0
class TestCase(TCMSActionModel):
    history = KiwiHistoricalRecords()

    case_id = models.AutoField(primary_key=True)
    create_date = models.DateTimeField(db_column='creation_date',
                                       auto_now_add=True)
    is_automated = models.IntegerField(db_column='isautomated', default=0)
    is_automated_proposed = models.BooleanField(default=False)
    script = models.TextField(blank=True, null=True)
    arguments = models.TextField(blank=True, null=True)
    extra_link = models.CharField(max_length=1024,
                                  default=None,
                                  blank=True,
                                  null=True)
    summary = models.CharField(max_length=255)
    requirement = models.CharField(max_length=255, blank=True, null=True)
    alias = models.CharField(max_length=255, blank=True)
    estimated_time = models.DurationField(default=timedelta(0))
    notes = models.TextField(blank=True, null=True)

    case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE)
    category = models.ForeignKey(Category,
                                 related_name='category_case',
                                 on_delete=models.CASCADE)
    priority = models.ForeignKey('management.Priority',
                                 related_name='priority_case',
                                 on_delete=models.CASCADE)
    author = models.ForeignKey(settings.AUTH_USER_MODEL,
                               related_name='cases_as_author',
                               on_delete=models.CASCADE)
    default_tester = models.ForeignKey(settings.AUTH_USER_MODEL,
                                       related_name='cases_as_default_tester',
                                       blank=True,
                                       null=True,
                                       on_delete=models.CASCADE)
    reviewer = models.ForeignKey(settings.AUTH_USER_MODEL,
                                 related_name='cases_as_reviewer',
                                 null=True,
                                 on_delete=models.CASCADE)

    # FIXME: related_name should be cases instead of case. But now keep it
    # named case due to historical reason.
    plan = models.ManyToManyField('testplans.TestPlan',
                                  related_name='case',
                                  through='testcases.TestCasePlan')

    component = models.ManyToManyField('management.Component',
                                       related_name='cases',
                                       through='testcases.TestCaseComponent')

    tag = models.ManyToManyField('management.Tag',
                                 related_name='case',
                                 through='testcases.TestCaseTag')

    # todo: Auto-generated attributes from back-references:
    # 'texts' : list of TestCaseTexts (from TestCaseTexts.case)

    def __str__(self):
        return self.summary

    @classmethod
    def to_xmlrpc(cls, query=None):
        from tcms.xmlrpc.serializer import TestCaseXMLRPCSerializer
        from tcms.xmlrpc.utils import distinct_filter

        _query = query or {}
        qs = distinct_filter(TestCase, _query).order_by('pk')
        s = TestCaseXMLRPCSerializer(model_class=cls, queryset=qs)
        return s.serialize_queryset()

    @classmethod
    def create(cls, author, values):
        """
        Create the case element based on models/forms.
        """
        case = cls.objects.create(
            author=author,
            is_automated=values['is_automated'],
            is_automated_proposed=values['is_automated_proposed'],
            # sortkey = values['sortkey'],
            script=values['script'],
            arguments=values['arguments'],
            extra_link=values['extra_link'],
            summary=values['summary'],
            requirement=values['requirement'],
            alias=values['alias'],
            estimated_time=values['estimated_time'],
            case_status=values['case_status'],
            category=values['category'],
            priority=values['priority'],
            default_tester=values['default_tester'],
            notes=values['notes'],
        )
        tags = values.get('tag')
        if tags:
            map(lambda c: case.add_tag(c), tags)
        return case

    @classmethod
    def list(cls, query, plan=None):
        """List the cases with request"""
        from django.db.models import Q

        if not plan:
            q = cls.objects
        else:
            q = cls.objects.filter(plan=plan)

        if query.get('case_id_set'):
            q = q.filter(pk__in=query['case_id_set'])

        if query.get('search'):
            q = q.filter(
                Q(pk__icontains=query['search'])
                | Q(summary__icontains=query['search'])
                | Q(author__email__startswith=query['search']))

        if query.get('summary'):
            q = q.filter(Q(summary__icontains=query['summary']))

        if query.get('author'):
            q = q.filter(
                Q(author__first_name__startswith=query['author'])
                | Q(author__last_name__startswith=query['author'])
                | Q(author__username__icontains=query['author'])
                | Q(author__email__startswith=query['author']))

        if query.get('default_tester'):
            q = q.filter(
                Q(default_tester__first_name__startswith=query[
                    'default_tester']) |
                Q(default_tester__last_name__startswith=query['default_tester']
                  ) |
                Q(default_tester__username__icontains=query['default_tester'])
                | Q(default_tester__email__startswith=query['default_tester']))

        if query.get('tag__name__in'):
            q = q.filter(tag__name__in=query['tag__name__in'])

        if query.get('category'):
            q = q.filter(category__name=query['category'].name)

        if query.get('priority'):
            q = q.filter(priority__in=query['priority'])

        if query.get('case_status'):
            q = q.filter(case_status__in=query['case_status'])

        # If plan exists, remove leading and trailing whitespace from it.
        plan_str = query.get('plan', '').strip()
        if plan_str:
            try:
                # Is it an integer?  If so treat as a plan_id:
                plan_id = int(plan_str)
                q = q.filter(plan__plan_id=plan_id)
            except ValueError:
                # Not an integer - treat plan_str as a plan name:
                q = q.filter(plan__name__icontains=plan_str)
        del plan_str

        if query.get('product'):
            q = q.filter(category__product=query['product'])

        if query.get('component'):
            q = q.filter(component=query['component'])

        if query.get('bug_id'):
            q = q.filter(case_bug__bug_id__in=query['bug_id'])

        if query.get('is_automated'):
            q = q.filter(is_automated=query['is_automated'])

        if query.get('is_automated_proposed'):
            q = q.filter(is_automated_proposed=query['is_automated_proposed'])

        return q.distinct()

    @classmethod
    def list_confirmed(cls):
        confirmed_case_status = TestCaseStatus.get_CONFIRMED()

        query = {
            'case_status_id': confirmed_case_status.case_status_id,
        }

        return cls.list(query)

    def add_bug(self,
                bug_id,
                bug_system_id,
                summary=None,
                description=None,
                case_run=None,
                bz_external_track=False):
        bug, created = self.case_bug.get_or_create(
            bug_id=bug_id,
            case_run=case_run,
            bug_system_id=bug_system_id,
            summary=summary,
            description=description,
        )

        if created:
            if bz_external_track:
                bug_system = BugSystem.objects.get(pk=bug_system_id)
                it = IssueTrackerType.from_name(
                    bug_system.tracker_type)(bug_system)
                if not it.is_adding_testcase_to_issue_disabled():
                    it.add_testcase_to_issue([self], bug)
                else:
                    raise ValueError(
                        'Enable linking test cases by configuring API parameters '
                        'for this Issue Tracker!')
        else:
            raise ValueError('Bug %s already exist.' % bug_id)

    def add_component(self, component):
        return TestCaseComponent.objects.get_or_create(case=self,
                                                       component=component)

    def add_tag(self, tag):
        return TestCaseTag.objects.get_or_create(case=self, tag=tag)

    def update_tags(self, new_tags):
        """
        Update case.tag
        so that case.tag == new_tags
        """
        if new_tags is None or not isinstance(new_tags, list):
            return
        owning_tags = set(self.tag.iterator())
        new_tags = set(new_tags)
        tags_to_remove = owning_tags.difference(new_tags)
        tags_to_add = new_tags.difference(owning_tags)
        map(lambda c: self.add_tag(c), tags_to_add)
        map(lambda c: self.remove_tag(c), tags_to_remove)

    def add_text(self,
                 action,
                 effect,
                 setup,
                 breakdown,
                 author=None,
                 create_date=datetime.now(),
                 case_text_version=1):
        if not author:
            author = self.author

        new_checksum = checksum(action + effect + setup + breakdown)
        latest_text = self.latest_text()
        old_checksum = checksum(latest_text.action + latest_text.effect +
                                latest_text.setup + latest_text.breakdown)

        if old_checksum == new_checksum:
            return latest_text

        case_text_version = self.latest_text_version() + 1
        return TestCaseText.objects.create(case=self,
                                           case_text_version=case_text_version,
                                           create_date=create_date,
                                           author=author,
                                           action=action,
                                           effect=effect,
                                           setup=setup,
                                           breakdown=breakdown)

    def add_to_plan(self, plan):
        TestCasePlan.objects.get_or_create(case=self, plan=plan)

    def clear_components(self):
        return TestCaseComponent.objects.filter(case=self, ).delete()

    def get_bugs(self):
        return Bug.objects.select_related(
            'case_run', 'bug_system').filter(case__case_id=self.case_id)

    def get_components(self):
        return self.component.all()

    def get_component_names(self):
        return self.component.values_list('name', flat=True)

    def get_choiced(self, obj_value, choices):
        for x in choices:
            if x[0] == obj_value:
                return x[1]

    def get_is_automated(self):
        return self.get_choiced(self.is_automated, AUTOMATED_CHOICES)

    def get_is_automated_form_value(self):
        if self.is_automated == 2:
            return [0, 1]

        return (self.is_automated, )

    def get_is_automated_status(self):
        return self.get_is_automated() + (self.is_automated_proposed
                                          and ' (Autoproposed)' or '')

    def get_previous_and_next(self, pk_list):
        current_idx = pk_list.index(self.pk)
        prev = TestCase.objects.get(pk=pk_list[current_idx - 1])
        try:
            next = TestCase.objects.get(pk=pk_list[current_idx + 1])
        except IndexError:
            next = TestCase.objects.get(pk=pk_list[0])

        return (prev, next)

    def get_text_with_version(self, case_text_version=None):
        if case_text_version:
            try:
                return TestCaseText.objects.get(
                    case__case_id=self.case_id,
                    case_text_version=case_text_version)
            except TestCaseText.DoesNotExist:
                return NoneText

        return self.latest_text()

    def latest_text(self, text_required=True):
        text = self.text
        if not text_required:
            text = text.defer('action', 'effect', 'setup', 'breakdown')
        qs = text.order_by('-case_text_version')[0:1]
        return NoneText if len(qs) == 0 else qs[0]

    def latest_text_version(self):
        qs = self.text.order_by('-case_text_version').only(
            'case_text_version')[0:1]
        return 0 if len(qs) == 0 else qs[0].case_text_version

    def mail(self, template, subject, context={}, to=[], request=None):
        from tcms.core.utils.mailto import mailto

        if not to:
            to = self.author.email

        to = list(set(to))
        mailto(template, subject, to, context, request)

    def remove_bug(self, bug_id, run_id=None):
        query = Bug.objects.filter(bug_id=bug_id, case=self.pk)
        if run_id:
            query = query.filter(case_run=run_id)
        else:
            query = query.filter(case_run__isnull=True)

        query.delete()

    def remove_component(self, component):
        # note: cannot use self.component.remove(component) on a ManyToManyField
        # which specifies an intermediary model so we use the model manager!
        self.component.through.objects.filter(case=self.pk,
                                              component=component.pk).delete()

    def remove_plan(self, plan):
        self.plan.through.objects.filter(case=self.pk, plan=plan.pk).delete()

    def remove_tag(self, tag):
        self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete()

    def _get_absolute_url(self, request=None):
        return reverse('testcases-get', args=[
            self.pk,
        ])

    def _get_email_conf(self):
        try:
            return self.email_settings
        except ObjectDoesNotExist:
            return TestCaseEmailSettings.objects.create(case=self)

    emailing = property(_get_email_conf)
Esempio n. 10
0
class TestRun(TCMSActionModel):
    history = KiwiHistoricalRecords()

    # todo: this field should be removed in favor of plan.product_version
    # no longer shown in edit forms
    product_version = models.ForeignKey("management.Version",
                                        related_name="version_run",
                                        on_delete=models.CASCADE)

    start_date = models.DateTimeField(auto_now_add=True, db_index=True)
    stop_date = models.DateTimeField(null=True, blank=True, db_index=True)
    summary = models.TextField()
    notes = models.TextField(blank=True)

    plan = models.ForeignKey("testplans.TestPlan",
                             related_name="run",
                             on_delete=models.CASCADE)
    build = models.ForeignKey("management.Build",
                              related_name="build_run",
                              on_delete=models.CASCADE)
    manager = models.ForeignKey(settings.AUTH_USER_MODEL,
                                related_name="manager",
                                on_delete=models.CASCADE)
    default_tester = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        related_name="default_tester",
        on_delete=models.CASCADE,
    )

    tag = models.ManyToManyField("management.Tag",
                                 through="testruns.TestRunTag",
                                 related_name="run")

    cc = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                through="testruns.TestRunCC")

    def __str__(self):
        return self.summary

    @classmethod
    def to_xmlrpc(cls, query=None):
        _query = query or {}
        qs = distinct_filter(TestRun, _query).order_by("pk")
        serializer = TestRunRPCSerializer(model_class=cls, queryset=qs)
        return serializer.serialize_queryset()

    def _get_absolute_url(self):
        return reverse(
            "testruns-get",
            args=[
                self.pk,
            ],
        )

    def get_absolute_url(self):
        return self._get_absolute_url()

    def get_notify_addrs(self):
        """
        Get the all related mails from the run
        """
        send_to = [self.manager.email]
        send_to.extend(self.cc.values_list("email", flat=True))
        if self.default_tester_id:
            send_to.append(self.default_tester.email)

        for tcr in self.case_run.select_related("assignee").all():
            if tcr.assignee_id:
                send_to.append(tcr.assignee.email)

        send_to = set(send_to)
        # don't email author of last change
        send_to.discard(
            getattr(
                self.history.latest().history_user,  # pylint: disable=no-member
                "email",
                "",
            ))
        return list(send_to)

    def create_execution(
        self,
        case,
        status=None,
        assignee=None,
        case_text_version=None,
        build=None,
        sortkey=0,
    ):
Esempio n. 11
0
class TestCase(models.Model, UrlMixin):
    history = KiwiHistoricalRecords()

    create_date = models.DateTimeField(auto_now_add=True)
    is_automated = models.BooleanField(default=False)
    script = models.TextField(blank=True, null=True)
    arguments = models.TextField(blank=True, null=True)
    extra_link = models.CharField(max_length=1024,
                                  default=None,
                                  blank=True,
                                  null=True)
    summary = models.CharField(max_length=255, db_index=True)
    requirement = models.CharField(max_length=255, blank=True, null=True)
    notes = models.TextField(blank=True, null=True)
    text = models.TextField(blank=True)
    setup_duration = models.DurationField(db_index=True, null=True, blank=True)
    testing_duration = models.DurationField(db_index=True,
                                            null=True,
                                            blank=True)

    case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE)
    category = models.ForeignKey(Category,
                                 related_name="category_case",
                                 on_delete=models.CASCADE)
    priority = models.ForeignKey("management.Priority",
                                 related_name="priority_case",
                                 on_delete=models.CASCADE)
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name="cases_as_author",
        on_delete=models.CASCADE,
    )
    default_tester = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name="cases_as_default_tester",
        blank=True,
        null=True,
        on_delete=models.CASCADE,
    )
    reviewer = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name="cases_as_reviewer",
        null=True,
        on_delete=models.CASCADE,
    )

    plan = models.ManyToManyField("testplans.TestPlan",
                                  related_name="cases",
                                  through="testcases.TestCasePlan")

    component = models.ManyToManyField(
        "management.Component",
        related_name="cases",
        through="testcases.TestCaseComponent",
    )

    tag = models.ManyToManyField("management.Tag",
                                 related_name="case",
                                 through="testcases.TestCaseTag")

    @property
    def expected_duration(self):
        result = timedelta(0)
        result += self.setup_duration or timedelta(0)
        result += self.testing_duration or timedelta(0)
        return result

    def __str__(self):
        return self.summary

    def add_component(self, component):
        return TestCaseComponent.objects.get_or_create(case=self,
                                                       component=component)

    def add_tag(self, tag):
        return TestCaseTag.objects.get_or_create(case=self, tag=tag)

    def get_text_with_version(self, case_text_version=None):
        if case_text_version:
            try:
                return self.history.get(  # pylint: disable=no-member
                    history_id=case_text_version).text
            except ObjectDoesNotExist:
                return self.text

        return self.text

    def remove_component(self, component):
        # note: cannot use self.component.remove(component) on a ManyToManyField
        # which specifies an intermediary model so we use the model manager!
        self.component.through.objects.filter(case=self.pk,
                                              component=component.pk).delete()

    def remove_tag(self, tag):
        self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete()

    def _get_absolute_url(self, request=None):
        return reverse(
            "testcases-get",
            args=[
                self.pk,
            ],
        )

    def get_absolute_url(self):
        return self._get_absolute_url()

    def _get_email_conf(self):
        try:
            # note: this is the reverse_name of a 1-to-1 field
            return self.email_settings  # pylint: disable=no-member
        except ObjectDoesNotExist:
            return TestCaseEmailSettings.objects.create(case=self)

    emailing = property(_get_email_conf)

    def clone(self, new_author, test_plans):
        values = self.__dict__.copy()
        del values["_state"]
        del values["id"]
        values["case_status_id"] = (TestCaseStatus.objects.filter(
            is_confirmed=False).first().pk)
        values["author_id"] = new_author.pk

        new_tc = self.__class__.objects.create(**values)

        # apply tags as well
        for tag in self.tag.all():
            new_tc.add_tag(tag)

        for plan in test_plans:
            plan.add_case(new_tc)

            # clone TC category b/c we may be cloning a 'linked'
            # TC which has a different Product that doesn't have the
            # same categories yet
            try:
                tc_category = plan.product.category.get(
                    name=self.category.name)
            except ObjectDoesNotExist:
                tc_category = plan.product.category.create(
                    name=self.category.name,
                    description=self.category.description,
                )
            new_tc.category = tc_category
            new_tc.save()

            # clone TC components b/c we may be cloning a 'linked'
            # TC which has a different Product that doesn't have the
            # same components yet
            for component in self.component.all():
                try:
                    new_component = plan.product.component.get(
                        name=component.name)
                except ObjectDoesNotExist:
                    new_component = plan.product.component.create(
                        name=component.name,
                        initial_owner=new_author,
                        description=component.description,
                    )
                new_tc.add_component(new_component)

        return new_tc
Esempio n. 12
0
class TestCase(TCMSActionModel):
    history = KiwiHistoricalRecords()

    create_date = models.DateTimeField(auto_now_add=True)
    is_automated = models.BooleanField(default=False)
    script = models.TextField(blank=True, null=True)
    arguments = models.TextField(blank=True, null=True)
    extra_link = models.CharField(max_length=1024,
                                  default=None,
                                  blank=True,
                                  null=True)
    summary = models.CharField(max_length=255, db_index=True)
    requirement = models.CharField(max_length=255, blank=True, null=True)
    notes = models.TextField(blank=True, null=True)
    text = models.TextField(blank=True)

    case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE)
    category = models.ForeignKey(Category,
                                 related_name="category_case",
                                 on_delete=models.CASCADE)
    priority = models.ForeignKey("management.Priority",
                                 related_name="priority_case",
                                 on_delete=models.CASCADE)
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name="cases_as_author",
        on_delete=models.CASCADE,
    )
    default_tester = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name="cases_as_default_tester",
        blank=True,
        null=True,
        on_delete=models.CASCADE,
    )
    reviewer = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        related_name="cases_as_reviewer",
        null=True,
        on_delete=models.CASCADE,
    )

    plan = models.ManyToManyField("testplans.TestPlan",
                                  related_name="cases",
                                  through="testcases.TestCasePlan")

    component = models.ManyToManyField(
        "management.Component",
        related_name="cases",
        through="testcases.TestCaseComponent",
    )

    tag = models.ManyToManyField("management.Tag",
                                 related_name="case",
                                 through="testcases.TestCaseTag")

    def __str__(self):
        return self.summary

    @classmethod
    def to_xmlrpc(cls, query=None):

        _query = query or {}
        qs = distinct_filter(TestCase, _query).order_by("pk")
        serializer = TestCaseRPCSerializer(model_class=cls, queryset=qs)
        return serializer.serialize_queryset()

    def add_component(self, component):
        return TestCaseComponent.objects.get_or_create(case=self,
                                                       component=component)

    def add_tag(self, tag):
        return TestCaseTag.objects.get_or_create(case=self, tag=tag)

    def get_text_with_version(self, case_text_version=None):
        if case_text_version:
            try:
                return self.history.get(history_id=case_text_version).text
            except ObjectDoesNotExist:
                return self.text

        return self.text

    def remove_component(self, component):
        # note: cannot use self.component.remove(component) on a ManyToManyField
        # which specifies an intermediary model so we use the model manager!
        self.component.through.objects.filter(case=self.pk,
                                              component=component.pk).delete()

    def remove_tag(self, tag):
        self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete()

    def _get_absolute_url(self, request=None):
        return reverse(
            "testcases-get",
            args=[
                self.pk,
            ],
        )

    def get_absolute_url(self):
        return self._get_absolute_url()

    def _get_email_conf(self):
        try:
            return self.email_settings
        except ObjectDoesNotExist:
            return TestCaseEmailSettings.objects.create(case=self)

    emailing = property(_get_email_conf)

    def clone(self, new_author, test_plans):
        new_tc = self.__class__.objects.create(
            is_automated=self.is_automated,
            script=self.script,
            arguments=self.arguments,
            extra_link=self.extra_link,
            summary=self.summary,
            requirement=self.requirement,
            case_status=TestCaseStatus.objects.filter(
                is_confirmed=False).first(),
            category=self.category,
            priority=self.priority,
            notes=self.notes,
            text=self.text,
            author=new_author,
            default_tester=self.default_tester,
        )

        # apply tags as well
        for tag in self.tag.all():
            new_tc.add_tag(tag)

        for plan in test_plans:
            plan.add_case(new_tc)

            # clone TC category b/c we may be cloning a 'linked'
            # TC which has a different Product that doesn't have the
            # same categories yet
            try:
                tc_category = plan.product.category.get(
                    name=self.category.name)
            except ObjectDoesNotExist:
                tc_category = plan.product.category.create(
                    name=self.category.name,
                    description=self.category.description,
                )
            new_tc.category = tc_category
            new_tc.save()

            # clone TC components b/c we may be cloning a 'linked'
            # TC which has a different Product that doesn't have the
            # same components yet
            for component in self.component.all():
                try:
                    new_component = plan.product.component.get(
                        name=component.name)
                except ObjectDoesNotExist:
                    new_component = plan.product.component.create(
                        name=component.name,
                        initial_owner=new_author,
                        description=component.description,
                    )
                new_tc.add_component(new_component)

        return new_tc
Esempio n. 13
0
class TestRun(models.Model, UrlMixin):
    history = KiwiHistoricalRecords()

    start_date = models.DateTimeField(auto_now_add=True, db_index=True)
    stop_date = models.DateTimeField(null=True, blank=True, db_index=True)
    planned_start = models.DateTimeField(db_index=True, null=True, blank=True)
    planned_stop = models.DateTimeField(db_index=True, null=True, blank=True)

    summary = models.TextField()
    notes = models.TextField(blank=True)

    plan = models.ForeignKey("testplans.TestPlan",
                             related_name="run",
                             on_delete=models.CASCADE)
    build = models.ForeignKey("management.Build",
                              related_name="build_run",
                              on_delete=models.CASCADE)
    manager = models.ForeignKey(settings.AUTH_USER_MODEL,
                                related_name="manager",
                                on_delete=models.CASCADE)
    default_tester = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        blank=True,
        related_name="default_tester",
        on_delete=models.CASCADE,
    )

    tag = models.ManyToManyField("management.Tag",
                                 through="testruns.TestRunTag",
                                 related_name="run")

    cc = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                through="testruns.TestRunCC")

    def __str__(self):
        return self.summary

    def _get_absolute_url(self):
        return reverse(
            "testruns-get",
            args=[
                self.pk,
            ],
        )

    def get_absolute_url(self):
        return self._get_absolute_url()

    def get_notify_addrs(self):
        """
        Get the all related mails from the run
        """
        send_to = [self.manager.email]
        send_to.extend(self.cc.values_list("email", flat=True))
        if self.default_tester_id:
            send_to.append(self.default_tester.email)

        for execution in self.executions.select_related("assignee").all():
            if execution.assignee_id:
                send_to.append(execution.assignee.email)

        send_to = set(send_to)
        # don't email author of last change
        send_to.discard(
            getattr(
                self.history.latest().history_user,  # pylint: disable=no-member
                "email",
                "",
            ))
        return list(send_to)

    def create_execution(
        self,
        case,
        assignee=None,
        build=None,
        sortkey=0,
    ):
Esempio n. 14
0
class TestCase(TCMSActionModel):
    history = KiwiHistoricalRecords()

    create_date = models.DateTimeField(auto_now_add=True)
    is_automated = models.BooleanField(default=False)
    script = models.TextField(blank=True, null=True)
    arguments = models.TextField(blank=True, null=True)
    extra_link = models.CharField(max_length=1024,
                                  default=None,
                                  blank=True,
                                  null=True)
    summary = models.CharField(max_length=255, db_index=True)
    requirement = models.CharField(max_length=255, blank=True, null=True)
    notes = models.TextField(blank=True, null=True)
    text = models.TextField(blank=True)

    case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE)
    category = models.ForeignKey(Category,
                                 related_name='category_case',
                                 on_delete=models.CASCADE)
    priority = models.ForeignKey('management.Priority',
                                 related_name='priority_case',
                                 on_delete=models.CASCADE)
    author = models.ForeignKey(settings.AUTH_USER_MODEL,
                               related_name='cases_as_author',
                               on_delete=models.CASCADE)
    default_tester = models.ForeignKey(settings.AUTH_USER_MODEL,
                                       related_name='cases_as_default_tester',
                                       blank=True,
                                       null=True,
                                       on_delete=models.CASCADE)
    reviewer = models.ForeignKey(settings.AUTH_USER_MODEL,
                                 related_name='cases_as_reviewer',
                                 null=True,
                                 on_delete=models.CASCADE)

    # FIXME: related_name should be cases instead of case. But now keep it
    # named case due to historical reason.
    plan = models.ManyToManyField('testplans.TestPlan',
                                  related_name='case',
                                  through='testcases.TestCasePlan')

    component = models.ManyToManyField('management.Component',
                                       related_name='cases',
                                       through='testcases.TestCaseComponent')

    tag = models.ManyToManyField('management.Tag',
                                 related_name='case',
                                 through='testcases.TestCaseTag')

    def __str__(self):
        return self.summary

    @classmethod
    def to_xmlrpc(cls, query=None):

        _query = query or {}
        qs = distinct_filter(TestCase, _query).order_by('pk')
        serializer = TestCaseRPCSerializer(model_class=cls, queryset=qs)
        return serializer.serialize_queryset()

    @classmethod
    def list(cls, query, plan=None):
        """List the cases with request"""

        if not plan:
            queryset = cls.objects
        else:
            queryset = cls.objects.filter(plan=plan)

        if query.get('case_id_set'):
            queryset = queryset.filter(pk__in=query['case_id_set'])

        if query.get('search'):
            queryset = queryset.filter(
                Q(pk__icontains=query['search'])
                | Q(summary__icontains=query['search'])
                | Q(author__email__startswith=query['search']))

        if query.get('summary'):
            queryset = queryset.filter(Q(summary__icontains=query['summary']))

        if query.get('author'):
            queryset = queryset.filter(
                Q(author__first_name__startswith=query['author'])
                | Q(author__last_name__startswith=query['author'])
                | Q(author__username__icontains=query['author'])
                | Q(author__email__startswith=query['author']))

        if query.get('default_tester'):
            queryset = queryset.filter(
                Q(default_tester__first_name__startswith=query[
                    'default_tester']) |
                Q(default_tester__last_name__startswith=query['default_tester']
                  ) |
                Q(default_tester__username__icontains=query['default_tester'])
                | Q(default_tester__email__startswith=query['default_tester']))

        if query.get('tag__name__in'):
            queryset = queryset.filter(tag__name__in=query['tag__name__in'])

        if query.get('category'):
            queryset = queryset.filter(category__name=query['category'].name)

        if query.get('priority'):
            queryset = queryset.filter(priority__in=query['priority'])

        if query.get('case_status'):
            queryset = queryset.filter(case_status__in=query['case_status'])

        # If plan exists, remove leading and trailing whitespace from it.
        # todo: this is the same as the if condition above !!! - this entire method
        # should be removed in favor of API
        plan_str = query.get('plan', '').strip()
        if plan_str:
            try:
                # Is it an integer?  If so treat as a plan_id:
                plan_id = int(plan_str)
                queryset = queryset.filter(plan__pk=plan_id)
            except ValueError:
                # Not an integer - treat plan_str as a plan name:
                queryset = queryset.filter(plan__name__icontains=plan_str)
        del plan_str

        if query.get('product'):
            queryset = queryset.filter(category__product=query['product'])

        if query.get('component'):
            queryset = queryset.filter(component=query['component'])

        if query.get('is_automated'):
            queryset = queryset.filter(is_automated=query['is_automated'])

        return queryset.distinct()

    def add_component(self, component):
        return TestCaseComponent.objects.get_or_create(case=self,
                                                       component=component)

    def add_tag(self, tag):
        return TestCaseTag.objects.get_or_create(case=self, tag=tag)

    def get_text_with_version(self, case_text_version=None):
        if case_text_version:
            try:
                return self.history.get(history_id=case_text_version).text
            except ObjectDoesNotExist:
                return self.text

        return self.text

    def remove_component(self, component):
        # note: cannot use self.component.remove(component) on a ManyToManyField
        # which specifies an intermediary model so we use the model manager!
        self.component.through.objects.filter(case=self.pk,
                                              component=component.pk).delete()

    def remove_tag(self, tag):
        self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete()

    def _get_absolute_url(self, request=None):
        return reverse('testcases-get', args=[
            self.pk,
        ])

    def get_absolute_url(self):
        return self._get_absolute_url()

    def _get_email_conf(self):
        try:
            return self.email_settings
        except ObjectDoesNotExist:
            return TestCaseEmailSettings.objects.create(case=self)

    emailing = property(_get_email_conf)

    def clone(self, new_author, test_plans):
        new_tc = self.__class__.objects.create(
            is_automated=self.is_automated,
            script=self.script,
            arguments=self.arguments,
            extra_link=self.extra_link,
            summary=self.summary,
            requirement=self.requirement,
            case_status=TestCaseStatus.get_proposed(),
            category=self.category,
            priority=self.priority,
            notes=self.notes,
            text=self.text,
            author=new_author,
            default_tester=self.default_tester,
        )

        # apply tags as well
        for tag in self.tag.all():
            new_tc.add_tag(tag)

        for plan in test_plans:
            plan.add_case(new_tc)

            # clone TC category b/c we may be cloning a 'linked'
            # TC which has a different Product that doesn't have the
            # same categories yet
            try:
                tc_category = plan.product.category.get(
                    name=self.category.name)
            except ObjectDoesNotExist:
                tc_category = plan.product.category.create(
                    name=self.category.name,
                    description=self.category.description,
                )
            new_tc.category = tc_category
            new_tc.save()

            # clone TC components b/c we may be cloning a 'linked'
            # TC which has a different Product that doesn't have the
            # same components yet
            for component in self.component.all():
                try:
                    new_component = plan.product.component.get(
                        name=component.name)
                except ObjectDoesNotExist:
                    new_component = plan.product.component.create(
                        name=component.name,
                        initial_owner=new_author,
                        description=component.description,
                    )
                new_tc.add_component(new_component)

        return new_tc
Esempio n. 15
0
class TestRun(TCMSActionModel):
    history = KiwiHistoricalRecords()

    run_id = models.AutoField(primary_key=True)

    product_version = models.ForeignKey('management.Version',
                                        related_name='version_run',
                                        on_delete=models.CASCADE)

    start_date = models.DateTimeField(auto_now_add=True, db_index=True)
    stop_date = models.DateTimeField(null=True, blank=True, db_index=True)
    summary = models.TextField()
    notes = models.TextField(blank=True)
    estimated_time = models.DurationField(default=datetime.timedelta(0))

    plan = models.ForeignKey('testplans.TestPlan',
                             related_name='run',
                             on_delete=models.CASCADE)
    environment_id = models.IntegerField(default=0)
    build = models.ForeignKey('management.Build',
                              related_name='build_run',
                              on_delete=models.CASCADE)
    manager = models.ForeignKey(settings.AUTH_USER_MODEL,
                                related_name='manager',
                                on_delete=models.CASCADE)
    default_tester = models.ForeignKey(settings.AUTH_USER_MODEL,
                                       null=True,
                                       blank=True,
                                       related_name='default_tester',
                                       on_delete=models.CASCADE)

    env_value = models.ManyToManyField('management.EnvValue',
                                       through='testruns.EnvRunValueMap')

    tag = models.ManyToManyField('management.Tag',
                                 through='testruns.TestRunTag',
                                 related_name='run')

    cc = models.ManyToManyField('auth.User', through='testruns.TestRunCC')

    class Meta:
        unique_together = ('run_id', 'product_version')

    def __str__(self):
        return self.summary

    @classmethod
    def to_xmlrpc(cls, query=None):
        _query = query or {}
        qs = distinct_filter(TestRun, _query).order_by('pk')
        serializer = TestRunXMLRPCSerializer(model_class=cls, queryset=qs)
        return serializer.serialize_queryset()

    @classmethod
    def list(cls, query):
        mapping = {
            'search':
            lambda value: Q(run_id__icontains=value) | Q(summary__icontains=
                                                         value),
            'summary':
            lambda value: Q(summary__icontains=value),
            'product':
            lambda value: Q(build__product=value),
            'product_version':
            lambda value: Q(product_version=value),
            'plan':
            plan_by_id_or_name,
            'build':
            lambda value: Q(build=value),
            'env_group':
            lambda value: Q(plan__env_group=value),
            'people_id':
            lambda value: Q(manager__id=value) | Q(default_tester__id=value),
            'manager':
            lambda value: Q(manager=value),
            'default_tester':
            lambda value: Q(default_tester=value),
            'tag__name__in':
            lambda value: Q(tag__name__in=value),
            'env_value__value__in':
            lambda value: Q(env_value__value__in=value),
            'case_run__assignee':
            lambda value: Q(case_run__assignee=value),
            'status':
            lambda value: {
                'running': Q(stop_date__isnull=True),
                'finished': Q(stop_date__isnull=False),
            }[value.lower()],
            'people':
            lambda value: {
                'default_tester': Q(default_tester=value),
                'manager': Q(manager=value),
                'people': Q(manager=value) | Q(default_tester=value),
                # TODO: Remove first one after upgrade to newer version.
                # query.set can return either '' or None sometimes, so
                # currently keeping these two lines here is a workaround.
                '': Q(manager=value) | Q(default_tester=value),
                None: Q(manager=value) | Q(default_tester=value),
            }[query.get('people_type')],
        }

        conditions = [
            mapping[key](value) for key, value in query.items()
            if value and key in mapping
        ]

        runs = cls.objects.filter(*conditions)

        value = query.get('sortby')
        if value:
            runs = runs.order_by(value)

        return runs.distinct()

    def _get_absolute_url(self):
        return reverse('testruns-get', args=[
            self.pk,
        ])

    def get_notify_addrs(self):
        """
        Get the all related mails from the run
        """
        send_to = [self.manager.email]
        send_to.extend(self.cc.values_list('email', flat=True))
        if self.default_tester_id:
            send_to.append(self.default_tester.email)

        for tcr in self.case_run.select_related('assignee').all():
            if tcr.assignee_id:
                send_to.append(tcr.assignee.email)

        send_to = set(send_to)
        # don't email author of last change
        send_to.discard(
            getattr(self.history.latest().history_user, 'email', ''))
        return list(send_to)

    # FIXME: rewrite to use multiple values INSERT statement
    def add_case_run(self,
                     case,
                     case_run_status=1,
                     assignee=None,
                     case_text_version=None,
                     build=None,
                     notes=None,
                     sortkey=0):
Esempio n. 16
0
class TestCaseRun(TCMSActionModel):
    history = KiwiHistoricalRecords()

    objects = TestCaseRunManager()

    case_run_id = models.AutoField(primary_key=True)
    assignee = models.ForeignKey(settings.AUTH_USER_MODEL,
                                 blank=True,
                                 null=True,
                                 related_name='case_run_assignee',
                                 on_delete=models.CASCADE)
    tested_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                  blank=True,
                                  null=True,
                                  related_name='case_run_tester',
                                  on_delete=models.CASCADE)
    case_text_version = models.IntegerField()
    running_date = models.DateTimeField(null=True, blank=True)
    close_date = models.DateTimeField(null=True, blank=True)
    notes = models.TextField(null=True, blank=True)
    sortkey = models.IntegerField(null=True, blank=True)

    run = models.ForeignKey(TestRun,
                            related_name='case_run',
                            on_delete=models.CASCADE)
    case = models.ForeignKey('testcases.TestCase',
                             related_name='case_run',
                             on_delete=models.CASCADE)
    case_run_status = models.ForeignKey(TestCaseRunStatus,
                                        on_delete=models.CASCADE)
    build = models.ForeignKey('management.Build', on_delete=models.CASCADE)
    environment_id = models.IntegerField(default=0)

    class Meta:
        unique_together = ('case', 'run', 'case_text_version')

    def links(self):
        """
            Returns all links attached to this object!
        """
        return LinkReference.objects.filter(test_case_run=self.pk)

    def __str__(self):
        return '%s: %s' % (self.pk, self.case_id)

    @classmethod
    def to_xmlrpc(cls, query={}):
        qs = distinct_filter(TestCaseRun, query).order_by('pk')
        serializer = TestCaseRunXMLRPCSerializer(model_class=cls, queryset=qs)
        return serializer.serialize_queryset()

    def add_bug(self,
                bug_id,
                bug_system_id,
                summary=None,
                description=None,
                bz_external_track=False):
        return self.case.add_bug(bug_id=bug_id,
                                 bug_system_id=bug_system_id,
                                 summary=summary,
                                 description=description,
                                 case_run=self,
                                 bz_external_track=bz_external_track)

    def remove_bug(self, bug_id, run_id=None):
        self.case.remove_bug(bug_id=bug_id, run_id=run_id)

    def get_bugs(self):
        return Bug.objects.filter(case_run__case_run_id=self.case_run_id)

    def get_bugs_count(self):
        return self.get_bugs().count()

    def get_text_versions(self):
        return TestCaseText.objects.filter(case__pk=self.case.pk).values_list(
            'case_text_version', flat=True)

    def get_text_with_version(self, case_text_version=None):
        if case_text_version:
            try:
                return TestCaseText.objects.get(
                    case__case_id=self.case_id,
                    case_text_version=case_text_version)
            except TestCaseText.DoesNotExist:
                return NoneText
        try:
            return TestCaseText.objects.get(
                case__case_id=self.case_id,
                case_text_version=self.case_text_version)
        except TestCaseText.DoesNotExist:
            return NoneText

    def latest_text(self):
        try:
            return TestCaseText.objects.filter(
                case__case_id=self.case_id).order_by('-case_text_version')[0]
        except IndexError:
            return NoneText

    def _get_absolute_url(self):
        # NOTE: this returns the URL to the TestRun containing this TestCaseRun!
        return reverse('testruns-get', args=[self.run_id])
Esempio n. 17
0
class TestCase(TCMSActionModel):
    history = KiwiHistoricalRecords()

    case_id = models.AutoField(primary_key=True)
    create_date = models.DateTimeField(db_column='creation_date', auto_now_add=True)
    is_automated = models.BooleanField(default=False)
    script = models.TextField(blank=True, null=True)
    arguments = models.TextField(blank=True, null=True)
    extra_link = models.CharField(max_length=1024, default=None, blank=True, null=True)
    summary = models.CharField(max_length=255)
    requirement = models.CharField(max_length=255, blank=True, null=True)
    notes = models.TextField(blank=True, null=True)
    text = models.TextField(blank=True)

    case_status = models.ForeignKey(TestCaseStatus, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, related_name='category_case',
                                 on_delete=models.CASCADE)
    priority = models.ForeignKey('management.Priority', related_name='priority_case',
                                 on_delete=models.CASCADE)
    author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='cases_as_author',
                               on_delete=models.CASCADE)
    default_tester = models.ForeignKey(settings.AUTH_USER_MODEL,
                                       related_name='cases_as_default_tester',
                                       blank=True,
                                       null=True,
                                       on_delete=models.CASCADE)
    reviewer = models.ForeignKey(settings.AUTH_USER_MODEL,
                                 related_name='cases_as_reviewer',
                                 null=True,
                                 on_delete=models.CASCADE)

    # FIXME: related_name should be cases instead of case. But now keep it
    # named case due to historical reason.
    plan = models.ManyToManyField('testplans.TestPlan', related_name='case',
                                  through='testcases.TestCasePlan')

    component = models.ManyToManyField('management.Component', related_name='cases',
                                       through='testcases.TestCaseComponent')

    tag = models.ManyToManyField('management.Tag', related_name='case',
                                 through='testcases.TestCaseTag')

    def __str__(self):
        return self.summary

    @classmethod
    def to_xmlrpc(cls, query=None):
        from tcms.xmlrpc.serializer import TestCaseXMLRPCSerializer
        from tcms.xmlrpc.utils import distinct_filter

        _query = query or {}
        qs = distinct_filter(TestCase, _query).order_by('pk')
        serializer = TestCaseXMLRPCSerializer(model_class=cls, queryset=qs)
        return serializer.serialize_queryset()

    # todo: does this check permissions ???
    @classmethod
    def create(cls, author, values):
        """
        Create the case element based on models/forms.
        """
        case = cls.objects.create(
            author=author,
            is_automated=values['is_automated'],
            # sortkey = values['sortkey'],
            script=values['script'],
            arguments=values['arguments'],
            extra_link=values['extra_link'],
            summary=values['summary'],
            requirement=values['requirement'],
            case_status=values['case_status'],
            category=values['category'],
            priority=values['priority'],
            default_tester=values['default_tester'],
            notes=values['notes'],
            text=values['text'],
        )

        # todo: should use add_tag
        tags = values.get('tag')
        if tags:
            map(case.add_tag, tags)
        return case

    @classmethod
    def list(cls, query, plan=None):
        """List the cases with request"""
        from django.db.models import Q

        if not plan:
            queryset = cls.objects
        else:
            queryset = cls.objects.filter(plan=plan)

        if query.get('case_id_set'):
            queryset = queryset.filter(pk__in=query['case_id_set'])

        if query.get('search'):
            queryset = queryset.filter(
                Q(pk__icontains=query['search']) |
                Q(summary__icontains=query['search']) |
                Q(author__email__startswith=query['search'])
            )

        if query.get('summary'):
            queryset = queryset.filter(Q(summary__icontains=query['summary']))

        if query.get('author'):
            queryset = queryset.filter(
                Q(author__first_name__startswith=query['author']) |
                Q(author__last_name__startswith=query['author']) |
                Q(author__username__icontains=query['author']) |
                Q(author__email__startswith=query['author'])
            )

        if query.get('default_tester'):
            queryset = queryset.filter(
                Q(default_tester__first_name__startswith=query[
                    'default_tester']) |
                Q(default_tester__last_name__startswith=query[
                    'default_tester']) |
                Q(default_tester__username__icontains=query[
                    'default_tester']) |
                Q(default_tester__email__startswith=query[
                    'default_tester'])
            )

        if query.get('tag__name__in'):
            queryset = queryset.filter(tag__name__in=query['tag__name__in'])

        if query.get('category'):
            queryset = queryset.filter(category__name=query['category'].name)

        if query.get('priority'):
            queryset = queryset.filter(priority__in=query['priority'])

        if query.get('case_status'):
            queryset = queryset.filter(case_status__in=query['case_status'])

        # If plan exists, remove leading and trailing whitespace from it.
        plan_str = query.get('plan', '').strip()
        if plan_str:
            try:
                # Is it an integer?  If so treat as a plan_id:
                plan_id = int(plan_str)
                queryset = queryset.filter(plan__plan_id=plan_id)
            except ValueError:
                # Not an integer - treat plan_str as a plan name:
                queryset = queryset.filter(plan__name__icontains=plan_str)
        del plan_str

        if query.get('product'):
            queryset = queryset.filter(category__product=query['product'])

        if query.get('component'):
            queryset = queryset.filter(component=query['component'])

        if query.get('bug_id'):
            queryset = queryset.filter(case_bug__bug_id__in=query['bug_id'])

        if query.get('is_automated'):
            queryset = queryset.filter(is_automated=query['is_automated'])

        return queryset.distinct()

    def add_bug(self, bug_id, bug_system_id, summary=None, description=None,
                case_run=None, bz_external_track=False):
        bug, created = self.case_bug.get_or_create(
            bug_id=bug_id,
            case_run=case_run,
            bug_system_id=bug_system_id,
            summary=summary,
            description=description,
        )

        if created:
            if bz_external_track:
                bug_system = BugSystem.objects.get(pk=bug_system_id)
                issue_tracker = IssueTrackerType.from_name(bug_system.tracker_type)(bug_system)
                if not issue_tracker.is_adding_testcase_to_issue_disabled():
                    issue_tracker.add_testcase_to_issue([self], bug)
                else:
                    raise ValueError('Enable linking test cases by configuring API parameters '
                                     'for this Issue Tracker!')
        else:
            raise ValueError('Bug %s already exist.' % bug_id)

    def add_component(self, component):
        return TestCaseComponent.objects.get_or_create(case=self, component=component)

    def add_tag(self, tag):
        return TestCaseTag.objects.get_or_create(case=self, tag=tag)

    def get_bugs(self):
        return Bug.objects.select_related(
            'case_run', 'bug_system'
        ).filter(case__case_id=self.case_id)

    def get_previous_and_next(self, pk_list):
        current_idx = pk_list.index(self.pk)
        prev = TestCase.objects.get(pk=pk_list[current_idx - 1])
        try:
            _next = TestCase.objects.get(pk=pk_list[current_idx + 1])
        except IndexError:
            _next = TestCase.objects.get(pk=pk_list[0])

        return (prev, _next)

    def get_text_with_version(self, case_text_version=None):
        if case_text_version:
            try:
                return self.history.get(history_id=case_text_version).text
            except ObjectDoesNotExist:
                return self.text

        return self.text

    def remove_bug(self, bug_id, run_id=None):
        query = Bug.objects.filter(
            bug_id=bug_id,
            case=self.pk
        )
        if run_id:
            query = query.filter(case_run=run_id)
        else:
            query = query.filter(case_run__isnull=True)

        query.delete()

    def remove_component(self, component):
        # note: cannot use self.component.remove(component) on a ManyToManyField
        # which specifies an intermediary model so we use the model manager!
        self.component.through.objects.filter(case=self.pk, component=component.pk).delete()

    def remove_tag(self, tag):
        self.tag.through.objects.filter(case=self.pk, tag=tag.pk).delete()

    def _get_absolute_url(self, request=None):
        return reverse('testcases-get', args=[self.pk, ])

    def _get_email_conf(self):
        try:
            return self.email_settings
        except ObjectDoesNotExist:
            return TestCaseEmailSettings.objects.create(case=self)

    emailing = property(_get_email_conf)