• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# -*- coding: utf-8 -*-
2# SPDX-License-Identifier:	GPL-2.0+
3#
4# Copyright 2017 Google, Inc
5#
6
7import contextlib
8import os
9import re
10import shutil
11import sys
12import tempfile
13import unittest
14
15try:
16    from StringIO import StringIO
17except ImportError:
18    from io import StringIO
19
20import gitutil
21import patchstream
22import settings
23import tools
24
25
26@contextlib.contextmanager
27def capture():
28    import sys
29    oldout,olderr = sys.stdout, sys.stderr
30    try:
31        out=[StringIO(), StringIO()]
32        sys.stdout,sys.stderr = out
33        yield out
34    finally:
35        sys.stdout,sys.stderr = oldout, olderr
36        out[0] = out[0].getvalue()
37        out[1] = out[1].getvalue()
38
39
40class TestFunctional(unittest.TestCase):
41    def setUp(self):
42        self.tmpdir = tempfile.mkdtemp(prefix='patman.')
43
44    def tearDown(self):
45        shutil.rmtree(self.tmpdir)
46
47    @staticmethod
48    def GetPath(fname):
49        return os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
50                            'test', fname)
51
52    @classmethod
53    def GetText(self, fname):
54        return open(self.GetPath(fname), encoding='utf-8').read()
55
56    @classmethod
57    def GetPatchName(self, subject):
58        fname = re.sub('[ :]', '-', subject)
59        return fname.replace('--', '-')
60
61    def CreatePatchesForTest(self, series):
62        cover_fname = None
63        fname_list = []
64        for i, commit in enumerate(series.commits):
65            clean_subject = self.GetPatchName(commit.subject)
66            src_fname = '%04d-%s.patch' % (i + 1, clean_subject[:52])
67            fname = os.path.join(self.tmpdir, src_fname)
68            shutil.copy(self.GetPath(src_fname), fname)
69            fname_list.append(fname)
70        if series.get('cover'):
71            src_fname = '0000-cover-letter.patch'
72            cover_fname = os.path.join(self.tmpdir, src_fname)
73            fname = os.path.join(self.tmpdir, src_fname)
74            shutil.copy(self.GetPath(src_fname), fname)
75
76        return cover_fname, fname_list
77
78    def testBasic(self):
79        """Tests the basic flow of patman
80
81        This creates a series from some hard-coded patches build from a simple
82        tree with the following metadata in the top commit:
83
84            Series-to: u-boot
85            Series-prefix: RFC
86            Series-cc: Stefan Brüns <stefan.bruens@rwth-aachen.de>
87            Cover-letter-cc: Lord Mëlchett <clergy@palace.gov>
88            Series-version: 2
89            Series-changes: 4
90            - Some changes
91
92            Cover-letter:
93            test: A test patch series
94            This is a test of how the cover
95            leter
96            works
97            END
98
99        and this in the first commit:
100
101            Series-notes:
102            some notes
103            about some things
104            from the first commit
105            END
106
107            Commit-notes:
108            Some notes about
109            the first commit
110            END
111
112        with the following commands:
113
114           git log -n2 --reverse >/path/to/tools/patman/test/test01.txt
115           git format-patch --subject-prefix RFC --cover-letter HEAD~2
116           mv 00* /path/to/tools/patman/test
117
118        It checks these aspects:
119            - git log can be processed by patchstream
120            - emailing patches uses the correct command
121            - CC file has information on each commit
122            - cover letter has the expected text and subject
123            - each patch has the correct subject
124            - dry-run information prints out correctly
125            - unicode is handled correctly
126            - Series-to, Series-cc, Series-prefix, Cover-letter
127            - Cover-letter-cc, Series-version, Series-changes, Series-notes
128            - Commit-notes
129        """
130        process_tags = True
131        ignore_bad_tags = True
132        stefan = b'Stefan Br\xc3\xbcns <stefan.bruens@rwth-aachen.de>'.decode('utf-8')
133        rick = 'Richard III <richard@palace.gov>'
134        mel = b'Lord M\xc3\xablchett <clergy@palace.gov>'.decode('utf-8')
135        ed = b'Lond Edmund Blackadd\xc3\xabr <weasel@blackadder.org'.decode('utf-8')
136        fred = 'Fred Bloggs <f.bloggs@napier.net>'
137        add_maintainers = [stefan, rick]
138        dry_run = True
139        in_reply_to = mel
140        count = 2
141        settings.alias = {
142                'fdt': ['simon'],
143                'u-boot': ['u-boot@lists.denx.de'],
144                'simon': [ed],
145                'fred': [fred],
146        }
147
148        text = self.GetText('test01.txt')
149        series = patchstream.GetMetaDataForTest(text)
150        cover_fname, args = self.CreatePatchesForTest(series)
151        with capture() as out:
152            patchstream.FixPatches(series, args)
153            if cover_fname and series.get('cover'):
154                patchstream.InsertCoverLetter(cover_fname, series, count)
155            series.DoChecks()
156            cc_file = series.MakeCcFile(process_tags, cover_fname,
157                                        not ignore_bad_tags, add_maintainers,
158                                        None)
159            cmd = gitutil.EmailPatches(series, cover_fname, args,
160                    dry_run, not ignore_bad_tags, cc_file,
161                    in_reply_to=in_reply_to, thread=None)
162            series.ShowActions(args, cmd, process_tags)
163        cc_lines = open(cc_file, encoding='utf-8').read().splitlines()
164        os.remove(cc_file)
165
166        lines = out[0].splitlines()
167        self.assertEqual('Cleaned %s patches' % len(series.commits), lines[0])
168        self.assertEqual('Change log missing for v2', lines[1])
169        self.assertEqual('Change log missing for v3', lines[2])
170        self.assertEqual('Change log for unknown version v4', lines[3])
171        self.assertEqual("Alias 'pci' not found", lines[4])
172        self.assertIn('Dry run', lines[5])
173        self.assertIn('Send a total of %d patches' % count, lines[7])
174        line = 8
175        for i, commit in enumerate(series.commits):
176            self.assertEqual('   %s' % args[i], lines[line + 0])
177            line += 1
178            while 'Cc:' in lines[line]:
179                line += 1
180        self.assertEqual('To:	  u-boot@lists.denx.de', lines[line])
181        self.assertEqual('Cc:	  %s' % tools.FromUnicode(stefan),
182                         lines[line + 1])
183        self.assertEqual('Version:  3', lines[line + 2])
184        self.assertEqual('Prefix:\t  RFC', lines[line + 3])
185        self.assertEqual('Cover: 4 lines', lines[line + 4])
186        line += 5
187        self.assertEqual('      Cc:  %s' % fred, lines[line + 0])
188        self.assertEqual('      Cc:  %s' % tools.FromUnicode(ed),
189                         lines[line + 1])
190        self.assertEqual('      Cc:  %s' % tools.FromUnicode(mel),
191                         lines[line + 2])
192        self.assertEqual('      Cc:  %s' % rick, lines[line + 3])
193        expected = ('Git command: git send-email --annotate '
194                    '--in-reply-to="%s" --to "u-boot@lists.denx.de" '
195                    '--cc "%s" --cc-cmd "%s --cc-cmd %s" %s %s'
196                    % (in_reply_to, stefan, sys.argv[0], cc_file, cover_fname,
197                       ' '.join(args)))
198        line += 4
199        self.assertEqual(expected, tools.ToUnicode(lines[line]))
200
201        self.assertEqual(('%s %s\0%s' % (args[0], rick, stefan)),
202                         tools.ToUnicode(cc_lines[0]))
203        self.assertEqual(('%s %s\0%s\0%s\0%s' % (args[1], fred, ed, rick,
204                                     stefan)), tools.ToUnicode(cc_lines[1]))
205
206        expected = '''
207This is a test of how the cover
208leter
209works
210
211some notes
212about some things
213from the first commit
214
215Changes in v4:
216- Some changes
217
218Simon Glass (2):
219  pci: Correct cast for sandbox
220  fdt: Correct cast for sandbox in fdtdec_setup_mem_size_base()
221
222 cmd/pci.c                   | 3 ++-
223 fs/fat/fat.c                | 1 +
224 lib/efi_loader/efi_memory.c | 1 +
225 lib/fdtdec.c                | 3 ++-
226 4 files changed, 6 insertions(+), 2 deletions(-)
227
228--\x20
2292.7.4
230
231'''
232        lines = open(cover_fname, encoding='utf-8').read().splitlines()
233        self.assertEqual(
234                'Subject: [RFC PATCH v3 0/2] test: A test patch series',
235                lines[3])
236        self.assertEqual(expected.splitlines(), lines[7:])
237
238        for i, fname in enumerate(args):
239            lines = open(fname, encoding='utf-8').read().splitlines()
240            subject = [line for line in lines if line.startswith('Subject')]
241            self.assertEqual('Subject: [RFC %d/%d]' % (i + 1, count),
242                             subject[0][:18])
243            if i == 0:
244                # Check that we got our commit notes
245                self.assertEqual('---', lines[17])
246                self.assertEqual('Some notes about', lines[18])
247                self.assertEqual('the first commit', lines[19])
248