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