Passed
Push — master ( 62d7d9...8230b1 )
by Alexander
03:29
created

tcms.issuetracker.base.IntegrationThread.run()   A

Complexity

Conditions 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 11
rs 10
c 0
b 0
f 0
cc 2
nop 1
1
import re
2
import warnings
3
import threading
4
5
from opengraph.opengraph import OpenGraph
6
7
RE_ENDS_IN_INT = re.compile(r'[\d]+$')
8
9
10
class IssueTrackerType:
11
    """
12
        Represents actions which can be performed with issue trackers.
13
        This is a common interface for all issue trackers that Kiwi TCMS
14
        supports!
15
    """
16
    rpc_cache = {}
17
18
    def __init__(self, bug_system):
19
        """
20
            :bug_system: - BugSystem object
21
        """
22
        self.bug_system = bug_system
23
24
    @classmethod
25
    def bug_id_from_url(cls, url):
26
        """
27
            Returns a unique identifier for reported defect. This is used by the
28
            underlying integration libraries. Usually that identifier is an
29
            integer number.
30
31
            The default implementation is to leave the last group of numeric
32
            characters at the end of a string!
33
        """
34
        return int(RE_ENDS_IN_INT.search(url.strip()).group(0))
35
36
    def details(self, url):  # pylint: disable=no-self-use
37
        """
38
            Returns bug details to be used later. By default this method
39
            returns OpenGraph metadata (dict) which is shown in the UI as tooltips.
40
            You can override this method to provide different information.
41
        """
42
        result = OpenGraph(url, scrape=True)
43
44
        # remove data which we don't need
45
        for key in ['_url', 'url', 'scrape', 'type']:
46
            if key in result:
47
                del result[key]
48
49
        return result
50
51
    def _report_comment(self, execution):  # pylint: disable=no-self-use
52
        """
53
            Returns the comment which is used in the original defect report.
54
        """
55
        txt = execution.case.get_text_with_version(execution.case_text_version)
56
57
        comment = "Filed from execution %s\n\n" % execution.get_full_url()
58
        comment += "**Product:**\n%s\n\n" % execution.run.plan.product.name
59
        comment += "**Component(s):**\n%s\n\n" % \
60
                   execution.case.component.values_list('name', flat=True)
61
        comment += "**Version-Release number** (if applicable):\n"
62
        comment += "%s\n\n" % execution.build.name
63
        comment += "**Steps to reproduce**: \n%s\n\n" % txt
64
        comment += "**Actual results**: \n<describe what happened>\n\n"
65
66
        return comment
67
68
    def report_issue_from_testexecution(self, execution, user):
69
        """
70
            When marking TestExecution results inside a Test Run there is a
71
            `Report` link. When the `Report` link is clicked this method is called
72
            to help the user report an issue in the IT.
73
74
            This is implemented by constructing an URL string which will pre-fill
75
            bug details like steps to reproduce, product, version, etc from the
76
            test case. Then we open this URL into another browser window!
77
78
            :execution: TestExecution object
79
            :user: User object
80
            :return: - string - URL
81
        """
82
        raise NotImplementedError()
83
84
    def add_testexecution_to_issue(self, executions, issue_url):
85
        """
86
            When linking defect URLs to Test Execution results there is a
87
            'Add comment to Issue tracker' checkbox. If
88
            selected this method is called. It should 'link' the existing
89
            defect back to the TE/TR which reproduced it.
90
91
            Usually this is implemented by adding a new comment pointing
92
            back to the TR/TE via the internal RPC object.
93
94
            :executions: - iterable of TestExecution objects
95
            :issue_url: - the URL of the existing defect
96
        """
97
        raise NotImplementedError()
98
99
    def is_adding_testcase_to_issue_disabled(self):  # pylint: disable=invalid-name, no-self-use
100
        """
101
            When is linking a TC to a Bug report disabled?
102
            Usually when not all of the required credentials are provided.
103
104
            :return: bool
105
        """
106
        return not (self.bug_system.api_url
107
                    and self.bug_system.api_username
108
                    and self.bug_system.api_password)
109
110
    def _rpc_connection(self):
111
        """
112
            Returns an object which is used to communicate to the external system.
113
            This method is meant to be overriden by inherited classes.
114
        """
115
        raise NotImplementedError()
116
117
    @property
118
    def rpc(self):
119
        """
120
            Returns an object which is used to communicate to the external system.
121
            This property is meant to be used by the rest of the integration code
122
            and provides caching b/c connecting to a remote system may be a slow
123
            operation.
124
        """
125
        # b/c jira.JIRA tries to connect when object is created
126
        # see https://github.com/kiwitcms/Kiwi/issues/100
127
        if not self.is_adding_testcase_to_issue_disabled():
128
            return None
129
130
        if self.bug_system.base_url not in self.rpc_cache:
131
            self.rpc_cache[self.bug_system.base_url] = self._rpc_connection()
132
133
        return self.rpc_cache[self.bug_system.base_url]
134
135
136
class IntegrationThread(threading.Thread):
137
    """
138
        Used as a base class for everything else.
139
    """
140
141
    def __init__(self, rpc, bug_system, execution, bug_id):
142
        """
143
            :param rpc: Bugzilla XML-RPC object
144
            :param bug_system: BugSystem object
145
            :param execution: TestExecution object
146
            :param bug_id: Unique defect identifier in the system. Usually an int.
147
        """
148
        self.rpc = rpc
149
        self.bug_system = bug_system
150
        self.execution = execution
151
        self.bug_id = bug_id
152
153
        super().__init__()
154
155
    def text(self):
156
        """
157
            Returns the text that will be posted as a comment to
158
            the reported bug!
159
        """
160
        return """---- Confirmed via test execution ----
161
TR-%d: %s
162
%s
163
TE-%d: %s""" % (self.execution.run.pk,
164
                self.execution.run.summary,
165
                self.execution.run.get_full_url(),
166
                self.execution.pk,
167
                self.execution.case.summary)
168
169
    def post_comment(self):
170
        raise NotImplementedError()
171
172
    def run(self):
173
        """
174
            Using Bugzilla's XML-RPC try to link the test case with
175
            the bug!
176
        """
177
178
        try:
179
            self.post_comment()
180
        except Exception as err:  # pylint: disable=broad-except
181
            message = '%s: %s' % (err.__class__.__name__, err)
182
            warnings.warn(message)
183