]> git.ozlabs.org Git - patchwork/blob - apps/patchwork/tests/bundles.py
00a2323f79fe02ded4d44d10fea15f075cdc8925
[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 checkPatchformErrors(self, response):
131         formname = 'patchform'
132         if not formname in response.context:
133             return
134         form = response.context[formname]
135         if not form:
136             return
137         self.assertEquals(form.errors, {})
138
139     def publicString(self, public):
140         if public:
141             return 'on'
142         return ''
143
144     def testNoAction(self):
145         data = {
146             'form': 'bundle',
147             'name': self.newname,
148             'public': self.publicString(not self.bundle.public)
149         }
150         response = self.client.post('/user/bundle/%d/' % self.bundle.id, data)
151         self.assertEqual(response.status_code, 200)
152
153         bundle = Bundle.objects.get(pk = self.bundle.pk)
154         self.assertEqual(bundle.name, self.bundle.name)
155         self.assertEqual(bundle.public, self.bundle.public)
156
157     def testUpdateName(self):
158         newname = 'newbundlename'
159         data = {
160             'form': 'bundle',
161             'action': 'update',
162             'name': newname,
163             'public': self.publicString(self.bundle.public)
164         }
165         response = self.client.post('/user/bundle/%d/' % self.bundle.id, data)
166         self.assertEqual(response.status_code, 200)
167         bundle = Bundle.objects.get(pk = self.bundle.pk)
168         self.assertEqual(bundle.name, newname)
169         self.assertEqual(bundle.public, self.bundle.public)
170
171         # check other forms for errors
172         self.checkPatchformErrors(response)
173
174     def testUpdatePublic(self):
175         newname = 'newbundlename'
176         data = {
177             'form': 'bundle',
178             'action': 'update',
179             'name': self.bundle.name,
180             'public': self.publicString(not self.bundle.public)
181         }
182         response = self.client.post('/user/bundle/%d/' % self.bundle.id, data)
183         self.assertEqual(response.status_code, 200)
184         bundle = Bundle.objects.get(pk = self.bundle.pk)
185         self.assertEqual(bundle.name, self.bundle.name)
186         self.assertEqual(bundle.public, not self.bundle.public)
187
188         # check other forms for errors
189         self.checkPatchformErrors(response)
190
191 class BundleMaintainerUpdateTest(BundleUpdateTest):
192
193     def setUp(self):
194         super(BundleMaintainerUpdateTest, self).setUp()
195         profile = self.user.get_profile()
196         profile.maintainer_projects.add(defaults.project)
197         profile.save()
198
199 class BundlePublicViewTest(BundleTestBase):
200
201     def setUp(self):
202         super(BundlePublicViewTest, self).setUp()
203         self.client.logout()
204         self.bundle.append_patch(self.patches[0])
205         self.url = '/bundle/%s/%s/' % (self.user.username, self.bundle.name)
206
207     def testPublicBundle(self):
208         self.bundle.public = True
209         self.bundle.save()
210         response = self.client.get(self.url)
211         self.assertEqual(response.status_code, 200)
212         self.assertContains(response, self.patches[0].name)
213
214     def testPrivateBundle(self):
215         self.bundle.public = False
216         self.bundle.save()
217         response = self.client.get(self.url)
218         self.assertEqual(response.status_code, 404)
219
220 class BundlePublicViewMboxTest(BundlePublicViewTest):
221     def setUp(self):
222         super(BundlePublicViewMboxTest, self).setUp()
223         self.url = '/bundle/%s/%s/mbox/' % (self.user.username,
224                                             self.bundle.name)
225
226 class BundleCreateFromListTest(BundleTestBase):
227     def testCreateEmptyBundle(self):
228         newbundlename = 'testbundle-new'
229         params = {'form': 'patchlistform',
230                   'bundle_name': newbundlename,
231                   'action': 'Create',
232                   'project': defaults.project.id}
233
234         response = self.client.post(
235                 '/project/%s/list/' % defaults.project.linkname,
236                 params)
237
238         self.assertContains(response, 'Bundle %s created' % newbundlename)
239
240     def testCreateNonEmptyBundle(self):
241         newbundlename = 'testbundle-new'
242         patch = self.patches[0]
243
244         params = {'form': 'patchlistform',
245                   'bundle_name': newbundlename,
246                   'action': 'Create',
247                   'project': defaults.project.id,
248                   'patch_id:%d' % patch.id: 'checked'}
249
250         response = self.client.post(
251                 '/project/%s/list/' % defaults.project.linkname,
252                 params)
253
254         self.assertContains(response, 'Bundle %s created' % newbundlename)
255         self.assertContains(response, 'added to bundle %s' % newbundlename,
256             count = 1)
257
258         bundle = Bundle.objects.get(name = newbundlename)
259         self.failUnlessEqual(bundle.patches.count(), 1)
260         self.failUnlessEqual(bundle.patches.all()[0], patch)
261
262     def testCreateNonEmptyBundleEmptyName(self):
263         newbundlename = 'testbundle-new'
264         patch = self.patches[0]
265
266         n_bundles = Bundle.objects.count()
267
268         params = {'form': 'patchlistform',
269                   'bundle_name': '',
270                   'action': 'Create',
271                   'project': defaults.project.id,
272                   'patch_id:%d' % patch.id: 'checked'}
273
274         response = self.client.post(
275                 '/project/%s/list/' % defaults.project.linkname,
276                 params)
277
278         self.assertContains(response, 'No bundle name was specified',
279                 status_code = 200)
280
281         # test that no new bundles are present
282         self.failUnlessEqual(n_bundles, Bundle.objects.count())
283
284     def testCreateDuplicateName(self):
285         newbundlename = 'testbundle-dup'
286         patch = self.patches[0]
287
288         params = {'form': 'patchlistform',
289                   'bundle_name': newbundlename,
290                   'action': 'Create',
291                   'project': defaults.project.id,
292                   'patch_id:%d' % patch.id: 'checked'}
293
294         response = self.client.post(
295                 '/project/%s/list/' % defaults.project.linkname,
296                 params)
297
298         n_bundles = Bundle.objects.count()
299         self.assertContains(response, 'Bundle %s created' % newbundlename)
300         self.assertContains(response, 'added to bundle %s' % newbundlename,
301             count = 1)
302
303         bundle = Bundle.objects.get(name = newbundlename)
304         self.failUnlessEqual(bundle.patches.count(), 1)
305         self.failUnlessEqual(bundle.patches.all()[0], patch)
306
307         response = self.client.post(
308                 '/project/%s/list/' % defaults.project.linkname,
309                 params)
310
311         self.assertNotContains(response, 'Bundle %s created' % newbundlename)
312         self.assertContains(response, 'You already have a bundle called')
313         self.assertEqual(Bundle.objects.count(), n_bundles)
314         self.assertEqual(bundle.patches.count(), 1)
315
316 class BundleCreateFromPatchTest(BundleTestBase):
317     def testCreateNonEmptyBundle(self):
318         newbundlename = 'testbundle-new'
319         patch = self.patches[0]
320
321         params = {'name': newbundlename,
322                   'action': 'createbundle'}
323
324         response = self.client.post('/patch/%d/' % patch.id, params)
325
326         self.assertContains(response,
327                 'Bundle %s created' % newbundlename)
328
329         bundle = Bundle.objects.get(name = newbundlename)
330         self.failUnlessEqual(bundle.patches.count(), 1)
331         self.failUnlessEqual(bundle.patches.all()[0], patch)
332
333     def testCreateWithExistingName(self):
334         newbundlename = self.bundle.name
335         patch = self.patches[0]
336
337         params = {'name': newbundlename,
338                   'action': 'createbundle'}
339
340         response = self.client.post('/patch/%d/' % patch.id, params)
341
342         self.assertContains(response,
343                 'A bundle called %s already exists' % newbundlename)
344
345         count = Bundle.objects.count()
346         self.failUnlessEqual(Bundle.objects.count(), 1)
347
348 class BundleAddFromListTest(BundleTestBase):
349     def testAddToEmptyBundle(self):
350         patch = self.patches[0]
351         params = {'form': 'patchlistform',
352                   'action': 'Add',
353                   'project': defaults.project.id,
354                   'bundle_id': self.bundle.id,
355                   'patch_id:%d' % patch.id: 'checked'}
356
357         response = self.client.post(
358                 '/project/%s/list/' % defaults.project.linkname,
359                 params)
360
361         self.assertContains(response, 'added to bundle %s' % self.bundle.name,
362             count = 1)
363
364         self.failUnlessEqual(self.bundle.patches.count(), 1)
365         self.failUnlessEqual(self.bundle.patches.all()[0], patch)
366
367     def testAddToNonEmptyBundle(self):
368         self.bundle.append_patch(self.patches[0])
369         patch = self.patches[1]
370         params = {'form': 'patchlistform',
371                   'action': 'Add',
372                   'project': defaults.project.id,
373                   'bundle_id': self.bundle.id,
374                   'patch_id:%d' % patch.id: 'checked'}
375
376         response = self.client.post(
377                 '/project/%s/list/' % defaults.project.linkname,
378                 params)
379
380         self.assertContains(response, 'added to bundle %s' % self.bundle.name,
381             count = 1)
382
383         self.failUnlessEqual(self.bundle.patches.count(), 2)
384         self.failUnless(self.patches[0] in self.bundle.patches.all())
385         self.failUnless(self.patches[1] in self.bundle.patches.all())
386
387         # check order
388         bps = [ BundlePatch.objects.get(bundle = self.bundle,
389                                         patch = self.patches[i]) \
390                 for i in [0, 1] ]
391         self.failUnless(bps[0].order < bps[1].order)
392
393     def testAddDuplicate(self):
394         self.bundle.append_patch(self.patches[0])
395         count = self.bundle.patches.count()
396         patch = self.patches[0]
397
398         params = {'form': 'patchlistform',
399                   'action': 'Add',
400                   'project': defaults.project.id,
401                   'bundle_id': self.bundle.id,
402                   'patch_id:%d' % patch.id: 'checked'}
403
404         response = self.client.post(
405                 '/project/%s/list/' % defaults.project.linkname,
406                 params)
407
408         self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
409                             % patch.name, count = 1, status_code = 200)
410
411         self.assertEquals(count, self.bundle.patches.count())
412
413     def testAddNewAndDuplicate(self):
414         self.bundle.append_patch(self.patches[0])
415         count = self.bundle.patches.count()
416         patch = self.patches[0]
417
418         params = {'form': 'patchlistform',
419                   'action': 'Add',
420                   'project': defaults.project.id,
421                   'bundle_id': self.bundle.id,
422                   'patch_id:%d' % patch.id: 'checked',
423                   'patch_id:%d' % self.patches[1].id: 'checked'}
424
425         response = self.client.post(
426                 '/project/%s/list/' % defaults.project.linkname,
427                 params)
428
429         self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
430                             % patch.name, count = 1, status_code = 200)
431         self.assertContains(response, 'Patch &#39;%s&#39; added to bundle' \
432                             % self.patches[1].name, count = 1,
433                             status_code = 200)
434         self.assertEquals(count + 1, self.bundle.patches.count())
435
436 class BundleAddFromPatchTest(BundleTestBase):
437     def testAddToEmptyBundle(self):
438         patch = self.patches[0]
439         params = {'action': 'addtobundle',
440                   'bundle_id': self.bundle.id}
441
442         response = self.client.post('/patch/%d/' % patch.id, params)
443
444         self.assertContains(response,
445                 'added to bundle &quot;%s&quot;' % self.bundle.name,
446                 count = 1)
447
448         self.failUnlessEqual(self.bundle.patches.count(), 1)
449         self.failUnlessEqual(self.bundle.patches.all()[0], patch)
450
451     def testAddToNonEmptyBundle(self):
452         self.bundle.append_patch(self.patches[0])
453         patch = self.patches[1]
454         params = {'action': 'addtobundle',
455                   'bundle_id': self.bundle.id}
456
457         response = self.client.post('/patch/%d/' % patch.id, params)
458
459         self.assertContains(response,
460                 'added to bundle &quot;%s&quot;' % self.bundle.name,
461                 count = 1)
462
463         self.failUnlessEqual(self.bundle.patches.count(), 2)
464         self.failUnless(self.patches[0] in self.bundle.patches.all())
465         self.failUnless(self.patches[1] in self.bundle.patches.all())
466
467         # check order
468         bps = [ BundlePatch.objects.get(bundle = self.bundle,
469                                         patch = self.patches[i]) \
470                 for i in [0, 1] ]
471         self.failUnless(bps[0].order < bps[1].order)
472
473 class BundleInitialOrderTest(BundleTestBase):
474     """When creating bundles from a patch list, ensure that the patches in the
475        bundle are ordered by date"""
476
477     def setUp(self):
478         super(BundleInitialOrderTest, self).setUp(5)
479
480         # put patches in an arbitrary order
481         idxs = [2, 4, 3, 1, 0]
482         self.patches = [ self.patches[i] for i in idxs ]
483
484         # set dates to be sequential
485         last_patch = self.patches[0]
486         for patch in self.patches[1:]:
487             patch.date = last_patch.date + datetime.timedelta(0, 1)
488             patch.save()
489             last_patch = patch
490
491     def _testOrder(self, ids, expected_order):
492         newbundlename = 'testbundle-new'
493
494         # need to define our querystring explicity to enforce ordering
495         params = {'form': 'patchlistform',
496                   'bundle_name': newbundlename,
497                   'action': 'Create',
498                   'project': defaults.project.id,
499         }
500
501         data = urlencode(params) + \
502                ''.join([ '&patch_id:%d=checked' % i for i in ids ])
503
504         response = self.client.post(
505                 '/project/%s/list/' % defaults.project.linkname,
506                 data = data,
507                 content_type = 'application/x-www-form-urlencoded',
508                 )
509
510         self.assertContains(response, 'Bundle %s created' % newbundlename)
511         self.assertContains(response, 'added to bundle %s' % newbundlename,
512             count = 5)
513
514         bundle = Bundle.objects.get(name = newbundlename)
515
516         # BundlePatches should be sorted by .order by default
517         bps = BundlePatch.objects.filter(bundle = bundle)
518
519         for (bp, p) in zip(bps, expected_order):
520             self.assertEqual(bp.patch.pk, p.pk)
521
522         bundle.delete()
523
524     def testBundleForwardOrder(self):
525         ids = map(lambda p: p.id, self.patches)
526         self._testOrder(ids, self.patches)
527
528     def testBundleReverseOrder(self):
529         ids = map(lambda p: p.id, self.patches)
530         ids.reverse()
531         self._testOrder(ids, self.patches)
532
533 class BundleReorderTest(BundleTestBase):
534     def setUp(self):
535         super(BundleReorderTest, self).setUp(5)
536         for i in range(5):
537             self.bundle.append_patch(self.patches[i])
538
539     def checkReordering(self, neworder, start, end):
540         neworder_ids = [ self.patches[i].id for i in neworder ]
541
542         firstpatch = BundlePatch.objects.get(bundle = self.bundle,
543                 patch = self.patches[start]).patch
544
545         slice_ids = neworder_ids[start:end]
546         params = {'form': 'reorderform',
547                   'order_start': firstpatch.id,
548                   'neworder': slice_ids}
549
550         response = self.client.post('/user/bundle/%d/' % self.bundle.id,
551                                     params)
552
553         self.failUnlessEqual(response.status_code, 200)
554
555         bps = BundlePatch.objects.filter(bundle = self.bundle) \
556                         .order_by('order')
557
558         # check if patch IDs are in the expected order:
559         bundle_ids = [ bp.patch.id for bp in bps ]
560         self.failUnlessEqual(neworder_ids, bundle_ids)
561
562         # check if order field is still sequential:
563         order_numbers = [ bp.order for bp in bps ]
564         expected_order = range(1, len(neworder)+1) # [1 ... len(neworder)]
565         self.failUnlessEqual(order_numbers, expected_order)
566
567     def testBundleReorderAll(self):
568         # reorder all patches:
569         self.checkReordering([2,1,4,0,3], 0, 5)
570
571     def testBundleReorderEnd(self):
572         # reorder only the last three patches
573         self.checkReordering([0,1,3,2,4], 2, 5)
574
575     def testBundleReorderBegin(self):
576         # reorder only the first three patches
577         self.checkReordering([2,0,1,3,4], 0, 3)
578
579     def testBundleReorderMiddle(self):
580         # reorder only 2nd, 3rd, and 4th patches
581         self.checkReordering([0,2,3,1,4], 1, 4)