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 BundleCreateFromListTest(BundleTestBase):
125 def testCreateEmptyBundle(self):
126 newbundlename = 'testbundle-new'
127 params = {'form': 'patchlistform',
128 'bundle_name': newbundlename,
130 'project': defaults.project.id}
132 response = self.client.post(
133 '/project/%s/list/' % defaults.project.linkname,
136 self.assertContains(response, 'Bundle %s created' % newbundlename)
138 def testCreateNonEmptyBundle(self):
139 newbundlename = 'testbundle-new'
140 patch = self.patches[0]
142 params = {'form': 'patchlistform',
143 'bundle_name': newbundlename,
145 'project': defaults.project.id,
146 'patch_id:%d' % patch.id: 'checked'}
148 response = self.client.post(
149 '/project/%s/list/' % defaults.project.linkname,
152 self.assertContains(response, 'Bundle %s created' % newbundlename)
153 self.assertContains(response, 'added to bundle %s' % newbundlename,
156 bundle = Bundle.objects.get(name = newbundlename)
157 self.failUnlessEqual(bundle.patches.count(), 1)
158 self.failUnlessEqual(bundle.patches.all()[0], patch)
160 def testCreateNonEmptyBundleEmptyName(self):
161 newbundlename = 'testbundle-new'
162 patch = self.patches[0]
164 n_bundles = Bundle.objects.count()
166 params = {'form': 'patchlistform',
169 'project': defaults.project.id,
170 'patch_id:%d' % patch.id: 'checked'}
172 response = self.client.post(
173 '/project/%s/list/' % defaults.project.linkname,
176 self.assertContains(response, 'No bundle name was specified',
179 # test that no new bundles are present
180 self.failUnlessEqual(n_bundles, Bundle.objects.count())
182 def testCreateDuplicateName(self):
183 newbundlename = 'testbundle-dup'
184 patch = self.patches[0]
186 params = {'form': 'patchlistform',
187 'bundle_name': newbundlename,
189 'project': defaults.project.id,
190 'patch_id:%d' % patch.id: 'checked'}
192 response = self.client.post(
193 '/project/%s/list/' % defaults.project.linkname,
196 n_bundles = Bundle.objects.count()
197 self.assertContains(response, 'Bundle %s created' % newbundlename)
198 self.assertContains(response, 'added to bundle %s' % newbundlename,
201 bundle = Bundle.objects.get(name = newbundlename)
202 self.failUnlessEqual(bundle.patches.count(), 1)
203 self.failUnlessEqual(bundle.patches.all()[0], patch)
205 response = self.client.post(
206 '/project/%s/list/' % defaults.project.linkname,
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)
214 class BundleCreateFromPatchTest(BundleTestBase):
215 def testCreateNonEmptyBundle(self):
216 newbundlename = 'testbundle-new'
217 patch = self.patches[0]
219 params = {'name': newbundlename,
220 'action': 'createbundle'}
222 response = self.client.post('/patch/%d/' % patch.id, params)
224 self.assertContains(response,
225 'Bundle %s created' % newbundlename)
227 bundle = Bundle.objects.get(name = newbundlename)
228 self.failUnlessEqual(bundle.patches.count(), 1)
229 self.failUnlessEqual(bundle.patches.all()[0], patch)
231 def testCreateWithExistingName(self):
232 newbundlename = self.bundle.name
233 patch = self.patches[0]
235 params = {'name': newbundlename,
236 'action': 'createbundle'}
238 response = self.client.post('/patch/%d/' % patch.id, params)
240 self.assertContains(response,
241 'A bundle called %s already exists' % newbundlename)
243 count = Bundle.objects.count()
244 self.failUnlessEqual(Bundle.objects.count(), 1)
246 class BundleAddFromListTest(BundleTestBase):
247 def testAddToEmptyBundle(self):
248 patch = self.patches[0]
249 params = {'form': 'patchlistform',
251 'project': defaults.project.id,
252 'bundle_id': self.bundle.id,
253 'patch_id:%d' % patch.id: 'checked'}
255 response = self.client.post(
256 '/project/%s/list/' % defaults.project.linkname,
259 self.assertContains(response, 'added to bundle %s' % self.bundle.name,
262 self.failUnlessEqual(self.bundle.patches.count(), 1)
263 self.failUnlessEqual(self.bundle.patches.all()[0], patch)
265 def testAddToNonEmptyBundle(self):
266 self.bundle.append_patch(self.patches[0])
267 patch = self.patches[1]
268 params = {'form': 'patchlistform',
270 'project': defaults.project.id,
271 'bundle_id': self.bundle.id,
272 'patch_id:%d' % patch.id: 'checked'}
274 response = self.client.post(
275 '/project/%s/list/' % defaults.project.linkname,
278 self.assertContains(response, 'added to bundle %s' % self.bundle.name,
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())
286 bps = [ BundlePatch.objects.get(bundle = self.bundle,
287 patch = self.patches[i]) \
289 self.failUnless(bps[0].order < bps[1].order)
291 def testAddDuplicate(self):
292 self.bundle.append_patch(self.patches[0])
293 count = self.bundle.patches.count()
294 patch = self.patches[0]
296 params = {'form': 'patchlistform',
298 'project': defaults.project.id,
299 'bundle_id': self.bundle.id,
300 'patch_id:%d' % patch.id: 'checked'}
302 response = self.client.post(
303 '/project/%s/list/' % defaults.project.linkname,
306 self.assertContains(response, 'Patch '%s' already in bundle' \
307 % patch.name, count = 1, status_code = 200)
309 self.assertEquals(count, self.bundle.patches.count())
311 def testAddNewAndDuplicate(self):
312 self.bundle.append_patch(self.patches[0])
313 count = self.bundle.patches.count()
314 patch = self.patches[0]
316 params = {'form': 'patchlistform',
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'}
323 response = self.client.post(
324 '/project/%s/list/' % defaults.project.linkname,
327 self.assertContains(response, 'Patch '%s' already in bundle' \
328 % patch.name, count = 1, status_code = 200)
329 self.assertContains(response, 'Patch '%s' added to bundle' \
330 % self.patches[1].name, count = 1,
332 self.assertEquals(count + 1, self.bundle.patches.count())
334 class BundleAddFromPatchTest(BundleTestBase):
335 def testAddToEmptyBundle(self):
336 patch = self.patches[0]
337 params = {'action': 'addtobundle',
338 'bundle_id': self.bundle.id}
340 response = self.client.post('/patch/%d/' % patch.id, params)
342 self.assertContains(response,
343 'added to bundle "%s"' % self.bundle.name,
346 self.failUnlessEqual(self.bundle.patches.count(), 1)
347 self.failUnlessEqual(self.bundle.patches.all()[0], patch)
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}
355 response = self.client.post('/patch/%d/' % patch.id, params)
357 self.assertContains(response,
358 'added to bundle "%s"' % self.bundle.name,
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())
366 bps = [ BundlePatch.objects.get(bundle = self.bundle,
367 patch = self.patches[i]) \
369 self.failUnless(bps[0].order < bps[1].order)
371 class BundleInitialOrderTest(BundleTestBase):
372 """When creating bundles from a patch list, ensure that the patches in the
373 bundle are ordered by date"""
376 super(BundleInitialOrderTest, self).setUp(5)
378 # put patches in an arbitrary order
379 idxs = [2, 4, 3, 1, 0]
380 self.patches = [ self.patches[i] for i in idxs ]
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)
389 def _testOrder(self, ids, expected_order):
390 newbundlename = 'testbundle-new'
392 # need to define our querystring explicity to enforce ordering
393 params = {'form': 'patchlistform',
394 'bundle_name': newbundlename,
396 'project': defaults.project.id,
399 data = urlencode(params) + \
400 ''.join([ '&patch_id:%d=checked' % i for i in ids ])
402 response = self.client.post(
403 '/project/%s/list/' % defaults.project.linkname,
405 content_type = 'application/x-www-form-urlencoded',
408 self.assertContains(response, 'Bundle %s created' % newbundlename)
409 self.assertContains(response, 'added to bundle %s' % newbundlename,
412 bundle = Bundle.objects.get(name = newbundlename)
414 # BundlePatches should be sorted by .order by default
415 bps = BundlePatch.objects.filter(bundle = bundle)
417 for (bp, p) in zip(bps, expected_order):
418 self.assertEqual(bp.patch.pk, p.pk)
422 def testBundleForwardOrder(self):
423 ids = map(lambda p: p.id, self.patches)
424 self._testOrder(ids, self.patches)
426 def testBundleReverseOrder(self):
427 ids = map(lambda p: p.id, self.patches)
429 self._testOrder(ids, self.patches)
431 class BundleReorderTest(BundleTestBase):
433 super(BundleReorderTest, self).setUp(5)
435 self.bundle.append_patch(self.patches[i])
437 def checkReordering(self, neworder, start, end):
438 neworder_ids = [ self.patches[i].id for i in neworder ]
440 firstpatch = BundlePatch.objects.get(bundle = self.bundle,
441 patch = self.patches[start]).patch
443 slice_ids = neworder_ids[start:end]
444 params = {'form': 'reorderform',
445 'order_start': firstpatch.id,
446 'neworder': slice_ids}
448 response = self.client.post('/user/bundle/%d/' % self.bundle.id,
451 self.failUnlessEqual(response.status_code, 200)
453 bps = BundlePatch.objects.filter(bundle = self.bundle) \
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)
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)
465 def testBundleReorderAll(self):
466 # reorder all patches:
467 self.checkReordering([2,1,4,0,3], 0, 5)
469 def testBundleReorderEnd(self):
470 # reorder only the last three patches
471 self.checkReordering([0,1,3,2,4], 2, 5)
473 def testBundleReorderBegin(self):
474 # reorder only the first three patches
475 self.checkReordering([2,0,1,3,4], 0, 3)
477 def testBundleReorderMiddle(self):
478 # reorder only 2nd, 3rd, and 4th patches
479 self.checkReordering([0,2,3,1,4], 1, 4)