~launchpad-pqm/launchpad/devel

11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
1
ExternalBugTracker: Bugzilla
2
============================
3048.4.1 by Bjorn Tillenius
add tests for existing ExternalSystem functionality. fix malonify_status() to use dbschema objects instead of strings.
3
3357.3.4 by Bjorn Tillenius
replace the ExternalSystem class with a simple function.
4
An ExternalBugtracker is used to talk to remote bug trackers and update bug
3691.135.4 by kiko
Rearrange bugwatch-related tests slightly
5
watches. This document describes how the Bugzilla implementation of
6
ExternalBugTracker works.
7
6570.1.5 by Graham Binns
Drive-by cleanup of bugzilla tests.
8
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
9
Basics
10
------
3691.135.4 by kiko
Rearrange bugwatch-related tests slightly
11
7773.1.2 by Graham Binns
Added a verifyObject test for Bugzilla.
12
The class that implements ExternalBugTracker is called Bugzilla.
3211.1.1 by Bjorn Tillenius
raise a more specific exception for unsupported bugtracker versions. catch the exception in checkwatches.py.
13
8523.3.10 by Gavin Panella
Fix up some externalbugtracker imports.
14
    >>> from lp.bugs.externalbugtracker import Bugzilla
8523.3.1 by Gavin Panella
Bugs tree reorg after automated migration.
15
    >>> from lp.bugs.interfaces.externalbugtracker import (
7773.1.2 by Graham Binns
Added a verifyObject test for Bugzilla.
16
    ...     IExternalBugTracker)
14600.2.2 by Curtis Hovey
Moved webapp to lp.services.
17
    >>> from lp.services.webapp.testing import verifyObject
7773.1.2 by Graham Binns
Added a verifyObject test for Bugzilla.
18
19
    >>> external_bugzilla = Bugzilla('http://example.com')
20
    >>> verifyObject(IExternalBugTracker, external_bugzilla)
21
    True
22
23
The Bugzilla ExternalBugTracker works differently depending on which
24
version of Bugzilla it is talking to. If it's a version we can't parse,
12221.1.7 by Jeroen Vermeulen
s/Unparseable/Unparsable/g, plus lint.
25
UnparsableBugTrackerVersion is raised:
7773.1.2 by Graham Binns
Added a verifyObject test for Bugzilla.
26
14604.1.1 by Curtis Hovey
Separate test-authoring classes from test-running classes.
27
    >>> from lp.testing.layers import LaunchpadZopelessLayer
5325.1.2 by Tom Berger
post review changes
28
    >>> txn = LaunchpadZopelessLayer.txn
10136.3.2 by Gavin Panella
Make _parseVersion() just pick out numbers from the version string.
29
    >>> external_bugzilla = Bugzilla('http://example.com/', version='A.B')
3211.1.1 by Bjorn Tillenius
raise a more specific exception for unsupported bugtracker versions. catch the exception in checkwatches.py.
30
    Traceback (most recent call last):
31
      ...
12221.1.7 by Jeroen Vermeulen
s/Unparseable/Unparsable/g, plus lint.
32
    UnparsableBugTrackerVersion:
10136.3.2 by Gavin Panella
Make _parseVersion() just pick out numbers from the version string.
33
    Failed to parse version 'A.B' for http://...
3211.1.1 by Bjorn Tillenius
raise a more specific exception for unsupported bugtracker versions. catch the exception in checkwatches.py.
34
6999.3.1 by Graham Binns
Fixed bug 270245.
35
The version parsing is carried out by the Bugzilla._parseVersion()
36
method, which takes a version string and returns a tuple of
37
(major_version, minor_version).
38
39
    >>> external_bugzilla = Bugzilla('http://example.com')
40
    >>> print external_bugzilla._parseVersion('3.2')
41
    (3, 2)
42
43
It can handle version strings with an -$foo suffix.
44
45
    >>> print external_bugzilla._parseVersion('3.2-foobar')
46
    (3, 2)
47
48
And will also handle versions which contain the string 'rc'.
49
50
    >>> print external_bugzilla._parseVersion('3.2rc')
51
    (3, 2)
52
53
+ characters in the version string will be removed.
54
55
    >>> print external_bugzilla._parseVersion('3.2+1')
10136.3.2 by Gavin Panella
Make _parseVersion() just pick out numbers from the version string.
56
    (3, 2, 1)
6999.3.1 by Graham Binns
Fixed bug 270245.
57
3211.1.1 by Bjorn Tillenius
raise a more specific exception for unsupported bugtracker versions. catch the exception in checkwatches.py.
58
Since we don't want to depend on a working network connection, we use a
59
slightly modified implementation.
3048.4.1 by Bjorn Tillenius
add tests for existing ExternalSystem functionality. fix malonify_status() to use dbschema objects instead of strings.
60
11716.1.12 by Curtis Hovey
Sorted imports in doctests.
61
    >>> from lp.bugs.interfaces.bugtracker import IBugTrackerSet
8523.3.1 by Gavin Panella
Bugs tree reorg after automated migration.
62
    >>> from lp.bugs.tests.externalbugtracker import TestBugzilla
11304.1.27 by Bryce Harrington
Review comment: Use parenthesis instead of backslashes
63
    >>> gnome_bugzilla = (
64
    ...     getUtility(IBugTrackerSet).getByName('gnome-bugzilla'))
5816.1.1 by Bjorn Tillenius
Make ExternalBugTracker accept a URL instead of a transaction and bug tracker.
65
    >>> external_bugzilla = TestBugzilla(gnome_bugzilla.baseurl)
3357.3.4 by Bjorn Tillenius
replace the ExternalSystem class with a simple function.
66
    >>> version = external_bugzilla._probe_version()
3048.4.1 by Bjorn Tillenius
add tests for existing ExternalSystem functionality. fix malonify_status() to use dbschema objects instead of strings.
67
    >>> version
4810.11.6 by Graham Binns
Bugzilla tests now pass.
68
    (2, 20)
3048.4.1 by Bjorn Tillenius
add tests for existing ExternalSystem functionality. fix malonify_status() to use dbschema objects instead of strings.
69
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
70
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
71
Launchpad plugin
72
----------------
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
73
9570.2.1 by Graham Binns
s/BugzillaLPPlugin/BugzillaAPI in the tests for getExternalBugTrackerToUse().
74
Some Bugzillas offer the Bugzilla 3.4 XML-RPC API or have a Launchpad
75
plugin installed. For these bugtrackers, we use the BugzillaAPI
76
ExternalBugTracker or its subclass, BugzillaLPPlugin, depending upon
77
which type of Bugzilla we're dealing with. The Bugzilla
6570.1.6 by Graham Binns
Review changes.
78
ExternalBugTracker class has a getExternalBugTrackerToUse() method which
9570.2.1 by Graham Binns
s/BugzillaLPPlugin/BugzillaAPI in the tests for getExternalBugTrackerToUse().
79
will return a BugzillaAPI instance if the remote Bugzilla offers the 3.4
80
API or a  BugzillaLPPlugin instance if the remote Bugzilla has the
81
plugin installed. If neither of these is offered, a standard Bugzilla
82
ExternalBugTracker will be returned.
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
83
9570.2.1 by Graham Binns
s/BugzillaLPPlugin/BugzillaAPI in the tests for getExternalBugTrackerToUse().
84
The Bugzilla ExternalBugTracker has a _test_xmlrpc_proxy property which
85
we override for the purpose of this test.
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
86
87
    >>> import xmlrpclib
88
    >>> class FailingXMLRPCTransport(xmlrpclib.Transport):
89
    ...
10553.2.1 by Gavin Panella
Treat the fault code 'Client' the same as METHOD_NOT_FOUND when talking to Bugzilla over XML-RPC.
90
    ...     error = xmlrpclib.Fault(
91
    ...         xmlrpclib.METHOD_NOT_FOUND, "Method doesn't exist")
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
92
    ...
93
    ...     def request(self, host, handler, request, verbose=None):
94
    ...         if self.error is not None:
95
    ...             raise self.error
96
    ...         else:
97
    ...             # We need to return something here, otherwise
98
    ...             # xmlrpclib will explode.
99
    ...             return '0.42-test'
100
    ...
101
    >>> test_transport = FailingXMLRPCTransport()
102
103
    >>> class BugzillaWithFakeProxy(Bugzilla):
104
    ...
6604.1.18 by Tom Berger
if a test proxy member is present use it (only used in tests)
105
    ...     _test_xmlrpc_proxy = xmlrpclib.ServerProxy(
106
    ...         'http://example.com/xmlrpc.cgi', transport=test_transport)
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
107
108
    >>> bugzilla = BugzillaWithFakeProxy('http://example.com')
109
10553.2.1 by Gavin Panella
Treat the fault code 'Client' the same as METHOD_NOT_FOUND when talking to Bugzilla over XML-RPC.
110
When getExternalBugTrackerToUse() receives a Fault of type
111
METHOD_NOT_FOUND from the remote server in response to its check, it
112
will return a standard Bugzilla instance.
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
113
10512.4.24 by Gavin Panella
Get all the externalbugtracker*.txt doctests passing.
114
    >>> transaction.commit()
115
9570.2.2 by Graham Binns
BugzillaAPI instances are now detected properly. Of course, everything else is broken.
116
    >>> from lp.bugs.externalbugtracker import (
117
    ...     BugzillaAPI, BugzillaLPPlugin)
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
118
    >>> bugzilla_to_use = bugzilla.getExternalBugTrackerToUse()
119
120
The returned bugtracker will be a Bugzilla instance bug not a
9570.2.1 by Graham Binns
s/BugzillaLPPlugin/BugzillaAPI in the tests for getExternalBugTrackerToUse().
121
BugzillaAPI instance.
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
122
123
    >>> (isinstance(bugzilla_to_use, Bugzilla) and
9570.2.1 by Graham Binns
s/BugzillaLPPlugin/BugzillaAPI in the tests for getExternalBugTrackerToUse().
124
    ...  not isinstance(bugzilla_to_use, BugzillaAPI))
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
125
    True
126
127
The same is true if getExternalBugTrackerToUse() receives a 404 error
128
from the remote server.
129
130
    >>> test_transport.error = xmlrpclib.ProtocolError(
131
    ...     'http://example.com/xmlrpc.cgi', 404, 'Not Found', None)
132
133
    >>> bugzilla_to_use = bugzilla.getExternalBugTrackerToUse()
134
135
    >>> (isinstance(bugzilla_to_use, Bugzilla) and
9570.2.1 by Graham Binns
s/BugzillaLPPlugin/BugzillaAPI in the tests for getExternalBugTrackerToUse().
136
    ...  not isinstance(bugzilla_to_use, BugzillaAPI))
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
137
    True
138
6645.1.1 by Graham Binns
Added tests and a fix for bug 246285.
139
Some Bugzillas respond to an invalid XML-RPC method call by returning a
140
500 error. getExternalBugTrackerToUse() handles those, too.
141
142
    >>> test_transport.error = xmlrpclib.ProtocolError(
143
    ...     'http://example.com/xmlrpc.cgi', 500, 'Server Error', None)
144
145
    >>> bugzilla_to_use = bugzilla.getExternalBugTrackerToUse()
146
147
    >>> (isinstance(bugzilla_to_use, Bugzilla) and
9570.2.1 by Graham Binns
s/BugzillaLPPlugin/BugzillaAPI in the tests for getExternalBugTrackerToUse().
148
    ...  not isinstance(bugzilla_to_use, BugzillaAPI))
6645.1.1 by Graham Binns
Added tests and a fix for bug 246285.
149
    True
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
150
6715.1.1 by Bjorn Tillenius
catch ResponseError when checking if the LP plugin is installed on bugzilla.
151
Some other Bugzillas generate an unparsable response, causing
152
ResponseError to be raised.
153
154
    >>> test_transport.error = xmlrpclib.ResponseError()
155
    >>> bugzilla_to_use = bugzilla.getExternalBugTrackerToUse()
156
157
    >>> (isinstance(bugzilla_to_use, Bugzilla) and
9570.2.1 by Graham Binns
s/BugzillaLPPlugin/BugzillaAPI in the tests for getExternalBugTrackerToUse().
158
    ...  not isinstance(bugzilla_to_use, BugzillaAPI))
6715.1.1 by Bjorn Tillenius
catch ResponseError when checking if the LP plugin is installed on bugzilla.
159
    True
160
9570.2.2 by Graham Binns
BugzillaAPI instances are now detected properly. Of course, everything else is broken.
161
If the remote Bugzilla offers the Bugzilla 3.4 API, an instance of
162
BuzillaAPI will be returned. To test this, we use a specially-crafted
163
XML-RPC proxy that behaves like a Bugzilla 3.4 instance.
164
9570.2.3 by Graham Binns
Bugzilla.getExternalBugTrackerToUse() now knows how to recognise a Bugzilla with an API.
165
    >>> class APIXMLRPCTransport(xmlrpclib.Transport):
166
    ...
167
    ...     version = '3.4.2'
168
    ...
169
    ...     def request(self, host, handler, request, verbose=None):
170
    ...         args, method_name = xmlrpclib.loads(request)
171
    ...
172
    ...         if method_name == 'Bugzilla.version':
173
    ...             return [{'version': self.version}]
174
    ...         else:
10553.2.1 by Gavin Panella
Treat the fault code 'Client' the same as METHOD_NOT_FOUND when talking to Bugzilla over XML-RPC.
175
    ...             raise xmlrpclib.Fault(
176
    ...                 xmlrpclib.METHOD_NOT_FOUND, 'No such method')
9570.2.3 by Graham Binns
Bugzilla.getExternalBugTrackerToUse() now knows how to recognise a Bugzilla with an API.
177
    ...
178
    >>> test_transport = APIXMLRPCTransport()
179
9570.2.2 by Graham Binns
BugzillaAPI instances are now detected properly. Of course, everything else is broken.
180
    >>> bugzilla._test_xmlrpc_proxy = xmlrpclib.ServerProxy(
181
    ...     'http://example.com/xmlrpc.cgi',
9570.2.3 by Graham Binns
Bugzilla.getExternalBugTrackerToUse() now knows how to recognise a Bugzilla with an API.
182
    ...     transport=test_transport)
9570.2.2 by Graham Binns
BugzillaAPI instances are now detected properly. Of course, everything else is broken.
183
184
    >>> bugzilla_to_use = bugzilla.getExternalBugTrackerToUse()
185
    >>> (isinstance(bugzilla_to_use, BugzillaAPI) and
186
    ...  not isinstance(bugzilla_to_use, BugzillaLPPlugin))
187
    True
188
9570.2.3 by Graham Binns
Bugzilla.getExternalBugTrackerToUse() now knows how to recognise a Bugzilla with an API.
189
If the remote system has the Launchpad plugin installed, an
190
getExternalBugTrackerToUse() will return a BugzillaLPPlugin instance.
191
192
    >>> class PluginXMLRPCTransport(xmlrpclib.Transport):
193
    ...
194
    ...     def request(self, host, handler, request, verbose=None):
195
    ...         args, method_name = xmlrpclib.loads(request)
196
    ...
197
    ...         if method_name == 'Launchpad.plugin_version':
198
    ...             return [{'version': '0.2'}]
199
    ...         else:
10553.2.1 by Gavin Panella
Treat the fault code 'Client' the same as METHOD_NOT_FOUND when talking to Bugzilla over XML-RPC.
200
    ...             raise xmlrpclib.Fault(
201
    ...                 xmlrpclib.METHOD_NOT_FOUND, 'No such method')
9570.2.3 by Graham Binns
Bugzilla.getExternalBugTrackerToUse() now knows how to recognise a Bugzilla with an API.
202
    ...
203
    >>> test_transport = PluginXMLRPCTransport()
204
205
    >>> bugzilla._test_xmlrpc_proxy = xmlrpclib.ServerProxy(
206
    ...     'http://example.com/xmlrpc.cgi',
207
    ...     transport=test_transport)
208
209
    >>> bugzilla_to_use = bugzilla.getExternalBugTrackerToUse()
210
    >>> isinstance(bugzilla_to_use, BugzillaLPPlugin)
211
    True
212
10136.3.1 by Gavin Panella
Check that the version information returned by the Bugzilla API is a mapping.
213
Older versions of the Bugzilla API return tuples rather than mappings
214
in response to XML-RPC calls. When something other than a mapping is
215
returned, the standard non-API non-plugin external bug tracker is
216
selected.
217
218
    >>> class OldXMLRPCTransport(xmlrpclib.Transport):
219
    ...     def request(self, host, handler, request, verbose=None):
220
    ...         args, method_name = xmlrpclib.loads(request)
221
    ...
222
    ...         if method_name == 'Bugzilla.version':
223
    ...             return ('versionResponse', {'version': '3.2.5+'})
224
    ...         else:
10553.2.1 by Gavin Panella
Treat the fault code 'Client' the same as METHOD_NOT_FOUND when talking to Bugzilla over XML-RPC.
225
    ...             raise xmlrpclib.Fault(
226
    ...                 xmlrpclib.METHOD_NOT_FOUND, 'No such method')
10136.3.1 by Gavin Panella
Check that the version information returned by the Bugzilla API is a mapping.
227
    ...
228
    >>> test_transport = OldXMLRPCTransport()
229
230
    >>> bugzilla._test_xmlrpc_proxy = xmlrpclib.ServerProxy(
231
    ...     'http://example.com/xmlrpc.cgi',
232
    ...     transport=test_transport)
233
234
    >>> bugzilla_to_use = bugzilla.getExternalBugTrackerToUse()
235
    >>> (isinstance(bugzilla_to_use, BugzillaAPI) or
236
    ...  isinstance(bugzilla_to_use, BugzillaLPPlugin))
237
    False
238
10553.2.1 by Gavin Panella
Treat the fault code 'Client' the same as METHOD_NOT_FOUND when talking to Bugzilla over XML-RPC.
239
Some Bugzillas return 'Client' instead of METHOD_NOT_FOUND when a method
240
is not discovered over XML-RPC. It's not clear if this is an error in
241
Bugzilla or in and XML-RPC library used by Bugzilla. In any case, we
242
recognize and treat it the same as METHOD_NOT_FOUND.
243
244
    >>> class OldBrokenXMLRPCTransport(xmlrpclib.Transport):
245
    ...     def request(self, host, handler, request, verbose=None):
246
    ...         args, method_name = xmlrpclib.loads(request)
247
    ...
248
    ...         if method_name == 'Bugzilla.version':
249
    ...             return ('versionResponse', {'version': '3.2.5+'})
250
    ...         else:
251
    ...             raise xmlrpclib.Fault('Client', 'No such method')
252
    ...
253
    >>> test_transport = OldBrokenXMLRPCTransport()
254
255
    >>> bugzilla._test_xmlrpc_proxy = xmlrpclib.ServerProxy(
256
    ...     'http://example.com/xmlrpc.cgi',
257
    ...     transport=test_transport)
258
259
    >>> bugzilla_to_use = bugzilla.getExternalBugTrackerToUse()
260
    >>> (isinstance(bugzilla_to_use, BugzillaAPI) or
261
    ...  isinstance(bugzilla_to_use, BugzillaLPPlugin))
262
    False
263
6570.1.1 by Graham Binns
Added tests and implementation for Bugzilla.getExternalBugTrackerToUse().
264
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
265
Status Conversion
266
-----------------
3691.135.4 by kiko
Rearrange bugwatch-related tests slightly
267
3048.4.1 by Bjorn Tillenius
add tests for existing ExternalSystem functionality. fix malonify_status() to use dbschema objects instead of strings.
268
It contains a function for converting one of its own status to a Malone
269
status. Bugzilla statuses consist of two parts, the status, and the
270
resolution, separated by a space character. The resolution only exists
271
if the bug is closed:
272
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
273
    >>> external_bugzilla.convertRemoteStatus('UNCONFIRMED').title
4318.3.4 by Gavin Panella
Make manual changes to bug status.
274
    'New'
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
275
    >>> external_bugzilla.convertRemoteStatus('NEW').title
3392.1.1 by Bjorn Tillenius
modify the bugzilla status mapping.
276
    'Confirmed'
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
277
    >>> external_bugzilla.convertRemoteStatus('ASSIGNED').title
3392.1.1 by Bjorn Tillenius
modify the bugzilla status mapping.
278
    'In Progress'
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
279
    >>> external_bugzilla.convertRemoteStatus('REOPENED').title
3392.1.1 by Bjorn Tillenius
modify the bugzilla status mapping.
280
    'Confirmed'
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
281
    >>> external_bugzilla.convertRemoteStatus('NEEDINFO').title
4318.3.5 by Gavin Panella
More status renaming stuff in response to test failures.
282
    'Incomplete'
3691.62.18 by kiko
Fix for bug 42580: Map redhat's MODIFIED to Fix Committed. Add support to the bug watch sync infrastructure, and a test
283
    >>> external_bugzilla.convertRemoteStatus('NEEDINFO_REPORTER').title
4318.3.5 by Gavin Panella
More status renaming stuff in response to test failures.
284
    'Incomplete'
12435.1.6 by William Grant
Update tests with new statuses.
285
    >>> external_bugzilla.convertRemoteStatus('NEEDSINFO').title
286
    'Incomplete'
3691.62.18 by kiko
Fix for bug 42580: Map redhat's MODIFIED to Fix Committed. Add support to the bug watch sync infrastructure, and a test
287
    >>> external_bugzilla.convertRemoteStatus('MODIFIED').title
288
    'Fix Committed'
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
289
    >>> external_bugzilla.convertRemoteStatus('UPSTREAM').title
3392.1.1 by Bjorn Tillenius
modify the bugzilla status mapping.
290
    'Confirmed'
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
291
    >>> external_bugzilla.convertRemoteStatus('PENDINGUPLOAD').title
3048.4.1 by Bjorn Tillenius
add tests for existing ExternalSystem functionality. fix malonify_status() to use dbschema objects instead of strings.
292
    'Fix Committed'
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
293
    >>> external_bugzilla.convertRemoteStatus('RESOLVED FIXED').title
3048.4.1 by Bjorn Tillenius
add tests for existing ExternalSystem functionality. fix malonify_status() to use dbschema objects instead of strings.
294
    'Fix Released'
11411.7.24 by j.c.sackett
Merged from devel.
295
    >>> external_bugzilla.convertRemoteStatus('RESOLVED UPSTREAM').title
296
    "Won't Fix"
4810.11.6 by Graham Binns
Bugzilla tests now pass.
297
    >>> external_bugzilla.convertRemoteStatus(
298
    ...     'CLOSED PATCH_ALREADY_AVAILABLE').title
4483.2.1 by Graham Binns
Fixes bugs #121348 and #113974. Adds handling of statuses 'RESOLVED PATCH_ALREADY_AVALIABLE', 'RESOLVED CODE_FIX' and 'VERIFIED WONTFIX' to bugzilla remote status conversion
299
    'Fix Released'
300
    >>> external_bugzilla.convertRemoteStatus('RESOLVED CODE_FIX').title
301
    'Fix Released'
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
302
    >>> external_bugzilla.convertRemoteStatus('VERIFIED WONTFIX').title
4483.2.1 by Graham Binns
Fixes bugs #121348 and #113974. Adds handling of statuses 'RESOLVED PATCH_ALREADY_AVALIABLE', 'RESOLVED CODE_FIX' and 'VERIFIED WONTFIX' to bugzilla remote status conversion
303
    "Won't Fix"
3063.2.21 by Bjorn Tillenius
rename malonify_status, make it return a proper BugTaskStatus item.
304
    >>> external_bugzilla.convertRemoteStatus('CLOSED INVALID').title
4318.3.4 by Gavin Panella
Make manual changes to bug status.
305
    'Invalid'
12435.1.6 by William Grant
Update tests with new statuses.
306
    >>> external_bugzilla.convertRemoteStatus('CLOSED DUPLICATE').title
307
    'Invalid'
7822.1.1 by Bjorn Tillenius
Add mapping for Redhat's Bugzilla status CLOSED UPSTREAM.
308
    >>> external_bugzilla.convertRemoteStatus('CLOSED UPSTREAM').title
11411.7.24 by j.c.sackett
Merged from devel.
309
    "Won't Fix"
3048.4.1 by Bjorn Tillenius
add tests for existing ExternalSystem functionality. fix malonify_status() to use dbschema objects instead of strings.
310
5863.4.8 by Graham Binns
Fixed BugZilla tests and added a test to externalbugtracker-checkwatches.txt.
311
If the status can't be converted an UnknownRemoteStatusError will be
312
returned.
3048.4.13 by Bjorn Tillenius
review comments.
313
3063.2.39 by Bjorn Tillenius
use UNKNOWN instead of None to indicate that the value is unknown.
314
    >>> external_bugzilla.convertRemoteStatus('FOO').title
5863.4.8 by Graham Binns
Fixed BugZilla tests and added a test to externalbugtracker-checkwatches.txt.
315
    Traceback (most recent call last):
316
      ...
6253.2.3 by Gavin Panella
Update tests.
317
    UnknownRemoteStatusError: FOO
12435.1.6 by William Grant
Update tests with new statuses.
318
    >>> external_bugzilla.convertRemoteStatus('CLOSED BAR').title
319
    Traceback (most recent call last):
320
      ...
321
    UnknownRemoteStatusError: CLOSED BAR
5863.4.8 by Graham Binns
Fixed BugZilla tests and added a test to externalbugtracker-checkwatches.txt.
322
3048.4.13 by Bjorn Tillenius
review comments.
323
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
324
Importance Conversion
325
---------------------
11304.1.1 by Bryce Harrington
Stub in a getRemoteImportance() call. Doesn't do much but tests pass.
326
11304.1.20 by Bryce Harrington
As we only use the severity to determine importance when converting, it
327
There is also a function for conversion of bugzilla importances to
328
launchpad importances.  The Bugzilla importance is comprised of priority
329
and severity, but we only use severity in mapping the value unless it
330
isn't available in which case we map against priority values.
11304.1.1 by Bryce Harrington
Stub in a getRemoteImportance() call. Doesn't do much but tests pass.
331
11304.1.7 by Bryce Harrington
Implement convertRemoteImportance()
332
    >>> external_bugzilla.convertRemoteImportance('URGENT BLOCKER').title
333
    'Critical'
334
    >>> external_bugzilla.convertRemoteImportance('LOW BLOCKER').title
335
    'Critical'
336
    >>> external_bugzilla.convertRemoteImportance('BLOCKER').title
337
    'Critical'
338
339
    >>> external_bugzilla.convertRemoteImportance('URGENT CRITICAL').title
340
    'Critical'
341
    >>> external_bugzilla.convertRemoteImportance('LOW CRITICAL').title
342
    'Critical'
343
    >>> external_bugzilla.convertRemoteImportance('CRITICAL').title
344
    'Critical'
345
346
    >>> external_bugzilla.convertRemoteImportance('URGENT MAJOR').title
347
    'High'
348
    >>> external_bugzilla.convertRemoteImportance('LOW MAJOR').title
349
    'High'
350
    >>> external_bugzilla.convertRemoteImportance('MAJOR').title
351
    'High'
12435.1.6 by William Grant
Update tests with new statuses.
352
    >>> external_bugzilla.convertRemoteImportance('CRASH').title
353
    'High'
354
    >>> external_bugzilla.convertRemoteImportance('GRAVE').title
355
    'High'
11304.1.7 by Bryce Harrington
Implement convertRemoteImportance()
356
357
    >>> external_bugzilla.convertRemoteImportance('URGENT NORMAL').title
358
    'Medium'
359
    >>> external_bugzilla.convertRemoteImportance('LOW NORMAL').title
360
    'Medium'
361
    >>> external_bugzilla.convertRemoteImportance('NORMAL').title
362
    'Medium'
13663.1.1 by Graham Binns
The Bugzilla ExternalBugTracker code will now recognise 'NOR' as a remote priority.
363
    >>> external_bugzilla.convertRemoteImportance('NOR').title
364
    'Medium'
11304.1.7 by Bryce Harrington
Implement convertRemoteImportance()
365
366
    >>> external_bugzilla.convertRemoteImportance('URGENT MINOR').title
367
    'Low'
368
    >>> external_bugzilla.convertRemoteImportance('LOW MINOR').title
369
    'Low'
370
    >>> external_bugzilla.convertRemoteImportance('MINOR').title
371
    'Low'
372
373
    >>> external_bugzilla.convertRemoteImportance('URGENT TRIVIAL').title
374
    'Low'
375
    >>> external_bugzilla.convertRemoteImportance('LOW TRIVIAL').title
376
    'Low'
377
    >>> external_bugzilla.convertRemoteImportance('TRIVIAL').title
378
    'Low'
379
380
    >>> external_bugzilla.convertRemoteImportance('LOW ENHANCEMENT').title
381
    'Wishlist'
382
    >>> external_bugzilla.convertRemoteImportance('ENHANCEMENT').title
383
    'Wishlist'
12435.1.6 by William Grant
Update tests with new statuses.
384
    >>> external_bugzilla.convertRemoteImportance('WISHLIST').title
385
    'Wishlist'
11304.1.7 by Bryce Harrington
Implement convertRemoteImportance()
386
387
    >>> external_bugzilla.convertRemoteImportance('IMMEDIATE').title
388
    'Critical'
389
    >>> external_bugzilla.convertRemoteImportance('URGENT').title
390
    'Critical'
391
    >>> external_bugzilla.convertRemoteImportance('HIGH').title
392
    'High'
11304.1.20 by Bryce Harrington
As we only use the severity to determine importance when converting, it
393
    >>> external_bugzilla.convertRemoteImportance('MEDIUM').title
11304.1.7 by Bryce Harrington
Implement convertRemoteImportance()
394
    'Medium'
395
    >>> external_bugzilla.convertRemoteImportance('LOW').title
396
    'Low'
11304.1.1 by Bryce Harrington
Stub in a getRemoteImportance() call. Doesn't do much but tests pass.
397
12435.1.6 by William Grant
Update tests with new statuses.
398
    >>> external_bugzilla.convertRemoteImportance('P5').title
399
    'Critical'
400
    >>> external_bugzilla.convertRemoteImportance('P4').title
401
    'High'
402
    >>> external_bugzilla.convertRemoteImportance('P3').title
403
    'Medium'
404
    >>> external_bugzilla.convertRemoteImportance('P2').title
405
    'Low'
406
    >>> external_bugzilla.convertRemoteImportance('P1').title
407
    'Low'
408
11629.1.4 by Bryce Harrington
Handle case of bugzillas which can return '' for priority and severity.
409
Some bugzillas don't provide a value, resulting in blank strings for
410
priority and severity.  We simply leave the importance unknown in this
411
case.
412
413
    >>> external_bugzilla.convertRemoteImportance('').title
414
    'Unknown'
415
416
However, we still treat as an error if the priority or severity are set
417
to some other unexpected string.
418
419
    >>> external_bugzilla.convertRemoteImportance('foo bar')
420
    Traceback (most recent call last):
421
    ...
422
    UnknownRemoteImportanceError: foo bar
423
    >>> external_bugzilla.convertRemoteImportance('%&*@*#&$%!')
424
    Traceback (most recent call last):
425
    ...
426
    UnknownRemoteImportanceError: %&*@*#&$%!
427
428
11304.1.1 by Bryce Harrington
Stub in a getRemoteImportance() call. Doesn't do much but tests pass.
429
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
430
Updating Bug Watches
431
--------------------
3048.4.2 by Bjorn Tillenius
initial support for updating several bug watches in one request.
432
3357.3.4 by Bjorn Tillenius
replace the ExternalSystem class with a simple function.
433
The main use of an ExternalBugtracker is to update bug watches. This is
434
done through updateBugWatches(), which expects a list of bug watches to
3048.4.2 by Bjorn Tillenius
initial support for updating several bug watches in one request.
435
update:
436
10694.2.25 by Graham Binns
Renamed BugWatchUpdater -> CheckwatchesMaster. This is the wrong name for it, but I did it to avoid bikeshedding.
437
    >>> from lp.bugs.scripts.checkwatches import CheckwatchesMaster
438
    >>> bug_watch_updater = CheckwatchesMaster(txn)
3048.4.2 by Bjorn Tillenius
initial support for updating several bug watches in one request.
439
    >>> for bug_watch in gnome_bugzilla.watches:
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
440
    ...     print "%s: %s %s" % (bug_watch.remotebug,
441
    ...           bug_watch.remotestatus,
11304.1.8 by Bryce Harrington
Extend bug watch test to include checking remote_importance
442
    ...           bug_watch.remote_importance)
443
    304070: None None
444
    3224:  None
5626.5.2 by Bjorn Tillenius
move ExternalBugtracker.updateBugWatches() to BugWatchUpdater.updateBugWatches(). Debbugs.importBugComments() is now broken, though.
445
    >>> bug_watch_updater.updateBugWatches(
446
    ...     external_bugzilla, gnome_bugzilla.watches)
7910.2.7 by Graham Binns
Updated checkwatches tests to deal with logging output changes.
447
    INFO:...:Updating 2 watches for 2 bugs on http://bugzilla.gnome.org/bugs
12392.6.5 by William Grant
Log InvalidBugId, BugNotFound and PrivateRemoteBug at INFO, and update tests to cope.
448
    INFO:...Didn't find bug u'304070' on
449
    http://bugzilla.gnome.org/bugs (local bugs: 15).
5252.1.13 by Graham Binns
Added expected log output to bugzilla doctest.
450
3048.4.2 by Bjorn Tillenius
initial support for updating several bug watches in one request.
451
    >>> for bug_watch in gnome_bugzilla.watches:
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
452
    ...     print "%s: %s %s" % (bug_watch.remotebug,
453
    ...           bug_watch.remotestatus,
11304.1.8 by Bryce Harrington
Extend bug watch test to include checking remote_importance
454
    ...           bug_watch.remote_importance)
455
    304070: None None
11304.1.14 by Bryce Harrington
Hook up priority and severity for various bugzilla derivitives
456
    3224: RESOLVED FIXED MINOR URGENT
3048.4.2 by Bjorn Tillenius
initial support for updating several bug watches in one request.
457
5099.3.1 by Gavin Panella
Stop Bugzilla.getRemoteStatusBatch from clobbering the status cache every time it runs, and ensure that the batch method is used every time for Bugzilla.
458
Let's add a handful of watches:
3048.4.3 by Bjorn Tillenius
use more than one bug watch when testing to update them.
459
11716.1.6 by Curtis Hovey
Converted glob imports in doctests to import for the true module.
460
    >>> from lp.bugs.interfaces.bug import IBugSet
461
    >>> from lp.bugs.interfaces.bugwatch import IBugWatchSet
462
    >>> from lp.registry.interfaces.person import IPersonSet
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
463
    >>> sample_person = getUtility(IPersonSet).getByEmail(
464
    ...     'test@canonical.com')
3048.4.3 by Bjorn Tillenius
use more than one bug watch when testing to update them.
465
    >>> bug_one = getUtility(IBugSet).get(1)
466
    >>> bug_watch_set = getUtility(IBugWatchSet)
5099.3.1 by Gavin Panella
Stop Bugzilla.getRemoteStatusBatch from clobbering the status cache every time it runs, and ensure that the batch method is used every time for Bugzilla.
467
468
    >>> expected_remote_statuses = dict(
469
    ...     [(int(bug_watch.remotebug), bug_watch.remotestatus)
470
    ...      for bug_watch in gnome_bugzilla.watches])
11304.1.9 by Bryce Harrington
Add values for priority and severity to sample data in various places
471
    >>> expected_remote_importances = dict(
472
    ...     [(int(bug_watch.remotebug), bug_watch.remote_importance)
473
    ...      for bug_watch in gnome_bugzilla.watches])
5099.3.1 by Gavin Panella
Stop Bugzilla.getRemoteStatusBatch from clobbering the status cache every time it runs, and ensure that the batch method is used every time for Bugzilla.
474
    >>> for remote_bug_id in range(50,55):
475
    ...     bug_watch = bug_watch_set.createBugWatch(
476
    ...         bug=bug_one, owner=sample_person, bugtracker=gnome_bugzilla,
477
    ...         remotebug=str(remote_bug_id))
478
    ...     external_bugzilla.bugzilla_bugs[remote_bug_id] = (
11304.1.9 by Bryce Harrington
Add values for priority and severity to sample data in various places
479
    ...         'RESOLVED', 'FIXED', 'HIGH', 'ENHANCEMENT')
5099.3.1 by Gavin Panella
Stop Bugzilla.getRemoteStatusBatch from clobbering the status cache every time it runs, and ensure that the batch method is used every time for Bugzilla.
480
    ...     expected_remote_statuses[remote_bug_id] = 'RESOLVED FIXED'
11304.1.9 by Bryce Harrington
Add values for priority and severity to sample data in various places
481
    ...     expected_remote_importances[remote_bug_id] = 'HIGH ENHANCEMENT'
5099.3.1 by Gavin Panella
Stop Bugzilla.getRemoteStatusBatch from clobbering the status cache every time it runs, and ensure that the batch method is used every time for Bugzilla.
482
483
Set the batch threshold higher than the number of bug watches.
484
485
    >>> external_bugzilla.batch_query_threshold = 10
486
487
Then updateBugWatches() will make one request per bug watch:
488
489
    >>> external_bugzilla.trace_calls = True
5626.5.2 by Bjorn Tillenius
move ExternalBugtracker.updateBugWatches() to BugWatchUpdater.updateBugWatches(). Debbugs.importBugComments() is now broken, though.
490
    >>> bug_watch_updater.updateBugWatches(
491
    ...     external_bugzilla, gnome_bugzilla.watches)
7910.2.7 by Graham Binns
Updated checkwatches tests to deal with logging output changes.
492
    INFO:...:Updating 7 watches for 7 bugs on http://bugzilla.gnome.org/bugs
5292.4.26 by Graham Binns
Fixed the errors in externalbugtracker-bugzilla caused by sampledata changes.
493
    CALLED _postPage()
494
    CALLED _postPage()
495
    CALLED _postPage()
496
    CALLED _postPage()
497
    CALLED _postPage()
498
    CALLED _postPage()
499
    CALLED _postPage()
12392.6.5 by William Grant
Log InvalidBugId, BugNotFound and PrivateRemoteBug at INFO, and update tests to cope.
500
    INFO:...:Didn't find bug u'304070' on
501
    http://bugzilla.gnome.org/bugs (local bugs: 15).
5099.3.1 by Gavin Panella
Stop Bugzilla.getRemoteStatusBatch from clobbering the status cache every time it runs, and ensure that the batch method is used every time for Bugzilla.
502
503
    >>> remote_statuses = dict(
504
    ...     [(int(bug_watch.remotebug), bug_watch.remotestatus)
505
    ...      for bug_watch in gnome_bugzilla.watches])
506
    >>> remote_statuses == expected_remote_statuses
507
    True
508
11304.1.9 by Bryce Harrington
Add values for priority and severity to sample data in various places
509
    >>> remote_importances = dict(
510
    ...     [(int(bug_watch.remotebug), bug_watch.remote_importance)
511
    ...      for bug_watch in gnome_bugzilla.watches])
512
    >>> remote_importances == expected_remote_importances
513
    True
514
5099.3.1 by Gavin Panella
Stop Bugzilla.getRemoteStatusBatch from clobbering the status cache every time it runs, and ensure that the batch method is used every time for Bugzilla.
515
    >>> external_bugzilla.trace_calls = False
516
517
Let's add a few more watches:
518
3048.4.3 by Bjorn Tillenius
use more than one bug watch when testing to update them.
519
    >>> expected_remote_statuses = dict(
520
    ...     [(int(bug_watch.remotebug), bug_watch.remotestatus)
521
    ...      for bug_watch in gnome_bugzilla.watches])
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
522
    >>> expected_remote_importances = dict(
523
    ...     [(int(bug_watch.remotebug), bug_watch.remote_importance)
524
    ...      for bug_watch in gnome_bugzilla.watches])
3048.4.3 by Bjorn Tillenius
use more than one bug watch when testing to update them.
525
    >>> for remote_bug_id in range(100,300):
526
    ...     bug_watch = bug_watch_set.createBugWatch(
527
    ...         bug=bug_one, owner=sample_person, bugtracker=gnome_bugzilla,
528
    ...         remotebug=str(remote_bug_id))
3357.3.4 by Bjorn Tillenius
replace the ExternalSystem class with a simple function.
529
    ...     external_bugzilla.bugzilla_bugs[remote_bug_id] = (
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
530
    ...         'ASSIGNED', '', 'MEDIUM', 'URGENT')
3048.4.3 by Bjorn Tillenius
use more than one bug watch when testing to update them.
531
    ...     expected_remote_statuses[remote_bug_id] = 'ASSIGNED'
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
532
    ...     expected_remote_importances[remote_bug_id] = 'MEDIUM URGENT'
5855.4.16 by Bjorn Tillenius
fix test failures.
533
3048.4.4 by Bjorn Tillenius
make sure only one request is used for updating the watches.
534
5854.1.1 by Graham Binns
Altered settings to enable batching.
535
Set the batch threshold very low and remove the batch size limit:
5099.3.1 by Gavin Panella
Stop Bugzilla.getRemoteStatusBatch from clobbering the status cache every time it runs, and ensure that the batch method is used every time for Bugzilla.
536
537
    >>> external_bugzilla.batch_query_threshold = 0
5854.1.1 by Graham Binns
Altered settings to enable batching.
538
    >>> external_bugzilla.batch_size = None
5099.3.1 by Gavin Panella
Stop Bugzilla.getRemoteStatusBatch from clobbering the status cache every time it runs, and ensure that the batch method is used every time for Bugzilla.
539
3048.4.4 by Bjorn Tillenius
make sure only one request is used for updating the watches.
540
Instead of issuing one request per bug watch, like was done before,
541
updateBugWatches() issues only one request to update all watches:
542
3357.3.4 by Bjorn Tillenius
replace the ExternalSystem class with a simple function.
543
    >>> external_bugzilla.trace_calls = True
5626.5.2 by Bjorn Tillenius
move ExternalBugtracker.updateBugWatches() to BugWatchUpdater.updateBugWatches(). Debbugs.importBugComments() is now broken, though.
544
    >>> bug_watch_updater.updateBugWatches(
545
    ...     external_bugzilla, gnome_bugzilla.watches)
7910.2.7 by Graham Binns
Updated checkwatches tests to deal with logging output changes.
546
    INFO:...:Updating 207 watches for 207 bugs...
3048.4.4 by Bjorn Tillenius
make sure only one request is used for updating the watches.
547
    CALLED _postPage()
12392.6.5 by William Grant
Log InvalidBugId, BugNotFound and PrivateRemoteBug at INFO, and update tests to cope.
548
    INFO:...:Didn't find bug u'304070' on
549
    http://bugzilla.gnome.org/bugs (local bugs: 15).
3048.4.4 by Bjorn Tillenius
make sure only one request is used for updating the watches.
550
3048.4.3 by Bjorn Tillenius
use more than one bug watch when testing to update them.
551
    >>> remote_statuses = dict(
552
    ...     [(int(bug_watch.remotebug), bug_watch.remotestatus)
553
    ...      for bug_watch in gnome_bugzilla.watches])
554
    >>> remote_statuses == expected_remote_statuses
555
    True
3048.4.4 by Bjorn Tillenius
make sure only one request is used for updating the watches.
556
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
557
    >>> remote_importances = dict(
558
    ...     [(int(bug_watch.remotebug), bug_watch.remote_importance)
559
    ...      for bug_watch in gnome_bugzilla.watches])
560
    >>> remote_importances == expected_remote_importances
561
    True
562
3357.3.4 by Bjorn Tillenius
replace the ExternalSystem class with a simple function.
563
    >>> external_bugzilla.trace_calls = False
3048.4.10 by Bjorn Tillenius
doc tweaks
564
3048.4.8 by Bjorn Tillenius
make sure updateBugWatches() sets lastchecked.
565
updateBugWatches() updates the lastchecked attribute on the watches, so
566
now no bug watches are in need of updating:
567
14606.3.1 by William Grant
Merge canonical.database into lp.services.database.
568
    >>> from lp.services.database.sqlbase import flush_database_updates
3048.4.8 by Bjorn Tillenius
make sure updateBugWatches() sets lastchecked.
569
    >>> flush_database_updates()
7675.604.8 by Gavin Panella
Use watches_needing_update instead of getBugWatchesNeedingUpdate.
570
    >>> gnome_bugzilla.watches_needing_update.count()
3048.4.8 by Bjorn Tillenius
make sure updateBugWatches() sets lastchecked.
571
    0
3063.2.23 by Bjorn Tillenius
initial support for syncing bugwatches and bugtasks.
572
3357.3.15 by Bjorn Tillenius
fix incorrect comparision.
573
If the status isn't different, the lastchanged attribute doesn't get
574
updated:
575
576
    >>> import pytz
577
    >>> from datetime import datetime, timedelta
578
    >>> bug_watch = gnome_bugzilla.watches[0]
579
    >>> now = datetime.now(pytz.timezone('UTC'))
580
    >>> bug_watch.lastchanged = now - timedelta(weeks=2)
581
    >>> old_last_changed = bug_watch.lastchanged
5626.5.2 by Bjorn Tillenius
move ExternalBugtracker.updateBugWatches() to BugWatchUpdater.updateBugWatches(). Debbugs.importBugComments() is now broken, though.
582
    >>> bug_watch_updater.updateBugWatches(external_bugzilla, [bug_watch])
7910.2.7 by Graham Binns
Updated checkwatches tests to deal with logging output changes.
583
    INFO:...:Updating 1 watches for 1 bugs on http://bugzilla.gnome.org/bugs
3357.3.15 by Bjorn Tillenius
fix incorrect comparision.
584
    >>> bug_watch.lastchanged == old_last_changed
585
    True
586
3063.2.23 by Bjorn Tillenius
initial support for syncing bugwatches and bugtasks.
587
Now let's take a look at what happens when a bug watch is linked to
588
from a bug task.
589
590
    >>> bug_nine = getUtility(IBugSet).get(9)
591
    >>> thunderbird_task = bug_nine.bugtasks[0]
3063.2.39 by Bjorn Tillenius
use UNKNOWN instead of None to indicate that the value is unknown.
592
    >>> print thunderbird_task.status.title
593
    Unknown
3063.2.23 by Bjorn Tillenius
initial support for syncing bugwatches and bugtasks.
594
    >>> thunderbird_task.bugwatch.remotestatus is None
595
    True
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
596
    >>> thunderbird_task.bugwatch.remote_importance is None
597
    True
3063.2.23 by Bjorn Tillenius
initial support for syncing bugwatches and bugtasks.
598
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
599
Importance gets updated for Bugzilla bugs.  Let's set it to some bogus
600
value, and see that it gets set to a proper value.
3063.2.39 by Bjorn Tillenius
use UNKNOWN instead of None to indicate that the value is unknown.
601
11692.6.2 by Curtis Hovey
Use deglober to fixing simple glob imports in doctests.
602
    >>> from lp.bugs.interfaces.bugtask import BugTaskImportance
6602.5.3 by Tom Berger
convert all assingments to bugtask.importance to calls to transitionToImportance
603
    >>> thunderbird_task.transitionToImportance(
604
    ...     BugTaskImportance.HIGH,
605
    ...     thunderbird_task.pillar.owner)
3063.2.39 by Bjorn Tillenius
use UNKNOWN instead of None to indicate that the value is unknown.
606
3357.3.4 by Bjorn Tillenius
replace the ExternalSystem class with a simple function.
607
We need to create a new ExternalBugtracker for the Mozilla tracker:
3063.2.23 by Bjorn Tillenius
initial support for syncing bugwatches and bugtasks.
608
609
    >>> mozilla_bugzilla = getUtility(IBugTrackerSet).getByName(
610
    ...     'mozilla.org')
5816.1.1 by Bjorn Tillenius
Make ExternalBugTracker accept a URL instead of a transaction and bug tracker.
611
    >>> external_bugzilla = TestBugzilla(mozilla_bugzilla.baseurl, '2.20')
11304.1.9 by Bryce Harrington
Add values for priority and severity to sample data in various places
612
    >>> external_bugzilla.bugzilla_bugs = {1234: (
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
613
    ...     'ASSIGNED', '', 'MEDIUM', 'ENHANCEMENT')}
3063.2.23 by Bjorn Tillenius
initial support for syncing bugwatches and bugtasks.
614
615
Let's update the bug watch, and see that the linked bug watch got
616
synced:
617
5626.5.2 by Bjorn Tillenius
move ExternalBugtracker.updateBugWatches() to BugWatchUpdater.updateBugWatches(). Debbugs.importBugComments() is now broken, though.
618
    >>> bug_watch_updater.updateBugWatches(
619
    ...     external_bugzilla, [thunderbird_task.bugwatch])
7910.2.7 by Graham Binns
Updated checkwatches tests to deal with logging output changes.
620
    INFO:...:Updating 1 watches for 1 bugs on https://bugzilla.mozilla.org
5252.1.13 by Graham Binns
Added expected log output to bugzilla doctest.
621
5855.4.1 by Bjorn Tillenius
make initializeRemoteBugDB to be called outside a db transaction.
622
    >>> bug_nine = getUtility(IBugSet).get(9)
623
    >>> thunderbird_task = bug_nine.bugtasks[0]
3063.2.23 by Bjorn Tillenius
initial support for syncing bugwatches and bugtasks.
624
    >>> print thunderbird_task.status.title
3392.1.1 by Bjorn Tillenius
modify the bugzilla status mapping.
625
    In Progress
3270.3.10 by Matthew Paul Thomas
Changes BugTaskSeverity to BugTaskImportance.
626
    >>> print thunderbird_task.importance.title
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
627
    Wishlist
3063.2.23 by Bjorn Tillenius
initial support for syncing bugwatches and bugtasks.
628
    >>> print thunderbird_task.bugwatch.remotestatus
629
    ASSIGNED
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
630
    >>> print thunderbird_task.bugwatch.remote_importance
631
    MEDIUM ENHANCEMENT
3063.2.38 by Bjorn Tillenius
merge rf, resolve conflicts.
632
3392.1.5 by Bjorn Tillenius
update the malone status even if the remote status hasn't changed for a bug watch.
633
If we change the bugtask status, it will be updated again even though
634
the remote status hasn't changed. This can happen if we change the
635
status mapping.
636
11692.6.2 by Curtis Hovey
Use deglober to fixing simple glob imports in doctests.
637
    >>> from lp.bugs.interfaces.bugtask import BugTaskStatus
4318.3.11 by Gavin Panella
Changing transitionToStatus to accept user argument, part 2.
638
    >>> thunderbird_task.transitionToStatus(
4945.2.2 by Bjorn Tillenius
make externalbugtracker-bugzilla.txt pass when run as the checkwates db user.
639
    ...     BugTaskStatus.CONFIRMED,
640
    ...     getUtility(IPersonSet).getByName('no-priv'))
5626.5.2 by Bjorn Tillenius
move ExternalBugtracker.updateBugWatches() to BugWatchUpdater.updateBugWatches(). Debbugs.importBugComments() is now broken, though.
641
    >>> bug_watch_updater.updateBugWatches(
642
    ...     external_bugzilla, [thunderbird_task.bugwatch])
7910.2.7 by Graham Binns
Updated checkwatches tests to deal with logging output changes.
643
    INFO:...:Updating 1 watches for 1 bugs on https://bugzilla.mozilla.org
5252.1.13 by Graham Binns
Added expected log output to bugzilla doctest.
644
5855.4.1 by Bjorn Tillenius
make initializeRemoteBugDB to be called outside a db transaction.
645
    >>> bug_nine = getUtility(IBugSet).get(9)
646
    >>> thunderbird_task = bug_nine.bugtasks[0]
3392.1.5 by Bjorn Tillenius
update the malone status even if the remote status hasn't changed for a bug watch.
647
    >>> print thunderbird_task.status.title
648
    In Progress
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
649
    >>> print thunderbird_task.importance.title
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
650
    Wishlist
3392.1.5 by Bjorn Tillenius
update the malone status even if the remote status hasn't changed for a bug watch.
651
    >>> print thunderbird_task.bugwatch.remotestatus
652
    ASSIGNED
11304.1.25 by Bryce Harrington
Extend more tests to check importance.
653
    >>> print thunderbird_task.bugwatch.remote_importance
654
    MEDIUM ENHANCEMENT
3392.1.5 by Bjorn Tillenius
update the malone status even if the remote status hasn't changed for a bug watch.
655
3691.60.1 by Bjorn Tillenius
apply kiko's patch for bug-54898. add tests. remove XXX
656
If there are two bug watches, linked to different bugs, pointing to the
657
same remote bug, both will of course be updated.
658
11304.1.9 by Bryce Harrington
Add values for priority and severity to sample data in various places
659
    >>> external_bugzilla.bugzilla_bugs[42] = (
660
    ...     'RESOLVED', 'FIXED', 'LOW', 'BLOCKER')
3691.60.1 by Bjorn Tillenius
apply kiko's patch for bug-54898. add tests. remove XXX
661
    >>> bug_watch1 = bug_watch_set.createBugWatch(
662
    ...     bug=bug_one, owner=sample_person, bugtracker=mozilla_bugzilla,
663
    ...     remotebug='42')
5855.4.1 by Bjorn Tillenius
make initializeRemoteBugDB to be called outside a db transaction.
664
    >>> bug_watch1_id = bug_watch1.id
3691.60.1 by Bjorn Tillenius
apply kiko's patch for bug-54898. add tests. remove XXX
665
    >>> bug_two = getUtility(IBugSet).get(2)
666
    >>> bug_watch2 = bug_watch_set.createBugWatch(
667
    ...     bug=bug_two, owner=sample_person, bugtracker=mozilla_bugzilla,
668
    ...     remotebug='42')
5855.4.1 by Bjorn Tillenius
make initializeRemoteBugDB to be called outside a db transaction.
669
    >>> bug_watch2_id = bug_watch2.id
5626.5.2 by Bjorn Tillenius
move ExternalBugtracker.updateBugWatches() to BugWatchUpdater.updateBugWatches(). Debbugs.importBugComments() is now broken, though.
670
    >>> bug_watch_updater.updateBugWatches(
671
    ...     external_bugzilla, [bug_watch1, bug_watch2])
7910.2.7 by Graham Binns
Updated checkwatches tests to deal with logging output changes.
672
    INFO:...:Updating 2 watches for 1 bugs on https://bugzilla.mozilla.org
5252.1.13 by Graham Binns
Added expected log output to bugzilla doctest.
673
5855.4.1 by Bjorn Tillenius
make initializeRemoteBugDB to be called outside a db transaction.
674
    >>> bug_watch1 = getUtility(IBugWatchSet).get(bug_watch1_id)
3691.60.1 by Bjorn Tillenius
apply kiko's patch for bug-54898. add tests. remove XXX
675
    >>> print bug_watch1.remotestatus
676
    RESOLVED FIXED
11304.1.9 by Bryce Harrington
Add values for priority and severity to sample data in various places
677
    >>> print bug_watch1.remote_importance
678
    LOW BLOCKER
5855.4.1 by Bjorn Tillenius
make initializeRemoteBugDB to be called outside a db transaction.
679
    >>> bug_watch2 = getUtility(IBugWatchSet).get(bug_watch2_id)
3691.60.1 by Bjorn Tillenius
apply kiko's patch for bug-54898. add tests. remove XXX
680
    >>> print bug_watch2.remotestatus
681
    RESOLVED FIXED
11304.1.9 by Bryce Harrington
Add values for priority and severity to sample data in various places
682
    >>> print bug_watch2.remote_importance
683
    LOW BLOCKER
3691.60.1 by Bjorn Tillenius
apply kiko's patch for bug-54898. add tests. remove XXX
684
3225.2.1 by Bjorn Tillenius
add test for what happens if checkwatches encounter invalid xml, make it log an error instead of breaking.
685
If updateBugWatches() can't parse the XML file returned from the remote
686
bug tracker, an error is logged.
687
3357.3.4 by Bjorn Tillenius
replace the ExternalSystem class with a simple function.
688
    >>> external_bugzilla._postPage = (
12221.1.3 by Jeroen Vermeulen
Update various _postPage test doubles to the new signature.
689
    ...     lambda self, data, repost_on_redirect: '<invalid xml>')
5626.5.2 by Bjorn Tillenius
move ExternalBugtracker.updateBugWatches() to BugWatchUpdater.updateBugWatches(). Debbugs.importBugComments() is now broken, though.
690
    >>> bug_watch_updater.updateBugWatches(
691
    ...     external_bugzilla, [bug_watch1, bug_watch2])
3691.196.1 by kiko
First cut at Issuezilla and old Bugzilla version support. Makes the version and attribute parsing slightly more robust, fixing issues with the Bugzilla instances we currently have registered. Includes basic tests, but some items are XXXed for reimplementation.
692
    Traceback (most recent call last):
693
    ...
12221.1.7 by Jeroen Vermeulen
s/Unparseable/Unparsable/g, plus lint.
694
    UnparsableBugData:
3691.196.1 by kiko
First cut at Issuezilla and old Bugzilla version support. Makes the version and attribute parsing slightly more robust, fixing issues with the Bugzilla instances we currently have registered. Includes basic tests, but some items are XXXed for reimplementation.
695
    Failed to parse XML description for https://bugzilla.mozilla.org...
3336.1.1 by Bjorn Tillenius
externalsystem -> externalbugtracker in checkwatches.py
696
4948.1.35 by Graham Binns
Renamed BugWatch.lasterror to last_error_type in accordance with stubs upcoming DB changes.
697
The error is also recorded in each bug watch's last_error_type field so that
4948.1.7 by Graham Binns
Made BugWatchErrorType mapping into a helper function since doing it as a dict lookup failed miserably.
698
it can be displayed to the user.
699
5855.4.1 by Bjorn Tillenius
make initializeRemoteBugDB to be called outside a db transaction.
700
    >>> bug_watch1 = getUtility(IBugWatchSet).get(bug_watch1_id)
4948.1.35 by Graham Binns
Renamed BugWatch.lasterror to last_error_type in accordance with stubs upcoming DB changes.
701
    >>> bug_watch1.last_error_type.title
4948.1.7 by Graham Binns
Made BugWatchErrorType mapping into a helper function since doing it as a dict lookup failed miserably.
702
    'Unparsable Bug'
5855.4.1 by Bjorn Tillenius
make initializeRemoteBugDB to be called outside a db transaction.
703
    >>> bug_watch2 = getUtility(IBugWatchSet).get(bug_watch2_id)
4948.1.35 by Graham Binns
Renamed BugWatch.lasterror to last_error_type in accordance with stubs upcoming DB changes.
704
    >>> bug_watch2.last_error_type.title
4948.1.7 by Graham Binns
Made BugWatchErrorType mapping into a helper function since doing it as a dict lookup failed miserably.
705
    'Unparsable Bug'
7781.1.3 by Bjorn Tillenius
Implement Bugzilla.getRemoteProduct().
706
707
11304.1.22 by Bryce Harrington
Cleanup a heapload of lint errors
708
Getting Remote Product
709
----------------------
7781.1.3 by Bjorn Tillenius
Implement Bugzilla.getRemoteProduct().
710
711
getRemoteProduct() returns the product a remote bug is associated with
712
in Bugzilla. getRemoteProduct() has to be called after
713
initializeRemoteBugDB() has been called, in order for the bug
714
information to be fetched from the external Bugzilla instance.
715
716
    >>> external_bugzilla = TestBugzilla()
11304.1.11 by Bryce Harrington
Yet more sample data needing to be specified for tests
717
    >>> external_bugzilla.bugzilla_bugs = {84: (
718
    ...     'RESOLVED', 'FIXED', 'MEDIUM', 'NORMAL')}
7781.1.3 by Bjorn Tillenius
Implement Bugzilla.getRemoteProduct().
719
    >>> external_bugzilla.initializeRemoteBugDB(['84'])
720
    >>> external_bugzilla.remote_bug_product['84']
721
    u'product-84'
722
    >>> external_bugzilla.getRemoteProduct('84')
723
    u'product-84'
724
7781.1.4 by Bjorn Tillenius
Handle the case where we don't get the product in the XML listing.
725
Sometimes we might not get the product in the bug listing. In these
726
cases getRemoteProduct() returns None.
7781.1.3 by Bjorn Tillenius
Implement Bugzilla.getRemoteProduct().
727
7781.1.4 by Bjorn Tillenius
Handle the case where we don't get the product in the XML listing.
728
    >>> external_bugzilla = TestBugzilla()
11304.1.11 by Bryce Harrington
Yet more sample data needing to be specified for tests
729
    >>> external_bugzilla.bugzilla_bugs = {84: (
730
    ...     'RESOLVED', 'FIXED', 'MEDIUM', 'NORMAL')}
7781.1.6 by Bjorn Tillenius
Make sure getRemoteProduct() raises BugNotFound.
731
    >>> # Make the buglist XML not include the product tag.
7781.1.4 by Bjorn Tillenius
Handle the case where we don't get the product in the XML listing.
732
    >>> external_bugzilla.bug_item_file = 'gnome_bug_li_item_noproduct.xml'
733
    >>> external_bugzilla.initializeRemoteBugDB(['84'])
734
    >>> print external_bugzilla.getRemoteProduct('84')
735
    None
7781.1.6 by Bjorn Tillenius
Make sure getRemoteProduct() raises BugNotFound.
736
737
Requesting the product for a bug that doesn't exist raises BugNotFound.
738
739
    >>> external_bugzilla = TestBugzilla()
11304.1.11 by Bryce Harrington
Yet more sample data needing to be specified for tests
740
    >>> external_bugzilla.bugzilla_bugs = {84: (
741
    ...     'RESOLVED', 'FIXED', 'MEDIUM', 'NORMAL')}
7781.1.6 by Bjorn Tillenius
Make sure getRemoteProduct() raises BugNotFound.
742
    >>> external_bugzilla.initializeRemoteBugDB(['84'])
743
    >>> external_bugzilla.getRemoteProduct('42')
744
    Traceback (most recent call last):
745
    ...
746
    BugNotFound: 42