]> git.ozlabs.org Git - patchwork/blob - apps/patchwork/tests/bundles.py
e500b3e5412b3180ed119b84c423c45a642125dc
[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 BundleCreateFromListTest(BundleTestBase):
125     def testCreateEmptyBundle(self):
126         newbundlename = 'testbundle-new'
127         params = {'form': 'patchlistform',
128                   'bundle_name': newbundlename,
129                   'action': 'Create',
130                   'project': defaults.project.id}
131
132         response = self.client.post(
133                 '/project/%s/list/' % defaults.project.linkname,
134                 params)
135
136         self.assertContains(response, 'Bundle %s created' % newbundlename)
137
138     def testCreateNonEmptyBundle(self):
139         newbundlename = 'testbundle-new'
140         patch = self.patches[0]
141
142         params = {'form': 'patchlistform',
143                   'bundle_name': newbundlename,
144                   'action': 'Create',
145                   'project': defaults.project.id,
146                   'patch_id:%d' % patch.id: 'checked'}
147
148         response = self.client.post(
149                 '/project/%s/list/' % defaults.project.linkname,
150                 params)
151
152         self.assertContains(response, 'Bundle %s created' % newbundlename)
153         self.assertContains(response, 'added to bundle %s' % newbundlename,
154             count = 1)
155
156         bundle = Bundle.objects.get(name = newbundlename)
157         self.failUnlessEqual(bundle.patches.count(), 1)
158         self.failUnlessEqual(bundle.patches.all()[0], patch)
159
160     def testCreateNonEmptyBundleEmptyName(self):
161         newbundlename = 'testbundle-new'
162         patch = self.patches[0]
163
164         n_bundles = Bundle.objects.count()
165
166         params = {'form': 'patchlistform',
167                   'bundle_name': '',
168                   'action': 'Create',
169                   'project': defaults.project.id,
170                   'patch_id:%d' % patch.id: 'checked'}
171
172         response = self.client.post(
173                 '/project/%s/list/' % defaults.project.linkname,
174                 params)
175
176         self.assertContains(response, 'No bundle name was specified',
177                 status_code = 200)
178
179         # test that no new bundles are present
180         self.failUnlessEqual(n_bundles, Bundle.objects.count())
181
182     def testCreateDuplicateName(self):
183         newbundlename = 'testbundle-dup'
184         patch = self.patches[0]
185
186         params = {'form': 'patchlistform',
187                   'bundle_name': newbundlename,
188                   'action': 'Create',
189                   'project': defaults.project.id,
190                   'patch_id:%d' % patch.id: 'checked'}
191
192         response = self.client.post(
193                 '/project/%s/list/' % defaults.project.linkname,
194                 params)
195
196         n_bundles = Bundle.objects.count()
197         self.assertContains(response, 'Bundle %s created' % newbundlename)
198         self.assertContains(response, 'added to bundle %s' % newbundlename,
199             count = 1)
200
201         bundle = Bundle.objects.get(name = newbundlename)
202         self.failUnlessEqual(bundle.patches.count(), 1)
203         self.failUnlessEqual(bundle.patches.all()[0], patch)
204
205         response = self.client.post(
206                 '/project/%s/list/' % defaults.project.linkname,
207                 params)
208
209         self.assertNotContains(response, 'Bundle %s created' % newbundlename)
210         self.assertContains(response, 'You already have a bundle called')
211         self.assertEqual(Bundle.objects.count(), n_bundles)
212         self.assertEqual(bundle.patches.count(), 1)
213
214 class BundleCreateFromPatchTest(BundleTestBase):
215     def testCreateNonEmptyBundle(self):
216         newbundlename = 'testbundle-new'
217         patch = self.patches[0]
218
219         params = {'name': newbundlename,
220                   'action': 'createbundle'}
221
222         response = self.client.post('/patch/%d/' % patch.id, params)
223
224         self.assertContains(response,
225                 'Bundle %s created' % newbundlename)
226
227         bundle = Bundle.objects.get(name = newbundlename)
228         self.failUnlessEqual(bundle.patches.count(), 1)
229         self.failUnlessEqual(bundle.patches.all()[0], patch)
230
231     def testCreateWithExistingName(self):
232         newbundlename = self.bundle.name
233         patch = self.patches[0]
234
235         params = {'name': newbundlename,
236                   'action': 'createbundle'}
237
238         response = self.client.post('/patch/%d/' % patch.id, params)
239
240         self.assertContains(response,
241                 'A bundle called %s already exists' % newbundlename)
242
243         count = Bundle.objects.count()
244         self.failUnlessEqual(Bundle.objects.count(), 1)
245
246 class BundleAddFromListTest(BundleTestBase):
247     def testAddToEmptyBundle(self):
248         patch = self.patches[0]
249         params = {'form': 'patchlistform',
250                   'action': 'Add',
251                   'project': defaults.project.id,
252                   'bundle_id': self.bundle.id,
253                   'patch_id:%d' % patch.id: 'checked'}
254
255         response = self.client.post(
256                 '/project/%s/list/' % defaults.project.linkname,
257                 params)
258
259         self.assertContains(response, 'added to bundle %s' % self.bundle.name,
260             count = 1)
261
262         self.failUnlessEqual(self.bundle.patches.count(), 1)
263         self.failUnlessEqual(self.bundle.patches.all()[0], patch)
264
265     def testAddToNonEmptyBundle(self):
266         self.bundle.append_patch(self.patches[0])
267         patch = self.patches[1]
268         params = {'form': 'patchlistform',
269                   'action': 'Add',
270                   'project': defaults.project.id,
271                   'bundle_id': self.bundle.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, 'added to bundle %s' % self.bundle.name,
279             count = 1)
280
281         self.failUnlessEqual(self.bundle.patches.count(), 2)
282         self.failUnless(self.patches[0] in self.bundle.patches.all())
283         self.failUnless(self.patches[1] in self.bundle.patches.all())
284
285         # check order
286         bps = [ BundlePatch.objects.get(bundle = self.bundle,
287                                         patch = self.patches[i]) \
288                 for i in [0, 1] ]
289         self.failUnless(bps[0].order < bps[1].order)
290
291     def testAddDuplicate(self):
292         self.bundle.append_patch(self.patches[0])
293         count = self.bundle.patches.count()
294         patch = self.patches[0]
295
296         params = {'form': 'patchlistform',
297                   'action': 'Add',
298                   'project': defaults.project.id,
299                   'bundle_id': self.bundle.id,
300                   'patch_id:%d' % patch.id: 'checked'}
301
302         response = self.client.post(
303                 '/project/%s/list/' % defaults.project.linkname,
304                 params)
305
306         self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
307                             % patch.name, count = 1, status_code = 200)
308
309         self.assertEquals(count, self.bundle.patches.count())
310
311     def testAddNewAndDuplicate(self):
312         self.bundle.append_patch(self.patches[0])
313         count = self.bundle.patches.count()
314         patch = self.patches[0]
315
316         params = {'form': 'patchlistform',
317                   'action': 'Add',
318                   'project': defaults.project.id,
319                   'bundle_id': self.bundle.id,
320                   'patch_id:%d' % patch.id: 'checked',
321                   'patch_id:%d' % self.patches[1].id: 'checked'}
322
323         response = self.client.post(
324                 '/project/%s/list/' % defaults.project.linkname,
325                 params)
326
327         self.assertContains(response, 'Patch &#39;%s&#39; already in bundle' \
328                             % patch.name, count = 1, status_code = 200)
329         self.assertContains(response, 'Patch &#39;%s&#39; added to bundle' \
330                             % self.patches[1].name, count = 1,
331                             status_code = 200)
332         self.assertEquals(count + 1, self.bundle.patches.count())
333
334 class BundleAddFromPatchTest(BundleTestBase):
335     def testAddToEmptyBundle(self):
336         patch = self.patches[0]
337         params = {'action': 'addtobundle',
338                   'bundle_id': self.bundle.id}
339
340         response = self.client.post('/patch/%d/' % patch.id, params)
341
342         self.assertContains(response,
343                 'added to bundle &quot;%s&quot;' % self.bundle.name,
344                 count = 1)
345
346         self.failUnlessEqual(self.bundle.patches.count(), 1)
347         self.failUnlessEqual(self.bundle.patches.all()[0], patch)
348
349     def testAddToNonEmptyBundle(self):
350         self.bundle.append_patch(self.patches[0])
351         patch = self.patches[1]
352         params = {'action': 'addtobundle',
353                   'bundle_id': self.bundle.id}
354
355         response = self.client.post('/patch/%d/' % patch.id, params)
356
357         self.assertContains(response,
358                 'added to bundle &quot;%s&quot;' % self.bundle.name,
359                 count = 1)
360
361         self.failUnlessEqual(self.bundle.patches.count(), 2)
362         self.failUnless(self.patches[0] in self.bundle.patches.all())
363         self.failUnless(self.patches[1] in self.bundle.patches.all())
364
365         # check order
366         bps = [ BundlePatch.objects.get(bundle = self.bundle,
367                                         patch = self.patches[i]) \
368                 for i in [0, 1] ]
369         self.failUnless(bps[0].order < bps[1].order)
370
371 class BundleInitialOrderTest(BundleTestBase):
372     """When creating bundles from a patch list, ensure that the patches in the
373        bundle are ordered by date"""
374
375     def setUp(self):
376         super(BundleInitialOrderTest, self).setUp(5)
377
378         # put patches in an arbitrary order
379         idxs = [2, 4, 3, 1, 0]
380         self.patches = [ self.patches[i] for i in idxs ]
381
382         # set dates to be sequential
383         last_patch = self.patches[0]
384         for patch in self.patches[1:]:
385             patch.date = last_patch.date + datetime.timedelta(0, 1)
386             patch.save()
387             last_patch = patch
388
389     def _testOrder(self, ids, expected_order):
390         newbundlename = 'testbundle-new'
391
392         # need to define our querystring explicity to enforce ordering
393         params = {'form': 'patchlistform',
394                   'bundle_name': newbundlename,
395                   'action': 'Create',
396                   'project': defaults.project.id,
397         }
398
399         data = urlencode(params) + \
400                ''.join([ '&patch_id:%d=checked' % i for i in ids ])
401
402         response = self.client.post(
403                 '/project/%s/list/' % defaults.project.linkname,
404                 data = data,
405                 content_type = 'application/x-www-form-urlencoded',
406                 )
407
408         self.assertContains(response, 'Bundle %s created' % newbundlename)
409         self.assertContains(response, 'added to bundle %s' % newbundlename,
410             count = 5)
411
412         bundle = Bundle.objects.get(name = newbundlename)
413
414         # BundlePatches should be sorted by .order by default
415         bps = BundlePatch.objects.filter(bundle = bundle)
416
417         for (bp, p) in zip(bps, expected_order):
418             self.assertEqual(bp.patch.pk, p.pk)
419
420         bundle.delete()
421
422     def testBundleForwardOrder(self):
423         ids = map(lambda p: p.id, self.patches)
424         self._testOrder(ids, self.patches)
425
426     def testBundleReverseOrder(self):
427         ids = map(lambda p: p.id, self.patches)
428         ids.reverse()
429         self._testOrder(ids, self.patches)
430
431 class BundleReorderTest(BundleTestBase):
432     def setUp(self):
433         super(BundleReorderTest, self).setUp(5)
434         for i in range(5):
435             self.bundle.append_patch(self.patches[i])
436
437     def checkReordering(self, neworder, start, end):
438         neworder_ids = [ self.patches[i].id for i in neworder ]
439
440         firstpatch = BundlePatch.objects.get(bundle = self.bundle,
441                 patch = self.patches[start]).patch
442
443         slice_ids = neworder_ids[start:end]
444         params = {'form': 'reorderform',
445                   'order_start': firstpatch.id,
446                   'neworder': slice_ids}
447
448         response = self.client.post('/user/bundle/%d/' % self.bundle.id,
449                                     params)
450
451         self.failUnlessEqual(response.status_code, 200)
452
453         bps = BundlePatch.objects.filter(bundle = self.bundle) \
454                         .order_by('order')
455
456         # check if patch IDs are in the expected order:
457         bundle_ids = [ bp.patch.id for bp in bps ]
458         self.failUnlessEqual(neworder_ids, bundle_ids)
459
460         # check if order field is still sequential:
461         order_numbers = [ bp.order for bp in bps ]
462         expected_order = range(1, len(neworder)+1) # [1 ... len(neworder)]
463         self.failUnlessEqual(order_numbers, expected_order)
464
465     def testBundleReorderAll(self):
466         # reorder all patches:
467         self.checkReordering([2,1,4,0,3], 0, 5)
468
469     def testBundleReorderEnd(self):
470         # reorder only the last three patches
471         self.checkReordering([0,1,3,2,4], 2, 5)
472
473     def testBundleReorderBegin(self):
474         # reorder only the first three patches
475         self.checkReordering([2,0,1,3,4], 0, 3)
476
477     def testBundleReorderMiddle(self):
478         # reorder only 2nd, 3rd, and 4th patches
479         self.checkReordering([0,2,3,1,4], 1, 4)