Merge lp:~adeuring/launchpad/bug-594247-unittests-for-searchtasks-3 into lp:launchpad
- bug-594247-unittests-for-searchtasks-3
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Gavin Panella |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11843 |
Proposed branch: | lp:~adeuring/launchpad/bug-594247-unittests-for-searchtasks-3 |
Merge into: | lp:launchpad |
Diff against target: |
310 lines (+230/-8) 2 files modified
lib/lp/bugs/tests/test_bugtask_search.py (+218/-3) lib/lp/testing/factory.py (+12/-5) |
To merge this branch: | bzr merge lp:~adeuring/launchpad/bug-594247-unittests-for-searchtasks-3 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email: mp+39523@code.launchpad.net |
Commit message
unit tests for BugTaskSet.
Description of the change
This branch adds more unit tests for BugTaskSet.
I modified the testing factory method makeBug(): It is now possible to pass a distribution as the bug target. Without this change, tests of bug tasks for bugs without an upstream bugtask would have been somehwat pointless: Without this change, any bug created via makeBug() or makeBugTask() has at least one bugtask targeted to a product. In other words, a search for bugtasks without a related upstream task would never return anything.
test: ./bin/test -vvt lp.bugs.
no lint
Abel Deuring (adeuring) wrote : | # |
Hi Gavin,
thanks for the review!
On 28.10.2010 15:27, Gavin Panella wrote:
> Review: Approve
> Cool. Only one very trivial comment.
>
>
> [1]
>
> + assert(
>
> This works, but it's misleading because assert is a statement not a
> function. Actually... did you mean to remove it before landing? If
> not, perhaps it should be changed to an assertEqual() call.
yeah, sure this is just a relic from debugging. I was a bit puzzled to
see it in the diff -- I could not find it in my local version. Turns out
that I forgot to "bzr commit" my latest changes...
Gavin Panella (allenap) wrote : | # |
Hi Abel, I decided to start from scratch!
[1]
+from lp.registry.
+from lp.registry.
These are out of order. Consider using utilities/
it's more convenient brother format-
[2]
+ search_result = self.runSearch(
+ expected = self.resultValu
+ self.assertEqua
It's not worth changing it here, but a custom assertion method might
have saved a few keystrokes:
def assertSearchFin
expected = self.resultValu
[3]
+ raise AssertionError(
+ 'No bug task found for a product that is not the target of '
+ 'the main test bugtask.')
Either raise self.failureExc
Preview Diff
1 | === modified file 'lib/lp/bugs/tests/test_bugtask_search.py' | |||
2 | --- lib/lp/bugs/tests/test_bugtask_search.py 2010-10-22 13:25:22 +0000 | |||
3 | +++ lib/lp/bugs/tests/test_bugtask_search.py 2010-10-29 13:04:06 +0000 | |||
4 | @@ -3,13 +3,22 @@ | |||
5 | 3 | 3 | ||
6 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
7 | 5 | 5 | ||
8 | 6 | from datetime import ( | ||
9 | 7 | datetime, | ||
10 | 8 | timedelta, | ||
11 | 9 | ) | ||
12 | 6 | from new import classobj | 10 | from new import classobj |
13 | 11 | import pytz | ||
14 | 7 | import sys | 12 | import sys |
15 | 8 | import unittest | 13 | import unittest |
16 | 9 | 14 | ||
17 | 10 | from zope.component import getUtility | 15 | from zope.component import getUtility |
18 | 11 | 16 | ||
20 | 12 | from canonical.launchpad.searchbuilder import any | 17 | from canonical.launchpad.searchbuilder import ( |
21 | 18 | all, | ||
22 | 19 | any, | ||
23 | 20 | greater_than, | ||
24 | 21 | ) | ||
25 | 13 | from canonical.testing.layers import ( | 22 | from canonical.testing.layers import ( |
26 | 14 | LaunchpadFunctionalLayer, | 23 | LaunchpadFunctionalLayer, |
27 | 15 | ) | 24 | ) |
28 | @@ -21,7 +30,14 @@ | |||
29 | 21 | BugTaskStatus, | 30 | BugTaskStatus, |
30 | 22 | IBugTaskSet, | 31 | IBugTaskSet, |
31 | 23 | ) | 32 | ) |
32 | 33 | from lp.registry.interfaces.distribution import IDistribution | ||
33 | 34 | from lp.registry.interfaces.distributionsourcepackage import ( | ||
34 | 35 | IDistributionSourcePackage, | ||
35 | 36 | ) | ||
36 | 37 | from lp.registry.interfaces.distroseries import IDistroSeries | ||
37 | 24 | from lp.registry.interfaces.person import IPersonSet | 38 | from lp.registry.interfaces.person import IPersonSet |
38 | 39 | from lp.registry.interfaces.product import IProduct | ||
39 | 40 | from lp.registry.interfaces.sourcepackage import ISourcePackage | ||
40 | 25 | from lp.testing import ( | 41 | from lp.testing import ( |
41 | 26 | person_logged_in, | 42 | person_logged_in, |
42 | 27 | TestCaseWithFactory, | 43 | TestCaseWithFactory, |
43 | @@ -173,6 +189,204 @@ | |||
44 | 173 | expected = self.resultValuesForBugtasks(self.bugtasks[:2]) | 189 | expected = self.resultValuesForBugtasks(self.bugtasks[:2]) |
45 | 174 | self.assertEqual(expected, search_result) | 190 | self.assertEqual(expected, search_result) |
46 | 175 | 191 | ||
47 | 192 | def setUpFullTextSearchTests(self): | ||
48 | 193 | # Set text fields indexed by Bug.fti, BugTask.fti or | ||
49 | 194 | # MessageChunk.fti to values we can search for. | ||
50 | 195 | for bugtask, number in zip(self.bugtasks, ('one', 'two', 'three')): | ||
51 | 196 | commenter = self.bugtasks[0].bug.owner | ||
52 | 197 | with person_logged_in(commenter): | ||
53 | 198 | bugtask.statusexplanation = 'status explanation %s' % number | ||
54 | 199 | bugtask.bug.title = 'bug title %s' % number | ||
55 | 200 | bugtask.bug.newMessage( | ||
56 | 201 | owner=commenter, content='comment %s' % number) | ||
57 | 202 | |||
58 | 203 | def test_fulltext_search(self): | ||
59 | 204 | # Full text searches find text indexed by Bug.fti... | ||
60 | 205 | self.setUpFullTextSearchTests() | ||
61 | 206 | params = self.getBugTaskSearchParams( | ||
62 | 207 | user=None, searchtext='one title') | ||
63 | 208 | search_result = self.runSearch(params) | ||
64 | 209 | expected = self.resultValuesForBugtasks(self.bugtasks[:1]) | ||
65 | 210 | self.assertEqual(expected, search_result) | ||
66 | 211 | # ... by BugTask.fti ... | ||
67 | 212 | params = self.getBugTaskSearchParams( | ||
68 | 213 | user=None, searchtext='two explanation') | ||
69 | 214 | search_result = self.runSearch(params) | ||
70 | 215 | expected = self.resultValuesForBugtasks(self.bugtasks[1:2]) | ||
71 | 216 | self.assertEqual(expected, search_result) | ||
72 | 217 | # ...and by MessageChunk.fti | ||
73 | 218 | params = self.getBugTaskSearchParams( | ||
74 | 219 | user=None, searchtext='three comment') | ||
75 | 220 | search_result = self.runSearch(params) | ||
76 | 221 | expected = self.resultValuesForBugtasks(self.bugtasks[2:3]) | ||
77 | 222 | self.assertEqual(expected, search_result) | ||
78 | 223 | |||
79 | 224 | def test_fast_fulltext_search(self): | ||
80 | 225 | # Fast full text searches find text indexed by Bug.fti... | ||
81 | 226 | self.setUpFullTextSearchTests() | ||
82 | 227 | params = self.getBugTaskSearchParams( | ||
83 | 228 | user=None, fast_searchtext='one title') | ||
84 | 229 | search_result = self.runSearch(params) | ||
85 | 230 | expected = self.resultValuesForBugtasks(self.bugtasks[:1]) | ||
86 | 231 | self.assertEqual(expected, search_result) | ||
87 | 232 | # ... but not text indexed by BugTask.fti ... | ||
88 | 233 | params = self.getBugTaskSearchParams( | ||
89 | 234 | user=None, fast_searchtext='two explanation') | ||
90 | 235 | search_result = self.runSearch(params) | ||
91 | 236 | self.assertEqual([], search_result) | ||
92 | 237 | # ..or by MessageChunk.fti | ||
93 | 238 | params = self.getBugTaskSearchParams( | ||
94 | 239 | user=None, fast_searchtext='three comment') | ||
95 | 240 | search_result = self.runSearch(params) | ||
96 | 241 | self.assertEqual([], search_result) | ||
97 | 242 | |||
98 | 243 | def test_has_no_upstream_bugtask(self): | ||
99 | 244 | # Search results can be limited to bugtasks of bugs that do | ||
100 | 245 | # not have a related upstream task. | ||
101 | 246 | # | ||
102 | 247 | # All bugs created in makeBugTasks() have at least one | ||
103 | 248 | # bug task for a product: The default bug task created | ||
104 | 249 | # by lp.testing.factory.Factory.makeBug() if neither a | ||
105 | 250 | # product nor a distribution is specified. For distribution | ||
106 | 251 | # related tests we need another bug which does not have | ||
107 | 252 | # an upstream (aka product) bug task, otherwise the set of | ||
108 | 253 | # bugtasks returned for a search for has_no_upstream_bugtask | ||
109 | 254 | # would always be empty. | ||
110 | 255 | if (IDistribution.providedBy(self.searchtarget) or | ||
111 | 256 | IDistroSeries.providedBy(self.searchtarget) or | ||
112 | 257 | ISourcePackage.providedBy(self.searchtarget) or | ||
113 | 258 | IDistributionSourcePackage.providedBy(self.searchtarget)): | ||
114 | 259 | if IDistribution.providedBy(self.searchtarget): | ||
115 | 260 | bug = self.factory.makeBug(distribution=self.searchtarget) | ||
116 | 261 | expected = self.resultValuesForBugtasks([bug.default_bugtask]) | ||
117 | 262 | else: | ||
118 | 263 | bug = self.factory.makeBug( | ||
119 | 264 | distribution=self.searchtarget.distribution) | ||
120 | 265 | bugtask = self.factory.makeBugTask( | ||
121 | 266 | bug=bug, target=self.searchtarget) | ||
122 | 267 | expected = self.resultValuesForBugtasks([bugtask]) | ||
123 | 268 | else: | ||
124 | 269 | # Bugs without distribution related bugtasks have always at | ||
125 | 270 | # least one product related bugtask, hence a | ||
126 | 271 | # has_no_upstream_bugtask search will always return an | ||
127 | 272 | # empty result set. | ||
128 | 273 | expected = [] | ||
129 | 274 | params = self.getBugTaskSearchParams( | ||
130 | 275 | user=None, has_no_upstream_bugtask=True) | ||
131 | 276 | search_result = self.runSearch(params) | ||
132 | 277 | self.assertEqual(expected, search_result) | ||
133 | 278 | |||
134 | 279 | def changeStatusOfBugTaskForOtherProduct(self, bugtask, new_status): | ||
135 | 280 | # Change the status of another bugtask of the same bug to the | ||
136 | 281 | # given status. | ||
137 | 282 | bug = bugtask.bug | ||
138 | 283 | for other_task in bug.bugtasks: | ||
139 | 284 | other_target = other_task.target | ||
140 | 285 | if other_task != bugtask and IProduct.providedBy(other_target): | ||
141 | 286 | with person_logged_in(other_target.owner): | ||
142 | 287 | other_task.transitionToStatus( | ||
143 | 288 | new_status, other_target.owner) | ||
144 | 289 | return | ||
145 | 290 | self.fail( | ||
146 | 291 | 'No bug task found for a product that is not the target of ' | ||
147 | 292 | 'the main test bugtask.') | ||
148 | 293 | |||
149 | 294 | def test_upstream_status(self): | ||
150 | 295 | # Search results can be filtered by the status of an upstream | ||
151 | 296 | # bug task. | ||
152 | 297 | # | ||
153 | 298 | # The bug task status of the default test data has only bug tasks | ||
154 | 299 | # with status NEW for the "other" product, hence all bug tasks | ||
155 | 300 | # will be returned in a search for bugs that are open upstream. | ||
156 | 301 | params = self.getBugTaskSearchParams(user=None, open_upstream=True) | ||
157 | 302 | search_result = self.runSearch(params) | ||
158 | 303 | expected = self.resultValuesForBugtasks(self.bugtasks) | ||
159 | 304 | self.assertEqual(expected, search_result) | ||
160 | 305 | # A search for tasks resolved upstream does not yield any bugtask. | ||
161 | 306 | params = self.getBugTaskSearchParams( | ||
162 | 307 | user=None, resolved_upstream=True) | ||
163 | 308 | search_result = self.runSearch(params) | ||
164 | 309 | self.assertEqual([], search_result) | ||
165 | 310 | # But if we set upstream bug tasks to "fix committed" or "fix | ||
166 | 311 | # released", the related bug tasks for our test target appear in | ||
167 | 312 | # the search result. | ||
168 | 313 | self.changeStatusOfBugTaskForOtherProduct( | ||
169 | 314 | self.bugtasks[0], BugTaskStatus.FIXCOMMITTED) | ||
170 | 315 | self.changeStatusOfBugTaskForOtherProduct( | ||
171 | 316 | self.bugtasks[1], BugTaskStatus.FIXRELEASED) | ||
172 | 317 | search_result = self.runSearch(params) | ||
173 | 318 | expected = self.resultValuesForBugtasks(self.bugtasks[:2]) | ||
174 | 319 | self.assertEqual(expected, search_result) | ||
175 | 320 | # A search for bug tasks open upstream now returns only one | ||
176 | 321 | # test task. | ||
177 | 322 | params = self.getBugTaskSearchParams(user=None, open_upstream=True) | ||
178 | 323 | search_result = self.runSearch(params) | ||
179 | 324 | expected = self.resultValuesForBugtasks(self.bugtasks[2:]) | ||
180 | 325 | |||
181 | 326 | def test_tags(self): | ||
182 | 327 | # Search results can be limited to bugs having given tags. | ||
183 | 328 | with person_logged_in(self.owner): | ||
184 | 329 | self.bugtasks[0].bug.tags = ['tag1', 'tag2'] | ||
185 | 330 | self.bugtasks[1].bug.tags = ['tag1', 'tag3'] | ||
186 | 331 | params = self.getBugTaskSearchParams( | ||
187 | 332 | user=None, tag=any('tag2', 'tag3')) | ||
188 | 333 | search_result = self.runSearch(params) | ||
189 | 334 | expected = self.resultValuesForBugtasks(self.bugtasks[:2]) | ||
190 | 335 | self.assertEqual(expected, search_result) | ||
191 | 336 | |||
192 | 337 | params = self.getBugTaskSearchParams( | ||
193 | 338 | user=None, tag=all('tag2', 'tag3')) | ||
194 | 339 | search_result = self.runSearch(params) | ||
195 | 340 | self.assertEqual([], search_result) | ||
196 | 341 | |||
197 | 342 | params = self.getBugTaskSearchParams( | ||
198 | 343 | user=None, tag=all('tag1', 'tag3')) | ||
199 | 344 | search_result = self.runSearch(params) | ||
200 | 345 | expected = self.resultValuesForBugtasks(self.bugtasks[1:2]) | ||
201 | 346 | self.assertEqual(expected, search_result) | ||
202 | 347 | |||
203 | 348 | params = self.getBugTaskSearchParams( | ||
204 | 349 | user=None, tag=all('tag1', '-tag3')) | ||
205 | 350 | search_result = self.runSearch(params) | ||
206 | 351 | expected = self.resultValuesForBugtasks(self.bugtasks[:1]) | ||
207 | 352 | self.assertEqual(expected, search_result) | ||
208 | 353 | |||
209 | 354 | params = self.getBugTaskSearchParams( | ||
210 | 355 | user=None, tag=all('-tag1')) | ||
211 | 356 | search_result = self.runSearch(params) | ||
212 | 357 | expected = self.resultValuesForBugtasks(self.bugtasks[2:]) | ||
213 | 358 | self.assertEqual(expected, search_result) | ||
214 | 359 | |||
215 | 360 | params = self.getBugTaskSearchParams( | ||
216 | 361 | user=None, tag=all('*')) | ||
217 | 362 | search_result = self.runSearch(params) | ||
218 | 363 | expected = self.resultValuesForBugtasks(self.bugtasks[:2]) | ||
219 | 364 | self.assertEqual(expected, search_result) | ||
220 | 365 | |||
221 | 366 | params = self.getBugTaskSearchParams( | ||
222 | 367 | user=None, tag=all('-*')) | ||
223 | 368 | search_result = self.runSearch(params) | ||
224 | 369 | expected = self.resultValuesForBugtasks(self.bugtasks[2:]) | ||
225 | 370 | self.assertEqual(expected, search_result) | ||
226 | 371 | |||
227 | 372 | def test_date_closed(self): | ||
228 | 373 | # Search results can be filtered by the date_closed time | ||
229 | 374 | # of a bugtask. | ||
230 | 375 | with person_logged_in(self.owner): | ||
231 | 376 | self.bugtasks[2].transitionToStatus( | ||
232 | 377 | BugTaskStatus.FIXRELEASED, self.owner) | ||
233 | 378 | utc_now = datetime.now(pytz.timezone('UTC')) | ||
234 | 379 | self.assertTrue(utc_now >= self.bugtasks[2].date_closed) | ||
235 | 380 | params = self.getBugTaskSearchParams( | ||
236 | 381 | user=None, date_closed=greater_than(utc_now-timedelta(days=1))) | ||
237 | 382 | search_result = self.runSearch(params) | ||
238 | 383 | expected = self.resultValuesForBugtasks(self.bugtasks[2:]) | ||
239 | 384 | self.assertEqual(expected, search_result) | ||
240 | 385 | params = self.getBugTaskSearchParams( | ||
241 | 386 | user=None, date_closed=greater_than(utc_now+timedelta(days=1))) | ||
242 | 387 | search_result = self.runSearch(params) | ||
243 | 388 | self.assertEqual([], search_result) | ||
244 | 389 | |||
245 | 176 | 390 | ||
246 | 177 | class ProductAndDistributionTests: | 391 | class ProductAndDistributionTests: |
247 | 178 | """Tests which are useful for distributions and products.""" | 392 | """Tests which are useful for distributions and products.""" |
248 | @@ -190,8 +404,7 @@ | |||
249 | 190 | with person_logged_in(self.owner): | 404 | with person_logged_in(self.owner): |
250 | 191 | self.bugtasks[0].bug.addNomination(nominator, series1) | 405 | self.bugtasks[0].bug.addNomination(nominator, series1) |
251 | 192 | self.bugtasks[1].bug.addNomination(nominator, series2) | 406 | self.bugtasks[1].bug.addNomination(nominator, series2) |
254 | 193 | params = self.getBugTaskSearchParams( | 407 | params = self.getBugTaskSearchParams(user=None, nominated_for=series1) |
253 | 194 | user=None, nominated_for=series1) | ||
255 | 195 | search_result = self.runSearch(params) | 408 | search_result = self.runSearch(params) |
256 | 196 | expected = self.resultValuesForBugtasks(self.bugtasks[:1]) | 409 | expected = self.resultValuesForBugtasks(self.bugtasks[:1]) |
257 | 197 | self.assertEqual(expected, search_result) | 410 | self.assertEqual(expected, search_result) |
258 | @@ -323,6 +536,8 @@ | |||
259 | 323 | 536 | ||
260 | 324 | product = self.factory.makeProduct(owner=self.owner) | 537 | product = self.factory.makeProduct(owner=self.owner) |
261 | 325 | product.project = self.searchtarget | 538 | product.project = self.searchtarget |
262 | 539 | self.bugtasks.append( | ||
263 | 540 | self.factory.makeBugTask(target=product)) | ||
264 | 326 | self.bugtasks[-1].importance = BugTaskImportance.LOW | 541 | self.bugtasks[-1].importance = BugTaskImportance.LOW |
265 | 327 | self.bugtasks[-1].transitionToStatus( | 542 | self.bugtasks[-1].transitionToStatus( |
266 | 328 | BugTaskStatus.NEW, self.owner) | 543 | BugTaskStatus.NEW, self.owner) |
267 | 329 | 544 | ||
268 | === modified file 'lib/lp/testing/factory.py' | |||
269 | --- lib/lp/testing/factory.py 2010-10-27 22:33:01 +0000 | |||
270 | +++ lib/lp/testing/factory.py 2010-10-29 13:04:06 +0000 | |||
271 | @@ -1333,19 +1333,25 @@ | |||
272 | 1333 | def makeBug(self, product=None, owner=None, bug_watch_url=None, | 1333 | def makeBug(self, product=None, owner=None, bug_watch_url=None, |
273 | 1334 | private=False, date_closed=None, title=None, | 1334 | private=False, date_closed=None, title=None, |
274 | 1335 | date_created=None, description=None, comment=None, | 1335 | date_created=None, description=None, comment=None, |
276 | 1336 | status=None): | 1336 | status=None, distribution=None): |
277 | 1337 | """Create and return a new, arbitrary Bug. | 1337 | """Create and return a new, arbitrary Bug. |
278 | 1338 | 1338 | ||
279 | 1339 | The bug returned uses default values where possible. See | 1339 | The bug returned uses default values where possible. See |
280 | 1340 | `IBugSet.new` for more information. | 1340 | `IBugSet.new` for more information. |
281 | 1341 | 1341 | ||
284 | 1342 | :param product: If the product is not set, one is created | 1342 | :param product: If the product is not set, and if the parameter |
285 | 1343 | and this is used as the primary bug target. | 1343 | distribution is not set, a product is created and this is |
286 | 1344 | used as the primary bug target. | ||
287 | 1344 | :param owner: The reporter of the bug. If not set, one is created. | 1345 | :param owner: The reporter of the bug. If not set, one is created. |
288 | 1345 | :param bug_watch_url: If specified, create a bug watch pointing | 1346 | :param bug_watch_url: If specified, create a bug watch pointing |
289 | 1346 | to this URL. | 1347 | to this URL. |
290 | 1348 | :param distribution: If set, the distribution is used as the | ||
291 | 1349 | default bug target. | ||
292 | 1350 | |||
293 | 1351 | At least one of the parameters distribution and product must be | ||
294 | 1352 | None, otherwise, an assertion error will be raised. | ||
295 | 1347 | """ | 1353 | """ |
297 | 1348 | if product is None: | 1354 | if product is None and distribution is None: |
298 | 1349 | product = self.makeProduct() | 1355 | product = self.makeProduct() |
299 | 1350 | if owner is None: | 1356 | if owner is None: |
300 | 1351 | owner = self.makePerson() | 1357 | owner = self.makePerson() |
301 | @@ -1357,7 +1363,8 @@ | |||
302 | 1357 | owner, title, comment=comment, private=private, | 1363 | owner, title, comment=comment, private=private, |
303 | 1358 | datecreated=date_created, description=description, | 1364 | datecreated=date_created, description=description, |
304 | 1359 | status=status) | 1365 | status=status) |
306 | 1360 | create_bug_params.setBugTarget(product=product) | 1366 | create_bug_params.setBugTarget( |
307 | 1367 | product=product, distribution=distribution) | ||
308 | 1361 | bug = getUtility(IBugSet).createBug(create_bug_params) | 1368 | bug = getUtility(IBugSet).createBug(create_bug_params) |
309 | 1362 | if bug_watch_url is not None: | 1369 | if bug_watch_url is not None: |
310 | 1363 | # fromText() creates a bug watch associated with the bug. | 1370 | # fromText() creates a bug watch associated with the bug. |
Cool. Only one very trivial comment.
[1]
+ assert( bug.default_ bugtask. target == bugtarget)
This works, but it's misleading because assert is a statement not a
function. Actually... did you mean to remove it before landing? If
not, perhaps it should be changed to an assertEqual() call.