[PATCH v7 5/8] tests: Add tests for series
Stephen Finucane
stephen at that.guru
Sun Oct 30 00:13:37 AEDT 2016
Add a number of integration tests for parsing real series mbox files.
Signed-off-by: Stephen Finucane <stephen at that.guru>
Reviewed-by: Andy Doan <andy.doan at linaro.org>
Tested-by: Russell Currey <ruscur at russell.cc>
---
v7:
- Update to reference 'latest_series' property
v6:
- Split mbox files into a precursor patch to mitigate mailman issues
- Add tests for series name parsing
---
patchwork/tests/test_series.py | 408 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 408 insertions(+)
create mode 100644 patchwork/tests/test_series.py
diff --git a/patchwork/tests/test_series.py b/patchwork/tests/test_series.py
new file mode 100644
index 0000000..7879db9
--- /dev/null
+++ b/patchwork/tests/test_series.py
@@ -0,0 +1,408 @@
+# Patchwork - automated patch tracking system
+# Copyright (C) 2016 Stephen Finucane <stephenfinucane at hotmail.com>
+#
+# This file is part of the Patchwork package.
+#
+# Patchwork is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# Patchwork is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Patchwork; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import mailbox
+import os
+
+from django.test import TestCase
+
+from patchwork import models
+from patchwork import parser
+from patchwork.tests import utils
+
+
+TEST_SERIES_DIR = os.path.join(os.path.dirname(__file__), 'series')
+
+
+class _BaseTestCase(TestCase):
+
+ def setUp(self):
+ self.project = utils.create_project()
+ utils.create_state()
+
+ def _parse_mbox(self, name, counts):
+ """Parse an mbox file and return the results.
+
+ :param name: Name of mbox file
+ :param counts: A three-tuple of expected number of cover
+ letters, patches and replies parsed
+ """
+ results = [[], [], []]
+
+ mbox = mailbox.mbox(os.path.join(TEST_SERIES_DIR, name))
+ for msg in mbox:
+ obj = parser.parse_mail(msg, self.project.listid)
+ if type(obj) == models.CoverLetter:
+ results[0].append(obj)
+ elif type(obj) == models.Patch:
+ results[1].append(obj)
+ else:
+ results[2].append(obj)
+
+ self.assertParsed(results, counts)
+
+ return results
+
+ def assertParsed(self, results, counts):
+ self.assertEqual([len(x) for x in results], counts)
+
+ def assertSerialized(self, patches, counts):
+ """Validate correct series-ification.
+
+ TODO(stephen): Eventually this should ensure the series
+ revisions correctly linked and ordered in a series group
+
+ :param patches: list of Patch instances
+ :param count: list of integers corrsponding to number of
+ patches per series
+ """
+ series = models.Series.objects.all().order_by('date')
+
+ # sanity checks
+ self.assertEqual(series.count(), len(counts))
+ self.assertEqual(sum(counts), len(patches))
+
+ # walk through each series, ensuring each indexed patch
+ # corresponds to the correct series
+ start_idx = 0
+ for idx, count in enumerate(counts):
+ end_idx = start_idx + count
+
+ patches_ = patches[start_idx:end_idx]
+ for patch in patches_:
+ self.assertEqual(patch.latest_series, series[idx])
+
+ start_idx = end_idx
+
+
+class BaseSeriesTest(_BaseTestCase):
+ """Tests for a series without any revisions."""
+
+ def test_cover_letter(self):
+ """Series with a cover letter.
+
+ Parse a series with a cover letter and two patches.
+
+ Input:
+
+ - [PATCH 0/2] A sample series
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ """
+ covers, patches, comments = self._parse_mbox(
+ 'base-cover-letter.mbox', [1, 2, 0])
+
+ self.assertSerialized(patches, [2])
+ self.assertSerialized(covers, [1])
+
+ def test_no_cover_letter(self):
+ """Series without a cover letter.
+
+ Parse a series with two patches but no cover letter.
+
+ Input:
+
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ """
+ _, patches, _ = self._parse_mbox(
+ 'base-no-cover-letter.mbox', [0, 2, 0])
+
+ self.assertSerialized(patches, [2])
+
+ def test_out_of_order(self):
+ """Series received out of order.
+
+ Parse a series with a cover letter and two patches that is
+ received out of order.
+
+ Input:
+
+ - [PATCH 2/2] test: Convert to Markdown
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 0/2] A sample series
+ """
+ covers, patches, comments = self._parse_mbox(
+ 'base-out-of-order.mbox', [1, 2, 0])
+
+ self.assertSerialized(patches, [2])
+ self.assertSerialized(covers, [1])
+
+
+class RevisedSeriesTest(_BaseTestCase):
+ """Tests for a series plus a single revision.
+
+ NOTE(stephenfin): In each sample mbox, it is necessary to ensure
+ the first series is placed before the revision. If not, they will
+ not be parsed correctly. This is OK as in practice it would very
+ unlikely to receive a revision before a previous revision.
+ """
+
+ def test_basic(self):
+ """Series with a simple revision.
+
+ Parse a series with a cover letter and two patches, followed by
+ a second revision of the same. The second revision is correctly
+ labeled and is not sent in reply to the first revision.
+
+ Input:
+
+ - [PATCH 0/2] A sample series
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ - [PATCH v2 0/2] A sample series
+ - [PATCH v2 1/2] test: Add some lorem ipsum
+ - [PATCH v2 2/2] test: Convert to Markdown
+ """
+ covers, patches, _ = self._parse_mbox(
+ 'revision-basic.mbox', [2, 4, 0])
+
+ self.assertSerialized(patches, [2, 2])
+ self.assertSerialized(covers, [1, 1])
+
+ def test_threaded_to_cover(self):
+ """Series with a revision sent in-reply-to a cover.
+
+ Parse a series with a cover letter and two patches, followed by
+ a second revision of the same. The second revision is correctly
+ labeled but is sent in reply to the cover letter of the first
+ revision.
+
+ Input:
+
+ - [PATCH 0/2] A sample series
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ - [PATCH v2 0/2] A sample series
+ - [PATCH v2 1/2] test: Add some lorem ipsum
+ - [PATCH v2 2/2] test: Convert to Markdown
+ """
+ covers, patches, _ = self._parse_mbox(
+ 'revision-threaded-to-cover.mbox', [2, 4, 0])
+
+ self.assertSerialized(patches, [2, 2])
+ self.assertSerialized(covers, [1, 1])
+
+ def test_threaded_to_patch(self):
+ """Series with a revision sent in-reply-to a patch.
+
+ Parse a series with a cover letter and two patches, followed by
+ a second revision of the same. The second revision is correctly
+ labeled but is sent in reply to the second patch of the first
+ revision.
+
+ Input:
+
+ - [PATCH 0/2] A sample series
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ - [PATCH v2 0/2] A sample series
+ - [PATCH v2 1/2] test: Add some lorem ipsum
+ - [PATCH v2 2/2] test: Convert to Markdown
+ """
+ covers, patches, _ = self._parse_mbox(
+ 'revision-threaded-to-patch.mbox', [2, 4, 0])
+
+ self.assertSerialized(patches, [2, 2])
+ self.assertSerialized(covers, [1, 1])
+
+ def test_out_of_order(self):
+ """Series with a revision received out-of-order.
+
+ Parse a series with a cover letter and two patches, followed by
+ a second revision of the same. The second revision is correctly
+ labeled but is sent in reply to the second patch and is
+ received out of order.
+
+ Input:
+
+ - [PATCH 0/2] A sample series
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ - [PATCH v2 2/2] test: Convert to Markdown
+ - [PATCH v2 1/2] test: Add some lorem ipsum
+ - [PATCH v2 0/2] A sample series
+ """
+ covers, patches, _ = self._parse_mbox(
+ 'revision-out-of-order.mbox', [2, 4, 0])
+
+ self.assertSerialized(patches, [2, 2])
+ self.assertSerialized(covers, [1, 1])
+
+ def test_no_cover_letter(self):
+ """Series with a revision sent without a cover letter.
+
+ Parse a series with a cover letter and two patches, followed by
+ a second revision of the same. The second revision is not
+ labeled with a series version marker.
+
+ Input:
+
+ - [PATCH 0/2] A sample series
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ """
+ covers, patches, _ = self._parse_mbox(
+ 'revision-no-cover-letter.mbox', [1, 4, 0])
+
+ self.assertSerialized(patches, [2, 2])
+ self.assertSerialized(covers, [1, 0])
+
+ def test_unlabeled(self):
+ """Series with a revision sent without a version label.
+
+ Parse a series with a cover letter and two patches, followed by
+ a second revision of the same. The second revision is not
+ labeled with a series version marker.
+
+ Input:
+
+ - [PATCH 0/2] A sample series
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ - [PATCH 0/2] A sample series
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ """
+ covers, patches, _ = self._parse_mbox(
+ 'revision-unlabeled.mbox', [2, 4, 0])
+
+ self.assertSerialized(patches, [2, 2])
+ self.assertSerialized(covers, [1, 1])
+
+
+class SeriesNameTestCase(TestCase):
+
+ def setUp(self):
+ self.project = utils.create_project()
+ utils.create_state()
+
+ @staticmethod
+ def _get_mbox(name):
+ """Open an mbox file.
+
+ :param name: Name of mbox file
+ """
+ return mailbox.mbox(os.path.join(TEST_SERIES_DIR, name))
+
+ def _parse_mail(self, mail):
+ return parser.parse_mail(mail, self.project.listid)
+
+ @staticmethod
+ def _format_name(cover):
+ return cover.name.split(']')[-1]
+
+ def test_cover_letter(self):
+ """Cover letter name set as series name.
+
+ Parse a series with a cover letter and two patches and ensure
+ the series name is set to the cover letter.
+
+ Input:
+
+ - [PATCH 0/2] A sample series
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ """
+ mbox = self._get_mbox('base-cover-letter.mbox')
+
+ cover = self._parse_mail(mbox[0])
+ cover_name = self._format_name(cover)
+ self.assertEqual(cover.latest_series.name, cover_name)
+
+ self._parse_mail(mbox[1])
+ self.assertEqual(cover.latest_series.name, cover_name)
+
+ self._parse_mail(mbox[2])
+ self.assertEqual(cover.latest_series.name, cover_name)
+
+ def test_no_cover_letter(self):
+ """Series without a cover letter.
+
+ Parse a series with two patches but no cover letter and ensure
+ the series name is set to the first patch's entire subject.
+
+ Input:
+
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 2/2] test: Convert to Markdown
+ """
+ mbox = self._get_mbox('base-no-cover-letter.mbox')
+
+ patch = self._parse_mail(mbox[0])
+ series = patch.latest_series
+ self.assertEqual(series.name, patch.name)
+
+ self._parse_mail(mbox[1])
+ self.assertEqual(series.name, patch.name)
+
+ def test_out_of_order(self):
+ """Series received out of order.
+
+ Parse a series with a cover letter and two patches that is
+ received out of order. Ensure the name is updated as new
+ patches are received.
+
+ Input:
+
+ - [PATCH 2/2] test: Convert to Markdown
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 0/2] A sample series
+ """
+ mbox = self._get_mbox('base-out-of-order.mbox')
+
+ patch = self._parse_mail(mbox[0])
+ self.assertIsNone(patch.latest_series.name)
+
+ patch = self._parse_mail(mbox[1])
+ self.assertEqual(patch.latest_series.name, patch.name)
+
+ cover = self._parse_mail(mbox[2])
+ self.assertEqual(cover.latest_series.name, self._format_name(cover))
+
+ def test_custom_name(self):
+ """Series with custom name.
+
+ Parse a series with a cover letter and two patches that is
+ recevied out of order. Ensure a custom name set on the series
+ is not overriden by subsequent patches received.
+
+ Input:
+
+ - [PATCH 2/2] test: Convert to Markdown
+ - [PATCH 1/2] test: Add some lorem ipsum
+ - [PATCH 0/2] A sample series
+ """
+ mbox = self._get_mbox('base-out-of-order.mbox')
+
+ series = self._parse_mail(mbox[0]).latest_series
+ self.assertIsNone(series.name)
+
+ series_name = 'My custom series name'
+ series.name = series_name
+ series.save()
+ self.assertEqual(series.name, series_name)
+
+ self._parse_mail(mbox[1])
+ self.assertEqual(series.name, series_name)
+
+ self._parse_mail(mbox[2])
+ self.assertEqual(series.name, series_name)
--
2.7.4
More information about the Patchwork
mailing list