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