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