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 checkPatchformErrors(self, response):
131 formname = 'patchform'
132 if not formname in response.context:
134 form = response.context[formname]
137 self.assertEquals(form.errors, {})
139 def publicString(self, public):
144 def testNoAction(self):
147 'name': self.newname,
148 'public': self.publicString(not self.bundle.public)
150 response = self.client.post('/user/bundle/%d/' % self.bundle.id, data)
151 self.assertEqual(response.status_code, 200)
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)
157 def testUpdateName(self):
158 newname = 'newbundlename'
163 'public': self.publicString(self.bundle.public)
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)
171 # check other forms for errors
172 self.checkPatchformErrors(response)
174 def testUpdatePublic(self):
175 newname = 'newbundlename'
179 'name': self.bundle.name,
180 'public': self.publicString(not self.bundle.public)
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)
188 # check other forms for errors
189 self.checkPatchformErrors(response)
191 class BundleMaintainerUpdateTest(BundleUpdateTest):
194 super(BundleMaintainerUpdateTest, self).setUp()
195 profile = self.user.get_profile()
196 profile.maintainer_projects.add(defaults.project)
199 class BundlePublicViewTest(BundleTestBase):
202 super(BundlePublicViewTest, self).setUp()
204 self.bundle.append_patch(self.patches[0])
205 self.url = '/bundle/%s/%s/' % (self.user.username, self.bundle.name)
207 def testPublicBundle(self):
208 self.bundle.public = True
210 response = self.client.get(self.url)
211 self.assertEqual(response.status_code, 200)
212 self.assertContains(response, self.patches[0].name)
214 def testPrivateBundle(self):
215 self.bundle.public = False
217 response = self.client.get(self.url)
218 self.assertEqual(response.status_code, 404)
220 class BundlePublicViewMboxTest(BundlePublicViewTest):
222 super(BundlePublicViewMboxTest, self).setUp()
223 self.url = '/bundle/%s/%s/mbox/' % (self.user.username,
226 class BundleCreateFromListTest(BundleTestBase):
227 def testCreateEmptyBundle(self):
228 newbundlename = 'testbundle-new'
229 params = {'form': 'patchlistform',
230 'bundle_name': newbundlename,
232 'project': defaults.project.id}
234 response = self.client.post(
235 '/project/%s/list/' % defaults.project.linkname,
238 self.assertContains(response, 'Bundle %s created' % newbundlename)
240 def testCreateNonEmptyBundle(self):
241 newbundlename = 'testbundle-new'
242 patch = self.patches[0]
244 params = {'form': 'patchlistform',
245 'bundle_name': newbundlename,
247 'project': defaults.project.id,
248 'patch_id:%d' % patch.id: 'checked'}
250 response = self.client.post(
251 '/project/%s/list/' % defaults.project.linkname,
254 self.assertContains(response, 'Bundle %s created' % newbundlename)
255 self.assertContains(response, 'added to bundle %s' % newbundlename,
258 bundle = Bundle.objects.get(name = newbundlename)
259 self.failUnlessEqual(bundle.patches.count(), 1)
260 self.failUnlessEqual(bundle.patches.all()[0], patch)
262 def testCreateNonEmptyBundleEmptyName(self):
263 newbundlename = 'testbundle-new'
264 patch = self.patches[0]
266 n_bundles = Bundle.objects.count()
268 params = {'form': 'patchlistform',
271 'project': defaults.project.id,
272 'patch_id:%d' % patch.id: 'checked'}
274 response = self.client.post(
275 '/project/%s/list/' % defaults.project.linkname,
278 self.assertContains(response, 'No bundle name was specified',
281 # test that no new bundles are present
282 self.failUnlessEqual(n_bundles, Bundle.objects.count())
284 def testCreateDuplicateName(self):
285 newbundlename = 'testbundle-dup'
286 patch = self.patches[0]
288 params = {'form': 'patchlistform',
289 'bundle_name': newbundlename,
291 'project': defaults.project.id,
292 'patch_id:%d' % patch.id: 'checked'}
294 response = self.client.post(
295 '/project/%s/list/' % defaults.project.linkname,
298 n_bundles = Bundle.objects.count()
299 self.assertContains(response, 'Bundle %s created' % newbundlename)
300 self.assertContains(response, 'added to bundle %s' % newbundlename,
303 bundle = Bundle.objects.get(name = newbundlename)
304 self.failUnlessEqual(bundle.patches.count(), 1)
305 self.failUnlessEqual(bundle.patches.all()[0], patch)
307 response = self.client.post(
308 '/project/%s/list/' % defaults.project.linkname,
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)
316 class BundleCreateFromPatchTest(BundleTestBase):
317 def testCreateNonEmptyBundle(self):
318 newbundlename = 'testbundle-new'
319 patch = self.patches[0]
321 params = {'name': newbundlename,
322 'action': 'createbundle'}
324 response = self.client.post('/patch/%d/' % patch.id, params)
326 self.assertContains(response,
327 'Bundle %s created' % newbundlename)
329 bundle = Bundle.objects.get(name = newbundlename)
330 self.failUnlessEqual(bundle.patches.count(), 1)
331 self.failUnlessEqual(bundle.patches.all()[0], patch)
333 def testCreateWithExistingName(self):
334 newbundlename = self.bundle.name
335 patch = self.patches[0]
337 params = {'name': newbundlename,
338 'action': 'createbundle'}
340 response = self.client.post('/patch/%d/' % patch.id, params)
342 self.assertContains(response,
343 'A bundle called %s already exists' % newbundlename)
345 count = Bundle.objects.count()
346 self.failUnlessEqual(Bundle.objects.count(), 1)
348 class BundleAddFromListTest(BundleTestBase):
349 def testAddToEmptyBundle(self):
350 patch = self.patches[0]
351 params = {'form': 'patchlistform',
353 'project': defaults.project.id,
354 'bundle_id': self.bundle.id,
355 'patch_id:%d' % patch.id: 'checked'}
357 response = self.client.post(
358 '/project/%s/list/' % defaults.project.linkname,
361 self.assertContains(response, 'added to bundle %s' % self.bundle.name,
364 self.failUnlessEqual(self.bundle.patches.count(), 1)
365 self.failUnlessEqual(self.bundle.patches.all()[0], patch)
367 def testAddToNonEmptyBundle(self):
368 self.bundle.append_patch(self.patches[0])
369 patch = self.patches[1]
370 params = {'form': 'patchlistform',
372 'project': defaults.project.id,
373 'bundle_id': self.bundle.id,
374 'patch_id:%d' % patch.id: 'checked'}
376 response = self.client.post(
377 '/project/%s/list/' % defaults.project.linkname,
380 self.assertContains(response, 'added to bundle %s' % self.bundle.name,
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())
388 bps = [ BundlePatch.objects.get(bundle = self.bundle,
389 patch = self.patches[i]) \
391 self.failUnless(bps[0].order < bps[1].order)
393 def testAddDuplicate(self):
394 self.bundle.append_patch(self.patches[0])
395 count = self.bundle.patches.count()
396 patch = self.patches[0]
398 params = {'form': 'patchlistform',
400 'project': defaults.project.id,
401 'bundle_id': self.bundle.id,
402 'patch_id:%d' % patch.id: 'checked'}
404 response = self.client.post(
405 '/project/%s/list/' % defaults.project.linkname,
408 self.assertContains(response, 'Patch '%s' already in bundle' \
409 % patch.name, count = 1, status_code = 200)
411 self.assertEquals(count, self.bundle.patches.count())
413 def testAddNewAndDuplicate(self):
414 self.bundle.append_patch(self.patches[0])
415 count = self.bundle.patches.count()
416 patch = self.patches[0]
418 params = {'form': 'patchlistform',
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'}
425 response = self.client.post(
426 '/project/%s/list/' % defaults.project.linkname,
429 self.assertContains(response, 'Patch '%s' already in bundle' \
430 % patch.name, count = 1, status_code = 200)
431 self.assertContains(response, 'Patch '%s' added to bundle' \
432 % self.patches[1].name, count = 1,
434 self.assertEquals(count + 1, self.bundle.patches.count())
436 class BundleAddFromPatchTest(BundleTestBase):
437 def testAddToEmptyBundle(self):
438 patch = self.patches[0]
439 params = {'action': 'addtobundle',
440 'bundle_id': self.bundle.id}
442 response = self.client.post('/patch/%d/' % patch.id, params)
444 self.assertContains(response,
445 'added to bundle "%s"' % self.bundle.name,
448 self.failUnlessEqual(self.bundle.patches.count(), 1)
449 self.failUnlessEqual(self.bundle.patches.all()[0], patch)
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}
457 response = self.client.post('/patch/%d/' % patch.id, params)
459 self.assertContains(response,
460 'added to bundle "%s"' % self.bundle.name,
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())
468 bps = [ BundlePatch.objects.get(bundle = self.bundle,
469 patch = self.patches[i]) \
471 self.failUnless(bps[0].order < bps[1].order)
473 class BundleInitialOrderTest(BundleTestBase):
474 """When creating bundles from a patch list, ensure that the patches in the
475 bundle are ordered by date"""
478 super(BundleInitialOrderTest, self).setUp(5)
480 # put patches in an arbitrary order
481 idxs = [2, 4, 3, 1, 0]
482 self.patches = [ self.patches[i] for i in idxs ]
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)
491 def _testOrder(self, ids, expected_order):
492 newbundlename = 'testbundle-new'
494 # need to define our querystring explicity to enforce ordering
495 params = {'form': 'patchlistform',
496 'bundle_name': newbundlename,
498 'project': defaults.project.id,
501 data = urlencode(params) + \
502 ''.join([ '&patch_id:%d=checked' % i for i in ids ])
504 response = self.client.post(
505 '/project/%s/list/' % defaults.project.linkname,
507 content_type = 'application/x-www-form-urlencoded',
510 self.assertContains(response, 'Bundle %s created' % newbundlename)
511 self.assertContains(response, 'added to bundle %s' % newbundlename,
514 bundle = Bundle.objects.get(name = newbundlename)
516 # BundlePatches should be sorted by .order by default
517 bps = BundlePatch.objects.filter(bundle = bundle)
519 for (bp, p) in zip(bps, expected_order):
520 self.assertEqual(bp.patch.pk, p.pk)
524 def testBundleForwardOrder(self):
525 ids = map(lambda p: p.id, self.patches)
526 self._testOrder(ids, self.patches)
528 def testBundleReverseOrder(self):
529 ids = map(lambda p: p.id, self.patches)
531 self._testOrder(ids, self.patches)
533 class BundleReorderTest(BundleTestBase):
535 super(BundleReorderTest, self).setUp(5)
537 self.bundle.append_patch(self.patches[i])
539 def checkReordering(self, neworder, start, end):
540 neworder_ids = [ self.patches[i].id for i in neworder ]
542 firstpatch = BundlePatch.objects.get(bundle = self.bundle,
543 patch = self.patches[start]).patch
545 slice_ids = neworder_ids[start:end]
546 params = {'form': 'reorderform',
547 'order_start': firstpatch.id,
548 'neworder': slice_ids}
550 response = self.client.post('/user/bundle/%d/' % self.bundle.id,
553 self.failUnlessEqual(response.status_code, 200)
555 bps = BundlePatch.objects.filter(bundle = self.bundle) \
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)
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)
567 def testBundleReorderAll(self):
568 # reorder all patches:
569 self.checkReordering([2,1,4,0,3], 0, 5)
571 def testBundleReorderEnd(self):
572 # reorder only the last three patches
573 self.checkReordering([0,1,3,2,4], 2, 5)
575 def testBundleReorderBegin(self):
576 # reorder only the first three patches
577 self.checkReordering([2,0,1,3,4], 0, 3)
579 def testBundleReorderMiddle(self):
580 # reorder only 2nd, 3rd, and 4th patches
581 self.checkReordering([0,2,3,1,4], 1, 4)