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 BundleUpdateTest(BundleTestBase):
127 super(BundleUpdateTest, self).setUp()
128 self.newname = 'newbundlename'
130 def publicString(self, public):
135 def testNoAction(self):
138 'name': self.newname,
139 'public': self.publicString(not self.bundle.public)
141 response = self.client.post('/user/bundle/%d/' % self.bundle.id, data)
142 self.assertEqual(response.status_code, 200)
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)
148 def testUpdateName(self):
149 newname = 'newbundlename'
154 'public': self.publicString(self.bundle.public)
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)
162 def testUpdatePublic(self):
163 newname = 'newbundlename'
167 'name': self.bundle.name,
168 'public': self.publicString(not self.bundle.public)
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)
176 class BundlePublicViewTest(BundleTestBase):
179 super(BundlePublicViewTest, self).setUp()
181 self.bundle.append_patch(self.patches[0])
182 self.url = '/bundle/%s/%s/' % (self.user.username, self.bundle.name)
184 def testPublicBundle(self):
185 self.bundle.public = True
187 response = self.client.get(self.url)
188 self.assertEqual(response.status_code, 200)
189 self.assertContains(response, self.patches[0].name)
191 def testPrivateBundle(self):
192 self.bundle.public = False
194 response = self.client.get(self.url)
195 self.assertEqual(response.status_code, 404)
197 class BundlePublicViewMboxTest(BundlePublicViewTest):
199 super(BundlePublicViewMboxTest, self).setUp()
200 self.url = '/bundle/%s/%s/mbox/' % (self.user.username,
203 class BundleCreateFromListTest(BundleTestBase):
204 def testCreateEmptyBundle(self):
205 newbundlename = 'testbundle-new'
206 params = {'form': 'patchlistform',
207 'bundle_name': newbundlename,
209 'project': defaults.project.id}
211 response = self.client.post(
212 '/project/%s/list/' % defaults.project.linkname,
215 self.assertContains(response, 'Bundle %s created' % newbundlename)
217 def testCreateNonEmptyBundle(self):
218 newbundlename = 'testbundle-new'
219 patch = self.patches[0]
221 params = {'form': 'patchlistform',
222 'bundle_name': newbundlename,
224 'project': defaults.project.id,
225 'patch_id:%d' % patch.id: 'checked'}
227 response = self.client.post(
228 '/project/%s/list/' % defaults.project.linkname,
231 self.assertContains(response, 'Bundle %s created' % newbundlename)
232 self.assertContains(response, 'added to bundle %s' % newbundlename,
235 bundle = Bundle.objects.get(name = newbundlename)
236 self.failUnlessEqual(bundle.patches.count(), 1)
237 self.failUnlessEqual(bundle.patches.all()[0], patch)
239 def testCreateNonEmptyBundleEmptyName(self):
240 newbundlename = 'testbundle-new'
241 patch = self.patches[0]
243 n_bundles = Bundle.objects.count()
245 params = {'form': 'patchlistform',
248 'project': defaults.project.id,
249 'patch_id:%d' % patch.id: 'checked'}
251 response = self.client.post(
252 '/project/%s/list/' % defaults.project.linkname,
255 self.assertContains(response, 'No bundle name was specified',
258 # test that no new bundles are present
259 self.failUnlessEqual(n_bundles, Bundle.objects.count())
261 def testCreateDuplicateName(self):
262 newbundlename = 'testbundle-dup'
263 patch = self.patches[0]
265 params = {'form': 'patchlistform',
266 'bundle_name': newbundlename,
268 'project': defaults.project.id,
269 'patch_id:%d' % patch.id: 'checked'}
271 response = self.client.post(
272 '/project/%s/list/' % defaults.project.linkname,
275 n_bundles = Bundle.objects.count()
276 self.assertContains(response, 'Bundle %s created' % newbundlename)
277 self.assertContains(response, 'added to bundle %s' % newbundlename,
280 bundle = Bundle.objects.get(name = newbundlename)
281 self.failUnlessEqual(bundle.patches.count(), 1)
282 self.failUnlessEqual(bundle.patches.all()[0], patch)
284 response = self.client.post(
285 '/project/%s/list/' % defaults.project.linkname,
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)
293 class BundleCreateFromPatchTest(BundleTestBase):
294 def testCreateNonEmptyBundle(self):
295 newbundlename = 'testbundle-new'
296 patch = self.patches[0]
298 params = {'name': newbundlename,
299 'action': 'createbundle'}
301 response = self.client.post('/patch/%d/' % patch.id, params)
303 self.assertContains(response,
304 'Bundle %s created' % newbundlename)
306 bundle = Bundle.objects.get(name = newbundlename)
307 self.failUnlessEqual(bundle.patches.count(), 1)
308 self.failUnlessEqual(bundle.patches.all()[0], patch)
310 def testCreateWithExistingName(self):
311 newbundlename = self.bundle.name
312 patch = self.patches[0]
314 params = {'name': newbundlename,
315 'action': 'createbundle'}
317 response = self.client.post('/patch/%d/' % patch.id, params)
319 self.assertContains(response,
320 'A bundle called %s already exists' % newbundlename)
322 count = Bundle.objects.count()
323 self.failUnlessEqual(Bundle.objects.count(), 1)
325 class BundleAddFromListTest(BundleTestBase):
326 def testAddToEmptyBundle(self):
327 patch = self.patches[0]
328 params = {'form': 'patchlistform',
330 'project': defaults.project.id,
331 'bundle_id': self.bundle.id,
332 'patch_id:%d' % patch.id: 'checked'}
334 response = self.client.post(
335 '/project/%s/list/' % defaults.project.linkname,
338 self.assertContains(response, 'added to bundle %s' % self.bundle.name,
341 self.failUnlessEqual(self.bundle.patches.count(), 1)
342 self.failUnlessEqual(self.bundle.patches.all()[0], patch)
344 def testAddToNonEmptyBundle(self):
345 self.bundle.append_patch(self.patches[0])
346 patch = self.patches[1]
347 params = {'form': 'patchlistform',
349 'project': defaults.project.id,
350 'bundle_id': self.bundle.id,
351 'patch_id:%d' % patch.id: 'checked'}
353 response = self.client.post(
354 '/project/%s/list/' % defaults.project.linkname,
357 self.assertContains(response, 'added to bundle %s' % self.bundle.name,
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())
365 bps = [ BundlePatch.objects.get(bundle = self.bundle,
366 patch = self.patches[i]) \
368 self.failUnless(bps[0].order < bps[1].order)
370 def testAddDuplicate(self):
371 self.bundle.append_patch(self.patches[0])
372 count = self.bundle.patches.count()
373 patch = self.patches[0]
375 params = {'form': 'patchlistform',
377 'project': defaults.project.id,
378 'bundle_id': self.bundle.id,
379 'patch_id:%d' % patch.id: 'checked'}
381 response = self.client.post(
382 '/project/%s/list/' % defaults.project.linkname,
385 self.assertContains(response, 'Patch '%s' already in bundle' \
386 % patch.name, count = 1, status_code = 200)
388 self.assertEquals(count, self.bundle.patches.count())
390 def testAddNewAndDuplicate(self):
391 self.bundle.append_patch(self.patches[0])
392 count = self.bundle.patches.count()
393 patch = self.patches[0]
395 params = {'form': 'patchlistform',
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'}
402 response = self.client.post(
403 '/project/%s/list/' % defaults.project.linkname,
406 self.assertContains(response, 'Patch '%s' already in bundle' \
407 % patch.name, count = 1, status_code = 200)
408 self.assertContains(response, 'Patch '%s' added to bundle' \
409 % self.patches[1].name, count = 1,
411 self.assertEquals(count + 1, self.bundle.patches.count())
413 class BundleAddFromPatchTest(BundleTestBase):
414 def testAddToEmptyBundle(self):
415 patch = self.patches[0]
416 params = {'action': 'addtobundle',
417 'bundle_id': self.bundle.id}
419 response = self.client.post('/patch/%d/' % patch.id, params)
421 self.assertContains(response,
422 'added to bundle "%s"' % self.bundle.name,
425 self.failUnlessEqual(self.bundle.patches.count(), 1)
426 self.failUnlessEqual(self.bundle.patches.all()[0], patch)
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}
434 response = self.client.post('/patch/%d/' % patch.id, params)
436 self.assertContains(response,
437 'added to bundle "%s"' % self.bundle.name,
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())
445 bps = [ BundlePatch.objects.get(bundle = self.bundle,
446 patch = self.patches[i]) \
448 self.failUnless(bps[0].order < bps[1].order)
450 class BundleInitialOrderTest(BundleTestBase):
451 """When creating bundles from a patch list, ensure that the patches in the
452 bundle are ordered by date"""
455 super(BundleInitialOrderTest, self).setUp(5)
457 # put patches in an arbitrary order
458 idxs = [2, 4, 3, 1, 0]
459 self.patches = [ self.patches[i] for i in idxs ]
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)
468 def _testOrder(self, ids, expected_order):
469 newbundlename = 'testbundle-new'
471 # need to define our querystring explicity to enforce ordering
472 params = {'form': 'patchlistform',
473 'bundle_name': newbundlename,
475 'project': defaults.project.id,
478 data = urlencode(params) + \
479 ''.join([ '&patch_id:%d=checked' % i for i in ids ])
481 response = self.client.post(
482 '/project/%s/list/' % defaults.project.linkname,
484 content_type = 'application/x-www-form-urlencoded',
487 self.assertContains(response, 'Bundle %s created' % newbundlename)
488 self.assertContains(response, 'added to bundle %s' % newbundlename,
491 bundle = Bundle.objects.get(name = newbundlename)
493 # BundlePatches should be sorted by .order by default
494 bps = BundlePatch.objects.filter(bundle = bundle)
496 for (bp, p) in zip(bps, expected_order):
497 self.assertEqual(bp.patch.pk, p.pk)
501 def testBundleForwardOrder(self):
502 ids = map(lambda p: p.id, self.patches)
503 self._testOrder(ids, self.patches)
505 def testBundleReverseOrder(self):
506 ids = map(lambda p: p.id, self.patches)
508 self._testOrder(ids, self.patches)
510 class BundleReorderTest(BundleTestBase):
512 super(BundleReorderTest, self).setUp(5)
514 self.bundle.append_patch(self.patches[i])
516 def checkReordering(self, neworder, start, end):
517 neworder_ids = [ self.patches[i].id for i in neworder ]
519 firstpatch = BundlePatch.objects.get(bundle = self.bundle,
520 patch = self.patches[start]).patch
522 slice_ids = neworder_ids[start:end]
523 params = {'form': 'reorderform',
524 'order_start': firstpatch.id,
525 'neworder': slice_ids}
527 response = self.client.post('/user/bundle/%d/' % self.bundle.id,
530 self.failUnlessEqual(response.status_code, 200)
532 bps = BundlePatch.objects.filter(bundle = self.bundle) \
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)
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)
544 def testBundleReorderAll(self):
545 # reorder all patches:
546 self.checkReordering([2,1,4,0,3], 0, 5)
548 def testBundleReorderEnd(self):
549 # reorder only the last three patches
550 self.checkReordering([0,1,3,2,4], 2, 5)
552 def testBundleReorderBegin(self):
553 # reorder only the first three patches
554 self.checkReordering([2,0,1,3,4], 0, 3)
556 def testBundleReorderMiddle(self):
557 # reorder only 2nd, 3rd, and 4th patches
558 self.checkReordering([0,2,3,1,4], 1, 4)