1 # Patchwork - automated patch tracking system
2 # Copyright (C) 2009 Jeremy Kerr <jk@ozlabs.org>
4 # This file is part of the Patchwork package.
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.
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.
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
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
28 class BundleListTest(TestCase):
30 self.user = create_user()
31 self.client.login(username = self.user.username,
32 password = self.user.username)
34 def testNoBundles(self):
35 response = self.client.get('/user/bundles/')
36 self.failUnlessEqual(response.status_code, 200)
38 len(find_in_context(response.context, 'bundles')), 0)
40 def testSingleBundle(self):
41 defaults.project.save()
42 bundle = Bundle(owner = self.user, project = defaults.project)
44 response = self.client.get('/user/bundles/')
45 self.failUnlessEqual(response.status_code, 200)
47 len(find_in_context(response.context, 'bundles')), 1)
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,
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),
70 self.patches.append(patch)
73 for patch in self.patches:
78 class BundleViewTest(BundleTestBase):
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)
86 def testNonEmptyBundle(self):
87 self.bundle.append_patch(self.patches[0])
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)
94 def testBundleOrder(self):
95 for patch in self.patches:
96 self.bundle.append_patch(patch)
98 response = self.client.get('/user/bundle/%d/' % self.bundle.id)
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)
107 # reorder and recheck
109 for patch in self.patches.__reversed__():
110 bundlepatch = BundlePatch.objects.get(bundle = self.bundle,
112 bundlepatch.order = i
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)
124 class BundlePublicViewTest(BundleTestBase):
127 super(BundlePublicViewTest, self).setUp()
129 self.bundle.append_patch(self.patches[0])
130 self.url = '/bundle/%s/%s/' % (self.user.username, self.bundle.name)
132 def testPublicBundle(self):
133 self.bundle.public = True
135 response = self.client.get(self.url)
136 self.assertEqual(response.status_code, 200)
137 self.assertContains(response, self.patches[0].name)
139 def testPrivateBundle(self):
140 self.bundle.public = False
142 response = self.client.get(self.url)
143 self.assertEqual(response.status_code, 404)
145 class BundlePublicViewMboxTest(BundlePublicViewTest):
147 super(BundlePublicViewMboxTest, self).setUp()
148 self.url = '/bundle/%s/%s/mbox/' % (self.user.username,
151 class BundleCreateFromListTest(BundleTestBase):
152 def testCreateEmptyBundle(self):
153 newbundlename = 'testbundle-new'
154 params = {'form': 'patchlistform',
155 'bundle_name': newbundlename,
157 'project': defaults.project.id}
159 response = self.client.post(
160 '/project/%s/list/' % defaults.project.linkname,
163 self.assertContains(response, 'Bundle %s created' % newbundlename)
165 def testCreateNonEmptyBundle(self):
166 newbundlename = 'testbundle-new'
167 patch = self.patches[0]
169 params = {'form': 'patchlistform',
170 'bundle_name': newbundlename,
172 'project': defaults.project.id,
173 'patch_id:%d' % patch.id: 'checked'}
175 response = self.client.post(
176 '/project/%s/list/' % defaults.project.linkname,
179 self.assertContains(response, 'Bundle %s created' % newbundlename)
180 self.assertContains(response, 'added to bundle %s' % newbundlename,
183 bundle = Bundle.objects.get(name = newbundlename)
184 self.failUnlessEqual(bundle.patches.count(), 1)
185 self.failUnlessEqual(bundle.patches.all()[0], patch)
187 def testCreateNonEmptyBundleEmptyName(self):
188 newbundlename = 'testbundle-new'
189 patch = self.patches[0]
191 n_bundles = Bundle.objects.count()
193 params = {'form': 'patchlistform',
196 'project': defaults.project.id,
197 'patch_id:%d' % patch.id: 'checked'}
199 response = self.client.post(
200 '/project/%s/list/' % defaults.project.linkname,
203 self.assertContains(response, 'No bundle name was specified',
206 # test that no new bundles are present
207 self.failUnlessEqual(n_bundles, Bundle.objects.count())
209 def testCreateDuplicateName(self):
210 newbundlename = 'testbundle-dup'
211 patch = self.patches[0]
213 params = {'form': 'patchlistform',
214 'bundle_name': newbundlename,
216 'project': defaults.project.id,
217 'patch_id:%d' % patch.id: 'checked'}
219 response = self.client.post(
220 '/project/%s/list/' % defaults.project.linkname,
223 n_bundles = Bundle.objects.count()
224 self.assertContains(response, 'Bundle %s created' % newbundlename)
225 self.assertContains(response, 'added to bundle %s' % newbundlename,
228 bundle = Bundle.objects.get(name = newbundlename)
229 self.failUnlessEqual(bundle.patches.count(), 1)
230 self.failUnlessEqual(bundle.patches.all()[0], patch)
232 response = self.client.post(
233 '/project/%s/list/' % defaults.project.linkname,
236 self.assertNotContains(response, 'Bundle %s created' % newbundlename)
237 self.assertContains(response, 'You already have a bundle called')
238 self.assertEqual(Bundle.objects.count(), n_bundles)
239 self.assertEqual(bundle.patches.count(), 1)
241 class BundleCreateFromPatchTest(BundleTestBase):
242 def testCreateNonEmptyBundle(self):
243 newbundlename = 'testbundle-new'
244 patch = self.patches[0]
246 params = {'name': newbundlename,
247 'action': 'createbundle'}
249 response = self.client.post('/patch/%d/' % patch.id, params)
251 self.assertContains(response,
252 'Bundle %s created' % newbundlename)
254 bundle = Bundle.objects.get(name = newbundlename)
255 self.failUnlessEqual(bundle.patches.count(), 1)
256 self.failUnlessEqual(bundle.patches.all()[0], patch)
258 def testCreateWithExistingName(self):
259 newbundlename = self.bundle.name
260 patch = self.patches[0]
262 params = {'name': newbundlename,
263 'action': 'createbundle'}
265 response = self.client.post('/patch/%d/' % patch.id, params)
267 self.assertContains(response,
268 'A bundle called %s already exists' % newbundlename)
270 count = Bundle.objects.count()
271 self.failUnlessEqual(Bundle.objects.count(), 1)
273 class BundleAddFromListTest(BundleTestBase):
274 def testAddToEmptyBundle(self):
275 patch = self.patches[0]
276 params = {'form': 'patchlistform',
278 'project': defaults.project.id,
279 'bundle_id': self.bundle.id,
280 'patch_id:%d' % patch.id: 'checked'}
282 response = self.client.post(
283 '/project/%s/list/' % defaults.project.linkname,
286 self.assertContains(response, 'added to bundle %s' % self.bundle.name,
289 self.failUnlessEqual(self.bundle.patches.count(), 1)
290 self.failUnlessEqual(self.bundle.patches.all()[0], patch)
292 def testAddToNonEmptyBundle(self):
293 self.bundle.append_patch(self.patches[0])
294 patch = self.patches[1]
295 params = {'form': 'patchlistform',
297 'project': defaults.project.id,
298 'bundle_id': self.bundle.id,
299 'patch_id:%d' % patch.id: 'checked'}
301 response = self.client.post(
302 '/project/%s/list/' % defaults.project.linkname,
305 self.assertContains(response, 'added to bundle %s' % self.bundle.name,
308 self.failUnlessEqual(self.bundle.patches.count(), 2)
309 self.failUnless(self.patches[0] in self.bundle.patches.all())
310 self.failUnless(self.patches[1] in self.bundle.patches.all())
313 bps = [ BundlePatch.objects.get(bundle = self.bundle,
314 patch = self.patches[i]) \
316 self.failUnless(bps[0].order < bps[1].order)
318 def testAddDuplicate(self):
319 self.bundle.append_patch(self.patches[0])
320 count = self.bundle.patches.count()
321 patch = self.patches[0]
323 params = {'form': 'patchlistform',
325 'project': defaults.project.id,
326 'bundle_id': self.bundle.id,
327 'patch_id:%d' % patch.id: 'checked'}
329 response = self.client.post(
330 '/project/%s/list/' % defaults.project.linkname,
333 self.assertContains(response, 'Patch '%s' already in bundle' \
334 % patch.name, count = 1, status_code = 200)
336 self.assertEquals(count, self.bundle.patches.count())
338 def testAddNewAndDuplicate(self):
339 self.bundle.append_patch(self.patches[0])
340 count = self.bundle.patches.count()
341 patch = self.patches[0]
343 params = {'form': 'patchlistform',
345 'project': defaults.project.id,
346 'bundle_id': self.bundle.id,
347 'patch_id:%d' % patch.id: 'checked',
348 'patch_id:%d' % self.patches[1].id: 'checked'}
350 response = self.client.post(
351 '/project/%s/list/' % defaults.project.linkname,
354 self.assertContains(response, 'Patch '%s' already in bundle' \
355 % patch.name, count = 1, status_code = 200)
356 self.assertContains(response, 'Patch '%s' added to bundle' \
357 % self.patches[1].name, count = 1,
359 self.assertEquals(count + 1, self.bundle.patches.count())
361 class BundleAddFromPatchTest(BundleTestBase):
362 def testAddToEmptyBundle(self):
363 patch = self.patches[0]
364 params = {'action': 'addtobundle',
365 'bundle_id': self.bundle.id}
367 response = self.client.post('/patch/%d/' % patch.id, params)
369 self.assertContains(response,
370 'added to bundle "%s"' % self.bundle.name,
373 self.failUnlessEqual(self.bundle.patches.count(), 1)
374 self.failUnlessEqual(self.bundle.patches.all()[0], patch)
376 def testAddToNonEmptyBundle(self):
377 self.bundle.append_patch(self.patches[0])
378 patch = self.patches[1]
379 params = {'action': 'addtobundle',
380 'bundle_id': self.bundle.id}
382 response = self.client.post('/patch/%d/' % patch.id, params)
384 self.assertContains(response,
385 'added to bundle "%s"' % self.bundle.name,
388 self.failUnlessEqual(self.bundle.patches.count(), 2)
389 self.failUnless(self.patches[0] in self.bundle.patches.all())
390 self.failUnless(self.patches[1] in self.bundle.patches.all())
393 bps = [ BundlePatch.objects.get(bundle = self.bundle,
394 patch = self.patches[i]) \
396 self.failUnless(bps[0].order < bps[1].order)
398 class BundleInitialOrderTest(BundleTestBase):
399 """When creating bundles from a patch list, ensure that the patches in the
400 bundle are ordered by date"""
403 super(BundleInitialOrderTest, self).setUp(5)
405 # put patches in an arbitrary order
406 idxs = [2, 4, 3, 1, 0]
407 self.patches = [ self.patches[i] for i in idxs ]
409 # set dates to be sequential
410 last_patch = self.patches[0]
411 for patch in self.patches[1:]:
412 patch.date = last_patch.date + datetime.timedelta(0, 1)
416 def _testOrder(self, ids, expected_order):
417 newbundlename = 'testbundle-new'
419 # need to define our querystring explicity to enforce ordering
420 params = {'form': 'patchlistform',
421 'bundle_name': newbundlename,
423 'project': defaults.project.id,
426 data = urlencode(params) + \
427 ''.join([ '&patch_id:%d=checked' % i for i in ids ])
429 response = self.client.post(
430 '/project/%s/list/' % defaults.project.linkname,
432 content_type = 'application/x-www-form-urlencoded',
435 self.assertContains(response, 'Bundle %s created' % newbundlename)
436 self.assertContains(response, 'added to bundle %s' % newbundlename,
439 bundle = Bundle.objects.get(name = newbundlename)
441 # BundlePatches should be sorted by .order by default
442 bps = BundlePatch.objects.filter(bundle = bundle)
444 for (bp, p) in zip(bps, expected_order):
445 self.assertEqual(bp.patch.pk, p.pk)
449 def testBundleForwardOrder(self):
450 ids = map(lambda p: p.id, self.patches)
451 self._testOrder(ids, self.patches)
453 def testBundleReverseOrder(self):
454 ids = map(lambda p: p.id, self.patches)
456 self._testOrder(ids, self.patches)
458 class BundleReorderTest(BundleTestBase):
460 super(BundleReorderTest, self).setUp(5)
462 self.bundle.append_patch(self.patches[i])
464 def checkReordering(self, neworder, start, end):
465 neworder_ids = [ self.patches[i].id for i in neworder ]
467 firstpatch = BundlePatch.objects.get(bundle = self.bundle,
468 patch = self.patches[start]).patch
470 slice_ids = neworder_ids[start:end]
471 params = {'form': 'reorderform',
472 'order_start': firstpatch.id,
473 'neworder': slice_ids}
475 response = self.client.post('/user/bundle/%d/' % self.bundle.id,
478 self.failUnlessEqual(response.status_code, 200)
480 bps = BundlePatch.objects.filter(bundle = self.bundle) \
483 # check if patch IDs are in the expected order:
484 bundle_ids = [ bp.patch.id for bp in bps ]
485 self.failUnlessEqual(neworder_ids, bundle_ids)
487 # check if order field is still sequential:
488 order_numbers = [ bp.order for bp in bps ]
489 expected_order = range(1, len(neworder)+1) # [1 ... len(neworder)]
490 self.failUnlessEqual(order_numbers, expected_order)
492 def testBundleReorderAll(self):
493 # reorder all patches:
494 self.checkReordering([2,1,4,0,3], 0, 5)
496 def testBundleReorderEnd(self):
497 # reorder only the last three patches
498 self.checkReordering([0,1,3,2,4], 2, 5)
500 def testBundleReorderBegin(self):
501 # reorder only the first three patches
502 self.checkReordering([2,0,1,3,4], 0, 3)
504 def testBundleReorderMiddle(self):
505 # reorder only 2nd, 3rd, and 4th patches
506 self.checkReordering([0,2,3,1,4], 1, 4)