]> git.ozlabs.org Git - patchwork/blob - apps/patchwork/tests/bundles.py
0404576e905c82555a9664d247def6834bfbecfe
[patchwork] / apps / patchwork / tests / bundles.py
1 # Patchwork - automated patch tracking system
2 # Copyright (C) 2009 Jeremy Kerr <jk@ozlabs.org>
3 #
4 # This file is part of the Patchwork package.
5 #
6 # Patchwork is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # Patchwork is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with Patchwork; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 import unittest
21 import datetime
22 from django.test import TestCase
23 from django.test.client import Client
24 from django.utils.http import urlencode
25 from patchwork.models import Patch, Bundle, BundlePatch, Person
26 from patchwork.tests.utils import defaults, create_user, find_in_context
27
28 class BundleListTest(TestCase):
29     def setUp(self):
30         self.user = create_user()
31         self.client.login(username = self.user.username,
32                 password = self.user.username)
33
34     def testNoBundles(self):
35         response = self.client.get('/user/bundles/')
36         self.failUnlessEqual(response.status_code, 200)
37         self.failUnlessEqual(
38                 len(find_in_context(response.context, 'bundles')), 0)
39
40     def testSingleBundle(self):
41         defaults.project.save()
42         bundle = Bundle(owner = self.user, project = defaults.project)
43         bundle.save()
44         response = self.client.get('/user/bundles/')
45         self.failUnlessEqual(response.status_code, 200)
46         self.failUnlessEqual(
47                 len(find_in_context(response.context, 'bundles')), 1)
48
49     def tearDown(self):
50         self.user.delete()
51
52 class BundleTestBase(TestCase):
53     def setUp(self, patch_count=3):
54         patch_names = ['testpatch%d' % (i) for i in range(1, patch_count+1)]
55         self.user = create_user()
56         self.client.login(username = self.user.username,
57                 password = self.user.username)
58         defaults.project.save()
59         self.bundle = Bundle(owner = self.user, project = defaults.project,
60                 name = 'testbundle')
61         self.bundle.save()
62         self.patches = []
63
64         for patch_name in patch_names:
65             patch = Patch(project = defaults.project,
66                                msgid = patch_name, name = patch_name,
67                                submitter = Person.objects.get(user = self.user),
68                                content = '')
69             patch.save()
70             self.patches.append(patch)
71
72     def tearDown(self):
73         for patch in self.patches:
74             patch.delete()
75         self.bundle.delete()
76         self.user.delete()
77
78 class BundleViewTest(BundleTestBase):
79
80     def testEmptyBundle(self):
81         response = self.client.get('/user/bundle/%d/' % self.bundle.id)
82         self.failUnlessEqual(response.status_code, 200)
83         page = find_in_context(response.context, 'page')
84         self.failUnlessEqual(len(page.object_list), 0)
85
86     def testNonEmptyBundle(self):
87         self.bundle.append_patch(self.patches[0])
88
89         response = self.client.get('/user/bundle/%d/' % self.bundle.id)
90         self.failUnlessEqual(response.status_code, 200)
91         page = find_in_context(response.context, 'page')
92         self.failUnlessEqual(len(page.object_list), 1)
93
94     def testBundleOrder(self):
95         for patch in self.patches:
96             self.bundle.append_patch(patch)
97
98         response = self.client.get('/user/bundle/%d/' % self.bundle.id)
99
100         pos = 0
101         for patch in self.patches:
102             next_pos = response.content.find(patch.name)
103             # ensure that this patch is after the previous
104             self.failUnless(next_pos > pos)
105             pos = next_pos
106
107         # reorder and recheck
108         i = 0
109         for patch in self.patches.__reversed__():
110             bundlepatch = BundlePatch.objects.get(bundle = self.bundle,
111                     patch = patch)
112             bundlepatch.order = i
113             bundlepatch.save()
114             i += 1
115
116         response = self.client.get('/user/bundle/%d/' % self.bundle.id)
117         pos = len(response.content)
118         for patch in self.patches:
119             next_pos = response.content.find(patch.name)
120             # ensure that this patch is now *before* the previous
121             self.failUnless(next_pos < pos)
122             pos = next_pos
123
124 class BundleUpdateTest(BundleTestBase):
125
126     def setUp(self):
127         super(BundleUpdateTest, self).setUp()
128         self.newname = 'newbundlename'
129
130     def publicString(self, public):
131         if public:
132             return 'on'
133         return ''
134
135     def testNoAction(self):
136         data = {
137             'form': 'bundle',
138             'name': self.newname,
139             'public': self.publicString(not self.bundle.public)
140         }
141         response = self.client.post('/user/bundle/%d/' % self.bundle.id, data)
142         self.assertEqual(response.status_code, 200)
143
144         bundle = Bundle.objects.get(pk = self.bundle.pk)
145         self.assertEqual(bundle.name, self.bundle.name)
146         self.assertEqual(bundle.public, self.bundle.public)
147
148     def testUpdateName(self):
149         newname = 'newbundlename'
150         data = {
151             'form': 'bundle',
152             'action': 'update',
153             'name': newname,
154             'public': self.publicString(self.bundle.public)
155         }
156         response = self.client.post('/user/bundle/%d/' % self.bundle.id, data)
157         self.assertEqual(response.status_code, 200)
158         bundle = Bundle.objects.get(pk = self.bundle.pk)
159         self.assertEqual(bundle.name, newname)
160         self.assertEqual(bundle.public, self.bundle.public)
161
162     def testUpdatePublic(self):
163         newname = 'newbundlename'
164         data = {
165             'form': 'bundle',
166             'action': 'update',
167             'name': self.bundle.name,
168             'public': self.publicString(not self.bundle.public)
169         }
170         response = self.client.post('/user/bundle/%d/' % self.bundle.id, data)
171         self.assertEqual(response.status_code, 200)
172         bundle = Bundle.objects.get(pk = self.bundle.pk)
173         self.assertEqual(bundle.name, self.bundle.name)
174         self.assertEqual(bundle.public, not self.bundle.public)
175
176 class BundlePublicViewTest(BundleTestBase):
177
178     def setUp(self):
179         super(BundlePublicViewTest, self).setUp()
180         self.client.logout()
181         self.bundle.append_patch(self.patches[0])
182         self.url = '/bundle/%s/%s/' % (self.user.username, self.bundle.name)
183
184     def testPublicBundle(self):
185         self.bundle.public = True
186         self.bundle.save()
187         response = self.client.get(self.url)
188         self.assertEqual(response.status_code, 200)
189         self.assertContains(response, self.patches[0].name)
190
191     def testPrivateBundle(self):
192         self.bundle.public = False
193         self.bundle.save()
194         response = self.client.get(self.url)
195         self.assertEqual(response.status_code, 404)
196
197 class BundlePublicViewMboxTest(BundlePublicViewTest):
198     def setUp(self):
199         super(BundlePublicViewMboxTest, self).setUp()
200         self.url = '/bundle/%s/%s/mbox/' % (self.user.username,
201                                             self.bundle.name)
202
203 class BundleCreateFromListTest(BundleTestBase):
204     def testCreateEmptyBundle(self):
205         newbundlename = 'testbundle-new'
206         params = {'form': 'patchlistform',
207                   'bundle_name': newbundlename,
208                   'action': 'Create',
209                   'project': defaults.project.id}
210
211         response = self.client.post(
212                 '/project/%s/list/' % defaults.project.linkname,
213                 params)
214
215         self.assertContains(response, 'Bundle %s created' % newbundlename)
216
217     def testCreateNonEmptyBundle(self):
218         newbundlename = 'testbundle-new'
219         patch = self.patches[0]
220
221         params = {'form': 'patchlistform',
222                   'bundle_name': newbundlename,
223                   'action': 'Create',
224                   'project': defaults.project.id,
225                   'patch_id:%d' % patch.id: 'checked'}
226
227         response = self.client.post(
228                 '/project/%s/list/' % defaults.project.linkname,
229                 params)
230
231         self.assertContains(response, 'Bundle %s created' % newbundlename)
232         self.assertContains(response, 'added to bundle %s' % newbundlename,
233             count = 1)
234
235         bundle = Bundle.objects.get(name = newbundlename)
236         self.failUnlessEqual(bundle.patches.count(), 1)
237         self.failUnlessEqual(bundle.patches.all()[0], patch)
238
239     def testCreateNonEmptyBundleEmptyName(self):
240         newbundlename = 'testbundle-new'
241         patch = self.patches[0]
242
243         n_bundles = Bundle.objects.count()
244
245         params = {'form': 'patchlistform',
246                   'bundle_name': '',
247                   'action': 'Create',
248                   'project': defaults.project.id,
249                   'patch_id:%d' % patch.id: 'checked'}
250
251         response = self.client.post(
252                 '/project/%s/list/' % defaults.project.linkname,
253                 params)
254
255         self.assertContains(response, 'No bundle name was specified',
256                 status_code = 200)
257
258         # test that no new bundles are present
259         self.failUnlessEqual(n_bundles, Bundle.objects.count())
260
261     def testCreateDuplicateName(self):
262         newbundlename = 'testbundle-dup'
263         patch = self.patches[0]
264
265         params = {'form': 'patchlistform',
266                   'bundle_name': newbundlename,
267                   'action': 'Create',
268                   'project': defaults.project.id,
269                   'patch_id:%d' % patch.id: 'checked'}
270
271         response = self.client.post(
272                 '/project/%s/list/' % defaults.project.linkname,
273                 params)
274
275         n_bundles = Bundle.objects.count()
276         self.assertContains(response, 'Bundle %s created' % newbundlename)
277         self.assertContains(response, 'added to bundle %s' % newbundlename,
278             count = 1)
279
280         bundle = Bundle.objects.get(name = newbundlename)
281         self.failUnlessEqual(bundle.patches.count(), 1)
282         self.failUnlessEqual(bundle.patches.all()[0], patch)
283
284         response = self.client.post(
285                 '/project/%s/list/' % defaults.project.linkname,
286                 params)
287
288         self.assertNotContains(response, 'Bundle %s created' % newbundlename)
289         self.assertContains(response, 'You already have a bundle called')
290         self.assertEqual(Bundle.objects.count(), n_bundles)
291         self.assertEqual(bundle.patches.count(), 1)
292
293 class BundleCreateFromPatchTest(BundleTestBase):
294     def testCreateNonEmptyBundle(self):
295         newbundlename = 'testbundle-new'
296         patch = self.patches[0]
297
298         params = {'name': newbundlename,
299                   'action': 'createbundle'}
300
301         response = self.client.post('/patch/%d/' % patch.id, params)
302
303         self.assertContains(response,
304                 'Bundle %s created' % newbundlename)
305
306         bundle = Bundle.objects.get(name = newbundlename)
307         self.failUnlessEqual(bundle.patches.count(), 1)
308         self.failUnlessEqual(bundle.patches.all()[0], patch)
309
310     def testCreateWithExistingName(self):
311         newbundlename = self.bundle.name
312         patch = self.patches[0]
313
314         params = {'name': newbundlename,
315                   'action': 'createbundle'}
316
317         response = self.client.post('/patch/%d/' % patch.id, params)
318
319         self.assertContains(response,
320                 'A bundle called %s already exists' % newbundlename)
321
322         count = Bundle.objects.count()
323         self.failUnlessEqual(Bundle.objects.count(), 1)
324
325 class BundleAddFromListTest(BundleTestBase):
326     def testAddToEmptyBundle(self):
327         patch = self.patches[0]
328         params = {'form': 'patchlistform',
329                   'action': 'Add',
330                   'project': defaults.project.id,
331                   'bundle_id': self.bundle.id,
332                   'patch_id:%d' % patch.id: 'checked'}
333
334         response = self.client.post(
335                 '/project/%s/list/' % defaults.project.linkname,
336                 params)
337
338         self.assertContains(response, 'added to bundle %s' % self.bundle.name,
339             count = 1)
340
341         self.failUnlessEqual(self.bundle.patches.count(), 1)
342         self.failUnlessEqual(self.bundle.patches.all()[0], patch)
343
344     def testAddToNonEmptyBundle(self):
345         self.bundle.append_patch(self.patches[0])
346         patch = self.patches[1]
347         params = {'form': 'patchlistform',
348                   'action': 'Add',
349                   'project': defaults.project.id,
350                   'bundle_id': self.bundle.id,
351                   'patch_id:%d' % patch.id: 'checked'}
352
353         response = self.client.post(
354                 '/project/%s/list/' % defaults.project.linkname,
355                 params)
356
357         self.assertContains(response, 'added to bundle %s' % self.bundle.name,
358             count = 1)
359
360         self.failUnlessEqual(self.bundle.patches.count(), 2)
361         self.failUnless(self.patches[0] in self.bundle.patches.all())
362         self.failUnless(self.patches[1] in self.bundle.patches.all())
363
364         # check order
365         bps = [ BundlePatch.objects.get(bundle = self.bundle,
366                                         patch = self.patches[i]) \
367                 for i in [0, 1] ]
368         self.failUnless(bps[0].order < bps[1].order)
369
370     def testAddDuplicate(self):
371         self.bundle.append_patch(self.patches[0])
372         count = self.bundle.patches.count()
373         patch = self.patches[0]
374
375         params = {'form': 'patchlistform',
376                   'action': 'Add',
377                   'project': defaults.project.id,
378                   'bundle_id': self.bundle.id,
379                   'patch_id:%d' % patch.id: 'checked'}
380
381         response = self.client.post(
382                 '/project/%s/list/' % defaults.project.linkname,
383                 params)
384
385         self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
386                             % patch.name, count = 1, status_code = 200)
387
388         self.assertEquals(count, self.bundle.patches.count())
389
390     def testAddNewAndDuplicate(self):
391         self.bundle.append_patch(self.patches[0])
392         count = self.bundle.patches.count()
393         patch = self.patches[0]
394
395         params = {'form': 'patchlistform',
396                   'action': 'Add',
397                   'project': defaults.project.id,
398                   'bundle_id': self.bundle.id,
399                   'patch_id:%d' % patch.id: 'checked',
400                   'patch_id:%d' % self.patches[1].id: 'checked'}
401
402         response = self.client.post(
403                 '/project/%s/list/' % defaults.project.linkname,
404                 params)
405
406         self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
407                             % patch.name, count = 1, status_code = 200)
408         self.assertContains(response, 'Patch &#39;%s&#39; added to bundle' \
409                             % self.patches[1].name, count = 1,
410                             status_code = 200)
411         self.assertEquals(count + 1, self.bundle.patches.count())
412
413 class BundleAddFromPatchTest(BundleTestBase):
414     def testAddToEmptyBundle(self):
415         patch = self.patches[0]
416         params = {'action': 'addtobundle',
417                   'bundle_id': self.bundle.id}
418
419         response = self.client.post('/patch/%d/' % patch.id, params)
420
421         self.assertContains(response,
422                 'added to bundle &quot;%s&quot;' % self.bundle.name,
423                 count = 1)
424
425         self.failUnlessEqual(self.bundle.patches.count(), 1)
426         self.failUnlessEqual(self.bundle.patches.all()[0], patch)
427
428     def testAddToNonEmptyBundle(self):
429         self.bundle.append_patch(self.patches[0])
430         patch = self.patches[1]
431         params = {'action': 'addtobundle',
432                   'bundle_id': self.bundle.id}
433
434         response = self.client.post('/patch/%d/' % patch.id, params)
435
436         self.assertContains(response,
437                 'added to bundle &quot;%s&quot;' % self.bundle.name,
438                 count = 1)
439
440         self.failUnlessEqual(self.bundle.patches.count(), 2)
441         self.failUnless(self.patches[0] in self.bundle.patches.all())
442         self.failUnless(self.patches[1] in self.bundle.patches.all())
443
444         # check order
445         bps = [ BundlePatch.objects.get(bundle = self.bundle,
446                                         patch = self.patches[i]) \
447                 for i in [0, 1] ]
448         self.failUnless(bps[0].order < bps[1].order)
449
450 class BundleInitialOrderTest(BundleTestBase):
451     """When creating bundles from a patch list, ensure that the patches in the
452        bundle are ordered by date"""
453
454     def setUp(self):
455         super(BundleInitialOrderTest, self).setUp(5)
456
457         # put patches in an arbitrary order
458         idxs = [2, 4, 3, 1, 0]
459         self.patches = [ self.patches[i] for i in idxs ]
460
461         # set dates to be sequential
462         last_patch = self.patches[0]
463         for patch in self.patches[1:]:
464             patch.date = last_patch.date + datetime.timedelta(0, 1)
465             patch.save()
466             last_patch = patch
467
468     def _testOrder(self, ids, expected_order):
469         newbundlename = 'testbundle-new'
470
471         # need to define our querystring explicity to enforce ordering
472         params = {'form': 'patchlistform',
473                   'bundle_name': newbundlename,
474                   'action': 'Create',
475                   'project': defaults.project.id,
476         }
477
478         data = urlencode(params) + \
479                ''.join([ '&patch_id:%d=checked' % i for i in ids ])
480
481         response = self.client.post(
482                 '/project/%s/list/' % defaults.project.linkname,
483                 data = data,
484                 content_type = 'application/x-www-form-urlencoded',
485                 )
486
487         self.assertContains(response, 'Bundle %s created' % newbundlename)
488         self.assertContains(response, 'added to bundle %s' % newbundlename,
489             count = 5)
490
491         bundle = Bundle.objects.get(name = newbundlename)
492
493         # BundlePatches should be sorted by .order by default
494         bps = BundlePatch.objects.filter(bundle = bundle)
495
496         for (bp, p) in zip(bps, expected_order):
497             self.assertEqual(bp.patch.pk, p.pk)
498
499         bundle.delete()
500
501     def testBundleForwardOrder(self):
502         ids = map(lambda p: p.id, self.patches)
503         self._testOrder(ids, self.patches)
504
505     def testBundleReverseOrder(self):
506         ids = map(lambda p: p.id, self.patches)
507         ids.reverse()
508         self._testOrder(ids, self.patches)
509
510 class BundleReorderTest(BundleTestBase):
511     def setUp(self):
512         super(BundleReorderTest, self).setUp(5)
513         for i in range(5):
514             self.bundle.append_patch(self.patches[i])
515
516     def checkReordering(self, neworder, start, end):
517         neworder_ids = [ self.patches[i].id for i in neworder ]
518
519         firstpatch = BundlePatch.objects.get(bundle = self.bundle,
520                 patch = self.patches[start]).patch
521
522         slice_ids = neworder_ids[start:end]
523         params = {'form': 'reorderform',
524                   'order_start': firstpatch.id,
525                   'neworder': slice_ids}
526
527         response = self.client.post('/user/bundle/%d/' % self.bundle.id,
528                                     params)
529
530         self.failUnlessEqual(response.status_code, 200)
531
532         bps = BundlePatch.objects.filter(bundle = self.bundle) \
533                         .order_by('order')
534
535         # check if patch IDs are in the expected order:
536         bundle_ids = [ bp.patch.id for bp in bps ]
537         self.failUnlessEqual(neworder_ids, bundle_ids)
538
539         # check if order field is still sequential:
540         order_numbers = [ bp.order for bp in bps ]
541         expected_order = range(1, len(neworder)+1) # [1 ... len(neworder)]
542         self.failUnlessEqual(order_numbers, expected_order)
543
544     def testBundleReorderAll(self):
545         # reorder all patches:
546         self.checkReordering([2,1,4,0,3], 0, 5)
547
548     def testBundleReorderEnd(self):
549         # reorder only the last three patches
550         self.checkReordering([0,1,3,2,4], 2, 5)
551
552     def testBundleReorderBegin(self):
553         # reorder only the first three patches
554         self.checkReordering([2,0,1,3,4], 0, 3)
555
556     def testBundleReorderMiddle(self):
557         # reorder only 2nd, 3rd, and 4th patches
558         self.checkReordering([0,2,3,1,4], 1, 4)